From 78b9749c0a4ea980a8b934645da6ae98fcc665e8 Mon Sep 17 00:00:00 2001 From: dim Date: Wed, 6 Jan 2016 20:12:03 +0000 Subject: [PATCH 1/3] Vendor import of lldb trunk r256945: https://llvm.org/svn/llvm-project/lldb/trunk@256945 --- .arcconfig | 4 + .clang-format | 9 + .gitignore | 41 + CMakeLists.txt | 62 + CODE_OWNERS.txt | 59 + INSTALL.txt | 13 + Makefile | 120 + cmake/LLDBDependencies.cmake | 207 + cmake/modules/AddLLDB.cmake | 114 + cmake/modules/LLDBConfig.cmake | 412 + cmake/modules/LLDBStandalone.cmake | 99 + cmake/platforms/Android.cmake | 188 + docs/CMakeLists.txt | 41 + docs/building-with-debug-llvm.txt | 50 + docs/code-signing.txt | 61 + docs/doxygen.cfg.in | 1631 + docs/doxygen.footer | 13 + docs/doxygen.header | 9 + docs/doxygen.intro | 19 + docs/lldb-for-gdb-users.txt | 488 + docs/lldb-gdb-remote.txt | 1675 + ...gsCommandTestCase.test_set_output_path.log | 43 + docs/testsuite/a-detailed-walkthrough.txt | 309 + docs/testsuite/best-practices.txt | 93 + examples/customization/bin-utils/.lldbinit | 5 + examples/customization/bin-utils/README | 36 + examples/customization/bin-utils/binutils.py | 122 + examples/customization/import-python/README | 40 + .../customization/import-python/importcmd.py | 31 + .../customization/pwd-cd-and-system/.lldbinit | 7 + .../customization/pwd-cd-and-system/README | 41 + .../customization/pwd-cd-and-system/utils.py | 49 + examples/darwin/heap_find/heap.py | 1244 + examples/darwin/heap_find/heap/Makefile | 33 + examples/darwin/heap_find/heap/heap_find.cpp | 1071 + examples/functions/Makefile | 18 + examples/functions/main.cpp | 364 + .../darwin/fd_interposing/FDInterposing.cpp | 1152 + .../darwin/fd_interposing/Makefile | 7 + examples/lookup/Makefile | 17 + examples/lookup/main.cpp | 235 + examples/plugins/commands/fooplugin.cpp | 56 + examples/python/cmdtemplate.py | 76 + examples/python/crashlog.py | 829 + examples/python/delta.py | 115 + examples/python/diagnose_nsstring.py | 171 + examples/python/diagnose_unwind.py | 270 + examples/python/dict_utils.py | 61 + examples/python/disasm-stress-test.py | 168 + examples/python/disasm.py | 119 + examples/python/file_extract.py | 221 + examples/python/gdb_disassemble.py | 24 + examples/python/gdbremote.py | 1362 + examples/python/globals.py | 72 + examples/python/jump.py | 173 + examples/python/lldb_module_utils.py | 59 + examples/python/lldbtk.py | 544 + examples/python/mach_o.py | 1687 + examples/python/memory.py | 181 + examples/python/operating_system.py | 104 + examples/python/performance.py | 335 + examples/python/process_events.py | 278 + examples/python/pytracer.py | 328 + examples/python/sbvalue.py | 255 + examples/python/scripted_step.py | 186 + examples/python/sources.py | 28 + examples/python/stacks.py | 59 + examples/python/symbolication.py | 640 + examples/python/types.py | 265 + .../python/x86_64_linux_target_definition.py | 353 + .../python/x86_64_qemu_target_definition.py | 352 + examples/python/x86_64_target_definition.py | 357 + examples/scripting/dictionary.c | 200 + examples/scripting/tree_utils.py | 118 + examples/summaries/cocoa/CFArray.py | 204 + examples/summaries/cocoa/CFBag.py | 146 + examples/summaries/cocoa/CFBinaryHeap.py | 142 + examples/summaries/cocoa/CFBitVector.py | 175 + examples/summaries/cocoa/CFDictionary.py | 234 + examples/summaries/cocoa/CFString.py | 325 + examples/summaries/cocoa/Class.py | 21 + examples/summaries/cocoa/Logger.py | 122 + examples/summaries/cocoa/NSBundle.py | 127 + examples/summaries/cocoa/NSData.py | 163 + examples/summaries/cocoa/NSDate.py | 269 + examples/summaries/cocoa/NSException.py | 114 + examples/summaries/cocoa/NSIndexSet.py | 150 + examples/summaries/cocoa/NSMachPort.py | 123 + examples/summaries/cocoa/NSNotification.py | 110 + examples/summaries/cocoa/NSNumber.py | 235 + examples/summaries/cocoa/NSSet.py | 263 + examples/summaries/cocoa/NSURL.py | 137 + examples/summaries/cocoa/Selector.py | 14 + examples/summaries/cocoa/attrib_fromdict.py | 38 + examples/summaries/cocoa/cache.py | 35 + examples/summaries/cocoa/metrics.py | 94 + examples/summaries/cocoa/objc_runtime.py | 781 + examples/summaries/essentials | 5 + examples/summaries/lldb | 28 + examples/summaries/objc.py | 16 + examples/summaries/pysummary.py | 18 + examples/summaries/sp_cp.py | 61 + examples/summaries/unicode_strings.py | 48 + examples/synthetic/bitfield/example.py | 100 + examples/synthetic/bitfield/program.cpp | 74 + examples/synthetic/gnu_libstdcpp.py | 451 + examples/synthetic/libcxx.py | 787 + examples/synthetic/unordered_multi.py | 110 + examples/test/.lldb-loggings | 20 + examples/test/.lldb-pre-post-flight | 12 + examples/test/.lldb-pre-post-flight.bad | 8 + examples/test/.lldbtest-config | 6 + examples/test/.lldbtest-config2 | 19 + examples/test/lldbtest-stderr | 39 + examples/test/lldbtest-stdout | 0 ...eakpointCommandTestCase.test_with_dsym.txt | 55 + ...akpointCommandTestCase.test_with_dwarf.txt | 55 + examples/test/usage-config | 10 + examples/test/usage-lldb-loggings | 125 + examples/test/usage-pre-post-flight | 65 + include/Makefile | 13 + include/lldb/Core/StringList.h | 24 + include/lldb/Host/android/Android.h | 31 + include/lldb/Host/android/Config.h | 28 + include/lldb/Host/android/HostInfoAndroid.h | 33 + .../Host/android/ProcessLauncherAndroid.h | 26 + include/lldb/Host/linux/AbstractSocket.h | 28 + include/lldb/Host/linux/Config.h | 28 + include/lldb/Host/linux/HostInfoLinux.h | 50 + include/lldb/Host/linux/HostThreadLinux.h | 32 + include/lldb/Host/linux/Personality.h | 25 + include/lldb/Host/linux/Ptrace.h | 66 + include/lldb/Host/linux/Signalfd.h | 54 + include/lldb/Host/linux/Uio.h | 23 + include/lldb/Host/macosx/Config.h | 28 + include/lldb/Host/macosx/HostInfoMacOSX.h | 47 + include/lldb/Host/macosx/HostThreadMacOSX.h | 31 + include/lldb/Host/mingw/Config.h | 30 + include/lldb/Host/msvc/Config.h | 37 + include/lldb/Host/windows/AutoHandle.h | 40 + .../windows/ConnectionGenericFileWindows.h | 68 + include/lldb/Host/windows/HostInfoWindows.h | 46 + .../lldb/Host/windows/HostProcessWindows.h | 47 + include/lldb/Host/windows/HostThreadWindows.h | 42 + include/lldb/Host/windows/LockFileWindows.h | 49 + include/lldb/Host/windows/PipeWindows.h | 74 + .../Host/windows/ProcessLauncherWindows.h | 31 + include/lldb/Host/windows/editlinewin.h | 123 + include/lldb/Host/windows/win32.h | 107 + include/lldb/Host/windows/windows.h | 29 + include/lldb/Makefile | 31 + lib/Makefile | 217 + lit/CMakeLists.txt | 36 + lit/Unit/lit.cfg | 23 + lit/Unit/lit.site.cfg.in | 25 + lit/lit.cfg | 152 + lit/lit.site.cfg.in | 22 + lldb.xcodeproj/project.pbxproj | 9546 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/LLDB.xcscheme | 107 + .../xcschemes/Run Testsuite.xcscheme | 123 + .../xcschemes/darwin-debug.xcscheme | 108 + .../xcshareddata/xcschemes/desktop.xcscheme | 80 + .../xcschemes/launcherRootXPCService.xcscheme | 72 + .../xcschemes/launcherXPCService.xcscheme | 76 + .../xcschemes/lldb-gtest.xcscheme | 92 + .../xcschemes/lldb-python-test-suite.xcscheme | 80 + .../xcshareddata/xcschemes/lldb-tool.xcscheme | 215 + lldb.xcworkspace/contents.xcworkspacedata | 13 + packages/Python/lldbsuite/__init__.py | 28 + packages/Python/lldbsuite/support/__init__.py | 0 packages/Python/lldbsuite/support/fs.py | 64 + packages/Python/lldbsuite/support/seven.py | 20 + packages/Python/lldbsuite/support/sockutil.py | 23 + packages/Python/lldbsuite/test/.categories | 0 packages/Python/lldbsuite/test/Makefile | 33 + .../Python/lldbsuite/test/README-TestSuite | 159 + packages/Python/lldbsuite/test/__init__.py | 7 + .../lldbsuite/test/android/platform/Makefile | 4 + .../platform/TestDefaultCacheLineSize.py | 38 + .../lldbsuite/test/android/platform/main.cpp | 13 + .../api/check_public_api_headers/Makefile | 5 + .../TestPublicAPIHeaders.py | 88 + .../main.cpp.template | 24 + .../test/api/multiple-debuggers/.categories | 1 + .../test/api/multiple-debuggers/Makefile | 8 + .../TestMultipleDebuggers.py | 44 + .../multi-process-driver.cpp | 283 + .../test/api/multiple-debuggers/testprog.cpp | 12 + .../lldbsuite/test/api/multithreaded/Makefile | 9 + .../api/multithreaded/TestMultithreaded.py | 91 + .../lldbsuite/test/api/multithreaded/common.h | 68 + .../test/api/multithreaded/driver.cpp | 38 + .../test/api/multithreaded/inferior.cpp | 17 + .../test/api/multithreaded/listener_test.cpp | 74 + .../test/api/multithreaded/lldb-headers.h | 11 + .../test_breakpoint_callback.cpp | 49 + .../test_listener_event_description.cpp | 97 + .../test_listener_event_process_state.cpp | 68 + .../multithreaded/test_listener_resume.cpp | 53 + .../test/arm_emulation/TestEmulations.py | 54 + .../new-test-files/test-add-1-arm.dat | 111 + .../new-test-files/test-add-1-thumb.dat | 111 + .../new-test-files/test-add-10-thumb.dat | 111 + .../new-test-files/test-add-11-thumb.dat | 111 + .../new-test-files/test-add-12-thumb.dat | 111 + .../new-test-files/test-add-2-arm.dat | 111 + .../new-test-files/test-add-2-thumb.dat | 111 + .../new-test-files/test-add-3-arm.dat | 111 + .../new-test-files/test-add-3-thumb.dat | 111 + .../new-test-files/test-add-4-arm.dat | 111 + .../new-test-files/test-add-4-thumb.dat | 111 + .../new-test-files/test-add-5-arm.dat | 111 + .../new-test-files/test-add-5-thumb.dat | 111 + .../new-test-files/test-add-6-arm.dat | 111 + .../new-test-files/test-add-6-thumb.dat | 111 + .../new-test-files/test-add-7-arm.dat | 111 + .../new-test-files/test-add-7-thumb.dat | 111 + .../new-test-files/test-add-8-arm.dat | 111 + .../new-test-files/test-add-8-thumb.dat | 111 + .../new-test-files/test-add-9-thumb.dat | 111 + .../new-test-files/test-bic-1-arm.dat | 111 + .../new-test-files/test-bic-1-thumb.dat | 111 + .../new-test-files/test-ldmia-1-arm.dat | 119 + .../new-test-files/test-ldmia-1-thumb.dat | 119 + .../new-test-files/test-ldmia-2-arm.dat | 123 + .../new-test-files/test-ldmia-2-thumb.dat | 123 + .../new-test-files/test-ldmia-3-arm.dat | 119 + .../new-test-files/test-ldmia-3-thumb.dat | 119 + .../new-test-files/test-ldr-1-arm.dat | 118 + .../new-test-files/test-ldr-1-thumb.dat | 118 + .../new-test-files/test-ldr-10-thumb.dat | 118 + .../new-test-files/test-ldr-11-thumb.dat | 118 + .../new-test-files/test-ldr-12-thumb.dat | 118 + .../new-test-files/test-ldr-2-arm.dat | 118 + .../new-test-files/test-ldr-2-thumb.dat | 118 + .../new-test-files/test-ldr-3-arm.dat | 118 + .../new-test-files/test-ldr-3-thumb.dat | 118 + .../new-test-files/test-ldr-4-arm.dat | 118 + .../new-test-files/test-ldr-4-thumb.dat | 118 + .../new-test-files/test-ldr-5-arm.dat | 118 + .../new-test-files/test-ldr-5-thumb.dat | 118 + .../new-test-files/test-ldr-6-arm.dat | 118 + .../new-test-files/test-ldr-6-thumb.dat | 118 + .../new-test-files/test-ldr-7-arm.dat | 118 + .../new-test-files/test-ldr-7-thumb.dat | 118 + .../new-test-files/test-ldr-8-arm.dat | 118 + .../new-test-files/test-ldr-8-thumb.dat | 118 + .../new-test-files/test-ldr-9-thumb.dat | 118 + .../new-test-files/test-ldrd-1-arm.dat | 119 + .../new-test-files/test-ldrd-1-thumb.dat | 119 + .../new-test-files/test-ldrd-2-thumb.dat | 119 + .../new-test-files/test-ldrh-1-thumb.dat | 118 + .../new-test-files/test-ldrsh-1-arm.dat | 118 + .../new-test-files/test-ldrsh-2-arm.dat | 118 + .../new-test-files/test-mov-1-arm.dat | 111 + .../new-test-files/test-mov-1-thumb.dat | 111 + .../new-test-files/test-mov-10-thumb.dat | 111 + .../new-test-files/test-mov-11-thumb.dat | 111 + .../new-test-files/test-mov-12-thumb.dat | 111 + .../new-test-files/test-mov-13-thumb.dat | 111 + .../new-test-files/test-mov-14-thumb.dat | 111 + .../new-test-files/test-mov-15-thumb.dat | 111 + .../new-test-files/test-mov-16-thumb.dat | 111 + .../new-test-files/test-mov-17-thumb.dat | 111 + .../new-test-files/test-mov-18-thumb.dat | 111 + .../new-test-files/test-mov-19-thumb.dat | 111 + .../new-test-files/test-mov-2-arm.dat | 111 + .../new-test-files/test-mov-2-thumb.dat | 111 + .../new-test-files/test-mov-20-thumb.dat | 111 + .../new-test-files/test-mov-21-thumb.dat | 111 + .../new-test-files/test-mov-22-thumb.dat | 111 + .../new-test-files/test-mov-23-thumb.dat | 111 + .../new-test-files/test-mov-24-thumb.dat | 111 + .../new-test-files/test-mov-25-thumb.dat | 111 + .../new-test-files/test-mov-26-thumb.dat | 111 + .../new-test-files/test-mov-27-thumb.dat | 111 + .../new-test-files/test-mov-28-thumb.dat | 111 + .../new-test-files/test-mov-29-thumb.dat | 111 + .../new-test-files/test-mov-3-arm.dat | 111 + .../new-test-files/test-mov-3-thumb.dat | 111 + .../new-test-files/test-mov-30-thumb.dat | 111 + .../new-test-files/test-mov-31-thumb.dat | 111 + .../new-test-files/test-mov-4-arm.dat | 111 + .../new-test-files/test-mov-4-thumb.dat | 111 + .../new-test-files/test-mov-5-arm.dat | 111 + .../new-test-files/test-mov-5-thumb.dat | 111 + .../new-test-files/test-mov-6-arm.dat | 111 + .../new-test-files/test-mov-6-thumb.dat | 111 + .../new-test-files/test-mov-7-thumb.dat | 111 + .../new-test-files/test-mov-8-thumb.dat | 111 + .../new-test-files/test-mov-9-thumb.dat | 111 + .../new-test-files/test-moveq-1-arm.dat | 111 + .../new-test-files/test-movs-1-arm.dat | 111 + .../new-test-files/test-mvn-1-arm.dat | 111 + .../new-test-files/test-mvn-1-thumb.dat | 111 + .../new-test-files/test-mvn-2-arm.dat | 111 + .../new-test-files/test-mvn-2-thumb.dat | 111 + .../new-test-files/test-mvn-3-arm.dat | 111 + .../new-test-files/test-mvn-3-thumb.dat | 111 + .../new-test-files/test-mvn-4-arm.dat | 111 + .../new-test-files/test-mvn-4-thumb.dat | 111 + .../new-test-files/test-pop-1-arm.dat | 121 + .../new-test-files/test-pop-1-thumb.dat | 121 + .../new-test-files/test-pop-2-arm.dat | 118 + .../new-test-files/test-pop-2-thumb.dat | 118 + .../new-test-files/test-pop-3-thumb.dat | 118 + .../new-test-files/test-push-1-arm.dat | 111 + .../new-test-files/test-push-1-thumb.dat | 111 + .../new-test-files/test-push-2-arm.dat | 111 + .../new-test-files/test-push-2-thumb.dat | 111 + .../new-test-files/test-push-3-arm.dat | 111 + .../new-test-files/test-push-3-thumb.dat | 111 + .../new-test-files/test-str-1-arm.dat | 111 + .../new-test-files/test-str-1-thumb.dat | 111 + .../new-test-files/test-str-2-arm.dat | 111 + .../new-test-files/test-str-2-thumb.dat | 111 + .../new-test-files/test-str-3-arm.dat | 111 + .../new-test-files/test-str-3-thumb.dat | 111 + .../new-test-files/test-str-4-arm.dat | 111 + .../new-test-files/test-str-4-thumb.dat | 111 + .../new-test-files/test-str-5-arm.dat | 111 + .../new-test-files/test-strb-1-arm.dat | 111 + .../new-test-files/test-strb-2-arm.dat | 111 + .../new-test-files/test-strbt-1-arm.dat | 111 + .../new-test-files/test-strd-1-thumb.dat | 111 + .../new-test-files/test-strt-1-arm.dat | 111 + .../new-test-files/test-sub-1-arm.dat | 111 + .../new-test-files/test-sub-1-thumb.dat | 111 + .../new-test-files/test-sub-10-arm.dat | 111 + .../new-test-files/test-sub-2-arm.dat | 111 + .../new-test-files/test-sub-2-thumb.dat | 111 + .../new-test-files/test-sub-3-arm.dat | 111 + .../new-test-files/test-sub-3-thumb.dat | 111 + .../new-test-files/test-sub-4-arm.dat | 111 + .../new-test-files/test-sub-4-thumb.dat | 111 + .../new-test-files/test-sub-5-arm.dat | 111 + .../new-test-files/test-sub-5-thumb.dat | 111 + .../new-test-files/test-sub-6-arm.dat | 111 + .../new-test-files/test-sub-6-thumb.dat | 111 + .../new-test-files/test-sub-8-arm.dat | 111 + .../new-test-files/test-sub-9-arm.dat | 111 + .../new-test-files/test-subs-1-arm.dat | 111 + .../new-test-files/test-subs-1-thumb.dat | 111 + .../new-test-files/test-subs-10-thumb.dat | 111 + .../new-test-files/test-subs-2-thumb.dat | 111 + .../new-test-files/test-subs-3-thumb.dat | 111 + .../new-test-files/test-subs-4-thumb.dat | 111 + .../new-test-files/test-subs-5-thumb.dat | 111 + .../new-test-files/test-subs-6-thumb.dat | 111 + .../new-test-files/test-subs-8-thumb.dat | 111 + .../new-test-files/test-subs-9-thumb.dat | 111 + .../new-test-files/test-vpop-1-thumb.dat | 125 + .../new-test-files/test-vpop-2-thumb.dat | 118 + .../new-test-files/test-vpop-3-thumb.dat | 121 + .../new-test-files/test-vpush-1-thumb.dat | 111 + .../new-test-files/test-vpush-2-thumb.dat | 111 + .../new-test-files/test-vpush-3-thumb.dat | 111 + .../Python/lldbsuite/test/attic/dotest.pl | 44 + .../Python/lldbsuite/test/attic/tester.py | 115 + packages/Python/lldbsuite/test/bench-history | 206 + packages/Python/lldbsuite/test/bench.py | 74 + .../test/benchmarks/continue/Makefile | 5 + .../continue/TestBenchmarkContinue.py | 66 + .../test/benchmarks/continue/main.cpp | 36 + .../benchmarks/disassembly/TestDisassembly.py | 155 + .../TestDoAttachThenDisassembly.py | 67 + .../TestXcode41Vs42GDBDisassembly.py | 94 + .../test/benchmarks/expression/Makefile | 5 + .../expression/TestExpressionCmd.py | 72 + .../expression/TestRepeatedExprs.py | 130 + .../test/benchmarks/expression/main.cpp | 51 + .../TestFrameVariableResponse.py | 70 + .../test/benchmarks/libcxxlist/Makefile | 5 + .../libcxxlist/TestBenchmarkLibcxxList.py | 59 + .../test/benchmarks/libcxxlist/main.cpp | 11 + .../test/benchmarks/libcxxmap/Makefile | 5 + .../libcxxmap/TestBenchmarkLibcxxMap.py | 59 + .../test/benchmarks/libcxxmap/main.cpp | 11 + .../benchmarks/startup/TestStartupDelays.py | 80 + .../benchmarks/stepping/TestSteppingSpeed.py | 67 + .../TestCompileRunToBreakpointTurnaround.py | 119 + .../Python/lldbsuite/test/configuration.py | 161 + packages/Python/lldbsuite/test/crashinfo.c | 64 + .../Python/lldbsuite/test/curses_results.py | 224 + packages/Python/lldbsuite/test/dosep.py | 1680 + packages/Python/lldbsuite/test/dotest.py | 1128 + packages/Python/lldbsuite/test/dotest_args.py | 188 + .../Python/lldbsuite/test/dotest_channels.py | 200 + .../lldbsuite/test/driver/batch_mode/Makefile | 5 + .../test/driver/batch_mode/TestBatchMode.py | 89 + .../lldbsuite/test/driver/batch_mode/main.c | 15 + .../test/example/TestSequenceFunctions.py | 36 + .../test/expression_command/.categories | 1 + .../expression_command/call-function/Makefile | 15 + .../TestCallStdStringFunction.py | 43 + .../call-function/TestCallStopAndContinue.py | 46 + .../TestCallUserDefinedFunction.py | 52 + .../expression_command/call-function/main.cpp | 53 + .../expression_command/call-restarts/Makefile | 5 + .../call-restarts/TestCallThatRestarts.py | 139 + .../call-restarts/lotta-signals.c | 61 + .../expression_command/call-throws/Makefile | 6 + .../call-throws/TestCallThatThrows.py | 112 + .../call-throws/call-throws.m | 47 + .../test/expression_command/char/Makefile | 5 + .../expression_command/char/TestExprsChar.py | 69 + .../test/expression_command/char/main.cpp | 10 + .../expr-in-syscall/Makefile | 5 + .../TestExpressionInSyscall.py | 82 + .../expr-in-syscall/main.cpp | 12 + .../expression_command/formatters/Makefile | 5 + .../formatters/TestFormatters.py | 167 + .../expression_command/formatters/foosynth.py | 29 + .../formatters/formatters.py | 17 + .../expression_command/formatters/main.cpp | 48 + .../expression_command/issue_11588/Makefile | 5 + .../issue_11588/Test11588.py | 77 + .../expression_command/issue_11588/main.cpp | 54 + .../expression_command/issue_11588/s11588.py | 26 + .../test/expression_command/macros/Makefile | 8 + .../expression_command/macros/TestMacros.py | 106 + .../test/expression_command/macros/macro1.h | 17 + .../test/expression_command/macros/macro2.h | 8 + .../test/expression_command/macros/main.cpp | 15 + .../test/expression_command/options/Makefile | 5 + .../options/TestExprOptions.py | 76 + .../test/expression_command/options/foo.cpp | 11 + .../test/expression_command/options/main.cpp | 15 + .../persist_objc_pointeetype/Makefile | 7 + .../TestPersistObjCPointeeType.py | 50 + .../persist_objc_pointeetype/main.m | 80 + .../persistent_ptr_update/Makefile | 7 + .../TestPersistentPtrUpdate.py | 41 + .../persistent_ptr_update/main.c | 11 + .../persistent_types/Makefile | 5 + .../TestNestedPersistentTypes.py | 41 + .../persistent_types/TestPersistentTypes.py | 57 + .../persistent_types/main.c | 14 + .../persistent_variables/Makefile | 5 + .../TestPersistentVariables.py | 54 + .../persistent_variables/main.c | 14 + .../expression_command/po_verbosity/Makefile | 6 + .../po_verbosity/TestPoVerbosity.py | 61 + .../expression_command/po_verbosity/main.m | 9 + .../expression_command/radar_8638051/Makefile | 5 + .../radar_8638051/Test8638051.py | 39 + .../expression_command/radar_8638051/main.c | 54 + .../expression_command/radar_9531204/Makefile | 5 + .../radar_9531204/TestPrintfAfterUp.py | 42 + .../expression_command/radar_9531204/main.c | 25 + .../expression_command/radar_9673664/Makefile | 5 + .../radar_9673664/TestExprHelpExamples.py | 41 + .../expression_command/radar_9673664/main.c | 16 + .../test/expression_command/test/Makefile | 5 + .../test/expression_command/test/TestExprs.py | 248 + .../expression_command/test/TestExprs2.py | 60 + .../test/expression_command/test/main.cpp | 44 + .../test/expression_command/timeout/Makefile | 5 + .../timeout/TestCallWithTimeout.py | 92 + .../timeout/wait-a-while.cpp | 35 + .../expression_command/two-files/Makefile | 7 + .../TestObjCTypeQueryFromOtherCompileUnit.py | 39 + .../test/expression_command/two-files/foo.m | 28 + .../test/expression_command/two-files/main.m | 22 + .../functionalities/abbreviation/.categories | 1 + .../abbreviation/TestAbbreviations.py | 101 + .../abbreviation/TestCommonShortSpellings.py | 39 + .../test/functionalities/alias/.categories | 1 + .../test/functionalities/archives/Makefile | 9 + .../test/functionalities/archives/README | 62 + .../archives/TestBSDArchives.py | 57 + .../test/functionalities/archives/a.c | 19 + .../test/functionalities/archives/b.c | 19 + .../test/functionalities/archives/main.c | 17 + .../test/functionalities/asan/Makefile | 6 + .../functionalities/asan/TestMemoryHistory.py | 103 + .../functionalities/asan/TestReportData.py | 79 + .../test/functionalities/asan/main.c | 34 + .../functionalities/attach_resume/Makefile | 7 + .../attach_resume/TestAttachResume.py | 73 + .../functionalities/attach_resume/main.cpp | 50 + .../functionalities/avoids-fd-leak/Makefile | 5 + .../avoids-fd-leak/TestFdLeak.py | 78 + .../functionalities/avoids-fd-leak/main.c | 28 + .../functionalities/backticks/.categories | 1 + .../backticks/TestBackticksWithoutATarget.py | 21 + .../breakpoint/address_breakpoints/Makefile | 6 + .../TestAddressBreakpoints.py | 91 + .../breakpoint/address_breakpoints/main.c | 8 + .../breakpoint/breakpoint_command/Makefile | 5 + .../TestBreakpointCommand.py | 206 + .../TestBreakpointCommandsFromPython.py | 95 + .../TestRegexpBreakCommand.py | 51 + .../breakpoint/breakpoint_command/a.c | 9 + .../breakpoint/breakpoint_command/b.c | 9 + .../breakpoint/breakpoint_command/bktptcmd.py | 6 + .../breakpoint/breakpoint_command/main.c | 13 + .../breakpoint/breakpoint_conditions/Makefile | 6 + .../TestBreakpointConditions.py | 181 + .../breakpoint/breakpoint_conditions/main.c | 54 + .../breakpoint/breakpoint_ids/Makefile | 9 + .../breakpoint_ids/TestBreakpointIDs.py | 51 + .../breakpoint/breakpoint_ids/main.cpp | 65 + .../breakpoint_ignore_count/Makefile | 5 + .../TestBreakpointIgnoreCount.py | 136 + .../breakpoint/breakpoint_ignore_count/main.c | 54 + .../breakpoint/breakpoint_language/Makefile | 6 + .../TestBreakpointLanguage.py | 85 + .../breakpoint/breakpoint_language/a.c | 5 + .../breakpoint/breakpoint_language/b.cpp | 5 + .../breakpoint/breakpoint_language/main.cpp | 11 + .../breakpoint/breakpoint_locations/Makefile | 9 + .../TestBreakpointLocations.py | 88 + .../breakpoint/breakpoint_locations/main.c | 43 + .../breakpoint/breakpoint_options/Makefile | 5 + .../TestBreakpointOptions.py | 93 + .../breakpoint/breakpoint_options/foo.cpp | 12 + .../breakpoint/breakpoint_options/main.cpp | 8 + .../breakpoint/comp_dir_symlink/Makefile | 7 + .../comp_dir_symlink/TestCompDirSymLink.py | 63 + .../breakpoint/comp_dir_symlink/main.cpp | 13 + .../consecutive_breakpoins/Makefile | 9 + .../TestConsecutiveBreakpoints.py | 59 + .../consecutive_breakpoins/main.cpp | 19 + .../functionalities/breakpoint/cpp/Makefile | 9 + .../cpp/TestCPPBreakpointLocations.py | 62 + .../functionalities/breakpoint/cpp/main.cpp | 77 + .../breakpoint/cpp_exception/Makefile | 5 + .../TestCPPExceptionBreakpoint.py | 48 + .../breakpoint/cpp_exception/main.cpp | 13 + .../dummy_target_breakpoints/Makefile | 9 + .../TestBreakpointsWithNoTargets.py | 67 + .../dummy_target_breakpoints/main.c | 11 + .../breakpoint/inlined_breakpoints/Makefile | 5 + .../TestInlinedBreakpoints.py | 57 + .../inlined_breakpoints/basic_type.cpp | 178 + .../breakpoint/inlined_breakpoints/int.cpp | 9 + .../functionalities/breakpoint/objc/Makefile | 7 + .../breakpoint/objc/TestObjCBreakpoints.py | 94 + .../functionalities/breakpoint/objc/main.m | 98 + .../command_history/.categories | 1 + .../command_history/TestCommandHistory.py | 68 + .../functionalities/command_regex/.categories | 1 + .../command_regex/TestCommandRegex.py | 56 + .../command_script/.categories | 1 + .../functionalities/command_script/Makefile | 5 + .../command_script/TestCommandScript.py | 142 + .../command_script/bug11569.py | 7 + .../command_script/import/Makefile | 6 + .../command_script/import/TestImport.py | 73 + .../command_script/import/bar/bar.py | 12 + .../command_script/import/bar/barutil.py | 2 + .../command_script/import/dummymodule.py | 2 + .../command_script/import/foo/bar/foobar.py | 5 + .../command_script/import/foo/foo.py | 5 + .../command_script/import/foo/foo2.py | 9 + .../command_script/import/main.c | 15 + .../import/rdar-12586188/Makefile | 3 + .../import/rdar-12586188/TestRdar12586188.py | 31 + .../import/rdar-12586188/fail12586188.py | 4 + .../import/rdar-12586188/fail212586188.py | 4 + .../import/thepackage/TPunitA.py | 6 + .../import/thepackage/TPunitB.py | 6 + .../import/thepackage/__init__.py | 6 + .../functionalities/command_script/main.cpp | 70 + .../functionalities/command_script/mysto.py | 23 + .../functionalities/command_script/py_import | 12 + .../functionalities/command_script/welcome.py | 46 + .../command_source/.categories | 1 + .../test/functionalities/command_source/.lldb | 1 + .../command_source/TestCommandSource.py | 36 + .../test/functionalities/command_source/my.py | 6 + .../functionalities/completion/.categories | 1 + .../test/functionalities/completion/Makefile | 5 + .../completion/TestCompletion.py | 324 + .../test/functionalities/completion/main.cpp | 14 + .../functionalities/conditional_break/.lldb | 3 + .../conditional_break/Makefile | 5 + .../conditional_break/TestConditionalBreak.py | 133 + .../conditional_break/conditional_break.py | 30 + .../functionalities/conditional_break/main.c | 54 + .../data-formatter/.categories | 1 + .../data-formatter/boolreference/Makefile | 9 + .../boolreference/TestFormattersBoolRefPtr.py | 72 + .../data-formatter/boolreference/main.mm | 29 + .../data-formatter/compactvectors/Makefile | 7 + .../compactvectors/TestCompactVectors.py | 58 + .../data-formatter/compactvectors/main.cpp | 26 + .../data-formatter-advanced/Makefile | 5 + .../TestDataFormatterAdv.py | 285 + .../data-formatter-advanced/main.cpp | 174 + .../data-formatter-categories/Makefile | 5 + .../TestDataFormatterCategories.py | 329 + .../data-formatter-categories/main.cpp | 46 + .../data-formatter-cpp/Makefile | 5 + .../TestDataFormatterCpp.py | 265 + .../data-formatter-cpp/main.cpp | 121 + .../data-formatter-disabling/Makefile | 5 + .../TestDataFormatterDisabling.py | 80 + .../data-formatter-disabling/main.cpp | 18 + .../data-formatter-enum-format/Makefile | 5 + .../TestDataFormatterEnumFormat.py | 65 + .../data-formatter-enum-format/main.cpp | 13 + .../data-formatter-globals/Makefile | 5 + .../TestDataFormatterGlobals.py | 67 + .../data-formatter-globals/main.cpp | 27 + .../data-formatter-named-summaries/Makefile | 5 + .../TestDataFormatterNamedSummaries.py | 122 + .../data-formatter-named-summaries/main.cpp | 59 + .../data-formatter-objc/.categories | 1 + .../data-formatter-objc/Makefile | 9 + .../TestDataFormatterObjC.py | 464 + .../data-formatter/data-formatter-objc/main.m | 694 + .../data-formatter-objc/nsstring/Makefile | 9 + .../nsstring/TestDataFormatterNSString.py | 107 + .../data-formatter-objc/nsstring/main.m | 99 + .../data-formatter-proper-plurals/Makefile | 9 + .../TestFormattersOneIsSingular.py | 90 + .../data-formatter-proper-plurals/main.m | 42 + .../data-formatter-ptr-to-array/Makefile | 5 + .../TestPtrToArrayFormatting.py | 57 + .../data-formatter-ptr-to-array/main.cpp | 17 + .../data-formatter-python-synth/Makefile | 5 + .../TestDataFormatterPythonSynth.py | 252 + .../fooSynthProvider.py | 23 + .../data-formatter-python-synth/ftsp.py | 32 + .../data-formatter-python-synth/main.cpp | 66 + .../data-formatter-script/Makefile | 5 + .../TestDataFormatterScript.py | 170 + .../data-formatter-script/main.cpp | 53 + .../data-formatter-skip-summary/Makefile | 19 + .../TestDataFormatterSkipSummary.py | 175 + .../data-formatter-skip-summary/main.cpp | 57 + .../data-formatter-smart-array/Makefile | 5 + .../TestDataFormatterSmartArray.py | 348 + .../data-formatter-smart-array/main.cpp | 65 + .../libcxx/initializerlist/Makefile | 4 + .../initializerlist/TestInitializerList.py | 40 + .../libcxx/initializerlist/main.cpp | 21 + .../libcxx/iterator/Makefile | 7 + .../TestDataFormatterLibccIterator.py | 66 + .../libcxx/iterator/main.cpp | 42 + .../data-formatter-stl/libcxx/list/Makefile | 7 + .../list/TestDataFormatterLibcxxList.py | 186 + .../libcxx/list/loop/Makefile | 7 + .../loop/TestDataFormatterLibcxxListLoop.py | 54 + .../libcxx/list/loop/main.cpp | 30 + .../data-formatter-stl/libcxx/list/main.cpp | 43 + .../data-formatter-stl/libcxx/map/Makefile | 7 + .../libcxx/map/TestDataFormatterLibccMap.py | 298 + .../data-formatter-stl/libcxx/map/main.cpp | 77 + .../libcxx/multimap/Makefile | 7 + .../TestDataFormatterLibccMultiMap.py | 298 + .../libcxx/multimap/main.cpp | 77 + .../libcxx/multiset/Makefile | 7 + .../TestDataFormatterLibcxxMultiSet.py | 69 + .../libcxx/multiset/main.cpp | 57 + .../data-formatter-stl/libcxx/set/Makefile | 7 + .../libcxx/set/TestDataFormatterLibcxxSet.py | 69 + .../data-formatter-stl/libcxx/set/main.cpp | 57 + .../data-formatter-stl/libcxx/string/Makefile | 7 + .../string/TestDataFormatterLibcxxString.py | 86 + .../data-formatter-stl/libcxx/string/main.cpp | 15 + .../libcxx/unordered/Makefile | 7 + .../unordered/TestDataFormatterUnordered.py | 75 + .../libcxx/unordered/main.cpp | 84 + .../data-formatter-stl/libcxx/vbool/Makefile | 7 + .../vbool/TestDataFormatterLibcxxVBool.py | 58 + .../data-formatter-stl/libcxx/vbool/main.cpp | 69 + .../data-formatter-stl/libcxx/vector/Makefile | 7 + .../vector/TestDataFormatterLibcxxVector.py | 180 + .../data-formatter-stl/libcxx/vector/main.cpp | 35 + .../libstdcpp/iterator/Makefile | 15 + .../iterator/TestDataFormatterStdIterator.py | 62 + .../libstdcpp/iterator/main.cpp | 38 + .../libstdcpp/list/Makefile | 15 + .../list/TestDataFormatterStdList.py | 188 + .../libstdcpp/list/main.cpp | 34 + .../data-formatter-stl/libstdcpp/map/Makefile | 14 + .../libstdcpp/map/TestDataFormatterStdMap.py | 322 + .../data-formatter-stl/libstdcpp/map/main.cpp | 55 + .../libstdcpp/string/Makefile | 15 + .../string/TestDataFormatterStdString.py | 66 + .../libstdcpp/string/main.cpp | 12 + .../libstdcpp/vbool/Makefile | 8 + .../vbool/TestDataFormatterStdVBool.py | 58 + .../libstdcpp/vbool/main.cpp | 63 + .../libstdcpp/vector/Makefile | 15 + .../vector/TestDataFormatterStdVector.py | 207 + .../libstdcpp/vector/main.cpp | 31 + .../data-formatter-synth/Makefile | 12 + .../TestDataFormatterSynth.py | 205 + .../data-formatter-synth/main.cpp | 86 + .../data-formatter-synthval/Makefile | 5 + .../TestDataFormatterSynthVal.py | 96 + .../data-formatter-synthval/main.cpp | 29 + .../myIntSynthProvider.py | 36 + .../data-formatter/dump_dynamic/Makefile | 12 + .../dump_dynamic/TestDumpDynamic.py | 5 + .../data-formatter/dump_dynamic/main.cpp | 35 + .../format-propagation/Makefile | 5 + .../TestFormatPropagation.py | 75 + .../format-propagation/main.cpp | 13 + .../frameformat_smallstruct/Makefile | 5 + .../TestFrameFormatSmallStruct.py | 38 + .../frameformat_smallstruct/main.cpp | 25 + .../data-formatter/hexcaps/Makefile | 5 + .../hexcaps/TestDataFormatterHexCaps.py | 82 + .../data-formatter/hexcaps/main.cpp | 28 + .../language_category_updates/Makefile | 5 + ...estDataFormatterLanguageCategoryUpdates.py | 60 + .../language_category_updates/main.cpp | 20 + .../data-formatter/nsarraysynth/Makefile | 9 + .../nsarraysynth/TestNSArraySynthetic.py | 67 + .../data-formatter/nsarraysynth/main.m | 35 + .../data-formatter/nsdictionarysynth/Makefile | 9 + .../TestNSDictionarySynthetic.py | 69 + .../data-formatter/nsdictionarysynth/main.m | 30 + .../data-formatter/nssetsynth/Makefile | 9 + .../nssetsynth/TestNSSetSynthetic.py | 74 + .../data-formatter/nssetsynth/main.m | 34 + .../data-formatter/ostypeformatting/Makefile | 9 + .../ostypeformatting/TestFormattersOsType.py | 52 + .../data-formatter/ostypeformatting/main.mm | 23 + .../data-formatter/ptr_ref_typedef/Makefile | 7 + .../ptr_ref_typedef/TestPtrRef2Typedef.py | 57 + .../data-formatter/ptr_ref_typedef/main.cpp | 19 + .../refpointer-recursion/Makefile | 5 + .../TestDataFormatterRefPtrRecursion.py | 41 + .../refpointer-recursion/main.cpp | 21 + .../setvaluefromcstring/Makefile | 4 + .../TestSetValueFromCString.py | 4 + .../data-formatter/setvaluefromcstring/main.m | 19 + .../data-formatter/stringprinter/Makefile | 12 + .../stringprinter/TestStringPrinter.py | 4 + .../data-formatter/stringprinter/main.cpp | 40 + .../summary-string-onfail/Makefile | 12 + .../Test-rdar-9974002.py | 133 + .../summary-string-onfail/main.cpp | 30 + .../data-formatter/synthcapping/Makefile | 5 + .../synthcapping/TestSyntheticCapping.py | 75 + .../synthcapping/fooSynthProvider.py | 21 + .../data-formatter/synthcapping/main.cpp | 62 + .../data-formatter/synthupdate/Makefile | 12 + .../TestSyntheticFilterRecompute.py | 74 + .../data-formatter/synthupdate/main.m | 25 + .../data-formatter/typedef_array/Makefile | 4 + .../typedef_array/TestTypedefArray.py | 4 + .../data-formatter/typedef_array/main.cpp | 14 + .../user-format-vs-summary/Makefile | 5 + .../TestUserFormatVsSummary.py | 59 + .../user-format-vs-summary/main.cpp | 20 + .../var-in-aggregate-misuse/Makefile | 11 + .../TestVarInAggregateMisuse.py | 74 + .../var-in-aggregate-misuse/main.cpp | 41 + .../varscript_formatting/Makefile | 5 + .../TestDataFormatterVarScriptFormatting.py | 56 + .../varscript_formatting/helperfunc.py | 5 + .../varscript_formatting/main.cpp | 8 + .../data-formatter/vector-types/Makefile | 5 + .../vector-types/TestVectorTypesFormatting.py | 73 + .../data-formatter/vector-types/main.cpp | 17 + .../test/functionalities/dead-strip/Makefile | 18 + .../dead-strip/TestDeadStrip.py | 58 + .../test/functionalities/dead-strip/cmds.txt | 4 + .../test/functionalities/dead-strip/main.c | 53 + .../test/functionalities/disassembly/Makefile | 5 + .../disassembly/TestDisassembleBreakpoint.py | 54 + .../test/functionalities/disassembly/main.cpp | 28 + .../dynamic_value_child_count/Makefile | 5 + .../TestDynamicValueChildCount.py | 78 + .../pass-to-base.cpp | 36 + .../embedded_interpreter/Makefile | 5 + .../TestConvenienceVariables.py | 78 + .../embedded_interpreter/main.c | 6 + .../test/functionalities/exec/Makefile | 5 + .../test/functionalities/exec/TestExec.py | 85 + .../test/functionalities/exec/main.cpp | 94 + .../expr-doesnt-deadlock/.categories | 1 + .../expr-doesnt-deadlock/Makefile | 6 + .../TestExprDoesntBlock.py | 57 + .../expr-doesnt-deadlock/locking.c | 80 + .../functionalities/fat_archives/Makefile | 14 + .../fat_archives/TestFatArchives.py | 58 + .../test/functionalities/fat_archives/a.c | 4 + .../test/functionalities/fat_archives/a.h | 1 + .../test/functionalities/fat_archives/main.c | 6 + .../test/functionalities/format/Makefile | 5 + .../functionalities/format/TestFormats.py | 59 + .../test/functionalities/format/main.c | 15 + .../functionalities/inferior-assert/Makefile | 5 + .../inferior-assert/TestInferiorAssert.py | 253 + .../functionalities/inferior-assert/main.c | 19 + .../functionalities/inferior-changed/Makefile | 5 + .../inferior-changed/TestInferiorChanged.py | 80 + .../functionalities/inferior-changed/main.c | 16 + .../functionalities/inferior-changed/main2.c | 18 + .../inferior-crashing/Makefile | 5 + .../inferior-crashing/TestInferiorCrashing.py | 220 + .../functionalities/inferior-crashing/main.c | 18 + .../recursive-inferior/Makefile | 7 + .../TestRecursiveInferior.py | 220 + .../recursive-inferior/main.c | 19 + .../functionalities/inline-stepping/Makefile | 9 + .../inline-stepping/TestInlineStepping.py | 270 + .../inline-stepping/calling.cpp | 136 + .../functionalities/jitloader_gdb/Makefile | 5 + .../jitloader_gdb/TestJITLoaderGDB.py | 37 + .../test/functionalities/jitloader_gdb/main.c | 44 + .../launch_with_shellexpand/Makefile | 5 + .../TestLaunchWithShellExpand.py | 97 + .../launch_with_shellexpand/file1.txt | 0 .../launch_with_shellexpand/file2.txt | 0 .../launch_with_shellexpand/file3.txt | 0 .../launch_with_shellexpand/file4.txy | 0 .../launch_with_shellexpand/file5.tyx | 0 .../launch_with_shellexpand/foo bar | 0 .../launch_with_shellexpand/main.cpp | 5 + .../test/functionalities/load_unload/Makefile | 24 + .../load_unload/TestLoadUnload.py | 357 + .../test/functionalities/load_unload/a.cpp | 22 + .../test/functionalities/load_unload/a.mk | 23 + .../test/functionalities/load_unload/b.cpp | 21 + .../test/functionalities/load_unload/b.mk | 11 + .../test/functionalities/load_unload/c.cpp | 13 + .../test/functionalities/load_unload/c.mk | 11 + .../test/functionalities/load_unload/cmds.txt | 2 + .../test/functionalities/load_unload/d.cpp | 21 + .../test/functionalities/load_unload/d.mk | 13 + .../load_unload/hidden/Makefile | 11 + .../functionalities/load_unload/hidden/d.cpp | 21 + .../test/functionalities/load_unload/main.cpp | 80 + .../test/functionalities/longjmp/Makefile | 5 + .../functionalities/longjmp/TestLongjmp.py | 85 + .../test/functionalities/longjmp/main.c | 31 + .../test/functionalities/memory/read/Makefile | 5 + .../memory/read/TestMemoryRead.py | 99 + .../test/functionalities/memory/read/main.cpp | 19 + .../non-overlapping-index-variable-i/Makefile | 5 + .../TestIndexVariable.py | 43 + .../non-overlapping-index-variable-i/main.cpp | 51 + .../test/functionalities/nosucharch/Makefile | 5 + .../nosucharch/TestNoSuchArch.py | 29 + .../test/functionalities/nosucharch/main.cpp | 3 + .../TestImageListMultiArchitecture.py | 41 + .../bin/hello-freebsd-10.0-x86_64-clang-3.3 | Bin 0 -> 7477 bytes .../bin/hello-freebsd-10.0-x86_64-gcc-4.7.3 | Bin 0 -> 7520 bytes .../bin/hello-netbsd-6.1-x86_64-gcc-4.5.3 | Bin 0 -> 7352 bytes .../hello-ubuntu-14.04-x86_64-clang-3.5pre | Bin 0 -> 8112 bytes .../bin/hello-ubuntu-14.04-x86_64-gcc-4.8.2 | Bin 0 -> 8056 bytes .../bin/hello-unknown-kalimba_arch4-kcc-36 | Bin 0 -> 17224 bytes .../bin/hello-unknown-kalimba_arch5-kcc-39 | Bin 0 -> 28356 bytes .../functionalities/object-file/bin/hello.c | 8 + .../functionalities/object-file/bin/hello.cpp | 8 + .../test/functionalities/paths/TestPaths.py | 48 + .../platform/TestPlatformCommand.py | 65 + .../functionalities/plugins/commands/Makefile | 8 + .../plugins/commands/TestPluginCommands.py | 58 + .../plugins/commands/plugin.cpp | 62 + .../plugins/python_os_plugin/Makefile | 3 + .../python_os_plugin/TestPythonOSPlugin.py | 150 + .../plugins/python_os_plugin/main.c | 7 + .../python_os_plugin/operating_system.py | 90 + .../python_os_plugin/operating_system2.py | 88 + .../postmortem/minidump/Makefile | 6 + .../postmortem/minidump/TestMiniDump.py | 128 + .../postmortem/minidump/fizzbuzz.cpp | 31 + .../postmortem/minidump/fizzbuzz_no_heap.dmp | Bin 0 -> 6297 bytes .../postmortem/minidump/main.cpp | 21 + .../functionalities/process_attach/Makefile | 7 + .../process_attach/TestProcessAttach.py | 58 + .../process_attach/attach_denied/Makefile | 7 + .../attach_denied/TestAttachDenied.py | 60 + .../process_attach/attach_denied/main.cpp | 108 + .../functionalities/process_attach/main.cpp | 37 + .../functionalities/process_group/Makefile | 5 + .../process_group/TestChangeProcessGroup.py | 107 + .../test/functionalities/process_group/main.c | 86 + .../functionalities/process_launch/Makefile | 7 + .../process_launch/TestProcessLaunch.py | 207 + .../process_launch/input-file.txt | 2 + .../functionalities/process_launch/main.cpp | 17 + .../process_launch/my_working_dir/.keep | 0 .../process_launch/print_cwd.cpp | 21 + .../process_launch/print_env.cpp | 11 + .../process_save_core/Makefile | 6 + .../process_save_core/TestProcessSaveCore.py | 58 + .../process_save_core/main.cpp | 21 + .../test/functionalities/recursion/Makefile | 5 + .../recursion/TestValueObjectRecursion.py | 59 + .../test/functionalities/recursion/main.cpp | 41 + .../test/functionalities/register/Makefile | 5 + .../functionalities/register/TestRegisters.py | 349 + .../test/functionalities/register/a.cpp | 44 + .../test/functionalities/register/main.cpp | 51 + .../test/functionalities/rerun/Makefile | 5 + .../test/functionalities/rerun/TestRerun.py | 76 + .../test/functionalities/rerun/main.cpp | 5 + .../functionalities/return-value/Makefile | 5 + .../return-value/TestReturnValue.py | 214 + .../functionalities/return-value/call-func.c | 407 + .../test/functionalities/set-data/Makefile | 7 + .../functionalities/set-data/TestSetData.py | 62 + .../test/functionalities/set-data/main.m | 19 + .../test/functionalities/signal/Makefile | 5 + .../functionalities/signal/TestSendSignal.py | 105 + .../signal/handle-segv/Makefile | 5 + .../signal/handle-segv/TestHandleSegv.py | 43 + .../functionalities/signal/handle-segv/main.c | 58 + .../test/functionalities/signal/main.c | 27 + .../functionalities/signal/raise/Makefile | 5 + .../functionalities/signal/raise/TestRaise.py | 221 + .../test/functionalities/signal/raise/main.c | 42 + .../single-quote-in-filename-to-lldb/Makefile | 5 + .../TestSingleQuoteInFilename.py | 71 + .../single-quote-in-filename-to-lldb/main.c | 7 + .../path with '09/.keep | 0 .../step-avoids-no-debug/Makefile | 8 + .../step-avoids-no-debug/TestStepNoDebug.py | 113 + .../step-avoids-no-debug/with-debug.c | 29 + .../step-avoids-no-debug/without-debug.c | 17 + .../test/functionalities/stop-hook/Makefile | 5 + .../stop-hook/TestStopHookCmd.py | 65 + .../stop-hook/TestStopHookMechanism.py | 101 + .../test/functionalities/stop-hook/main.cpp | 54 + .../stop-hook/multiple_threads/Makefile | 6 + .../TestStopHookMultipleThreads.py | 77 + .../stop-hook/multiple_threads/main.cpp | 77 + .../functionalities/target_command/Makefile | 8 + .../target_command/TestTargetCommand.py | 201 + .../test/functionalities/target_command/a.c | 16 + .../test/functionalities/target_command/b.c | 13 + .../test/functionalities/target_command/c.c | 29 + .../functionalities/target_command/globals.c | 25 + .../test/functionalities/thread/Makefile | 5 + .../functionalities/thread/TestNumThreads.py | 53 + .../thread/backtrace_all/Makefile | 6 + .../thread/backtrace_all/ParallelTask.cpp | 151 + .../thread/backtrace_all/TestBacktraceAll.py | 57 + .../thread/break_after_join/Makefile | 5 + .../break_after_join/TestBreakAfterJoin.py | 77 + .../thread/break_after_join/main.cpp | 118 + .../thread/concurrent_events/Makefile | 7 + .../concurrent_events/TestConcurrentEvents.py | 491 + .../thread/concurrent_events/main.cpp | 200 + .../thread/crash_during_step/Makefile | 4 + .../crash_during_step/TestCrashDuringStep.py | 53 + .../thread/crash_during_step/main.cpp | 16 + .../thread/create_after_attach/Makefile | 5 + .../TestCreateAfterAttach.py | 122 + .../thread/create_after_attach/main.cpp | 78 + .../thread/create_during_step/Makefile | 5 + .../TestCreateDuringStep.py | 127 + .../thread/create_during_step/main.cpp | 89 + .../thread/exit_during_break/Makefile | 5 + .../exit_during_break/TestExitDuringBreak.py | 81 + .../thread/exit_during_break/main.cpp | 130 + .../thread/exit_during_step/Makefile | 5 + .../exit_during_step/TestExitDuringStep.py | 144 + .../thread/exit_during_step/main.cpp | 87 + .../test/functionalities/thread/jump/Makefile | 5 + .../thread/jump/TestThreadJump.py | 60 + .../test/functionalities/thread/jump/main.cpp | 35 + .../functionalities/thread/jump/other.cpp | 13 + .../test/functionalities/thread/main.cpp | 50 + .../thread/multi_break/Makefile | 5 + .../multi_break/TestMultipleBreakpoints.py | 77 + .../thread/multi_break/main.cpp | 61 + .../functionalities/thread/state/Makefile | 4 + .../thread/state/TestThreadStates.py | 333 + .../functionalities/thread/state/main.cpp | 45 + .../functionalities/thread/step_out/Makefile | 6 + .../thread/step_out/TestThreadStepOut.py | 131 + .../functionalities/thread/step_out/main.cpp | 63 + .../thread/thread_exit/Makefile | 5 + .../thread/thread_exit/TestThreadExit.py | 116 + .../thread/thread_exit/main.cpp | 85 + .../thread/thread_specific_break/Makefile | 6 + .../TestThreadSpecificBreakpoint.py | 63 + .../thread/thread_specific_break/main.cpp | 20 + .../Makefile | 6 + .../TestThreadSpecificBpPlusCondition.py | 65 + .../main.cpp | 39 + .../test/functionalities/tty/TestTerminal.py | 40 + .../functionalities/type_completion/Makefile | 12 + .../type_completion/TestTypeCompletion.py | 108 + .../functionalities/type_completion/main.cpp | 81 + .../test/functionalities/type_lookup/Makefile | 9 + .../type_lookup/TestTypeLookup.py | 43 + .../test/functionalities/type_lookup/main.m | 16 + .../functionalities/unwind/noreturn/Makefile | 7 + .../unwind/noreturn/TestNoreturnUnwind.py | 76 + .../functionalities/unwind/noreturn/main.c | 37 + .../functionalities/unwind/sigtramp/Makefile | 5 + .../unwind/sigtramp/TestSigtrampUnwind.py | 81 + .../functionalities/unwind/sigtramp/main.c | 27 + .../functionalities/unwind/standard/Makefile | 3 + .../unwind/standard/TestStandardUnwind.py | 145 + .../unwind/standard/hand_written/divmod.cpp | 15 + .../unwind/standard/hand_written/fprintf.cpp | 16 + .../standard/hand_written/new_delete.cpp | 15 + .../functionalities/value_md5_crash/Makefile | 5 + .../value_md5_crash/TestValueMD5Crash.py | 52 + .../functionalities/value_md5_crash/main.cpp | 29 + .../watchpoint/hello_watchlocation/Makefile | 6 + .../hello_watchlocation/TestWatchLocation.py | 98 + .../watchpoint/hello_watchlocation/main.cpp | 106 + .../watchpoint/hello_watchpoint/Makefile | 5 + .../hello_watchpoint/TestMyFirstWatchpoint.py | 84 + .../watchpoint/hello_watchpoint/main.c | 30 + .../watchpoint/multiple_threads/Makefile | 6 + .../TestWatchpointMultipleThreads.py | 137 + .../watchpoint/multiple_threads/main.cpp | 83 + .../watchpoint/step_over_watchpoint/Makefile | 5 + .../TestStepOverWatchpoint.py | 113 + .../watchpoint/step_over_watchpoint/main.c | 19 + .../watchpoint/variable_out_of_scope/Makefile | 5 + .../TestWatchedVarHitWhenInScope.py | 83 + .../watchpoint/variable_out_of_scope/main.c | 15 + .../watchpoint/watchpoint_commands/Makefile | 5 + .../TestWatchpointCommands.py | 313 + .../watchpoint_commands/command/Makefile | 5 + .../command/TestWatchpointCommandLLDB.py | 139 + .../command/TestWatchpointCommandPython.py | 87 + .../watchpoint_commands/command/main.cpp | 28 + .../watchpoint_commands/condition/Makefile | 5 + .../condition/TestWatchpointConditionCmd.py | 78 + .../watchpoint_commands/condition/main.cpp | 28 + .../watchpoint/watchpoint_commands/main.c | 24 + .../watchpoint/watchpoint_events/Makefile | 5 + .../watchpoint_events/TestWatchpointEvents.py | 89 + .../watchpoint/watchpoint_events/main.c | 9 + .../watchpoint/watchpoint_on_vectors/Makefile | 5 + .../TestValueOfVectorVariable.py | 47 + .../watchpoint/watchpoint_on_vectors/main.c | 16 + .../watchpoint_set_command/Makefile | 6 + .../TestWatchLocationWithWatchSet.py | 89 + .../TestWatchpointSetErrorCases.py | 67 + .../watchpoint_set_command/main.cpp | 121 + .../Python/lldbsuite/test/help/TestHelp.py | 165 + .../test/issue_verification/Makefile | 4 + .../test/issue_verification/README.txt | 5 + .../TestExpectedTimeout.py.park | 20 + .../test/issue_verification/TestFail.py.park | 16 + .../issue_verification/TestRerunFail.py.park | 23 + .../TestRerunInline.py.park | 13 + .../TestRerunTimeout.py.park | 22 + .../issue_verification/TestSignal.py.park | 26 + .../TestSignalOutsideTestMethod.py.park | 24 + .../issue_verification/TestTimeout.py.park | 19 + .../test/issue_verification/disable.py | 20 + .../test/issue_verification/enable.py | 20 + .../inline_rerun_inferior.cpp | 14 + .../test/issue_verification/rerun_base.py | 28 + .../lldbsuite/test/lang/c/anonymous/Makefile | 5 + .../test/lang/c/anonymous/TestAnonymous.py | 147 + .../lldbsuite/test/lang/c/anonymous/main.c | 82 + .../test/lang/c/array_types/Makefile | 5 + .../test/lang/c/array_types/TestArrayTypes.py | 198 + .../test/lang/c/array_types/cmds.txt | 3 + .../lldbsuite/test/lang/c/array_types/main.c | 51 + .../lldbsuite/test/lang/c/bitfields/Makefile | 5 + .../test/lang/c/bitfields/TestBitfields.py | 162 + .../lldbsuite/test/lang/c/bitfields/main.c | 67 + .../lldbsuite/test/lang/c/blocks/Makefile | 6 + .../test/lang/c/blocks/TestBlocks.py | 61 + .../lldbsuite/test/lang/c/blocks/main.c | 21 + .../test/lang/c/const_variables/Makefile | 7 + .../c/const_variables/TestConstVariables.py | 64 + .../test/lang/c/const_variables/functions.c | 18 + .../test/lang/c/const_variables/main.c | 23 + .../lldbsuite/test/lang/c/enum_types/Makefile | 5 + .../test/lang/c/enum_types/TestEnumTypes.py | 71 + .../lldbsuite/test/lang/c/enum_types/main.c | 29 + .../lldbsuite/test/lang/c/forward/Makefile | 5 + .../lldbsuite/test/lang/c/forward/README.txt | 5 + .../lang/c/forward/TestForwardDeclaration.py | 47 + .../lldbsuite/test/lang/c/forward/foo.c | 8 + .../lldbsuite/test/lang/c/forward/foo.h | 4 + .../lldbsuite/test/lang/c/forward/main.c | 18 + .../test/lang/c/function_types/Makefile | 5 + .../c/function_types/TestFunctionTypes.py | 76 + .../test/lang/c/function_types/main.c | 22 + .../test/lang/c/global_variables/Makefile | 8 + .../c/global_variables/TestGlobalVariables.py | 76 + .../test/lang/c/global_variables/a.c | 15 + .../test/lang/c/global_variables/cmds.txt | 3 + .../test/lang/c/global_variables/main.c | 24 + .../lldbsuite/test/lang/c/inlines/Makefile | 5 + .../lldbsuite/test/lang/c/inlines/inlines.c | 53 + .../lldbsuite/test/lang/c/inlines/inlines.h | 4 + .../lldbsuite/test/lang/c/modules/Makefile | 5 + .../test/lang/c/modules/TestCModules.py | 65 + .../lldbsuite/test/lang/c/modules/main.c | 20 + .../lldbsuite/test/lang/c/recurse/Makefile | 5 + .../lldbsuite/test/lang/c/recurse/main.c | 28 + .../test/lang/c/register_variables/Makefile | 7 + .../TestRegisterVariables.py | 70 + .../test/lang/c/register_variables/test.c | 27 + .../lldbsuite/test/lang/c/set_values/Makefile | 5 + .../test/lang/c/set_values/TestSetValues.py | 111 + .../lldbsuite/test/lang/c/set_values/main.c | 116 + .../lldbsuite/test/lang/c/shared_lib/Makefile | 8 + .../test/lang/c/shared_lib/TestSharedLib.py | 71 + .../lldbsuite/test/lang/c/shared_lib/foo.c | 22 + .../lldbsuite/test/lang/c/shared_lib/foo.h | 10 + .../lldbsuite/test/lang/c/shared_lib/main.c | 13 + .../c/shared_lib_stripped_symbols/Makefile | 10 + .../TestSharedLibStrippedSymbols.py | 73 + .../lang/c/shared_lib_stripped_symbols/foo.c | 22 + .../lang/c/shared_lib_stripped_symbols/foo.h | 12 + .../lang/c/shared_lib_stripped_symbols/main.c | 13 + .../lldbsuite/test/lang/c/stepping/Makefile | 5 + .../lang/c/stepping/TestStepAndBreakpoints.py | 242 + .../lang/c/stepping/TestThreadStepping.py | 81 + .../lldbsuite/test/lang/c/stepping/main.c | 69 + .../lldbsuite/test/lang/c/strings/Makefile | 5 + .../test/lang/c/strings/TestCStrings.py | 54 + .../lldbsuite/test/lang/c/strings/main.c | 18 + .../test/lang/c/struct_types/Makefile | 3 + .../lang/c/struct_types/TestStructTypes.py | 4 + .../lldbsuite/test/lang/c/struct_types/main.c | 43 + .../test/lang/c/tls_globals/Makefile | 11 + .../test/lang/c/tls_globals/TestTlsGlobals.py | 75 + .../lldbsuite/test/lang/c/tls_globals/a.c | 18 + .../lldbsuite/test/lang/c/tls_globals/main.c | 36 + .../lldbsuite/test/lang/c/typedef/Makefile | 5 + .../test/lang/c/typedef/Testtypedef.py | 40 + .../lldbsuite/test/lang/c/typedef/main.c | 46 + .../lldbsuite/test/lang/cpp/auto/Makefile | 7 + .../test/lang/cpp/auto/TestCPPAuto.py | 25 + .../lldbsuite/test/lang/cpp/auto/main.cpp | 16 + .../lldbsuite/test/lang/cpp/bool/Makefile | 5 + .../test/lang/cpp/bool/TestCPPBool.py | 26 + .../lldbsuite/test/lang/cpp/bool/main.cpp | 17 + .../lang/cpp/breakpoint-commands/Makefile | 5 + .../TestCPPBreakpointCommands.py | 84 + .../lang/cpp/breakpoint-commands/nested.cpp | 76 + .../test/lang/cpp/call-function/Makefile | 5 + .../cpp/call-function/TestCallCPPFunction.py | 33 + .../test/lang/cpp/call-function/main.cpp | 11 + .../test/lang/cpp/chained-calls/Makefile | 5 + .../cpp/chained-calls/TestCppChainedCalls.py | 73 + .../test/lang/cpp/chained-calls/main.cpp | 33 + .../test/lang/cpp/char1632_t/.categories | 1 + .../test/lang/cpp/char1632_t/Makefile | 8 + .../test/lang/cpp/char1632_t/TestChar1632T.py | 90 + .../test/lang/cpp/char1632_t/main.cpp | 44 + .../test/lang/cpp/class_static/Makefile | 5 + .../cpp/class_static/TestStaticVariables.py | 120 + .../test/lang/cpp/class_static/main.cpp | 53 + .../test/lang/cpp/class_types/Makefile | 5 + .../lang/cpp/class_types/TestClassTypes.py | 215 + .../class_types/TestClassTypesDisassembly.py | 94 + .../test/lang/cpp/class_types/cmds.txt | 3 + .../test/lang/cpp/class_types/main.cpp | 126 + .../lldbsuite/test/lang/cpp/diamond/Makefile | 5 + .../test/lang/cpp/diamond/TestDiamond.py | 41 + .../lldbsuite/test/lang/cpp/diamond/main.cpp | 85 + .../test/lang/cpp/dynamic-value/Makefile | 5 + .../cpp/dynamic-value/TestCppValueCast.py | 129 + .../cpp/dynamic-value/TestDynamicValue.py | 223 + .../lang/cpp/dynamic-value/pass-to-base.cpp | 69 + .../lang/cpp/dynamic-value/sbvalue-cast.cpp | 80 + .../test/lang/cpp/enum_types/Makefile | 10 + .../lang/cpp/enum_types/TestCPP11EnumTypes.py | 110 + .../test/lang/cpp/enum_types/main.cpp | 33 + .../test/lang/cpp/exceptions/Makefile | 5 + .../exceptions/TestCPPExceptionBreakpoints.py | 65 + .../test/lang/cpp/exceptions/exceptions.cpp | 42 + .../lang/cpp/frame-var-anon-unions/Makefile | 5 + .../TestFrameVariableAnonymousUnions.py | 27 + .../lang/cpp/frame-var-anon-unions/main.cpp | 23 + .../test/lang/cpp/global_operators/Makefile | 5 + .../TestCppGlobalOperators.py | 54 + .../test/lang/cpp/global_operators/main.cpp | 16 + .../test/lang/cpp/incomplete-types/Makefile | 35 + .../TestCppIncompleteTypes.py | 66 + .../test/lang/cpp/incomplete-types/a.cpp | 10 + .../test/lang/cpp/incomplete-types/a.h | 11 + .../test/lang/cpp/incomplete-types/length.cpp | 8 + .../test/lang/cpp/incomplete-types/length.h | 8 + .../test/lang/cpp/incomplete-types/main.cpp | 18 + .../test/lang/cpp/limit-debug-info/Makefile | 7 + .../TestWithLimitDebugInfo.py | 48 + .../test/lang/cpp/limit-debug-info/base.cpp | 6 + .../test/lang/cpp/limit-debug-info/base.h | 10 + .../lang/cpp/limit-debug-info/derived.cpp | 6 + .../test/lang/cpp/limit-debug-info/derived.h | 13 + .../test/lang/cpp/limit-debug-info/main.cpp | 7 + .../test/lang/cpp/namespace/Makefile | 5 + .../test/lang/cpp/namespace/TestNamespace.py | 125 + .../lang/cpp/namespace/TestNamespaceLookup.py | 223 + .../test/lang/cpp/namespace/cmds.txt | 3 + .../test/lang/cpp/namespace/main.cpp | 124 + .../lldbsuite/test/lang/cpp/namespace/ns.cpp | 32 + .../lldbsuite/test/lang/cpp/namespace/ns.h | 36 + .../lldbsuite/test/lang/cpp/namespace/ns2.cpp | 65 + .../lldbsuite/test/lang/cpp/namespace/ns3.cpp | 27 + .../lldbsuite/test/lang/cpp/nsimport/Makefile | 5 + .../test/lang/cpp/nsimport/TestCppNsImport.py | 101 + .../lldbsuite/test/lang/cpp/nsimport/main.cpp | 72 + .../lang/cpp/overloaded-functions/Makefile | 5 + .../TestOverloadedFunctions.py | 36 + .../lang/cpp/overloaded-functions/main.cpp | 43 + .../cpp/overloaded-functions/static-a.cpp | 9 + .../cpp/overloaded-functions/static-b.cpp | 9 + .../test/lang/cpp/rdar12991846/Makefile | 8 + .../lang/cpp/rdar12991846/TestRdar12991846.py | 85 + .../test/lang/cpp/rdar12991846/main.cpp | 21 + .../test/lang/cpp/rvalue-references/Makefile | 7 + .../rvalue-references/TestRvalueReferences.py | 49 + .../test/lang/cpp/rvalue-references/main.cpp | 12 + .../lldbsuite/test/lang/cpp/scope/Makefile | 5 + .../test/lang/cpp/scope/TestCppScope.py | 67 + .../lldbsuite/test/lang/cpp/scope/main.cpp | 25 + .../test/lang/cpp/signed_types/Makefile | 5 + .../lang/cpp/signed_types/TestSignedTypes.py | 60 + .../test/lang/cpp/signed_types/main.cpp | 33 + .../test/lang/cpp/static_members/Makefile | 5 + .../static_members/TestCPPStaticMembers.py | 59 + .../test/lang/cpp/static_members/main.cpp | 36 + .../test/lang/cpp/static_methods/Makefile | 5 + .../static_methods/TestCPPStaticMethods.py | 36 + .../test/lang/cpp/static_methods/main.cpp | 38 + .../lldbsuite/test/lang/cpp/stl/Makefile | 15 + .../lldbsuite/test/lang/cpp/stl/TestSTL.py | 118 + .../lang/cpp/stl/TestStdCXXDisassembly.py | 111 + .../lldbsuite/test/lang/cpp/stl/cmds.txt | 3 + .../lldbsuite/test/lang/cpp/stl/main.cpp | 30 + .../lldbsuite/test/lang/cpp/this/Makefile | 5 + .../test/lang/cpp/this/TestCPPThis.py | 53 + .../lldbsuite/test/lang/cpp/this/main.cpp | 53 + .../test/lang/cpp/unique-types/Makefile | 5 + .../lang/cpp/unique-types/TestUniqueTypes.py | 62 + .../test/lang/cpp/unique-types/main.cpp | 24 + .../test/lang/cpp/unsigned_types/Makefile | 5 + .../cpp/unsigned_types/TestUnsignedTypes.py | 54 + .../test/lang/cpp/unsigned_types/main.cpp | 22 + .../lldbsuite/test/lang/cpp/virtual/Makefile | 5 + .../test/lang/cpp/virtual/TestVirtual.py | 90 + .../lldbsuite/test/lang/cpp/virtual/main.cpp | 113 + .../test/lang/cpp/wchar_t/.categories | 1 + .../lldbsuite/test/lang/cpp/wchar_t/Makefile | 8 + .../test/lang/cpp/wchar_t/TestCxxWCharT.py | 74 + .../lldbsuite/test/lang/cpp/wchar_t/main.cpp | 35 + .../lang/go/expressions/TestExpressions.py | 113 + .../test/lang/go/expressions/main.go | 21 + .../lang/go/formatters/TestGoFormatters.py | 65 + .../lldbsuite/test/lang/go/formatters/main.go | 9 + .../test/lang/go/goroutines/TestGoroutines.py | 85 + .../lldbsuite/test/lang/go/goroutines/main.go | 89 + .../lang/go/runtime/TestGoLanguageRuntime | 80 + .../lldbsuite/test/lang/go/runtime/main.go | 38 + .../test/lang/go/types/TestGoASTContext.py | 133 + .../lldbsuite/test/lang/go/types/main.go | 47 + .../Python/lldbsuite/test/lang/mixed/Makefile | 6 + .../test/lang/mixed/TestMixedLanguages.py | 56 + .../Python/lldbsuite/test/lang/mixed/foo.cpp | 11 + .../Python/lldbsuite/test/lang/mixed/main.c | 15 + .../lldbsuite/test/lang/objc/.categories | 1 + .../lldbsuite/test/lang/objc/blocks/Makefile | 6 + .../lang/objc/blocks/TestObjCIvarsInBlocks.py | 103 + .../test/lang/objc/blocks/ivars-in-blocks.h | 11 + .../test/lang/objc/blocks/ivars-in-blocks.m | 57 + .../lldbsuite/test/lang/objc/blocks/main.m | 10 + .../test/lang/objc/forward-decl/Container.h | 13 + .../test/lang/objc/forward-decl/Container.m | 27 + .../test/lang/objc/forward-decl/Makefile | 9 + .../lang/objc/forward-decl/TestForwardDecl.py | 54 + .../test/lang/objc/forward-decl/main.m | 14 + .../test/lang/objc/foundation/Makefile | 8 + .../lang/objc/foundation/TestConstStrings.py | 53 + .../foundation/TestFoundationDisassembly.py | 134 + .../lang/objc/foundation/TestObjCMethods.py | 262 + .../lang/objc/foundation/TestObjCMethods2.py | 169 + .../foundation/TestObjectDescriptionAPI.py | 71 + .../lang/objc/foundation/TestRuntimeTypes.py | 48 + .../lang/objc/foundation/TestSymbolTable.py | 68 + .../test/lang/objc/foundation/const-strings.m | 24 + .../test/lang/objc/foundation/main.m | 141 + .../test/lang/objc/foundation/my-base.h | 8 + .../test/lang/objc/foundation/my-base.m | 10 + .../lang/objc/hidden-ivars/InternalDefiner.h | 11 + .../lang/objc/hidden-ivars/InternalDefiner.m | 31 + .../test/lang/objc/hidden-ivars/Makefile | 9 + .../lang/objc/hidden-ivars/TestHiddenIvars.py | 174 + .../test/lang/objc/hidden-ivars/main.m | 54 + .../test/lang/objc/ivar-IMP/Makefile | 12 + .../lang/objc/ivar-IMP/TestObjCiVarIMP.py | 63 + .../test/lang/objc/ivar-IMP/myclass.h | 6 + .../test/lang/objc/ivar-IMP/myclass.m | 16 + .../lldbsuite/test/lang/objc/ivar-IMP/repro.m | 7 + .../lang/objc/modules-auto-import/Makefile | 6 + .../TestModulesAutoImport.py | 53 + .../test/lang/objc/modules-auto-import/main.m | 7 + .../lang/objc/modules-incomplete/Makefile | 8 + .../TestIncompleteModules.py | 61 + .../test/lang/objc/modules-incomplete/main.m | 11 + .../lang/objc/modules-incomplete/module.map | 4 + .../lang/objc/modules-incomplete/myModule.h | 8 + .../lang/objc/modules-incomplete/myModule.m | 14 + .../objc/modules-inline-functions/Makefile | 9 + .../TestModulesInlineFunctions.py | 55 + .../lang/objc/modules-inline-functions/main.m | 9 + .../objc/modules-inline-functions/module.map | 4 + .../objc/modules-inline-functions/myModule.c | 7 + .../objc/modules-inline-functions/myModule.h | 7 + .../lldbsuite/test/lang/objc/modules/Makefile | 7 + .../test/lang/objc/modules/TestObjCModules.py | 71 + .../lldbsuite/test/lang/objc/modules/main.m | 12 + .../lldbsuite/test/lang/objc/objc++/Makefile | 6 + .../test/lang/objc/objc++/TestObjCXX.py | 33 + .../lldbsuite/test/lang/objc/objc++/main.mm | 19 + .../lang/objc/objc-baseclass-sbtype/Makefile | 6 + .../TestObjCBaseClassSBType.py | 58 + .../lang/objc/objc-baseclass-sbtype/main.m | 22 + .../lang/objc/objc-builtin-types/Makefile | 5 + .../TestObjCBuiltinTypes.py | 55 + .../lang/objc/objc-builtin-types/main.cpp | 9 + .../test/lang/objc/objc-checker/Makefile | 6 + .../objc/objc-checker/TestObjCCheckers.py | 75 + .../test/lang/objc/objc-checker/main.m | 32 + .../test/lang/objc/objc-class-method/Makefile | 6 + .../objc-class-method/TestObjCClassMethod.py | 56 + .../test/lang/objc/objc-class-method/class.m | 24 + .../lang/objc/objc-dyn-sbtype/.categories | 1 + .../test/lang/objc/objc-dyn-sbtype/Makefile | 7 + .../objc-dyn-sbtype/TestObjCDynamicSBType.py | 62 + .../test/lang/objc/objc-dyn-sbtype/main.m | 53 + .../lang/objc/objc-dynamic-value/Makefile | 6 + .../TestObjCDynamicValue.py | 177 + .../objc/objc-dynamic-value/dynamic-value.m | 147 + .../test/lang/objc/objc-ivar-offsets/Makefile | 6 + .../objc-ivar-offsets/TestObjCIvarOffsets.py | 74 + .../test/lang/objc/objc-ivar-offsets/main.m | 15 + .../objc-ivar-offsets/objc-ivar-offsets.h | 27 + .../objc-ivar-offsets/objc-ivar-offsets.m | 19 + .../objc-ivar-protocols/TestIvarProtocols.py | 4 + .../test/lang/objc/objc-ivar-protocols/main.m | 33 + .../lang/objc/objc-ivar-stripped/Makefile | 15 + .../TestObjCIvarStripped.py | 59 + .../test/lang/objc/objc-ivar-stripped/main.m | 33 + .../test/lang/objc/objc-new-syntax/Makefile | 7 + .../objc/objc-new-syntax/TestObjCNewSyntax.py | 103 + .../test/lang/objc/objc-new-syntax/main.m | 21 + .../test/lang/objc/objc-optimized/Makefile | 8 + .../objc/objc-optimized/TestObjcOptimized.py | 63 + .../test/lang/objc/objc-optimized/main.m | 44 + .../test/lang/objc/objc-property/Makefile | 6 + .../objc/objc-property/TestObjCProperty.py | 114 + .../test/lang/objc/objc-property/main.m | 100 + .../lang/objc/objc-runtime-ivars/Makefile | 6 + .../objc-runtime-ivars/TestRuntimeIvars.py | 4 + .../test/lang/objc/objc-runtime-ivars/main.m | 10 + .../objc/objc-static-method-stripped/Makefile | 15 + .../TestObjCStaticMethodStripped.py | 65 + .../objc/objc-static-method-stripped/static.m | 29 + .../lang/objc/objc-static-method/Makefile | 6 + .../TestObjCStaticMethod.py | 61 + .../lang/objc/objc-static-method/static.m | 29 + .../test/lang/objc/objc-stepping/Makefile | 6 + .../objc/objc-stepping/TestObjCStepping.py | 172 + .../lang/objc/objc-stepping/stepping-tests.m | 138 + .../lang/objc/objc-struct-argument/Makefile | 6 + .../TestObjCStructArgument.py | 57 + .../lang/objc/objc-struct-argument/test.m | 34 + .../lang/objc/objc-struct-return/Makefile | 6 + .../TestObjCStructReturn.py | 53 + .../test/lang/objc/objc-struct-return/test.m | 23 + .../test/lang/objc/objc-super/Makefile | 6 + .../lang/objc/objc-super/TestObjCSuper.py | 59 + .../test/lang/objc/objc-super/class.m | 39 + .../test/lang/objc/print-obj/Makefile | 6 + .../test/lang/objc/print-obj/TestPrintObj.py | 87 + .../test/lang/objc/print-obj/blocked.m | 73 + .../test/lang/objc/radar-9691614/Makefile | 7 + .../TestObjCMethodReturningBOOL.py | 45 + .../test/lang/objc/radar-9691614/main.m | 67 + .../test/lang/objc/rdar-10967107/Makefile | 7 + .../objc/rdar-10967107/TestRdar10967107.py | 44 + .../test/lang/objc/rdar-10967107/main.m | 13 + .../test/lang/objc/rdar-11355592/Makefile | 7 + .../objc/rdar-11355592/TestRdar11355592.py | 65 + .../test/lang/objc/rdar-11355592/main.m | 37 + .../test/lang/objc/rdar-12408181/Makefile | 11 + .../objc/rdar-12408181/TestRdar12408181.py | 48 + .../test/lang/objc/rdar-12408181/main.m | 24 + .../test/lang/objc/real-definition/Bar.h | 12 + .../test/lang/objc/real-definition/Bar.m | 43 + .../test/lang/objc/real-definition/Foo.h | 11 + .../test/lang/objc/real-definition/Foo.m | 25 + .../test/lang/objc/real-definition/Makefile | 6 + .../real-definition/TestRealDefinition.py | 86 + .../test/lang/objc/real-definition/main.m | 13 + .../lldbsuite/test/lang/objc/sample/Makefile | 6 + .../lldbsuite/test/lang/objc/sample/main.m | 70 + .../lldbsuite/test/lang/objc/self/Makefile | 6 + .../test/lang/objc/self/TestObjCSelf.py | 36 + .../lldbsuite/test/lang/objc/self/main.m | 54 + .../objcxx-ivar-vector/TestIvarVector.py | 4 + .../lang/objcxx/objcxx-ivar-vector/main.mm | 33 + .../test/lang/objcxx/sample/Makefile | 6 + .../lldbsuite/test/lang/objcxx/sample/main.mm | 71 + .../test/linux/builtin_trap/Makefile | 5 + .../linux/builtin_trap/TestBuiltinTrap.py | 51 + .../test/linux/builtin_trap/main.cpp | 17 + .../create_during_instruction_step/Makefile | 5 + .../TestCreateDuringInstructionStep.py | 66 + .../create_during_instruction_step/main.cpp | 55 + .../lldbsuite/test/lldb_pylint_helper.py | 181 + packages/Python/lldbsuite/test/lldbbench.py | 118 + packages/Python/lldbsuite/test/lldbcurses.py | 1143 + packages/Python/lldbsuite/test/lldbinline.py | 210 + packages/Python/lldbsuite/test/lldbpexpect.py | 63 + .../Python/lldbsuite/test/lldbplatformutil.py | 27 + packages/Python/lldbsuite/test/lldbtest.py | 2775 + .../Python/lldbsuite/test/lldbtest_config.py | 21 + packages/Python/lldbsuite/test/lldbutil.py | 1016 + packages/Python/lldbsuite/test/lock.py | 27 + .../Python/lldbsuite/test/logging/Makefile | 5 + .../lldbsuite/test/logging/TestLogging.py | 107 + .../Python/lldbsuite/test/logging/main.cpp | 62 + .../lldbsuite/test/macosx/add-dsym/Makefile | 11 + .../TestAddDsymMidExecutionCommand.py | 45 + .../lldbsuite/test/macosx/add-dsym/main.c | 7 + .../macosx/debug-info/apple_types/Makefile | 6 + .../apple_types/TestAppleTypesIsProduced.py | 67 + .../test/macosx/debug-info/apple_types/main.c | 27 + .../test/macosx/indirect_symbol/Makefile | 48 + .../indirect_symbol/TestIndirectSymbols.py | 88 + .../test/macosx/indirect_symbol/alias.list | 1 + .../test/macosx/indirect_symbol/indirect.c | 14 + .../test/macosx/indirect_symbol/main.c | 14 + .../test/macosx/indirect_symbol/reexport.c | 7 + .../lldbsuite/test/macosx/order/Makefile | 7 + .../test/macosx/order/TestOrderFile.py | 36 + .../lldbsuite/test/macosx/order/cmds.txt | 3 + .../Python/lldbsuite/test/macosx/order/main.c | 54 + .../lldbsuite/test/macosx/order/order-file | 4 + .../lldbsuite/test/macosx/queues/Makefile | 28 + .../test/macosx/queues/TestQueues.py | 243 + .../lldbsuite/test/macosx/queues/main.c | 133 + .../test/macosx/safe-to-func-call/Makefile | 28 + .../safe-to-func-call/TestSafeFuncCalls.py | 63 + .../test/macosx/safe-to-func-call/main.c | 30 + .../lldbsuite/test/macosx/universal/Makefile | 19 + .../test/macosx/universal/TestUniversal.py | 107 + .../lldbsuite/test/macosx/universal/main.c | 7 + .../Python/lldbsuite/test/make/Makefile.rules | 600 + .../Python/lldbsuite/test/make/test_common.h | 19 + .../lldbsuite/test/plugins/builder_base.py | 137 + .../lldbsuite/test/plugins/builder_darwin.py | 21 + .../lldbsuite/test/plugins/builder_freebsd.py | 4 + .../lldbsuite/test/plugins/builder_linux2.py | 4 + .../lldbsuite/test/plugins/builder_netbsd.py | 4 + .../lldbsuite/test/plugins/builder_win32.py | 4 + .../lldbsuite/test/python_api/.categories | 1 + .../test/python_api/breakpoint/Makefile | 5 + .../breakpoint/TestBreakpointAPI.py | 45 + .../test/python_api/breakpoint/main.c | 14 + .../test/python_api/class_members/Makefile | 5 + .../class_members/TestSBTypeClassMembers.py | 77 + .../test/python_api/class_members/main.mm | 47 + .../python_api/debugger/TestDebuggerAPI.py | 40 + .../TestDefaultConstructorForAPIObjects.py | 395 + .../default-constructor/sb_address.py | 22 + .../default-constructor/sb_block.py | 17 + .../default-constructor/sb_breakpoint.py | 36 + .../sb_breakpointlocation.py | 28 + .../default-constructor/sb_broadcaster.py | 20 + .../default-constructor/sb_communication.py | 28 + .../default-constructor/sb_compileunit.py | 15 + .../default-constructor/sb_debugger.py | 56 + .../default-constructor/sb_error.py | 25 + .../default-constructor/sb_event.py | 17 + .../default-constructor/sb_filespec.py | 14 + .../default-constructor/sb_frame.py | 37 + .../default-constructor/sb_function.py | 19 + .../default-constructor/sb_instruction.py | 16 + .../default-constructor/sb_instructionlist.py | 17 + .../default-constructor/sb_lineentry.py | 14 + .../default-constructor/sb_listener.py | 23 + .../default-constructor/sb_module.py | 29 + .../default-constructor/sb_process.py | 49 + .../default-constructor/sb_section.py | 22 + .../default-constructor/sb_stringlist.py | 17 + .../default-constructor/sb_symbol.py | 16 + .../default-constructor/sb_symbolcontext.py | 15 + .../default-constructor/sb_target.py | 65 + .../default-constructor/sb_thread.py | 37 + .../python_api/default-constructor/sb_type.py | 22 + .../default-constructor/sb_value.py | 67 + .../default-constructor/sb_valuelist.py | 14 + .../default-constructor/sb_watchpoint.py | 21 + .../TestDisassembleRawData.py | 39 + .../TestDisassemble_VST1_64.py | 58 + .../lldbsuite/test/python_api/event/Makefile | 5 + .../test/python_api/event/TestEvents.py | 288 + .../lldbsuite/test/python_api/event/main.c | 49 + .../TestExprPathSynthetic.py | 4 + .../python_api/exprpath_synthetic/main.mm | 20 + .../python_api/findvalue_duplist/Makefile | 8 + .../findvalue_duplist/TestSBFrameFindValue.py | 50 + .../python_api/findvalue_duplist/main.cpp | 7 + .../test/python_api/formatters/Makefile | 8 + .../formatters/TestFormattersSBAPI.py | 346 + .../test/python_api/formatters/main.cpp | 59 + .../test/python_api/formatters/synth.py | 105 + .../lldbsuite/test/python_api/frame/Makefile | 5 + .../test/python_api/frame/TestFrames.py | 207 + .../test/python_api/frame/inlines/Makefile | 9 + .../frame/inlines/TestInlinedFrame.py | 79 + .../test/python_api/frame/inlines/inlines.c | 53 + .../test/python_api/frame/inlines/inlines.h | 4 + .../lldbsuite/test/python_api/frame/main.c | 58 + .../test/python_api/function_symbol/Makefile | 5 + .../function_symbol/TestDisasmAPI.py | 111 + .../function_symbol/TestSymbolAPI.py | 81 + .../test/python_api/function_symbol/main.c | 60 + .../test/python_api/hello_world/Makefile | 7 + .../python_api/hello_world/TestHelloWorld.py | 147 + .../test/python_api/hello_world/main.c | 35 + .../test/python_api/interpreter/Makefile | 5 + .../interpreter/TestCommandInterpreterAPI.py | 73 + .../test/python_api/interpreter/main.c | 6 + .../test/python_api/lldbutil/frame/Makefile | 6 + .../lldbutil/frame/TestFrameUtils.py | 59 + .../test/python_api/lldbutil/frame/main.c | 47 + .../test/python_api/lldbutil/iter/Makefile | 8 + .../lldbutil/iter/TestLLDBIterator.py | 122 + .../lldbutil/iter/TestRegistersIterator.py | 94 + .../test/python_api/lldbutil/iter/main.cpp | 134 + .../test/python_api/lldbutil/process/Makefile | 8 + .../lldbutil/process/TestPrintStackTraces.py | 52 + .../test/python_api/lldbutil/process/main.cpp | 136 + .../test/python_api/module_section/Makefile | 8 + .../module_section/TestModuleAndSection.py | 127 + .../test/python_api/module_section/b.cpp | 3 + .../test/python_api/module_section/c.cpp | 3 + .../test/python_api/module_section/main.cpp | 136 + .../test/python_api/objc_type/Makefile | 9 + .../test/python_api/objc_type/TestObjCType.py | 64 + .../test/python_api/objc_type/main.m | 52 + .../test/python_api/process/Makefile | 5 + .../test/python_api/process/TestProcessAPI.py | 289 + .../test/python_api/process/io/Makefile | 6 + .../python_api/process/io/TestProcessIO.py | 206 + .../test/python_api/process/io/main.c | 19 + .../test/python_api/process/main.cpp | 31 + .../test/python_api/rdar-12481949/Makefile | 5 + .../rdar-12481949/Test-rdar-12481949.py | 54 + .../test/python_api/rdar-12481949/main.cpp | 17 + .../lldbsuite/test/python_api/sbdata/Makefile | 5 + .../test/python_api/sbdata/TestSBData.py | 348 + .../lldbsuite/test/python_api/sbdata/main.cpp | 43 + .../sbtype_typeclass/TestSBTypeTypeClass.py | 4 + .../test/python_api/sbtype_typeclass/main.m | 34 + .../python_api/sbvalue_const_addrof/Makefile | 4 + .../TestSBValueConstAddrOf.py | 3 + .../python_api/sbvalue_const_addrof/main.cpp | 40 + .../test/python_api/sbvalue_persist/Makefile | 15 + .../sbvalue_persist/TestSBValuePersist.py | 74 + .../test/python_api/sbvalue_persist/main.cpp | 14 + .../test/python_api/section/Makefile | 5 + .../test/python_api/section/TestSectionAPI.py | 41 + .../lldbsuite/test/python_api/section/main.c | 28 + .../test/python_api/signals/Makefile | 5 + .../test/python_api/signals/TestSignalsAPI.py | 47 + .../test/python_api/signals/main.cpp | 28 + .../test/python_api/symbol-context/Makefile | 5 + .../symbol-context/TestSymbolContext.py | 89 + .../test/python_api/symbol-context/main.c | 51 + .../lldbsuite/test/python_api/target/Makefile | 5 + .../test/python_api/target/TestTargetAPI.py | 372 + .../lldbsuite/test/python_api/target/main.c | 60 + .../lldbsuite/test/python_api/thread/Makefile | 5 + .../test/python_api/thread/TestThreadAPI.py | 245 + .../lldbsuite/test/python_api/thread/main.cpp | 26 + .../test/python_api/thread/main2.cpp | 54 + .../lldbsuite/test/python_api/type/Makefile | 5 + .../test/python_api/type/TestTypeList.py | 116 + .../lldbsuite/test/python_api/type/main.cpp | 60 + .../lldbsuite/test/python_api/value/Makefile | 5 + .../test/python_api/value/TestValueAPI.py | 135 + .../python_api/value/change_values/Makefile | 5 + .../value/change_values/TestChangeValueAPI.py | 148 + .../python_api/value/change_values/main.c | 29 + .../python_api/value/linked_list/Makefile | 5 + .../linked_list/TestValueAPILinkedList.py | 133 + .../python_api/value/linked_list/main.cpp | 56 + .../lldbsuite/test/python_api/value/main.c | 52 + .../test/python_api/value_var_update/Makefile | 8 + .../value_var_update/TestValueVarUpdate.py | 59 + .../test/python_api/value_var_update/main.c | 15 + .../test/python_api/watchpoint/Makefile | 5 + .../watchpoint/TestSetWatchpoint.py | 93 + .../watchpoint/TestWatchpointIgnoreCount.py | 89 + .../watchpoint/TestWatchpointIter.py | 115 + .../python_api/watchpoint/condition/Makefile | 5 + .../condition/TestWatchpointConditionAPI.py | 89 + .../python_api/watchpoint/condition/main.cpp | 28 + .../test/python_api/watchpoint/main.c | 24 + .../watchpoint/watchlocation/Makefile | 6 + .../watchlocation/TestSetWatchlocation.py | 90 + .../watchlocation/TestTargetWatchAddress.py | 130 + .../watchpoint/watchlocation/main.cpp | 104 + packages/Python/lldbsuite/test/redo.py | 195 + .../Python/lldbsuite/test/result_formatter.py | 1304 + .../Python/lldbsuite/test/settings/Makefile | 5 + .../lldbsuite/test/settings/TestSettings.py | 495 + .../Python/lldbsuite/test/settings/main.cpp | 75 + .../lldbsuite/test/settings/quoting/Makefile | 5 + .../test/settings/quoting/TestQuoting.py | 92 + .../lldbsuite/test/settings/quoting/main.c | 13 + .../lldbsuite/test/source-manager/Makefile | 5 + .../test/source-manager/TestSourceManager.py | 172 + .../test/source-manager/hidden/.keep | 0 .../lldbsuite/test/source-manager/main.c | 6 + .../test/terminal/TestSTTYBeforeAndAfter.py | 121 + .../Python/lldbsuite/test/test_categories.py | 70 + packages/Python/lldbsuite/test/test_result.py | 224 + .../lldbsuite/test/test_runner/README.txt | 5 + .../test/test_runner/lib/lldb_utils.py | 66 + .../test/test_runner/lib/process_control.py | 705 + .../test/test_runner/test/inferior.py | 146 + .../test_runner/test/process_control_tests.py | 235 + .../lldbsuite/test/tools/lldb-mi/Makefile | 5 + .../test/tools/lldb-mi/TestMiExit.py | 84 + .../test/tools/lldb-mi/TestMiFile.py | 77 + .../test/tools/lldb-mi/TestMiGdbSetShow.py | 190 + .../test/tools/lldb-mi/TestMiLibraryLoaded.py | 33 + .../test/tools/lldb-mi/TestMiPrompt.py | 54 + .../test/tools/lldb-mi/breakpoint/Makefile | 5 + .../tools/lldb-mi/breakpoint/TestMiBreak.py | 246 + .../test/tools/lldb-mi/breakpoint/main.cpp | 27 + .../test/tools/lldb-mi/control/Makefile | 5 + .../test/tools/lldb-mi/control/TestMiExec.py | 457 + .../test/tools/lldb-mi/control/main.cpp | 33 + .../test/tools/lldb-mi/data/Makefile | 5 + .../test/tools/lldb-mi/data/TestMiData.py | 337 + .../test/tools/lldb-mi/data/main.cpp | 59 + .../test/tools/lldb-mi/interpreter/Makefile | 5 + .../lldb-mi/interpreter/TestMiCliSupport.py | 206 + .../interpreter/TestMiInterpreterExec.py | 229 + .../test/tools/lldb-mi/interpreter/main.cpp | 19 + .../test/tools/lldb-mi/lldbmi_testcase.py | 54 + .../lldbsuite/test/tools/lldb-mi/main.cpp | 19 + .../test/tools/lldb-mi/signal/Makefile | 5 + .../test/tools/lldb-mi/signal/TestMiSignal.py | 197 + .../test/tools/lldb-mi/signal/main.cpp | 33 + .../test/tools/lldb-mi/stack/Makefile | 5 + .../test/tools/lldb-mi/stack/TestMiStack.py | 479 + .../test/tools/lldb-mi/stack/main.cpp | 127 + .../tools/lldb-mi/startup_options/Makefile | 5 + .../startup_options/TestMiStartupOptions.py | 290 + .../tools/lldb-mi/startup_options/main.cpp | 15 + .../lldb-mi/startup_options/start_script | 5 + .../startup_options/start_script_error | 2 + .../lldb-mi/startup_options/start_script_exit | 7 + .../test/tools/lldb-mi/symbol/Makefile | 5 + .../test/tools/lldb-mi/symbol/TestMiSymbol.py | 81 + .../test/tools/lldb-mi/symbol/main.cpp | 18 + .../symbol/symbol_list_lines_inline_test.cpp | 39 + .../symbol/symbol_list_lines_inline_test.h | 24 + .../symbol/symbol_list_lines_inline_test2.cpp | 38 + .../test/tools/lldb-mi/syntax/Makefile | 5 + .../test/tools/lldb-mi/syntax/TestMiSyntax.py | 80 + .../test/tools/lldb-mi/syntax/main.cpp | 17 + .../test/tools/lldb-mi/target/Makefile | 5 + .../test/tools/lldb-mi/target/TestMiTarget.py | 125 + .../test/tools/lldb-mi/target/test_attach.cpp | 21 + .../test/tools/lldb-mi/variable/Makefile | 5 + .../lldb-mi/variable/TestMiGdbSetShowPrint.py | 227 + .../test/tools/lldb-mi/variable/TestMiVar.py | 396 + .../test/tools/lldb-mi/variable/main.cpp | 152 + .../lldbsuite/test/tools/lldb-server/Makefile | 8 + .../lldb-server/TestGDBRemoteMemoryRead.py | 41 + .../tools/lldb-server/TestGdbRemoteAttach.py | 60 + .../lldb-server/TestGdbRemoteAuxvSupport.py | 201 + .../TestGdbRemoteExpeditedRegisters.py | 144 + .../tools/lldb-server/TestGdbRemoteKill.py | 53 + .../lldb-server/TestGdbRemoteProcessInfo.py | 179 + .../lldb-server/TestGdbRemoteRegisterState.py | 126 + .../lldb-server/TestGdbRemoteSingleStep.py | 25 + .../TestGdbRemoteThreadsInStopReply.py | 164 + .../TestGdbRemote_qThreadStopInfo.py | 150 + .../tools/lldb-server/TestGdbRemote_vCont.py | 116 + .../tools/lldb-server/TestLldbGdbServer.py | 1473 + .../commandline/TestStubReverseConnect.py | 87 + .../lldb-server/commandline/TestStubSetSID.py | 82 + .../tools/lldb-server/gdbremote_testcase.py | 1299 + .../tools/lldb-server/inferior-crash/Makefile | 8 + .../inferior-crash/TestGdbRemoteAbort.py | 42 + .../inferior-crash/TestGdbRemoteSegFault.py | 40 + .../tools/lldb-server/inferior-crash/main.cpp | 39 + .../tools/lldb-server/lldbgdbserverutils.py | 843 + .../lldbsuite/test/tools/lldb-server/main.cpp | 404 + .../platform-process-connect/Makefile | 5 + .../TestPlatformProcessConnect.py | 56 + .../platform-process-connect/main.cpp | 7 + .../tools/lldb-server/socket_packet_pump.py | 187 + .../test/test_lldbgdbserverutils.py | 49 + .../lldbsuite/test/types/AbstractBase.py | 259 + .../lldbsuite/test/types/HideTestFailures.py | 78 + packages/Python/lldbsuite/test/types/Makefile | 7 + .../lldbsuite/test/types/TestFloatTypes.py | 41 + .../test/types/TestFloatTypesExpr.py | 44 + .../lldbsuite/test/types/TestIntegerTypes.py | 113 + .../test/types/TestIntegerTypesExpr.py | 113 + .../test/types/TestRecursiveTypes.py | 51 + .../lldbsuite/test/types/basic_type.cpp | 211 + packages/Python/lldbsuite/test/types/char.cpp | 9 + .../Python/lldbsuite/test/types/double.cpp | 9 + .../Python/lldbsuite/test/types/float.cpp | 9 + packages/Python/lldbsuite/test/types/int.cpp | 9 + packages/Python/lldbsuite/test/types/long.cpp | 18 + .../Python/lldbsuite/test/types/long_long.cpp | 18 + .../lldbsuite/test/types/recursive_type_1.cpp | 12 + .../lldbsuite/test/types/recursive_type_2.cpp | 10 + .../test/types/recursive_type_main.cpp | 8 + .../Python/lldbsuite/test/types/short.cpp | 9 + .../lldbsuite/test/types/unsigned_char.cpp | 9 + .../lldbsuite/test/types/unsigned_int.cpp | 9 + .../lldbsuite/test/types/unsigned_long.cpp | 18 + .../test/types/unsigned_long_long.cpp | 18 + .../lldbsuite/test/types/unsigned_short.cpp | 9 + .../lldbsuite/test/warnings/uuid/Makefile | 5 + .../test/warnings/uuid/TestAddDsymCommand.py | 107 + .../test/warnings/uuid/main.cpp.template | 19 + .../Python/lldbsuite/test/xunit_formatter.py | 565 + resources/LLDB-Info.plist | 24 + scripts/CMakeLists.txt | 37 + scripts/Makefile | 17 + scripts/Python/Makefile | 15 + scripts/Python/android/host_art_bt.py | 192 + scripts/Python/finish-swig-Python-LLDB.sh | 310 + scripts/Python/finishSwigPythonLLDB.py | 793 + scripts/Python/modify-python-lldb.py | 486 + scripts/Python/modules/CMakeLists.txt | 11 + scripts/Python/modules/Makefile | 20 + .../Python/modules/readline/CMakeLists.txt | 25 + scripts/Python/modules/readline/Makefile | 100 + scripts/Python/modules/readline/readline.cpp | 76 + scripts/Python/prepare_binding_Python.py | 435 + scripts/Python/python-extensions.swig | 1087 + scripts/Python/python-swigsafecast.swig | 142 + scripts/Python/python-typemaps.swig | 601 + scripts/Python/python-wrapper.swig | 936 + scripts/Python/remote-build.py | 300 + scripts/Python/use_lldb_suite.py | 22 + scripts/build-lldb-llvm-clang | 74 + scripts/build-llvm.pl | 407 + scripts/buildbot.py | 152 + scripts/checkpoint-llvm.pl | 126 + scripts/disasm-gdb-remote.pl | 2283 + scripts/finish-swig-wrapper-classes.sh | 101 + scripts/finishSwigWrapperClasses.py | 368 + scripts/generate-vers.pl | 56 + scripts/get_relative_lib_dir.py | 44 + scripts/install-lldb.sh | 59 + scripts/install_custom_python.py | 134 + scripts/interface/SBAddress.i | 201 + scripts/interface/SBAttachInfo.i | 116 + scripts/interface/SBBlock.i | 179 + scripts/interface/SBBreakpoint.i | 266 + scripts/interface/SBBreakpointLocation.i | 130 + scripts/interface/SBBroadcaster.i | 68 + scripts/interface/SBCommandInterpreter.i | 218 + scripts/interface/SBCommandReturnObject.i | 107 + scripts/interface/SBCommunication.i | 82 + scripts/interface/SBCompileUnit.i | 130 + scripts/interface/SBData.i | 340 + scripts/interface/SBDebugger.i | 388 + scripts/interface/SBDeclaration.i | 68 + scripts/interface/SBError.i | 128 + scripts/interface/SBEvent.i | 153 + scripts/interface/SBExecutionContext.i | 57 + scripts/interface/SBExpressionOptions.i | 137 + scripts/interface/SBFileSpec.i | 103 + scripts/interface/SBFileSpecList.i | 45 + scripts/interface/SBFrame.i | 407 + scripts/interface/SBFunction.i | 145 + scripts/interface/SBHostOS.i | 47 + scripts/interface/SBInstruction.i | 103 + scripts/interface/SBInstructionList.i | 91 + scripts/interface/SBLanguageRuntime.i | 22 + scripts/interface/SBLaunchInfo.i | 132 + scripts/interface/SBLineEntry.i | 106 + scripts/interface/SBListener.i | 99 + scripts/interface/SBModule.i | 530 + scripts/interface/SBModuleSpec.i | 133 + scripts/interface/SBPlatform.i | 196 + scripts/interface/SBProcess.i | 502 + scripts/interface/SBQueue.i | 75 + scripts/interface/SBQueueItem.i | 46 + scripts/interface/SBSection.i | 154 + scripts/interface/SBSourceManager.i | 54 + scripts/interface/SBStream.i | 100 + scripts/interface/SBStringList.i | 44 + scripts/interface/SBSymbol.i | 110 + scripts/interface/SBSymbolContext.i | 112 + scripts/interface/SBSymbolContextList.i | 140 + scripts/interface/SBTarget.i | 899 + scripts/interface/SBThread.i | 408 + scripts/interface/SBThreadCollection.i | 38 + scripts/interface/SBThreadPlan.i | 123 + scripts/interface/SBType.i | 500 + scripts/interface/SBTypeCategory.i | 246 + scripts/interface/SBTypeEnumMember.i | 108 + scripts/interface/SBTypeFilter.i | 75 + scripts/interface/SBTypeFormat.i | 78 + scripts/interface/SBTypeNameSpecifier.i | 68 + scripts/interface/SBTypeSummary.i | 123 + scripts/interface/SBTypeSynthetic.i | 80 + scripts/interface/SBUnixSignals.i | 74 + scripts/interface/SBValue.i | 540 + scripts/interface/SBValueList.i | 142 + scripts/interface/SBVariablesOptions.i | 61 + scripts/interface/SBWatchpoint.i | 99 + scripts/lldb.swig | 199 + scripts/package-clang-headers.py | 80 + scripts/prepare_bindings.py | 213 + scripts/sed-sources | 251 + scripts/shush | 64 + scripts/swig_bot.py | 81 + scripts/swig_bot_lib/__init__.py | 0 scripts/swig_bot_lib/client.py | 205 + scripts/swig_bot_lib/local.py | 131 + scripts/swig_bot_lib/remote.py | 39 + scripts/swig_bot_lib/server.py | 138 + scripts/use_lldb_suite.py | 22 + scripts/utilsArgsParse.py | 139 + scripts/utilsDebug.py | 120 + scripts/utilsOsType.py | 90 + scripts/verify_api.py | 84 + source/API/CMakeLists.txt | 119 + source/API/Makefile | 18 + source/API/SBProcess.cpp | 9 +- source/API/SystemInitializerFull.cpp | 6 +- source/Breakpoint/CMakeLists.txt | 23 + source/Breakpoint/Makefile | 14 + source/CMakeLists.txt | 92 + source/Commands/CMakeLists.txt | 32 + source/Commands/CommandObjectSource.cpp | 673 +- source/Commands/CommandObjectTarget.cpp | 3 +- source/Commands/CommandObjectType.cpp | 5 - source/Commands/Makefile | 16 + source/Core/CMakeLists.txt | 76 + source/Core/Makefile | 14 + source/Core/StringList.cpp | 37 + source/DataFormatters/CMakeLists.txt | 19 + source/DataFormatters/Makefile | 14 + source/Expression/CMakeLists.txt | 16 + source/Expression/Makefile | 14 + source/Host/CMakeLists.txt | 190 + source/Host/Makefile | 65 + source/Host/android/HostInfoAndroid.cpp | 104 + source/Host/android/LibcGlue.cpp | 40 + .../Host/android/ProcessLauncherAndroid.cpp | 108 + source/Host/linux/AbstractSocket.cpp | 31 + source/Host/linux/Host.cpp | 393 + source/Host/linux/HostInfoLinux.cpp | 282 + source/Host/linux/HostThreadLinux.cpp | 52 + source/Host/linux/LibcGlue.cpp | 30 + source/Host/linux/ThisThread.cpp | 29 + source/Host/macosx/Host.mm | 1587 + source/Host/macosx/HostInfoMacOSX.mm | 372 + source/Host/macosx/HostThreadMacOSX.mm | 102 + source/Host/macosx/Symbols.cpp | 559 + source/Host/macosx/ThisThread.cpp | 39 + source/Host/macosx/cfcpp/CFCBundle.cpp | 99 + source/Host/macosx/cfcpp/CFCBundle.h | 50 + source/Host/macosx/cfcpp/CFCData.cpp | 82 + source/Host/macosx/cfcpp/CFCData.h | 35 + source/Host/macosx/cfcpp/CFCMutableArray.cpp | 166 + source/Host/macosx/cfcpp/CFCMutableArray.h | 39 + .../macosx/cfcpp/CFCMutableDictionary.cpp | 529 + .../Host/macosx/cfcpp/CFCMutableDictionary.h | 79 + source/Host/macosx/cfcpp/CFCMutableSet.cpp | 114 + source/Host/macosx/cfcpp/CFCMutableSet.h | 53 + source/Host/macosx/cfcpp/CFCReleaser.h | 158 + source/Host/macosx/cfcpp/CFCString.cpp | 195 + source/Host/macosx/cfcpp/CFCString.h | 41 + source/Host/macosx/cfcpp/CoreFoundationCPP.h | 30 + source/Host/netbsd/Makefile | 14 + source/Host/windows/Condition.cpp | 98 + .../windows/ConnectionGenericFileWindows.cpp | 354 + source/Host/windows/EditLineWin.cpp | 435 + source/Host/windows/FileSystem.cpp | 221 + source/Host/windows/Host.cpp | 326 + source/Host/windows/HostInfoWindows.cpp | 118 + source/Host/windows/HostProcessWindows.cpp | 137 + source/Host/windows/HostThreadWindows.cpp | 98 + source/Host/windows/LockFileWindows.cpp | 93 + source/Host/windows/Mutex.cpp | 109 + source/Host/windows/PipeWindows.cpp | 336 + .../Host/windows/ProcessLauncherWindows.cpp | 105 + source/Host/windows/ProcessRunLock.cpp | 106 + source/Host/windows/ThisThread.cpp | 66 + source/Host/windows/Windows.cpp | 231 + source/Initialization/CMakeLists.txt | 5 + source/Initialization/Makefile | 14 + source/Interpreter/CMakeLists.txt | 45 + source/Interpreter/CommandHistory.cpp | 4 +- source/Interpreter/Makefile | 51 + source/Makefile | 37 + source/Plugins/ABI/CMakeLists.txt | 12 + source/Plugins/ABI/MacOSX-arm/CMakeLists.txt | 3 + source/Plugins/ABI/MacOSX-arm/Makefile | 14 + .../Plugins/ABI/MacOSX-arm64/CMakeLists.txt | 3 + source/Plugins/ABI/MacOSX-arm64/Makefile | 14 + source/Plugins/ABI/MacOSX-i386/CMakeLists.txt | 3 + source/Plugins/ABI/MacOSX-i386/Makefile | 14 + source/Plugins/ABI/SysV-arm/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-arm/Makefile | 14 + source/Plugins/ABI/SysV-arm64/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-arm64/Makefile | 14 + .../Plugins/ABI/SysV-hexagon/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-hexagon/Makefile | 14 + source/Plugins/ABI/SysV-i386/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-i386/Makefile | 14 + source/Plugins/ABI/SysV-mips/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-mips/Makefile | 14 + source/Plugins/ABI/SysV-mips64/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-mips64/Makefile | 14 + source/Plugins/ABI/SysV-ppc/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-ppc/Makefile | 14 + source/Plugins/ABI/SysV-ppc64/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-ppc64/Makefile | 14 + source/Plugins/ABI/SysV-x86_64/CMakeLists.txt | 3 + source/Plugins/ABI/SysV-x86_64/Makefile | 14 + source/Plugins/CMakeLists.txt | 20 + source/Plugins/Disassembler/CMakeLists.txt | 1 + .../Plugins/Disassembler/llvm/CMakeLists.txt | 3 + source/Plugins/Disassembler/llvm/Makefile | 14 + source/Plugins/DynamicLoader/CMakeLists.txt | 9 + .../Darwin-Kernel/CMakeLists.txt | 3 + .../DynamicLoaderDarwinKernel.cpp | 1698 + .../Darwin-Kernel/DynamicLoaderDarwinKernel.h | 371 + .../DynamicLoader/Darwin-Kernel/Makefile | 14 + .../DynamicLoader/Hexagon-DYLD/CMakeLists.txt | 4 + .../DynamicLoader/Hexagon-DYLD/Makefile | 14 + .../DynamicLoader/MacOSX-DYLD/CMakeLists.txt | 3 + .../MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp | 2075 + .../MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h | 385 + .../DynamicLoader/MacOSX-DYLD/Makefile | 14 + .../DynamicLoader/POSIX-DYLD/CMakeLists.txt | 5 + .../Plugins/DynamicLoader/POSIX-DYLD/Makefile | 14 + .../DynamicLoader/Static/CMakeLists.txt | 3 + source/Plugins/DynamicLoader/Static/Makefile | 14 + .../DynamicLoader/Windows-DYLD/CMakeLists.txt | 3 + .../DynamicLoader/Windows-DYLD/Makefile | 14 + .../Plugins/ExpressionParser/CMakeLists.txt | 2 + .../ExpressionParser/Clang/CMakeLists.txt | 15 + .../Plugins/ExpressionParser/Clang/Makefile | 14 + .../ExpressionParser/Go/CMakeLists.txt | 5 + source/Plugins/ExpressionParser/Go/Makefile | 14 + source/Plugins/Instruction/ARM/CMakeLists.txt | 4 + source/Plugins/Instruction/ARM/Makefile | 14 + .../Plugins/Instruction/ARM64/CMakeLists.txt | 3 + source/Plugins/Instruction/ARM64/Makefile | 14 + source/Plugins/Instruction/CMakeLists.txt | 4 + .../Plugins/Instruction/MIPS/CMakeLists.txt | 3 + .../MIPS/EmulateInstructionMIPS.cpp | 2318 +- .../Instruction/MIPS/EmulateInstructionMIPS.h | 136 +- source/Plugins/Instruction/MIPS/Makefile | 14 + .../Plugins/Instruction/MIPS64/CMakeLists.txt | 3 + source/Plugins/Instruction/MIPS64/Makefile | 14 + .../AddressSanitizer/CMakeLists.txt | 3 + .../AddressSanitizer/Makefile | 14 + .../InstrumentationRuntime/CMakeLists.txt | 1 + source/Plugins/JITLoader/CMakeLists.txt | 1 + source/Plugins/JITLoader/GDB/CMakeLists.txt | 6 + source/Plugins/JITLoader/GDB/Makefile | 14 + source/Plugins/Language/CMakeLists.txt | 4 + .../Plugins/Language/CPlusPlus/CMakeLists.txt | 11 + source/Plugins/Language/CPlusPlus/Makefile | 14 + source/Plugins/Language/Go/CMakeLists.txt | 4 + source/Plugins/Language/Go/Makefile | 14 + source/Plugins/Language/ObjC/CMakeLists.txt | 13 + source/Plugins/Language/ObjC/Makefile | 14 + .../Language/ObjCPlusPlus/CMakeLists.txt | 3 + source/Plugins/Language/ObjCPlusPlus/Makefile | 14 + source/Plugins/LanguageRuntime/CMakeLists.txt | 4 + .../LanguageRuntime/CPlusPlus/CMakeLists.txt | 2 + .../CPlusPlus/ItaniumABI/CMakeLists.txt | 3 + .../CPlusPlus/ItaniumABI/Makefile | 14 + .../Plugins/LanguageRuntime/Go/CMakeLists.txt | 5 + source/Plugins/LanguageRuntime/Go/Makefile | 14 + .../ObjC/AppleObjCRuntime/CMakeLists.txt | 10 + .../ObjC/AppleObjCRuntime/Makefile | 14 + .../LanguageRuntime/ObjC/CMakeLists.txt | 1 + .../RenderScript/CMakeLists.txt | 1 + .../RenderScriptRuntime/CMakeLists.txt | 3 + .../RenderScript/RenderScriptRuntime/Makefile | 14 + .../RenderScriptRuntime.cpp | 87 +- source/Plugins/Makefile | 67 + source/Plugins/MemoryHistory/CMakeLists.txt | 1 + .../Plugins/MemoryHistory/asan/CMakeLists.txt | 3 + source/Plugins/MemoryHistory/asan/Makefile | 14 + .../BSD-Archive/CMakeLists.txt | 3 + .../ObjectContainer/BSD-Archive/Makefile | 14 + source/Plugins/ObjectContainer/CMakeLists.txt | 2 + .../Universal-Mach-O/CMakeLists.txt | 3 + .../ObjectContainer/Universal-Mach-O/Makefile | 14 + .../ObjectContainerUniversalMachO.cpp | 310 + .../ObjectContainerUniversalMachO.h | 105 + source/Plugins/ObjectFile/CMakeLists.txt | 4 + source/Plugins/ObjectFile/ELF/CMakeLists.txt | 4 + source/Plugins/ObjectFile/ELF/Makefile | 14 + .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 96 +- source/Plugins/ObjectFile/JIT/CMakeLists.txt | 3 + source/Plugins/ObjectFile/JIT/Makefile | 14 + .../Plugins/ObjectFile/Mach-O/CMakeLists.txt | 3 + source/Plugins/ObjectFile/Mach-O/Makefile | 14 + .../ObjectFile/Mach-O/ObjectFileMachO.cpp | 6096 ++ .../ObjectFile/Mach-O/ObjectFileMachO.h | 252 + .../Plugins/ObjectFile/PECOFF/CMakeLists.txt | 4 + source/Plugins/ObjectFile/PECOFF/Makefile | 14 + .../ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 1056 + .../ObjectFile/PECOFF/ObjectFilePECOFF.h | 303 + .../ObjectFile/PECOFF/WindowsMiniDump.cpp | 56 + .../ObjectFile/PECOFF/WindowsMiniDump.h | 24 + source/Plugins/OperatingSystem/CMakeLists.txt | 2 + .../Plugins/OperatingSystem/Go/CMakeLists.txt | 3 + source/Plugins/OperatingSystem/Go/Makefile | 14 + .../OperatingSystem/Python/CMakeLists.txt | 3 + .../Plugins/OperatingSystem/Python/Makefile | 14 + source/Plugins/Platform/Android/AdbClient.cpp | 569 + source/Plugins/Platform/Android/AdbClient.h | 132 + .../Plugins/Platform/Android/CMakeLists.txt | 5 + source/Plugins/Platform/Android/Makefile | 14 + .../Platform/Android/PlatformAndroid.cpp | 389 + .../Platform/Android/PlatformAndroid.h | 114 + .../PlatformAndroidRemoteGDBServer.cpp | 275 + .../Android/PlatformAndroidRemoteGDBServer.h | 79 + source/Plugins/Platform/CMakeLists.txt | 16 + .../Plugins/Platform/FreeBSD/CMakeLists.txt | 3 + source/Plugins/Platform/FreeBSD/Makefile | 14 + .../Plugins/Platform/Kalimba/CMakeLists.txt | 3 + source/Plugins/Platform/Kalimba/Makefile | 14 + .../Platform/Kalimba/PlatformKalimba.cpp | 324 + .../Platform/Kalimba/PlatformKalimba.h | 99 + source/Plugins/Platform/Linux/CMakeLists.txt | 3 + source/Plugins/Platform/Linux/Makefile | 14 + .../Plugins/Platform/Linux/PlatformLinux.cpp | 868 + source/Plugins/Platform/Linux/PlatformLinux.h | 122 + source/Plugins/Platform/MacOSX/CMakeLists.txt | 27 + source/Plugins/Platform/MacOSX/Makefile | 15 + .../MacOSX/PlatformAppleSimulator.cpp | 303 + .../Platform/MacOSX/PlatformAppleSimulator.h | 81 + .../MacOSX/PlatformAppleTVSimulator.cpp | 466 + .../MacOSX/PlatformAppleTVSimulator.h | 121 + .../MacOSX/PlatformAppleWatchSimulator.cpp | 466 + .../MacOSX/PlatformAppleWatchSimulator.h | 120 + .../Platform/MacOSX/PlatformDarwin.cpp | 1705 + .../Plugins/Platform/MacOSX/PlatformDarwin.h | 156 + .../Platform/MacOSX/PlatformDarwinKernel.cpp | 979 + .../Platform/MacOSX/PlatformDarwinKernel.h | 228 + .../Platform/MacOSX/PlatformMacOSX.cpp | 386 + .../Plugins/Platform/MacOSX/PlatformMacOSX.h | 104 + .../Platform/MacOSX/PlatformRemoteAppleTV.cpp | 912 + .../Platform/MacOSX/PlatformRemoteAppleTV.h | 171 + .../MacOSX/PlatformRemoteAppleWatch.cpp | 944 + .../MacOSX/PlatformRemoteAppleWatch.h | 173 + .../Platform/MacOSX/PlatformRemoteiOS.cpp | 974 + .../Platform/MacOSX/PlatformRemoteiOS.h | 173 + .../Platform/MacOSX/PlatformiOSSimulator.cpp | 502 + .../Platform/MacOSX/PlatformiOSSimulator.h | 116 + ...PlatformiOSSimulatorCoreSimulatorSupport.h | 315 + ...latformiOSSimulatorCoreSimulatorSupport.mm | 773 + source/Plugins/Platform/Makefile | 36 + source/Plugins/Platform/NetBSD/CMakeLists.txt | 3 + source/Plugins/Platform/NetBSD/Makefile | 14 + source/Plugins/Platform/POSIX/CMakeLists.txt | 3 + source/Plugins/Platform/POSIX/Makefile | 14 + .../Plugins/Platform/Windows/CMakeLists.txt | 3 + source/Plugins/Platform/Windows/Makefile | 14 + .../Platform/Windows/PlatformWindows.cpp | 753 + .../Platform/Windows/PlatformWindows.h | 169 + .../Platform/gdb-server/CMakeLists.txt | 3 + source/Plugins/Platform/gdb-server/Makefile | 14 + source/Plugins/Process/CMakeLists.txt | 19 + source/Plugins/Process/FreeBSD/CMakeLists.txt | 16 + source/Plugins/Process/FreeBSD/Makefile | 17 + .../Plugins/Process/FreeBSD/ProcessFreeBSD.h | 4 +- .../Process/FreeBSD/ProcessMonitor.cpp | 4 +- ...gisterContextPOSIXProcessMonitor_arm64.cpp | 4 +- source/Plugins/Process/Linux/CMakeLists.txt | 14 + source/Plugins/Process/Linux/Makefile | 17 + .../Process/Linux/NativeProcessLinux.cpp | 3189 + .../Process/Linux/NativeProcessLinux.h | 329 + .../Linux/NativeRegisterContextLinux.cpp | 230 + .../Linux/NativeRegisterContextLinux.h | 107 + .../Linux/NativeRegisterContextLinux_arm.cpp | 1017 + .../Linux/NativeRegisterContextLinux_arm.h | 185 + .../NativeRegisterContextLinux_arm64.cpp | 1042 + .../Linux/NativeRegisterContextLinux_arm64.h | 196 + .../NativeRegisterContextLinux_mips64.cpp | 1431 + .../Linux/NativeRegisterContextLinux_mips64.h | 167 + .../NativeRegisterContextLinux_x86_64.cpp | 1239 + .../Linux/NativeRegisterContextLinux_x86_64.h | 171 + .../Process/Linux/NativeThreadLinux.cpp | 440 + .../Plugins/Process/Linux/NativeThreadLinux.h | 120 + .../Plugins/Process/Linux/ProcFileReader.cpp | 108 + source/Plugins/Process/Linux/ProcFileReader.h | 37 + source/Plugins/Process/Linux/Procfs.h | 31 + .../Process/MacOSX-Kernel/CMakeLists.txt | 10 + .../MacOSX-Kernel/CommunicationKDP.cpp | 1445 + .../Process/MacOSX-Kernel/CommunicationKDP.h | 347 + source/Plugins/Process/MacOSX-Kernel/Makefile | 14 + .../Process/MacOSX-Kernel/ProcessKDP.cpp | 1211 + .../Process/MacOSX-Kernel/ProcessKDP.h | 274 + .../Process/MacOSX-Kernel/ProcessKDPLog.cpp | 186 + .../Process/MacOSX-Kernel/ProcessKDPLog.h | 54 + .../MacOSX-Kernel/RegisterContextKDP_arm.cpp | 161 + .../MacOSX-Kernel/RegisterContextKDP_arm.h | 61 + .../RegisterContextKDP_arm64.cpp | 161 + .../MacOSX-Kernel/RegisterContextKDP_arm64.h | 61 + .../MacOSX-Kernel/RegisterContextKDP_i386.cpp | 129 + .../MacOSX-Kernel/RegisterContextKDP_i386.h | 53 + .../RegisterContextKDP_x86_64.cpp | 127 + .../MacOSX-Kernel/RegisterContextKDP_x86_64.h | 54 + .../Process/MacOSX-Kernel/ThreadKDP.cpp | 211 + .../Plugins/Process/MacOSX-Kernel/ThreadKDP.h | 98 + source/Plugins/Process/POSIX/CMakeLists.txt | 8 + source/Plugins/Process/POSIX/Makefile | 32 + source/Plugins/Process/Utility/CMakeLists.txt | 47 + source/Plugins/Process/Utility/Makefile | 14 + .../Process/Windows/Common/CMakeLists.txt | 23 + .../Process/Windows/Common/ExceptionRecord.h | 99 + .../Process/Windows/Common/ProcessWindows.cpp | 100 + .../Process/Windows/Common/ProcessWindows.h | 52 + .../Windows/Common/ProcessWindowsLog.cpp | 194 + .../Windows/Common/ProcessWindowsLog.h | 96 + .../Windows/Common/RegisterContextWindows.cpp | 155 + .../Windows/Common/RegisterContextWindows.h | 67 + .../Windows/Common/TargetThreadWindows.cpp | 98 + .../Windows/Common/TargetThreadWindows.h | 50 + .../Common/x64/RegisterContextWindows_x64.cpp | 323 + .../Common/x64/RegisterContextWindows_x64.h | 48 + .../Common/x86/RegisterContextWindows_x86.cpp | 178 + .../Common/x86/RegisterContextWindows_x86.h | 48 + .../Process/Windows/Live/CMakeLists.txt | 24 + .../Process/Windows/Live/DebuggerThread.cpp | 546 + .../Process/Windows/Live/DebuggerThread.h | 100 + .../Process/Windows/Live/ForwardDecl.h | 41 + .../Process/Windows/Live/IDebugDelegate.h | 46 + .../Windows/Live/LocalDebugDelegate.cpp | 91 + .../Process/Windows/Live/LocalDebugDelegate.h | 68 + .../Windows/Live/ProcessWindowsLive.cpp | 989 + .../Process/Windows/Live/ProcessWindowsLive.h | 125 + .../Windows/Live/TargetThreadWindowsLive.cpp | 147 + .../Windows/Live/TargetThreadWindowsLive.h | 55 + .../x64/RegisterContextWindowsLive_x64.cpp | 171 + .../Live/x64/RegisterContextWindowsLive_x64.h | 40 + .../x86/RegisterContextWindowsLive_x86.cpp | 100 + .../Live/x86/RegisterContextWindowsLive_x86.h | 36 + .../Process/Windows/MiniDump/CMakeLists.txt | 21 + .../Windows/MiniDump/ProcessWinMiniDump.cpp | 550 + .../Windows/MiniDump/ProcessWinMiniDump.h | 140 + .../Windows/MiniDump/ThreadWinMiniDump.cpp | 104 + .../Windows/MiniDump/ThreadWinMiniDump.h | 49 + .../RegisterContextWindowsMiniDump_x64.cpp | 47 + .../x64/RegisterContextWindowsMiniDump_x64.h | 36 + .../RegisterContextWindowsMiniDump_x86.cpp | 47 + .../x86/RegisterContextWindowsMiniDump_x86.h | 36 + .../Plugins/Process/elf-core/CMakeLists.txt | 11 + source/Plugins/Process/elf-core/Makefile | 14 + .../Plugins/Process/gdb-remote/CMakeLists.txt | 16 + source/Plugins/Process/gdb-remote/Makefile | 14 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 288 +- .../Plugins/Process/mach-core/CMakeLists.txt | 6 + source/Plugins/Process/mach-core/Makefile | 14 + .../Process/mach-core/ProcessMachCore.cpp | 520 + .../Process/mach-core/ProcessMachCore.h | 164 + .../Process/mach-core/ThreadMachCore.cpp | 132 + .../Process/mach-core/ThreadMachCore.h | 91 + .../Plugins/ScriptInterpreter/CMakeLists.txt | 2 + .../ScriptInterpreter/None/CMakeLists.txt | 3 + .../Plugins/ScriptInterpreter/None/Makefile | 14 + .../ScriptInterpreter/Python/CMakeLists.txt | 5 + .../Plugins/ScriptInterpreter/Python/Makefile | 14 + source/Plugins/SymbolFile/CMakeLists.txt | 2 + .../Plugins/SymbolFile/DWARF/CMakeLists.txt | 33 + source/Plugins/SymbolFile/DWARF/Makefile | 14 + source/Plugins/SymbolFile/DWARF/NameToDIE.cpp | 2 +- .../Plugins/SymbolFile/Symtab/CMakeLists.txt | 3 + source/Plugins/SymbolFile/Symtab/Makefile | 14 + source/Plugins/SymbolVendor/CMakeLists.txt | 5 + .../Plugins/SymbolVendor/ELF/CMakeLists.txt | 3 + source/Plugins/SymbolVendor/ELF/Makefile | 14 + .../SymbolVendor/MacOSX/CMakeLists.txt | 5 + source/Plugins/SymbolVendor/MacOSX/Makefile | 14 + .../MacOSX/SymbolVendorMacOSX.cpp | 251 + .../SymbolVendor/MacOSX/SymbolVendorMacOSX.h | 58 + source/Plugins/SystemRuntime/CMakeLists.txt | 1 + .../MacOSX/AppleGetItemInfoHandler.cpp | 383 + .../MacOSX/AppleGetItemInfoHandler.h | 117 + .../MacOSX/AppleGetPendingItemsHandler.cpp | 383 + .../MacOSX/AppleGetPendingItemsHandler.h | 119 + .../MacOSX/AppleGetQueuesHandler.cpp | 382 + .../MacOSX/AppleGetQueuesHandler.h | 116 + .../MacOSX/AppleGetThreadItemInfoHandler.cpp | 385 + .../MacOSX/AppleGetThreadItemInfoHandler.h | 113 + .../SystemRuntime/MacOSX/CMakeLists.txt | 7 + source/Plugins/SystemRuntime/MacOSX/Makefile | 14 + .../MacOSX/SystemRuntimeMacOSX.cpp | 1010 + .../MacOSX/SystemRuntimeMacOSX.h | 336 + source/Plugins/UnwindAssembly/CMakeLists.txt | 2 + .../InstEmulation/CMakeLists.txt | 3 + .../UnwindAssembly/InstEmulation/Makefile | 14 + .../Plugins/UnwindAssembly/x86/CMakeLists.txt | 3 + source/Plugins/UnwindAssembly/x86/Makefile | 14 + source/Symbol/CMakeLists.txt | 36 + source/Symbol/Makefile | 14 + source/Target/CMakeLists.txt | 59 + source/Target/Makefile | 14 + source/Target/Process.cpp | 2 +- source/Utility/CMakeLists.txt | 20 + source/Utility/Makefile | 15 + test/CMakeLists.txt | 77 + test/dotest.py | 7 + test/testcases | 1 + test/use_lldb_suite.py | 22 + third_party/Python/module/pexpect-2.4/ANSI.py | 334 + third_party/Python/module/pexpect-2.4/FSM.py | 331 + third_party/Python/module/pexpect-2.4/INSTALL | 31 + third_party/Python/module/pexpect-2.4/LICENSE | 21 + .../Python/module/pexpect-2.4/PKG-INFO | 10 + third_party/Python/module/pexpect-2.4/README | 45 + .../Python/module/pexpect-2.4/doc/clean.css | 103 + .../Python/module/pexpect-2.4/doc/email.png | Bin 0 -> 322 bytes .../module/pexpect-2.4/doc/examples.html | 135 + .../Python/module/pexpect-2.4/doc/index.html | 868 + .../pexpect-2.4/doc/index.template.html | 868 + .../Python/module/pexpect-2.4/examples/README | 72 + .../module/pexpect-2.4/examples/astat.py | 74 + .../module/pexpect-2.4/examples/bd_client.py | 38 + .../module/pexpect-2.4/examples/bd_serv.py | 316 + .../module/pexpect-2.4/examples/cgishell.cgi | 762 + .../module/pexpect-2.4/examples/chess.py | 131 + .../module/pexpect-2.4/examples/chess2.py | 131 + .../module/pexpect-2.4/examples/chess3.py | 138 + .../Python/module/pexpect-2.4/examples/df.py | 34 + .../pexpect-2.4/examples/fix_cvs_files.py | 95 + .../Python/module/pexpect-2.4/examples/ftp.py | 47 + .../module/pexpect-2.4/examples/hive.py | 437 + .../module/pexpect-2.4/examples/monitor.py | 208 + .../module/pexpect-2.4/examples/passmass.py | 90 + .../module/pexpect-2.4/examples/python.py | 22 + .../module/pexpect-2.4/examples/rippy.py | 993 + .../module/pexpect-2.4/examples/script.py | 103 + .../pexpect-2.4/examples/ssh_session.py | 94 + .../module/pexpect-2.4/examples/ssh_tunnel.py | 72 + .../module/pexpect-2.4/examples/sshls.py | 56 + .../pexpect-2.4/examples/table_test.html | 106 + .../module/pexpect-2.4/examples/topip.py | 267 + .../module/pexpect-2.4/examples/uptime.py | 57 + .../Python/module/pexpect-2.4/fdpexpect.py | 82 + .../Python/module/pexpect-2.4/pexpect.py | 1844 + .../Python/module/pexpect-2.4/pxssh.py | 311 + .../Python/module/pexpect-2.4/screen.py | 380 + .../Python/module/pexpect-2.4/setup.py | 39 + .../Python/module/progress/progress.py | 154 + third_party/Python/module/six/LICENSE | 18 + third_party/Python/module/six/six.py | 868 + .../module/unittest2/unittest2/__init__.py | 78 + .../module/unittest2/unittest2/__main__.py | 10 + .../Python/module/unittest2/unittest2/case.py | 1119 + .../module/unittest2/unittest2/collector.py | 9 + .../unittest2/unittest2/compatibility.py | 64 + .../module/unittest2/unittest2/loader.py | 314 + .../Python/module/unittest2/unittest2/main.py | 242 + .../module/unittest2/unittest2/result.py | 195 + .../module/unittest2/unittest2/runner.py | 203 + .../module/unittest2/unittest2/signals.py | 57 + .../module/unittest2/unittest2/suite.py | 288 + .../unittest2/unittest2/test/__init__.py | 1 + .../module/unittest2/unittest2/test/dummy.py | 0 .../unittest2/unittest2/test/support.py | 177 + .../unittest2/test/test_assertions.py | 254 + .../unittest2/unittest2/test/test_break.py | 260 + .../unittest2/unittest2/test/test_case.py | 1066 + .../unittest2/test/test_discovery.py | 371 + .../unittest2/test/test_functiontestcase.py | 150 + .../unittest2/unittest2/test/test_loader.py | 1271 + .../unittest2/test/test_new_tests.py | 46 + .../unittest2/unittest2/test/test_program.py | 239 + .../unittest2/unittest2/test/test_result.py | 418 + .../unittest2/unittest2/test/test_runner.py | 129 + .../unittest2/unittest2/test/test_setups.py | 502 + .../unittest2/unittest2/test/test_skipping.py | 143 + .../unittest2/unittest2/test/test_suite.py | 341 + .../unittest2/test/test_unittest2_with.py | 143 + .../Python/module/unittest2/unittest2/util.py | 99 + tools/CMakeLists.txt | 12 + tools/Makefile | 30 + tools/argdumper/CMakeLists.txt | 8 + tools/darwin-debug/CMakeLists.txt | 6 + tools/darwin-debug/darwin-debug.cpp | 354 + tools/darwin-threads/examine-threads.c | 514 + tools/debugserver/CMakeLists.txt | 2 + tools/debugserver/Makefile | 13 + tools/debugserver/debugnub-exports | 2 + .../debugserver.xcodeproj/project.pbxproj | 1945 + .../contents.xcworkspacedata | 7 + .../xcschemes/debugserver.xcscheme | 110 + .../resources/lldb-debugserver-Info.plist | 21 + .../scripts/diagnose-termination.d | 18 + .../debugserver/source/ARM_DWARF_Registers.h | 209 + .../source/ARM_ehframe_Registers.h | 35 + tools/debugserver/source/CMakeLists.txt | 62 + tools/debugserver/source/ChangeLog | 1515 + tools/debugserver/source/DNB.cpp | 1979 + tools/debugserver/source/DNB.h | 173 + tools/debugserver/source/DNBArch.cpp | 97 + tools/debugserver/source/DNBArch.h | 129 + tools/debugserver/source/DNBBreakpoint.cpp | 225 + tools/debugserver/source/DNBBreakpoint.h | 165 + tools/debugserver/source/DNBDataRef.cpp | 386 + tools/debugserver/source/DNBDataRef.h | 125 + tools/debugserver/source/DNBDefs.h | 372 + tools/debugserver/source/DNBError.cpp | 128 + tools/debugserver/source/DNBError.h | 102 + tools/debugserver/source/DNBLog.cpp | 377 + tools/debugserver/source/DNBLog.h | 96 + tools/debugserver/source/DNBRegisterInfo.cpp | 219 + tools/debugserver/source/DNBRegisterInfo.h | 31 + tools/debugserver/source/DNBRuntimeAction.h | 25 + .../source/DNBThreadResumeActions.cpp | 116 + .../source/DNBThreadResumeActions.h | 102 + tools/debugserver/source/DNBTimer.h | 163 + tools/debugserver/source/JSONGenerator.h | 490 + tools/debugserver/source/MacOSX/CFBundle.cpp | 97 + tools/debugserver/source/MacOSX/CFBundle.h | 43 + tools/debugserver/source/MacOSX/CFData.cpp | 85 + tools/debugserver/source/MacOSX/CFData.h | 39 + tools/debugserver/source/MacOSX/CFString.cpp | 201 + tools/debugserver/source/MacOSX/CFString.h | 43 + tools/debugserver/source/MacOSX/CFUtils.h | 81 + .../debugserver/source/MacOSX/CMakeLists.txt | 89 + tools/debugserver/source/MacOSX/Genealogy.cpp | 300 + tools/debugserver/source/MacOSX/Genealogy.h | 116 + .../debugserver/source/MacOSX/GenealogySPI.h | 96 + tools/debugserver/source/MacOSX/HasAVX.h | 27 + tools/debugserver/source/MacOSX/HasAVX.s | 50 + .../source/MacOSX/MachException.cpp | 582 + .../debugserver/source/MacOSX/MachException.h | 133 + tools/debugserver/source/MacOSX/MachProcess.h | 364 + .../debugserver/source/MacOSX/MachProcess.mm | 3685 ++ tools/debugserver/source/MacOSX/MachTask.h | 134 + tools/debugserver/source/MacOSX/MachTask.mm | 1144 + .../debugserver/source/MacOSX/MachThread.cpp | 922 + tools/debugserver/source/MacOSX/MachThread.h | 159 + .../source/MacOSX/MachThreadList.cpp | 668 + .../source/MacOSX/MachThreadList.h | 87 + .../source/MacOSX/MachVMMemory.cpp | 598 + .../debugserver/source/MacOSX/MachVMMemory.h | 51 + .../source/MacOSX/MachVMRegion.cpp | 202 + .../debugserver/source/MacOSX/MachVMRegion.h | 77 + tools/debugserver/source/MacOSX/Makefile | 54 + tools/debugserver/source/MacOSX/ThreadInfo.h | 26 + .../source/MacOSX/arm/DNBArchImpl.cpp | 2162 + .../source/MacOSX/arm/DNBArchImpl.h | 282 + .../source/MacOSX/arm64/DNBArchImplARM64.cpp | 2095 + .../source/MacOSX/arm64/DNBArchImplARM64.h | 272 + .../debugserver/source/MacOSX/dbgnub-mig.defs | 5 + .../source/MacOSX/i386/CMakeLists.txt | 4 + .../source/MacOSX/i386/DNBArchImplI386.cpp | 1901 + .../source/MacOSX/i386/DNBArchImplI386.h | 255 + .../MacOSX/i386/MachRegisterStatesI386.h | 180 + tools/debugserver/source/MacOSX/i386/Makefile | 19 + .../source/MacOSX/ppc/DNBArchImpl.cpp | 569 + .../source/MacOSX/ppc/DNBArchImpl.h | 179 + .../debugserver/source/MacOSX/stack_logging.h | 122 + .../source/MacOSX/x86_64/CMakeLists.txt | 8 + .../MacOSX/x86_64/DNBArchImplX86_64.cpp | 2289 + .../source/MacOSX/x86_64/DNBArchImplX86_64.h | 260 + .../MacOSX/x86_64/MachRegisterStatesX86_64.h | 210 + .../debugserver/source/MacOSX/x86_64/Makefile | 19 + tools/debugserver/source/Makefile | 46 + tools/debugserver/source/PThreadCondition.h | 53 + tools/debugserver/source/PThreadEvent.cpp | 227 + tools/debugserver/source/PThreadEvent.h | 59 + tools/debugserver/source/PThreadMutex.cpp | 84 + tools/debugserver/source/PThreadMutex.h | 148 + tools/debugserver/source/PseudoTerminal.cpp | 227 + tools/debugserver/source/PseudoTerminal.h | 94 + tools/debugserver/source/RNBContext.cpp | 279 + tools/debugserver/source/RNBContext.h | 159 + tools/debugserver/source/RNBDefs.h | 87 + tools/debugserver/source/RNBRemote.cpp | 6083 ++ tools/debugserver/source/RNBRemote.h | 452 + tools/debugserver/source/RNBServices.cpp | 226 + tools/debugserver/source/RNBServices.h | 28 + tools/debugserver/source/RNBSocket.cpp | 421 + tools/debugserver/source/RNBSocket.h | 85 + tools/debugserver/source/SysSignal.cpp | 66 + tools/debugserver/source/SysSignal.h | 23 + tools/debugserver/source/TTYState.cpp | 122 + tools/debugserver/source/TTYState.h | 61 + ...m.apple.debugserver.applist.internal.plist | 16 + .../com.apple.debugserver.applist.plist | 19 + .../com.apple.debugserver.internal.plist | 15 + .../source/com.apple.debugserver.plist | 18 + .../source/com.apple.debugserver.posix.plist | 18 + .../source/debugserver-entitlements.plist | 28 + .../debugserver-macosx-entitlements.plist | 8 + tools/debugserver/source/debugserver.cpp | 1672 + tools/debugserver/source/libdebugserver.cpp | 397 + tools/debugserver/source/libdebugserver.h | 15 + tools/driver/CMakeLists.txt | 28 + tools/driver/Makefile | 36 + tools/driver/lldb-Info.plist | 21 + tools/install-headers/Makefile | 23 + tools/lldb-mi/CMakeLists.txt | 102 + tools/lldb-mi/MICmdCmdSymbol.cpp | 28 +- tools/lldb-mi/Makefile | 32 + tools/lldb-mi/lldb-Info.plist | 21 + tools/lldb-perf/README | 295 + tools/lldb-perf/common/clang/build-clang.sh | 33 + .../common/clang/lldb_perf_clang.cpp | 484 + tools/lldb-perf/common/clang/main.cpp | 24 + .../common/stepping/lldb-perf-stepping.cpp | 335 + .../common/stepping/stepping-testcase.cpp | 42 + .../darwin/formatters/fmts_tester.mm | 79 + .../darwin/formatters/formatters.cpp | 246 + tools/lldb-perf/darwin/sketch/foobar.sketch2 | Bin 0 -> 10027 bytes tools/lldb-perf/darwin/sketch/sketch.cpp | 380 + tools/lldb-perf/lib/Gauge.cpp | 53 + tools/lldb-perf/lib/Gauge.h | 64 + tools/lldb-perf/lib/Measurement.h | 217 + tools/lldb-perf/lib/MemoryGauge.cpp | 165 + tools/lldb-perf/lib/MemoryGauge.h | 147 + tools/lldb-perf/lib/Metric.cpp | 85 + tools/lldb-perf/lib/Metric.h | 72 + tools/lldb-perf/lib/Results.cpp | 275 + tools/lldb-perf/lib/Results.h | 312 + tools/lldb-perf/lib/TestCase.cpp | 358 + tools/lldb-perf/lib/TestCase.h | 205 + tools/lldb-perf/lib/Timer.cpp | 61 + tools/lldb-perf/lib/Timer.h | 66 + tools/lldb-perf/lib/Xcode.cpp | 165 + tools/lldb-perf/lib/Xcode.h | 64 + .../lldbperf.xcodeproj/project.pbxproj | 1224 + tools/lldb-server/CMakeLists.txt | 58 + tools/lldb-server/Makefile | 25 + unittests/CMakeLists.txt | 31 + unittests/Editline/CMakeLists.txt | 3 + unittests/Editline/EditlineTest.cpp | 368 + unittests/Expression/CMakeLists.txt | 3 + unittests/Expression/GoParserTest.cpp | 250 + unittests/Host/CMakeLists.txt | 5 + unittests/Host/SocketAddressTest.cpp | 77 + unittests/Host/SocketTest.cpp | 201 + unittests/Host/SymbolsTest.cpp | 30 + unittests/Interpreter/CMakeLists.txt | 7 + unittests/Interpreter/TestArgs.cpp | 70 + unittests/ScriptInterpreter/CMakeLists.txt | 3 + .../ScriptInterpreter/Python/CMakeLists.txt | 8 + .../Python/PythonDataObjectsTests.cpp | 564 + .../Python/PythonExceptionStateTests.cpp | 174 + .../Python/PythonTestSuite.cpp | 42 + .../Python/PythonTestSuite.h | 26 + unittests/Utility/CMakeLists.txt | 5 + unittests/Utility/StringExtractorTest.cpp | 406 + unittests/Utility/TaskPoolTest.cpp | 62 + unittests/Utility/UriParserTest.cpp | 159 + unittests/gtest_common.h | 24 + use_lldb_suite_root.py | 22 + utils/git-svn/convert.py | 65 + utils/lui/Readme | 36 + utils/lui/breakwin.py | 90 + utils/lui/commandwin.py | 121 + utils/lui/cui.py | 320 + utils/lui/debuggerdriver.py | 138 + utils/lui/eventwin.py | 25 + utils/lui/lldbutil.py | 899 + utils/lui/lui.py | 135 + utils/lui/sandbox.py | 73 + utils/lui/sourcewin.py | 232 + utils/lui/statuswin.py | 40 + utils/misc/grep-svn-log.py | 85 + utils/sync-source/README.txt | 293 + utils/sync-source/lib/transfer/__init__.py | 0 utils/sync-source/lib/transfer/protocol.py | 7 + utils/sync-source/lib/transfer/rsync.py | 60 + .../sync-source/lib/transfer/transfer_spec.py | 11 + utils/sync-source/pylintrc | 2 + utils/sync-source/syncsource.py | 270 + utils/test/README-disasm | 406 + utils/test/README-lldb-disasm | 94 + utils/test/README-run-until-faulted | 18 + utils/test/disasm.py | 198 + utils/test/lldb-disasm.py | 243 + utils/test/llvm-mc-shell.py | 100 + utils/test/main.c | 14 + utils/test/ras.py | 176 + utils/test/run-dis.py | 125 + utils/test/run-until-faulted.py | 111 + utils/vim-lldb/README | 59 + utils/vim-lldb/doc/lldb.txt | 115 + utils/vim-lldb/plugin/lldb.vim | 151 + utils/vim-lldb/python-vim-lldb/import_lldb.py | 61 + .../python-vim-lldb/lldb_controller.py | 384 + utils/vim-lldb/python-vim-lldb/plugin.py | 14 + utils/vim-lldb/python-vim-lldb/vim_panes.py | 618 + utils/vim-lldb/python-vim-lldb/vim_signs.py | 73 + utils/vim-lldb/python-vim-lldb/vim_ui.py | 235 + www/SB-api-coding-rules.html | 69 + www/adding-language-support.html | 193 + www/architecture.html | 294 + www/architecture/index.html | 281 + www/architecture/varformats.html | 324 + www/build.html | 551 + www/cpp_reference/html/LLDB_8h.html | 90 + www/cpp_reference/html/LLDB_8h__incl.map | 42 + www/cpp_reference/html/LLDB_8h__incl.md5 | 1 + www/cpp_reference/html/LLDB_8h__incl.png | Bin 0 -> 667656 bytes www/cpp_reference/html/LLDB_8h_source.html | 99 + www/cpp_reference/html/SBAddress_8h.html | 75 + .../html/SBAddress_8h__dep__incl.map | 12 + .../html/SBAddress_8h__dep__incl.md5 | 1 + .../html/SBAddress_8h__dep__incl.png | Bin 0 -> 72494 bytes www/cpp_reference/html/SBAddress_8h__incl.map | 24 + www/cpp_reference/html/SBAddress_8h__incl.md5 | 1 + www/cpp_reference/html/SBAddress_8h__incl.png | Bin 0 -> 230920 bytes .../html/SBAddress_8h_source.html | 195 + www/cpp_reference/html/SBBlock_8h.html | 77 + .../html/SBBlock_8h__dep__incl.map | 12 + .../html/SBBlock_8h__dep__incl.md5 | 1 + .../html/SBBlock_8h__dep__incl.png | Bin 0 -> 68310 bytes www/cpp_reference/html/SBBlock_8h__incl.map | 24 + www/cpp_reference/html/SBBlock_8h__incl.md5 | 1 + www/cpp_reference/html/SBBlock_8h__incl.png | Bin 0 -> 227766 bytes www/cpp_reference/html/SBBlock_8h_source.html | 168 + .../html/SBBreakpointLocation_8h.html | 75 + .../SBBreakpointLocation_8h__dep__incl.map | 3 + .../SBBreakpointLocation_8h__dep__incl.md5 | 1 + .../SBBreakpointLocation_8h__dep__incl.png | Bin 0 -> 3066 bytes .../html/SBBreakpointLocation_8h__incl.map | 4 + .../html/SBBreakpointLocation_8h__incl.md5 | 1 + .../html/SBBreakpointLocation_8h__incl.png | Bin 0 -> 18998 bytes .../html/SBBreakpointLocation_8h_source.html | 155 + www/cpp_reference/html/SBBreakpoint_8h.html | 74 + .../html/SBBreakpoint_8h__dep__incl.map | 4 + .../html/SBBreakpoint_8h__dep__incl.md5 | 1 + .../html/SBBreakpoint_8h__dep__incl.png | Bin 0 -> 7871 bytes .../html/SBBreakpoint_8h__incl.map | 3 + .../html/SBBreakpoint_8h__incl.md5 | 1 + .../html/SBBreakpoint_8h__incl.png | Bin 0 -> 13030 bytes .../html/SBBreakpoint_8h_source.html | 220 + www/cpp_reference/html/SBBroadcaster_8h.html | 74 + .../html/SBBroadcaster_8h__dep__incl.map | 13 + .../html/SBBroadcaster_8h__dep__incl.md5 | 1 + .../html/SBBroadcaster_8h__dep__incl.png | Bin 0 -> 85064 bytes .../html/SBBroadcaster_8h__incl.map | 3 + .../html/SBBroadcaster_8h__incl.md5 | 1 + .../html/SBBroadcaster_8h__incl.png | Bin 0 -> 13144 bytes .../html/SBBroadcaster_8h_source.html | 142 + .../html/SBCommandInterpreter_8h.html | 77 + .../SBCommandInterpreter_8h__dep__incl.map | 3 + .../SBCommandInterpreter_8h__dep__incl.md5 | 1 + .../SBCommandInterpreter_8h__dep__incl.png | Bin 0 -> 3166 bytes .../html/SBCommandInterpreter_8h__incl.map | 4 + .../html/SBCommandInterpreter_8h__incl.md5 | 1 + .../html/SBCommandInterpreter_8h__incl.png | Bin 0 -> 20372 bytes .../html/SBCommandInterpreter_8h_source.html | 238 + .../html/SBCommandReturnObject_8h.html | 75 + .../SBCommandReturnObject_8h__dep__incl.map | 3 + .../SBCommandReturnObject_8h__dep__incl.md5 | 1 + .../SBCommandReturnObject_8h__dep__incl.png | Bin 0 -> 3757 bytes .../html/SBCommandReturnObject_8h__incl.map | 3 + .../html/SBCommandReturnObject_8h__incl.md5 | 1 + .../html/SBCommandReturnObject_8h__incl.png | Bin 0 -> 16245 bytes .../html/SBCommandReturnObject_8h_source.html | 178 + .../html/SBCommunication_8h.html | 75 + .../html/SBCommunication_8h__dep__incl.map | 3 + .../html/SBCommunication_8h__dep__incl.md5 | 1 + .../html/SBCommunication_8h__dep__incl.png | Bin 0 -> 2885 bytes .../html/SBCommunication_8h__incl.map | 4 + .../html/SBCommunication_8h__incl.md5 | 1 + .../html/SBCommunication_8h__incl.png | Bin 0 -> 18160 bytes .../html/SBCommunication_8h_source.html | 144 + www/cpp_reference/html/SBCompileUnit_8h.html | 75 + .../html/SBCompileUnit_8h__dep__incl.map | 13 + .../html/SBCompileUnit_8h__dep__incl.md5 | 1 + .../html/SBCompileUnit_8h__dep__incl.png | Bin 0 -> 71219 bytes .../html/SBCompileUnit_8h__incl.map | 4 + .../html/SBCompileUnit_8h__incl.md5 | 1 + .../html/SBCompileUnit_8h__incl.png | Bin 0 -> 18485 bytes .../html/SBCompileUnit_8h_source.html | 161 + www/cpp_reference/html/SBData_8h.html | 74 + .../html/SBData_8h__dep__incl.map | 16 + .../html/SBData_8h__dep__incl.md5 | 1 + .../html/SBData_8h__dep__incl.png | Bin 0 -> 101571 bytes www/cpp_reference/html/SBData_8h__incl.map | 3 + www/cpp_reference/html/SBData_8h__incl.md5 | 1 + www/cpp_reference/html/SBData_8h__incl.png | Bin 0 -> 12393 bytes www/cpp_reference/html/SBData_8h_source.html | 225 + www/cpp_reference/html/SBDebugger_8h.html | 75 + .../html/SBDebugger_8h__dep__incl.map | 4 + .../html/SBDebugger_8h__dep__incl.md5 | 1 + .../html/SBDebugger_8h__dep__incl.png | Bin 0 -> 7851 bytes .../html/SBDebugger_8h__incl.map | 3 + .../html/SBDebugger_8h__incl.md5 | 1 + .../html/SBDebugger_8h__incl.png | Bin 0 -> 15135 bytes .../html/SBDebugger_8h_source.html | 384 + www/cpp_reference/html/SBDeclaration_8h.html | 75 + .../html/SBDeclaration_8h__dep__incl.map | 3 + .../html/SBDeclaration_8h__dep__incl.md5 | 1 + .../html/SBDeclaration_8h__dep__incl.png | Bin 0 -> 2788 bytes .../html/SBDeclaration_8h__incl.map | 4 + .../html/SBDeclaration_8h__incl.md5 | 1 + .../html/SBDeclaration_8h__incl.png | Bin 0 -> 18497 bytes .../html/SBDeclaration_8h_source.html | 134 + www/cpp_reference/html/SBDefines_8h.html | 71 + .../html/SBDefines_8h__dep__incl.map | 50 + .../html/SBDefines_8h__dep__incl.md5 | 1 + .../html/SBDefines_8h__dep__incl.png | Bin 0 -> 576803 bytes www/cpp_reference/html/SBDefines_8h__incl.map | 2 + www/cpp_reference/html/SBDefines_8h__incl.md5 | 1 + www/cpp_reference/html/SBDefines_8h__incl.png | Bin 0 -> 10282 bytes .../html/SBDefines_8h_source.html | 129 + www/cpp_reference/html/SBError_8h.html | 74 + .../html/SBError_8h__dep__incl.map | 14 + .../html/SBError_8h__dep__incl.md5 | 1 + .../html/SBError_8h__dep__incl.png | Bin 0 -> 102811 bytes www/cpp_reference/html/SBError_8h__incl.map | 3 + www/cpp_reference/html/SBError_8h__incl.md5 | 1 + www/cpp_reference/html/SBError_8h__incl.png | Bin 0 -> 12307 bytes www/cpp_reference/html/SBError_8h_source.html | 151 + www/cpp_reference/html/SBEvent_8h.html | 76 + .../html/SBEvent_8h__dep__incl.map | 3 + .../html/SBEvent_8h__dep__incl.md5 | 1 + .../html/SBEvent_8h__dep__incl.png | Bin 0 -> 2255 bytes www/cpp_reference/html/SBEvent_8h__incl.map | 3 + www/cpp_reference/html/SBEvent_8h__incl.md5 | 1 + www/cpp_reference/html/SBEvent_8h__incl.png | Bin 0 -> 15780 bytes www/cpp_reference/html/SBEvent_8h_source.html | 147 + .../html/SBExpressionOptions_8h.html | 68 + .../html/SBExpressionOptions_8h__incl.map | 3 + .../html/SBExpressionOptions_8h__incl.md5 | 1 + .../html/SBExpressionOptions_8h__incl.png | Bin 0 -> 15662 bytes .../html/SBExpressionOptions_8h_source.html | 134 + www/cpp_reference/html/SBFileSpecList_8h.html | 74 + .../html/SBFileSpecList_8h__dep__incl.map | 13 + .../html/SBFileSpecList_8h__dep__incl.md5 | 1 + .../html/SBFileSpecList_8h__dep__incl.png | Bin 0 -> 83896 bytes .../html/SBFileSpecList_8h__incl.map | 3 + .../html/SBFileSpecList_8h__incl.md5 | 1 + .../html/SBFileSpecList_8h__incl.png | Bin 0 -> 12849 bytes .../html/SBFileSpecList_8h_source.html | 117 + www/cpp_reference/html/SBFileSpec_8h.html | 74 + .../html/SBFileSpec_8h__dep__incl.map | 17 + .../html/SBFileSpec_8h__dep__incl.md5 | 1 + .../html/SBFileSpec_8h__dep__incl.png | Bin 0 -> 120076 bytes .../html/SBFileSpec_8h__incl.map | 3 + .../html/SBFileSpec_8h__incl.md5 | 1 + .../html/SBFileSpec_8h__incl.png | Bin 0 -> 12619 bytes .../html/SBFileSpec_8h_source.html | 141 + www/cpp_reference/html/SBFrame_8h.html | 75 + .../html/SBFrame_8h__dep__incl.map | 13 + .../html/SBFrame_8h__dep__incl.md5 | 1 + .../html/SBFrame_8h__dep__incl.png | Bin 0 -> 80587 bytes www/cpp_reference/html/SBFrame_8h__incl.map | 4 + www/cpp_reference/html/SBFrame_8h__incl.md5 | 1 + www/cpp_reference/html/SBFrame_8h__incl.png | Bin 0 -> 18220 bytes www/cpp_reference/html/SBFrame_8h_source.html | 284 + www/cpp_reference/html/SBFunction_8h.html | 76 + .../html/SBFunction_8h__dep__incl.map | 12 + .../html/SBFunction_8h__dep__incl.md5 | 1 + .../html/SBFunction_8h__dep__incl.png | Bin 0 -> 64287 bytes .../html/SBFunction_8h__incl.map | 24 + .../html/SBFunction_8h__incl.md5 | 1 + .../html/SBFunction_8h__incl.png | Bin 0 -> 235393 bytes .../html/SBFunction_8h_source.html | 138 + www/cpp_reference/html/SBHostOS_8h.html | 75 + .../html/SBHostOS_8h__dep__incl.map | 3 + .../html/SBHostOS_8h__dep__incl.md5 | 1 + .../html/SBHostOS_8h__dep__incl.png | Bin 0 -> 2504 bytes www/cpp_reference/html/SBHostOS_8h__incl.map | 4 + www/cpp_reference/html/SBHostOS_8h__incl.md5 | 1 + www/cpp_reference/html/SBHostOS_8h__incl.png | Bin 0 -> 18161 bytes .../html/SBHostOS_8h_source.html | 102 + www/cpp_reference/html/SBInputReader_8h.html | 74 + .../html/SBInputReader_8h__dep__incl.map | 3 + .../html/SBInputReader_8h__dep__incl.md5 | 1 + .../html/SBInputReader_8h__dep__incl.png | Bin 0 -> 2789 bytes .../html/SBInputReader_8h__incl.map | 3 + .../html/SBInputReader_8h__incl.md5 | 1 + .../html/SBInputReader_8h__incl.png | Bin 0 -> 13054 bytes .../html/SBInputReader_8h_source.html | 142 + .../html/SBInstructionList_8h.html | 75 + .../html/SBInstructionList_8h__dep__incl.map | 13 + .../html/SBInstructionList_8h__dep__incl.md5 | 1 + .../html/SBInstructionList_8h__dep__incl.png | Bin 0 -> 81436 bytes .../html/SBInstructionList_8h__incl.map | 3 + .../html/SBInstructionList_8h__incl.md5 | 1 + .../html/SBInstructionList_8h__incl.png | Bin 0 -> 15060 bytes .../html/SBInstructionList_8h_source.html | 116 + www/cpp_reference/html/SBInstruction_8h.html | 76 + .../html/SBInstruction_8h__dep__incl.map | 3 + .../html/SBInstruction_8h__dep__incl.md5 | 1 + .../html/SBInstruction_8h__dep__incl.png | Bin 0 -> 2600 bytes .../html/SBInstruction_8h__incl.map | 4 + .../html/SBInstruction_8h__incl.md5 | 1 + .../html/SBInstruction_8h__incl.png | Bin 0 -> 18850 bytes .../html/SBInstruction_8h_source.html | 139 + www/cpp_reference/html/SBLineEntry_8h.html | 76 + .../html/SBLineEntry_8h__dep__incl.map | 12 + .../html/SBLineEntry_8h__dep__incl.md5 | 1 + .../html/SBLineEntry_8h__dep__incl.png | Bin 0 -> 64343 bytes .../html/SBLineEntry_8h__incl.map | 24 + .../html/SBLineEntry_8h__incl.md5 | 1 + .../html/SBLineEntry_8h__incl.png | Bin 0 -> 232817 bytes .../html/SBLineEntry_8h_source.html | 144 + www/cpp_reference/html/SBListener_8h.html | 74 + .../html/SBListener_8h__dep__incl.map | 3 + .../html/SBListener_8h__dep__incl.md5 | 1 + .../html/SBListener_8h__dep__incl.png | Bin 0 -> 2417 bytes .../html/SBListener_8h__incl.map | 3 + .../html/SBListener_8h__incl.md5 | 1 + .../html/SBListener_8h__incl.png | Bin 0 -> 12635 bytes .../html/SBListener_8h_source.html | 180 + www/cpp_reference/html/SBModuleSpec_8h.html | 69 + .../html/SBModuleSpec_8h__incl.map | 4 + .../html/SBModuleSpec_8h__incl.md5 | 1 + .../html/SBModuleSpec_8h__incl.png | Bin 0 -> 18539 bytes .../html/SBModuleSpec_8h_source.html | 199 + www/cpp_reference/html/SBModule_8h.html | 78 + .../html/SBModule_8h__dep__incl.map | 12 + .../html/SBModule_8h__dep__incl.md5 | 1 + .../html/SBModule_8h__dep__incl.png | Bin 0 -> 71890 bytes www/cpp_reference/html/SBModule_8h__incl.map | 24 + www/cpp_reference/html/SBModule_8h__incl.md5 | 1 + www/cpp_reference/html/SBModule_8h__incl.png | Bin 0 -> 222154 bytes .../html/SBModule_8h_source.html | 332 + www/cpp_reference/html/SBProcess_8h.html | 77 + .../html/SBProcess_8h__dep__incl.map | 3 + .../html/SBProcess_8h__dep__incl.md5 | 1 + .../html/SBProcess_8h__dep__incl.png | Bin 0 -> 2588 bytes www/cpp_reference/html/SBProcess_8h__incl.map | 25 + www/cpp_reference/html/SBProcess_8h__incl.md5 | 1 + www/cpp_reference/html/SBProcess_8h__incl.png | Bin 0 -> 260352 bytes .../html/SBProcess_8h_source.html | 340 + www/cpp_reference/html/SBSection_8h.html | 75 + .../html/SBSection_8h__dep__incl.map | 13 + .../html/SBSection_8h__dep__incl.md5 | 1 + .../html/SBSection_8h__dep__incl.png | Bin 0 -> 73710 bytes www/cpp_reference/html/SBSection_8h__incl.map | 4 + www/cpp_reference/html/SBSection_8h__incl.md5 | 1 + www/cpp_reference/html/SBSection_8h__incl.png | Bin 0 -> 17810 bytes .../html/SBSection_8h_source.html | 149 + .../html/SBSourceManager_8h.html | 75 + .../html/SBSourceManager_8h__dep__incl.map | 3 + .../html/SBSourceManager_8h__dep__incl.md5 | 1 + .../html/SBSourceManager_8h__dep__incl.png | Bin 0 -> 3196 bytes .../html/SBSourceManager_8h__incl.map | 3 + .../html/SBSourceManager_8h__incl.md5 | 1 + .../html/SBSourceManager_8h__incl.png | Bin 0 -> 15631 bytes .../html/SBSourceManager_8h_source.html | 98 + www/cpp_reference/html/SBStream_8h.html | 75 + .../html/SBStream_8h__dep__incl.map | 3 + .../html/SBStream_8h__dep__incl.md5 | 1 + .../html/SBStream_8h__dep__incl.png | Bin 0 -> 2345 bytes www/cpp_reference/html/SBStream_8h__incl.map | 3 + www/cpp_reference/html/SBStream_8h__incl.md5 | 1 + www/cpp_reference/html/SBStream_8h__incl.png | Bin 0 -> 14825 bytes .../html/SBStream_8h_source.html | 156 + www/cpp_reference/html/SBStringList_8h.html | 74 + .../html/SBStringList_8h__dep__incl.map | 3 + .../html/SBStringList_8h__dep__incl.md5 | 1 + .../html/SBStringList_8h__dep__incl.png | Bin 0 -> 2470 bytes .../html/SBStringList_8h__incl.map | 3 + .../html/SBStringList_8h__incl.md5 | 1 + .../html/SBStringList_8h__incl.png | Bin 0 -> 12745 bytes .../html/SBStringList_8h_source.html | 116 + .../html/SBSymbolContextList_8h.html | 75 + .../SBSymbolContextList_8h__dep__incl.map | 12 + .../SBSymbolContextList_8h__dep__incl.md5 | 1 + .../SBSymbolContextList_8h__dep__incl.png | Bin 0 -> 79224 bytes .../html/SBSymbolContextList_8h__incl.map | 24 + .../html/SBSymbolContextList_8h__incl.md5 | 1 + .../html/SBSymbolContextList_8h__incl.png | Bin 0 -> 204924 bytes .../html/SBSymbolContextList_8h_source.html | 114 + .../html/SBSymbolContext_8h.html | 80 + .../html/SBSymbolContext_8h__dep__incl.map | 12 + .../html/SBSymbolContext_8h__dep__incl.md5 | 1 + .../html/SBSymbolContext_8h__dep__incl.png | Bin 0 -> 65046 bytes .../html/SBSymbolContext_8h__incl.map | 24 + .../html/SBSymbolContext_8h__incl.md5 | 1 + .../html/SBSymbolContext_8h__incl.png | Bin 0 -> 221045 bytes .../html/SBSymbolContext_8h_source.html | 139 + www/cpp_reference/html/SBSymbol_8h.html | 77 + .../html/SBSymbol_8h__dep__incl.map | 12 + .../html/SBSymbol_8h__dep__incl.md5 | 1 + .../html/SBSymbol_8h__dep__incl.png | Bin 0 -> 68347 bytes www/cpp_reference/html/SBSymbol_8h__incl.map | 24 + www/cpp_reference/html/SBSymbol_8h__incl.md5 | 1 + www/cpp_reference/html/SBSymbol_8h__incl.png | Bin 0 -> 241177 bytes .../html/SBSymbol_8h_source.html | 154 + www/cpp_reference/html/SBTarget_8h.html | 84 + .../html/SBTarget_8h__dep__incl.map | 12 + .../html/SBTarget_8h__dep__incl.md5 | 1 + .../html/SBTarget_8h__dep__incl.png | Bin 0 -> 81841 bytes www/cpp_reference/html/SBTarget_8h__incl.map | 24 + www/cpp_reference/html/SBTarget_8h__incl.md5 | 1 + www/cpp_reference/html/SBTarget_8h__incl.png | Bin 0 -> 230045 bytes .../html/SBTarget_8h_source.html | 873 + www/cpp_reference/html/SBThread_8h.html | 75 + .../html/SBThread_8h__dep__incl.map | 3 + .../html/SBThread_8h__dep__incl.md5 | 1 + .../html/SBThread_8h__dep__incl.png | Bin 0 -> 2353 bytes www/cpp_reference/html/SBThread_8h__incl.map | 3 + www/cpp_reference/html/SBThread_8h__incl.md5 | 1 + www/cpp_reference/html/SBThread_8h__incl.png | Bin 0 -> 14820 bytes .../html/SBThread_8h_source.html | 265 + www/cpp_reference/html/SBTypeCategory_8h.html | 67 + .../html/SBTypeCategory_8h__incl.map | 3 + .../html/SBTypeCategory_8h__incl.md5 | 1 + .../html/SBTypeCategory_8h__incl.png | Bin 0 -> 13463 bytes .../html/SBTypeCategory_8h_source.html | 213 + www/cpp_reference/html/SBTypeFilter_8h.html | 67 + .../html/SBTypeFilter_8h__incl.map | 3 + .../html/SBTypeFilter_8h__incl.md5 | 1 + .../html/SBTypeFilter_8h__incl.png | Bin 0 -> 12835 bytes .../html/SBTypeFilter_8h_source.html | 137 + www/cpp_reference/html/SBTypeFormat_8h.html | 67 + .../html/SBTypeFormat_8h__incl.map | 3 + .../html/SBTypeFormat_8h__incl.md5 | 1 + .../html/SBTypeFormat_8h__incl.png | Bin 0 -> 13153 bytes .../html/SBTypeFormat_8h_source.html | 129 + .../html/SBTypeNameSpecifier_8h.html | 67 + .../html/SBTypeNameSpecifier_8h__incl.map | 3 + .../html/SBTypeNameSpecifier_8h__incl.md5 | 1 + .../html/SBTypeNameSpecifier_8h__incl.png | Bin 0 -> 13483 bytes .../html/SBTypeNameSpecifier_8h_source.html | 122 + www/cpp_reference/html/SBTypeSummary_8h.html | 67 + .../html/SBTypeSummary_8h__incl.map | 3 + .../html/SBTypeSummary_8h__incl.md5 | 1 + .../html/SBTypeSummary_8h__incl.png | Bin 0 -> 13012 bytes .../html/SBTypeSummary_8h_source.html | 160 + .../html/SBTypeSynthetic_8h.html | 67 + .../html/SBTypeSynthetic_8h__incl.map | 3 + .../html/SBTypeSynthetic_8h__incl.md5 | 1 + .../html/SBTypeSynthetic_8h__incl.png | Bin 0 -> 13021 bytes .../html/SBTypeSynthetic_8h_source.html | 147 + www/cpp_reference/html/SBType_8h.html | 76 + .../html/SBType_8h__dep__incl.map | 14 + .../html/SBType_8h__dep__incl.md5 | 1 + .../html/SBType_8h__dep__incl.png | Bin 0 -> 98472 bytes www/cpp_reference/html/SBType_8h__incl.map | 3 + www/cpp_reference/html/SBType_8h__incl.md5 | 1 + www/cpp_reference/html/SBType_8h__incl.png | Bin 0 -> 12606 bytes www/cpp_reference/html/SBType_8h_source.html | 289 + www/cpp_reference/html/SBValueList_8h.html | 74 + .../html/SBValueList_8h__dep__incl.map | 14 + .../html/SBValueList_8h__dep__incl.md5 | 1 + .../html/SBValueList_8h__dep__incl.png | Bin 0 -> 100149 bytes .../html/SBValueList_8h__incl.map | 3 + .../html/SBValueList_8h__incl.md5 | 1 + .../html/SBValueList_8h__incl.png | Bin 0 -> 12919 bytes .../html/SBValueList_8h_source.html | 138 + www/cpp_reference/html/SBValue_8h.html | 76 + .../html/SBValue_8h__dep__incl.map | 13 + .../html/SBValue_8h__dep__incl.md5 | 1 + .../html/SBValue_8h__dep__incl.png | Bin 0 -> 84602 bytes www/cpp_reference/html/SBValue_8h__incl.map | 5 + www/cpp_reference/html/SBValue_8h__incl.md5 | 1 + www/cpp_reference/html/SBValue_8h__incl.png | Bin 0 -> 20011 bytes www/cpp_reference/html/SBValue_8h_source.html | 533 + www/cpp_reference/html/SBWatchpoint_8h.html | 74 + .../html/SBWatchpoint_8h__dep__incl.map | 13 + .../html/SBWatchpoint_8h__dep__incl.md5 | 1 + .../html/SBWatchpoint_8h__dep__incl.png | Bin 0 -> 84184 bytes .../html/SBWatchpoint_8h__incl.map | 3 + .../html/SBWatchpoint_8h__incl.md5 | 1 + .../html/SBWatchpoint_8h__incl.png | Bin 0 -> 13157 bytes .../html/SBWatchpoint_8h_source.html | 149 + www/cpp_reference/html/annotated.html | 102 + www/cpp_reference/html/bc_s.png | Bin 0 -> 680 bytes www/cpp_reference/html/bdwn.png | Bin 0 -> 147 bytes www/cpp_reference/html/classes.html | 63 + .../html/classlldb_1_1SBAddress-members.html | 94 + .../html/classlldb_1_1SBAddress.html | 976 + .../classlldb_1_1SBAttachInfo-members.html | 86 + .../html/classlldb_1_1SBAttachInfo.html | 707 + ...classlldb_1_1SBAttachInfo__coll__graph.map | 2 + ...classlldb_1_1SBAttachInfo__coll__graph.md5 | 1 + ...classlldb_1_1SBAttachInfo__coll__graph.png | Bin 0 -> 4271 bytes .../html/classlldb_1_1SBBlock-members.html | 75 + .../html/classlldb_1_1SBBlock.html | 557 + .../classlldb_1_1SBBreakpoint-members.html | 93 + .../html/classlldb_1_1SBBreakpoint.html | 862 + ...slldb_1_1SBBreakpointLocation-members.html | 78 + .../classlldb_1_1SBBreakpointLocation.html | 552 + .../classlldb_1_1SBBroadcaster-members.html | 76 + .../html/classlldb_1_1SBBroadcaster.html | 639 + .../html/classlldb_1_1SBCommand-members.html | 58 + .../html/classlldb_1_1SBCommand.html | 239 + ...slldb_1_1SBCommandInterpreter-members.html | 83 + .../classlldb_1_1SBCommandInterpreter.html | 758 + ...b_1_1SBCommandPluginInterface-members.html | 52 + ...classlldb_1_1SBCommandPluginInterface.html | 133 + ...lldb_1_1SBCommandReturnObject-members.html | 86 + .../classlldb_1_1SBCommandReturnObject.html | 751 + .../classlldb_1_1SBCommunication-members.html | 75 + .../html/classlldb_1_1SBCommunication.html | 495 + .../classlldb_1_1SBCompileUnit-members.html | 71 + .../html/classlldb_1_1SBCompileUnit.html | 506 + .../html/classlldb_1_1SBData-members.html | 100 + .../html/classlldb_1_1SBData.html | 1402 + .../html/classlldb_1_1SBDebugger-members.html | 145 + .../html/classlldb_1_1SBDebugger.html | 2028 + .../classlldb_1_1SBDeclaration-members.html | 66 + .../html/classlldb_1_1SBDeclaration.html | 352 + .../html/classlldb_1_1SBError-members.html | 83 + .../html/classlldb_1_1SBError.html | 745 + .../html/classlldb_1_1SBEvent-members.html | 78 + .../html/classlldb_1_1SBEvent.html | 649 + ...sslldb_1_1SBExpressionOptions-members.html | 72 + .../classlldb_1_1SBExpressionOptions.html | 479 + .../html/classlldb_1_1SBFileSpec-members.html | 78 + .../html/classlldb_1_1SBFileSpec.html | 670 + .../classlldb_1_1SBFileSpecList-members.html | 62 + .../html/classlldb_1_1SBFileSpecList.html | 291 + .../html/classlldb_1_1SBFrame-members.html | 102 + .../html/classlldb_1_1SBFrame.html | 1190 + .../classlldb_1_1SBFrame__coll__graph.map | 2 + .../classlldb_1_1SBFrame__coll__graph.md5 | 1 + .../classlldb_1_1SBFrame__coll__graph.png | Bin 0 -> 4135 bytes .../html/classlldb_1_1SBFunction-members.html | 72 + .../html/classlldb_1_1SBFunction.html | 480 + .../html/classlldb_1_1SBHostOS-members.html | 57 + .../html/classlldb_1_1SBHostOS.html | 294 + .../classlldb_1_1SBInputReader-members.html | 68 + .../html/classlldb_1_1SBInputReader.html | 457 + .../classlldb_1_1SBInstruction-members.html | 71 + .../html/classlldb_1_1SBInstruction.html | 465 + ...lasslldb_1_1SBInstructionList-members.html | 66 + .../html/classlldb_1_1SBInstructionList.html | 366 + .../classlldb_1_1SBLaunchInfo-members.html | 82 + .../html/classlldb_1_1SBLaunchInfo.html | 701 + ...classlldb_1_1SBLaunchInfo__coll__graph.map | 2 + ...classlldb_1_1SBLaunchInfo__coll__graph.md5 | 1 + ...classlldb_1_1SBLaunchInfo__coll__graph.png | Bin 0 -> 4035 bytes .../classlldb_1_1SBLineEntry-members.html | 71 + .../html/classlldb_1_1SBLineEntry.html | 453 + .../html/classlldb_1_1SBListener-members.html | 77 + .../html/classlldb_1_1SBListener.html | 712 + .../html/classlldb_1_1SBModule-members.html | 93 + .../html/classlldb_1_1SBModule.html | 955 + .../classlldb_1_1SBModuleSpec-members.html | 73 + .../html/classlldb_1_1SBModuleSpec.html | 483 + ...classlldb_1_1SBModuleSpecList-members.html | 62 + .../html/classlldb_1_1SBModuleSpecList.html | 277 + .../html/classlldb_1_1SBProcess-members.html | 129 + .../html/classlldb_1_1SBProcess.html | 1761 + .../classlldb_1_1SBProcess__coll__graph.map | 2 + .../classlldb_1_1SBProcess__coll__graph.md5 | 1 + .../classlldb_1_1SBProcess__coll__graph.png | Bin 0 -> 3964 bytes .../html/classlldb_1_1SBSection-members.html | 74 + .../html/classlldb_1_1SBSection.html | 493 + .../classlldb_1_1SBSourceManager-members.html | 59 + .../html/classlldb_1_1SBSourceManager.html | 279 + .../html/classlldb_1_1SBStream-members.html | 95 + .../html/classlldb_1_1SBStream.html | 1085 + .../classlldb_1_1SBStringList-members.html | 66 + .../html/classlldb_1_1SBStringList.html | 384 + .../html/classlldb_1_1SBSymbol-members.html | 74 + .../html/classlldb_1_1SBSymbol.html | 519 + .../classlldb_1_1SBSymbolContext-members.html | 82 + .../html/classlldb_1_1SBSymbolContext.html | 705 + ...sslldb_1_1SBSymbolContextList-members.html | 65 + .../classlldb_1_1SBSymbolContextList.html | 349 + .../html/classlldb_1_1SBTarget-members.html | 151 + .../html/classlldb_1_1SBTarget.html | 2520 + .../html/classlldb_1_1SBThread-members.html | 105 + .../html/classlldb_1_1SBThread.html | 1097 + .../html/classlldb_1_1SBType-members.html | 100 + .../html/classlldb_1_1SBType.html | 1020 + .../classlldb_1_1SBTypeCategory-members.html | 92 + .../html/classlldb_1_1SBTypeCategory.html | 888 + ...asslldb_1_1SBTypeCategory__coll__graph.map | 2 + ...asslldb_1_1SBTypeCategory__coll__graph.md5 | 1 + ...asslldb_1_1SBTypeCategory__coll__graph.png | Bin 0 -> 4917 bytes .../classlldb_1_1SBTypeFilter-members.html | 75 + .../html/classlldb_1_1SBTypeFilter.html | 576 + ...classlldb_1_1SBTypeFilter__coll__graph.map | 2 + ...classlldb_1_1SBTypeFilter__coll__graph.md5 | 1 + ...classlldb_1_1SBTypeFilter__coll__graph.png | Bin 0 -> 4039 bytes .../classlldb_1_1SBTypeFormat-members.html | 72 + .../html/classlldb_1_1SBTypeFormat.html | 526 + ...classlldb_1_1SBTypeFormat__coll__graph.map | 2 + ...classlldb_1_1SBTypeFormat__coll__graph.md5 | 1 + ...classlldb_1_1SBTypeFormat__coll__graph.png | Bin 0 -> 4332 bytes .../html/classlldb_1_1SBTypeList-members.html | 60 + .../html/classlldb_1_1SBTypeList.html | 247 + .../classlldb_1_1SBTypeMember-members.html | 67 + .../html/classlldb_1_1SBTypeMember.html | 409 + ...classlldb_1_1SBTypeMember__coll__graph.map | 2 + ...classlldb_1_1SBTypeMember__coll__graph.md5 | 1 + ...classlldb_1_1SBTypeMember__coll__graph.png | Bin 0 -> 5164 bytes ...sslldb_1_1SBTypeNameSpecifier-members.html | 70 + .../classlldb_1_1SBTypeNameSpecifier.html | 478 + ...db_1_1SBTypeNameSpecifier__coll__graph.map | 2 + ...db_1_1SBTypeNameSpecifier__coll__graph.md5 | 1 + ...db_1_1SBTypeNameSpecifier__coll__graph.png | Bin 0 -> 5055 bytes .../classlldb_1_1SBTypeSummary-members.html | 80 + .../html/classlldb_1_1SBTypeSummary.html | 715 + ...lasslldb_1_1SBTypeSummary__coll__graph.map | 2 + ...lasslldb_1_1SBTypeSummary__coll__graph.md5 | 1 + ...lasslldb_1_1SBTypeSummary__coll__graph.png | Bin 0 -> 4254 bytes .../classlldb_1_1SBTypeSynthetic-members.html | 76 + .../html/classlldb_1_1SBTypeSynthetic.html | 622 + ...sslldb_1_1SBTypeSynthetic__coll__graph.map | 2 + ...sslldb_1_1SBTypeSynthetic__coll__graph.md5 | 1 + ...sslldb_1_1SBTypeSynthetic__coll__graph.png | Bin 0 -> 4755 bytes .../html/classlldb_1_1SBType__coll__graph.map | 2 + .../html/classlldb_1_1SBType__coll__graph.md5 | 1 + .../html/classlldb_1_1SBType__coll__graph.png | Bin 0 -> 3677 bytes .../html/classlldb_1_1SBValue-members.html | 138 + .../html/classlldb_1_1SBValue.html | 1954 + .../classlldb_1_1SBValueList-members.html | 63 + .../html/classlldb_1_1SBValueList.html | 302 + .../classlldb_1_1SBWatchpoint-members.html | 77 + .../html/classlldb_1_1SBWatchpoint.html | 567 + www/cpp_reference/html/closed.png | Bin 0 -> 132 bytes .../dir_217b186c19a2bb8bc0ee0f71fb72d4e8.html | 52 + ...r_217b186c19a2bb8bc0ee0f71fb72d4e8_dep.map | 4 + ...r_217b186c19a2bb8bc0ee0f71fb72d4e8_dep.md5 | 1 + ...r_217b186c19a2bb8bc0ee0f71fb72d4e8_dep.png | Bin 0 -> 1366 bytes .../dir_36ad4ea5df2b352cce0512435d34c8a4.html | 100 + ...r_36ad4ea5df2b352cce0512435d34c8a4_dep.map | 4 + ...r_36ad4ea5df2b352cce0512435d34c8a4_dep.md5 | 1 + ...r_36ad4ea5df2b352cce0512435d34c8a4_dep.png | Bin 0 -> 1355 bytes .../dir_8b321541f691a81675dae4ec7a1864bb.html | 52 + ...r_8b321541f691a81675dae4ec7a1864bb_dep.map | 4 + ...r_8b321541f691a81675dae4ec7a1864bb_dep.md5 | 1 + ...r_8b321541f691a81675dae4ec7a1864bb_dep.png | Bin 0 -> 1596 bytes .../dir_9d4b8469db156c557ab1d649b027ec2e.html | 52 + ...r_9d4b8469db156c557ab1d649b027ec2e_dep.map | 5 + ...r_9d4b8469db156c557ab1d649b027ec2e_dep.md5 | 1 + ...r_9d4b8469db156c557ab1d649b027ec2e_dep.png | Bin 0 -> 2283 bytes .../dir_c02e3ff158c3064d7b78c6aa6fc411e6.html | 52 + ...r_c02e3ff158c3064d7b78c6aa6fc411e6_dep.map | 5 + ...r_c02e3ff158c3064d7b78c6aa6fc411e6_dep.md5 | 1 + ...r_c02e3ff158c3064d7b78c6aa6fc411e6_dep.png | Bin 0 -> 2361 bytes .../dir_fa64c3fa8a988674a1a867b97ca9a790.html | 53 + ...r_fa64c3fa8a988674a1a867b97ca9a790_dep.map | 6 + ...r_fa64c3fa8a988674a1a867b97ca9a790_dep.md5 | 1 + ...r_fa64c3fa8a988674a1a867b97ca9a790_dep.png | Bin 0 -> 2965 bytes www/cpp_reference/html/doxygen.css | 408 + www/cpp_reference/html/doxygen.png | Bin 0 -> 3779 bytes www/cpp_reference/html/doxygen_8intro.html | 47 + .../html/doxygen_8intro_source.html | 64 + www/cpp_reference/html/dynsections.js | 78 + www/cpp_reference/html/files.html | 95 + www/cpp_reference/html/ftv2blank.png | Bin 0 -> 86 bytes www/cpp_reference/html/ftv2cl.png | Bin 0 -> 453 bytes www/cpp_reference/html/ftv2doc.png | Bin 0 -> 746 bytes www/cpp_reference/html/ftv2folderclosed.png | Bin 0 -> 616 bytes www/cpp_reference/html/ftv2folderopen.png | Bin 0 -> 597 bytes www/cpp_reference/html/ftv2lastnode.png | Bin 0 -> 86 bytes www/cpp_reference/html/ftv2link.png | Bin 0 -> 746 bytes www/cpp_reference/html/ftv2mlastnode.png | Bin 0 -> 246 bytes www/cpp_reference/html/ftv2mnode.png | Bin 0 -> 246 bytes www/cpp_reference/html/ftv2mo.png | Bin 0 -> 403 bytes www/cpp_reference/html/ftv2node.png | Bin 0 -> 86 bytes www/cpp_reference/html/ftv2ns.png | Bin 0 -> 388 bytes www/cpp_reference/html/ftv2plastnode.png | Bin 0 -> 229 bytes www/cpp_reference/html/ftv2pnode.png | Bin 0 -> 229 bytes www/cpp_reference/html/ftv2splitbar.png | Bin 0 -> 314 bytes www/cpp_reference/html/ftv2vertline.png | Bin 0 -> 86 bytes www/cpp_reference/html/functions.html | 175 + www/cpp_reference/html/functions_0x62.html | 118 + www/cpp_reference/html/functions_0x63.html | 202 + www/cpp_reference/html/functions_0x64.html | 152 + www/cpp_reference/html/functions_0x65.html | 197 + www/cpp_reference/html/functions_0x66.html | 166 + www/cpp_reference/html/functions_0x67.html | 1214 + www/cpp_reference/html/functions_0x68.html | 104 + www/cpp_reference/html/functions_0x69.html | 229 + www/cpp_reference/html/functions_0x6b.html | 82 + www/cpp_reference/html/functions_0x6c.html | 95 + www/cpp_reference/html/functions_0x6d.html | 103 + www/cpp_reference/html/functions_0x6e.html | 82 + www/cpp_reference/html/functions_0x6f.html | 200 + www/cpp_reference/html/functions_0x70.html | 114 + www/cpp_reference/html/functions_0x72.html | 196 + www/cpp_reference/html/functions_0x73.html | 903 + www/cpp_reference/html/functions_0x74.html | 103 + www/cpp_reference/html/functions_0x75.html | 86 + www/cpp_reference/html/functions_0x76.html | 82 + www/cpp_reference/html/functions_0x77.html | 109 + www/cpp_reference/html/functions_0x7e.html | 235 + www/cpp_reference/html/functions_eval.html | 129 + www/cpp_reference/html/functions_func.html | 174 + .../html/functions_func_0x62.html | 114 + .../html/functions_func_0x63.html | 198 + .../html/functions_func_0x64.html | 151 + .../html/functions_func_0x65.html | 119 + .../html/functions_func_0x66.html | 165 + .../html/functions_func_0x67.html | 1213 + .../html/functions_func_0x68.html | 103 + .../html/functions_func_0x69.html | 228 + .../html/functions_func_0x6b.html | 81 + .../html/functions_func_0x6c.html | 90 + .../html/functions_func_0x6d.html | 84 + .../html/functions_func_0x6e.html | 81 + .../html/functions_func_0x6f.html | 199 + .../html/functions_func_0x70.html | 113 + .../html/functions_func_0x72.html | 193 + .../html/functions_func_0x73.html | 670 + .../html/functions_func_0x74.html | 102 + .../html/functions_func_0x75.html | 85 + .../html/functions_func_0x77.html | 108 + .../html/functions_func_0x7e.html | 234 + www/cpp_reference/html/functions_rela.html | 64 + .../html/functions_rela_0x73.html | 373 + .../html/functions_rela_0x76.html | 63 + www/cpp_reference/html/functions_type.html | 60 + www/cpp_reference/html/functions_vars.html | 69 + www/cpp_reference/html/graph_legend.html | 100 + www/cpp_reference/html/graph_legend.md5 | 1 + www/cpp_reference/html/graph_legend.png | Bin 0 -> 17036 bytes www/cpp_reference/html/index.html | 49 + www/cpp_reference/html/namespacelldb.html | 101 + www/cpp_reference/html/namespaces.html | 46 + www/cpp_reference/html/nav_f.png | Bin 0 -> 153 bytes www/cpp_reference/html/nav_g.png | Bin 0 -> 95 bytes www/cpp_reference/html/nav_h.png | Bin 0 -> 98 bytes www/cpp_reference/html/open.png | Bin 0 -> 123 bytes www/cpp_reference/html/sync_off.png | Bin 0 -> 853 bytes www/cpp_reference/html/sync_on.png | Bin 0 -> 845 bytes www/cpp_reference/html/tab_a.png | Bin 0 -> 142 bytes www/cpp_reference/html/tab_b.png | Bin 0 -> 167 bytes www/cpp_reference/html/tab_h.png | Bin 0 -> 192 bytes www/cpp_reference/html/tab_s.png | Bin 0 -> 184 bytes www/cpp_reference/html/tabs.css | 60 + www/customization.html | 33 + www/docs.html | 34 + www/download.html | 56 + www/faq.html | 30 + www/features.html | 60 + www/formats.html | 288 + www/goals.html | 63 + www/index.html | 136 + www/lldb-coding-conventions.html | 139 + www/lldb-gdb.html | 1288 + www/python-reference.html | 636 + www/python_reference/_lldb'-module.html | 26356 ++++++++ www/python_reference/api-objects.txt | 4556 ++ www/python_reference/class-tree.html | 384 + www/python_reference/crarr.png | Bin 0 -> 340 bytes www/python_reference/epydoc.css | 322 + www/python_reference/epydoc.js | 293 + www/python_reference/frames.html | 17 + www/python_reference/help.html | 272 + www/python_reference/identifier-index-A.html | 292 + www/python_reference/identifier-index-B.html | 214 + www/python_reference/identifier-index-C.html | 355 + www/python_reference/identifier-index-D.html | 390 + www/python_reference/identifier-index-E.html | 2574 + www/python_reference/identifier-index-F.html | 355 + www/python_reference/identifier-index-G.html | 1590 + www/python_reference/identifier-index-H.html | 197 + www/python_reference/identifier-index-I.html | 460 + www/python_reference/identifier-index-J.html | 135 + www/python_reference/identifier-index-K.html | 164 + www/python_reference/identifier-index-L.html | 450 + www/python_reference/identifier-index-M.html | 202 + www/python_reference/identifier-index-N.html | 419 + www/python_reference/identifier-index-O.html | 166 + www/python_reference/identifier-index-P.html | 228 + www/python_reference/identifier-index-Q.html | 149 + www/python_reference/identifier-index-R.html | 261 + www/python_reference/identifier-index-S.html | 3940 ++ www/python_reference/identifier-index-T.html | 228 + www/python_reference/identifier-index-U.html | 214 + www/python_reference/identifier-index-V.html | 182 + www/python_reference/identifier-index-W.html | 182 + www/python_reference/identifier-index-X.html | 151 + www/python_reference/identifier-index-Y.html | 135 + www/python_reference/identifier-index-Z.html | 135 + www/python_reference/identifier-index-_.html | 1916 + www/python_reference/identifier-index.html | 292 + www/python_reference/index.html | 17 + www/python_reference/lldb-module.html | 5319 ++ www/python_reference/lldb-pysrc.html | 50203 ++++++++++++++++ .../lldb.SBAddress-class.html | 1300 + .../lldb.SBAttachInfo-class.html | 883 + www/python_reference/lldb.SBBlock-class.html | 1293 + .../lldb.SBBlock.ranges_access-class.html | 279 + .../lldb.SBBreakpoint-class.html | 1405 + .../lldb.SBBreakpointLocation-class.html | 895 + .../lldb.SBBroadcaster-class.html | 656 + .../lldb.SBCommandInterpreter-class.html | 852 + .../lldb.SBCommandReturnObject-class.html | 880 + .../lldb.SBCommunication-class.html | 773 + .../lldb.SBCompileUnit-class.html | 875 + www/python_reference/lldb.SBData-class.html | 2124 + .../lldb.SBData.read_data_helper-class.html | 195 + .../lldb.SBDebugger-class.html | 2012 + .../lldb.SBDeclaration-class.html | 755 + www/python_reference/lldb.SBError-class.html | 890 + www/python_reference/lldb.SBEvent-class.html | 713 + .../lldb.SBExpressionOptions-class.html | 759 + .../lldb.SBFileSpec-class.html | 834 + .../lldb.SBFileSpecList-class.html | 550 + www/python_reference/lldb.SBFrame-class.html | 1976 + .../lldb.SBFunction-class.html | 963 + www/python_reference/lldb.SBHostOS-class.html | 538 + .../lldb.SBInputReader-class.html | 569 + .../lldb.SBInstruction-class.html | 981 + .../lldb.SBInstructionList-class.html | 692 + .../lldb.SBLaunchInfo-class.html | 902 + .../lldb.SBLineEntry-class.html | 868 + .../lldb.SBListener-class.html | 759 + www/python_reference/lldb.SBModule-class.html | 1986 + ...b.SBModule.compile_units_access-class.html | 305 + .../lldb.SBModule.sections_access-class.html | 304 + .../lldb.SBModule.symbols_access-class.html | 304 + ....symbols_access.re_compile_type-class.html | 520 + .../lldb.SBModuleSpec-class.html | 760 + .../lldb.SBModuleSpecList-class.html | 610 + .../lldb.SBProcess-class.html | 2394 + .../lldb.SBProcess.threads_access-class.html | 279 + .../lldb.SBSection-class.html | 1064 + .../lldb.SBSourceManager-class.html | 489 + www/python_reference/lldb.SBStream-class.html | 708 + .../lldb.SBStringList-class.html | 580 + www/python_reference/lldb.SBSymbol-class.html | 979 + .../lldb.SBSymbolContext-class.html | 986 + .../lldb.SBSymbolContextList-class.html | 924 + www/python_reference/lldb.SBTarget-class.html | 2663 + .../lldb.SBTarget.modules_access-class.html | 279 + www/python_reference/lldb.SBThread-class.html | 1663 + .../lldb.SBThread.frames_access-class.html | 279 + www/python_reference/lldb.SBType-class.html | 1592 + .../lldb.SBTypeCategory-class.html | 1573 + ...ategory.formatters_access_class-class.html | 285 + .../lldb.SBTypeFilter-class.html | 767 + .../lldb.SBTypeFormat-class.html | 721 + .../lldb.SBTypeList-class.html | 568 + .../lldb.SBTypeMember-class.html | 774 + .../lldb.SBTypeNameSpecifier-class.html | 695 + .../lldb.SBTypeSummary-class.html | 920 + .../lldb.SBTypeSynthetic-class.html | 825 + www/python_reference/lldb.SBValue-class.html | 2823 + .../lldb.SBValueList-class.html | 692 + .../lldb.SBWatchpoint-class.html | 895 + .../lldb.declaration-class.html | 247 + .../lldb.embedded_interpreter-module.html | 209 + .../lldb.embedded_interpreter-pysrc.html | 333 + ...embedded_interpreter.SimpleREPL-class.html | 347 + .../lldb.formatters-module.html | 173 + .../lldb.formatters-pysrc.html | 122 + .../lldb.formatters.Logger-module.html | 180 + .../lldb.formatters.Logger-pysrc.html | 346 + ...db.formatters.Logger.FileLogger-class.html | 195 + .../lldb.formatters.Logger.Logger-class.html | 229 + ...ldb.formatters.Logger.NopLogger-class.html | 194 + ....formatters.Logger.StdoutLogger-class.html | 194 + ...ldb.formatters.attrib_fromdict-module.html | 164 + ...lldb.formatters.attrib_fromdict-pysrc.html | 282 + ...b_fromdict.AttributesDictionary-class.html | 249 + .../lldb.formatters.cache-module.html | 164 + .../lldb.formatters.cache-pysrc.html | 162 + .../lldb.formatters.cache.Cache-class.html | 198 + .../lldb.formatters.cpp-module.html | 166 + .../lldb.formatters.cpp-pysrc.html | 123 + ...b.formatters.cpp.gnu_libstdcpp-module.html | 195 + ...db.formatters.cpp.gnu_libstdcpp-pysrc.html | 1121 + ..._libstdcpp.StdListSynthProvider-class.html | 328 + ...u_libstdcpp.StdMapSynthProvider-class.html | 347 + ...ibstdcpp.StdVectorSynthProvider-class.html | 262 + .../lldb.formatters.cpp.libcxx-module.html | 396 + .../lldb.formatters.cpp.libcxx-pysrc.html | 1932 + ...p.libcxx.stddeque_SynthProvider-class.html | 245 + ...pp.libcxx.stdlist_SynthProvider-class.html | 311 + ...atters.cpp.libcxx.stdlist_entry-class.html | 347 + ...ers.cpp.libcxx.stdlist_iterator-class.html | 212 + ...cpp.libcxx.stdmap_SynthProvider-class.html | 278 + ...ters.cpp.libcxx.stdmap_iterator-class.html | 264 + ...cpp.libcxx.stdmap_iterator_node-class.html | 405 + ...bcxx.stdsharedptr_SynthProvider-class.html | 229 + ....libcxx.stdvector_SynthProvider-class.html | 229 + .../lldb.formatters.metrics-module.html | 192 + .../lldb.formatters.metrics-pysrc.html | 355 + ...lldb.formatters.metrics.Counter-class.html | 178 + ...lldb.formatters.metrics.Metrics-class.html | 247 + ....metrics.MetricsPrinter_Compact-class.html | 162 + ....metrics.MetricsPrinter_Verbose-class.html | 162 + ....formatters.metrics.TimeMetrics-class.html | 215 + www/python_reference/lldb.runtime-module.html | 133 + www/python_reference/lldb.runtime-pysrc.html | 122 + www/python_reference/lldb.utils-module.html | 164 + www/python_reference/lldb.utils-pysrc.html | 122 + .../lldb.utils.symbolication-module.html | 304 + .../lldb.utils.symbolication-pysrc.html | 1290 + ...ldb.utils.symbolication.Address-class.html | 245 + .../lldb.utils.symbolication.Image-class.html | 397 + ...ldb.utils.symbolication.Section-class.html | 277 + ...tils.symbolication.Symbolicator-class.html | 230 + www/python_reference/lldb.value-class.html | 1038 + .../lldb.value_iter-class.html | 274 + www/python_reference/module-tree.html | 134 + www/python_reference/redirect.html | 38 + www/python_reference/toc-_lldb'-module.html | 5062 ++ www/python_reference/toc-everything.html | 5787 ++ www/python_reference/toc-lldb-module.html | 650 + .../toc-lldb.embedded_interpreter-module.html | 36 + .../toc-lldb.formatters-module.html | 36 + .../toc-lldb.formatters.Logger-module.html | 36 + ...ldb.formatters.attrib_fromdict-module.html | 33 + .../toc-lldb.formatters.cache-module.html | 33 + .../toc-lldb.formatters.cpp-module.html | 36 + ...b.formatters.cpp.gnu_libstdcpp-module.html | 44 + ...toc-lldb.formatters.cpp.libcxx-module.html | 64 + .../toc-lldb.formatters.metrics-module.html | 37 + .../toc-lldb.runtime-module.html | 33 + .../toc-lldb.utils-module.html | 36 + .../toc-lldb.utils.symbolication-module.html | 43 + www/python_reference/toc.html | 48 + .../uml_class_diagram_for_lldb_dec.gif | Bin 0 -> 1863 bytes .../uml_class_diagram_for_lldb_emb.gif | Bin 0 -> 14440 bytes .../uml_class_diagram_for_lldb_sba.gif | Bin 0 -> 10653 bytes .../uml_class_diagram_for_lldb_sba_2.gif | Bin 0 -> 10714 bytes .../uml_class_diagram_for_lldb_sbb.gif | Bin 0 -> 11267 bytes .../uml_class_diagram_for_lldb_sbb_2.gif | Bin 0 -> 2707 bytes .../uml_class_diagram_for_lldb_sbb_3.gif | Bin 0 -> 10014 bytes .../uml_class_diagram_for_lldb_sbb_4.gif | Bin 0 -> 8939 bytes .../uml_class_diagram_for_lldb_sbb_5.gif | Bin 0 -> 9883 bytes .../uml_class_diagram_for_lldb_sbc.gif | Bin 0 -> 15539 bytes .../uml_class_diagram_for_lldb_sbc_2.gif | Bin 0 -> 9424 bytes .../uml_class_diagram_for_lldb_sbc_3.gif | Bin 0 -> 15211 bytes .../uml_class_diagram_for_lldb_sbc_4.gif | Bin 0 -> 9361 bytes .../uml_class_diagram_for_lldb_sbd.gif | Bin 0 -> 12043 bytes .../uml_class_diagram_for_lldb_sbd_2.gif | Bin 0 -> 8169 bytes .../uml_class_diagram_for_lldb_sbd_3.gif | Bin 0 -> 9560 bytes .../uml_class_diagram_for_lldb_sbe.gif | Bin 0 -> 9315 bytes .../uml_class_diagram_for_lldb_sbe_2.gif | Bin 0 -> 9906 bytes .../uml_class_diagram_for_lldb_sbe_3.gif | Bin 0 -> 12010 bytes .../uml_class_diagram_for_lldb_sbf.gif | Bin 0 -> 9550 bytes .../uml_class_diagram_for_lldb_sbf_2.gif | Bin 0 -> 9469 bytes .../uml_class_diagram_for_lldb_sbf_3.gif | Bin 0 -> 10946 bytes .../uml_class_diagram_for_lldb_sbf_4.gif | Bin 0 -> 10830 bytes .../uml_class_diagram_for_lldb_sbh.gif | Bin 0 -> 8458 bytes .../uml_class_diagram_for_lldb_sbi.gif | Bin 0 -> 8296 bytes .../uml_class_diagram_for_lldb_sbi_2.gif | Bin 0 -> 10485 bytes .../uml_class_diagram_for_lldb_sbi_3.gif | Bin 0 -> 9050 bytes .../uml_class_diagram_for_lldb_sbl.gif | Bin 0 -> 10154 bytes .../uml_class_diagram_for_lldb_sbl_2.gif | Bin 0 -> 9663 bytes .../uml_class_diagram_for_lldb_sbl_3.gif | Bin 0 -> 10398 bytes .../uml_class_diagram_for_lldb_sbm.gif | Bin 0 -> 11632 bytes .../uml_class_diagram_for_lldb_sbm_2.gif | Bin 0 -> 3196 bytes .../uml_class_diagram_for_lldb_sbm_3.gif | Bin 0 -> 3129 bytes .../uml_class_diagram_for_lldb_sbm_4.gif | Bin 0 -> 3162 bytes .../uml_class_diagram_for_lldb_sbm_5.gif | Bin 0 -> 6716 bytes .../uml_class_diagram_for_lldb_sbm_6.gif | Bin 0 -> 9380 bytes .../uml_class_diagram_for_lldb_sbm_7.gif | Bin 0 -> 10651 bytes .../uml_class_diagram_for_lldb_sbp.gif | Bin 0 -> 13849 bytes .../uml_class_diagram_for_lldb_sbp_2.gif | Bin 0 -> 2782 bytes .../uml_class_diagram_for_lldb_sbs.gif | Bin 0 -> 9921 bytes .../uml_class_diagram_for_lldb_sbs_2.gif | Bin 0 -> 6581 bytes .../uml_class_diagram_for_lldb_sbs_3.gif | Bin 0 -> 8891 bytes .../uml_class_diagram_for_lldb_sbs_4.gif | Bin 0 -> 9303 bytes .../uml_class_diagram_for_lldb_sbs_5.gif | Bin 0 -> 11075 bytes .../uml_class_diagram_for_lldb_sbs_6.gif | Bin 0 -> 10229 bytes .../uml_class_diagram_for_lldb_sbs_7.gif | Bin 0 -> 10816 bytes .../uml_class_diagram_for_lldb_sbt.gif | Bin 0 -> 15030 bytes .../uml_class_diagram_for_lldb_sbt_10.gif | Bin 0 -> 8035 bytes .../uml_class_diagram_for_lldb_sbt_11.gif | Bin 0 -> 10553 bytes .../uml_class_diagram_for_lldb_sbt_12.gif | Bin 0 -> 9530 bytes .../uml_class_diagram_for_lldb_sbt_13.gif | Bin 0 -> 11452 bytes .../uml_class_diagram_for_lldb_sbt_14.gif | Bin 0 -> 10565 bytes .../uml_class_diagram_for_lldb_sbt_2.gif | Bin 0 -> 2744 bytes .../uml_class_diagram_for_lldb_sbt_3.gif | Bin 0 -> 11640 bytes .../uml_class_diagram_for_lldb_sbt_4.gif | Bin 0 -> 2685 bytes .../uml_class_diagram_for_lldb_sbt_5.gif | Bin 0 -> 10717 bytes .../uml_class_diagram_for_lldb_sbt_6.gif | Bin 0 -> 12181 bytes .../uml_class_diagram_for_lldb_sbt_7.gif | Bin 0 -> 4552 bytes .../uml_class_diagram_for_lldb_sbt_8.gif | Bin 0 -> 11066 bytes .../uml_class_diagram_for_lldb_sbt_9.gif | Bin 0 -> 9342 bytes .../uml_class_diagram_for_lldb_sbv.gif | Bin 0 -> 13367 bytes .../uml_class_diagram_for_lldb_sbv_2.gif | Bin 0 -> 8635 bytes .../uml_class_diagram_for_lldb_sbw.gif | Bin 0 -> 8458 bytes .../uml_class_diagram_for_lldb_val.gif | Bin 0 -> 6904 bytes .../uml_class_diagram_for_lldb_val_2.gif | Bin 0 -> 2192 bytes www/remote.html | 145 + www/scripting.html | 586 + www/sidebar.incl | 58 + www/source.html | 87 + www/status.html | 209 + www/style.css | 161 + www/symbolication.html | 363 + www/symbols.html | 345 + www/test.html | 132 + www/troubleshooting.html | 89 + www/tutorial.html | 726 + www/varformats.html | 1331 + 3406 files changed, 571528 insertions(+), 2248 deletions(-) create mode 100644 .arcconfig create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CODE_OWNERS.txt create mode 100644 INSTALL.txt create mode 100644 Makefile create mode 100644 cmake/LLDBDependencies.cmake create mode 100644 cmake/modules/AddLLDB.cmake create mode 100644 cmake/modules/LLDBConfig.cmake create mode 100644 cmake/modules/LLDBStandalone.cmake create mode 100644 cmake/platforms/Android.cmake create mode 100644 docs/CMakeLists.txt create mode 100644 docs/building-with-debug-llvm.txt create mode 100644 docs/code-signing.txt create mode 100644 docs/doxygen.cfg.in create mode 100644 docs/doxygen.footer create mode 100644 docs/doxygen.header create mode 100644 docs/doxygen.intro create mode 100644 docs/lldb-for-gdb-users.txt create mode 100644 docs/lldb-gdb-remote.txt create mode 100644 docs/testsuite/2010-10-19-14_10_49.059609/TestSettings.SettingsCommandTestCase.test_set_output_path.log create mode 100644 docs/testsuite/a-detailed-walkthrough.txt create mode 100644 docs/testsuite/best-practices.txt create mode 100644 examples/customization/bin-utils/.lldbinit create mode 100644 examples/customization/bin-utils/README create mode 100644 examples/customization/bin-utils/binutils.py create mode 100644 examples/customization/import-python/README create mode 100644 examples/customization/import-python/importcmd.py create mode 100644 examples/customization/pwd-cd-and-system/.lldbinit create mode 100644 examples/customization/pwd-cd-and-system/README create mode 100644 examples/customization/pwd-cd-and-system/utils.py create mode 100644 examples/darwin/heap_find/heap.py create mode 100644 examples/darwin/heap_find/heap/Makefile create mode 100644 examples/darwin/heap_find/heap/heap_find.cpp create mode 100644 examples/functions/Makefile create mode 100644 examples/functions/main.cpp create mode 100644 examples/interposing/darwin/fd_interposing/FDInterposing.cpp create mode 100644 examples/interposing/darwin/fd_interposing/Makefile create mode 100644 examples/lookup/Makefile create mode 100644 examples/lookup/main.cpp create mode 100644 examples/plugins/commands/fooplugin.cpp create mode 100644 examples/python/cmdtemplate.py create mode 100755 examples/python/crashlog.py create mode 100755 examples/python/delta.py create mode 100644 examples/python/diagnose_nsstring.py create mode 100644 examples/python/diagnose_unwind.py create mode 100755 examples/python/dict_utils.py create mode 100755 examples/python/disasm-stress-test.py create mode 100755 examples/python/disasm.py create mode 100755 examples/python/file_extract.py create mode 100755 examples/python/gdb_disassemble.py create mode 100755 examples/python/gdbremote.py create mode 100755 examples/python/globals.py create mode 100644 examples/python/jump.py create mode 100644 examples/python/lldb_module_utils.py create mode 100644 examples/python/lldbtk.py create mode 100755 examples/python/mach_o.py create mode 100755 examples/python/memory.py create mode 100644 examples/python/operating_system.py create mode 100755 examples/python/performance.py create mode 100755 examples/python/process_events.py create mode 100644 examples/python/pytracer.py create mode 100755 examples/python/sbvalue.py create mode 100644 examples/python/scripted_step.py create mode 100644 examples/python/sources.py create mode 100755 examples/python/stacks.py create mode 100755 examples/python/symbolication.py create mode 100755 examples/python/types.py create mode 100644 examples/python/x86_64_linux_target_definition.py create mode 100644 examples/python/x86_64_qemu_target_definition.py create mode 100644 examples/python/x86_64_target_definition.py create mode 100644 examples/scripting/dictionary.c create mode 100755 examples/scripting/tree_utils.py create mode 100644 examples/summaries/cocoa/CFArray.py create mode 100644 examples/summaries/cocoa/CFBag.py create mode 100644 examples/summaries/cocoa/CFBinaryHeap.py create mode 100644 examples/summaries/cocoa/CFBitVector.py create mode 100644 examples/summaries/cocoa/CFDictionary.py create mode 100644 examples/summaries/cocoa/CFString.py create mode 100644 examples/summaries/cocoa/Class.py create mode 100644 examples/summaries/cocoa/Logger.py create mode 100644 examples/summaries/cocoa/NSBundle.py create mode 100644 examples/summaries/cocoa/NSData.py create mode 100644 examples/summaries/cocoa/NSDate.py create mode 100644 examples/summaries/cocoa/NSException.py create mode 100644 examples/summaries/cocoa/NSIndexSet.py create mode 100644 examples/summaries/cocoa/NSMachPort.py create mode 100644 examples/summaries/cocoa/NSNotification.py create mode 100644 examples/summaries/cocoa/NSNumber.py create mode 100644 examples/summaries/cocoa/NSSet.py create mode 100644 examples/summaries/cocoa/NSURL.py create mode 100644 examples/summaries/cocoa/Selector.py create mode 100644 examples/summaries/cocoa/attrib_fromdict.py create mode 100644 examples/summaries/cocoa/cache.py create mode 100644 examples/summaries/cocoa/metrics.py create mode 100644 examples/summaries/cocoa/objc_runtime.py create mode 100644 examples/summaries/essentials create mode 100644 examples/summaries/lldb create mode 100644 examples/summaries/objc.py create mode 100644 examples/summaries/pysummary.py create mode 100644 examples/summaries/sp_cp.py create mode 100644 examples/summaries/unicode_strings.py create mode 100644 examples/synthetic/bitfield/example.py create mode 100644 examples/synthetic/bitfield/program.cpp create mode 100644 examples/synthetic/gnu_libstdcpp.py create mode 100644 examples/synthetic/libcxx.py create mode 100644 examples/synthetic/unordered_multi.py create mode 100644 examples/test/.lldb-loggings create mode 100644 examples/test/.lldb-pre-post-flight create mode 100644 examples/test/.lldb-pre-post-flight.bad create mode 100644 examples/test/.lldbtest-config create mode 100644 examples/test/.lldbtest-config2 create mode 100644 examples/test/lldbtest-stderr create mode 100644 examples/test/lldbtest-stdout create mode 100644 examples/test/tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dsym.txt create mode 100644 examples/test/tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dwarf.txt create mode 100644 examples/test/usage-config create mode 100644 examples/test/usage-lldb-loggings create mode 100644 examples/test/usage-pre-post-flight create mode 100644 include/Makefile create mode 100644 include/lldb/Host/android/Android.h create mode 100644 include/lldb/Host/android/Config.h create mode 100644 include/lldb/Host/android/HostInfoAndroid.h create mode 100644 include/lldb/Host/android/ProcessLauncherAndroid.h create mode 100644 include/lldb/Host/linux/AbstractSocket.h create mode 100644 include/lldb/Host/linux/Config.h create mode 100644 include/lldb/Host/linux/HostInfoLinux.h create mode 100644 include/lldb/Host/linux/HostThreadLinux.h create mode 100644 include/lldb/Host/linux/Personality.h create mode 100644 include/lldb/Host/linux/Ptrace.h create mode 100644 include/lldb/Host/linux/Signalfd.h create mode 100644 include/lldb/Host/linux/Uio.h create mode 100644 include/lldb/Host/macosx/Config.h create mode 100644 include/lldb/Host/macosx/HostInfoMacOSX.h create mode 100644 include/lldb/Host/macosx/HostThreadMacOSX.h create mode 100644 include/lldb/Host/mingw/Config.h create mode 100644 include/lldb/Host/msvc/Config.h create mode 100644 include/lldb/Host/windows/AutoHandle.h create mode 100644 include/lldb/Host/windows/ConnectionGenericFileWindows.h create mode 100644 include/lldb/Host/windows/HostInfoWindows.h create mode 100644 include/lldb/Host/windows/HostProcessWindows.h create mode 100644 include/lldb/Host/windows/HostThreadWindows.h create mode 100644 include/lldb/Host/windows/LockFileWindows.h create mode 100644 include/lldb/Host/windows/PipeWindows.h create mode 100644 include/lldb/Host/windows/ProcessLauncherWindows.h create mode 100644 include/lldb/Host/windows/editlinewin.h create mode 100644 include/lldb/Host/windows/win32.h create mode 100644 include/lldb/Host/windows/windows.h create mode 100644 include/lldb/Makefile create mode 100644 lib/Makefile create mode 100644 lit/CMakeLists.txt create mode 100644 lit/Unit/lit.cfg create mode 100644 lit/Unit/lit.site.cfg.in create mode 100644 lit/lit.cfg create mode 100644 lit/lit.site.cfg.in create mode 100644 lldb.xcodeproj/project.pbxproj create mode 100644 lldb.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/LLDB.xcscheme create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/Run Testsuite.xcscheme create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/darwin-debug.xcscheme create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/desktop.xcscheme create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/launcherRootXPCService.xcscheme create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/launcherXPCService.xcscheme create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/lldb-gtest.xcscheme create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/lldb-python-test-suite.xcscheme create mode 100644 lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme create mode 100644 lldb.xcworkspace/contents.xcworkspacedata create mode 100644 packages/Python/lldbsuite/__init__.py create mode 100644 packages/Python/lldbsuite/support/__init__.py create mode 100644 packages/Python/lldbsuite/support/fs.py create mode 100644 packages/Python/lldbsuite/support/seven.py create mode 100644 packages/Python/lldbsuite/support/sockutil.py create mode 100644 packages/Python/lldbsuite/test/.categories create mode 100644 packages/Python/lldbsuite/test/Makefile create mode 100644 packages/Python/lldbsuite/test/README-TestSuite create mode 100644 packages/Python/lldbsuite/test/__init__.py create mode 100644 packages/Python/lldbsuite/test/android/platform/Makefile create mode 100644 packages/Python/lldbsuite/test/android/platform/TestDefaultCacheLineSize.py create mode 100644 packages/Python/lldbsuite/test/android/platform/main.cpp create mode 100644 packages/Python/lldbsuite/test/api/check_public_api_headers/Makefile create mode 100644 packages/Python/lldbsuite/test/api/check_public_api_headers/TestPublicAPIHeaders.py create mode 100644 packages/Python/lldbsuite/test/api/check_public_api_headers/main.cpp.template create mode 100644 packages/Python/lldbsuite/test/api/multiple-debuggers/.categories create mode 100644 packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile create mode 100644 packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py create mode 100644 packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp create mode 100644 packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/Makefile create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/common.h create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/driver.cpp create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/inferior.cpp create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/listener_test.cpp create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/lldb-headers.h create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/test_breakpoint_callback.cpp create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_description.cpp create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_process_state.cpp create mode 100644 packages/Python/lldbsuite/test/api/multithreaded/test_listener_resume.cpp create mode 100644 packages/Python/lldbsuite/test/arm_emulation/TestEmulations.py create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-10-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-11-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-12-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-3-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-4-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-4-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-5-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-5-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-6-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-6-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-7-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-7-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-8-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-8-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-9-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-bic-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-bic-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-3-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-10-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-11-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-12-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-3-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-4-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-4-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-5-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-5-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-6-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-6-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-7-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-7-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-8-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-8-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-9-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrh-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrsh-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrsh-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-10-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-11-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-12-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-13-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-14-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-15-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-16-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-17-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-18-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-19-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-20-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-21-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-22-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-23-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-24-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-25-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-26-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-27-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-28-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-29-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-3-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-30-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-31-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-4-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-4-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-5-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-5-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-6-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-6-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-7-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-8-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-9-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-moveq-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-movs-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-3-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-4-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-4-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-3-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-3-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-4-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-4-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-5-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strb-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strb-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strbt-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strd-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strt-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-10-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-2-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-3-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-4-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-4-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-5-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-5-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-6-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-6-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-8-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-9-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-1-arm.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-10-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-4-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-5-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-6-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-8-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-9-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-1-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-2-thumb.dat create mode 100644 packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-3-thumb.dat create mode 100644 packages/Python/lldbsuite/test/attic/dotest.pl create mode 100644 packages/Python/lldbsuite/test/attic/tester.py create mode 100644 packages/Python/lldbsuite/test/bench-history create mode 100644 packages/Python/lldbsuite/test/bench.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/continue/Makefile create mode 100644 packages/Python/lldbsuite/test/benchmarks/continue/TestBenchmarkContinue.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/continue/main.cpp create mode 100644 packages/Python/lldbsuite/test/benchmarks/disassembly/TestDisassembly.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/disassembly/TestDoAttachThenDisassembly.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/disassembly/TestXcode41Vs42GDBDisassembly.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/expression/Makefile create mode 100644 packages/Python/lldbsuite/test/benchmarks/expression/TestExpressionCmd.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/expression/TestRepeatedExprs.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/expression/main.cpp create mode 100644 packages/Python/lldbsuite/test/benchmarks/frame_variable/TestFrameVariableResponse.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/libcxxlist/Makefile create mode 100644 packages/Python/lldbsuite/test/benchmarks/libcxxlist/TestBenchmarkLibcxxList.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/libcxxlist/main.cpp create mode 100644 packages/Python/lldbsuite/test/benchmarks/libcxxmap/Makefile create mode 100644 packages/Python/lldbsuite/test/benchmarks/libcxxmap/TestBenchmarkLibcxxMap.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/libcxxmap/main.cpp create mode 100644 packages/Python/lldbsuite/test/benchmarks/startup/TestStartupDelays.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/stepping/TestSteppingSpeed.py create mode 100644 packages/Python/lldbsuite/test/benchmarks/turnaround/TestCompileRunToBreakpointTurnaround.py create mode 100644 packages/Python/lldbsuite/test/configuration.py create mode 100644 packages/Python/lldbsuite/test/crashinfo.c create mode 100644 packages/Python/lldbsuite/test/curses_results.py create mode 100644 packages/Python/lldbsuite/test/dosep.py create mode 100644 packages/Python/lldbsuite/test/dotest.py create mode 100644 packages/Python/lldbsuite/test/dotest_args.py create mode 100644 packages/Python/lldbsuite/test/dotest_channels.py create mode 100644 packages/Python/lldbsuite/test/driver/batch_mode/Makefile create mode 100644 packages/Python/lldbsuite/test/driver/batch_mode/TestBatchMode.py create mode 100644 packages/Python/lldbsuite/test/driver/batch_mode/main.c create mode 100644 packages/Python/lldbsuite/test/example/TestSequenceFunctions.py create mode 100644 packages/Python/lldbsuite/test/expression_command/.categories create mode 100644 packages/Python/lldbsuite/test/expression_command/call-function/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/call-function/TestCallStdStringFunction.py create mode 100644 packages/Python/lldbsuite/test/expression_command/call-function/TestCallStopAndContinue.py create mode 100644 packages/Python/lldbsuite/test/expression_command/call-function/TestCallUserDefinedFunction.py create mode 100644 packages/Python/lldbsuite/test/expression_command/call-function/main.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/call-restarts/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py create mode 100644 packages/Python/lldbsuite/test/expression_command/call-restarts/lotta-signals.c create mode 100644 packages/Python/lldbsuite/test/expression_command/call-throws/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/call-throws/TestCallThatThrows.py create mode 100644 packages/Python/lldbsuite/test/expression_command/call-throws/call-throws.m create mode 100644 packages/Python/lldbsuite/test/expression_command/char/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py create mode 100644 packages/Python/lldbsuite/test/expression_command/char/main.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/expr-in-syscall/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/expr-in-syscall/TestExpressionInSyscall.py create mode 100644 packages/Python/lldbsuite/test/expression_command/expr-in-syscall/main.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/formatters/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/formatters/TestFormatters.py create mode 100644 packages/Python/lldbsuite/test/expression_command/formatters/foosynth.py create mode 100644 packages/Python/lldbsuite/test/expression_command/formatters/formatters.py create mode 100644 packages/Python/lldbsuite/test/expression_command/formatters/main.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/issue_11588/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/issue_11588/Test11588.py create mode 100644 packages/Python/lldbsuite/test/expression_command/issue_11588/main.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/issue_11588/s11588.py create mode 100644 packages/Python/lldbsuite/test/expression_command/macros/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/macros/TestMacros.py create mode 100644 packages/Python/lldbsuite/test/expression_command/macros/macro1.h create mode 100644 packages/Python/lldbsuite/test/expression_command/macros/macro2.h create mode 100644 packages/Python/lldbsuite/test/expression_command/macros/main.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/options/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/options/TestExprOptions.py create mode 100644 packages/Python/lldbsuite/test/expression_command/options/foo.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/options/main.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/TestPersistObjCPointeeType.py create mode 100644 packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/main.m create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/TestPersistentPtrUpdate.py create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/main.c create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_types/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_types/TestNestedPersistentTypes.py create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_types/TestPersistentTypes.py create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_types/main.c create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_variables/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_variables/TestPersistentVariables.py create mode 100644 packages/Python/lldbsuite/test/expression_command/persistent_variables/main.c create mode 100644 packages/Python/lldbsuite/test/expression_command/po_verbosity/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/po_verbosity/TestPoVerbosity.py create mode 100644 packages/Python/lldbsuite/test/expression_command/po_verbosity/main.m create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_8638051/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_8638051/Test8638051.py create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_8638051/main.c create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_9531204/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_9531204/TestPrintfAfterUp.py create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_9531204/main.c create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_9673664/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_9673664/TestExprHelpExamples.py create mode 100644 packages/Python/lldbsuite/test/expression_command/radar_9673664/main.c create mode 100644 packages/Python/lldbsuite/test/expression_command/test/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/test/TestExprs.py create mode 100644 packages/Python/lldbsuite/test/expression_command/test/TestExprs2.py create mode 100644 packages/Python/lldbsuite/test/expression_command/test/main.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/timeout/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/timeout/TestCallWithTimeout.py create mode 100644 packages/Python/lldbsuite/test/expression_command/timeout/wait-a-while.cpp create mode 100644 packages/Python/lldbsuite/test/expression_command/two-files/Makefile create mode 100644 packages/Python/lldbsuite/test/expression_command/two-files/TestObjCTypeQueryFromOtherCompileUnit.py create mode 100644 packages/Python/lldbsuite/test/expression_command/two-files/foo.m create mode 100644 packages/Python/lldbsuite/test/expression_command/two-files/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/abbreviation/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/abbreviation/TestAbbreviations.py create mode 100644 packages/Python/lldbsuite/test/functionalities/abbreviation/TestCommonShortSpellings.py create mode 100644 packages/Python/lldbsuite/test/functionalities/alias/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/archives/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/archives/README create mode 100644 packages/Python/lldbsuite/test/functionalities/archives/TestBSDArchives.py create mode 100644 packages/Python/lldbsuite/test/functionalities/archives/a.c create mode 100644 packages/Python/lldbsuite/test/functionalities/archives/b.c create mode 100644 packages/Python/lldbsuite/test/functionalities/archives/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/asan/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/asan/TestMemoryHistory.py create mode 100644 packages/Python/lldbsuite/test/functionalities/asan/TestReportData.py create mode 100644 packages/Python/lldbsuite/test/functionalities/asan/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/attach_resume/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/attach_resume/TestAttachResume.py create mode 100644 packages/Python/lldbsuite/test/functionalities/attach_resume/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/avoids-fd-leak/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/avoids-fd-leak/TestFdLeak.py create mode 100644 packages/Python/lldbsuite/test/functionalities/avoids-fd-leak/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/backticks/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/backticks/TestBackticksWithoutATarget.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestAddressBreakpoints.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/a.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/b.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/TestBreakpointIDs.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/TestBreakpointIgnoreCount.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/TestBreakpointLanguage.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/a.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/b.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/TestBreakpointLocations.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/TestBreakpointOptions.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/foo.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/TestCompDirSymLink.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/TestConsecutiveBreakpoints.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/TestCPPBreakpointLocations.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/TestCPPExceptionBreakpoint.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/TestBreakpointsWithNoTargets.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/TestInlinedBreakpoints.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/basic_type.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/int.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/objc/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/objc/TestObjCBreakpoints.py create mode 100644 packages/Python/lldbsuite/test/functionalities/breakpoint/objc/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/command_history/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/command_history/TestCommandHistory.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_regex/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/command_regex/TestCommandRegex.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/TestCommandScript.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/bug11569.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/TestImport.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/bar/bar.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/bar/barutil.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/dummymodule.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/foo/bar/foobar.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/foo/foo.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/foo/foo2.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/TestRdar12586188.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/fail12586188.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/fail212586188.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/TPunitA.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/TPunitB.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/__init__.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/mysto.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/py_import create mode 100644 packages/Python/lldbsuite/test/functionalities/command_script/welcome.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_source/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/command_source/.lldb create mode 100644 packages/Python/lldbsuite/test/functionalities/command_source/TestCommandSource.py create mode 100644 packages/Python/lldbsuite/test/functionalities/command_source/my.py create mode 100644 packages/Python/lldbsuite/test/functionalities/completion/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/completion/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py create mode 100644 packages/Python/lldbsuite/test/functionalities/completion/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/conditional_break/.lldb create mode 100644 packages/Python/lldbsuite/test/functionalities/conditional_break/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/conditional_break/TestConditionalBreak.py create mode 100644 packages/Python/lldbsuite/test/functionalities/conditional_break/conditional_break.py create mode 100644 packages/Python/lldbsuite/test/functionalities/conditional_break/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/TestFormattersBoolRefPtr.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/main.mm create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/TestCompactVectors.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/TestDataFormatterAdv.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/TestDataFormatterDisabling.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/TestDataFormatterEnumFormat.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/TestDataFormatterGlobals.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/TestDataFormatterNamedSummaries.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/TestDataFormatterNSString.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/TestFormattersOneIsSingular.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/TestPtrToArrayFormatting.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/ftsp.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/TestDataFormatterScript.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/TestDataFormatterSkipSummary.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/TestDataFormatterSmartArray.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/TestInitializerList.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/TestDataFormatterLibcxxList.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/TestDataFormatterLibcxxListLoop.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/TestDataFormatterLibccMultiMap.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/TestDataFormatterUnordered.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/TestDataFormatterLibcxxVBool.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/TestDataFormatterLibcxxVector.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/TestDataFormatterStdIterator.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/TestDataFormatterStdList.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/TestDataFormatterStdMap.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/TestDataFormatterStdVBool.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/TestDataFormatterStdVector.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/TestDataFormatterSynthVal.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/myIntSynthProvider.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/TestDumpDynamic.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/TestFormatPropagation.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/TestFrameFormatSmallStruct.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/TestDataFormatterHexCaps.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/TestDataFormatterLanguageCategoryUpdates.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/TestNSArraySynthetic.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/TestNSDictionarySynthetic.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/TestNSSetSynthetic.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/TestFormattersOsType.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/main.mm create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/TestPtrRef2Typedef.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/TestDataFormatterRefPtrRecursion.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/TestSetValueFromCString.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/TestStringPrinter.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/Test-rdar-9974002.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/TestSyntheticCapping.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/fooSynthProvider.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/TestSyntheticFilterRecompute.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/TestTypedefArray.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/TestUserFormatVsSummary.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/TestVarInAggregateMisuse.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/TestDataFormatterVarScriptFormatting.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/helperfunc.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py create mode 100644 packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/dead-strip/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/dead-strip/TestDeadStrip.py create mode 100644 packages/Python/lldbsuite/test/functionalities/dead-strip/cmds.txt create mode 100644 packages/Python/lldbsuite/test/functionalities/dead-strip/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/disassembly/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/disassembly/TestDisassembleBreakpoint.py create mode 100644 packages/Python/lldbsuite/test/functionalities/disassembly/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/TestDynamicValueChildCount.py create mode 100644 packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/pass-to-base.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/embedded_interpreter/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/embedded_interpreter/TestConvenienceVariables.py create mode 100644 packages/Python/lldbsuite/test/functionalities/embedded_interpreter/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/exec/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/exec/TestExec.py create mode 100644 packages/Python/lldbsuite/test/functionalities/exec/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/.categories create mode 100644 packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/TestExprDoesntBlock.py create mode 100644 packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/locking.c create mode 100644 packages/Python/lldbsuite/test/functionalities/fat_archives/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/fat_archives/TestFatArchives.py create mode 100644 packages/Python/lldbsuite/test/functionalities/fat_archives/a.c create mode 100644 packages/Python/lldbsuite/test/functionalities/fat_archives/a.h create mode 100644 packages/Python/lldbsuite/test/functionalities/fat_archives/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/format/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/format/TestFormats.py create mode 100644 packages/Python/lldbsuite/test/functionalities/format/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-assert/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-assert/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-changed/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-changed/TestInferiorChanged.py create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-changed/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-changed/main2.c create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-crashing/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-crashing/TestInferiorCrashing.py create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-crashing/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferior.py create mode 100644 packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/inline-stepping/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/inline-stepping/TestInlineStepping.py create mode 100644 packages/Python/lldbsuite/test/functionalities/inline-stepping/calling.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/jitloader_gdb/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/jitloader_gdb/TestJITLoaderGDB.py create mode 100644 packages/Python/lldbsuite/test/functionalities/jitloader_gdb/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/TestLaunchWithShellExpand.py create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file1.txt create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file2.txt create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file3.txt create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file4.txy create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file5.tyx create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/foo bar create mode 100644 packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/a.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/a.mk create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/b.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/b.mk create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/c.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/c.mk create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/cmds.txt create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/d.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/d.mk create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/hidden/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/hidden/d.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/load_unload/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/longjmp/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/longjmp/TestLongjmp.py create mode 100644 packages/Python/lldbsuite/test/functionalities/longjmp/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/memory/read/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/memory/read/TestMemoryRead.py create mode 100644 packages/Python/lldbsuite/test/functionalities/memory/read/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/TestIndexVariable.py create mode 100644 packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/nosucharch/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/nosucharch/TestNoSuchArch.py create mode 100644 packages/Python/lldbsuite/test/functionalities/nosucharch/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/TestImageListMultiArchitecture.py create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-freebsd-10.0-x86_64-clang-3.3 create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-freebsd-10.0-x86_64-gcc-4.7.3 create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-netbsd-6.1-x86_64-gcc-4.5.3 create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-ubuntu-14.04-x86_64-clang-3.5pre create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-ubuntu-14.04-x86_64-gcc-4.8.2 create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-unknown-kalimba_arch4-kcc-36 create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-unknown-kalimba_arch5-kcc-39 create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello.c create mode 100644 packages/Python/lldbsuite/test/functionalities/object-file/bin/hello.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/paths/TestPaths.py create mode 100644 packages/Python/lldbsuite/test/functionalities/platform/TestPlatformCommand.py create mode 100644 packages/Python/lldbsuite/test/functionalities/plugins/commands/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/plugins/commands/TestPluginCommands.py create mode 100644 packages/Python/lldbsuite/test/functionalities/plugins/commands/plugin.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/TestPythonOSPlugin.py create mode 100644 packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/operating_system.py create mode 100644 packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/operating_system2.py create mode 100644 packages/Python/lldbsuite/test/functionalities/postmortem/minidump/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/postmortem/minidump/TestMiniDump.py create mode 100644 packages/Python/lldbsuite/test/functionalities/postmortem/minidump/fizzbuzz.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/postmortem/minidump/fizzbuzz_no_heap.dmp create mode 100644 packages/Python/lldbsuite/test/functionalities/postmortem/minidump/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/process_attach/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/process_attach/TestProcessAttach.py create mode 100644 packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/TestAttachDenied.py create mode 100644 packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/process_attach/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/process_group/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/process_group/TestChangeProcessGroup.py create mode 100644 packages/Python/lldbsuite/test/functionalities/process_group/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/process_launch/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/process_launch/TestProcessLaunch.py create mode 100644 packages/Python/lldbsuite/test/functionalities/process_launch/input-file.txt create mode 100644 packages/Python/lldbsuite/test/functionalities/process_launch/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/process_launch/my_working_dir/.keep create mode 100644 packages/Python/lldbsuite/test/functionalities/process_launch/print_cwd.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/process_launch/print_env.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/process_save_core/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/process_save_core/TestProcessSaveCore.py create mode 100644 packages/Python/lldbsuite/test/functionalities/process_save_core/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/recursion/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/recursion/TestValueObjectRecursion.py create mode 100644 packages/Python/lldbsuite/test/functionalities/recursion/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/register/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/register/TestRegisters.py create mode 100644 packages/Python/lldbsuite/test/functionalities/register/a.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/register/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/rerun/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/rerun/TestRerun.py create mode 100644 packages/Python/lldbsuite/test/functionalities/rerun/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/return-value/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py create mode 100644 packages/Python/lldbsuite/test/functionalities/return-value/call-func.c create mode 100644 packages/Python/lldbsuite/test/functionalities/set-data/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/set-data/TestSetData.py create mode 100644 packages/Python/lldbsuite/test/functionalities/set-data/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/TestSendSignal.py create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/handle-segv/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/handle-segv/TestHandleSegv.py create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/handle-segv/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/raise/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/raise/TestRaise.py create mode 100644 packages/Python/lldbsuite/test/functionalities/signal/raise/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/TestSingleQuoteInFilename.py create mode 100644 packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/path with '09/.keep create mode 100644 packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/TestStepNoDebug.py create mode 100644 packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/with-debug.c create mode 100644 packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/without-debug.c create mode 100644 packages/Python/lldbsuite/test/functionalities/stop-hook/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/stop-hook/TestStopHookCmd.py create mode 100644 packages/Python/lldbsuite/test/functionalities/stop-hook/TestStopHookMechanism.py create mode 100644 packages/Python/lldbsuite/test/functionalities/stop-hook/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/TestStopHookMultipleThreads.py create mode 100644 packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/target_command/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/target_command/TestTargetCommand.py create mode 100644 packages/Python/lldbsuite/test/functionalities/target_command/a.c create mode 100644 packages/Python/lldbsuite/test/functionalities/target_command/b.c create mode 100644 packages/Python/lldbsuite/test/functionalities/target_command/c.c create mode 100644 packages/Python/lldbsuite/test/functionalities/target_command/globals.c create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/Makefile create mode 100755 packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/ParallelTask.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/TestBacktraceAll.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/break_after_join/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/break_after_join/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/TestCrashDuringStep.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/create_during_step/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/create_during_step/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/TestExitDuringStep.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/jump/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/jump/TestThreadJump.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/jump/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/jump/other.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/multi_break/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/multi_break/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/state/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/state/TestThreadStates.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/state/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/step_out/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/step_out/TestThreadStepOut.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/step_out/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_exit/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_exit/TestThreadExit.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_exit/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/TestThreadSpecificBpPlusCondition.py create mode 100644 packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/tty/TestTerminal.py create mode 100644 packages/Python/lldbsuite/test/functionalities/type_completion/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/type_completion/TestTypeCompletion.py create mode 100644 packages/Python/lldbsuite/test/functionalities/type_completion/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/type_lookup/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/type_lookup/TestTypeLookup.py create mode 100644 packages/Python/lldbsuite/test/functionalities/type_lookup/main.m create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/noreturn/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/noreturn/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/TestSigtrampUnwind.py create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/standard/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/standard/TestStandardUnwind.py create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/divmod.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/fprintf.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/new_delete.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/value_md5_crash/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/value_md5_crash/TestValueMD5Crash.py create mode 100644 packages/Python/lldbsuite/test/functionalities/value_md5_crash/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/TestWatchedVarHitWhenInScope.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/TestWatchpointCommands.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/TestWatchpointCommandLLDB.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/TestWatchpointCommandPython.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/TestWatchpointConditionCmd.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/TestWatchpointEvents.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/TestValueOfVectorVariable.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/main.c create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/Makefile create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/TestWatchpointSetErrorCases.py create mode 100644 packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/main.cpp create mode 100644 packages/Python/lldbsuite/test/help/TestHelp.py create mode 100644 packages/Python/lldbsuite/test/issue_verification/Makefile create mode 100644 packages/Python/lldbsuite/test/issue_verification/README.txt create mode 100644 packages/Python/lldbsuite/test/issue_verification/TestExpectedTimeout.py.park create mode 100644 packages/Python/lldbsuite/test/issue_verification/TestFail.py.park create mode 100644 packages/Python/lldbsuite/test/issue_verification/TestRerunFail.py.park create mode 100644 packages/Python/lldbsuite/test/issue_verification/TestRerunInline.py.park create mode 100644 packages/Python/lldbsuite/test/issue_verification/TestRerunTimeout.py.park create mode 100644 packages/Python/lldbsuite/test/issue_verification/TestSignal.py.park create mode 100644 packages/Python/lldbsuite/test/issue_verification/TestSignalOutsideTestMethod.py.park create mode 100644 packages/Python/lldbsuite/test/issue_verification/TestTimeout.py.park create mode 100755 packages/Python/lldbsuite/test/issue_verification/disable.py create mode 100755 packages/Python/lldbsuite/test/issue_verification/enable.py create mode 100644 packages/Python/lldbsuite/test/issue_verification/inline_rerun_inferior.cpp create mode 100644 packages/Python/lldbsuite/test/issue_verification/rerun_base.py create mode 100644 packages/Python/lldbsuite/test/lang/c/anonymous/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/anonymous/TestAnonymous.py create mode 100644 packages/Python/lldbsuite/test/lang/c/anonymous/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/array_types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/array_types/TestArrayTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/c/array_types/cmds.txt create mode 100644 packages/Python/lldbsuite/test/lang/c/array_types/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/bitfields/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/bitfields/TestBitfields.py create mode 100644 packages/Python/lldbsuite/test/lang/c/bitfields/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/blocks/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/blocks/TestBlocks.py create mode 100644 packages/Python/lldbsuite/test/lang/c/blocks/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/const_variables/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/const_variables/TestConstVariables.py create mode 100644 packages/Python/lldbsuite/test/lang/c/const_variables/functions.c create mode 100644 packages/Python/lldbsuite/test/lang/c/const_variables/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/enum_types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/c/enum_types/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/forward/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/forward/README.txt create mode 100644 packages/Python/lldbsuite/test/lang/c/forward/TestForwardDeclaration.py create mode 100644 packages/Python/lldbsuite/test/lang/c/forward/foo.c create mode 100644 packages/Python/lldbsuite/test/lang/c/forward/foo.h create mode 100644 packages/Python/lldbsuite/test/lang/c/forward/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/function_types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/function_types/TestFunctionTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/c/function_types/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/global_variables/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/global_variables/TestGlobalVariables.py create mode 100644 packages/Python/lldbsuite/test/lang/c/global_variables/a.c create mode 100644 packages/Python/lldbsuite/test/lang/c/global_variables/cmds.txt create mode 100644 packages/Python/lldbsuite/test/lang/c/global_variables/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/inlines/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/inlines/inlines.c create mode 100644 packages/Python/lldbsuite/test/lang/c/inlines/inlines.h create mode 100644 packages/Python/lldbsuite/test/lang/c/modules/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/modules/TestCModules.py create mode 100644 packages/Python/lldbsuite/test/lang/c/modules/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/recurse/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/recurse/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/register_variables/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py create mode 100644 packages/Python/lldbsuite/test/lang/c/register_variables/test.c create mode 100644 packages/Python/lldbsuite/test/lang/c/set_values/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/set_values/TestSetValues.py create mode 100644 packages/Python/lldbsuite/test/lang/c/set_values/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib/TestSharedLib.py create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib/foo.c create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib/foo.h create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/foo.c create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/foo.h create mode 100644 packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/stepping/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/stepping/TestStepAndBreakpoints.py create mode 100644 packages/Python/lldbsuite/test/lang/c/stepping/TestThreadStepping.py create mode 100644 packages/Python/lldbsuite/test/lang/c/stepping/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/strings/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/strings/TestCStrings.py create mode 100644 packages/Python/lldbsuite/test/lang/c/strings/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/struct_types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/struct_types/TestStructTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/c/struct_types/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/tls_globals/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/tls_globals/TestTlsGlobals.py create mode 100644 packages/Python/lldbsuite/test/lang/c/tls_globals/a.c create mode 100644 packages/Python/lldbsuite/test/lang/c/tls_globals/main.c create mode 100644 packages/Python/lldbsuite/test/lang/c/typedef/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/c/typedef/Testtypedef.py create mode 100644 packages/Python/lldbsuite/test/lang/c/typedef/main.c create mode 100644 packages/Python/lldbsuite/test/lang/cpp/auto/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/auto/TestCPPAuto.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/auto/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/bool/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/bool/TestCPPBool.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/bool/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/TestCPPBreakpointCommands.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/nested.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/call-function/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/call-function/TestCallCPPFunction.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/call-function/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/chained-calls/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/chained-calls/TestCppChainedCalls.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/chained-calls/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/char1632_t/.categories create mode 100644 packages/Python/lldbsuite/test/lang/cpp/char1632_t/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/char1632_t/TestChar1632T.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/char1632_t/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/class_static/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/class_static/TestStaticVariables.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/class_static/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/class_types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/class_types/TestClassTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/class_types/TestClassTypesDisassembly.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/class_types/cmds.txt create mode 100644 packages/Python/lldbsuite/test/lang/cpp/class_types/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/diamond/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/diamond/TestDiamond.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/diamond/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/dynamic-value/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/dynamic-value/TestCppValueCast.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/dynamic-value/TestDynamicValue.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/dynamic-value/pass-to-base.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/dynamic-value/sbvalue-cast.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/enum_types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/enum_types/TestCPP11EnumTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/enum_types/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/exceptions/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/exceptions/TestCPPExceptionBreakpoints.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/exceptions/exceptions.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/TestFrameVariableAnonymousUnions.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/global_operators/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/global_operators/TestCppGlobalOperators.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/global_operators/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/incomplete-types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/incomplete-types/TestCppIncompleteTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/incomplete-types/a.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/incomplete-types/a.h create mode 100644 packages/Python/lldbsuite/test/lang/cpp/incomplete-types/length.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/incomplete-types/length.h create mode 100644 packages/Python/lldbsuite/test/lang/cpp/incomplete-types/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/TestWithLimitDebugInfo.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/base.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/base.h create mode 100644 packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/derived.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/derived.h create mode 100644 packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespace.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespaceLookup.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/cmds.txt create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/ns.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/ns.h create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/nsimport/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/nsimport/TestCppNsImport.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/nsimport/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/TestOverloadedFunctions.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/static-a.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/static-b.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/rdar12991846/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/rdar12991846/TestRdar12991846.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/rdar12991846/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/rvalue-references/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/rvalue-references/TestRvalueReferences.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/rvalue-references/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/scope/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/scope/TestCppScope.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/scope/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/signed_types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/signed_types/TestSignedTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/signed_types/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/static_members/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/static_members/TestCPPStaticMembers.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/static_members/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/static_methods/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/static_methods/TestCPPStaticMethods.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/static_methods/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/stl/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/stl/TestSTL.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/stl/TestStdCXXDisassembly.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/stl/cmds.txt create mode 100644 packages/Python/lldbsuite/test/lang/cpp/stl/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/this/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/this/TestCPPThis.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/this/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/unique-types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/unique-types/TestUniqueTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/unique-types/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/unsigned_types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/unsigned_types/TestUnsignedTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/unsigned_types/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/virtual/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/virtual/TestVirtual.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/virtual/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/cpp/wchar_t/.categories create mode 100644 packages/Python/lldbsuite/test/lang/cpp/wchar_t/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/cpp/wchar_t/TestCxxWCharT.py create mode 100644 packages/Python/lldbsuite/test/lang/cpp/wchar_t/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/go/expressions/TestExpressions.py create mode 100644 packages/Python/lldbsuite/test/lang/go/expressions/main.go create mode 100644 packages/Python/lldbsuite/test/lang/go/formatters/TestGoFormatters.py create mode 100644 packages/Python/lldbsuite/test/lang/go/formatters/main.go create mode 100644 packages/Python/lldbsuite/test/lang/go/goroutines/TestGoroutines.py create mode 100644 packages/Python/lldbsuite/test/lang/go/goroutines/main.go create mode 100644 packages/Python/lldbsuite/test/lang/go/runtime/TestGoLanguageRuntime create mode 100644 packages/Python/lldbsuite/test/lang/go/runtime/main.go create mode 100644 packages/Python/lldbsuite/test/lang/go/types/TestGoASTContext.py create mode 100644 packages/Python/lldbsuite/test/lang/go/types/main.go create mode 100644 packages/Python/lldbsuite/test/lang/mixed/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/mixed/TestMixedLanguages.py create mode 100644 packages/Python/lldbsuite/test/lang/mixed/foo.cpp create mode 100644 packages/Python/lldbsuite/test/lang/mixed/main.c create mode 100644 packages/Python/lldbsuite/test/lang/objc/.categories create mode 100644 packages/Python/lldbsuite/test/lang/objc/blocks/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/blocks/TestObjCIvarsInBlocks.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/blocks/ivars-in-blocks.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/blocks/ivars-in-blocks.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/blocks/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/forward-decl/Container.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/forward-decl/Container.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/forward-decl/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/forward-decl/TestForwardDecl.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/forward-decl/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/TestConstStrings.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/TestFoundationDisassembly.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods2.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/TestObjectDescriptionAPI.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/TestRuntimeTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/TestSymbolTable.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/const-strings.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/my-base.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/foundation/my-base.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/hidden-ivars/InternalDefiner.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/hidden-ivars/InternalDefiner.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/hidden-ivars/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/hidden-ivars/TestHiddenIvars.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/hidden-ivars/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/ivar-IMP/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/ivar-IMP/TestObjCiVarIMP.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/ivar-IMP/myclass.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/ivar-IMP/myclass.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/ivar-IMP/repro.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-auto-import/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-auto-import/TestModulesAutoImport.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-auto-import/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-incomplete/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-incomplete/TestIncompleteModules.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-incomplete/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-incomplete/module.map create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-incomplete/myModule.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-incomplete/myModule.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/TestModulesInlineFunctions.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/module.map create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/myModule.c create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/myModule.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules/TestObjCModules.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/modules/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc++/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc++/TestObjCXX.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc++/main.mm create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/TestObjCBaseClassSBType.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/TestObjCBuiltinTypes.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/main.cpp create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-checker/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-checker/TestObjCCheckers.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-checker/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-class-method/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-class-method/TestObjCClassMethod.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-class-method/class.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/.categories create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/TestObjCDynamicSBType.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/TestObjCDynamicValue.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/dynamic-value.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/TestObjCIvarOffsets.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/objc-ivar-offsets.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/objc-ivar-offsets.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-protocols/TestIvarProtocols.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-protocols/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-optimized/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-optimized/TestObjcOptimized.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-optimized/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-property/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-property/TestObjCProperty.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-property/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/TestRuntimeIvars.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/TestObjCStaticMethodStripped.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/static.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-static-method/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-static-method/TestObjCStaticMethod.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-static-method/static.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-stepping/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-stepping/TestObjCStepping.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-stepping/stepping-tests.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/TestObjCStructArgument.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/test.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-struct-return/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-struct-return/TestObjCStructReturn.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-struct-return/test.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-super/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-super/TestObjCSuper.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/objc-super/class.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/print-obj/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/print-obj/TestPrintObj.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/print-obj/blocked.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/radar-9691614/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/radar-9691614/TestObjCMethodReturningBOOL.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/radar-9691614/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-10967107/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-10967107/TestRdar10967107.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-10967107/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-11355592/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-11355592/TestRdar11355592.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-11355592/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-12408181/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-12408181/TestRdar12408181.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/rdar-12408181/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/real-definition/Bar.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/real-definition/Bar.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/real-definition/Foo.h create mode 100644 packages/Python/lldbsuite/test/lang/objc/real-definition/Foo.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/real-definition/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/real-definition/TestRealDefinition.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/real-definition/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/sample/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/sample/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objc/self/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objc/self/TestObjCSelf.py create mode 100644 packages/Python/lldbsuite/test/lang/objc/self/main.m create mode 100644 packages/Python/lldbsuite/test/lang/objcxx/objcxx-ivar-vector/TestIvarVector.py create mode 100644 packages/Python/lldbsuite/test/lang/objcxx/objcxx-ivar-vector/main.mm create mode 100644 packages/Python/lldbsuite/test/lang/objcxx/sample/Makefile create mode 100644 packages/Python/lldbsuite/test/lang/objcxx/sample/main.mm create mode 100644 packages/Python/lldbsuite/test/linux/builtin_trap/Makefile create mode 100644 packages/Python/lldbsuite/test/linux/builtin_trap/TestBuiltinTrap.py create mode 100644 packages/Python/lldbsuite/test/linux/builtin_trap/main.cpp create mode 100644 packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/Makefile create mode 100644 packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py create mode 100644 packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/main.cpp create mode 100644 packages/Python/lldbsuite/test/lldb_pylint_helper.py create mode 100644 packages/Python/lldbsuite/test/lldbbench.py create mode 100644 packages/Python/lldbsuite/test/lldbcurses.py create mode 100644 packages/Python/lldbsuite/test/lldbinline.py create mode 100644 packages/Python/lldbsuite/test/lldbpexpect.py create mode 100644 packages/Python/lldbsuite/test/lldbplatformutil.py create mode 100644 packages/Python/lldbsuite/test/lldbtest.py create mode 100644 packages/Python/lldbsuite/test/lldbtest_config.py create mode 100644 packages/Python/lldbsuite/test/lldbutil.py create mode 100644 packages/Python/lldbsuite/test/lock.py create mode 100644 packages/Python/lldbsuite/test/logging/Makefile create mode 100644 packages/Python/lldbsuite/test/logging/TestLogging.py create mode 100644 packages/Python/lldbsuite/test/logging/main.cpp create mode 100644 packages/Python/lldbsuite/test/macosx/add-dsym/Makefile create mode 100644 packages/Python/lldbsuite/test/macosx/add-dsym/TestAddDsymMidExecutionCommand.py create mode 100644 packages/Python/lldbsuite/test/macosx/add-dsym/main.c create mode 100644 packages/Python/lldbsuite/test/macosx/debug-info/apple_types/Makefile create mode 100644 packages/Python/lldbsuite/test/macosx/debug-info/apple_types/TestAppleTypesIsProduced.py create mode 100644 packages/Python/lldbsuite/test/macosx/debug-info/apple_types/main.c create mode 100644 packages/Python/lldbsuite/test/macosx/indirect_symbol/Makefile create mode 100644 packages/Python/lldbsuite/test/macosx/indirect_symbol/TestIndirectSymbols.py create mode 100644 packages/Python/lldbsuite/test/macosx/indirect_symbol/alias.list create mode 100644 packages/Python/lldbsuite/test/macosx/indirect_symbol/indirect.c create mode 100644 packages/Python/lldbsuite/test/macosx/indirect_symbol/main.c create mode 100644 packages/Python/lldbsuite/test/macosx/indirect_symbol/reexport.c create mode 100644 packages/Python/lldbsuite/test/macosx/order/Makefile create mode 100644 packages/Python/lldbsuite/test/macosx/order/TestOrderFile.py create mode 100644 packages/Python/lldbsuite/test/macosx/order/cmds.txt create mode 100644 packages/Python/lldbsuite/test/macosx/order/main.c create mode 100644 packages/Python/lldbsuite/test/macosx/order/order-file create mode 100644 packages/Python/lldbsuite/test/macosx/queues/Makefile create mode 100644 packages/Python/lldbsuite/test/macosx/queues/TestQueues.py create mode 100644 packages/Python/lldbsuite/test/macosx/queues/main.c create mode 100644 packages/Python/lldbsuite/test/macosx/safe-to-func-call/Makefile create mode 100644 packages/Python/lldbsuite/test/macosx/safe-to-func-call/TestSafeFuncCalls.py create mode 100644 packages/Python/lldbsuite/test/macosx/safe-to-func-call/main.c create mode 100644 packages/Python/lldbsuite/test/macosx/universal/Makefile create mode 100644 packages/Python/lldbsuite/test/macosx/universal/TestUniversal.py create mode 100644 packages/Python/lldbsuite/test/macosx/universal/main.c create mode 100644 packages/Python/lldbsuite/test/make/Makefile.rules create mode 100644 packages/Python/lldbsuite/test/make/test_common.h create mode 100644 packages/Python/lldbsuite/test/plugins/builder_base.py create mode 100644 packages/Python/lldbsuite/test/plugins/builder_darwin.py create mode 100644 packages/Python/lldbsuite/test/plugins/builder_freebsd.py create mode 100644 packages/Python/lldbsuite/test/plugins/builder_linux2.py create mode 100644 packages/Python/lldbsuite/test/plugins/builder_netbsd.py create mode 100644 packages/Python/lldbsuite/test/plugins/builder_win32.py create mode 100644 packages/Python/lldbsuite/test/python_api/.categories create mode 100644 packages/Python/lldbsuite/test/python_api/breakpoint/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/breakpoint/TestBreakpointAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/breakpoint/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/class_members/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/class_members/TestSBTypeClassMembers.py create mode 100644 packages/Python/lldbsuite/test/python_api/class_members/main.mm create mode 100644 packages/Python/lldbsuite/test/python_api/debugger/TestDebuggerAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/TestDefaultConstructorForAPIObjects.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_address.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_block.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_breakpoint.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_breakpointlocation.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_broadcaster.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_communication.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_compileunit.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_debugger.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_error.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_event.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_filespec.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_frame.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_function.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_instruction.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_instructionlist.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_lineentry.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_listener.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_module.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_process.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_section.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_stringlist.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_symbol.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_symbolcontext.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_target.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_thread.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_type.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_value.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_valuelist.py create mode 100644 packages/Python/lldbsuite/test/python_api/default-constructor/sb_watchpoint.py create mode 100644 packages/Python/lldbsuite/test/python_api/disassemble-raw-data/TestDisassembleRawData.py create mode 100644 packages/Python/lldbsuite/test/python_api/disassemble-raw-data/TestDisassemble_VST1_64.py create mode 100644 packages/Python/lldbsuite/test/python_api/event/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/event/TestEvents.py create mode 100644 packages/Python/lldbsuite/test/python_api/event/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/exprpath_synthetic/TestExprPathSynthetic.py create mode 100644 packages/Python/lldbsuite/test/python_api/exprpath_synthetic/main.mm create mode 100644 packages/Python/lldbsuite/test/python_api/findvalue_duplist/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/findvalue_duplist/TestSBFrameFindValue.py create mode 100644 packages/Python/lldbsuite/test/python_api/findvalue_duplist/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/formatters/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/formatters/TestFormattersSBAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/formatters/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/formatters/synth.py create mode 100644 packages/Python/lldbsuite/test/python_api/frame/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/frame/TestFrames.py create mode 100644 packages/Python/lldbsuite/test/python_api/frame/inlines/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/frame/inlines/TestInlinedFrame.py create mode 100644 packages/Python/lldbsuite/test/python_api/frame/inlines/inlines.c create mode 100644 packages/Python/lldbsuite/test/python_api/frame/inlines/inlines.h create mode 100644 packages/Python/lldbsuite/test/python_api/frame/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/function_symbol/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/function_symbol/TestDisasmAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/function_symbol/TestSymbolAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/function_symbol/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/hello_world/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/hello_world/TestHelloWorld.py create mode 100644 packages/Python/lldbsuite/test/python_api/hello_world/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/interpreter/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/interpreter/TestCommandInterpreterAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/interpreter/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/frame/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/frame/TestFrameUtils.py create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/frame/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/iter/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestLLDBIterator.py create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestRegistersIterator.py create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/iter/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/process/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/process/TestPrintStackTraces.py create mode 100644 packages/Python/lldbsuite/test/python_api/lldbutil/process/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/module_section/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/module_section/TestModuleAndSection.py create mode 100644 packages/Python/lldbsuite/test/python_api/module_section/b.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/module_section/c.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/module_section/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/objc_type/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/objc_type/TestObjCType.py create mode 100644 packages/Python/lldbsuite/test/python_api/objc_type/main.m create mode 100644 packages/Python/lldbsuite/test/python_api/process/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/process/TestProcessAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/process/io/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/process/io/TestProcessIO.py create mode 100644 packages/Python/lldbsuite/test/python_api/process/io/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/process/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/rdar-12481949/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/rdar-12481949/Test-rdar-12481949.py create mode 100644 packages/Python/lldbsuite/test/python_api/rdar-12481949/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/sbdata/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/sbdata/TestSBData.py create mode 100644 packages/Python/lldbsuite/test/python_api/sbdata/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/sbtype_typeclass/TestSBTypeTypeClass.py create mode 100644 packages/Python/lldbsuite/test/python_api/sbtype_typeclass/main.m create mode 100644 packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/TestSBValueConstAddrOf.py create mode 100644 packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/sbvalue_persist/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/sbvalue_persist/TestSBValuePersist.py create mode 100644 packages/Python/lldbsuite/test/python_api/sbvalue_persist/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/section/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/section/TestSectionAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/section/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/signals/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/signals/TestSignalsAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/signals/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/symbol-context/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/symbol-context/TestSymbolContext.py create mode 100644 packages/Python/lldbsuite/test/python_api/symbol-context/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/target/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/target/TestTargetAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/target/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/thread/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/thread/TestThreadAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/thread/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/thread/main2.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/type/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/type/TestTypeList.py create mode 100644 packages/Python/lldbsuite/test/python_api/type/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/value/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/value/TestValueAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/value/change_values/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/value/change_values/TestChangeValueAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/value/change_values/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/value/linked_list/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/value/linked_list/TestValueAPILinkedList.py create mode 100644 packages/Python/lldbsuite/test/python_api/value/linked_list/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/value/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/value_var_update/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/value_var_update/TestValueVarUpdate.py create mode 100644 packages/Python/lldbsuite/test/python_api/value_var_update/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/TestSetWatchpoint.py create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/TestWatchpointIgnoreCount.py create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/TestWatchpointIter.py create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/condition/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/condition/TestWatchpointConditionAPI.py create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/condition/main.cpp create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/main.c create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/Makefile create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestSetWatchlocation.py create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py create mode 100644 packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/main.cpp create mode 100644 packages/Python/lldbsuite/test/redo.py create mode 100644 packages/Python/lldbsuite/test/result_formatter.py create mode 100644 packages/Python/lldbsuite/test/settings/Makefile create mode 100644 packages/Python/lldbsuite/test/settings/TestSettings.py create mode 100644 packages/Python/lldbsuite/test/settings/main.cpp create mode 100644 packages/Python/lldbsuite/test/settings/quoting/Makefile create mode 100644 packages/Python/lldbsuite/test/settings/quoting/TestQuoting.py create mode 100644 packages/Python/lldbsuite/test/settings/quoting/main.c create mode 100644 packages/Python/lldbsuite/test/source-manager/Makefile create mode 100644 packages/Python/lldbsuite/test/source-manager/TestSourceManager.py create mode 100644 packages/Python/lldbsuite/test/source-manager/hidden/.keep create mode 100644 packages/Python/lldbsuite/test/source-manager/main.c create mode 100644 packages/Python/lldbsuite/test/terminal/TestSTTYBeforeAndAfter.py create mode 100644 packages/Python/lldbsuite/test/test_categories.py create mode 100644 packages/Python/lldbsuite/test/test_result.py create mode 100644 packages/Python/lldbsuite/test/test_runner/README.txt create mode 100644 packages/Python/lldbsuite/test/test_runner/lib/lldb_utils.py create mode 100644 packages/Python/lldbsuite/test/test_runner/lib/process_control.py create mode 100755 packages/Python/lldbsuite/test/test_runner/test/inferior.py create mode 100755 packages/Python/lldbsuite/test/test_runner/test/process_control_tests.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/TestMiExit.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/TestMiFile.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/TestMiGdbSetShow.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/TestMiLibraryLoaded.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/TestMiPrompt.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/TestMiBreak.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/control/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/control/TestMiExec.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/control/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/data/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/data/TestMiData.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/data/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/TestMiCliSupport.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/TestMiInterpreterExec.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/lldbmi_testcase.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/signal/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/signal/TestMiSignal.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/signal/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/stack/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/stack/TestMiStack.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/stack/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/TestMiStartupOptions.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script_error create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script_exit create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/symbol/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/symbol/TestMiSymbol.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/symbol/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.h create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test2.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/syntax/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/syntax/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/target/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/target/TestMiTarget.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/target/test_attach.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/variable/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/variable/TestMiGdbSetShowPrint.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/variable/TestMiVar.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-mi/variable/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGDBRemoteMemoryRead.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAttach.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAuxvSupport.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteExpeditedRegisters.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteKill.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteProcessInfo.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteRegisterState.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/commandline/TestStubReverseConnect.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/commandline/TestStubSetSID.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/TestGdbRemoteAbort.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/TestGdbRemoteSegFault.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/Makefile create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/TestPlatformProcessConnect.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/socket_packet_pump.py create mode 100644 packages/Python/lldbsuite/test/tools/lldb-server/test/test_lldbgdbserverutils.py create mode 100644 packages/Python/lldbsuite/test/types/AbstractBase.py create mode 100644 packages/Python/lldbsuite/test/types/HideTestFailures.py create mode 100644 packages/Python/lldbsuite/test/types/Makefile create mode 100644 packages/Python/lldbsuite/test/types/TestFloatTypes.py create mode 100644 packages/Python/lldbsuite/test/types/TestFloatTypesExpr.py create mode 100644 packages/Python/lldbsuite/test/types/TestIntegerTypes.py create mode 100644 packages/Python/lldbsuite/test/types/TestIntegerTypesExpr.py create mode 100644 packages/Python/lldbsuite/test/types/TestRecursiveTypes.py create mode 100644 packages/Python/lldbsuite/test/types/basic_type.cpp create mode 100644 packages/Python/lldbsuite/test/types/char.cpp create mode 100644 packages/Python/lldbsuite/test/types/double.cpp create mode 100644 packages/Python/lldbsuite/test/types/float.cpp create mode 100644 packages/Python/lldbsuite/test/types/int.cpp create mode 100644 packages/Python/lldbsuite/test/types/long.cpp create mode 100644 packages/Python/lldbsuite/test/types/long_long.cpp create mode 100644 packages/Python/lldbsuite/test/types/recursive_type_1.cpp create mode 100644 packages/Python/lldbsuite/test/types/recursive_type_2.cpp create mode 100644 packages/Python/lldbsuite/test/types/recursive_type_main.cpp create mode 100644 packages/Python/lldbsuite/test/types/short.cpp create mode 100644 packages/Python/lldbsuite/test/types/unsigned_char.cpp create mode 100644 packages/Python/lldbsuite/test/types/unsigned_int.cpp create mode 100644 packages/Python/lldbsuite/test/types/unsigned_long.cpp create mode 100644 packages/Python/lldbsuite/test/types/unsigned_long_long.cpp create mode 100644 packages/Python/lldbsuite/test/types/unsigned_short.cpp create mode 100644 packages/Python/lldbsuite/test/warnings/uuid/Makefile create mode 100644 packages/Python/lldbsuite/test/warnings/uuid/TestAddDsymCommand.py create mode 100644 packages/Python/lldbsuite/test/warnings/uuid/main.cpp.template create mode 100644 packages/Python/lldbsuite/test/xunit_formatter.py create mode 100644 resources/LLDB-Info.plist create mode 100644 scripts/CMakeLists.txt create mode 100644 scripts/Makefile create mode 100644 scripts/Python/Makefile create mode 100644 scripts/Python/android/host_art_bt.py create mode 100755 scripts/Python/finish-swig-Python-LLDB.sh create mode 100644 scripts/Python/finishSwigPythonLLDB.py create mode 100644 scripts/Python/modify-python-lldb.py create mode 100644 scripts/Python/modules/CMakeLists.txt create mode 100644 scripts/Python/modules/Makefile create mode 100644 scripts/Python/modules/readline/CMakeLists.txt create mode 100644 scripts/Python/modules/readline/Makefile create mode 100644 scripts/Python/modules/readline/readline.cpp create mode 100644 scripts/Python/prepare_binding_Python.py create mode 100644 scripts/Python/python-extensions.swig create mode 100644 scripts/Python/python-swigsafecast.swig create mode 100644 scripts/Python/python-typemaps.swig create mode 100644 scripts/Python/python-wrapper.swig create mode 100755 scripts/Python/remote-build.py create mode 100644 scripts/Python/use_lldb_suite.py create mode 100755 scripts/build-lldb-llvm-clang create mode 100644 scripts/build-llvm.pl create mode 100755 scripts/buildbot.py create mode 100755 scripts/checkpoint-llvm.pl create mode 100755 scripts/disasm-gdb-remote.pl create mode 100755 scripts/finish-swig-wrapper-classes.sh create mode 100644 scripts/finishSwigWrapperClasses.py create mode 100755 scripts/generate-vers.pl create mode 100644 scripts/get_relative_lib_dir.py create mode 100755 scripts/install-lldb.sh create mode 100644 scripts/install_custom_python.py create mode 100644 scripts/interface/SBAddress.i create mode 100644 scripts/interface/SBAttachInfo.i create mode 100644 scripts/interface/SBBlock.i create mode 100644 scripts/interface/SBBreakpoint.i create mode 100644 scripts/interface/SBBreakpointLocation.i create mode 100644 scripts/interface/SBBroadcaster.i create mode 100644 scripts/interface/SBCommandInterpreter.i create mode 100644 scripts/interface/SBCommandReturnObject.i create mode 100644 scripts/interface/SBCommunication.i create mode 100644 scripts/interface/SBCompileUnit.i create mode 100644 scripts/interface/SBData.i create mode 100644 scripts/interface/SBDebugger.i create mode 100644 scripts/interface/SBDeclaration.i create mode 100644 scripts/interface/SBError.i create mode 100644 scripts/interface/SBEvent.i create mode 100644 scripts/interface/SBExecutionContext.i create mode 100644 scripts/interface/SBExpressionOptions.i create mode 100644 scripts/interface/SBFileSpec.i create mode 100644 scripts/interface/SBFileSpecList.i create mode 100644 scripts/interface/SBFrame.i create mode 100644 scripts/interface/SBFunction.i create mode 100644 scripts/interface/SBHostOS.i create mode 100644 scripts/interface/SBInstruction.i create mode 100644 scripts/interface/SBInstructionList.i create mode 100644 scripts/interface/SBLanguageRuntime.i create mode 100644 scripts/interface/SBLaunchInfo.i create mode 100644 scripts/interface/SBLineEntry.i create mode 100644 scripts/interface/SBListener.i create mode 100644 scripts/interface/SBModule.i create mode 100644 scripts/interface/SBModuleSpec.i create mode 100644 scripts/interface/SBPlatform.i create mode 100644 scripts/interface/SBProcess.i create mode 100644 scripts/interface/SBQueue.i create mode 100644 scripts/interface/SBQueueItem.i create mode 100644 scripts/interface/SBSection.i create mode 100644 scripts/interface/SBSourceManager.i create mode 100644 scripts/interface/SBStream.i create mode 100644 scripts/interface/SBStringList.i create mode 100644 scripts/interface/SBSymbol.i create mode 100644 scripts/interface/SBSymbolContext.i create mode 100644 scripts/interface/SBSymbolContextList.i create mode 100644 scripts/interface/SBTarget.i create mode 100644 scripts/interface/SBThread.i create mode 100644 scripts/interface/SBThreadCollection.i create mode 100644 scripts/interface/SBThreadPlan.i create mode 100644 scripts/interface/SBType.i create mode 100644 scripts/interface/SBTypeCategory.i create mode 100644 scripts/interface/SBTypeEnumMember.i create mode 100644 scripts/interface/SBTypeFilter.i create mode 100644 scripts/interface/SBTypeFormat.i create mode 100644 scripts/interface/SBTypeNameSpecifier.i create mode 100644 scripts/interface/SBTypeSummary.i create mode 100644 scripts/interface/SBTypeSynthetic.i create mode 100644 scripts/interface/SBUnixSignals.i create mode 100644 scripts/interface/SBValue.i create mode 100644 scripts/interface/SBValueList.i create mode 100644 scripts/interface/SBVariablesOptions.i create mode 100644 scripts/interface/SBWatchpoint.i create mode 100644 scripts/lldb.swig create mode 100644 scripts/package-clang-headers.py create mode 100755 scripts/prepare_bindings.py create mode 100755 scripts/sed-sources create mode 100755 scripts/shush create mode 100644 scripts/swig_bot.py create mode 100644 scripts/swig_bot_lib/__init__.py create mode 100644 scripts/swig_bot_lib/client.py create mode 100644 scripts/swig_bot_lib/local.py create mode 100644 scripts/swig_bot_lib/remote.py create mode 100644 scripts/swig_bot_lib/server.py create mode 100644 scripts/use_lldb_suite.py create mode 100644 scripts/utilsArgsParse.py create mode 100644 scripts/utilsDebug.py create mode 100644 scripts/utilsOsType.py create mode 100755 scripts/verify_api.py create mode 100644 source/API/CMakeLists.txt create mode 100644 source/API/Makefile create mode 100644 source/Breakpoint/CMakeLists.txt create mode 100644 source/Breakpoint/Makefile create mode 100644 source/CMakeLists.txt create mode 100644 source/Commands/CMakeLists.txt create mode 100644 source/Commands/Makefile create mode 100644 source/Core/CMakeLists.txt create mode 100644 source/Core/Makefile create mode 100644 source/DataFormatters/CMakeLists.txt create mode 100644 source/DataFormatters/Makefile create mode 100644 source/Expression/CMakeLists.txt create mode 100644 source/Expression/Makefile create mode 100644 source/Host/CMakeLists.txt create mode 100644 source/Host/Makefile create mode 100644 source/Host/android/HostInfoAndroid.cpp create mode 100644 source/Host/android/LibcGlue.cpp create mode 100644 source/Host/android/ProcessLauncherAndroid.cpp create mode 100644 source/Host/linux/AbstractSocket.cpp create mode 100644 source/Host/linux/Host.cpp create mode 100644 source/Host/linux/HostInfoLinux.cpp create mode 100644 source/Host/linux/HostThreadLinux.cpp create mode 100644 source/Host/linux/LibcGlue.cpp create mode 100644 source/Host/linux/ThisThread.cpp create mode 100644 source/Host/macosx/Host.mm create mode 100644 source/Host/macosx/HostInfoMacOSX.mm create mode 100644 source/Host/macosx/HostThreadMacOSX.mm create mode 100644 source/Host/macosx/Symbols.cpp create mode 100644 source/Host/macosx/ThisThread.cpp create mode 100644 source/Host/macosx/cfcpp/CFCBundle.cpp create mode 100644 source/Host/macosx/cfcpp/CFCBundle.h create mode 100644 source/Host/macosx/cfcpp/CFCData.cpp create mode 100644 source/Host/macosx/cfcpp/CFCData.h create mode 100644 source/Host/macosx/cfcpp/CFCMutableArray.cpp create mode 100644 source/Host/macosx/cfcpp/CFCMutableArray.h create mode 100644 source/Host/macosx/cfcpp/CFCMutableDictionary.cpp create mode 100644 source/Host/macosx/cfcpp/CFCMutableDictionary.h create mode 100644 source/Host/macosx/cfcpp/CFCMutableSet.cpp create mode 100644 source/Host/macosx/cfcpp/CFCMutableSet.h create mode 100644 source/Host/macosx/cfcpp/CFCReleaser.h create mode 100644 source/Host/macosx/cfcpp/CFCString.cpp create mode 100644 source/Host/macosx/cfcpp/CFCString.h create mode 100644 source/Host/macosx/cfcpp/CoreFoundationCPP.h create mode 100644 source/Host/netbsd/Makefile create mode 100644 source/Host/windows/Condition.cpp create mode 100644 source/Host/windows/ConnectionGenericFileWindows.cpp create mode 100644 source/Host/windows/EditLineWin.cpp create mode 100644 source/Host/windows/FileSystem.cpp create mode 100644 source/Host/windows/Host.cpp create mode 100644 source/Host/windows/HostInfoWindows.cpp create mode 100644 source/Host/windows/HostProcessWindows.cpp create mode 100644 source/Host/windows/HostThreadWindows.cpp create mode 100644 source/Host/windows/LockFileWindows.cpp create mode 100644 source/Host/windows/Mutex.cpp create mode 100644 source/Host/windows/PipeWindows.cpp create mode 100644 source/Host/windows/ProcessLauncherWindows.cpp create mode 100644 source/Host/windows/ProcessRunLock.cpp create mode 100644 source/Host/windows/ThisThread.cpp create mode 100644 source/Host/windows/Windows.cpp create mode 100644 source/Initialization/CMakeLists.txt create mode 100644 source/Initialization/Makefile create mode 100644 source/Interpreter/CMakeLists.txt create mode 100644 source/Interpreter/Makefile create mode 100644 source/Makefile create mode 100644 source/Plugins/ABI/CMakeLists.txt create mode 100644 source/Plugins/ABI/MacOSX-arm/CMakeLists.txt create mode 100644 source/Plugins/ABI/MacOSX-arm/Makefile create mode 100644 source/Plugins/ABI/MacOSX-arm64/CMakeLists.txt create mode 100644 source/Plugins/ABI/MacOSX-arm64/Makefile create mode 100644 source/Plugins/ABI/MacOSX-i386/CMakeLists.txt create mode 100644 source/Plugins/ABI/MacOSX-i386/Makefile create mode 100644 source/Plugins/ABI/SysV-arm/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-arm/Makefile create mode 100644 source/Plugins/ABI/SysV-arm64/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-arm64/Makefile create mode 100644 source/Plugins/ABI/SysV-hexagon/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-hexagon/Makefile create mode 100644 source/Plugins/ABI/SysV-i386/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-i386/Makefile create mode 100644 source/Plugins/ABI/SysV-mips/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-mips/Makefile create mode 100644 source/Plugins/ABI/SysV-mips64/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-mips64/Makefile create mode 100644 source/Plugins/ABI/SysV-ppc/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-ppc/Makefile create mode 100644 source/Plugins/ABI/SysV-ppc64/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-ppc64/Makefile create mode 100644 source/Plugins/ABI/SysV-x86_64/CMakeLists.txt create mode 100644 source/Plugins/ABI/SysV-x86_64/Makefile create mode 100644 source/Plugins/CMakeLists.txt create mode 100644 source/Plugins/Disassembler/CMakeLists.txt create mode 100644 source/Plugins/Disassembler/llvm/CMakeLists.txt create mode 100644 source/Plugins/Disassembler/llvm/Makefile create mode 100644 source/Plugins/DynamicLoader/CMakeLists.txt create mode 100644 source/Plugins/DynamicLoader/Darwin-Kernel/CMakeLists.txt create mode 100644 source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp create mode 100644 source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h create mode 100644 source/Plugins/DynamicLoader/Darwin-Kernel/Makefile create mode 100644 source/Plugins/DynamicLoader/Hexagon-DYLD/CMakeLists.txt create mode 100644 source/Plugins/DynamicLoader/Hexagon-DYLD/Makefile create mode 100644 source/Plugins/DynamicLoader/MacOSX-DYLD/CMakeLists.txt create mode 100644 source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp create mode 100644 source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h create mode 100644 source/Plugins/DynamicLoader/MacOSX-DYLD/Makefile create mode 100644 source/Plugins/DynamicLoader/POSIX-DYLD/CMakeLists.txt create mode 100644 source/Plugins/DynamicLoader/POSIX-DYLD/Makefile create mode 100644 source/Plugins/DynamicLoader/Static/CMakeLists.txt create mode 100644 source/Plugins/DynamicLoader/Static/Makefile create mode 100644 source/Plugins/DynamicLoader/Windows-DYLD/CMakeLists.txt create mode 100644 source/Plugins/DynamicLoader/Windows-DYLD/Makefile create mode 100644 source/Plugins/ExpressionParser/CMakeLists.txt create mode 100644 source/Plugins/ExpressionParser/Clang/CMakeLists.txt create mode 100644 source/Plugins/ExpressionParser/Clang/Makefile create mode 100644 source/Plugins/ExpressionParser/Go/CMakeLists.txt create mode 100644 source/Plugins/ExpressionParser/Go/Makefile create mode 100644 source/Plugins/Instruction/ARM/CMakeLists.txt create mode 100644 source/Plugins/Instruction/ARM/Makefile create mode 100644 source/Plugins/Instruction/ARM64/CMakeLists.txt create mode 100644 source/Plugins/Instruction/ARM64/Makefile create mode 100644 source/Plugins/Instruction/CMakeLists.txt create mode 100644 source/Plugins/Instruction/MIPS/CMakeLists.txt create mode 100644 source/Plugins/Instruction/MIPS/Makefile create mode 100644 source/Plugins/Instruction/MIPS64/CMakeLists.txt create mode 100644 source/Plugins/Instruction/MIPS64/Makefile create mode 100644 source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt create mode 100644 source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile create mode 100644 source/Plugins/InstrumentationRuntime/CMakeLists.txt create mode 100644 source/Plugins/JITLoader/CMakeLists.txt create mode 100644 source/Plugins/JITLoader/GDB/CMakeLists.txt create mode 100644 source/Plugins/JITLoader/GDB/Makefile create mode 100644 source/Plugins/Language/CMakeLists.txt create mode 100644 source/Plugins/Language/CPlusPlus/CMakeLists.txt create mode 100644 source/Plugins/Language/CPlusPlus/Makefile create mode 100644 source/Plugins/Language/Go/CMakeLists.txt create mode 100644 source/Plugins/Language/Go/Makefile create mode 100644 source/Plugins/Language/ObjC/CMakeLists.txt create mode 100644 source/Plugins/Language/ObjC/Makefile create mode 100644 source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt create mode 100644 source/Plugins/Language/ObjCPlusPlus/Makefile create mode 100644 source/Plugins/LanguageRuntime/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/Makefile create mode 100644 source/Plugins/LanguageRuntime/Go/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/Go/Makefile create mode 100644 source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/Makefile create mode 100644 source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/RenderScript/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/Makefile create mode 100644 source/Plugins/Makefile create mode 100644 source/Plugins/MemoryHistory/CMakeLists.txt create mode 100644 source/Plugins/MemoryHistory/asan/CMakeLists.txt create mode 100644 source/Plugins/MemoryHistory/asan/Makefile create mode 100644 source/Plugins/ObjectContainer/BSD-Archive/CMakeLists.txt create mode 100644 source/Plugins/ObjectContainer/BSD-Archive/Makefile create mode 100644 source/Plugins/ObjectContainer/CMakeLists.txt create mode 100644 source/Plugins/ObjectContainer/Universal-Mach-O/CMakeLists.txt create mode 100644 source/Plugins/ObjectContainer/Universal-Mach-O/Makefile create mode 100644 source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp create mode 100644 source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h create mode 100644 source/Plugins/ObjectFile/CMakeLists.txt create mode 100644 source/Plugins/ObjectFile/ELF/CMakeLists.txt create mode 100644 source/Plugins/ObjectFile/ELF/Makefile create mode 100644 source/Plugins/ObjectFile/JIT/CMakeLists.txt create mode 100644 source/Plugins/ObjectFile/JIT/Makefile create mode 100644 source/Plugins/ObjectFile/Mach-O/CMakeLists.txt create mode 100644 source/Plugins/ObjectFile/Mach-O/Makefile create mode 100644 source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp create mode 100644 source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h create mode 100644 source/Plugins/ObjectFile/PECOFF/CMakeLists.txt create mode 100644 source/Plugins/ObjectFile/PECOFF/Makefile create mode 100644 source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp create mode 100644 source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h create mode 100644 source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp create mode 100644 source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h create mode 100644 source/Plugins/OperatingSystem/CMakeLists.txt create mode 100644 source/Plugins/OperatingSystem/Go/CMakeLists.txt create mode 100644 source/Plugins/OperatingSystem/Go/Makefile create mode 100644 source/Plugins/OperatingSystem/Python/CMakeLists.txt create mode 100644 source/Plugins/OperatingSystem/Python/Makefile create mode 100644 source/Plugins/Platform/Android/AdbClient.cpp create mode 100644 source/Plugins/Platform/Android/AdbClient.h create mode 100644 source/Plugins/Platform/Android/CMakeLists.txt create mode 100644 source/Plugins/Platform/Android/Makefile create mode 100644 source/Plugins/Platform/Android/PlatformAndroid.cpp create mode 100644 source/Plugins/Platform/Android/PlatformAndroid.h create mode 100644 source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp create mode 100644 source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h create mode 100644 source/Plugins/Platform/CMakeLists.txt create mode 100644 source/Plugins/Platform/FreeBSD/CMakeLists.txt create mode 100644 source/Plugins/Platform/FreeBSD/Makefile create mode 100644 source/Plugins/Platform/Kalimba/CMakeLists.txt create mode 100644 source/Plugins/Platform/Kalimba/Makefile create mode 100644 source/Plugins/Platform/Kalimba/PlatformKalimba.cpp create mode 100644 source/Plugins/Platform/Kalimba/PlatformKalimba.h create mode 100644 source/Plugins/Platform/Linux/CMakeLists.txt create mode 100644 source/Plugins/Platform/Linux/Makefile create mode 100644 source/Plugins/Platform/Linux/PlatformLinux.cpp create mode 100644 source/Plugins/Platform/Linux/PlatformLinux.h create mode 100644 source/Plugins/Platform/MacOSX/CMakeLists.txt create mode 100644 source/Plugins/Platform/MacOSX/Makefile create mode 100644 source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformDarwin.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformDarwin.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformMacOSX.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformRemoteiOS.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp create mode 100644 source/Plugins/Platform/MacOSX/PlatformiOSSimulator.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h create mode 100644 source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm create mode 100644 source/Plugins/Platform/Makefile create mode 100644 source/Plugins/Platform/NetBSD/CMakeLists.txt create mode 100644 source/Plugins/Platform/NetBSD/Makefile create mode 100644 source/Plugins/Platform/POSIX/CMakeLists.txt create mode 100644 source/Plugins/Platform/POSIX/Makefile create mode 100644 source/Plugins/Platform/Windows/CMakeLists.txt create mode 100644 source/Plugins/Platform/Windows/Makefile create mode 100644 source/Plugins/Platform/Windows/PlatformWindows.cpp create mode 100644 source/Plugins/Platform/Windows/PlatformWindows.h create mode 100644 source/Plugins/Platform/gdb-server/CMakeLists.txt create mode 100644 source/Plugins/Platform/gdb-server/Makefile create mode 100644 source/Plugins/Process/CMakeLists.txt create mode 100644 source/Plugins/Process/FreeBSD/CMakeLists.txt create mode 100644 source/Plugins/Process/FreeBSD/Makefile create mode 100644 source/Plugins/Process/Linux/CMakeLists.txt create mode 100644 source/Plugins/Process/Linux/Makefile create mode 100644 source/Plugins/Process/Linux/NativeProcessLinux.cpp create mode 100644 source/Plugins/Process/Linux/NativeProcessLinux.h create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux.h create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h create mode 100755 source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp create mode 100644 source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h create mode 100644 source/Plugins/Process/Linux/NativeThreadLinux.cpp create mode 100644 source/Plugins/Process/Linux/NativeThreadLinux.h create mode 100644 source/Plugins/Process/Linux/ProcFileReader.cpp create mode 100644 source/Plugins/Process/Linux/ProcFileReader.h create mode 100644 source/Plugins/Process/Linux/Procfs.h create mode 100644 source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt create mode 100644 source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp create mode 100644 source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h create mode 100644 source/Plugins/Process/MacOSX-Kernel/Makefile create mode 100644 source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp create mode 100644 source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h create mode 100644 source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp create mode 100644 source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h create mode 100644 source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp create mode 100644 source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h create mode 100644 source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp create mode 100644 source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h create mode 100644 source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp create mode 100644 source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h create mode 100644 source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp create mode 100644 source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h create mode 100644 source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp create mode 100644 source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h create mode 100644 source/Plugins/Process/POSIX/CMakeLists.txt create mode 100644 source/Plugins/Process/POSIX/Makefile create mode 100644 source/Plugins/Process/Utility/CMakeLists.txt create mode 100644 source/Plugins/Process/Utility/Makefile create mode 100644 source/Plugins/Process/Windows/Common/CMakeLists.txt create mode 100644 source/Plugins/Process/Windows/Common/ExceptionRecord.h create mode 100644 source/Plugins/Process/Windows/Common/ProcessWindows.cpp create mode 100644 source/Plugins/Process/Windows/Common/ProcessWindows.h create mode 100644 source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp create mode 100644 source/Plugins/Process/Windows/Common/ProcessWindowsLog.h create mode 100644 source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp create mode 100644 source/Plugins/Process/Windows/Common/RegisterContextWindows.h create mode 100644 source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp create mode 100644 source/Plugins/Process/Windows/Common/TargetThreadWindows.h create mode 100644 source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp create mode 100644 source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h create mode 100644 source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp create mode 100644 source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h create mode 100644 source/Plugins/Process/Windows/Live/CMakeLists.txt create mode 100644 source/Plugins/Process/Windows/Live/DebuggerThread.cpp create mode 100644 source/Plugins/Process/Windows/Live/DebuggerThread.h create mode 100644 source/Plugins/Process/Windows/Live/ForwardDecl.h create mode 100644 source/Plugins/Process/Windows/Live/IDebugDelegate.h create mode 100644 source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp create mode 100644 source/Plugins/Process/Windows/Live/LocalDebugDelegate.h create mode 100644 source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp create mode 100644 source/Plugins/Process/Windows/Live/ProcessWindowsLive.h create mode 100644 source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp create mode 100644 source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h create mode 100644 source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp create mode 100644 source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h create mode 100644 source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp create mode 100644 source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h create mode 100644 source/Plugins/Process/Windows/MiniDump/CMakeLists.txt create mode 100644 source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp create mode 100644 source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h create mode 100644 source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp create mode 100644 source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h create mode 100644 source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp create mode 100644 source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h create mode 100644 source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp create mode 100644 source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h create mode 100644 source/Plugins/Process/elf-core/CMakeLists.txt create mode 100644 source/Plugins/Process/elf-core/Makefile create mode 100644 source/Plugins/Process/gdb-remote/CMakeLists.txt create mode 100644 source/Plugins/Process/gdb-remote/Makefile create mode 100644 source/Plugins/Process/mach-core/CMakeLists.txt create mode 100644 source/Plugins/Process/mach-core/Makefile create mode 100644 source/Plugins/Process/mach-core/ProcessMachCore.cpp create mode 100644 source/Plugins/Process/mach-core/ProcessMachCore.h create mode 100644 source/Plugins/Process/mach-core/ThreadMachCore.cpp create mode 100644 source/Plugins/Process/mach-core/ThreadMachCore.h create mode 100644 source/Plugins/ScriptInterpreter/CMakeLists.txt create mode 100644 source/Plugins/ScriptInterpreter/None/CMakeLists.txt create mode 100644 source/Plugins/ScriptInterpreter/None/Makefile create mode 100644 source/Plugins/ScriptInterpreter/Python/CMakeLists.txt create mode 100644 source/Plugins/ScriptInterpreter/Python/Makefile create mode 100644 source/Plugins/SymbolFile/CMakeLists.txt create mode 100644 source/Plugins/SymbolFile/DWARF/CMakeLists.txt create mode 100644 source/Plugins/SymbolFile/DWARF/Makefile create mode 100644 source/Plugins/SymbolFile/Symtab/CMakeLists.txt create mode 100644 source/Plugins/SymbolFile/Symtab/Makefile create mode 100644 source/Plugins/SymbolVendor/CMakeLists.txt create mode 100644 source/Plugins/SymbolVendor/ELF/CMakeLists.txt create mode 100644 source/Plugins/SymbolVendor/ELF/Makefile create mode 100644 source/Plugins/SymbolVendor/MacOSX/CMakeLists.txt create mode 100644 source/Plugins/SymbolVendor/MacOSX/Makefile create mode 100644 source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp create mode 100644 source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h create mode 100644 source/Plugins/SystemRuntime/CMakeLists.txt create mode 100644 source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.cpp create mode 100644 source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.h create mode 100644 source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.cpp create mode 100644 source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.h create mode 100644 source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.cpp create mode 100644 source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.h create mode 100644 source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp create mode 100644 source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.h create mode 100644 source/Plugins/SystemRuntime/MacOSX/CMakeLists.txt create mode 100644 source/Plugins/SystemRuntime/MacOSX/Makefile create mode 100644 source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp create mode 100644 source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h create mode 100644 source/Plugins/UnwindAssembly/CMakeLists.txt create mode 100644 source/Plugins/UnwindAssembly/InstEmulation/CMakeLists.txt create mode 100644 source/Plugins/UnwindAssembly/InstEmulation/Makefile create mode 100644 source/Plugins/UnwindAssembly/x86/CMakeLists.txt create mode 100644 source/Plugins/UnwindAssembly/x86/Makefile create mode 100644 source/Symbol/CMakeLists.txt create mode 100644 source/Symbol/Makefile create mode 100644 source/Target/CMakeLists.txt create mode 100644 source/Target/Makefile create mode 100644 source/Utility/CMakeLists.txt create mode 100644 source/Utility/Makefile create mode 100644 test/CMakeLists.txt create mode 100755 test/dotest.py create mode 120000 test/testcases create mode 100644 test/use_lldb_suite.py create mode 100644 third_party/Python/module/pexpect-2.4/ANSI.py create mode 100644 third_party/Python/module/pexpect-2.4/FSM.py create mode 100644 third_party/Python/module/pexpect-2.4/INSTALL create mode 100644 third_party/Python/module/pexpect-2.4/LICENSE create mode 100644 third_party/Python/module/pexpect-2.4/PKG-INFO create mode 100644 third_party/Python/module/pexpect-2.4/README create mode 100644 third_party/Python/module/pexpect-2.4/doc/clean.css create mode 100644 third_party/Python/module/pexpect-2.4/doc/email.png create mode 100644 third_party/Python/module/pexpect-2.4/doc/examples.html create mode 100644 third_party/Python/module/pexpect-2.4/doc/index.html create mode 100644 third_party/Python/module/pexpect-2.4/doc/index.template.html create mode 100644 third_party/Python/module/pexpect-2.4/examples/README create mode 100644 third_party/Python/module/pexpect-2.4/examples/astat.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/bd_client.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/bd_serv.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/cgishell.cgi create mode 100644 third_party/Python/module/pexpect-2.4/examples/chess.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/chess2.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/chess3.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/df.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/fix_cvs_files.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/ftp.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/hive.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/monitor.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/passmass.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/python.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/rippy.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/script.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/ssh_session.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/ssh_tunnel.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/sshls.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/table_test.html create mode 100644 third_party/Python/module/pexpect-2.4/examples/topip.py create mode 100644 third_party/Python/module/pexpect-2.4/examples/uptime.py create mode 100644 third_party/Python/module/pexpect-2.4/fdpexpect.py create mode 100644 third_party/Python/module/pexpect-2.4/pexpect.py create mode 100644 third_party/Python/module/pexpect-2.4/pxssh.py create mode 100644 third_party/Python/module/pexpect-2.4/screen.py create mode 100644 third_party/Python/module/pexpect-2.4/setup.py create mode 100644 third_party/Python/module/progress/progress.py create mode 100644 third_party/Python/module/six/LICENSE create mode 100644 third_party/Python/module/six/six.py create mode 100644 third_party/Python/module/unittest2/unittest2/__init__.py create mode 100644 third_party/Python/module/unittest2/unittest2/__main__.py create mode 100644 third_party/Python/module/unittest2/unittest2/case.py create mode 100644 third_party/Python/module/unittest2/unittest2/collector.py create mode 100644 third_party/Python/module/unittest2/unittest2/compatibility.py create mode 100644 third_party/Python/module/unittest2/unittest2/loader.py create mode 100644 third_party/Python/module/unittest2/unittest2/main.py create mode 100644 third_party/Python/module/unittest2/unittest2/result.py create mode 100644 third_party/Python/module/unittest2/unittest2/runner.py create mode 100644 third_party/Python/module/unittest2/unittest2/signals.py create mode 100644 third_party/Python/module/unittest2/unittest2/suite.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/__init__.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/dummy.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/support.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_assertions.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_break.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_case.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_discovery.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_functiontestcase.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_loader.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_new_tests.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_program.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_result.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_runner.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_setups.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_skipping.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_suite.py create mode 100644 third_party/Python/module/unittest2/unittest2/test/test_unittest2_with.py create mode 100644 third_party/Python/module/unittest2/unittest2/util.py create mode 100644 tools/CMakeLists.txt create mode 100644 tools/Makefile create mode 100644 tools/argdumper/CMakeLists.txt create mode 100644 tools/darwin-debug/CMakeLists.txt create mode 100644 tools/darwin-debug/darwin-debug.cpp create mode 100644 tools/darwin-threads/examine-threads.c create mode 100644 tools/debugserver/CMakeLists.txt create mode 100644 tools/debugserver/Makefile create mode 100644 tools/debugserver/debugnub-exports create mode 100644 tools/debugserver/debugserver.xcodeproj/project.pbxproj create mode 100644 tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme create mode 100644 tools/debugserver/resources/lldb-debugserver-Info.plist create mode 100644 tools/debugserver/scripts/diagnose-termination.d create mode 100644 tools/debugserver/source/ARM_DWARF_Registers.h create mode 100644 tools/debugserver/source/ARM_ehframe_Registers.h create mode 100644 tools/debugserver/source/CMakeLists.txt create mode 100644 tools/debugserver/source/ChangeLog create mode 100644 tools/debugserver/source/DNB.cpp create mode 100644 tools/debugserver/source/DNB.h create mode 100644 tools/debugserver/source/DNBArch.cpp create mode 100644 tools/debugserver/source/DNBArch.h create mode 100644 tools/debugserver/source/DNBBreakpoint.cpp create mode 100644 tools/debugserver/source/DNBBreakpoint.h create mode 100644 tools/debugserver/source/DNBDataRef.cpp create mode 100644 tools/debugserver/source/DNBDataRef.h create mode 100644 tools/debugserver/source/DNBDefs.h create mode 100644 tools/debugserver/source/DNBError.cpp create mode 100644 tools/debugserver/source/DNBError.h create mode 100644 tools/debugserver/source/DNBLog.cpp create mode 100644 tools/debugserver/source/DNBLog.h create mode 100644 tools/debugserver/source/DNBRegisterInfo.cpp create mode 100644 tools/debugserver/source/DNBRegisterInfo.h create mode 100644 tools/debugserver/source/DNBRuntimeAction.h create mode 100644 tools/debugserver/source/DNBThreadResumeActions.cpp create mode 100644 tools/debugserver/source/DNBThreadResumeActions.h create mode 100644 tools/debugserver/source/DNBTimer.h create mode 100644 tools/debugserver/source/JSONGenerator.h create mode 100644 tools/debugserver/source/MacOSX/CFBundle.cpp create mode 100644 tools/debugserver/source/MacOSX/CFBundle.h create mode 100644 tools/debugserver/source/MacOSX/CFData.cpp create mode 100644 tools/debugserver/source/MacOSX/CFData.h create mode 100644 tools/debugserver/source/MacOSX/CFString.cpp create mode 100644 tools/debugserver/source/MacOSX/CFString.h create mode 100644 tools/debugserver/source/MacOSX/CFUtils.h create mode 100644 tools/debugserver/source/MacOSX/CMakeLists.txt create mode 100644 tools/debugserver/source/MacOSX/Genealogy.cpp create mode 100644 tools/debugserver/source/MacOSX/Genealogy.h create mode 100644 tools/debugserver/source/MacOSX/GenealogySPI.h create mode 100644 tools/debugserver/source/MacOSX/HasAVX.h create mode 100644 tools/debugserver/source/MacOSX/HasAVX.s create mode 100644 tools/debugserver/source/MacOSX/MachException.cpp create mode 100644 tools/debugserver/source/MacOSX/MachException.h create mode 100644 tools/debugserver/source/MacOSX/MachProcess.h create mode 100644 tools/debugserver/source/MacOSX/MachProcess.mm create mode 100644 tools/debugserver/source/MacOSX/MachTask.h create mode 100644 tools/debugserver/source/MacOSX/MachTask.mm create mode 100644 tools/debugserver/source/MacOSX/MachThread.cpp create mode 100644 tools/debugserver/source/MacOSX/MachThread.h create mode 100644 tools/debugserver/source/MacOSX/MachThreadList.cpp create mode 100644 tools/debugserver/source/MacOSX/MachThreadList.h create mode 100644 tools/debugserver/source/MacOSX/MachVMMemory.cpp create mode 100644 tools/debugserver/source/MacOSX/MachVMMemory.h create mode 100644 tools/debugserver/source/MacOSX/MachVMRegion.cpp create mode 100644 tools/debugserver/source/MacOSX/MachVMRegion.h create mode 100644 tools/debugserver/source/MacOSX/Makefile create mode 100644 tools/debugserver/source/MacOSX/ThreadInfo.h create mode 100644 tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp create mode 100644 tools/debugserver/source/MacOSX/arm/DNBArchImpl.h create mode 100644 tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp create mode 100644 tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h create mode 100644 tools/debugserver/source/MacOSX/dbgnub-mig.defs create mode 100644 tools/debugserver/source/MacOSX/i386/CMakeLists.txt create mode 100644 tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp create mode 100644 tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h create mode 100644 tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h create mode 100644 tools/debugserver/source/MacOSX/i386/Makefile create mode 100644 tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp create mode 100644 tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h create mode 100644 tools/debugserver/source/MacOSX/stack_logging.h create mode 100644 tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt create mode 100644 tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp create mode 100644 tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h create mode 100644 tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h create mode 100644 tools/debugserver/source/MacOSX/x86_64/Makefile create mode 100644 tools/debugserver/source/Makefile create mode 100644 tools/debugserver/source/PThreadCondition.h create mode 100644 tools/debugserver/source/PThreadEvent.cpp create mode 100644 tools/debugserver/source/PThreadEvent.h create mode 100644 tools/debugserver/source/PThreadMutex.cpp create mode 100644 tools/debugserver/source/PThreadMutex.h create mode 100644 tools/debugserver/source/PseudoTerminal.cpp create mode 100644 tools/debugserver/source/PseudoTerminal.h create mode 100644 tools/debugserver/source/RNBContext.cpp create mode 100644 tools/debugserver/source/RNBContext.h create mode 100644 tools/debugserver/source/RNBDefs.h create mode 100644 tools/debugserver/source/RNBRemote.cpp create mode 100644 tools/debugserver/source/RNBRemote.h create mode 100644 tools/debugserver/source/RNBServices.cpp create mode 100644 tools/debugserver/source/RNBServices.h create mode 100644 tools/debugserver/source/RNBSocket.cpp create mode 100644 tools/debugserver/source/RNBSocket.h create mode 100644 tools/debugserver/source/SysSignal.cpp create mode 100644 tools/debugserver/source/SysSignal.h create mode 100644 tools/debugserver/source/TTYState.cpp create mode 100644 tools/debugserver/source/TTYState.h create mode 100644 tools/debugserver/source/com.apple.debugserver.applist.internal.plist create mode 100644 tools/debugserver/source/com.apple.debugserver.applist.plist create mode 100644 tools/debugserver/source/com.apple.debugserver.internal.plist create mode 100644 tools/debugserver/source/com.apple.debugserver.plist create mode 100644 tools/debugserver/source/com.apple.debugserver.posix.plist create mode 100644 tools/debugserver/source/debugserver-entitlements.plist create mode 100644 tools/debugserver/source/debugserver-macosx-entitlements.plist create mode 100644 tools/debugserver/source/debugserver.cpp create mode 100644 tools/debugserver/source/libdebugserver.cpp create mode 100644 tools/debugserver/source/libdebugserver.h create mode 100644 tools/driver/CMakeLists.txt create mode 100644 tools/driver/Makefile create mode 100644 tools/driver/lldb-Info.plist create mode 100644 tools/install-headers/Makefile create mode 100644 tools/lldb-mi/CMakeLists.txt create mode 100644 tools/lldb-mi/Makefile create mode 100644 tools/lldb-mi/lldb-Info.plist create mode 100644 tools/lldb-perf/README create mode 100755 tools/lldb-perf/common/clang/build-clang.sh create mode 100644 tools/lldb-perf/common/clang/lldb_perf_clang.cpp create mode 100644 tools/lldb-perf/common/clang/main.cpp create mode 100644 tools/lldb-perf/common/stepping/lldb-perf-stepping.cpp create mode 100644 tools/lldb-perf/common/stepping/stepping-testcase.cpp create mode 100644 tools/lldb-perf/darwin/formatters/fmts_tester.mm create mode 100644 tools/lldb-perf/darwin/formatters/formatters.cpp create mode 100644 tools/lldb-perf/darwin/sketch/foobar.sketch2 create mode 100644 tools/lldb-perf/darwin/sketch/sketch.cpp create mode 100644 tools/lldb-perf/lib/Gauge.cpp create mode 100644 tools/lldb-perf/lib/Gauge.h create mode 100644 tools/lldb-perf/lib/Measurement.h create mode 100644 tools/lldb-perf/lib/MemoryGauge.cpp create mode 100644 tools/lldb-perf/lib/MemoryGauge.h create mode 100644 tools/lldb-perf/lib/Metric.cpp create mode 100644 tools/lldb-perf/lib/Metric.h create mode 100644 tools/lldb-perf/lib/Results.cpp create mode 100644 tools/lldb-perf/lib/Results.h create mode 100644 tools/lldb-perf/lib/TestCase.cpp create mode 100644 tools/lldb-perf/lib/TestCase.h create mode 100644 tools/lldb-perf/lib/Timer.cpp create mode 100644 tools/lldb-perf/lib/Timer.h create mode 100644 tools/lldb-perf/lib/Xcode.cpp create mode 100644 tools/lldb-perf/lib/Xcode.h create mode 100644 tools/lldb-perf/lldbperf.xcodeproj/project.pbxproj create mode 100644 tools/lldb-server/CMakeLists.txt create mode 100644 tools/lldb-server/Makefile create mode 100644 unittests/CMakeLists.txt create mode 100644 unittests/Editline/CMakeLists.txt create mode 100644 unittests/Editline/EditlineTest.cpp create mode 100644 unittests/Expression/CMakeLists.txt create mode 100644 unittests/Expression/GoParserTest.cpp create mode 100644 unittests/Host/CMakeLists.txt create mode 100644 unittests/Host/SocketAddressTest.cpp create mode 100644 unittests/Host/SocketTest.cpp create mode 100644 unittests/Host/SymbolsTest.cpp create mode 100644 unittests/Interpreter/CMakeLists.txt create mode 100644 unittests/Interpreter/TestArgs.cpp create mode 100644 unittests/ScriptInterpreter/CMakeLists.txt create mode 100644 unittests/ScriptInterpreter/Python/CMakeLists.txt create mode 100644 unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp create mode 100644 unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp create mode 100644 unittests/ScriptInterpreter/Python/PythonTestSuite.cpp create mode 100644 unittests/ScriptInterpreter/Python/PythonTestSuite.h create mode 100644 unittests/Utility/CMakeLists.txt create mode 100644 unittests/Utility/StringExtractorTest.cpp create mode 100644 unittests/Utility/TaskPoolTest.cpp create mode 100644 unittests/Utility/UriParserTest.cpp create mode 100644 unittests/gtest_common.h create mode 100644 use_lldb_suite_root.py create mode 100755 utils/git-svn/convert.py create mode 100644 utils/lui/Readme create mode 100644 utils/lui/breakwin.py create mode 100644 utils/lui/commandwin.py create mode 100755 utils/lui/cui.py create mode 100644 utils/lui/debuggerdriver.py create mode 100644 utils/lui/eventwin.py create mode 100644 utils/lui/lldbutil.py create mode 100755 utils/lui/lui.py create mode 100755 utils/lui/sandbox.py create mode 100644 utils/lui/sourcewin.py create mode 100644 utils/lui/statuswin.py create mode 100755 utils/misc/grep-svn-log.py create mode 100644 utils/sync-source/README.txt create mode 100644 utils/sync-source/lib/transfer/__init__.py create mode 100644 utils/sync-source/lib/transfer/protocol.py create mode 100644 utils/sync-source/lib/transfer/rsync.py create mode 100644 utils/sync-source/lib/transfer/transfer_spec.py create mode 100644 utils/sync-source/pylintrc create mode 100644 utils/sync-source/syncsource.py create mode 100644 utils/test/README-disasm create mode 100644 utils/test/README-lldb-disasm create mode 100644 utils/test/README-run-until-faulted create mode 100755 utils/test/disasm.py create mode 100755 utils/test/lldb-disasm.py create mode 100755 utils/test/llvm-mc-shell.py create mode 100644 utils/test/main.c create mode 100755 utils/test/ras.py create mode 100755 utils/test/run-dis.py create mode 100755 utils/test/run-until-faulted.py create mode 100644 utils/vim-lldb/README create mode 100644 utils/vim-lldb/doc/lldb.txt create mode 100644 utils/vim-lldb/plugin/lldb.vim create mode 100644 utils/vim-lldb/python-vim-lldb/import_lldb.py create mode 100644 utils/vim-lldb/python-vim-lldb/lldb_controller.py create mode 100644 utils/vim-lldb/python-vim-lldb/plugin.py create mode 100644 utils/vim-lldb/python-vim-lldb/vim_panes.py create mode 100644 utils/vim-lldb/python-vim-lldb/vim_signs.py create mode 100644 utils/vim-lldb/python-vim-lldb/vim_ui.py create mode 100644 www/SB-api-coding-rules.html create mode 100644 www/adding-language-support.html create mode 100755 www/architecture.html create mode 100755 www/architecture/index.html create mode 100644 www/architecture/varformats.html create mode 100755 www/build.html create mode 100644 www/cpp_reference/html/LLDB_8h.html create mode 100644 www/cpp_reference/html/LLDB_8h__incl.map create mode 100644 www/cpp_reference/html/LLDB_8h__incl.md5 create mode 100644 www/cpp_reference/html/LLDB_8h__incl.png create mode 100644 www/cpp_reference/html/LLDB_8h_source.html create mode 100644 www/cpp_reference/html/SBAddress_8h.html create mode 100644 www/cpp_reference/html/SBAddress_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBAddress_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBAddress_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBAddress_8h__incl.map create mode 100644 www/cpp_reference/html/SBAddress_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBAddress_8h__incl.png create mode 100644 www/cpp_reference/html/SBAddress_8h_source.html create mode 100644 www/cpp_reference/html/SBBlock_8h.html create mode 100644 www/cpp_reference/html/SBBlock_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBBlock_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBBlock_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBBlock_8h__incl.map create mode 100644 www/cpp_reference/html/SBBlock_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBBlock_8h__incl.png create mode 100644 www/cpp_reference/html/SBBlock_8h_source.html create mode 100644 www/cpp_reference/html/SBBreakpointLocation_8h.html create mode 100644 www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBBreakpointLocation_8h__incl.map create mode 100644 www/cpp_reference/html/SBBreakpointLocation_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBBreakpointLocation_8h__incl.png create mode 100644 www/cpp_reference/html/SBBreakpointLocation_8h_source.html create mode 100644 www/cpp_reference/html/SBBreakpoint_8h.html create mode 100644 www/cpp_reference/html/SBBreakpoint_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBBreakpoint_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBBreakpoint_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBBreakpoint_8h__incl.map create mode 100644 www/cpp_reference/html/SBBreakpoint_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBBreakpoint_8h__incl.png create mode 100644 www/cpp_reference/html/SBBreakpoint_8h_source.html create mode 100644 www/cpp_reference/html/SBBroadcaster_8h.html create mode 100644 www/cpp_reference/html/SBBroadcaster_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBBroadcaster_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBBroadcaster_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBBroadcaster_8h__incl.map create mode 100644 www/cpp_reference/html/SBBroadcaster_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBBroadcaster_8h__incl.png create mode 100644 www/cpp_reference/html/SBBroadcaster_8h_source.html create mode 100644 www/cpp_reference/html/SBCommandInterpreter_8h.html create mode 100644 www/cpp_reference/html/SBCommandInterpreter_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBCommandInterpreter_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBCommandInterpreter_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBCommandInterpreter_8h__incl.map create mode 100644 www/cpp_reference/html/SBCommandInterpreter_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBCommandInterpreter_8h__incl.png create mode 100644 www/cpp_reference/html/SBCommandInterpreter_8h_source.html create mode 100644 www/cpp_reference/html/SBCommandReturnObject_8h.html create mode 100644 www/cpp_reference/html/SBCommandReturnObject_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBCommandReturnObject_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBCommandReturnObject_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBCommandReturnObject_8h__incl.map create mode 100644 www/cpp_reference/html/SBCommandReturnObject_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBCommandReturnObject_8h__incl.png create mode 100644 www/cpp_reference/html/SBCommandReturnObject_8h_source.html create mode 100644 www/cpp_reference/html/SBCommunication_8h.html create mode 100644 www/cpp_reference/html/SBCommunication_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBCommunication_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBCommunication_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBCommunication_8h__incl.map create mode 100644 www/cpp_reference/html/SBCommunication_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBCommunication_8h__incl.png create mode 100644 www/cpp_reference/html/SBCommunication_8h_source.html create mode 100644 www/cpp_reference/html/SBCompileUnit_8h.html create mode 100644 www/cpp_reference/html/SBCompileUnit_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBCompileUnit_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBCompileUnit_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBCompileUnit_8h__incl.map create mode 100644 www/cpp_reference/html/SBCompileUnit_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBCompileUnit_8h__incl.png create mode 100644 www/cpp_reference/html/SBCompileUnit_8h_source.html create mode 100644 www/cpp_reference/html/SBData_8h.html create mode 100644 www/cpp_reference/html/SBData_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBData_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBData_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBData_8h__incl.map create mode 100644 www/cpp_reference/html/SBData_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBData_8h__incl.png create mode 100644 www/cpp_reference/html/SBData_8h_source.html create mode 100644 www/cpp_reference/html/SBDebugger_8h.html create mode 100644 www/cpp_reference/html/SBDebugger_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBDebugger_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBDebugger_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBDebugger_8h__incl.map create mode 100644 www/cpp_reference/html/SBDebugger_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBDebugger_8h__incl.png create mode 100644 www/cpp_reference/html/SBDebugger_8h_source.html create mode 100644 www/cpp_reference/html/SBDeclaration_8h.html create mode 100644 www/cpp_reference/html/SBDeclaration_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBDeclaration_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBDeclaration_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBDeclaration_8h__incl.map create mode 100644 www/cpp_reference/html/SBDeclaration_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBDeclaration_8h__incl.png create mode 100644 www/cpp_reference/html/SBDeclaration_8h_source.html create mode 100644 www/cpp_reference/html/SBDefines_8h.html create mode 100644 www/cpp_reference/html/SBDefines_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBDefines_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBDefines_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBDefines_8h__incl.map create mode 100644 www/cpp_reference/html/SBDefines_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBDefines_8h__incl.png create mode 100644 www/cpp_reference/html/SBDefines_8h_source.html create mode 100644 www/cpp_reference/html/SBError_8h.html create mode 100644 www/cpp_reference/html/SBError_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBError_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBError_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBError_8h__incl.map create mode 100644 www/cpp_reference/html/SBError_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBError_8h__incl.png create mode 100644 www/cpp_reference/html/SBError_8h_source.html create mode 100644 www/cpp_reference/html/SBEvent_8h.html create mode 100644 www/cpp_reference/html/SBEvent_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBEvent_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBEvent_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBEvent_8h__incl.map create mode 100644 www/cpp_reference/html/SBEvent_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBEvent_8h__incl.png create mode 100644 www/cpp_reference/html/SBEvent_8h_source.html create mode 100644 www/cpp_reference/html/SBExpressionOptions_8h.html create mode 100644 www/cpp_reference/html/SBExpressionOptions_8h__incl.map create mode 100644 www/cpp_reference/html/SBExpressionOptions_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBExpressionOptions_8h__incl.png create mode 100644 www/cpp_reference/html/SBExpressionOptions_8h_source.html create mode 100644 www/cpp_reference/html/SBFileSpecList_8h.html create mode 100644 www/cpp_reference/html/SBFileSpecList_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBFileSpecList_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBFileSpecList_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBFileSpecList_8h__incl.map create mode 100644 www/cpp_reference/html/SBFileSpecList_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBFileSpecList_8h__incl.png create mode 100644 www/cpp_reference/html/SBFileSpecList_8h_source.html create mode 100644 www/cpp_reference/html/SBFileSpec_8h.html create mode 100644 www/cpp_reference/html/SBFileSpec_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBFileSpec_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBFileSpec_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBFileSpec_8h__incl.map create mode 100644 www/cpp_reference/html/SBFileSpec_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBFileSpec_8h__incl.png create mode 100644 www/cpp_reference/html/SBFileSpec_8h_source.html create mode 100644 www/cpp_reference/html/SBFrame_8h.html create mode 100644 www/cpp_reference/html/SBFrame_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBFrame_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBFrame_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBFrame_8h__incl.map create mode 100644 www/cpp_reference/html/SBFrame_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBFrame_8h__incl.png create mode 100644 www/cpp_reference/html/SBFrame_8h_source.html create mode 100644 www/cpp_reference/html/SBFunction_8h.html create mode 100644 www/cpp_reference/html/SBFunction_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBFunction_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBFunction_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBFunction_8h__incl.map create mode 100644 www/cpp_reference/html/SBFunction_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBFunction_8h__incl.png create mode 100644 www/cpp_reference/html/SBFunction_8h_source.html create mode 100644 www/cpp_reference/html/SBHostOS_8h.html create mode 100644 www/cpp_reference/html/SBHostOS_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBHostOS_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBHostOS_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBHostOS_8h__incl.map create mode 100644 www/cpp_reference/html/SBHostOS_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBHostOS_8h__incl.png create mode 100644 www/cpp_reference/html/SBHostOS_8h_source.html create mode 100644 www/cpp_reference/html/SBInputReader_8h.html create mode 100644 www/cpp_reference/html/SBInputReader_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBInputReader_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBInputReader_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBInputReader_8h__incl.map create mode 100644 www/cpp_reference/html/SBInputReader_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBInputReader_8h__incl.png create mode 100644 www/cpp_reference/html/SBInputReader_8h_source.html create mode 100644 www/cpp_reference/html/SBInstructionList_8h.html create mode 100644 www/cpp_reference/html/SBInstructionList_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBInstructionList_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBInstructionList_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBInstructionList_8h__incl.map create mode 100644 www/cpp_reference/html/SBInstructionList_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBInstructionList_8h__incl.png create mode 100644 www/cpp_reference/html/SBInstructionList_8h_source.html create mode 100644 www/cpp_reference/html/SBInstruction_8h.html create mode 100644 www/cpp_reference/html/SBInstruction_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBInstruction_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBInstruction_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBInstruction_8h__incl.map create mode 100644 www/cpp_reference/html/SBInstruction_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBInstruction_8h__incl.png create mode 100644 www/cpp_reference/html/SBInstruction_8h_source.html create mode 100644 www/cpp_reference/html/SBLineEntry_8h.html create mode 100644 www/cpp_reference/html/SBLineEntry_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBLineEntry_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBLineEntry_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBLineEntry_8h__incl.map create mode 100644 www/cpp_reference/html/SBLineEntry_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBLineEntry_8h__incl.png create mode 100644 www/cpp_reference/html/SBLineEntry_8h_source.html create mode 100644 www/cpp_reference/html/SBListener_8h.html create mode 100644 www/cpp_reference/html/SBListener_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBListener_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBListener_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBListener_8h__incl.map create mode 100644 www/cpp_reference/html/SBListener_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBListener_8h__incl.png create mode 100644 www/cpp_reference/html/SBListener_8h_source.html create mode 100644 www/cpp_reference/html/SBModuleSpec_8h.html create mode 100644 www/cpp_reference/html/SBModuleSpec_8h__incl.map create mode 100644 www/cpp_reference/html/SBModuleSpec_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBModuleSpec_8h__incl.png create mode 100644 www/cpp_reference/html/SBModuleSpec_8h_source.html create mode 100644 www/cpp_reference/html/SBModule_8h.html create mode 100644 www/cpp_reference/html/SBModule_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBModule_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBModule_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBModule_8h__incl.map create mode 100644 www/cpp_reference/html/SBModule_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBModule_8h__incl.png create mode 100644 www/cpp_reference/html/SBModule_8h_source.html create mode 100644 www/cpp_reference/html/SBProcess_8h.html create mode 100644 www/cpp_reference/html/SBProcess_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBProcess_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBProcess_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBProcess_8h__incl.map create mode 100644 www/cpp_reference/html/SBProcess_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBProcess_8h__incl.png create mode 100644 www/cpp_reference/html/SBProcess_8h_source.html create mode 100644 www/cpp_reference/html/SBSection_8h.html create mode 100644 www/cpp_reference/html/SBSection_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBSection_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBSection_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBSection_8h__incl.map create mode 100644 www/cpp_reference/html/SBSection_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBSection_8h__incl.png create mode 100644 www/cpp_reference/html/SBSection_8h_source.html create mode 100644 www/cpp_reference/html/SBSourceManager_8h.html create mode 100644 www/cpp_reference/html/SBSourceManager_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBSourceManager_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBSourceManager_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBSourceManager_8h__incl.map create mode 100644 www/cpp_reference/html/SBSourceManager_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBSourceManager_8h__incl.png create mode 100644 www/cpp_reference/html/SBSourceManager_8h_source.html create mode 100644 www/cpp_reference/html/SBStream_8h.html create mode 100644 www/cpp_reference/html/SBStream_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBStream_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBStream_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBStream_8h__incl.map create mode 100644 www/cpp_reference/html/SBStream_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBStream_8h__incl.png create mode 100644 www/cpp_reference/html/SBStream_8h_source.html create mode 100644 www/cpp_reference/html/SBStringList_8h.html create mode 100644 www/cpp_reference/html/SBStringList_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBStringList_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBStringList_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBStringList_8h__incl.map create mode 100644 www/cpp_reference/html/SBStringList_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBStringList_8h__incl.png create mode 100644 www/cpp_reference/html/SBStringList_8h_source.html create mode 100644 www/cpp_reference/html/SBSymbolContextList_8h.html create mode 100644 www/cpp_reference/html/SBSymbolContextList_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBSymbolContextList_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBSymbolContextList_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBSymbolContextList_8h__incl.map create mode 100644 www/cpp_reference/html/SBSymbolContextList_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBSymbolContextList_8h__incl.png create mode 100644 www/cpp_reference/html/SBSymbolContextList_8h_source.html create mode 100644 www/cpp_reference/html/SBSymbolContext_8h.html create mode 100644 www/cpp_reference/html/SBSymbolContext_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBSymbolContext_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBSymbolContext_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBSymbolContext_8h__incl.map create mode 100644 www/cpp_reference/html/SBSymbolContext_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBSymbolContext_8h__incl.png create mode 100644 www/cpp_reference/html/SBSymbolContext_8h_source.html create mode 100644 www/cpp_reference/html/SBSymbol_8h.html create mode 100644 www/cpp_reference/html/SBSymbol_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBSymbol_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBSymbol_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBSymbol_8h__incl.map create mode 100644 www/cpp_reference/html/SBSymbol_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBSymbol_8h__incl.png create mode 100644 www/cpp_reference/html/SBSymbol_8h_source.html create mode 100644 www/cpp_reference/html/SBTarget_8h.html create mode 100644 www/cpp_reference/html/SBTarget_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBTarget_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBTarget_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBTarget_8h__incl.map create mode 100644 www/cpp_reference/html/SBTarget_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBTarget_8h__incl.png create mode 100644 www/cpp_reference/html/SBTarget_8h_source.html create mode 100644 www/cpp_reference/html/SBThread_8h.html create mode 100644 www/cpp_reference/html/SBThread_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBThread_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBThread_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBThread_8h__incl.map create mode 100644 www/cpp_reference/html/SBThread_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBThread_8h__incl.png create mode 100644 www/cpp_reference/html/SBThread_8h_source.html create mode 100644 www/cpp_reference/html/SBTypeCategory_8h.html create mode 100644 www/cpp_reference/html/SBTypeCategory_8h__incl.map create mode 100644 www/cpp_reference/html/SBTypeCategory_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBTypeCategory_8h__incl.png create mode 100644 www/cpp_reference/html/SBTypeCategory_8h_source.html create mode 100644 www/cpp_reference/html/SBTypeFilter_8h.html create mode 100644 www/cpp_reference/html/SBTypeFilter_8h__incl.map create mode 100644 www/cpp_reference/html/SBTypeFilter_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBTypeFilter_8h__incl.png create mode 100644 www/cpp_reference/html/SBTypeFilter_8h_source.html create mode 100644 www/cpp_reference/html/SBTypeFormat_8h.html create mode 100644 www/cpp_reference/html/SBTypeFormat_8h__incl.map create mode 100644 www/cpp_reference/html/SBTypeFormat_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBTypeFormat_8h__incl.png create mode 100644 www/cpp_reference/html/SBTypeFormat_8h_source.html create mode 100644 www/cpp_reference/html/SBTypeNameSpecifier_8h.html create mode 100644 www/cpp_reference/html/SBTypeNameSpecifier_8h__incl.map create mode 100644 www/cpp_reference/html/SBTypeNameSpecifier_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBTypeNameSpecifier_8h__incl.png create mode 100644 www/cpp_reference/html/SBTypeNameSpecifier_8h_source.html create mode 100644 www/cpp_reference/html/SBTypeSummary_8h.html create mode 100644 www/cpp_reference/html/SBTypeSummary_8h__incl.map create mode 100644 www/cpp_reference/html/SBTypeSummary_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBTypeSummary_8h__incl.png create mode 100644 www/cpp_reference/html/SBTypeSummary_8h_source.html create mode 100644 www/cpp_reference/html/SBTypeSynthetic_8h.html create mode 100644 www/cpp_reference/html/SBTypeSynthetic_8h__incl.map create mode 100644 www/cpp_reference/html/SBTypeSynthetic_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBTypeSynthetic_8h__incl.png create mode 100644 www/cpp_reference/html/SBTypeSynthetic_8h_source.html create mode 100644 www/cpp_reference/html/SBType_8h.html create mode 100644 www/cpp_reference/html/SBType_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBType_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBType_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBType_8h__incl.map create mode 100644 www/cpp_reference/html/SBType_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBType_8h__incl.png create mode 100644 www/cpp_reference/html/SBType_8h_source.html create mode 100644 www/cpp_reference/html/SBValueList_8h.html create mode 100644 www/cpp_reference/html/SBValueList_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBValueList_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBValueList_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBValueList_8h__incl.map create mode 100644 www/cpp_reference/html/SBValueList_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBValueList_8h__incl.png create mode 100644 www/cpp_reference/html/SBValueList_8h_source.html create mode 100644 www/cpp_reference/html/SBValue_8h.html create mode 100644 www/cpp_reference/html/SBValue_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBValue_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBValue_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBValue_8h__incl.map create mode 100644 www/cpp_reference/html/SBValue_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBValue_8h__incl.png create mode 100644 www/cpp_reference/html/SBValue_8h_source.html create mode 100644 www/cpp_reference/html/SBWatchpoint_8h.html create mode 100644 www/cpp_reference/html/SBWatchpoint_8h__dep__incl.map create mode 100644 www/cpp_reference/html/SBWatchpoint_8h__dep__incl.md5 create mode 100644 www/cpp_reference/html/SBWatchpoint_8h__dep__incl.png create mode 100644 www/cpp_reference/html/SBWatchpoint_8h__incl.map create mode 100644 www/cpp_reference/html/SBWatchpoint_8h__incl.md5 create mode 100644 www/cpp_reference/html/SBWatchpoint_8h__incl.png create mode 100644 www/cpp_reference/html/SBWatchpoint_8h_source.html create mode 100644 www/cpp_reference/html/annotated.html create mode 100644 www/cpp_reference/html/bc_s.png create mode 100644 www/cpp_reference/html/bdwn.png create mode 100644 www/cpp_reference/html/classes.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBAddress-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBAddress.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBAttachInfo-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBAttachInfo.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBAttachInfo__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBAttachInfo__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBAttachInfo__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBBlock-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBBlock.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBBreakpoint-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBBreakpoint.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBBreakpointLocation-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBBreakpointLocation.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBBroadcaster-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBBroadcaster.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommand-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommand.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommandInterpreter-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommandInterpreter.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommandPluginInterface-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommandPluginInterface.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommandReturnObject-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommandReturnObject.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommunication-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCommunication.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCompileUnit-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBCompileUnit.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBData-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBData.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBDebugger-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBDebugger.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBDeclaration-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBDeclaration.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBError-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBError.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBEvent-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBEvent.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBExpressionOptions-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBExpressionOptions.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBFileSpec-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBFileSpec.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBFileSpecList-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBFileSpecList.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBFrame-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBFrame.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBFrame__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBFrame__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBFrame__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBFunction-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBFunction.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBHostOS-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBHostOS.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBInputReader-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBInputReader.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBInstruction-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBInstruction.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBInstructionList-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBInstructionList.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBLaunchInfo-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBLaunchInfo.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBLaunchInfo__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBLaunchInfo__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBLaunchInfo__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBLineEntry-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBLineEntry.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBListener-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBListener.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBModule-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBModule.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBModuleSpec-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBModuleSpec.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBModuleSpecList-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBModuleSpecList.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBProcess-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBProcess.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBProcess__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBProcess__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBProcess__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBSection-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSection.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSourceManager-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSourceManager.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBStream-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBStream.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBStringList-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBStringList.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSymbol-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSymbol.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSymbolContext-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSymbolContext.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSymbolContextList-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBSymbolContextList.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTarget-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTarget.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBThread-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBThread.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBType-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBType.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeCategory-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeCategory.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeCategory__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeCategory__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeCategory__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFilter-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFilter.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFilter__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFilter__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFilter__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFormat-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFormat.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFormat__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFormat__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeFormat__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeList-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeList.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeMember-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeMember.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeMember__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeMember__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeMember__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeNameSpecifier-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeNameSpecifier.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeNameSpecifier__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeNameSpecifier__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeNameSpecifier__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSummary-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSummary.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSummary__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSummary__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSummary__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSynthetic-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSynthetic.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSynthetic__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSynthetic__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBTypeSynthetic__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBType__coll__graph.map create mode 100644 www/cpp_reference/html/classlldb_1_1SBType__coll__graph.md5 create mode 100644 www/cpp_reference/html/classlldb_1_1SBType__coll__graph.png create mode 100644 www/cpp_reference/html/classlldb_1_1SBValue-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBValue.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBValueList-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBValueList.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBWatchpoint-members.html create mode 100644 www/cpp_reference/html/classlldb_1_1SBWatchpoint.html create mode 100644 www/cpp_reference/html/closed.png create mode 100644 www/cpp_reference/html/dir_217b186c19a2bb8bc0ee0f71fb72d4e8.html create mode 100644 www/cpp_reference/html/dir_217b186c19a2bb8bc0ee0f71fb72d4e8_dep.map create mode 100644 www/cpp_reference/html/dir_217b186c19a2bb8bc0ee0f71fb72d4e8_dep.md5 create mode 100644 www/cpp_reference/html/dir_217b186c19a2bb8bc0ee0f71fb72d4e8_dep.png create mode 100644 www/cpp_reference/html/dir_36ad4ea5df2b352cce0512435d34c8a4.html create mode 100644 www/cpp_reference/html/dir_36ad4ea5df2b352cce0512435d34c8a4_dep.map create mode 100644 www/cpp_reference/html/dir_36ad4ea5df2b352cce0512435d34c8a4_dep.md5 create mode 100644 www/cpp_reference/html/dir_36ad4ea5df2b352cce0512435d34c8a4_dep.png create mode 100644 www/cpp_reference/html/dir_8b321541f691a81675dae4ec7a1864bb.html create mode 100644 www/cpp_reference/html/dir_8b321541f691a81675dae4ec7a1864bb_dep.map create mode 100644 www/cpp_reference/html/dir_8b321541f691a81675dae4ec7a1864bb_dep.md5 create mode 100644 www/cpp_reference/html/dir_8b321541f691a81675dae4ec7a1864bb_dep.png create mode 100644 www/cpp_reference/html/dir_9d4b8469db156c557ab1d649b027ec2e.html create mode 100644 www/cpp_reference/html/dir_9d4b8469db156c557ab1d649b027ec2e_dep.map create mode 100644 www/cpp_reference/html/dir_9d4b8469db156c557ab1d649b027ec2e_dep.md5 create mode 100644 www/cpp_reference/html/dir_9d4b8469db156c557ab1d649b027ec2e_dep.png create mode 100644 www/cpp_reference/html/dir_c02e3ff158c3064d7b78c6aa6fc411e6.html create mode 100644 www/cpp_reference/html/dir_c02e3ff158c3064d7b78c6aa6fc411e6_dep.map create mode 100644 www/cpp_reference/html/dir_c02e3ff158c3064d7b78c6aa6fc411e6_dep.md5 create mode 100644 www/cpp_reference/html/dir_c02e3ff158c3064d7b78c6aa6fc411e6_dep.png create mode 100644 www/cpp_reference/html/dir_fa64c3fa8a988674a1a867b97ca9a790.html create mode 100644 www/cpp_reference/html/dir_fa64c3fa8a988674a1a867b97ca9a790_dep.map create mode 100644 www/cpp_reference/html/dir_fa64c3fa8a988674a1a867b97ca9a790_dep.md5 create mode 100644 www/cpp_reference/html/dir_fa64c3fa8a988674a1a867b97ca9a790_dep.png create mode 100644 www/cpp_reference/html/doxygen.css create mode 100644 www/cpp_reference/html/doxygen.png create mode 100644 www/cpp_reference/html/doxygen_8intro.html create mode 100644 www/cpp_reference/html/doxygen_8intro_source.html create mode 100644 www/cpp_reference/html/dynsections.js create mode 100644 www/cpp_reference/html/files.html create mode 100644 www/cpp_reference/html/ftv2blank.png create mode 100644 www/cpp_reference/html/ftv2cl.png create mode 100644 www/cpp_reference/html/ftv2doc.png create mode 100644 www/cpp_reference/html/ftv2folderclosed.png create mode 100644 www/cpp_reference/html/ftv2folderopen.png create mode 100644 www/cpp_reference/html/ftv2lastnode.png create mode 100644 www/cpp_reference/html/ftv2link.png create mode 100644 www/cpp_reference/html/ftv2mlastnode.png create mode 100644 www/cpp_reference/html/ftv2mnode.png create mode 100644 www/cpp_reference/html/ftv2mo.png create mode 100644 www/cpp_reference/html/ftv2node.png create mode 100644 www/cpp_reference/html/ftv2ns.png create mode 100644 www/cpp_reference/html/ftv2plastnode.png create mode 100644 www/cpp_reference/html/ftv2pnode.png create mode 100644 www/cpp_reference/html/ftv2splitbar.png create mode 100644 www/cpp_reference/html/ftv2vertline.png create mode 100644 www/cpp_reference/html/functions.html create mode 100644 www/cpp_reference/html/functions_0x62.html create mode 100644 www/cpp_reference/html/functions_0x63.html create mode 100644 www/cpp_reference/html/functions_0x64.html create mode 100644 www/cpp_reference/html/functions_0x65.html create mode 100644 www/cpp_reference/html/functions_0x66.html create mode 100644 www/cpp_reference/html/functions_0x67.html create mode 100644 www/cpp_reference/html/functions_0x68.html create mode 100644 www/cpp_reference/html/functions_0x69.html create mode 100644 www/cpp_reference/html/functions_0x6b.html create mode 100644 www/cpp_reference/html/functions_0x6c.html create mode 100644 www/cpp_reference/html/functions_0x6d.html create mode 100644 www/cpp_reference/html/functions_0x6e.html create mode 100644 www/cpp_reference/html/functions_0x6f.html create mode 100644 www/cpp_reference/html/functions_0x70.html create mode 100644 www/cpp_reference/html/functions_0x72.html create mode 100644 www/cpp_reference/html/functions_0x73.html create mode 100644 www/cpp_reference/html/functions_0x74.html create mode 100644 www/cpp_reference/html/functions_0x75.html create mode 100644 www/cpp_reference/html/functions_0x76.html create mode 100644 www/cpp_reference/html/functions_0x77.html create mode 100644 www/cpp_reference/html/functions_0x7e.html create mode 100644 www/cpp_reference/html/functions_eval.html create mode 100644 www/cpp_reference/html/functions_func.html create mode 100644 www/cpp_reference/html/functions_func_0x62.html create mode 100644 www/cpp_reference/html/functions_func_0x63.html create mode 100644 www/cpp_reference/html/functions_func_0x64.html create mode 100644 www/cpp_reference/html/functions_func_0x65.html create mode 100644 www/cpp_reference/html/functions_func_0x66.html create mode 100644 www/cpp_reference/html/functions_func_0x67.html create mode 100644 www/cpp_reference/html/functions_func_0x68.html create mode 100644 www/cpp_reference/html/functions_func_0x69.html create mode 100644 www/cpp_reference/html/functions_func_0x6b.html create mode 100644 www/cpp_reference/html/functions_func_0x6c.html create mode 100644 www/cpp_reference/html/functions_func_0x6d.html create mode 100644 www/cpp_reference/html/functions_func_0x6e.html create mode 100644 www/cpp_reference/html/functions_func_0x6f.html create mode 100644 www/cpp_reference/html/functions_func_0x70.html create mode 100644 www/cpp_reference/html/functions_func_0x72.html create mode 100644 www/cpp_reference/html/functions_func_0x73.html create mode 100644 www/cpp_reference/html/functions_func_0x74.html create mode 100644 www/cpp_reference/html/functions_func_0x75.html create mode 100644 www/cpp_reference/html/functions_func_0x77.html create mode 100644 www/cpp_reference/html/functions_func_0x7e.html create mode 100644 www/cpp_reference/html/functions_rela.html create mode 100644 www/cpp_reference/html/functions_rela_0x73.html create mode 100644 www/cpp_reference/html/functions_rela_0x76.html create mode 100644 www/cpp_reference/html/functions_type.html create mode 100644 www/cpp_reference/html/functions_vars.html create mode 100644 www/cpp_reference/html/graph_legend.html create mode 100644 www/cpp_reference/html/graph_legend.md5 create mode 100644 www/cpp_reference/html/graph_legend.png create mode 100644 www/cpp_reference/html/index.html create mode 100644 www/cpp_reference/html/namespacelldb.html create mode 100644 www/cpp_reference/html/namespaces.html create mode 100644 www/cpp_reference/html/nav_f.png create mode 100644 www/cpp_reference/html/nav_g.png create mode 100644 www/cpp_reference/html/nav_h.png create mode 100644 www/cpp_reference/html/open.png create mode 100644 www/cpp_reference/html/sync_off.png create mode 100644 www/cpp_reference/html/sync_on.png create mode 100644 www/cpp_reference/html/tab_a.png create mode 100644 www/cpp_reference/html/tab_b.png create mode 100644 www/cpp_reference/html/tab_h.png create mode 100644 www/cpp_reference/html/tab_s.png create mode 100644 www/cpp_reference/html/tabs.css create mode 100755 www/customization.html create mode 100755 www/docs.html create mode 100755 www/download.html create mode 100755 www/faq.html create mode 100755 www/features.html create mode 100755 www/formats.html create mode 100755 www/goals.html create mode 100755 www/index.html create mode 100644 www/lldb-coding-conventions.html create mode 100755 www/lldb-gdb.html create mode 100755 www/python-reference.html create mode 100644 www/python_reference/_lldb'-module.html create mode 100644 www/python_reference/api-objects.txt create mode 100644 www/python_reference/class-tree.html create mode 100644 www/python_reference/crarr.png create mode 100644 www/python_reference/epydoc.css create mode 100644 www/python_reference/epydoc.js create mode 100644 www/python_reference/frames.html create mode 100644 www/python_reference/help.html create mode 100644 www/python_reference/identifier-index-A.html create mode 100644 www/python_reference/identifier-index-B.html create mode 100644 www/python_reference/identifier-index-C.html create mode 100644 www/python_reference/identifier-index-D.html create mode 100644 www/python_reference/identifier-index-E.html create mode 100644 www/python_reference/identifier-index-F.html create mode 100644 www/python_reference/identifier-index-G.html create mode 100644 www/python_reference/identifier-index-H.html create mode 100644 www/python_reference/identifier-index-I.html create mode 100644 www/python_reference/identifier-index-J.html create mode 100644 www/python_reference/identifier-index-K.html create mode 100644 www/python_reference/identifier-index-L.html create mode 100644 www/python_reference/identifier-index-M.html create mode 100644 www/python_reference/identifier-index-N.html create mode 100644 www/python_reference/identifier-index-O.html create mode 100644 www/python_reference/identifier-index-P.html create mode 100644 www/python_reference/identifier-index-Q.html create mode 100644 www/python_reference/identifier-index-R.html create mode 100644 www/python_reference/identifier-index-S.html create mode 100644 www/python_reference/identifier-index-T.html create mode 100644 www/python_reference/identifier-index-U.html create mode 100644 www/python_reference/identifier-index-V.html create mode 100644 www/python_reference/identifier-index-W.html create mode 100644 www/python_reference/identifier-index-X.html create mode 100644 www/python_reference/identifier-index-Y.html create mode 100644 www/python_reference/identifier-index-Z.html create mode 100644 www/python_reference/identifier-index-_.html create mode 100644 www/python_reference/identifier-index.html create mode 100644 www/python_reference/index.html create mode 100644 www/python_reference/lldb-module.html create mode 100644 www/python_reference/lldb-pysrc.html create mode 100644 www/python_reference/lldb.SBAddress-class.html create mode 100644 www/python_reference/lldb.SBAttachInfo-class.html create mode 100644 www/python_reference/lldb.SBBlock-class.html create mode 100644 www/python_reference/lldb.SBBlock.ranges_access-class.html create mode 100644 www/python_reference/lldb.SBBreakpoint-class.html create mode 100644 www/python_reference/lldb.SBBreakpointLocation-class.html create mode 100644 www/python_reference/lldb.SBBroadcaster-class.html create mode 100644 www/python_reference/lldb.SBCommandInterpreter-class.html create mode 100644 www/python_reference/lldb.SBCommandReturnObject-class.html create mode 100644 www/python_reference/lldb.SBCommunication-class.html create mode 100644 www/python_reference/lldb.SBCompileUnit-class.html create mode 100644 www/python_reference/lldb.SBData-class.html create mode 100644 www/python_reference/lldb.SBData.read_data_helper-class.html create mode 100644 www/python_reference/lldb.SBDebugger-class.html create mode 100644 www/python_reference/lldb.SBDeclaration-class.html create mode 100644 www/python_reference/lldb.SBError-class.html create mode 100644 www/python_reference/lldb.SBEvent-class.html create mode 100644 www/python_reference/lldb.SBExpressionOptions-class.html create mode 100644 www/python_reference/lldb.SBFileSpec-class.html create mode 100644 www/python_reference/lldb.SBFileSpecList-class.html create mode 100644 www/python_reference/lldb.SBFrame-class.html create mode 100644 www/python_reference/lldb.SBFunction-class.html create mode 100644 www/python_reference/lldb.SBHostOS-class.html create mode 100644 www/python_reference/lldb.SBInputReader-class.html create mode 100644 www/python_reference/lldb.SBInstruction-class.html create mode 100644 www/python_reference/lldb.SBInstructionList-class.html create mode 100644 www/python_reference/lldb.SBLaunchInfo-class.html create mode 100644 www/python_reference/lldb.SBLineEntry-class.html create mode 100644 www/python_reference/lldb.SBListener-class.html create mode 100644 www/python_reference/lldb.SBModule-class.html create mode 100644 www/python_reference/lldb.SBModule.compile_units_access-class.html create mode 100644 www/python_reference/lldb.SBModule.sections_access-class.html create mode 100644 www/python_reference/lldb.SBModule.symbols_access-class.html create mode 100644 www/python_reference/lldb.SBModule.symbols_access.re_compile_type-class.html create mode 100644 www/python_reference/lldb.SBModuleSpec-class.html create mode 100644 www/python_reference/lldb.SBModuleSpecList-class.html create mode 100644 www/python_reference/lldb.SBProcess-class.html create mode 100644 www/python_reference/lldb.SBProcess.threads_access-class.html create mode 100644 www/python_reference/lldb.SBSection-class.html create mode 100644 www/python_reference/lldb.SBSourceManager-class.html create mode 100644 www/python_reference/lldb.SBStream-class.html create mode 100644 www/python_reference/lldb.SBStringList-class.html create mode 100644 www/python_reference/lldb.SBSymbol-class.html create mode 100644 www/python_reference/lldb.SBSymbolContext-class.html create mode 100644 www/python_reference/lldb.SBSymbolContextList-class.html create mode 100644 www/python_reference/lldb.SBTarget-class.html create mode 100644 www/python_reference/lldb.SBTarget.modules_access-class.html create mode 100644 www/python_reference/lldb.SBThread-class.html create mode 100644 www/python_reference/lldb.SBThread.frames_access-class.html create mode 100644 www/python_reference/lldb.SBType-class.html create mode 100644 www/python_reference/lldb.SBTypeCategory-class.html create mode 100644 www/python_reference/lldb.SBTypeCategory.formatters_access_class-class.html create mode 100644 www/python_reference/lldb.SBTypeFilter-class.html create mode 100644 www/python_reference/lldb.SBTypeFormat-class.html create mode 100644 www/python_reference/lldb.SBTypeList-class.html create mode 100644 www/python_reference/lldb.SBTypeMember-class.html create mode 100644 www/python_reference/lldb.SBTypeNameSpecifier-class.html create mode 100644 www/python_reference/lldb.SBTypeSummary-class.html create mode 100644 www/python_reference/lldb.SBTypeSynthetic-class.html create mode 100644 www/python_reference/lldb.SBValue-class.html create mode 100644 www/python_reference/lldb.SBValueList-class.html create mode 100644 www/python_reference/lldb.SBWatchpoint-class.html create mode 100644 www/python_reference/lldb.declaration-class.html create mode 100644 www/python_reference/lldb.embedded_interpreter-module.html create mode 100644 www/python_reference/lldb.embedded_interpreter-pysrc.html create mode 100644 www/python_reference/lldb.embedded_interpreter.SimpleREPL-class.html create mode 100644 www/python_reference/lldb.formatters-module.html create mode 100644 www/python_reference/lldb.formatters-pysrc.html create mode 100644 www/python_reference/lldb.formatters.Logger-module.html create mode 100644 www/python_reference/lldb.formatters.Logger-pysrc.html create mode 100644 www/python_reference/lldb.formatters.Logger.FileLogger-class.html create mode 100644 www/python_reference/lldb.formatters.Logger.Logger-class.html create mode 100644 www/python_reference/lldb.formatters.Logger.NopLogger-class.html create mode 100644 www/python_reference/lldb.formatters.Logger.StdoutLogger-class.html create mode 100644 www/python_reference/lldb.formatters.attrib_fromdict-module.html create mode 100644 www/python_reference/lldb.formatters.attrib_fromdict-pysrc.html create mode 100644 www/python_reference/lldb.formatters.attrib_fromdict.AttributesDictionary-class.html create mode 100644 www/python_reference/lldb.formatters.cache-module.html create mode 100644 www/python_reference/lldb.formatters.cache-pysrc.html create mode 100644 www/python_reference/lldb.formatters.cache.Cache-class.html create mode 100644 www/python_reference/lldb.formatters.cpp-module.html create mode 100644 www/python_reference/lldb.formatters.cpp-pysrc.html create mode 100644 www/python_reference/lldb.formatters.cpp.gnu_libstdcpp-module.html create mode 100644 www/python_reference/lldb.formatters.cpp.gnu_libstdcpp-pysrc.html create mode 100644 www/python_reference/lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx-module.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx-pysrc.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stddeque_SynthProvider-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stdlist_SynthProvider-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stdlist_entry-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stdlist_iterator-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stdmap_SynthProvider-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stdmap_iterator-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stdmap_iterator_node-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stdsharedptr_SynthProvider-class.html create mode 100644 www/python_reference/lldb.formatters.cpp.libcxx.stdvector_SynthProvider-class.html create mode 100644 www/python_reference/lldb.formatters.metrics-module.html create mode 100644 www/python_reference/lldb.formatters.metrics-pysrc.html create mode 100644 www/python_reference/lldb.formatters.metrics.Counter-class.html create mode 100644 www/python_reference/lldb.formatters.metrics.Metrics-class.html create mode 100644 www/python_reference/lldb.formatters.metrics.MetricsPrinter_Compact-class.html create mode 100644 www/python_reference/lldb.formatters.metrics.MetricsPrinter_Verbose-class.html create mode 100644 www/python_reference/lldb.formatters.metrics.TimeMetrics-class.html create mode 100644 www/python_reference/lldb.runtime-module.html create mode 100644 www/python_reference/lldb.runtime-pysrc.html create mode 100644 www/python_reference/lldb.utils-module.html create mode 100644 www/python_reference/lldb.utils-pysrc.html create mode 100644 www/python_reference/lldb.utils.symbolication-module.html create mode 100644 www/python_reference/lldb.utils.symbolication-pysrc.html create mode 100644 www/python_reference/lldb.utils.symbolication.Address-class.html create mode 100644 www/python_reference/lldb.utils.symbolication.Image-class.html create mode 100644 www/python_reference/lldb.utils.symbolication.Section-class.html create mode 100644 www/python_reference/lldb.utils.symbolication.Symbolicator-class.html create mode 100644 www/python_reference/lldb.value-class.html create mode 100644 www/python_reference/lldb.value_iter-class.html create mode 100644 www/python_reference/module-tree.html create mode 100644 www/python_reference/redirect.html create mode 100644 www/python_reference/toc-_lldb'-module.html create mode 100644 www/python_reference/toc-everything.html create mode 100644 www/python_reference/toc-lldb-module.html create mode 100644 www/python_reference/toc-lldb.embedded_interpreter-module.html create mode 100644 www/python_reference/toc-lldb.formatters-module.html create mode 100644 www/python_reference/toc-lldb.formatters.Logger-module.html create mode 100644 www/python_reference/toc-lldb.formatters.attrib_fromdict-module.html create mode 100644 www/python_reference/toc-lldb.formatters.cache-module.html create mode 100644 www/python_reference/toc-lldb.formatters.cpp-module.html create mode 100644 www/python_reference/toc-lldb.formatters.cpp.gnu_libstdcpp-module.html create mode 100644 www/python_reference/toc-lldb.formatters.cpp.libcxx-module.html create mode 100644 www/python_reference/toc-lldb.formatters.metrics-module.html create mode 100644 www/python_reference/toc-lldb.runtime-module.html create mode 100644 www/python_reference/toc-lldb.utils-module.html create mode 100644 www/python_reference/toc-lldb.utils.symbolication-module.html create mode 100644 www/python_reference/toc.html create mode 100644 www/python_reference/uml_class_diagram_for_lldb_dec.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_emb.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sba.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sba_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbb.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbb_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbb_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbb_4.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbb_5.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbc.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbc_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbc_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbc_4.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbd.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbd_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbd_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbe.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbe_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbe_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbf.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbf_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbf_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbf_4.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbh.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbi.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbi_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbi_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbl.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbl_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbl_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbm.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbm_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbm_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbm_4.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbm_5.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbm_6.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbm_7.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbp.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbp_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbs.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbs_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbs_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbs_4.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbs_5.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbs_6.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbs_7.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_10.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_11.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_12.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_13.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_14.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_3.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_4.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_5.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_6.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_7.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_8.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbt_9.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbv.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbv_2.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_sbw.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_val.gif create mode 100644 www/python_reference/uml_class_diagram_for_lldb_val_2.gif create mode 100644 www/remote.html create mode 100755 www/scripting.html create mode 100644 www/sidebar.incl create mode 100755 www/source.html create mode 100755 www/status.html create mode 100755 www/style.css create mode 100755 www/symbolication.html create mode 100755 www/symbols.html create mode 100644 www/test.html create mode 100755 www/troubleshooting.html create mode 100755 www/tutorial.html create mode 100755 www/varformats.html diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 000000000000..eb16f4cea996 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,4 @@ +{ + "project_id" : "lldb", + "conduit_uri" : "http://reviews.llvm.org/" +} diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..c698dc5d99cf --- /dev/null +++ b/.clang-format @@ -0,0 +1,9 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +ColumnLimit: 120 +BreakBeforeBraces: Allman +AlwaysBreakAfterReturnType: All +AllowShortFunctionsOnASingleLine: Inline +ConstructorInitializerAllOnOneLineOrOnePerLine: true +IndentCaseLabels: true +AccessModifierOffset: -4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..06cb7040f33b --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +#==============================================================================# +# The file specifies intentionally untracked files that git should ignore. +# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html +# +# This file is intentionally different from the output of `git svn show-ignore`, +# as most of those are useless. +#==============================================================================# + +#==============================================================================# +# File extensions to be ignored anywhere in the tree. +#==============================================================================# +# Temp files created by most text editors. +*~ +# Merge files created by git. +*.orig +# Byte compiled python modules. +*.pyc +*.pyproj +*.sln +*.suo +# vim swap files +.*.swp +.sw? +# OS X specific files. +.DS_store +DerivedData/ + +# Remote build configuration files. +.remote-build.conf + +build/ +pyproj/ +llvm-build/ +*xcuserdata +test/20* +__pycache__/ + +# We should ignore Xcode-style embedding of llvm/ at lldb root dir. +# Do not add trailing '/'s, they skip symlinks. +/llvm +/DerivedData diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000000..82dd78c125a5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 2.8) + +include(cmake/modules/LLDBStandalone.cmake) +include(cmake/modules/LLDBConfig.cmake) +include(cmake/modules/AddLLDB.cmake) + +if (__ANDROID_NDK__ OR (CMAKE_SYSTEM_NAME MATCHES "Windows")) + set(LLDB_DEFAULT_DISABLE_LIBEDIT 1) +else() + set(LLDB_DEFAULT_DISABLE_LIBEDIT 0) +endif () + +# We need libedit support to go down both the source and +# the scripts directories. +set(LLDB_DISABLE_LIBEDIT ${LLDB_DEFAULT_DISABLE_LIBEDIT} CACHE BOOL "Disables the use of editline.") +if (LLDB_DISABLE_LIBEDIT) + add_definitions( -DLLDB_DISABLE_LIBEDIT ) +endif() + +# add_subdirectory(include) +add_subdirectory(docs) +if (NOT LLDB_DISABLE_PYTHON) + add_subdirectory(scripts) +endif () +add_subdirectory(source) +add_subdirectory(test) +add_subdirectory(tools) +add_subdirectory(unittests) +add_subdirectory(lit) + +if (NOT LLDB_DISABLE_PYTHON) + # Add a Post-Build Event to copy over Python files and create the symlink to liblldb.so for the Python API(hardlink on Windows) + add_custom_target( finish_swig ALL + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/finishSwigWrapperClasses.py "--srcRoot=${LLDB_SOURCE_DIR}" "--targetDir=${CMAKE_CURRENT_BINARY_DIR}/scripts" "--cfgBldDir=${CMAKE_CURRENT_BINARY_DIR}/scripts" "--prefix=${CMAKE_BINARY_DIR}" "--cmakeBuildConfiguration=${CMAKE_CFG_INTDIR}" -m + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/scripts/finishSwigWrapperClasses.py + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/scripts/lldb.py + COMMENT "Python script sym-linking LLDB Python API") + # We depend on liblldb being built before we can do this step. + add_dependencies(finish_swig liblldb lldb-argdumper) + + # If we build the readline module, we depend on that happening + # first. + if (TARGET readline) + add_dependencies(finish_swig readline) + endif() + + # Ensure we do the python post-build step when building lldb. + add_dependencies(lldb finish_swig) + + # Add a Post-Build Event to copy the custom Python DLL to the lldb binaries dir so that Windows can find it when launching + # lldb.exe or any other executables that were linked with liblldb. + if (WIN32 AND NOT "${PYTHON_DLL}" STREQUAL "") + # When using the Visual Studio CMake generator the lldb binaries end up in Release/bin, Debug/bin etc. + file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin" LLDB_BIN_DIR) + file(TO_NATIVE_PATH "${PYTHON_DLL}" PYTHON_DLL_NATIVE_PATH) + add_custom_command( + TARGET finish_swig + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy ${PYTHON_DLL_NATIVE_PATH} ${LLDB_BIN_DIR} + COMMENT "Copying Python DLL to LLDB binaries directory.") + endif () +endif () diff --git a/CODE_OWNERS.txt b/CODE_OWNERS.txt new file mode 100644 index 000000000000..b6ca2ef22cfa --- /dev/null +++ b/CODE_OWNERS.txt @@ -0,0 +1,59 @@ +This file is a list of the people responsible for ensuring that patches for a +particular part of LLDB are reviewed, either by themself or by someone else. +They are also the gatekeepers for their part of LLDB, with the final word on +what goes in or not. + +The list is sorted by surname and formatted to allow easy grepping and +beautification by scripts. The fields are: name (N), email (E), web-address +(W), PGP key ID and fingerprint (P), description (D), and snail-mail address +(S). + +N: Sean Callanan +E: scallanan@apple.com +D: Expression evaluator, IR interpreter, Clang integration + +N: Greg Clayton +E: clayborg@gmail.com (Phabricator) +E: gclayton@apple.com (Direct) +D: Overall LLDB architecture, Host (common+macosx), Symbol, API, ABI, Mac-specific code, +D: DynamicLoader, ObjectFile, IOHandler, EditLine, ValueObject, Watchpoints, debugserver +D: Build scripts, Test suite, Platform, gdb-remote, Anything not covered by this file + +N: Enrico Granata +E: egranata@apple.com +D: Data Formatters, Core/Value*, Objective C Language runtime, Test suite, Xcode build +D: SWIG + +N: Jim Ingham +E: jingham@apple.com +D: Overall LLDB architecture, Thread plans, Expression parser, ValueObject, Breakpoints, ABI +D: Watchpoints, Trampolines, Target, Command Interpreter, C++ / Objective C Language runtime + +N: Ilia K +E: ki.stfu@gmail.com +D: lldb-mi + +N: Ed Maste +E: emaste@freebsd.org +D: FreeBSD + +N: Jason Molenda +E: jmolenda@apple.com +D: ABI, Disassembler, Unwinding, iOS, debugserver, Platform + +N: Hafiz Abid Qadeer +E: abidh.haq@gmail.com +D: lldb-mi + +N: Zachary Turner +E: zturner@google.com +D: CMake build, Host (common+windows), Plugins/Process/Windows, Anything Windows-specific + +N: Oleksiy Vyalov +E: ovyalov@google.com +D: Linux, Android + +N: Todd Fiala +E: todd.fiala@gmail.com +D: Test Suite subsystems (concurrent test runners, test events, TestResults system), gdb-remote protocol tests + diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 000000000000..0f05823b6e4c --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,13 @@ +LLDB Installation Instructions +============================== + +LLDB builds on Mac OS X (with Xcode) and Linux (with GCC or Clang). + +On Mac OS X, in addition to using Xcode you'll need to enable code signing +on your system to either build lldb or debug using lldb. Please see the code +signing documentation in docs/code-signing.txt for more detailed directions. + +For instructions to build LLDB on Linux, or more details about supported +compiler versions, other dependencies, and build flags, see: + + http://lldb.llvm.org/build.html diff --git a/Makefile b/Makefile new file mode 100644 index 000000000000..c2f41bb097a8 --- /dev/null +++ b/Makefile @@ -0,0 +1,120 @@ +##===- Makefile --------------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +# If LLDB_LEVEL is not set, then we are the top-level Makefile. Otherwise, we +# are being included from a subdirectory makefile. + +ifndef LLDB_LEVEL + + +IS_TOP_LEVEL := 1 +LLDB_LEVEL := . +DIRS := include scripts source lib tools + +PARALLEL_DIRS := +endif + +### +# Common Makefile code, shared by all LLDB Makefiles. + +# Set LLVM source root level. +LEVEL := $(LLDB_LEVEL)/../.. + +# Include LLVM common makefile. +include $(LEVEL)/Makefile.common + +# Set common LLDB build flags. +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/include +CPP.Flags += -I$(PROJ_OBJ_DIR)/$(LLDB_LEVEL)/include +CPP.Flags += -I$(LLVM_SRC_ROOT)/tools/clang/include +CPP.Flags += -I$(LLVM_OBJ_ROOT)/tools/clang/include +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Utility +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/Utility +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/POSIX + +# Disable python and curses on mingw build +ifeq ($(HOST_OS),MingW) +CXXFLAGS += -DLLDB_DISABLE_PYTHON -DLLDB_DISABLE_CURSES +endif + +ifeq (,$(findstring -DLLDB_DISABLE_PYTHON,$(CXXFLAGS))) +# Set Python include directory +PYTHON_CONFIG?= python-config +PYTHON_INC_DIR = $(shell $(PYTHON_CONFIG) --includes) +CPP.Flags += $(PYTHON_INC_DIR) +endif + +ifeq ($(HOST_OS),Darwin) +CPP.Flags += $(subst -I,-I$(SDKROOT),$(PYTHON_INC_DIR)) +CPP.Flags += -F$(SDKROOT)/System/Library/Frameworks +CPP.Flags += -F$(SDKROOT)/System/Library/PrivateFrameworks +CPP.Flags += -I$(SDKROOT)/usr/include/libxml2 +endif +ifdef LLDB_VENDOR +CPP.Flags += -DLLDB_VENDOR='"$(LLDB_VENDOR) "' +endif + +# If building on a 32-bit system, make sure off_t can store offsets > 2GB +ifneq "$(HOST_ARCH)" "x86_64" +CPP.Flags += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +endif + +# Disable -fstrict-aliasing. Darwin disables it by default (and LLVM doesn't +# work with it enabled with GCC), Clang/llvm-gc don't support it yet, and newer +# GCC's have false positive warnings with it on Linux (which prove a pain to +# fix). For example: +# http://gcc.gnu.org/PR41874 +# http://gcc.gnu.org/PR41838 +# +# We can revisit this when LLVM/Clang support it. +CXX.Flags += -fno-strict-aliasing + +# Do not warn about pragmas. In particular, we are looking to ignore the +# "#pragma mark" construct which GCC warns about on platforms other than Darwin. +EXTRA_OPTIONS += -Wno-unknown-pragmas + +# Drop -Wsign-compare, which we are not currently clean with. +EXTRA_OPTIONS += -Wno-sign-compare + +### +# LLDB Top Level specific stuff. + +ifeq ($(IS_TOP_LEVEL),1) + +ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT)) +$(RecursiveTargets):: + $(Verb) if [ ! -f test/Makefile ]; then \ + $(MKDIR) test; \ + $(CP) $(PROJ_SRC_DIR)/test/Makefile test/Makefile; \ + fi +endif + +test:: + @ $(MAKE) -C test + +#report:: +# @ $(MAKE) -C test report + +#clean:: +# @ $(MAKE) -C test clean + +tags:: + $(Verb) etags `find . -type f -name '*.h' -or -name '*.cpp' | \ + grep -v /lib/Headers | grep -v /test/` + +cscope.files: + find tools lib include -name '*.cpp' \ + -or -name '*.def' \ + -or -name '*.td' \ + -or -name '*.h' > cscope.files + +.PHONY: test report clean cscope.files + +endif diff --git a/cmake/LLDBDependencies.cmake b/cmake/LLDBDependencies.cmake new file mode 100644 index 000000000000..073fa28b2528 --- /dev/null +++ b/cmake/LLDBDependencies.cmake @@ -0,0 +1,207 @@ +set( LLDB_USED_LIBS + lldbBase + lldbBreakpoint + lldbCommands + lldbDataFormatters + lldbHost + lldbCore + lldbExpression + lldbInitialization + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + + # Plugins + lldbPluginDisassemblerLLVM + lldbPluginSymbolFileDWARF + lldbPluginSymbolFileSymtab + lldbPluginDynamicLoaderStatic + lldbPluginDynamicLoaderPosixDYLD + lldbPluginDynamicLoaderHexagonDYLD + lldbPluginDynamicLoaderWindowsDYLD + + lldbPluginCPlusPlusLanguage + lldbPluginGoLanguage + lldbPluginObjCLanguage + lldbPluginObjCPlusPlusLanguage + + lldbPluginObjectFileELF + lldbPluginObjectFileJIT + lldbPluginSymbolVendorELF + lldbPluginObjectContainerBSDArchive + lldbPluginObjectContainerMachOArchive + lldbPluginProcessGDBRemote + lldbPluginProcessUtility + lldbPluginPlatformAndroid + lldbPluginPlatformGDB + lldbPluginPlatformFreeBSD + lldbPluginPlatformKalimba + lldbPluginPlatformLinux + lldbPluginPlatformNetBSD + lldbPluginPlatformPOSIX + lldbPluginPlatformWindows + lldbPluginObjectContainerMachOArchive + lldbPluginObjectContainerBSDArchive + lldbPluginPlatformMacOSX + lldbPluginDynamicLoaderMacOSXDYLD + lldbPluginUnwindAssemblyInstEmulation + lldbPluginUnwindAssemblyX86 + lldbPluginAppleObjCRuntime + lldbPluginRenderScriptRuntime + lldbPluginLanguageRuntimeGo + lldbPluginCXXItaniumABI + lldbPluginABIMacOSX_arm + lldbPluginABIMacOSX_arm64 + lldbPluginABIMacOSX_i386 + lldbPluginABISysV_arm + lldbPluginABISysV_arm64 + lldbPluginABISysV_i386 + lldbPluginABISysV_x86_64 + lldbPluginABISysV_hexagon + lldbPluginABISysV_ppc + lldbPluginABISysV_ppc64 + lldbPluginABISysV_mips + lldbPluginABISysV_mips64 + lldbPluginInstructionARM + lldbPluginInstructionARM64 + lldbPluginInstructionMIPS + lldbPluginInstructionMIPS64 + lldbPluginObjectFilePECOFF + lldbPluginOSGo + lldbPluginOSPython + lldbPluginMemoryHistoryASan + lldbPluginInstrumentationRuntimeAddressSanitizer + lldbPluginSystemRuntimeMacOSX + lldbPluginProcessElfCore + lldbPluginJITLoaderGDB + lldbPluginExpressionParserClang + lldbPluginExpressionParserGo + ) + +# Windows-only libraries +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + list(APPEND LLDB_USED_LIBS + lldbPluginProcessWindows + lldbPluginProcessWinMiniDump + lldbPluginProcessWindowsCommon + Ws2_32 + Rpcrt4 + ) +endif () + +# Linux-only libraries +if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) + list(APPEND LLDB_USED_LIBS + lldbPluginProcessLinux + lldbPluginProcessPOSIX + ) +endif () + +# FreeBSD-only libraries +if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" ) + list(APPEND LLDB_USED_LIBS + lldbPluginProcessFreeBSD + lldbPluginProcessPOSIX + ) +endif () + +# NetBSD-only libraries +if ( CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) + list(APPEND LLDB_USED_LIBS + lldbPluginProcessPOSIX + ) +endif () + +# Darwin-only libraries +if ( CMAKE_SYSTEM_NAME MATCHES "Darwin" ) + list(APPEND LLDB_USED_LIBS + lldbPluginDynamicLoaderDarwinKernel + lldbPluginObjectFileMachO + lldbPluginProcessMachCore + lldbPluginProcessMacOSXKernel + lldbPluginSymbolVendorMacOSX + ) +endif() + +set( CLANG_USED_LIBS + clangAnalysis + clangAST + clangBasic + clangCodeGen + clangDriver + clangEdit + clangFrontend + clangLex + clangParse + clangRewrite + clangRewriteFrontend + clangSema + clangSerialization + ) + +set(LLDB_SYSTEM_LIBS) +if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows" AND NOT __ANDROID_NDK__) + if (NOT LLDB_DISABLE_LIBEDIT) + list(APPEND LLDB_SYSTEM_LIBS edit) + endif() + if (NOT LLDB_DISABLE_CURSES) + list(APPEND LLDB_SYSTEM_LIBS ${CURSES_LIBRARIES}) + if(LLVM_ENABLE_TERMINFO AND HAVE_TERMINFO) + list(APPEND LLDB_SYSTEM_LIBS ${TERMINFO_LIBS}) + endif() + endif() +endif() +# On FreeBSD/NetBSD backtrace() is provided by libexecinfo, not libc. +if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD") + list(APPEND LLDB_SYSTEM_LIBS execinfo) +endif() + +if (NOT LLDB_DISABLE_PYTHON AND NOT LLVM_BUILD_STATIC) + list(APPEND LLDB_SYSTEM_LIBS ${PYTHON_LIBRARIES}) +endif() + +list(APPEND LLDB_SYSTEM_LIBS ${system_libs}) + +if (LLVM_BUILD_STATIC) + if (NOT LLDB_DISABLE_PYTHON) + list(APPEND LLDB_SYSTEM_LIBS python2.7 util) + endif() + if (NOT LLDB_DISABLE_CURSES) + list(APPEND LLDB_SYSTEM_LIBS gpm) + endif() +endif() + +set( LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + interpreter + asmparser + bitreader + bitwriter + codegen + ipo + selectiondag + bitreader + mc + mcjit + core + mcdisassembler + executionengine + runtimedyld + option + support + ) + +if ( NOT LLDB_DISABLE_PYTHON ) + set(LLDB_WRAP_PYTHON ${LLDB_BINARY_DIR}/scripts/LLDBWrapPython.cpp) + + set_source_files_properties(${LLDB_WRAP_PYTHON} PROPERTIES GENERATED 1) + if (CLANG_CL) + set_source_files_properties(${LLDB_WRAP_PYTHON} PROPERTIES COMPILE_FLAGS -Wno-unused-function) + endif() + if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND + NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") + set_property(SOURCE ${LLDB_WRAP_PYTHON} + APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-sequence-point -Wno-cast-qual") + endif () +endif() diff --git a/cmake/modules/AddLLDB.cmake b/cmake/modules/AddLLDB.cmake new file mode 100644 index 000000000000..672b0a53feaf --- /dev/null +++ b/cmake/modules/AddLLDB.cmake @@ -0,0 +1,114 @@ +function(lldb_link_common_libs name targetkind) + if (NOT LLDB_USED_LIBS) + return() + endif() + + if(${targetkind} MATCHES "SHARED") + set(LINK_KEYWORD ${cmake_2_8_12_PUBLIC}) + endif() + + if(${targetkind} MATCHES "SHARED" OR ${targetkind} MATCHES "EXE") + if (LLDB_LINKER_SUPPORTS_GROUPS) + target_link_libraries(${name} ${LINK_KEYWORD} + -Wl,--start-group ${LLDB_USED_LIBS} -Wl,--end-group) + else() + target_link_libraries(${name} ${LINK_KEYWORD} ${LLDB_USED_LIBS}) + endif() + endif() +endfunction(lldb_link_common_libs) + +macro(add_lldb_library name) + # only supported parameters to this macro are the optional + # MODULE;SHARED;STATIC library type and source files + cmake_parse_arguments(PARAM + "MODULE;SHARED;STATIC;OBJECT" + "" + "" + ${ARGN}) + llvm_process_sources(srcs ${PARAM_UNPARSED_ARGUMENTS}) + + if (MSVC_IDE OR XCODE) + string(REGEX MATCHALL "/[^/]+" split_path ${CMAKE_CURRENT_SOURCE_DIR}) + list(GET split_path -1 dir) + file(GLOB_RECURSE headers + ../../include/lldb${dir}/*.h) + set(srcs ${srcs} ${headers}) + endif() + if (PARAM_MODULE) + set(libkind MODULE) + elseif (PARAM_SHARED) + set(libkind SHARED) + elseif (PARAM_OBJECT) + set(libkind OBJECT) + else () + # PARAM_STATIC or library type unspecified. BUILD_SHARED_LIBS + # does not control the kind of libraries created for LLDB, + # only whether or not they link to shared/static LLVM/Clang + # libraries. + set(libkind STATIC) + endif() + + #PIC not needed on Win + if (NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + endif() + + if (PARAM_OBJECT) + add_library(${name} ${libkind} ${srcs}) + else() + llvm_add_library(${name} ${libkind} ${srcs}) + + lldb_link_common_libs(${name} "${libkind}") + + if (PARAM_SHARED) + if (LLDB_LINKER_SUPPORTS_GROUPS) + target_link_libraries(${name} ${cmake_2_8_12_PUBLIC} + -Wl,--start-group ${CLANG_USED_LIBS} -Wl,--end-group) + else() + target_link_libraries(${name} ${cmake_2_8_12_PUBLIC} ${CLANG_USED_LIBS}) + endif() + endif() + llvm_config(${name} ${LLVM_LINK_COMPONENTS}) + + if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY OR ${name} STREQUAL "liblldb") + if (PARAM_SHARED) + install(TARGETS ${name} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}) + else() + install(TARGETS ${name} + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}) + endif() + endif() + endif() + + # Hack: only some LLDB libraries depend on the clang autogenerated headers, + # but it is simple enough to make all of LLDB depend on some of those + # headers without negatively impacting much of anything. + add_dependencies(${name} libclang) + + set_target_properties(${name} PROPERTIES FOLDER "lldb libraries") +endmacro(add_lldb_library) + +macro(add_lldb_executable name) + add_llvm_executable(${name} ${ARGN}) + set_target_properties(${name} PROPERTIES FOLDER "lldb executables") +endmacro(add_lldb_executable) + +# Support appending linker flags to an existing target. +# This will preserve the existing linker flags on the +# target, if there are any. +function(lldb_append_link_flags target_name new_link_flags) + # Retrieve existing linker flags. + get_target_property(current_link_flags ${target_name} LINK_FLAGS) + + # If we had any linker flags, include them first in the new linker flags. + if(current_link_flags) + set(new_link_flags "${current_link_flags} ${new_link_flags}") + endif() + + # Now set them onto the target. + set_target_properties(${target_name} PROPERTIES LINK_FLAGS ${new_link_flags}) +endfunction() diff --git a/cmake/modules/LLDBConfig.cmake b/cmake/modules/LLDBConfig.cmake new file mode 100644 index 000000000000..f5247a269822 --- /dev/null +++ b/cmake/modules/LLDBConfig.cmake @@ -0,0 +1,412 @@ +set(LLDB_PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) +set(LLDB_SOURCE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/source") +set(LLDB_INCLUDE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/include") + +set(LLDB_LINKER_SUPPORTS_GROUPS OFF) +if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") + # The Darwin linker doesn't understand --start-group/--end-group. + set(LLDB_LINKER_SUPPORTS_GROUPS ON) +endif() + +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + set(LLDB_DEFAULT_DISABLE_PYTHON 0) + set(LLDB_DEFAULT_DISABLE_CURSES 1) +else() + if ( __ANDROID_NDK__ ) + set(LLDB_DEFAULT_DISABLE_PYTHON 1) + set(LLDB_DEFAULT_DISABLE_CURSES 1) + else() + set(LLDB_DEFAULT_DISABLE_PYTHON 0) + set(LLDB_DEFAULT_DISABLE_CURSES 0) + endif() +endif() + +set(LLDB_DISABLE_PYTHON ${LLDB_DEFAULT_DISABLE_PYTHON} CACHE BOOL + "Disables the Python scripting integration.") +set(LLDB_DISABLE_CURSES ${LLDB_DEFAULT_DISABLE_CURSES} CACHE BOOL + "Disables the Curses integration.") + +set(LLDB_RELOCATABLE_PYTHON 0 CACHE BOOL + "Causes LLDB to use the PYTHONHOME environment variable to locate Python.") + +if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") + set(LLDB_EXPORT_ALL_SYMBOLS 0 CACHE BOOL + "Causes lldb to export all symbols when building liblldb.") +else() + # Windows doesn't support toggling this, so don't bother making it a + # cache variable. + set(LLDB_EXPORT_ALL_SYMBOLS 0) +endif() + +if ((NOT MSVC) OR MSVC12) + add_definitions( -DHAVE_ROUND ) +endif() + +if (LLDB_DISABLE_CURSES) + add_definitions( -DLLDB_DISABLE_CURSES ) +endif() + +# On Windows, we can't use the normal FindPythonLibs module that comes with CMake, +# for a number of reasons. +# 1) Prior to MSVC 2015, it is only possible to embed Python if python itself was +# compiled with an identical version (and build configuration) of MSVC as LLDB. +# The standard algorithm does not take into account the differences between +# a binary release distribution of python and a custom built distribution. +# 2) From MSVC 2015 and onwards, it is only possible to use Python 3.5 or later. +# 3) FindPythonLibs queries the registry to locate Python, and when looking for a +# 64-bit version of Python, since cmake.exe is a 32-bit executable, it will see +# a 32-bit view of the registry. As such, it is impossible for FindPythonLibs to +# locate 64-bit Python libraries. +# This function is designed to address those limitations. Currently it only partially +# addresses them, but it can be improved and extended on an as-needed basis. +function(find_python_libs_windows) + if ("${PYTHON_HOME}" STREQUAL "") + message("LLDB embedded Python on Windows requires specifying a value for PYTHON_HOME. Python support disabled.") + set(LLDB_DISABLE_PYTHON 1 PARENT_SCOPE) + return() + endif() + + file(TO_CMAKE_PATH "${PYTHON_HOME}/Include" PYTHON_INCLUDE_DIRS) + + if(EXISTS "${PYTHON_INCLUDE_DIRS}/patchlevel.h") + file(STRINGS "${PYTHON_INCLUDE_DIRS}/patchlevel.h" python_version_str + REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"") + string(REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"+]+)[+]?\".*" "\\1" + PYTHONLIBS_VERSION_STRING "${python_version_str}") + message("-- Found Python version ${PYTHONLIBS_VERSION_STRING}") + string(REGEX REPLACE "([0-9]+)[.]([0-9]+)[.][0-9]+" "python\\1\\2" PYTHONLIBS_BASE_NAME "${PYTHONLIBS_VERSION_STRING}") + unset(python_version_str) + else() + message("Unable to find ${PYTHON_INCLUDE_DIRS}/patchlevel.h, Python installation is corrupt.") + message("Python support will be disabled for this build.") + set(LLDB_DISABLE_PYTHON 1 PARENT_SCOPE) + return() + endif() + + file(TO_CMAKE_PATH "${PYTHON_HOME}" PYTHON_HOME) + file(TO_CMAKE_PATH "${PYTHON_HOME}/python_d.exe" PYTHON_DEBUG_EXE) + file(TO_CMAKE_PATH "${PYTHON_HOME}/libs/${PYTHONLIBS_BASE_NAME}_d.lib" PYTHON_DEBUG_LIB) + file(TO_CMAKE_PATH "${PYTHON_HOME}/${PYTHONLIBS_BASE_NAME}_d.dll" PYTHON_DEBUG_DLL) + + file(TO_CMAKE_PATH "${PYTHON_HOME}/python.exe" PYTHON_RELEASE_EXE) + file(TO_CMAKE_PATH "${PYTHON_HOME}/libs/${PYTHONLIBS_BASE_NAME}.lib" PYTHON_RELEASE_LIB) + file(TO_CMAKE_PATH "${PYTHON_HOME}/${PYTHONLIBS_BASE_NAME}.dll" PYTHON_RELEASE_DLL) + + if (NOT EXISTS ${PYTHON_DEBUG_EXE}) + message("Unable to find ${PYTHON_DEBUG_EXE}") + unset(PYTHON_DEBUG_EXE) + endif() + + if (NOT EXISTS ${PYTHON_RELEASE_EXE}) + message("Unable to find ${PYTHON_RELEASE_EXE}") + unset(PYTHON_RELEASE_EXE) + endif() + + if (NOT EXISTS ${PYTHON_DEBUG_LIB}) + message("Unable to find ${PYTHON_DEBUG_LIB}") + unset(PYTHON_DEBUG_LIB) + endif() + + if (NOT EXISTS ${PYTHON_RELEASE_LIB}) + message("Unable to find ${PYTHON_RELEASE_LIB}") + unset(PYTHON_RELEASE_LIB) + endif() + + if (NOT EXISTS ${PYTHON_DEBUG_DLL}) + message("Unable to find ${PYTHON_DEBUG_DLL}") + unset(PYTHON_DEBUG_DLL) + endif() + + if (NOT EXISTS ${PYTHON_RELEASE_DLL}) + message("Unable to find ${PYTHON_RELEASE_DLL}") + unset(PYTHON_RELEASE_DLL) + endif() + + if (NOT (PYTHON_DEBUG_EXE AND PYTHON_RELEASE_EXE AND PYTHON_DEBUG_LIB AND PYTHON_RELEASE_LIB AND PYTHON_DEBUG_DLL AND PYTHON_RELEASE_DLL)) + message("Python installation is corrupt. Python support will be disabled for this build.") + set(LLDB_DISABLE_PYTHON 1 PARENT_SCOPE) + return() + endif() + + # Generator expressions are evaluated in the context of each build configuration generated + # by CMake. Here we use the $:VALUE logical generator expression to ensure + # that the debug Python library, DLL, and executable are used in the Debug build configuration. + # + # Generator expressions can be difficult to grok at first so here's a breakdown of the one + # used for PYTHON_LIBRARY: + # + # 1. $ evaluates to 1 when the Debug configuration is being generated, + # or 0 in all other cases. + # 2. $<$:${PYTHON_DEBUG_LIB}> expands to ${PYTHON_DEBUG_LIB} when the Debug + # configuration is being generated, or nothing (literally) in all other cases. + # 3. $<$>:${PYTHON_RELEASE_LIB}> expands to ${PYTHON_RELEASE_LIB} when + # any configuration other than Debug is being generated, or nothing in all other cases. + # 4. The conditionals in 2 & 3 are mutually exclusive. + # 5. A logical expression with a conditional that evaluates to 0 yields no value at all. + # + # Due to 4 & 5 it's possible to concatenate 2 & 3 to obtain a single value specific to each + # build configuration. In this example the value will be ${PYTHON_DEBUG_LIB} when generating the + # Debug configuration, or ${PYTHON_RELEASE_LIB} when generating any other configuration. + # Note that it's imperative that there is no whitespace between the two expressions, otherwise + # CMake will insert a semicolon between the two. + set (PYTHON_EXECUTABLE $<$:${PYTHON_DEBUG_EXE}>$<$>:${PYTHON_RELEASE_EXE}>) + set (PYTHON_LIBRARY $<$:${PYTHON_DEBUG_LIB}>$<$>:${PYTHON_RELEASE_LIB}>) + set (PYTHON_DLL $<$:${PYTHON_DEBUG_DLL}>$<$>:${PYTHON_RELEASE_DLL}>) + + set (PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} PARENT_SCOPE) + set (PYTHON_LIBRARY ${PYTHON_LIBRARY} PARENT_SCOPE) + set (PYTHON_DLL ${PYTHON_DLL} PARENT_SCOPE) + set (PYTHON_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} PARENT_SCOPE) + + message("-- LLDB Found PythonExecutable: ${PYTHON_RELEASE_EXE} and ${PYTHON_DEBUG_EXE}") + message("-- LLDB Found PythonLibs: ${PYTHON_RELEASE_LIB} and ${PYTHON_DEBUG_LIB}") + message("-- LLDB Found PythonDLL: ${PYTHON_RELEASE_DLL} and ${PYTHON_DEBUG_DLL}") + message("-- LLDB Found PythonIncludeDirs: ${PYTHON_INCLUDE_DIRS}") +endfunction(find_python_libs_windows) + +if (NOT LLDB_DISABLE_PYTHON) + if(UNIX) + # This is necessary for crosscompile on Ubuntu 14.04 64bit. Need a proper fix. + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(CMAKE_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") + endif() + endif() + + if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") + find_python_libs_windows() + + if (NOT LLDB_RELOCATABLE_PYTHON) + file(TO_CMAKE_PATH "${PYTHON_HOME}" LLDB_PYTHON_HOME) + add_definitions( -DLLDB_PYTHON_HOME="${LLDB_PYTHON_HOME}" ) + endif() + else() + find_package(PythonLibs REQUIRED) + endif() + + if (PYTHON_INCLUDE_DIRS) + include_directories(${PYTHON_INCLUDE_DIRS}) + endif() +endif() + +if (LLDB_DISABLE_PYTHON) + unset(PYTHON_INCLUDE_DIRS) + unset(PYTHON_LIBRARY) + add_definitions( -DLLDB_DISABLE_PYTHON ) +endif() + +if (LLVM_EXTERNAL_CLANG_SOURCE_DIR) + include_directories(${LLVM_EXTERNAL_CLANG_SOURCE_DIR}/include) +else () + include_directories(${CMAKE_SOURCE_DIR}/tools/clang/include) +endif () +include_directories("${CMAKE_CURRENT_BINARY_DIR}/../clang/include") + +# Disable GCC warnings +check_cxx_compiler_flag("-Wno-deprecated-declarations" + CXX_SUPPORTS_NO_DEPRECATED_DECLARATIONS) +if (CXX_SUPPORTS_NO_DEPRECATED_DECLARATIONS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") +endif () + +check_cxx_compiler_flag("-Wno-unknown-pragmas" + CXX_SUPPORTS_NO_UNKNOWN_PRAGMAS) +if (CXX_SUPPORTS_NO_UNKNOWN_PRAGMAS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") +endif () + +check_cxx_compiler_flag("-Wno-strict-aliasing" + CXX_SUPPORTS_NO_STRICT_ALIASING) +if (CXX_SUPPORTS_NO_STRICT_ALIASING) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing") +endif () + +# Disable Clang warnings +check_cxx_compiler_flag("-Wno-deprecated-register" + CXX_SUPPORTS_NO_DEPRECATED_REGISTER) +if (CXX_SUPPORTS_NO_DEPRECATED_REGISTER) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-register") +endif () + +check_cxx_compiler_flag("-Wno-vla-extension" + CXX_SUPPORTS_NO_VLA_EXTENSION) +if (CXX_SUPPORTS_NO_VLA_EXTENSION) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-vla-extension") +endif () + +# Disable MSVC warnings +if( MSVC ) + add_definitions( + -wd4018 # Suppress 'warning C4018: '>=' : signed/unsigned mismatch' + -wd4068 # Suppress 'warning C4068: unknown pragma' + -wd4150 # Suppress 'warning C4150: deletion of pointer to incomplete type' + -wd4251 # Suppress 'warning C4251: T must have dll-interface to be used by clients of class U.' + -wd4521 # Suppress 'warning C4521: 'type' : multiple copy constructors specified' + -wd4530 # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.' + ) +endif() + +set(LLDB_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(LLDB_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +# If building on a 32-bit system, make sure off_t can store offsets > 2GB +if( CMAKE_SIZEOF_VOID_P EQUAL 4 ) + add_definitions( -D_LARGEFILE_SOURCE ) + add_definitions( -D_FILE_OFFSET_BITS=64 ) +endif() + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite " +"the makefiles distributed with LLDB. Please create a directory and run cmake " +"from there, passing the path to this source directory as the last argument. " +"This process created the file `CMakeCache.txt' and the directory " +"`CMakeFiles'. Please delete them.") +endif() + +# Compute the LLDB version from the LLVM version. +string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLDB_VERSION + ${PACKAGE_VERSION}) +message(STATUS "LLDB version: ${LLDB_VERSION}") + +if (CMAKE_VERSION VERSION_LESS 2.8.12) + set(cmake_2_8_12_INTERFACE) + set(cmake_2_8_12_PRIVATE) + set(cmake_2_8_12_PUBLIC) +else () + set(cmake_2_8_12_INTERFACE INTERFACE) + set(cmake_2_8_12_PRIVATE PRIVATE) + set(cmake_2_8_12_PUBLIC PUBLIC) +endif () + +include_directories(BEFORE + ${CMAKE_CURRENT_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + install(DIRECTORY include/ + DESTINATION include + FILES_MATCHING + PATTERN "*.h" + PATTERN ".svn" EXCLUDE + ) +endif() + +if (NOT LIBXML2_FOUND AND NOT (CMAKE_SYSTEM_NAME MATCHES "Windows")) + # Skip Libxml2 on Windows. In CMake 3.4 and higher, the algorithm for + # finding libxml2 got "smarter", and it can now locate the version which is + # in gnuwin32, even though that version does not contain the headers that + # LLDB uses. + find_package(LibXml2) +endif() + +# Find libraries or frameworks that may be needed +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + find_library(CARBON_LIBRARY Carbon) + find_library(FOUNDATION_LIBRARY Foundation) + find_library(CORE_FOUNDATION_LIBRARY CoreFoundation) + find_library(CORE_SERVICES_LIBRARY CoreServices) + find_library(SECURITY_LIBRARY Security) + find_library(DEBUG_SYMBOLS_LIBRARY DebugSymbols PATHS "/System/Library/PrivateFrameworks") + + add_definitions( -DLIBXML2_DEFINED ) + list(APPEND system_libs xml2 ${CURSES_LIBRARIES}) + list(APPEND system_libs ${CARBON_LIBRARY} ${FOUNDATION_LIBRARY} + ${CORE_FOUNDATION_LIBRARY} ${CORE_SERVICES_LIBRARY} ${SECURITY_LIBRARY} + ${DEBUG_SYMBOLS_LIBRARY}) + +else() + if (LIBXML2_FOUND) + add_definitions( -DLIBXML2_DEFINED ) + list(APPEND system_libs ${LIBXML2_LIBRARIES}) + include_directories(${LIBXML2_INCLUDE_DIR}) + endif() + +endif() + +if (HAVE_LIBPTHREAD) + list(APPEND system_libs pthread) +endif(HAVE_LIBPTHREAD) + +if (HAVE_LIBDL) + list(APPEND system_libs ${CMAKE_DL_LIBS}) +endif() + +if(LLDB_REQUIRES_EH) + set(LLDB_REQUIRES_RTTI ON) +else() + if(LLVM_COMPILER_IS_GCC_COMPATIBLE) + set(LLDB_COMPILE_FLAGS "${LLDB_COMPILE_FLAGS} -fno-exceptions") + elseif(MSVC) + add_definitions( -D_HAS_EXCEPTIONS=0 ) + set(LLDB_COMPILE_FLAGS "${LLDB_COMPILE_FLAGS} /EHs-c-") + endif() +endif() + +# Disable RTTI by default +if(NOT LLDB_REQUIRES_RTTI) + if (LLVM_COMPILER_IS_GCC_COMPATIBLE) + set(LLDB_COMPILE_FLAGS "${LLDB_COMPILE_FLAGS} -fno-rtti") + elseif(MSVC) + set(LLDB_COMPILE_FLAGS "${LLDB_COMPILE_FLAGS} /GR-") + endif() +endif() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLDB_COMPILE_FLAGS}") + +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + # Check for syscall used by lldb-server on linux. + # If these are not found, it will fall back to ptrace (slow) for memory reads. + check_cxx_source_compiles(" + #include + int main() { process_vm_readv(0, nullptr, 0, nullptr, 0, 0); return 0; }" + HAVE_PROCESS_VM_READV) + + if (HAVE_PROCESS_VM_READV) + add_definitions(-DHAVE_PROCESS_VM_READV) + else() + # If we don't have the syscall wrapper function, but we know the syscall number, we can + # still issue the syscall manually + check_cxx_source_compiles(" + #include + int main() { return __NR_process_vm_readv; }" + HAVE_NR_PROCESS_VM_READV) + + if (HAVE_NR_PROCESS_VM_READV) + add_definitions(-DHAVE_NR_PROCESS_VM_READV) + endif() + endif() +endif() + +# Figure out if lldb could use lldb-server. If so, then we'll +# ensure we build lldb-server when an lldb target is being built. +if ((CMAKE_SYSTEM_NAME MATCHES "Darwin") OR + (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") OR + (CMAKE_SYSTEM_NAME MATCHES "Linux") OR + (CMAKE_SYSTEM_NAME MATCHES "NetBSD")) + set(LLDB_CAN_USE_LLDB_SERVER 1) +else() + set(LLDB_CAN_USE_LLDB_SERVER 0) +endif() + +# Figure out if lldb could use debugserver. If so, then we'll +# ensure we build debugserver when we build lldb. +if ( CMAKE_SYSTEM_NAME MATCHES "Darwin" ) + set(LLDB_CAN_USE_DEBUGSERVER 1) +else() + set(LLDB_CAN_USE_DEBUGSERVER 0) +endif() + +if (NOT LLDB_DISABLE_CURSES) + find_package(Curses REQUIRED) + + find_library(CURSES_PANEL_LIBRARY NAMES panel DOC "The curses panel library") + if (NOT CURSES_PANEL_LIBRARY) + message(FATAL_ERROR "A required curses' panel library not found.") + endif () + + # Add panels to the library path + set (CURSES_LIBRARIES ${CURSES_LIBRARIES} ${CURSES_PANEL_LIBRARY}) + + list(APPEND system_libs ${CURSES_LIBRARIES}) + include_directories(${CURSES_INCLUDE_DIR}) +endif () diff --git a/cmake/modules/LLDBStandalone.cmake b/cmake/modules/LLDBStandalone.cmake new file mode 100644 index 000000000000..d3955f1cdf6b --- /dev/null +++ b/cmake/modules/LLDBStandalone.cmake @@ -0,0 +1,99 @@ +# If we are not building as a part of LLVM, build LLDB as an +# standalone project, using LLVM as an external library: +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + project(lldb) + cmake_minimum_required(VERSION 2.8) + + option(LLVM_INSTALL_TOOLCHAIN_ONLY "Only include toolchain files in the 'install' target." OFF) + + set(LLDB_PATH_TO_LLVM_SOURCE "" CACHE PATH + "Path to LLVM source code. Not necessary if using an installed LLVM.") + set(LLDB_PATH_TO_LLVM_BUILD "" CACHE PATH + "Path to the directory where LLVM was built or installed.") + + set(LLDB_PATH_TO_CLANG_SOURCE "" CACHE PATH + "Path to Clang source code. Not necessary if using an installed Clang.") + set(LLDB_PATH_TO_CLANG_BUILD "" CACHE PATH + "Path to the directory where Clang was built or installed.") + + if (LLDB_PATH_TO_LLVM_SOURCE) + if (NOT EXISTS "${LLDB_PATH_TO_LLVM_SOURCE}/cmake/config-ix.cmake") + message(FATAL_ERROR "Please set LLDB_PATH_TO_LLVM_SOURCE to the root " + "directory of LLVM source code.") + else() + get_filename_component(LLVM_MAIN_SRC_DIR ${LLDB_PATH_TO_LLVM_SOURCE} + ABSOLUTE) + set(LLVM_MAIN_INCLUDE_DIR "${LLVM_MAIN_SRC_DIR}/include") + list(APPEND CMAKE_MODULE_PATH "${LLVM_MAIN_SRC_DIR}/cmake/modules") + endif() + endif() + + if (LLDB_PATH_TO_CLANG_SOURCE) + get_filename_component(CLANG_MAIN_SRC_DIR ${LLDB_PATH_TO_CLANG_SOURCE} + ABSOLUTE) + set(CLANG_MAIN_INCLUDE_DIR "${CLANG_MAIN_SRC_DIR}/include") + endif() + + list(APPEND CMAKE_MODULE_PATH "${LLDB_PATH_TO_LLVM_BUILD}/share/llvm/cmake") + + if (LLDB_PATH_TO_LLVM_BUILD) + get_filename_component(PATH_TO_LLVM_BUILD ${LLDB_PATH_TO_LLVM_BUILD} + ABSOLUTE) + else() + message(FATAL_ERROR "Please set LLDB_PATH_TO_LLVM_BUILD to the root " + "directory of LLVM build or install site.") + endif() + + if (LLDB_PATH_TO_CLANG_BUILD) + get_filename_component(PATH_TO_CLANG_BUILD ${LLDB_PATH_TO_CLANG_BUILD} + ABSOLUTE) + else() + message(FATAL_ERROR "Please set LLDB_PATH_TO_CLANG_BUILD to the root " + "directory of Clang build or install site.") + endif() + + + # These variables are used by add_llvm_library. + set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) + set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) + set(LLVM_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}) + + include(AddLLVM) + include(HandleLLVMOptions) + + if (PYTHON_EXECUTABLE STREQUAL "") + set(Python_ADDITIONAL_VERSIONS 3.5 3.4 3.3 3.2 3.1 3.0 2.7 2.6 2.5) + include(FindPythonInterp) + if( NOT PYTHONINTERP_FOUND ) + message(FATAL_ERROR + "Unable to find Python interpreter, required for builds and testing. + Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") + endif() + else() + message("-- Found PythonInterp: ${PYTHON_EXECUTABLE}") + endif() + # Import CMake library targets from LLVM and Clang. + include("${LLDB_PATH_TO_LLVM_BUILD}/share/llvm/cmake/LLVMConfig.cmake") + if (EXISTS "${LLDB_PATH_TO_CLANG_BUILD}/share/clang/cmake/ClangConfig.cmake") + include("${LLDB_PATH_TO_CLANG_BUILD}/share/clang/cmake/ClangConfig.cmake") + endif() + + set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") + + set(LLVM_BINARY_DIR ${CMAKE_BINARY_DIR}) + + set(CMAKE_INCLUDE_CURRENT_DIR ON) + include_directories("${PATH_TO_LLVM_BUILD}/include" + "${LLVM_MAIN_INCLUDE_DIR}" + "${PATH_TO_CLANG_BUILD}/include" + "${CLANG_MAIN_INCLUDE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/source") + link_directories("${PATH_TO_LLVM_BUILD}/lib${LLVM_LIBDIR_SUFFIX}" + "${PATH_TO_CLANG_BUILD}/lib${LLVM_LIBDIR_SUFFIX}") + + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) + + set(LLDB_BUILT_STANDALONE 1) +endif() diff --git a/cmake/platforms/Android.cmake b/cmake/platforms/Android.cmake new file mode 100644 index 000000000000..98b695be6ef6 --- /dev/null +++ b/cmake/platforms/Android.cmake @@ -0,0 +1,188 @@ +# Toolchain config for Android standalone NDK. +# +# Usage: +# build host llvm and clang first +# cmake -DCMAKE_TOOLCHAIN_FILE=../lldb/cmake/platforms/Android.cmake \ +# -DANDROID_TOOLCHAIN_DIR= \ +# -DANDROID_ABI= \ +# -DCMAKE_CXX_COMPILER_VERSION= \ +# -DLLVM_TARGET_ARCH= \ +# -DLLVM_TARGETS_TO_BUILD= \ +# -DLLVM_TABLEGEN= \ +# -DCLANG_TABLEGEN= +# +# Current Support: +# ANDROID_ABI = x86, x86_64 +# CMAKE_CXX_COMPILER_VERSION = 4.9 +# LLVM_TARGET_ARCH = X86 +# LLVM_TARGETS_TO_BUILD = X86 +# LLVM_TABLEGEN = path to host llvm-tblgen +# CLANG_TABLEGEN = path to host clang-tblgen + +if( DEFINED CMAKE_CROSSCOMPILING ) + return() +endif() + +get_property( IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE ) +if( IS_IN_TRY_COMPILE ) + # this seems necessary and works fine but I'm unsure if it breaks anything + return() +endif() + +set( CMAKE_SYSTEM_NAME Linux ) +include( CMakeForceCompiler ) + +# flags and definitions +remove_definitions( -DANDROID -D__ANDROID__ ) +add_definitions( -DANDROID -D__ANDROID_NDK__ -DLLDB_DISABLE_LIBEDIT ) +set( ANDROID True ) +set( __ANDROID_NDK__ True ) +set( LLDB_DEFAULT_DISABLE_LIBEDIT True ) + +# linking lldb-server statically for Android avoids the need to ship two +# binaries (pie for API 21+ and non-pie for API 16-). It's possible to use +# a non-pie shim on API 16-, but that requires lldb-server to dynamically export +# its symbols, which significantly increases the binary size. Static linking, on +# the other hand, has little to no effect on the binary size. +if( NOT DEFINED LLVM_BUILD_STATIC ) + set( LLVM_BUILD_STATIC True CACHE INTERNAL "" FORCE ) + set( LLVM_ENABLE_PIC FALSE CACHE INTERNAL "" FORCE ) + set( BUILD_SHARED_LIBS FALSE CACHE INTERNAL "" FORCE ) +endif() + +set( ANDROID_ABI "${ANDROID_ABI}" CACHE INTERNAL "Android Abi" FORCE ) +if( ANDROID_ABI STREQUAL "x86" ) + set( CMAKE_SYSTEM_PROCESSOR "i686" ) + set( ANDROID_TOOLCHAIN_NAME "i686-linux-android" ) +elseif( ANDROID_ABI STREQUAL "x86_64" ) + set( CMAKE_SYSTEM_PROCESSOR "x86_64" ) + set( ANDROID_TOOLCHAIN_NAME "x86_64-linux-android" ) +elseif( ANDROID_ABI STREQUAL "armeabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv5te" ) + set( ANDROID_TOOLCHAIN_NAME "arm-linux-androideabi" ) +elseif( ANDROID_ABI STREQUAL "aarch64" ) + set( CMAKE_SYSTEM_PROCESSOR "aarch64" ) + set( ANDROID_TOOLCHAIN_NAME "aarch64-linux-android" ) +elseif( ANDROID_ABI STREQUAL "mips" ) + set( CMAKE_SYSTEM_PROCESSOR "mips" ) + set( ANDROID_TOOLCHAIN_NAME "mipsel-linux-android" ) +elseif( ANDROID_ABI STREQUAL "mips64" ) + set( CMAKE_SYSTEM_PROCESSOR "mips64" ) + set( ANDROID_TOOLCHAIN_NAME "mips64el-linux-android" ) +else() + message( SEND_ERROR "Unknown ANDROID_ABI = \"${ANDROID_ABI}\"." ) +endif() + +set( ANDROID_TOOLCHAIN_DIR "${ANDROID_TOOLCHAIN_DIR}" CACHE PATH "Android standalone toolchain directory" ) +set( ANDROID_SYSROOT "${ANDROID_TOOLCHAIN_DIR}/sysroot" CACHE PATH "Android Sysroot" ) + +# CMAKE_EXECUTABLE_SUFFIX is undefined in CMAKE_TOOLCHAIN_FILE +if( WIN32 ) + set( EXECUTABLE_SUFFIX ".exe" ) +endif() + +set( PYTHON_EXECUTABLE "${ANDROID_TOOLCHAIN_DIR}/bin/python${EXECUTABLE_SUFFIX}" CACHE PATH "Python exec path" ) + +if( NOT CMAKE_C_COMPILER ) + set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-gcc${EXECUTABLE_SUFFIX}" CACHE PATH "C compiler" ) + set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-g++${EXECUTABLE_SUFFIX}" CACHE PATH "C++ compiler" ) + set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-gcc${EXECUTABLE_SUFFIX}" CACHE PATH "assembler" ) + set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-strip${EXECUTABLE_SUFFIX}" CACHE PATH "strip" ) + set( CMAKE_AR "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-ar${EXECUTABLE_SUFFIX}" CACHE PATH "archive" ) + set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-ld${EXECUTABLE_SUFFIX}" CACHE PATH "linker" ) + set( CMAKE_NM "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-nm${EXECUTABLE_SUFFIX}" CACHE PATH "nm" ) + set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-objcopy${EXECUTABLE_SUFFIX}" CACHE PATH "objcopy" ) + set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-objdump${EXECUTABLE_SUFFIX}" CACHE PATH "objdump" ) + set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_DIR}/bin/${ANDROID_TOOLCHAIN_NAME}-ranlib${EXECUTABLE_SUFFIX}" CACHE PATH "ranlib" ) +endif() + +set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT} -funwind-tables -fsigned-char -no-canonical-prefixes" ) +# TODO: different ARM abi have different flags such as neon, vfpv etc +if( X86 ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) +elseif( ANDROID_ABI STREQUAL "armeabi" ) + # 64 bit atomic operations used in c++ libraries require armv7-a instructions + # armv5te and armv6 were tried but do not work. + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mthumb" ) + if( LLVM_BUILD_STATIC ) + # Temporary workaround for static linking with the latest API. + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -DANDROID_ARM_BUILD_STATIC" ) + endif() +elseif( ANDROID_ABI STREQUAL "mips" ) + # http://b.android.com/182094 + list( FIND LLDB_SYSTEM_LIBS atomic index ) + if( index EQUAL -1 ) + list( APPEND LLDB_SYSTEM_LIBS atomic ) + set( LLDB_SYSTEM_LIBS ${LLDB_SYSTEM_LIBS} CACHE INTERNAL "" FORCE ) + endif() + if( LLVM_BUILD_STATIC ) + # Temporary workaround for static linking with the latest API. + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -DANDROID_MIPS_BUILD_STATIC" ) + endif() +endif() + +# Use gold linker and enable safe ICF in case of x86, x86_64 and arm +if ( ANDROID_ABI STREQUAL "x86" OR + ANDROID_ABI STREQUAL "x86_64" OR + ANDROID_ABI STREQUAL "armeabi") + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold -Wl,--icf=safe" ) +endif() + +if( NOT LLVM_BUILD_STATIC ) + # PIE is required for API 21+ so we enable it if we're not statically linking + # unfortunately, it is not supported before API 16 so we need to do something + # else there see http://llvm.org/pr23457 + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -pie -fPIE" ) +endif() + +# linker flags +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) +set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) + +# cache flags +set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" ) +set( CMAKE_C_FLAGS "" CACHE STRING "c flags" ) +set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" ) +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Android c/c++ flags" ) +set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Android c/c++ linker flags" ) + +# final flags +set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) +set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" ) +set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) + +# global includes and link directories +set( ANDROID_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_DIR}/include/c++/${ANDROID_COMPILER_VERSION}" ) +list( APPEND ANDROID_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_DIR}/include/python2.7" ) +include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_INCLUDE_DIRS} ) + +# target environment +set( CMAKE_FIND_ROOT_PATH "${ANDROID_TOOLCHAIN_DIR}/bin" "${ANDROID_TOOLCHAIN_DIR}/${ANDROID_TOOLCHAIN_NAME}" "${ANDROID_SYSROOT}" ) + +# only search for libraries and includes in the ndk toolchain +set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) +set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) +set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) + +################# BEGIN EVIL HACK ################## +# lldb-server links against libdl even though it's not being used and +# libdl.a is currently missing from the toolchain (b.android.com/178517). +# Therefore, in order to statically link lldb-server, we need a temporary +# workaround. This creates a dummy libdl.a stub until the actual +# libdl.a can be implemented in the toolchain. +if( LLVM_BUILD_STATIC ) + set( libdl "${CMAKE_BINARY_DIR}/libdl_stub" ) + file( MAKE_DIRECTORY ${libdl} ) + file( WRITE "${libdl}/libdl.c" " +#include +void * dlopen (const char *filename, int flag) { return 0; } +const char * dlerror (void) { return 0; } +void * dlsym (void *handle, const char *symbol) { return 0; } +int dlclose (void *handle) { return 0; }") + set( flags "${CMAKE_C_FLAGS}" ) + separate_arguments( flags ) + execute_process( COMMAND ${CMAKE_C_COMPILER} ${flags} -c ${libdl}/libdl.c -o ${libdl}/libdl.o ) + execute_process( COMMAND ${CMAKE_AR} rcs ${libdl}/libdl.a ${libdl}/libdl.o ) + set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${libdl}" ) +endif() +################# END EVIL HACK ################## diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 000000000000..045e816b727c --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,41 @@ + +include(FindDoxygen) + +if(DOXYGEN_FOUND) + set(abs_top_srcdir ${CMAKE_CURRENT_SOURCE_DIR}/..) + set(DOT dot) + set(PACKAGE_VERSION mainline) + set(abs_top_builddir ..) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxygen.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg @ONLY) + + add_custom_target(lldb-cpp-doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating LLDB C++ API reference with Doxygen" VERBATIM + ) +endif(DOXYGEN_FOUND) + +find_package(PythonInterp REQUIRED) +find_program(EPYDOC_EXECUTABLE NAMES epydoc epydoc.py) +if(EPYDOC_EXECUTABLE) + find_program(DOT_EXECUTABLE dot) + if(DOT_EXECUTABLE) + set(EPYDOC_OPTIONS ${EPYDOC_OPTIONS} --graph all --dotpath ${DOT_EXECUTABLE}) + endif() + set(DOC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/doc") + file(MAKE_DIRECTORY "${DOC_DIR}") + #set(ENV{PYTHONPATH} ${CMAKE_CURRENT_BINARY_DIR}/../../../lib/python2.7/site-packages) + add_custom_target(lldb-python-doc + ${EPYDOC_EXECUTABLE} + --html + lldb + -o ${CMAKE_CURRENT_BINARY_DIR}/python_reference + --name "LLDB python API" + --url "http://lldb.llvm.org" + ${EPYDOC_OPTIONS} + DEPENDS swig_wrapper liblldb + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../lib${LLVM_LIBDIR_SUFFIX}/python2.7/site-packages + COMMENT "Generating LLDB Python API reference with epydoc" VERBATIM + ) +endif(EPYDOC_EXECUTABLE) diff --git a/docs/building-with-debug-llvm.txt b/docs/building-with-debug-llvm.txt new file mode 100644 index 000000000000..f59ca410edb0 --- /dev/null +++ b/docs/building-with-debug-llvm.txt @@ -0,0 +1,50 @@ +This document describes how to build a debug version of LLVM for use with +LLDB, and how to make LLDB use it. + +It assumes that you are using the Xcode 3 series (I used 3.2.4) to build +LLDB. It also assumes that your shell is /bin/bash, and that you are +currently at a shell prompt in a checked-out LLDB repository. + +1. Check out LLVM and Clang from their repositories. To determine + the revision to use, consult scripts/build-llvm.pl (this is done + in the first command line below). !!! WARNING Do not use the + name "llvm" for your checkout, for reasons described in part 3 + below. + + $ export CLANG_REVISION=`cat scripts/build-llvm.pl | grep ^our.*llvm_revision | cut -d \' -f 2,2` + $ svn co -r $CLANG_REVISION http://llvm.org/svn/llvm-project/llvm/trunk llvm.checkout + $ svn co -r $CLANG_REVISION http://llvm.org/svn/llvm-project/cfe/trunk llvm.checkout/tools/clang + +2. Configure LLVM/Clang with the proper options and compilers. I use: + + $ cd llvm.checkout + $ CC="cc -g -O0" CXX="c++ -g -O0" ./configure --disable-optimized --enable-assertions --enable-targets=x86_64,arm + $ CC="cc -g -O0" CXX="c++ -g -O0" make -j 2 + $ cd .. + +3. Create a link to the built LLVM. !!! WARNING: Do not rename the + directory! The LLVM builder script that runs as part of the Xcode + build keys off the fact that llvm/ is a symlink to recognize that + we are building with a custom debug build. + + $ ln -sf llvm.checkout llvm + +4. Make sure that your Xcode project is set up correctly. Open + lldb.xcodeproj and do the following: + + Under "Targets" in the Groups & Files navigator, double-click + lldb-tool. In the resulting window, select "Debug" from the + "Configuration:" drop-down. Then, make sure that the setting + "Build Active Architecture Only" is enabled. Close the window. + + Under "Targets" in the Groups & Files navigator, double-click + LLDB. In the resulting window, select "Debug" from the + "Configuration:" drop-down. Then, make sure that the setting + "Build Active Architecture Only" is enabled. Close the window. + +5. Ensure that Xcode is building the lldb-tool target in Debug + configuration for your architecture (typically x86_64). You + can usually pick these options from the Overview drop-down at + the top left of the Xcode window. + +6. Build lldb.xcodeproj. diff --git a/docs/code-signing.txt b/docs/code-signing.txt new file mode 100644 index 000000000000..5407fd4bb42e --- /dev/null +++ b/docs/code-signing.txt @@ -0,0 +1,61 @@ +On MacOSX lldb needs to be code signed. The Debug, DebugClang and Release +builds are set to code sign using a code signing certificate named +"lldb_codesign". + +If you have re-installed a new OS, please delete all old lldb_codesign items +from your keychain. There will be a code signing certification and a public +and private key. Reboot after deleting them. You will also need to delete and +build folders that contained old signed items. The darwin kernel will cache +code signing using the executable's file system node, so you will need to +delete the file so the kernel clears its cache. + +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 Create +- Click Continue +- Click Done +- Click on the "My Certificates" +- Double click on your new lldb_codesign certificate +- Turn down the "Trust" disclosure triangle, scroll to the "Code Signing" trust + pulldown menu and select "Always Trust" and authenticate as needed using your + username and password. +- Drag the new "lldb_codesign" code signing certificate (not the public or private + keys of the same name) from the "login" keychain to the "System" keychain in the + Keychains pane on the left hand side of the main Keychain Access window. This will + move this certificate to the "System" keychain. You'll have to authorize a few + more times, set it to be "Always trusted" when asked. +- Remove "~/Desktop/lldb_codesign.cer" file on your desktop if there is one. +- In the Keychain Access GUI, click and drag "lldb_codesign" in the "System" keychain + onto the desktop. The drag will create a "~/Desktop/lldb_codesign.cer" file used in + the next step. +- 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 +rm -f ~/Desktop/lldb_codesign.cer + +- Drag the "lldb_codesign" certificate from the "System" keychain back into the + "login" keychain +- Quit Keychain Access +- Reboot +- Clean by removing all previously creating code signed binaries and rebuild + lldb and you should be able to debug. + +When you build your LLDB for the first time, the Xcode GUI will prompt you for permission +to use the "lldb_codesign" keychain. Be sure to click "Always Allow" on your first +build. From here on out, the "lldb_codesign" will be trusted and you can build from the +command line without having to authorize. Also the first time you debug using a LLDB that +was built with this code signing certificate, you will need to authenticate once. diff --git a/docs/doxygen.cfg.in b/docs/doxygen.cfg.in new file mode 100644 index 000000000000..725a26f2a03c --- /dev/null +++ b/docs/doxygen.cfg.in @@ -0,0 +1,1631 @@ +# Doxyfile 1.7.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = LLVM + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @abs_top_builddir@/docs/cpp_reference + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = ../.. + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = NO + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @abs_top_srcdir@/include/lldb/API \ + @abs_top_srcdir@/docs/doxygen.intro + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = @abs_top_srcdir@/examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = @abs_top_srcdir@/docs/img + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 4 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = llvm:: + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = @abs_top_srcdir@/docs/doxygen.header + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = @abs_top_srcdir@/docs/doxygen.footer + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = @abs_top_srcdir@/../../docs/doxygen.css + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvances is that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = letter + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = ../scripts/interface + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = YES + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = NO + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = @DOT@ + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/docs/doxygen.footer b/docs/doxygen.footer new file mode 100644 index 000000000000..c14814b4a7a5 --- /dev/null +++ b/docs/doxygen.footer @@ -0,0 +1,13 @@ +
+ + +
+ + + + diff --git a/docs/doxygen.header b/docs/doxygen.header new file mode 100644 index 000000000000..e6e0331cb858 --- /dev/null +++ b/docs/doxygen.header @@ -0,0 +1,9 @@ + + + + + +LLVM: $title + + +

LLDB API Documentation

diff --git a/docs/doxygen.intro b/docs/doxygen.intro new file mode 100644 index 000000000000..1e5cd2787ff1 --- /dev/null +++ b/docs/doxygen.intro @@ -0,0 +1,19 @@ +/// @mainpage LLDB +/// +/// @section main_intro Introduction +/// Welcome to LLDB. +/// +/// This documentation describes the @b interface that can drive LLDB. +/// There are no instructions here on how to use LLDB, only the APIs +/// that make up the software. For usage instructions, please see +/// the help command. +/// +/// @section main_caveat Caveat +/// This documentation is generated directly from the source code with doxygen. +/// Since LLDB is constantly under active development, what you're about to +/// read is out of date! However, it may still be useful since certain portions +/// of LLDB are very stable. +/// +/// @section main_changelog Change Log +/// - Adapted for LLDB 05/25/2013 by Daniel Malea +/// - Original content written 12/30/2003 by Reid Spencer diff --git a/docs/lldb-for-gdb-users.txt b/docs/lldb-for-gdb-users.txt new file mode 100644 index 000000000000..216903a55db5 --- /dev/null +++ b/docs/lldb-for-gdb-users.txt @@ -0,0 +1,488 @@ +Here's a short precis of how to run lldb if you are familiar with the +gdb command set: + + +1) LLDB Command Structure: + +First some details on lldb command structure to help orient you... + +Unlike gdb's command set, which is rather free-form, we tried to make +the lldb command syntax fairly structured. The commands are all of the +form + + [-options [option-value]] [argument [argument...]] + +The command line parsing is done before command execution, so it is +uniform across all the commands. The command syntax is very simple, +basically arguments, options and option values are all white-space +separated. If you need to put a backslash or double-quote character +in an argument you back-slash it in the argument. That makes the +command syntax more regular, but it also means you may have to +quote some arguments in lldb that you wouldn't in gdb. + +Options can be placed anywhere on the command line, but if the arguments +begin with a "-" then you have to tell lldb that you're done with options +using the "--" option. So for instance, the "process launch" command takes +the "-s" option to mean "stop the process at the first instruction". It's +arguments are the arguments you are passing to the program. So if you wanted +to pass an argument that contained a "-" you would have to do: + +(lldb) process launch -- -program_arg value + +We also tried to reduce the number of special purpose argument +parsers, which sometimes forces the user to be a little more explicit +about stating their intentions. The first instance you'll note of +this is the breakpoint command. In gdb, to set a breakpoint, you +would just say: + +(gdb) break foo.c:12 + +or + +(gdb) break foo + +if foo is a function. As time went on, the parser that tells foo.c:12 +from foo from foo.c::foo (which means the function foo in the file +foo.c) got more and more complex and bizarre, and especially in C++ +there are times where there's really no way to specify the function +you want to break on. The lldb commands are more verbose but also precise. +So you say: + +(lldb) breakpoint set -f foo.c -l 12 + +to set a file & line breakpoint. To set a breakpoint on a function +by name, you do: + +(lldb) breakpoint set -n foo + +This can allow us to be more expressive, so you can say: + +(lldb) breakpoint set -M foo + +to break on all C++ methods named foo, or: + +(lldb) breakpoint set -S alignLeftEdges: + +to set a breakpoint on all ObjC selectors called alignLeftEdges:. It +also makes it easy to compose specifications, like: + +(lldb) breakpoint set -s foo.dylib -n foo + +for all functions called foo in the shared library foo.dylib. Suggestions +on more interesting primitives of this sort are also very welcome. + +So for instance: + +(lldb) breakpoint set -n "-[SKTGraphicView alignLeftEdges:]" + +Just like gdb, the lldb command interpreter does a shortest unique +string match on command names, so the previous command can also be +typed: + +(lldb) b s -n "-[SKTGraphicView alignLeftEdges:]" + +lldb also supports command completion for source file names, symbol +names, file names, etc. Completion is initiated by a hitting a . +Individual options in a command can have different completers, so for +instance the -f option in "breakpoint" completes to source files, the +-s option to currently loaded shared libraries, etc... We can even do +things like if you specify -s, and are completing on -f, we will only +list source files in the shared library specified by -s... + +The individual commands are pretty extensively documented, using +the "help" command. And there is an "apropos" command that will +search the help for a particular word and dump a summary help string +for each matching command. + +Finally, there is a mechanism to construct aliases for commonly used +commands. So for instance if you get annoyed typing + +(lldb) b s -f foo.c -l 12 + +you can do: + +(lldb) command alias bfl breakpoint set -f %1 -l %2 +(lldb) bfl foo.c 12 + +We have added a few aliases for commonly used commands (e.g. "step", +"next" and "continue") but we haven't tried to be exhaustive because +in our experience it is more convenient to make the basic commands +unique down to a letter or two, and then learn these sequences than +fill the namespace with lots of aliases, and then have to type them +all the way out. + +However, users are free to customize lldb's command set however they +like, and since lldb reads the file ~/.lldbinit at startup, you can +store all your aliases there and they will be generally available to +you. Your aliases are also documented in the help command so you can +remind yourself of what you've set up. + +lldb also has a built-in Python interpreter, which is accessible by +the "script" command. All the functionality of the debugger is +available as classes in the Python interpreter, so the more complex +commands that in gdb you would introduce with the "define" command can +be done by writing Python functions using the lldb-Python library, +then loading the scripts into your running session and accessing them +with the "script" command. + + + +2) A typical session: + + +a) Setting the program to debug: + + +As with gdb, you can start lldb and specify the file you wish to debug +on the command line: + +$ lldb /Projects/Sketch/build/Debug/Sketch.app +Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64). + +or you can specify it after the fact with the "file" command: + +(lldb) file /Projects/Sketch/build/Debug/Sketch.app +Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64). + + +b) Setting breakpoints: + + +We've discussed how to set breakpoints above. You can use "help break set" +to see all the options for breakpoint setting. For instance, we might do: + +(lldb) b s -S alignLeftEdges: +Breakpoint created: 1: name = 'alignLeftEdges:', locations = 1, resolved = 1 + +You can find out about the breakpoints you've set with: + +(lldb) break list +Current breakpoints: +1: name = 'alignLeftEdges:', locations = 1, resolved = 1 + 1.1: where = Sketch`-[SKTGraphicView alignLeftEdges:] + 33 at /Projects/Sketch/SKTGraphicView.m:1405, address = 0x0000000100010d5b, resolved, hit count = 0 + +Note that each "logical" breakpoint can have multiple "locations". +The logical breakpoint has an integer id, and it's locations have an +id within their parent breakpoint (the two are joined by a ".", +e.g. 1.1 in the example above.) + +Also the breakpoints remain "live" so that if another shared library +were to be loaded that had another implementation of the +"alignLeftEdges:" selector, the new location would be added to +breakpoint 1 (e.g. a "1.2" breakpoint would be set on the newly loaded +selector). + +The other piece of information in the breakpoint listing is whether the +breakpoint location was "resolved" or not. A location gets resolved when +the file address it corresponds to gets loaded into the program you are +debugging. For instance if you set a breakpoint in a shared library that +then gets unloaded, that breakpoint location will remain, but it will no +longer be "resolved". + +One other thing to note for gdb users is that lldb acts like gdb with: + +(gdb) set breakpoint pending on + +That is, lldb should always make a breakpoint from your specification, even +if it couldn't find any locations that match the specification. You can tell +whether the expression was resolved or not by checking the locations field +in "breakpoint list", and we report the breakpoint as "pending" when you +set it so you can tell you've made a typo more easily, if that was indeed +the reason no locations were found: + +(lldb) b s -f no_such_file.c -l 10000000 +Breakpoint created: 1: file ='no_such_file.c', line = 10000000, locations = 0 (pending) + +You can delete, disable, set conditions and ignore counts either on all the +locations generated by your logical breakpoint, or on particular locations +your specification resolved to. For instance if we wanted to add a command +to print a backtrace when we hit this breakpoint we could do: + +(lldb) b command add -c 1.1 +Enter your debugger command(s). Type 'DONE' to end. +> bt +> DONE + +The "-c" option specifies that the breakpoint command is a set of lldb +command interpreter commands. Use "-s" if you want to implement your +breakpoint command using the Python interface instead. + + +c) Running the program: + +Then you can either launch the process with the command: + +(lldb) process launch + +or its alias: + +(lldb) r + +Or you can attach to a process by name with: + +(lldb) process attach -n Sketch + +the "attach by name" also supports the "-w" option which waits for the +next process of that name to show up, and attaches to that. You can also +attach by PID: + +(lldb) process attach -p 12345 +Process 46915 Attaching +(lldb) Process 46915 Stopped +1 of 3 threads stopped with reasons: +* thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10, stop reason = signal = SIGSTOP, queue = com.apple.main-thread + +Note that we tell you that "1 of 3 threads stopped with reasons" and +then list those threads. In a multi-threaded environment it is very +common for more than one thread to hit your breakpoint(s) before the +kernel actually returns control to the debugger. In that case, you +will see all the threads that stopped for some interesting reason +listed in the stop message. + + +d) Controlling execution: + + +After launching, we can continue until we hit our breakpoint. The primitive +commands for process control all exist under the "thread" command: + +(lldb) thread continue +Resuming thread 0x2c03 in process 46915 +Resuming process 46915 +(lldb) + +At present you can only operate on one thread at a time, but the +design will ultimately support saying "step over the function in +Thread 1, and step into the function in Thread 2, and continue Thread +3" etc. When we eventually support keeping some threads running while +others are stopped this will be particularly important. For +convenience, however, all the stepping commands have easy aliases. +So "thread continue" is just "c", etc. + +The other program stepping commands are pretty much the same as in gdb. +You've got: + + 1. (lldb) thread step-in + The same as gdb's "step" -- there is also the alias "s" in lldb + + 2. (lldb) thread step-over + The same as gdb's "next" -- there is also the alias "n" in lldb + + 3. (lldb) thread step-out + The same as gdb's "finish" -- there is also the alias "f" in lldb + +And the "by instruction" versions: + +(lldb) thread step-inst +(lldb) thread step-over-inst + +Finally, there's: + +(lldb) thread until 100 + +Which runs the thread in the current frame till it reaches line 100 in +this frame or stops if it leaves the current frame. This is a pretty +close equivalent to gdb's "until" command. + + +One thing here that might be a little disconcerting to gdb users here is that +when you resume process execution, you immediately get a prompt back. That's +because the lldb interpreter remains live when you are running the target. +This allows you to set a breakpoint, etc without having to explicitly interrupt +the program you are debugging. We're still working out all the operations +that it is safe to do while running. But this way of operation will set us +up for "no stop" debugging when we get to implementing that. + +If you want to interrupt a running program do: + +(lldb) process interrupt + +To find out the state of the program, use: + +(lldb) process status +Process 47958 is running. + +This is very convenient, but it does have the down-side that debugging +programs that use stdin is no longer as straightforward. For now, you +have to specify another tty to use as the program stdout & stdin using +the appropriate options to "process launch", or start your program in +another terminal and catch it with "process attach -w". We will come +up with some more convenient way to juggle the terminal back & forth +over time. + + +e) Examining program state: + +Once you've stopped, lldb will choose a current thread, usually the +one that stopped "for a reason", and a current frame in that thread. +Many the commands for inspecting state work on this current +thread/frame. + +To inspect the current state of your process, you can start with the +threads: + +(lldb) thread list +Process 46915 state is Stopped +* thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10, stop reason = signal = SIGSTOP, queue = com.apple.main-thread + thread #2: tid = 0x2e03, 0x00007fff85cbb08a, where = libSystem.B.dylib`kevent + 10, queue = com.apple.libdispatch-manager + thread #3: tid = 0x2f03, 0x00007fff85cbbeaa, where = libSystem.B.dylib`__workq_kernreturn + 10 + +The * indicates that Thread 1 is the current thread. To get a +backtrace for that thread, do: + +(lldb) thread backtrace +thread #1: tid = 0x2c03, stop reason = breakpoint 1.1, queue = com.apple.main-thread + frame #0: 0x0000000100010d5b, where = Sketch`-[SKTGraphicView alignLeftEdges:] + 33 at /Projects/Sketch/SKTGraphicView.m:1405 + frame #1: 0x00007fff8602d152, where = AppKit`-[NSApplication sendAction:to:from:] + 95 + frame #2: 0x00007fff860516be, where = AppKit`-[NSMenuItem _corePerformAction] + 365 + frame #3: 0x00007fff86051428, where = AppKit`-[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] + 121 + frame #4: 0x00007fff860370c1, where = AppKit`-[NSMenu performKeyEquivalent:] + 272 + frame #5: 0x00007fff86035e69, where = AppKit`-[NSApplication _handleKeyEquivalent:] + 559 + frame #6: 0x00007fff85f06aa1, where = AppKit`-[NSApplication sendEvent:] + 3630 + frame #7: 0x00007fff85e9d922, where = AppKit`-[NSApplication run] + 474 + frame #8: 0x00007fff85e965f8, where = AppKit`NSApplicationMain + 364 + frame #9: 0x0000000100015ae3, where = Sketch`main + 33 at /Projects/Sketch/SKTMain.m:11 + frame #10: 0x0000000100000f20, where = Sketch`start + 52 + +You can also provide a list of threads to backtrace, or the keyword +"all" to see all threads: + +(lldb) thread backtrace all + +Next task is inspecting data: + +The most convenient way to inspect a frame's arguments and local variables is: + +(lldb) frame variable +self = (SKTGraphicView *) 0x0000000100208b40 +_cmd = (struct objc_selector *) 0x000000010001bae1 +sender = (id) 0x00000001001264e0 +selection = (NSArray *) 0x00000001001264e0 +i = (NSUInteger) 0x00000001001264e0 +c = (NSUInteger) 0x00000001001253b0 + +You can also choose particular variables to view: + +(lldb) frame variable self +(SKTGraphicView *) self = 0x0000000100208b40 + +The frame variable command is not a full expression parser but it +does support some common operations like dereferencing: + +(lldb) fr v *self +(SKTGraphicView *) self = 0x0000000100208b40 + (NSView) NSView = { + (NSResponder) NSResponder = { +... + +and structure element references: + +(lldb) frame variable self.isa +(struct objc_class *) self.isa = 0x0000000100023730 + +The frame variable command will also perform "object printing" operations on +variables (currently we only support NSPrintForDebugger) with: + +(lldb) fr v -o self +(SKTGraphicView *) self = 0x0000000100208b40 + + +You can select another frame to view with: + +(lldb) frame select 9 +frame #9: 0x0000000100015ae3, where = Sketch`main + 33 at /Projects/Sketch/SKTMain.m:11 + 8 + 9 + 10 int main(int argc, const char *argv[]) { + 11 -> return NSApplicationMain(argc, argv); + 12 } + 13 + 14 + +Another neat trick that the variable list does is array references, so: + +(lldb) fr v argv[0] +(char const *) argv[0] = 0x00007fff5fbffaf8 "/Projects/Sketch/build/Debug/Sketch.app/Contents/MacOS/Sketch" + +If you need to view more complex data or change program data, you can +use the general "expression" command. It takes an expression and +evaluates it in the scope of the currently selected frame. For instance: + +(lldb) expr self +$0 = (SKTGraphicView *) 0x0000000100135430 +(lldb) expr self = 0x00 +$1 = (SKTGraphicView *) 0x0000000000000000 +(lldb) frame var self +(SKTGraphicView *) self = 0x0000000000000000 + +You can also call functions: + +(lldb) expr (int) printf ("I have a pointer 0x%llx.\n", self) +$2 = (int) 22 +I have a pointer 0x0. + +One thing to note from this example is that lldb commands can be defined to +take "raw" input. "expression" is one of these. So in the expression command, +you don't have to quote your whole expression, nor backslash protect quotes, +etc... + +Finally, the results of the expressions are stored in persistent variables +(of the form $[0-9]+) that you can use in further expressions, like: + +(lldb) expr self = $0 +$4 = (SKTGraphicView *) 0x0000000100135430 + +f) Customization: + +You can use the embedded Python interpreter to add the following 'pwd' and 'cd' commands +for your lldb session: + +(lldb) script import os +(lldb) command alias pwd script print os.getcwd() +(lldb) command regex cd "s/^(.*)$/script os.chdir(os.path.expanduser('%1'))/" + +... + +(lldb) cd /tmp +script os.chdir(os.path.expanduser('/tmp')) +(lldb) pwd +/private/tmp +(lldb) + +Or for a more capable 'cd' command, create ~/utils.py like this: + +import os + +def chdir(debugger, args, result, dict): + """Change the working directory, or cd to ${HOME}.""" + dir = args.strip() + if dir: + os.chdir(args) + else: + os.chdir(os.path.expanduser('~')) + print "Current working directory: %s" % os.getcwd() + +and, have the following in your ~/.lldbinit file: + +script import os, sys +script sys.path.append(os.path.expanduser('~')) +script import utils +command alias pwd script print os.getcwd() +command script add -f utils.chdir cd + +and, then in your lldb session, you can have: + +(lldb) help cd + +Change the working directory, or cd to ${HOME}. +Syntax: cd +(lldb) cd +Current working directory: /Volumes/data/Users/johnny +(lldb) cd /tmp +Current working directory: /private/tmp +(lldb) pwd +/private/tmp +(lldb) + +For more examples of customization, look under the ToT/examples/customization +directory. diff --git a/docs/lldb-gdb-remote.txt b/docs/lldb-gdb-remote.txt new file mode 100644 index 000000000000..5c4a10c82b49 --- /dev/null +++ b/docs/lldb-gdb-remote.txt @@ -0,0 +1,1675 @@ +LLDB has added new GDB server packets to better support multi-threaded and +remote debugging. Why? Normally you need to start the correct GDB and the +correct GDB server when debugging. If you have mismatch, then things go wrong +very quickly. LLDB makes extensive use of the GDB remote protocol and we +wanted to make sure that the experience was a bit more dynamic where we can +discover information about a remote target with having to know anything up +front. We also ran into performance issues with the existing GDB remote +protocol that can be overcome when using a reliable communications layer. +Some packets improve performance, others allow for remote process launching +(if you have an OS), and others allow us to dynamically figure out what +registers a thread might have. Again with GDB, both sides pre-agree on how the +registers will look (how many, their register number,name and offsets). We +prefer to be able to dynamically determine what kind of architecture, OS and +vendor we are debugging, as well as how things are laid out when it comes to +the thread register contexts. Below are the details on the new packets we have +added above and beyond the standard GDB remote protocol packets. + +//---------------------------------------------------------------------- +// "QStartNoAckMode" +// +// BRIEF +// Try to enable no ACK mode to skip sending ACKs and NACKs. +// +// PRIORITY TO IMPLEMENT +// High. Any GDB remote server that can implement this should if the +// connection is reliable. This improves packet throughput and increases +// the performance of the connection. +//---------------------------------------------------------------------- +Having to send an ACK/NACK after every packet slows things down a bit, so we +have a way to disable ACK packets to minimize the traffic for reliable +communication interfaces (like sockets). Below GDB or LLDB will send this +packet to try and disable ACKs. All lines that start with "send packet: " are +from GDB/LLDB, and all lines that start with "read packet: " are from the GDB +remote server: + +send packet: $QStartNoAckMode#b0 +read packet: + +read packet: $OK#9a +send packet: + + + + +//---------------------------------------------------------------------- +// "A" - launch args packet +// +// BRIEF +// Launch a program using the supplied arguments +// +// PRIORITY TO IMPLEMENT +// Low. Only needed if the remote target wants to launch a target after +// making a connection to a GDB server that isn't already connected to +// an inferior process. +//---------------------------------------------------------------------- + +We have added support for the "set program arguments" packet where we can +start a connection to a remote server and then later supply the path to the +executable and the arguments to use when executing: + +GDB remote docs for this: + +set program arguments(reserved) Aarglen,argnum,arg,... + +Where A is followed by the length in bytes of the hex encoded argument, +followed by an argument integer, and followed by the ASCII characters +converted into hex bytes foreach arg + +send packet: $A98,0,2f566f6c756d65732f776f726b2f67636c6179746f6e2f446f63756d656e74732f7372632f6174746163682f612e6f7574#00 +read packet: $OK#00 + +The above packet helps when you have remote debugging abilities where you +could launch a process on a remote host, this isn't needed for bare board +debugging. + +//---------------------------------------------------------------------- +// "QEnvironment:NAME=VALUE" +// +// BRIEF +// Setup the environment up for a new child process that will soon be +// launched using the "A" packet. +// +// NB: key/value pairs are sent as-is so gdb-remote protocol meta characters +// (e.g. '#' or '$') are not acceptable. If any non-printable or +// metacharacters are present in the strings, QEnvironmentHexEncoded +// should be used instead if it is available. If you don't want to +// scan the environment strings before sending, prefer +// the QEnvironmentHexEncoded packet over QEnvironment, if it is +// available. +// +// PRIORITY TO IMPLEMENT +// Low. Only needed if the remote target wants to launch a target after +// making a connection to a GDB server that isn't already connected to +// an inferior process. +//---------------------------------------------------------------------- + +Both GDB and LLDB support passing down environment variables. Is it ok to +respond with a "$#00" (unimplemented): + +send packet: $QEnvironment:ACK_COLOR_FILENAME=bold yellow#00 +read packet: $OK#00 + +This packet can be sent one or more times _prior_ to sending a "A" packet. + +//---------------------------------------------------------------------- +// "QEnvironmentHexEncoded:HEX-ENCODING(NAME=VALUE)" +// +// BRIEF +// Setup the environment up for a new child process that will soon be +// launched using the "A" packet. +// +// The only difference between this packet and QEnvironment is that the +// environment key-value pair is ascii hex encoded for transmission. +// This allows values with gdb-remote metacharacters like '#' to be sent. +// +// PRIORITY TO IMPLEMENT +// Low. Only needed if the remote target wants to launch a target after +// making a connection to a GDB server that isn't already connected to +// an inferior process. +//---------------------------------------------------------------------- + +Both GDB and LLDB support passing down environment variables. Is it ok to +respond with a "$#00" (unimplemented): + +send packet: $QEnvironment:41434b5f434f4c4f525f46494c454e414d453d626f6c642379656c6c6f77#00 +read packet: $OK#00 + +This packet can be sent one or more times _prior_ to sending a "A" packet. + +//---------------------------------------------------------------------- +// "QSetSTDIN:" +// "QSetSTDOUT:" +// "QSetSTDERR:" +// +// BRIEF +// Setup where STDIN, STDOUT, and STDERR go prior to sending an "A" +// packet. +// +// PRIORITY TO IMPLEMENT +// Low. Only needed if the remote target wants to launch a target after +// making a connection to a GDB server that isn't already connected to +// an inferior process. +//---------------------------------------------------------------------- + +When launching a program through the GDB remote protocol with the "A" packet, +you might also want to specify where stdin/out/err go: + +QSetSTDIN: +QSetSTDOUT: +QSetSTDERR: + +These packets must be sent _prior_ to sending a "A" packet. + +//---------------------------------------------------------------------- +// "QSetWorkingDir:" +// +// BRIEF +// Set the working directory prior to sending an "A" packet. +// +// PRIORITY TO IMPLEMENT +// Low. Only needed if the remote target wants to launch a target after +// making a connection to a GDB server that isn't already connected to +// an inferior process. +//---------------------------------------------------------------------- + +Or specify the working directory: + +QSetWorkingDir: + +This packet must be sent _prior_ to sending a "A" packet. + +//---------------------------------------------------------------------- +// "QSetDisableASLR:" +// +// BRIEF +// Enable or disable ASLR on the next "A" packet. +// +// PRIORITY TO IMPLEMENT +// Low. Only needed if the remote target wants to launch a target after +// making a connection to a GDB server that isn't already connected to +// an inferior process and if the target supports disabling ASLR +// (Address space layout randomization). +//---------------------------------------------------------------------- + +Or control if ASLR is enabled/disabled: + +send packet: QSetDisableASLR:1 +read packet: OK + +send packet: QSetDisableASLR:0 +read packet: OK + +This packet must be sent _prior_ to sending a "A" packet. + +//---------------------------------------------------------------------- +// QListThreadsInStopReply +// +// BRIEF +// Enable the threads: and thread-pcs: data in the question-mark packet +// ("T packet") responses when the stub reports that a program has +// stopped executing. +// +// PRIORITY TO IMPLEMENT +// Performance. This is a performance benefit to lldb if the thread id's +// and thread pc values are provided to lldb in the T stop packet -- if +// they are not provided to lldb, lldb will likely need to send one to +// two packets per thread to fetch the data at every private stop. +//---------------------------------------------------------------------- + +send packet: QListThreadsInStopReply +read packet: OK + +//---------------------------------------------------------------------- +// "qRegisterInfo" +// +// BRIEF +// Discover register information from the remote GDB server. +// +// PRIORITY TO IMPLEMENT +// High. Any target that can self describe its registers, should do so. +// This means if new registers are ever added to a remote target, they +// will get picked up automatically, and allows registers to change +// depending on the actual CPU type that is used. +// +// NB: As of summer 2015, lldb can get register information from the +// "qXfer:features:read:target.xml" FSF gdb standard register packet +// where the stub provides register definitions in an XML file. +// If qXfer:features:read:target.xml is supported, qRegisterInfo does +// not need to be implemented. +//---------------------------------------------------------------------- + +With LLDB, for register information, remote GDB servers can add +support for the "qRegisterInfoN" packet where "N" is a zero based +base16 register number that must start at zero and increase by one +for each register that is supported. The response is done in typical +GDB remote fashion where a series of "KEY:VALUE;" pairs are returned. +An example for the x86_64 registers is included below: + +send packet: $qRegisterInfo0#00 +read packet: $name:rax;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:0;dwarf:0;#00 +send packet: $qRegisterInfo1#00 +read packet: $name:rbx;bitsize:64;offset:8;encoding:uint;format:hex;set:General Purpose Registers;gcc:3;dwarf:3;#00 +send packet: $qRegisterInfo2#00 +read packet: $name:rcx;bitsize:64;offset:16;encoding:uint;format:hex;set:General Purpose Registers;gcc:2;dwarf:2;#00 +send packet: $qRegisterInfo3#00 +read packet: $name:rdx;bitsize:64;offset:24;encoding:uint;format:hex;set:General Purpose Registers;gcc:1;dwarf:1;#00 +send packet: $qRegisterInfo4#00 +read packet: $name:rdi;bitsize:64;offset:32;encoding:uint;format:hex;set:General Purpose Registers;gcc:5;dwarf:5;#00 +send packet: $qRegisterInfo5#00 +read packet: $name:rsi;bitsize:64;offset:40;encoding:uint;format:hex;set:General Purpose Registers;gcc:4;dwarf:4;#00 +send packet: $qRegisterInfo6#00 +read packet: $name:rbp;alt-name:fp;bitsize:64;offset:48;encoding:uint;format:hex;set:General Purpose Registers;gcc:6;dwarf:6;generic:fp;#00 +send packet: $qRegisterInfo7#00 +read packet: $name:rsp;alt-name:sp;bitsize:64;offset:56;encoding:uint;format:hex;set:General Purpose Registers;gcc:7;dwarf:7;generic:sp;#00 +send packet: $qRegisterInfo8#00 +read packet: $name:r8;bitsize:64;offset:64;encoding:uint;format:hex;set:General Purpose Registers;gcc:8;dwarf:8;#00 +send packet: $qRegisterInfo9#00 +read packet: $name:r9;bitsize:64;offset:72;encoding:uint;format:hex;set:General Purpose Registers;gcc:9;dwarf:9;#00 +send packet: $qRegisterInfoa#00 +read packet: $name:r10;bitsize:64;offset:80;encoding:uint;format:hex;set:General Purpose Registers;gcc:10;dwarf:10;#00 +send packet: $qRegisterInfob#00 +read packet: $name:r11;bitsize:64;offset:88;encoding:uint;format:hex;set:General Purpose Registers;gcc:11;dwarf:11;#00 +send packet: $qRegisterInfoc#00 +read packet: $name:r12;bitsize:64;offset:96;encoding:uint;format:hex;set:General Purpose Registers;gcc:12;dwarf:12;#00 +send packet: $qRegisterInfod#00 +read packet: $name:r13;bitsize:64;offset:104;encoding:uint;format:hex;set:General Purpose Registers;gcc:13;dwarf:13;#00 +send packet: $qRegisterInfoe#00 +read packet: $name:r14;bitsize:64;offset:112;encoding:uint;format:hex;set:General Purpose Registers;gcc:14;dwarf:14;#00 +send packet: $qRegisterInfof#00 +read packet: $name:r15;bitsize:64;offset:120;encoding:uint;format:hex;set:General Purpose Registers;gcc:15;dwarf:15;#00 +send packet: $qRegisterInfo10#00 +read packet: $name:rip;alt-name:pc;bitsize:64;offset:128;encoding:uint;format:hex;set:General Purpose Registers;gcc:16;dwarf:16;generic:pc;#00 +send packet: $qRegisterInfo11#00 +read packet: $name:rflags;alt-name:flags;bitsize:64;offset:136;encoding:uint;format:hex;set:General Purpose Registers;#00 +send packet: $qRegisterInfo12#00 +read packet: $name:cs;bitsize:64;offset:144;encoding:uint;format:hex;set:General Purpose Registers;#00 +send packet: $qRegisterInfo13#00 +read packet: $name:fs;bitsize:64;offset:152;encoding:uint;format:hex;set:General Purpose Registers;#00 +send packet: $qRegisterInfo14#00 +read packet: $name:gs;bitsize:64;offset:160;encoding:uint;format:hex;set:General Purpose Registers;#00 +send packet: $qRegisterInfo15#00 +read packet: $name:fctrl;bitsize:16;offset:176;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo16#00 +read packet: $name:fstat;bitsize:16;offset:178;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo17#00 +read packet: $name:ftag;bitsize:8;offset:180;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo18#00 +read packet: $name:fop;bitsize:16;offset:182;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo19#00 +read packet: $name:fioff;bitsize:32;offset:184;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo1a#00 +read packet: $name:fiseg;bitsize:16;offset:188;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo1b#00 +read packet: $name:fooff;bitsize:32;offset:192;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo1c#00 +read packet: $name:foseg;bitsize:16;offset:196;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo1d#00 +read packet: $name:mxcsr;bitsize:32;offset:200;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo1e#00 +read packet: $name:mxcsrmask;bitsize:32;offset:204;encoding:uint;format:hex;set:Floating Point Registers;#00 +send packet: $qRegisterInfo1f#00 +read packet: $name:stmm0;bitsize:80;offset:208;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:33;dwarf:33;#00 +send packet: $qRegisterInfo20#00 +read packet: $name:stmm1;bitsize:80;offset:224;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:34;dwarf:34;#00 +send packet: $qRegisterInfo21#00 +read packet: $name:stmm2;bitsize:80;offset:240;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:35;dwarf:35;#00 +send packet: $qRegisterInfo22#00 +read packet: $name:stmm3;bitsize:80;offset:256;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:36;dwarf:36;#00 +send packet: $qRegisterInfo23#00 +read packet: $name:stmm4;bitsize:80;offset:272;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:37;dwarf:37;#00 +send packet: $qRegisterInfo24#00 +read packet: $name:stmm5;bitsize:80;offset:288;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:38;dwarf:38;#00 +send packet: $qRegisterInfo25#00 +read packet: $name:stmm6;bitsize:80;offset:304;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:39;dwarf:39;#00 +send packet: $qRegisterInfo26#00 +read packet: $name:stmm7;bitsize:80;offset:320;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:40;dwarf:40;#00 +send packet: $qRegisterInfo27#00 +read packet: $name:xmm0;bitsize:128;offset:336;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:17;dwarf:17;#00 +send packet: $qRegisterInfo28#00 +read packet: $name:xmm1;bitsize:128;offset:352;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:18;dwarf:18;#00 +send packet: $qRegisterInfo29#00 +read packet: $name:xmm2;bitsize:128;offset:368;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:19;dwarf:19;#00 +send packet: $qRegisterInfo2a#00 +read packet: $name:xmm3;bitsize:128;offset:384;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:20;dwarf:20;#00 +send packet: $qRegisterInfo2b#00 +read packet: $name:xmm4;bitsize:128;offset:400;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:21;dwarf:21;#00 +send packet: $qRegisterInfo2c#00 +read packet: $name:xmm5;bitsize:128;offset:416;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:22;dwarf:22;#00 +send packet: $qRegisterInfo2d#00 +read packet: $name:xmm6;bitsize:128;offset:432;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:23;dwarf:23;#00 +send packet: $qRegisterInfo2e#00 +read packet: $name:xmm7;bitsize:128;offset:448;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:24;dwarf:24;#00 +send packet: $qRegisterInfo2f#00 +read packet: $name:xmm8;bitsize:128;offset:464;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:25;dwarf:25;#00 +send packet: $qRegisterInfo30#00 +read packet: $name:xmm9;bitsize:128;offset:480;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:26;dwarf:26;#00 +send packet: $qRegisterInfo31#00 +read packet: $name:xmm10;bitsize:128;offset:496;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:27;dwarf:27;#00 +send packet: $qRegisterInfo32#00 +read packet: $name:xmm11;bitsize:128;offset:512;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:28;dwarf:28;#00 +send packet: $qRegisterInfo33#00 +read packet: $name:xmm12;bitsize:128;offset:528;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:29;dwarf:29;#00 +send packet: $qRegisterInfo34#00 +read packet: $name:xmm13;bitsize:128;offset:544;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:30;dwarf:30;#00 +send packet: $qRegisterInfo35#00 +read packet: $name:xmm14;bitsize:128;offset:560;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:31;dwarf:31;#00 +send packet: $qRegisterInfo36#00 +read packet: $name:xmm15;bitsize:128;offset:576;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:32;dwarf:32;#00 +send packet: $qRegisterInfo37#00 +read packet: $name:trapno;bitsize:32;offset:696;encoding:uint;format:hex;set:Exception State Registers;#00 +send packet: $qRegisterInfo38#00 +read packet: $name:err;bitsize:32;offset:700;encoding:uint;format:hex;set:Exception State Registers;#00 +send packet: $qRegisterInfo39#00 +read packet: $name:faultvaddr;bitsize:64;offset:704;encoding:uint;format:hex;set:Exception State Registers;#00 +send packet: $qRegisterInfo3a#00 +read packet: $E45#00 + +As we see above we keep making subsequent calls to the remote server to +discover all registers by increasing the number appended to qRegisterInfo and +we get a response back that is a series of "key=value;" strings. + +The offset: fields should not leave a gap anywhere in the g/G packet -- the +register values should be appended one after another. For instance, if the +register context for a thread looks like + +struct rctx { + uint32_t gpr1; // offset 0 + uint32_t gpr2; // offset 4 + uint32_t gpr3; // offset 8 + uint64_t fp1; // offset 16 +}; + +You may end up with a 4-byte gap between gpr3 and fp1 on architectures +that align values like this. The correct offset: value for fp1 is 12 - +in the g/G packet fp1 will immediately follow gpr3, even though the +in-memory thread structure has an empty 4 bytes for alignment between +these two registers. + +The keys and values are detailed below: + +Key Value +========== ================================================================ +name The primary register name as a string ("rbp" for example) + +alt-name An alternate name for a register as a string ("fp" for example for + the above "rbp") + +bitsize Size in bits of a register (32, 64, etc). Base 10. + +offset The offset within the "g" and "G" packet of the register data for + this register. This is the byte offset once the data has been + transformed into binary, not the character offset into the g/G + packet. Base 10. + +encoding The encoding type of the register which must be one of: + + uint (unsigned integer) + sint (signed integer) + ieee754 (IEEE 754 float) + vector (vector register) + +format The preferred format for display of this register. The value must + be one of: + + binary + decimal + hex + float + vector-sint8 + vector-uint8 + vector-sint16 + vector-uint16 + vector-sint32 + vector-uint32 + vector-float32 + vector-uint128 + +set The register set name as a string that this register belongs to. + +gcc The GCC compiler registers number for this register (used for + EH frame and other compiler information that is encoded in the + executable files). The supplied number will be decoded like a + string passed to strtoul() with a base of zero, so the number + can be decimal, or hex if it is prefixed with "0x". + + NOTE: If the compiler doesn't have a register number for this + register, this key/value pair should be omitted. + +dwarf The DWARF register number for this register that is used for this + register in the debug information. The supplied number will be decoded + like a string passed to strtoul() with a base of zero, so the number + can be decimal, or hex if it is prefixed with "0x". + + NOTE: If the compiler doesn't have a register number for this + register, this key/value pair should be omitted. + +generic If the register is a generic register that most CPUs have, classify + it correctly so the debugger knows. Valid values are one of: + pc (a program counter register. for example "name=eip;" (i386), + "name=rip;" (x86_64), "name=r15;" (32 bit arm) would + include a "generic=pc;" key value pair) + sp (a stack pointer register. for example "name=esp;" (i386), + "name=rsp;" (x86_64), "name=r13;" (32 bit arm) would + include a "generic=sp;" key value pair) + fp (a frame pointer register. for example "name=ebp;" (i386), + "name=rbp;" (x86_64), "name=r7;" (32 bit arm with macosx + ABI) would include a "generic=fp;" key value pair) + ra (a return address register. for example "name=lr;" (32 bit ARM) + would include a "generic=ra;" key value pair) + fp (a CPU flags register. for example "name=eflags;" (i386), + "name=rflags;" (x86_64), "name=cpsr;" (32 bit ARM) + would include a "generic=flags;" key value pair) + arg1 - arg8 (specified for registers that contain function + arguments when the argument fits into a register) + +container-regs + The value for this key is a comma separated list of raw hex (optional + leading "0x") register numbers. + + This specifies that this register is contained in other concrete + register values. For example "eax" is in the lower 32 bits of the + "rax" register value for x86_64, so "eax" could specify that it is + contained in "rax" by specifying the register number for "rax" (whose + register number is 0x00) + + "container-regs:00;" + + If a register is comprised of one or more registers, like "d0" is ARM + which is a 64 bit register, it might be made up of "s0" and "s1". If + the register number for "s0" is 0x20, and the register number of "s1" + is "0x21", the "container-regs" key/value pair would be: + + "container-regs:20,21;" + + This is handy for defining what GDB used to call "pseudo" registers. + These registers are never requested by LLDB via the register read + or write packets, the container registers will be requested on behalf + of this register. + +invalidate-regs + The value for this key is a comma separated list of raw hex (optional + leading "0x") register numbers. + + This specifies which register values should be invalidated when this + register is modified. For example if modifying "eax" would cause "rax", + "eax", "ax", "ah", and "al" to be modified where rax is 0x0, eax is 0x15, + ax is 0x25, ah is 0x35, and al is 0x39, the "invalidate-regs" key/value + pair would be: + + "invalidate-regs:0,15,25,35,39;" + + If there is a single register that gets invalidated, then omit the comma + and just list a single register: + + "invalidate-regs:0;" + + This is handy when modifying a specific register can cause other + register values to change. For example, when debugging an ARM target, + modifying the CPSR register can cause the r8 - r14 and cpsr value to + change depending on if the mode has changed. + +//---------------------------------------------------------------------- +// "qPlatform_shell" +// +// BRIEF +// Run a command in a shell on the connected remote machine. +// +// PRIORITY TO IMPLEMENT +// High. This command allows LLDB clients to run arbitrary shell +// commands on a remote host. +// +/---------------------------------------------------------------------- + +The request consists of the command to be executed encoded in ASCII characters +converted into hex bytes. + +The response to this packet consists of the letter F followed by the return code, +followed by the signal number (or 0 if no signal was delivered), and escaped bytes +of captured program output. + +Below is an example communication from a client sending an "ls -la" command: + +send packet: $qPlatform_shell:6c73202d6c61,00000002#ec +read packet: $F,00000000,00000000,total 4736 +drwxrwxr-x 16 username groupname 4096 Aug 15 21:36 . +drwxr-xr-x 17 username groupname 4096 Aug 10 16:39 .. +-rw-rw-r-- 1 username groupname 73875 Aug 12 16:46 notes.txt +drwxrwxr-x 5 username groupname 4096 Aug 15 21:36 source.cpp +-rw-r--r-- 1 username groupname 2792 Aug 12 16:46 a.out +-rw-r--r-- 1 username groupname 3190 Aug 12 16:46 Makefile + +//---------------------------------------------------------------------- +// "qPlatform_mkdir" +// +// BRIEF +// Creates a new directory on the connected remote machine. +// +// PRIORITY TO IMPLEMENT +// Low. This command allows LLDB clients to create new directories on +// a remote host. +// +/---------------------------------------------------------------------- + +Request: + qPlatform_mkdir:, + +Reply: + F + mkdir called successfully and returned with the given return code + Exx + An error occurred + +//---------------------------------------------------------------------- +// "qPlatform_chmod" +// +// BRIEF +// Change the permissions of a file on the connected remote machine. +// +// PRIORITY TO IMPLEMENT +// Low. This command allows LLDB clients to change the permissions of +// a file on the remote host. +// +/---------------------------------------------------------------------- + +Request: + qPlatform_chmod:, + +Reply: + F + chmod called successfully and returned with the given return code + Exx + An error occurred + +//---------------------------------------------------------------------- +// "qHostInfo" +// +// BRIEF +// Get information about the host we are remotely connected to. +// +// PRIORITY TO IMPLEMENT +// High. This packet is usually very easy to implement and can help +// LLDB select the correct plug-ins for the job based on the target +// triple information that is supplied. +//---------------------------------------------------------------------- + +LLDB supports a host info call that gets all sorts of details of the system +that is being debugged: + +send packet: $qHostInfo#00 +read packet: $cputype:16777223;cpusubtype:3;ostype:darwin;vendor:apple;endian:little;ptrsize:8;#00 + +Key value pairs are one of: + +cputype: is a number that is the mach-o CPU type that is being debugged (base 10) +cpusubtype: is a number that is the mach-o CPU subtype type that is being debugged (base 10) +triple: a string for the target triple (x86_64-apple-macosx) that can be used to specify arch + vendor + os in one entry +vendor: a string for the vendor (apple), not needed if "triple" is specified +ostype: a string for the OS being debugged (macosx, linux, freebsd, ios, watchos), not needed if "triple" is specified +endian: is one of "little", "big", or "pdp" +ptrsize: an unsigned number that represents how big pointers are in bytes on the debug target +hostname: the hostname of the host that is running the GDB server if available +os_build: a string for the OS build for the remote host as a string value +os_kernel: a string describing the kernel version +os_version: a version string that represents the current OS version (10.8.2) +watchpoint_exceptions_received: one of "before" or "after" to specify if a watchpoint is triggered before or after the pc when it stops +default_packet_timeout: an unsigned number that specifies the default timeout in seconds +distribution_id: optional. For linux, specifies distribution id (e.g. ubuntu, fedora, etc.) +osmajor: optional, specifies the major version number of the OS (e.g. for Mac OS X 10.11.2, it would be 10) +osminor: optional, specifies the minor version number of the OS (e.g. for Mac OS X 10.11.2, it would be 11) +ospatch: optional, specifies the patch level number of the OS (e.g. for Mac OS X 10.11.2, it would be 2) + +//---------------------------------------------------------------------- +// "qGDBServerVersion" +// +// BRIEF +// Get version information about this implementation of the gdb-remote +// protocol. +// +// PRIORITY TO IMPLEMENT +// High. This packet is usually very easy to implement and can help +// LLDB to work around bugs in a server's implementation when they +// are found. +//---------------------------------------------------------------------- + +The goal of this packet is to provide enough information about an +implementation of the gdb-remote-protocol server that lldb can +work around implementation problems that are discovered after the +version has been released/deployed. The name and version number +should be sufficiently unique that lldb can unambiguously identify +the origin of the program (for instance, debugserver from lldb) and +the version/submission number/patch level of the program - whatever +is appropriate for your server implementation. + +The packet follows the key-value pair model, semicolon separated. + +send packet: $qGDBServerVersion#00 +read packet: $name:debugserver;version:310.2;#00 + +Other clients may find other key-value pairs to be useful for identifying +a gdb stub. Patch level, release name, build number may all be keys that +better describe your implementation's version. +Suggested key names: + + name : the name of your remote server - "debugserver" is the lldb standard + implementation + + version : identifies the version number of this server + + patch_level : the patch level of this server + + release_name : the name of this release, if your project uses names + + build_number : if you use a build system with increasing build numbers, + this may be the right key name for your server + + major_version : major version number + minor_version : minor version number + +//---------------------------------------------------------------------- +// "qProcessInfo" +// +// BRIEF +// Get information about the process we are currently debugging. +// +// PRIORITY TO IMPLEMENT +// Medium. On systems which can launch multiple different architecture processes, +// the qHostInfo may not disambiguate sufficiently to know what kind of +// process is being debugged. +// e.g. on a 64-bit x86 Mac system both 32-bit and 64-bit user processes are possible, +// and with Mach-O universal files, the executable file may contain both 32- and +// 64-bit slices so it may be impossible to know until you're attached to a real +// process to know what you're working with. +// +// All numeric fields return base-16 numbers without any "0x" prefix. +//---------------------------------------------------------------------- + +An i386 process: + +send packet: $qProcessInfo#00 +read packet: $pid:42a8;parent-pid:42bf;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:7;cpusubtype:3;ostype:macosx;vendor:apple;endian:little;ptrsize:4;#00 + +An x86_64 process: + +send packet: $qProcessInfo#00 +read packet: $pid:d22c;parent-pid:d34d;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:1000007;cpusubtype:3;ostype:macosx;vendor:apple;endian:little;ptrsize:8;#00 + +Key value pairs include: + +pid: the process id +parent-pid: the process of the parent process (often debugserver will become the parent when attaching) +real-uid: the real user id of the process +real-gid: the real group id of the process +effective-uid: the effective user id of the process +effective-gid: the effective group id of the process +cputype: the Mach-O CPU type of the process (base 16) +cpusubtype: the Mach-O CPU subtype of the process (base 16) +ostype: is a string the represents the OS being debugged (darwin, linux, freebsd) +vendor: is a string that represents the vendor (apple) +endian: is one of "little", "big", or "pdp" +ptrsize: is a number that represents how big pointers are in bytes + + +//---------------------------------------------------------------------- +// "qShlibInfoAddr" +// +// BRIEF +// Get an address where the dynamic linker stores information about +// where shared libraries are loaded. +// +// PRIORITY TO IMPLEMENT +// High if you have a dynamic loader plug-in in LLDB for your target +// triple (see the "qHostInfo" packet) that can use this information. +// Many times address load randomization can make it hard to detect +// where the dynamic loader binary and data structures are located and +// some platforms know, or can find out where this information is. +// +// Low if you have a debug target where all object and symbol files +// contain static load addresses. +//---------------------------------------------------------------------- + +LLDB and GDB both support the "qShlibInfoAddr" packet which is a hint to each +debugger as to where to find the dynamic loader information. For darwin +binaries that run in user land this is the address of the "all_image_infos" +structure in the "/usr/lib/dyld" executable, or the result of a TASK_DYLD_INFO +call. The result is returned as big endian hex bytes that are the address +value: + +send packet: $qShlibInfoAddr#00 +read packet: $7fff5fc40040#00 + + + +//---------------------------------------------------------------------- +// "qThreadStopInfo" +// +// BRIEF +// Get information about why a thread, whose ID is "", is stopped. +// +// PRIORITY TO IMPLEMENT +// High if you need to support multi-threaded or multi-core debugging. +// Many times one thread will hit a breakpoint and while the debugger +// is in the process of suspending the other threads, other threads +// will also hit a breakpoint. This packet allows LLDB to know why all +// threads (live system debug) / cores (JTAG) in your program have +// stopped and allows LLDB to display and control your program +// correctly. +//---------------------------------------------------------------------- + +LLDB tries to use the "qThreadStopInfo" packet which is formatted as +"qThreadStopInfo%x" where %x is the hex thread ID. This requests information +about why a thread is stopped. The response is the same as the stop reply +packets and tells us what happened to the other threads. The standard GDB +remote packets love to think that there is only _one_ reason that _one_ thread +stops at a time. This allows us to see why all threads stopped and allows us +to implement better multi-threaded debugging support. + +//---------------------------------------------------------------------- +// "QThreadSuffixSupported" +// +// BRIEF +// Try to enable thread suffix support for the 'g', 'G', 'p', and 'P' +// packets. +// +// PRIORITY TO IMPLEMENT +// High. Adding a thread suffix allows us to read and write registers +// more efficiently and stops us from having to select a thread with +// one packet and then read registers with a second packet. It also +// makes sure that no errors can occur where the debugger thinks it +// already has a thread selected (see the "Hg" packet from the standard +// GDB remote protocol documentation) yet the remote GDB server actually +// has another thread selected. +//---------------------------------------------------------------------- + +When reading thread registers, you currently need to set the current +thread, then read the registers. This is kind of cumbersome, so we added the +ability to query if the remote GDB server supports adding a "thread:;" +suffix to all packets that request information for a thread. To test if the +remote GDB server supports this feature: + +send packet: $QThreadSuffixSupported#00 +read packet: OK + +If "OK" is returned, then the 'g', 'G', 'p' and 'P' packets can accept a +thread suffix. So to send a 'g' packet (read all register values): + +send packet: $g;thread:;#00 +read packet: .... + +send packet: $G;thread:;#00 +read packet: .... + +send packet: $p1a;thread:;#00 +read packet: .... + +send packet: $P1a=1234abcd;thread:;#00 +read packet: .... + + +otherwise, without this you would need to always send two packets: + +send packet: $Hg#00 +read packet: .... +send packet: $g#00 +read packet: .... + +We also added support for allocating and deallocating memory. We use this to +allocate memory so we can run JITed code. + +//---------------------------------------------------------------------- +// "_M," +// +// BRIEF +// Allocate memory on the remote target with the specified size and +// permissions. +// +// PRIORITY TO IMPLEMENT +// High if you want LLDB to be able to JIT code and run that code. JIT +// code also needs data which is also allocated and tracked. +// +// Low if you don't support running JIT'ed code. +//---------------------------------------------------------------------- + +The allocate memory packet starts with "_M,". It returns a +raw big endian address value, or "" for unimplemented, or "EXX" for an error +code. The packet is formatted as: + +char packet[256]; +int packet_len; +packet_len = ::snprintf ( + packet, + sizeof(packet), + "_M%zx,%s%s%s", + (size_t)size, + permissions & lldb::ePermissionsReadable ? "r" : "", + permissions & lldb::ePermissionsWritable ? "w" : "", + permissions & lldb::ePermissionsExecutable ? "x" : ""); + +You request a size and give the permissions. This packet does NOT need to be +implemented if you don't want to support running JITed code. The return value +is just the address of the newly allocated memory as raw big endian hex bytes. + +//---------------------------------------------------------------------- +// "_m" +// +// BRIEF +// Deallocate memory that was previously allocated using an allocate +// memory pack. +// +// PRIORITY TO IMPLEMENT +// High if you want LLDB to be able to JIT code and run that code. JIT +// code also needs data which is also allocated and tracked. +// +// Low if you don't support running JIT'ed code. +//---------------------------------------------------------------------- + +The deallocate memory packet is "_m" where you pass in the address you +got back from a previous call to the allocate memory packet. It returns "OK" +if the memory was successfully deallocated, or "EXX" for an error, or "" if +not supported. + +//---------------------------------------------------------------------- +// "qMemoryRegionInfo:" +// +// BRIEF +// Get information about the address range that contains "" +// +// PRIORITY TO IMPLEMENT +// Medium. This is nice to have, but it isn't necessary. It helps LLDB +// do stack unwinding when we branch into memory that isn't executable. +// If we can detect that the code we are stopped in isn't executable, +// then we can recover registers for stack frames above the current +// frame. Otherwise we must assume we are in some JIT'ed code (not JIT +// code that LLDB has made) and assume that no registers are available +// in higher stack frames. +//---------------------------------------------------------------------- + +We added a way to get information for a memory region. The packet is: + + qMemoryRegionInfo: + +Where is a big endian hex address. The response is returned in a series +of tuples like the data returned in a stop reply packet. The currently valid +tuples to return are: + + start:; // is a big endian hex address that is + // the start address of the range that contains + + size:; // is a big endian hex byte size of the address + // of the range that contains + + permissions:; // is a string that contains one + // or more of the characters from "rwx" + + error:; // where is + // a hex encoded string value that + // contains an error string + +If the address requested is not in a mapped region (e.g. we've jumped through +a NULL pointer and are at 0x0) currently lldb expects to get back the size +of the unmapped region -- that is, the distance to the next valid region. +For instance, with a Mac OS X process which has nothing mapped in the first +4GB of its address space, if we're asking about address 0x2, + + qMemoryRegionInfo:2 + start:2;size:fffffffe; + +The lack of 'permissions:' indicates that none of read/write/execute are valid +for this region. + +//---------------------------------------------------------------------- +// "x" - Binary memory read +// +// Like the 'm' (read) and 'M' (write) packets, this is a partner to the +// 'X' (write binary data) packet, 'x'. +// +// It is called like +// +// xADDRESS,LENGTH +// +// where both ADDRESS and LENGTH are big-endian base 16 values. +// +// To test if this packet is available, send a addr/len of 0: +// +// x0,0 +// +// and you will get an "OK" response. +// +// The reply will be the data requested in 8-bit binary data format. +// The standard quoting is applied to the payload -- characters +// } # $ * +// will all be escaped with '}' (0x7d) character and then XOR'ed with 0x20. +// +// A typical use to read 512 bytes at 0x1000 would look like +// +// x0x1000,0x200 +// +// The "0x" prefixes are optional - like most of the gdb-remote packets, +// omitting them will work fine; these numbers are always base 16. +// +// The length of the payload is not provided. A reliable, 8-bit clean, +// transport layer is assumed. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// Detach and stay stopped: +// +// We extended the "D" packet to specify that the monitor should keep the +// target suspended on detach. The normal behavior is to resume execution +// on detach. We will send: +// +// qSupportsDetachAndStayStopped: +// +// to query whether the monitor supports the extended detach, and if it does, +// when we want the monitor to detach but not resume the target, we will +// send: +// +// D1 +// +// In any case, if we want the normal detach behavior we will just send: +// +// D +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// QSaveRegisterState +// QSaveRegisterState;thread:XXXX; +// +// BRIEF +// The QSaveRegisterState packet tells the remote debugserver to save +// all registers and return a non-zero unique integer ID that +// represents these save registers. If thread suffixes are enabled the +// second form of this packet is used, otherwise the first form is +// used. This packet is called prior to executing an expression, so +// the remote GDB server should do anything it needs to in order to +// ensure the registers that are saved are correct. On MacOSX this +// involves calling "thread_abort_safely(mach_port_t thread)" to +// ensure we get the correct registers for a thread in case it is +// currently having code run on its behalf in the kernel. +// +// RESPONSE +// unsigned - The save_id result is a non-zero unsigned integer value +// that can be passed back to the GDB server using a +// QRestoreRegisterState packet to restore the registers +// one time. +// "EXX" - or an error code in the form of EXX where XX is a +// hex error code. +// +// PRIORITY TO IMPLEMENT +// Low, this is mostly a convenience packet to avoid having to send all +// registers via a g packet. It should only be implemented if support +// for the QRestoreRegisterState is added. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// QRestoreRegisterState: +// QRestoreRegisterState:;thread:XXXX; +// +// BRIEF +// The QRestoreRegisterState packet tells the remote debugserver to +// restore all registers using the "save_id" which is an unsigned +// integer that was returned from a previous call to +// QSaveRegisterState. The restoration process can only be done once +// as the data backing the register state will be freed upon the +// completion of the QRestoreRegisterState command. +// +// If thread suffixes are enabled the second form of this packet is +// used, otherwise the first form is used. +// +// RESPONSE +// "OK" - if all registers were successfully restored +// "EXX" - for any errors +// +// PRIORITY TO IMPLEMENT +// Low, this is mostly a convenience packet to avoid having to send all +// registers via a g packet. It should only be implemented if support +// for the QSaveRegisterState is added. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// qFileLoadAddress: +// +// BRIEF +// Get the load address of a memory mapped file. +// The load address is defined as the address of the first memory +// region what contains data mapped from the specified file. +// +// RESPONSE +// - Load address of the file in big endian encoding +// "E01" - the requested file isn't loaded +// "EXX" - for any other errors +// +// PRIORITY TO IMPLEMENT +// Low, required if dynamic linker don't fill in the load address of +// some object file in the rendezvous data structure. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// qModuleInfo:; +// +// BRIEF +// Get information for a module by given module path and architecture. +// +// RESPONSE +// "(uuid|md5):...;triple:...;file_offset:...;file_size...;" +// "EXX" - for any errors +// +// PRIORITY TO IMPLEMENT +// Optional, required if dynamic loader cannot fetch module's information like +// UUID directly from inferior's memory. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// Stop reply packet extensions +// +// BRIEF +// This section describes some of the additional information you can +// specify in stop reply packets that help LLDB to know more detailed +// information about your threads. +// +// DESCRIPTION +// Standard GDB remote stop reply packets are reply packets sent in +// response to a packet that made the program run. They come in the +// following forms: +// +// "SAA" +// "S" means signal and "AA" is a hex signal number that describes why +// the thread or stopped. It doesn't specify which thread, so the "T" +// packet is recommended to use instead of the "S" packet. +// +// "TAAkey1:value1;key2:value2;..." +// "T" means a thread stopped due to a unix signal where "AA" is a hex +// signal number that describes why the program stopped. This is +// followed by a series of key/value pairs: +// - If key is a hex number, it is a register number and value is +// the hex value of the register in debuggee endian byte order. +// - If key == "thread", then the value is the big endian hex +// thread-id of the stopped thread. +// - If key == "core", then value is a hex number of the core on +// which the stop was detected. +// - If key == "watch" or key == "rwatch" or key == "awatch", then +// value is the data address in big endian hex +// - If key == "library", then value is ignore and "qXfer:libraries:read" +// packets should be used to detect any newly loaded shared libraries +// +// "WAA" +// "W" means the process exited and "AA" is the exit status. +// +// "XAA" +// "X" means the process exited and "AA" is signal that caused the program +// to exit. +// +// "O" +// "O" means STDOUT has data that was written to its console and is +// being delivered to the debugger. This packet happens asynchronously +// and the debugger is expected to continue to wait for another stop reply +// packet. +// +// LLDB EXTENSIONS +// +// We have extended the "T" packet to be able to also understand the +// following keys and values: +// +// KEY VALUE DESCRIPTION +// =========== ======== ================================================ +// "metype" unsigned mach exception type (the value of the EXC_XXX enumerations) +// as an unsigned integer. For targets with mach +// kernels only. +// +// "mecount" unsigned mach exception data count as an unsigned integer +// For targets with mach kernels only. +// +// "medata" unsigned There should be "mecount" of these and it is the data +// that goes along with a mach exception (as an unsigned +// integer). For targets with mach kernels only. +// +// "name" string The name of the thread as a plain string. The string +// must not contain an special packet characters or +// contain a ':' or a ';'. Use "hexname" if the thread +// name has special characters. +// +// "hexname" ascii-hex An ASCII hex string that contains the name of the thread +// +// "qaddr" hex Big endian hex value that contains the libdispatch +// queue address for the queue of the thread. +// +// "reason" enum The enumeration must be one of: +// "trace" the program stopped after a single instruction +// was executed on a core. Usually done when single +// stepping past a breakpoint +// "breakpoint" a breakpoint set using a 'z' packet was hit. +// "trap" stopped due to user interruption +// "signal" stopped due to an actual unix signal, not +// just the debugger using a unix signal to keep +// the GDB remote client happy. +// "watchpoint". Should be used in conjunction with +// the "watch"/"rwatch"/"awatch" key value pairs. +// "exception" an exception stop reason. Use with +// the "description" key/value pair to describe the +// exceptional event the user should see as the stop +// reason. +// "description" ascii-hex An ASCII hex string that contains a more descriptive +// reason that the thread stopped. This is only needed +// if none of the key/value pairs are enough to +// describe why something stopped. +// +// "threads" comma-sep-base16 A list of thread ids for all threads (including +// the thread that we're reporting as stopped) that +// are live in the process right now. lldb may +// request that this be included in the T packet via +// the QListThreadsInStopReply packet earlier in +// the debug session. +// +// Example: +// threads:63387,633b2,63424,63462,63486; +// +// "thread-pcs" comma-sep-base16 A list of pc values for all threads that currently +// exist in the process, including the thread that +// this T packet is reporting as stopped. +// This key-value pair will only be emitted when the +// "threads" key is already included in the T packet. +// The pc values correspond to the threads reported +// in the "threads" list. The number of pcs in the +// "thread-pcs" list will be the same as the number of +// threads in the "threads" list. +// lldb may request that this be included in the T +// packet via the QListThreadsInStopReply packet +// earlier in the debug session. +// +// Example: +// thread-pcs:dec14,2cf872b0,2cf8681c,2d02d68c,2cf716a8; +// +// BEST PRACTICES: +// Since register values can be supplied with this packet, it is often useful +// to return the PC, SP, FP, LR (if any), and FLAGS registers so that separate +// packets don't need to be sent to read each of these registers from each +// thread. +// +// If a thread is stopped for no reason (like just because another thread +// stopped, or because when one core stops all cores should stop), use a +// "T" packet with "00" as the signal number and fill in as many key values +// and registers as possible. +// +// LLDB likes to know why a thread stopped since many thread control +// operations like stepping over a source line, actually are implemented +// by running the process multiple times. If a breakpoint is hit while +// trying to step over a source line and LLDB finds out that a breakpoint +// is hit in the "reason", we will know to stop trying to do the step +// over because something happened that should stop us from trying to +// do the step. If we are at a breakpoint and we disable the breakpoint +// at the current PC and do an instruction single step, knowing that +// we stopped due to a "trace" helps us know that we can continue +// running versus stopping due to a "breakpoint" (if we have two +// breakpoint instruction on consecutive instructions). So the more info +// we can get about the reason a thread stops, the better job LLDB can +// do when controlling your process. A typical GDB server behavior is +// to send a SIGTRAP for breakpoints _and_ also when instruction single +// stepping, in this case the debugger doesn't really know why we +// stopped and it can make it hard for the debugger to control your +// program correctly. What if a real SIGTRAP was delivered to a thread +// while we were trying to single step? We wouldn't know the difference +// with a standard GDB remote server and we could do the wrong thing. +// +// PRIORITY TO IMPLEMENT +// High. Having the extra information in your stop reply packets makes +// your debug session more reliable and informative. +//---------------------------------------------------------------------- + + +//---------------------------------------------------------------------- +// PLATFORM EXTENSION - for use as a GDB remote platform +//---------------------------------------------------------------------- +// "qfProcessInfo" +// "qsProcessInfo" +// +// BRIEF +// Get the first process info (qfProcessInfo) or subsequent process +// info (qsProcessInfo) for one or more processes on the remote +// platform. The first call gets the first match and subsequent calls +// to qsProcessInfo gets the subsequent matches. Return an error EXX, +// where XX are two hex digits, when no more matches are available. +// +// PRIORITY TO IMPLEMENT +// Required. The qfProcessInfo packet can be followed by a ':' and +// some key value pairs. The key value pairs in the command are: +// +// KEY VALUE DESCRIPTION +// =========== ======== ================================================ +// "name" ascii-hex An ASCII hex string that contains the name of +// the process that will be matched. +// "name_match" enum One of: "equals", "starts_with", "ends_with", +// "contains" or "regex" +// "pid" integer A string value containing the decimal process ID +// "parent_pid" integer A string value containing the decimal parent +// process ID +// "uid" integer A string value containing the decimal user ID +// "gid" integer A string value containing the decimal group ID +// "euid" integer A string value containing the decimal effective user ID +// "egid" integer A string value containing the decimal effective group ID +// "all_users" bool A boolean value that specifies if processes should +// be listed for all users, not just the user that the +// platform is running as +// "triple" string An ASCII triple string ("x86_64", +// "x86_64-apple-macosx", "armv7-apple-ios") +// +// The response consists of key/value pairs where the key is separated from the +// values with colons and each pair is terminated with a semi colon. For a list +// of the key/value pairs in the response see the "qProcessInfoPID" packet +// documentation. +// +// Sample packet/response: +// send packet: $qfProcessInfo#00 +// read packet: $pid:60001;ppid:59948;uid:7746;gid:11;euid:7746;egid:11;name:6c6c6462;triple:x86_64-apple-macosx;#00 +// send packet: $qsProcessInfo#00 +// read packet: $pid:59992;ppid:192;uid:7746;gid:11;euid:7746;egid:11;name:6d64776f726b6572;triple:x86_64-apple-macosx;#00 +// send packet: $qsProcessInfo#00 +// read packet: $E04#00 +//---------------------------------------------------------------------- + + +//---------------------------------------------------------------------- +// PLATFORM EXTENSION - for use as a GDB remote platform +//---------------------------------------------------------------------- +// "qLaunchGDBServer" +// +// BRIEF +// Have the remote platform launch a GDB server. +// +// PRIORITY TO IMPLEMENT +// Required. The qLaunchGDBServer packet must be followed by a ':' and +// some key value pairs. The key value pairs in the command are: +// +// KEY VALUE DESCRIPTION +// =========== ======== ================================================ +// "port" integer A string value containing the decimal port ID or +// zero if the port should be bound and returned +// +// "host" integer The host that connections should be limited to +// when the GDB server is connected to. +// +// The response consists of key/value pairs where the key is separated from the +// values with colons and each pair is terminated with a semi colon. +// +// Sample packet/response: +// send packet: $qLaunchGDBServer:port:0;host:lldb.apple.com;#00 +// read packet: $pid:60025;port:50776;#00 +// +// The "pid" key/value pair is only specified if the remote platform launched +// a separate process for the GDB remote server and can be omitted if no +// process was separately launched. +// +// The "port" key/value pair in the response lets clients know what port number +// to attach to in case zero was specified as the "port" in the sent command. +//---------------------------------------------------------------------- + + +//---------------------------------------------------------------------- +// PLATFORM EXTENSION - for use as a GDB remote platform +//---------------------------------------------------------------------- +// "qProcessInfoPID:PID" +// +// BRIEF +// Have the remote platform get detailed information on a process by +// ID. PID is specified as a decimal integer. +// +// PRIORITY TO IMPLEMENT +// Optional. +// +// The response consists of key/value pairs where the key is separated from the +// values with colons and each pair is terminated with a semi colon. +// +// The key value pairs in the response are: +// +// KEY VALUE DESCRIPTION +// =========== ======== ================================================ +// "pid" integer Process ID as a decimal integer string +// "ppid" integer Parent process ID as a decimal integer string +// "uid" integer A string value containing the decimal user ID +// "gid" integer A string value containing the decimal group ID +// "euid" integer A string value containing the decimal effective user ID +// "egid" integer A string value containing the decimal effective group ID +// "name" ascii-hex An ASCII hex string that contains the name of the process +// "triple" string A target triple ("x86_64-apple-macosx", "armv7-apple-ios") +// +// Sample packet/response: +// send packet: $qProcessInfoPID:60050#00 +// read packet: $pid:60050;ppid:59948;uid:7746;gid:11;euid:7746;egid:11;name:6c6c6462;triple:x86_64-apple-macosx;#00 +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "vAttachName" +// +// BRIEF +// Same as vAttach, except instead of a "pid" you send a process name. +// +// PRIORITY TO IMPLEMENT +// Low. Only needed for "process attach -n". If the packet isn't supported +// then "process attach -n" will fail gracefully. So you need only to support +// it if attaching to a process by name makes sense for your environment. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "vAttachWait" +// +// BRIEF +// Same as vAttachName, except that the stub should wait for the next instance +// of a process by that name to be launched and attach to that. +// +// PRIORITY TO IMPLEMENT +// Low. Only needed to support "process attach -w -n" which will fail +// gracefully if the packet is not supported. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "qAttachOrWaitSupported" +// +// BRIEF +// This is a binary "is it supported" query. Return OK if you support +// vAttachOrWait +// +// PRIORITY TO IMPLEMENT +// Low. This is required if you support vAttachOrWait, otherwise no support +// is needed since the standard "I don't recognize this packet" response +// will do the right thing. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "vAttachOrWait" +// +// BRIEF +// Same as vAttachWait, except that the stub will attach to a process +// by name if it exists, and if it does not, it will wait for a process +// of that name to appear and attach to it. +// +// PRIORITY TO IMPLEMENT +// Low. Only needed to implement "process attach -w -i false -n". If +// you don't implement it but do implement -n AND lldb can somehow get +// a process list from your device, it will fall back on scanning the +// process list, and sending vAttach or vAttachWait depending on +// whether the requested process exists already. This is racy, +// however, so if you want to support this behavior it is better to +// support this packet. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "jThreadExtendedInfo" +// +// BRIEF +// This packet, which takes its arguments as JSON and sends its reply as +// JSON, allows the gdb remote stub to provide additional information +// about a given thread. +// +// PRIORITY TO IMPLEMENT +// Low. This packet is only needed if the gdb remote stub wants to +// provide interesting additional information about a thread for the +// user. +// +// This packet takes its arguments in JSON form ( http://www.json.org ). +// At a minimum, a thread must be specified, for example: +// +// jThreadExtendedInfo:{"thread":612910} +// +// Because this is a JSON string, the thread number is provided in base10. +// Additional key-value pairs may be provided by lldb to the gdb remote +// stub. For instance, on some versions of Mac OS X, lldb can read offset +// information out of the system libraries. Using those offsets, debugserver +// is able to find the Thread Specific Address (TSD) for a thread and include +// that in the return information. So lldb will send these additional fields +// like so: +// +// jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":612910} +// +// There are no requirements for what is included in the response. A simple +// reply on a Mac OS X Yosemite / iOS 8 may include the pthread_t value, the +// Thread Specific Data (TSD) address, the dispatch_queue_t value if the thread +// is associated with a GCD queue, and the requested Quality of Service (QoS) +// information about that thread. For instance, a reply may look like: +// +// {"tsd_address":4371349728,"requested_qos":{"enum_value":33,"constant_name":"QOS_CLASS_USER_INTERACTIVE","printable_name":"User Interactive"},"pthread_t":4371349504,"dispatch_queue_t":140735087127872} +// +// tsd_address, pthread_t, and dispatch_queue_t are all simple key-value pairs. +// The JSON standard requires that numbers be expressed in base 10 - so all of +// these are. requested_qos is a dictionary with three key-value pairs in it - +// so the UI layer may choose the form most appropriate for displaying to the user. +// +// Sending JSON over gdb-remote protocol introduces some problems. We may be +// sending strings with arbitrary contents in them, including the '#', '$', and '*' +// characters that have special meaning in gdb-remote protocol and cannot occur +// in the middle of the string. The standard solution for this would be to require +// ascii-hex encoding of all strings, or ascii-hex encode the entire JSON payload. +// +// Instead, the binary escaping convention is used for JSON data. This convention +// (e.g. used for the X packet) says that if '#', '$', '*', or '}' are to occur in +// the payload, the character '}' (0x7d) is emitted, then the metacharacter is emitted +// xor'ed by 0x20. The '}' character occurs in every JSON payload at least once, and +// '}' ^ 0x20 happens to be ']' so the raw packet characters for a request will look +// like +// +// jThreadExtendedInfo:{"thread":612910}] +// +// on the wire. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "QEnableCompression" +// +// BRIEF +// This packet enables compression of the packets that the debug stub sends to lldb. +// If the debug stub can support compression, it indictes this in the reply of the +// "qSupported" packet. e.g. +// LLDB SENDS: qSupported:xmlRegisters=i386,arm,mips +// STUB REPLIES: qXfer:features:read+;SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;DefaultCompressionMinSize=384 +// +// If lldb knows how to use any of these compression algorithms, it can ask that this +// compression mode be enabled. It may optionally change the minimum packet size +// where compression is used. Typically small packets do not benefit from compression, +// as well as compression headers -- compression is most beneficial with larger packets. +// +// QEnableCompression:type:zlib-deflate; +// or +// QEnableCompression:type:zlib-deflate;minsize:512; +// +// The debug stub should reply with an uncompressed "OK" packet to indicate that the +// request was accepted. All further packets the stub sends will use this compression. +// +// Packets are compressed as the last step before they are sent from the stub, and +// decompressed as the first step after they are received. The packet format in compressed +// mode becomes one of two: +// +// $N#00 +// +// $C:#00 +// +// Where "#00" is the actual checksum value if noack mode is not enabled. The checksum +// value is for the "N" or +// "C:" bytes in the packet. +// +// The size of the uncompressed payload in base10 is provided because it will simplify +// decompression if the final buffer size needed is known ahead of time. +// +// Compression on low-latency connections is unlikely to be an improvement. Particularly +// when the debug stub and lldb are running on the same host. It should only be used +// for slow connections, and likely only for larger packets. +// +// Example compression algorithsm that may be used include +// +// zlib-deflate +// The raw DEFLATE format as described in IETF RFC 1951. With the ZLIB library, you +// can compress to this format with an initialization like +// deflateInit2 (&stream, 5, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) +// and you can decompress with an initialization like +// inflateInit2 (&stream, -15) +// +// lz4 +// https://en.wikipedia.org/wiki/LZ4_(compression_algorithm) +// https://github.com/Cyan4973/lz4 +// The libcompression APIs on darwin systems call this COMPRESSION_LZ4_RAW. +// +// lzfse +// An Apple proprietary compression algorithm implemented in libcompression. +// +// lzma +// libcompression implements "LZMA level 6", the default compression for the +// open source LZMA implementation. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "jGetLoadedDynamicLibrariesInfos" +// +// BRIEF +// This packet asks the remote debug stub to send the details about libraries +// being added/removed from the process as a performance optimization. +// +// LLDB SENDS: jGetLoadedDynamicLibrariesInfos:{"image_count":1,"image_list_address":140734800075128} +// STUB REPLIES: ${"images":[{"load_address":4294967296,"mod_date":0,"pathname":"/tmp/a.out","uuid":"02CF262C-ED6F-3965-9E14-63538B465CFF","mach_header":{"magic":4277009103,"cputype":16777223,"cpusubtype":18446744071562067971,"filetype":2},"segments":{"name":"__PAGEZERO","vmaddr":0,"vmsize":4294967296,"fileoff":0,"filesize":0,"maxprot":0},{"name":"__TEXT","vmaddr":4294967296,"vmsize":4096,"fileoff":0,"filesize":4096,"maxprot":7},{"name":"__LINKEDIT","vmaddr":4294971392,"vmsize":4096,"fileoff":4096,"filesize":152,"maxprot":7}}]}#00 +// +// Or pretty-printed, +// +// STUB REPLIES: ${"images": +// [ +// {"load_address":4294967296, +// "mod_date":0, +// "pathname":"/tmp/a.out", +// "uuid":"02CF262C-ED6F-3965-9E14-63538B465CFF", +// "mach_header": +// {"magic":4277009103, +// "cputype":16777223, +// "cpusubtype":18446744071562067971, +// "filetype":2 +// }, +// "segments": +// [ +// {"name":"__PAGEZERO", +// "vmaddr":0, +// "vmsize":4294967296, +// "fileoff":0, +// "filesize":0, +// "maxprot":0 +// }, +// {"name":"__TEXT", +// "vmaddr":4294967296, +// "vmsize":4096, +// "fileoff":0, +// "filesize":4096, +// "maxprot":7 +// }, +// {"name":"__LINKEDIT", +// "vmaddr":4294971392, +// "vmsize":4096, +// "fileoff":4096, +// "filesize":152, +// "maxprot":7 +// } +// ] +// } +// ] +// } +// +// +// This is similar to the qXfer:libraries:read packet, and it could +// be argued that it should be merged into that packet. A separate +// packet was created primarily because lldb needs to specify the +// number of images to be read and the address from which the initial +// information is read. Also the XML DTD would need to be extended +// quite a bit to provide all the information that the DynamicLoaderMacOSX +// would need to work correctly on this platform. +// +// On Mac OS X / iOS, when libraries are added or removed, a stub +// function is called which lldb puts a breakpoint on. The arguments +// to the stub function include the number of libraries being added +// or removed and the address where the list of libraries can be +// found. The information at this address is the load address of the +// library, the filename, and the mod date of the library if available. +// DynamicLoaderMacOSX then parses the load commands in the Mach-O header +// at the load address before it can decide what action to take. +// +// The purpose of this packet is to eliminate all of the memory reads needed +// to read the Mach-O header and load commands for these libraries. +// On a typical GUI app, there can be a couple hundred shared libraries +// which results in megabytes of read packets. That same information can +// be returned in a couple hundred kilobytes in JSON format from the remote +// debugserver. +// +// +// PRIORITY TO IMPLEMENT +// Low. If this packet is absent, lldb will read the Mach-O headers/load +// commands out of memory. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "jThreadsInfo" +// +// BRIEF +// Ask for the server for thread stop information of all threads. +// +// PRIORITY TO IMPLEMENT +// Low. This is a performance optimization, which speeds up debugging by avoiding +// multiple round-trips for retrieving thread information. The information from this +// packet can be retrieved using a combination of qThreadStopInfo and m packets. +//---------------------------------------------------------------------- + +The data in this packet is very similar to the stop reply packets, but is packaged in +JSON and uses JSON arrays where applicable. The JSON output looks like: + [ + { "tid":1580681, + "metype":6, + "medata":[2,0], + "reason":"exception", + "qaddr":140735118423168, + "registers": { + "0":"8000000000000000", + "1":"0000000000000000", + "2":"20fabf5fff7f0000", + "3":"e8f8bf5fff7f0000", + "4":"0100000000000000", + "5":"d8f8bf5fff7f0000", + "6":"b0f8bf5fff7f0000", + "7":"20f4bf5fff7f0000", + "8":"8000000000000000", + "9":"61a8db78a61500db", + "10":"3200000000000000", + "11":"4602000000000000", + "12":"0000000000000000", + "13":"0000000000000000", + "14":"0000000000000000", + "15":"0000000000000000", + "16":"960b000001000000", + "17":"0202000000000000", + "18":"2b00000000000000", + "19":"0000000000000000", + "20":"0000000000000000" + }, + "memory":[ + {"address":140734799804592,"bytes":"c8f8bf5fff7f0000c9a59e8cff7f0000"}, + {"address":140734799804616,"bytes":"00000000000000000100000000000000"} + ] + } + ] + +It contains an array of dictionaries with all of the key value pairs that are +normally in the stop reply packet, including the expedited registers. The registers are +passed as hex-encoded JSON string in debuggee-endian byte order. Note that the register +numbers are decimal numbers, unlike the stop-reply packet, where they are written in +hex. The packet also contains expedited memory in the "memory" key. This allows the +server to expedite memory that the client is likely to use (e.g., areas around the +stack pointer, which are needed for computing backtraces) and it reduces the packet +count. + +On MacOSX with debugserver, we expedite the frame pointer backchain for a thread +(up to 256 entries) by reading 2 pointers worth of bytes at the frame pointer (for +the previous FP and PC), and follow the backchain. Most backtraces on MacOSX and +iOS now don't require us to read any memory! + +//---------------------------------------------------------------------- +// "qQueryGDBServer" +// +// BRIEF +// Ask the platform for the list of gdbservers we have to connect +// +// PRIORITY TO IMPLEMENT +// Low. The packet is required to support connecting to gdbserver started +// by the platform instance automatically. +//---------------------------------------------------------------------- + +If the remote platform automatically started one or more gdbserver instance (without +lldb asking it) then it have to return the list of port number or socket name for +each of them what can be used by lldb to connect to those instances. + +The data in this packet is a JSON array of JSON objects with the following keys: +"port": (optional) +"socket_name": (optional) + +Example packet: +[ + { "port": 1234 }, + { "port": 5432 }, + { "socket_name": "foo" } +] diff --git a/docs/testsuite/2010-10-19-14_10_49.059609/TestSettings.SettingsCommandTestCase.test_set_output_path.log b/docs/testsuite/2010-10-19-14_10_49.059609/TestSettings.SettingsCommandTestCase.test_set_output_path.log new file mode 100644 index 000000000000..e18199537e6f --- /dev/null +++ b/docs/testsuite/2010-10-19-14_10_49.059609/TestSettings.SettingsCommandTestCase.test_set_output_path.log @@ -0,0 +1,43 @@ + +os command: [['/bin/sh', '-c', 'make clean; make']] +stdout: rm -rf "a.out" "a.out.dSYM" main.o main.d +g++ -arch x86_64 -gdwarf-2 -O0 -c -o main.o main.cpp +g++ -arch x86_64 -gdwarf-2 -O0 main.o -o "a.out" +/usr/bin/dsymutil -o "a.out.dSYM" "a.out" + +stderr: None +retcode: 0 + + +runCmd: file /Volumes/data/lldb/svn/trunk/test/settings/a.out +output: Current executable set to '/Volumes/data/lldb/svn/trunk/test/settings/a.out' (x86_64). + + +runCmd: settings set target.process.output-path 'stdout.txt' +output: + +runCmd: settings show target.process.output-path +output: target.process.output-path (string) = 'stdout.txt' + + +Expecting start string: target.process.output-path (string) = 'stdout.txt' +Matched + +runCmd: run +output: Process 43533 launched: '/Volumes/data/lldb/svn/trunk/test/settings/a.out' (x86_64) + + +FAIL + +runCmd: process kill +check of return status not required +runCmd failed! +error: Process must be launched. + + +Traceback (most recent call last): + File "/Volumes/data/lldb/svn/trunk/test/settings/TestSettings.py", line 125, in test_set_output_path + "'stdout.txt' exists due to target.process.output-path.") +AssertionError: False is not True : 'stdout.txt' exists due to target.process.output-path. + + diff --git a/docs/testsuite/a-detailed-walkthrough.txt b/docs/testsuite/a-detailed-walkthrough.txt new file mode 100644 index 000000000000..6b5267f414ce --- /dev/null +++ b/docs/testsuite/a-detailed-walkthrough.txt @@ -0,0 +1,309 @@ +Let's pick test/settings/TestSettings.py as our example. First, notice the file +name "TestSettings.py", the Test*.py pattern is the default mechanism that the +test driver uses for discovery of tests. As to TestSettings.py, it defines a +class: + +class SettingsCommandTestCase(TestBase): + +derived from TestBase, which is defined in test/lldbtest.py and is itself +derived from Python's unittest framework's TestCase class. See also +http://docs.python.org/library/unittest.html for more details. + +To just run the TestSettings.py test, chdir to the lldb test directory, and then +type the following command: + +/Volumes/data/lldb/svn/trunk/test $ ./dotest.py settings +---------------------------------------------------------------------- +Collected 6 tests + +---------------------------------------------------------------------- +Ran 6 tests in 8.699s + +OK (expected failures=1) +/Volumes/data/lldb/svn/trunk/test $ + +Pass '-v' option to the test driver to also output verbose descriptions of the +individual test cases and their test status: + +/Volumes/data/lldb/svn/trunk/test $ ./dotest.py -v settings +---------------------------------------------------------------------- +Collected 6 tests + +test_set_auto_confirm (TestSettings.SettingsCommandTestCase) +Test that after 'set auto-confirm true', manual confirmation should not kick in. ... ok +test_set_output_path (TestSettings.SettingsCommandTestCase) +Test that setting target.process.output-path for the launched process works. ... expected failure +test_set_prompt (TestSettings.SettingsCommandTestCase) +Test that 'set prompt' actually changes the prompt. ... ok +test_set_term_width (TestSettings.SettingsCommandTestCase) +Test that 'set term-width' actually changes the term-width. ... ok +test_with_dsym (TestSettings.SettingsCommandTestCase) +Test that run-args and env-vars are passed to the launched process. ... ok +test_with_dwarf (TestSettings.SettingsCommandTestCase) +Test that run-args and env-vars are passed to the launched process. ... ok + +---------------------------------------------------------------------- +Ran 6 tests in 5.735s + +OK (expected failures=1) +/Volumes/data/lldb/svn/trunk/test $ + +Underneath, the '-v' option passes keyword argument verbosity=2 to the +Python's unittest.TextTestRunner (see also +http://docs.python.org/library/unittest.html#unittest.TextTestRunner). For very +detailed descriptions about what's going on during the test, pass '-t' to the +test driver, which asks the test driver to trace the commands executed and to +display their output. For brevity, the '-t' output is not included here. + +Notice the 'expected failures=1' message at the end of the run. This is because +of a bug currently in lldb such that setting target.process.output-path to +'stdout.txt' does not have any effect on the redirection of the standard output +of the subsequent launched process. We are using unittest2 (a backport of new +unittest features for Python 2.4-2.6) to decorate (mark) the particular test +method as such: + + @unittest2.expectedFailure + # rdar://problem/8435794 + # settings set target.process.output-path does not seem to work + def test_set_output_path(self): + +See http://pypi.python.org/pypi/unittest2 for more details. + +Now let's look inside the test method: + + def test_set_output_path(self): + """Test that setting target.process.output-path for the launched process works.""" + self.buildDefault() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Set the output-path and verify it is set. + self.runCmd("settings set target.process.output-path 'stdout.txt'") + self.expect("settings show target.process.output-path", + startstr = "target.process.output-path (string) = 'stdout.txt'") + + self.runCmd("run", RUN_SUCCEEDED) + + # The 'stdout.txt' file should now exist. + self.assertTrue(os.path.isfile("stdout.txt"), + "'stdout.txt' exists due to target.process.output-path.") + + # Read the output file produced by running the program. + with open('stdout.txt', 'r') as f: + output = f.read() + + self.expect(output, exe=False, + startstr = "This message should go to standard out.") + +The self.buildDefault() statement is used to build a default binary for this +test instance. For this particular test case, since we don't really care what +debugging format is used, we instruct the build subsystem to build the default +binary for us. The base class TestBase has defined three instance methods: + + def buildDefault(self, architecture=None, compiler=None, dictionary=None): + """Platform specific way to build the default binaries.""" + module = __import__(sys.platform) + if not module.buildDefault(self, architecture, compiler, dictionary): + raise Exception("Don't know how to build default binary") + + def buildDsym(self, architecture=None, compiler=None, dictionary=None): + """Platform specific way to build binaries with dsym info.""" + module = __import__(sys.platform) + if not module.buildDsym(self, architecture, compiler, dictionary): + raise Exception("Don't know how to build binary with dsym") + + def buildDwarf(self, architecture=None, compiler=None, dictionary=None): + """Platform specific way to build binaries with dwarf maps.""" + module = __import__(sys.platform) + if not module.buildDwarf(self, architecture, compiler, dictionary): + raise Exception("Don't know how to build binary with dwarf") + +And the test/plugins/darwin.py provides the implementation for all three build +methods using the makefile mechanism. We envision that linux plugin can use a +similar approach to accomplish the task of building the binaries. + +Mac OS X provides an additional way to manipulate archived DWARF debug symbol +files and produces dSYM files. The buildDsym() instance method is used by the +test method to build the binary with dsym info. For an example of this, +see test/array_types/TestArrayTypes.py: + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_with_dsym_and_run_command(self): + """Test 'frame variable var_name' on some variables with array types.""" + self.buildDsym() + self.array_types() + +This method is decorated with a skipUnless decorator so that it will only gets +included into the test suite if the platform it is running on is 'darwin', aka +Mac OS X. + +Type 'man dsymutil' for more details. + +After the binary is built, it is time to specify the file to be used as the main +executable by lldb: + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + +This is where the attribute assignment: + +class SettingsCommandTestCase(TestBase): + + mydir = "settings" + +which happens right after the SettingsCommandTestCase class declaration comes +into place. It specifies the relative directory to the top level 'test' so that +the test harness can change its working directory in order to find the +executable as well as the source code files. The runCmd() method is defined in +the TestBase base class (within test/lldbtest.py) and its purpose is to pass the +specified command to the lldb command interpreter. It's like you're typing the +command within an interactive lldb session. + +The CURRENT_EXECUTABLE_SET is an assert message defined in the lldbtest module +so that it can be reused from other test modules. + +By default, the runCmd() is going to check the return status of the command +execution and fails the test if it is not a success. The assert message, in our +case CURRENT_EXECUTABLE_SET, is used in the exception printout if this happens. + +There are cases when we don't care about the return status from the command +execution. This can be accomplished by passing the keyword argument pair +'check=False' to the method. + +After the current executable is set, we'll then execute two more commands: + + # Set the output-path and verify it is set. + self.runCmd("settings set target.process.output-path 'stdout.txt'") + self.expect("settings show target.process.output-path", + SETTING_MSG("target.process.output-path"), + startstr = "target.process.output-path (string) = 'stdout.txt'") + +The first uses the 'settings set' command to set the static setting +target.process.output-path to be 'stdout.txt', instead of the default +'/dev/stdout'. We then immediately issue a 'settings show' command to check +that, indeed, the setting did take place. Notice that we use a new method +expect() to accomplish the task, which in effect issues a runCmd() behind the +door and grabs the output from the command execution and expects to match the +start string of the output against what we pass in as the value of the keyword +argument pair: + + startstr = "target.process.output-path (string) = 'stdout.txt'" + +Take a look at TestBase.expect() within lldbtest.py for more details. Among +other things, it can also match against a list of regexp patterns as well as a +list of sub strings. And it can also perform negative matching, i.e., instead +of expecting something from the output of command execution, it can perform the +action of 'not expecting' something. + +This will launch/run the program: + + self.runCmd("run", RUN_SUCCEEDED) + +And this asserts that the file 'stdout.txt' should be present after running the +program. + + # The 'stdout.txt' file should now exist. + self.assertTrue(os.path.isfile("stdout.txt"), + "'stdout.txt' exists due to target.process.output-path.") + +Also take a look at main.cpp which emits some message to the stdout. Now, if we +pass this assertion, it's time to examine the contents of the file to make sure +it contains the same message as programmed in main.cpp: + + # Read the output file produced by running the program. + with open('stdout.txt', 'r') as f: + output = f.read() + + self.expect(output, exe=False, + startstr = "This message should go to standard out.") + +We open the file and read its contents into output, then issue an expect() +method. The 'exe=False' keyword argument pair tells expect() that don't try to +execute the first arg as a command at all. Instead, treat it as a string to +match against whatever is thrown in as keyword argument pairs! + +There are also other test methods present in the TestSettings.py mode: +test_set_prompt(), test_set_term_width(), test_set_auto_confirm(), +test_with_dsym(), and test_with_dwarf(). We are using the default test loader +from unittest framework, which uses the 'test' method name prefix to identify +test methods automatically. + +This finishes the walkthrough of the test method test_set_output_path(self). +Before we say goodbye, notice the little method definition at the top of the +file: + + @classmethod + def classCleanup(cls): + system(["/bin/sh", "-c", "rm -f output.txt"]) + system(["/bin/sh", "-c", "rm -f stdout.txt"]) + +This is a classmethod (as shown by the @classmethod decorator) which allows the +individual test class to perform cleanup actions after the test harness finishes +with the particular test class. This is part of the so-called test fixture in +the unittest framework. From http://docs.python.org/library/unittest.html: + +A test fixture represents the preparation needed to perform one or more tests, +and any associate cleanup actions. This may involve, for example, creating +temporary or proxy databases, directories, or starting a server process. + +The TestBase class uses such fixture with setUp(self), tearDown(self), +setUpClass(cls), and tearDownClass(cls). And within teraDownClass(cls), it +checks whether the current class has an attribute named 'classCleanup', and +executes as a method if present. In this particular case, the classCleanup() +calls a utility function system() defined in lldbtest.py in order to remove the +files created by running the program as the tests are executed. + +This system() function uses the Python subprocess module to spawn the process +and to retrieve its results. If the test instance passes the keyword argument +pair 'sender=self', the detailed command execution through the operating system +also gets recorded in a session object. If the test instance fails or errors, +the session info automatically gets dumped to a file grouped under a directory +named after the timestamp of the particular test suite run. + +For simple cases, look for the timestamp directory in the same directory of the +test driver program dotest.py. For example, if we comment out the +@expectedFailure decorator for TestSettings.py, and then run the test module: + +/Volumes/data/lldb/svn/trunk/test $ ./dotest.py -v settings +---------------------------------------------------------------------- +Collected 6 tests + +test_set_auto_confirm (TestSettings.SettingsCommandTestCase) +Test that after 'set auto-confirm true', manual confirmation should not kick in. ... ok +test_set_output_path (TestSettings.SettingsCommandTestCase) +Test that setting target.process.output-path for the launched process works. ... FAIL +test_set_prompt (TestSettings.SettingsCommandTestCase) +Test that 'set prompt' actually changes the prompt. ... ok +test_set_term_width (TestSettings.SettingsCommandTestCase) +Test that 'set term-width' actually changes the term-width. ... ok +test_with_dsym (TestSettings.SettingsCommandTestCase) +Test that run-args and env-vars are passed to the launched process. ... ok +test_with_dwarf (TestSettings.SettingsCommandTestCase) +Test that run-args and env-vars are passed to the launched process. ... ok + +====================================================================== +FAIL: test_set_output_path (TestSettings.SettingsCommandTestCase) +Test that setting target.process.output-path for the launched process works. +---------------------------------------------------------------------- +Traceback (most recent call last): + File "/Volumes/data/lldb/svn/trunk/test/settings/TestSettings.py", line 125, in test_set_output_path + "'stdout.txt' exists due to target.process.output-path.") +AssertionError: False is not True : 'stdout.txt' exists due to target.process.output-path. + +---------------------------------------------------------------------- +Ran 6 tests in 8.219s + +FAILED (failures=1) +/Volumes/data/lldb/svn/trunk/test $ ls 2010-10-19-14:10:49.059609 + +NOTE: This directory name has been changed to not contain the ':' character + which is not allowed in windows platforms. We'll change the ':' to '_' + and get rid of the microsecond resolution by modifying the test driver. + +TestSettings.SettingsCommandTestCase.test_set_output_path.log +/Volumes/data/lldb/svn/trunk/test $ + +We get one failure and a timestamp directory 2010-10-19-14:10:49.059609. +For education purposes, the directory and its contents are reproduced here in +the same directory as the current file. diff --git a/docs/testsuite/best-practices.txt b/docs/testsuite/best-practices.txt new file mode 100644 index 000000000000..b5a9156fd521 --- /dev/null +++ b/docs/testsuite/best-practices.txt @@ -0,0 +1,93 @@ +This document attempts to point out some best practices that prove to be helpful +when building new test cases in the tot/test directory. Everyone is welcomed to +add/modify contents into this file. + +o Do not use hard-coded line numbers in your test case. Instead, try to tag the + line with some distinguishing pattern, and use the function line_number() + defined in lldbtest.py which takes filename and string_to_match as arguments + and returns the line number. + +As an example, take a look at test/breakpoint_conditions/main.c which has these +two lines: + + return c(val); // Find the line number of c's parent call here. + +and + + return val + 3; // Find the line number of function "c" here. + +The Python test case TestBreakpointConditions.py uses the comment strings to +find the line numbers during setUp(self) and use them later on to verify that +the correct breakpoint is being stopped on and that its parent frame also has +the correct line number as intended through the breakpoint condition. + +o Take advantage of the unittest framework's decorator features to properly + mark your test class or method for platform-specific tests. + +As an example, take a look at test/forward/TestForwardDeclaration.py which has +these lines: + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_with_dsym_and_run_command(self): + """Display *bar_ptr when stopped on a function with forward declaration of struct bar.""" + self.buildDsym() + self.forward_declaration() + +This tells the test harness that unless we are running "darwin", the test should +be skipped. This is because we are asking to build the binaries with dsym debug +info, which is only available on the darwin platforms. + +o Cleanup after yourself. A classic example of this can be found in test/types/ + TestFloatTypes.py: + + def test_float_types_with_dsym(self): + """Test that float-type variables are displayed correctly.""" + d = {'CXX_SOURCES': 'float.cpp'} + self.buildDsym(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.float_type() + + ... + + def test_double_type_with_dsym(self): + """Test that double-type variables are displayed correctly.""" + d = {'CXX_SOURCES': 'double.cpp'} + self.buildDsym(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.double_type() + +This tests different data structures composed of float types to verify that what +the debugger prints out matches what the compiler does for different variables +of these types. We're using a dictionary to pass the build parameters to the +build system. After a particular test instance is done, it is a good idea to +clean up the files built. This eliminates the chance that some leftover files +can interfere with the build phase for the next test instance and render it +invalid. + +TestBase.setTearDownCleanup(self, dictionary) defined in lldbtest.py is created +to cope with this use case by taking the same build parameters in order to do +the cleanup when we are finished with a test instance, during +TestBase.tearDown(self). + +o Class-wise cleanup after yourself. + +TestBase.tearDownClass(cls) provides a mechanism to invoke the platform-specific +cleanup after finishing with a test class. A test class can have more than one +test methods, so the tearDownClass(cls) method gets run after all the test +methods have been executed by the test harness. + +The default cleanup action performed by the plugins/darwin.py module invokes the +"make clean" os command. + +If this default cleanup is not enough, individual class can provide an extra +cleanup hook with a class method named classCleanup , for example, +in test/breakpoint_command/TestBreakpointCommand.py: + + @classmethod + def classCleanup(cls): + system(["/bin/sh", "-c", "rm -f output.txt"]) + +The 'output.txt' file gets generated during the test run, so it makes sense to +explicitly spell out the action in the same TestBreakpointCommand.py file to do +the cleanup instead of artificially adding it as part of the default cleanup +action which serves to cleanup those intermediate and a.out files. diff --git a/examples/customization/bin-utils/.lldbinit b/examples/customization/bin-utils/.lldbinit new file mode 100644 index 000000000000..5a2f6feb94df --- /dev/null +++ b/examples/customization/bin-utils/.lldbinit @@ -0,0 +1,5 @@ +# So that ~/binutils.py takes precedence. +script sys.path[:0] = [os.path.expanduser('~')] +script import binutils +command script add -f binutils.itob itob +command script add -f binutils.utob utob diff --git a/examples/customization/bin-utils/README b/examples/customization/bin-utils/README new file mode 100644 index 000000000000..1352d93b2787 --- /dev/null +++ b/examples/customization/bin-utils/README @@ -0,0 +1,36 @@ +Files in this directory: + +o .lldbinit: + +An example lldb init file that imports the binutils.py module and adds the +following commands: 'itob' and 'utob'. + +o binutils.py: + +Python module which provides implementation for the 'itob' and 'utob' commands. + +o README: + +The file you are reading now. + +================================================================================ +The following terminal output shows an interaction with lldb using the .lldbinit +and the binutils.py files which are located in my HOME directory. The lldb init +file imports the utils Python module and adds the 'itob' and 'utob' commands. + +$ /Volumes/data/lldb/svn/trunk/build/Debug/lldb +(lldb) help itob +Convert the integer to print its two's complement representation. + args[0] (mandatory) is the integer to be converted + args[1] (mandatory) is the bit width of the two's complement representation + args[2] (optional) if specified, turns on verbose printing +Syntax: itob +(lldb) itob -5 4 + [1, 0, 1, 1] +(lldb) itob -5 32 v + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1] +(lldb) utob 0xABCD 32 v + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1] +(lldb) diff --git a/examples/customization/bin-utils/binutils.py b/examples/customization/bin-utils/binutils.py new file mode 100644 index 000000000000..313a354ec3a5 --- /dev/null +++ b/examples/customization/bin-utils/binutils.py @@ -0,0 +1,122 @@ +"Collection of tools for displaying bit representation of numbers.""" + +import StringIO + +def binary(n, width=None): + """ + Return a list of (0|1)'s for the binary representation of n where n >= 0. + If you specify a width, it must be > 0, otherwise it is ignored. The list + could be padded with 0 bits if width is specified. + """ + l = [] + if width and width <= 0: + width = None + while n > 0: + l.append(1 if n&1 else 0) + n = n >> 1 + + if width: + for i in range(width - len(l)): + l.append(0) + + l.reverse() + return l + +def twos_complement(n, width): + """ + Return a list of (0|1)'s for the binary representation of a width-bit two's + complement numeral system of an integer n which may be negative. + """ + val = 2**(width-1) + if n >= 0: + if n > (val-1): + return None + # It is safe to represent n with width-bits. + return binary(n, width) + + if n < 0: + if abs(n) > val: + return None + # It is safe to represent n (a negative int) with width-bits. + return binary(val*2 - abs(n)) + +# print binary(0xABCD) +# [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1] +# print binary(0x1F, 8) +# [0, 0, 0, 1, 1, 1, 1, 1] +# print twos_complement(-5, 4) +# [1, 0, 1, 1] +# print twos_complement(7, 4) +# [0, 1, 1, 1] +# print binary(7) +# [1, 1, 1] +# print twos_complement(-5, 64) +# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1] + +def positions(width): + """Helper function returning a list describing the bit positions. + Bit positions greater than 99 are truncated to 2 digits, for example, + 100 -> 00 and 127 -> 27.""" + return ['{0:2}'.format(i)[-2:] for i in reversed(range(width))] + + +def utob(debugger, command_line, result, dict): + """Convert the unsigned integer to print its binary representation. + args[0] (mandatory) is the unsigned integer to be converted + args[1] (optional) is the bit width of the binary representation + args[2] (optional) if specified, turns on verbose printing""" + args = command_line.split() + try: + n = int(args[0], 0) + width = None + if len(args) > 1: + width = int(args[1], 0) + if width < 0: + width = 0 + except: + print utob.__doc__ + return + + if len(args) > 2: + verbose = True + else: + verbose = False + + bits = binary(n, width) + if not bits: + print "insufficient width value: %d" % width + return + if verbose and width > 0: + pos = positions(width) + print ' '+' '.join(pos) + print ' %s' % str(bits) + +def itob(debugger, command_line, result, dict): + """Convert the integer to print its two's complement representation. + args[0] (mandatory) is the integer to be converted + args[1] (mandatory) is the bit width of the two's complement representation + args[2] (optional) if specified, turns on verbose printing""" + args = command_line.split() + try: + n = int(args[0], 0) + width = int(args[1], 0) + if width < 0: + width = 0 + except: + print itob.__doc__ + return + + if len(args) > 2: + verbose = True + else: + verbose = False + + bits = twos_complement(n, width) + if not bits: + print "insufficient width value: %d" % width + return + if verbose and width > 0: + pos = positions(width) + print ' '+' '.join(pos) + print ' %s' % str(bits) + diff --git a/examples/customization/import-python/README b/examples/customization/import-python/README new file mode 100644 index 000000000000..9122f8f46dcf --- /dev/null +++ b/examples/customization/import-python/README @@ -0,0 +1,40 @@ +Files in this directory: + +o importcmd.py: + +Python module which provides implementation for the 'import' command. + +o README: + +The file you are reading now. + +================================================================================ +The import command defined by importcmd.py can be used in LLDB to load a Python +module given its full pathname. +The command works by extending Python's sys.path lookup to include the path to +the module to be imported when required, and then going through the language +ordinary 'import' mechanism. In this respect, modules imported from LLDB command +line should not be distinguishable from those imported using the script interpreter. +The following terminal output shows an interaction with lldb using this new command. + +Enrico-Granatas-MacBook-Pro:Debug enricogranata$ ./lldb +(lldb) script import importcmd +(lldb) command script add import -f importcmd.pyimport_cmd +(lldb) import ../demo.py +(lldb) script demo.test_function('hello world') +I am a Python function that says hello world +(lldb) quit +Enrico-Granatas-MacBook-Pro:Debug enricogranata$ + +Of course, the commands to import the importcmd.py module and to define the import +command, can be included in the .lldbinit file to make this feature available at +debugger startup + +WARNING: The import command defined by importcmd.py is now obsolete +In TOT LLDB, you can say: +(lldb) command script import ../demo.py +(lldb) script demo.test_function('hello world') +I am a Python function that says hello world +(lldb) quit + +using the native "command script import" command, which offers a superset of what the import command provided by importcmd.py does diff --git a/examples/customization/import-python/importcmd.py b/examples/customization/import-python/importcmd.py new file mode 100644 index 000000000000..576a642d5a01 --- /dev/null +++ b/examples/customization/import-python/importcmd.py @@ -0,0 +1,31 @@ +import sys,os,lldb +def check_has_dir_in_path(dirname): + return sys.path.__contains__(dirname); + +def ensure_has_dir_in_path(dirname): + dirname = os.path.abspath(dirname) + if not (check_has_dir_in_path(dirname)): + sys.path.append(dirname); + +def do_import(debugger,modname): + if (len(modname) > 4 and modname[-4:] == '.pyc'): + modname = modname[:-4] + if (len(modname) > 3 and modname[-3:] == '.py'): + modname = modname[:-3] + debugger.HandleCommand("script import " + modname) + +def pyimport_cmd(debugger, args, result, dict): + """Import a Python module given its full path""" + print 'WARNING: obsolete feature - use native command "command script import"' + if args == "": + return "no module path given"; + if not (os.sep in args): + modname = args + ensure_has_dir_in_path('.') + else: + endofdir = args.rfind(os.sep) + modname = args[endofdir+1:] + args = args[0:endofdir] + ensure_has_dir_in_path(args) + do_import(debugger,modname) + return None diff --git a/examples/customization/pwd-cd-and-system/.lldbinit b/examples/customization/pwd-cd-and-system/.lldbinit new file mode 100644 index 000000000000..f477b5797bec --- /dev/null +++ b/examples/customization/pwd-cd-and-system/.lldbinit @@ -0,0 +1,7 @@ +script import os, sys +# So that ~/utils.py takes precedence. +script sys.path[:0] = [os.path.expanduser('~')] +script import utils +command alias pwd script print os.getcwd() +command script add -f utils.chdir cd +command script add -f utils.system system diff --git a/examples/customization/pwd-cd-and-system/README b/examples/customization/pwd-cd-and-system/README new file mode 100644 index 000000000000..1b67d0b09c01 --- /dev/null +++ b/examples/customization/pwd-cd-and-system/README @@ -0,0 +1,41 @@ +Files in this directory: + +o .lldbinit: + +An example lldb init file that imports the utils.py module and adds the +following commands: 'pwd', 'cd', and 'system'. + +o utils.py: + +Python module which provides implementation for the 'cd' and 'system' commands. + +o README: + +The file you are reading now. + +================================================================================ +The following terminal output shows an interaction with lldb using the .lldbinit +and the utils.py files which are located in my HOME directory. The lldb init +file imports the utils Python module and adds the 'pwd', 'cd', and 'system' +commands. + +Johnnys-MacBook-Pro:multiple_threads johnny$ pwd +/Volumes/data/lldb/svn/trunk/test/functionalities/watchpoint/multiple_threads +Johnnys-MacBook-Pro:multiple_threads johnny$ lldb +(lldb) pwd +/Volumes/data/lldb/svn/trunk/test/functionalities/watchpoint/multiple_threads +(lldb) cd .. +Current working directory: /Volumes/data/lldb/svn/trunk/test/functionalities/watchpoint +(lldb) help system + +Execute the command (a string) in a subshell. +Syntax: system +(lldb) system ls -l +total 0 +drwxr-xr-x 7 johnny admin 238 Oct 11 17:24 hello_watchlocation +drwxr-xr-x 7 johnny admin 238 Oct 11 17:24 hello_watchpoint +drwxr-xr-x 7 johnny admin 238 Oct 11 17:24 multiple_threads +drwxr-xr-x 7 johnny admin 238 Oct 11 17:24 watchpoint_commands + +retcode: 0 +(lldb) diff --git a/examples/customization/pwd-cd-and-system/utils.py b/examples/customization/pwd-cd-and-system/utils.py new file mode 100644 index 000000000000..e975e8869773 --- /dev/null +++ b/examples/customization/pwd-cd-and-system/utils.py @@ -0,0 +1,49 @@ +"""Utility for changing directories and execution of commands in a subshell.""" + +import os, shlex, subprocess + +# Store the previous working directory for the 'cd -' command. +class Holder: + """Holds the _prev_dir_ class attribute for chdir() function.""" + _prev_dir_ = None + + @classmethod + def prev_dir(cls): + return cls._prev_dir_ + + @classmethod + def swap(cls, dir): + cls._prev_dir_ = dir + +def chdir(debugger, args, result, dict): + """Change the working directory, or cd to ${HOME}. + You can also issue 'cd -' to change to the previous working directory.""" + new_dir = args.strip() + if not new_dir: + new_dir = os.path.expanduser('~') + elif new_dir == '-': + if not Holder.prev_dir(): + # Bad directory, not changing. + print "bad directory, not changing" + return + else: + new_dir = Holder.prev_dir() + + Holder.swap(os.getcwd()) + os.chdir(new_dir) + print "Current working directory: %s" % os.getcwd() + +def system(debugger, command_line, result, dict): + """Execute the command (a string) in a subshell.""" + args = shlex.split(command_line) + process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, error = process.communicate() + retcode = process.poll() + if output and error: + print "stdout=>\n", output + print "stderr=>\n", error + elif output: + print output + elif error: + print error + print "retcode:", retcode diff --git a/examples/darwin/heap_find/heap.py b/examples/darwin/heap_find/heap.py new file mode 100644 index 000000000000..fb3394323890 --- /dev/null +++ b/examples/darwin/heap_find/heap.py @@ -0,0 +1,1244 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# This module is designed to live inside the "lldb" python package +# in the "lldb.macosx" package. To use this in the embedded python +# interpreter using "lldb" just import it: +# +# (lldb) script import lldb.macosx.heap +#---------------------------------------------------------------------- + +import lldb +import commands +import optparse +import os +import os.path +import re +import shlex +import string +import sys +import tempfile +import lldb.utils.symbolication + +g_libheap_dylib_dir = None +g_libheap_dylib_dict = dict() + +def get_iterate_memory_expr(options, process, user_init_code, user_return_code): + expr = ''' +typedef unsigned natural_t; +typedef uintptr_t vm_size_t; +typedef uintptr_t vm_address_t; +typedef natural_t task_t; +typedef int kern_return_t; +#define KERN_SUCCESS 0 +typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size); +'''; + if options.search_vm_regions: + expr += ''' +typedef int vm_prot_t; +typedef unsigned int vm_inherit_t; +typedef unsigned long long memory_object_offset_t; +typedef unsigned int boolean_t; +typedef int vm_behavior_t; +typedef uint32_t vm32_object_id_t; +typedef natural_t mach_msg_type_number_t; +typedef uint64_t mach_vm_address_t; +typedef uint64_t mach_vm_offset_t; +typedef uint64_t mach_vm_size_t; +typedef uint64_t vm_map_offset_t; +typedef uint64_t vm_map_address_t; +typedef uint64_t vm_map_size_t; +#define VM_PROT_NONE ((vm_prot_t) 0x00) +#define VM_PROT_READ ((vm_prot_t) 0x01) +#define VM_PROT_WRITE ((vm_prot_t) 0x02) +#define VM_PROT_EXECUTE ((vm_prot_t) 0x04) +typedef struct vm_region_submap_short_info_data_64_t { + vm_prot_t protection; + vm_prot_t max_protection; + vm_inherit_t inheritance; + memory_object_offset_t offset; // offset into object/map + unsigned int user_tag; // user tag on map entry + unsigned int ref_count; // obj/map mappers, etc + unsigned short shadow_depth; // only for obj + unsigned char external_pager; // only for obj + unsigned char share_mode; // see enumeration + boolean_t is_submap; // submap vs obj + vm_behavior_t behavior; // access behavior hint + vm32_object_id_t object_id; // obj/map name, not a handle + unsigned short user_wired_count; +} vm_region_submap_short_info_data_64_t; +#define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))'''; + if user_init_code: + expr += user_init_code; + expr += ''' +task_t task = (task_t)mach_task_self(); +mach_vm_address_t vm_region_base_addr; +mach_vm_size_t vm_region_size; +natural_t vm_region_depth; +vm_region_submap_short_info_data_64_t vm_region_info; +kern_return_t err; +for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size) +{ + mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + err = (kern_return_t)mach_vm_region_recurse (task, + &vm_region_base_addr, + &vm_region_size, + &vm_region_depth, + &vm_region_info, + &vm_region_info_size); + if (err) + break; + // Check all read + write regions. This will cover the thread stacks + // and any regions of memory like __DATA segments, that might contain + // data we are looking for + if (vm_region_info.protection & VM_PROT_WRITE && + vm_region_info.protection & VM_PROT_READ) + { + baton.callback (task, + &baton, + 64, + vm_region_base_addr, + vm_region_size); + } +}''' + else: + if options.search_stack: + expr += get_thread_stack_ranges_struct (process) + if options.search_segments: + expr += get_sections_ranges_struct (process) + if user_init_code: + expr += user_init_code + if options.search_heap: + expr += ''' +#define MALLOC_PTR_IN_USE_RANGE_TYPE 1 +typedef struct vm_range_t { + vm_address_t address; + vm_size_t size; +} vm_range_t; +typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory); +typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size); +typedef struct malloc_introspection_t { + kern_return_t (*enumerator)(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder); /* enumerates all the malloc pointers in use */ +} malloc_introspection_t; +typedef struct malloc_zone_t { + void *reserved1[12]; + struct malloc_introspection_t *introspect; +} malloc_zone_t; +memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t { + *local_memory = (void*) remote_address; + return KERN_SUCCESS; +}; +vm_address_t *zones = 0; +unsigned int num_zones = 0;task_t task = 0; +kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones); +if (KERN_SUCCESS == err) +{ + for (unsigned int i=0; iintrospect) + zone->introspect->enumerator (task, + &baton, + MALLOC_PTR_IN_USE_RANGE_TYPE, + (vm_address_t)zone, + task_peek, + [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void + { + range_callback_t callback = ((callback_baton_t *)baton)->callback; + for (unsigned i=0; i 0) { + range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE); + } +} +#endif''' + + if options.search_segments: + expr += ''' +#ifdef NUM_SEGMENTS +// Call the callback for all segments +for (uint32_t i=0; i 0 and var_addr <= match_addr and match_addr < (var_addr + byte_size): + if verbose: + print 'match' + return var + return None + +def find_frame_for_stack_address(process, addr): + closest_delta = sys.maxint + closest_frame = None + #print 'find_frame_for_stack_address(%#x)' % (addr) + for thread in process: + prev_sp = lldb.LLDB_INVALID_ADDRESS + for frame in thread: + cfa = frame.GetCFA() + #print 'frame #%u: cfa = %#x' % (frame.GetFrameID(), cfa) + if addr < cfa: + delta = cfa - addr + #print '%#x < %#x, delta = %i' % (addr, cfa, delta) + if delta < closest_delta: + #print 'closest' + closest_delta = delta + closest_frame = frame + # else: + # print 'delta >= closest_delta' + return closest_frame + +def type_flags_to_description(process, type_flags, ptr_addr, ptr_size, offset, match_addr): + show_offset = False + if type_flags == 0 or type_flags & 4: + type_str = 'free(%#x)' % (ptr_addr,) + elif type_flags & 2 or type_flags & 1: + type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr) + show_offset = True + elif type_flags & 8: + type_str = 'stack' + frame = find_frame_for_stack_address(process, match_addr) + if frame: + type_str += ' in frame #%u of thread #%u: tid %#x' % (frame.GetFrameID(), frame.GetThread().GetIndexID(), frame.GetThread().GetThreadID()) + variables = frame.GetVariables(True,True,True,True) + matching_var = None + for var in variables: + var_addr = var.GetLoadAddress() + if var_addr != lldb.LLDB_INVALID_ADDRESS: + #print 'variable "%s" @ %#x (%#x)' % (var.name, var.load_addr, match_addr) + if var_addr == match_addr: + matching_var = var + break + else: + byte_size = var.GetType().GetByteSize() + if byte_size > 0 and var_addr <= match_addr and match_addr < (var_addr + byte_size): + matching_var = var + break + if matching_var: + type_str += ' in variable at %#x:\n %s' % (matching_var.GetLoadAddress(), matching_var) + elif type_flags & 16: + type_str = 'stack (red zone)' + elif type_flags & 32: + sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset) + type_str = 'segment [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr) + elif type_flags & 64: + sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset) + type_str = 'vm_region [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr) + else: + type_str = '%#x' % (ptr_addr,) + show_offset = True + if show_offset and offset != 0: + type_str += ' + %-6u' % (offset,) + return type_str + +def dump_stack_history_entry(options, result, stack_history_entry, idx): + address = int(stack_history_entry.address) + if address: + type_flags = int(stack_history_entry.type_flags) + symbolicator = lldb.utils.symbolication.Symbolicator() + symbolicator.target = lldb.debugger.GetSelectedTarget() + type_str = type_flags_to_string(type_flags) + result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str)) + frame_idx = 0 + idx = 0 + pc = int(stack_history_entry.frames[idx]) + while pc != 0: + if pc >= 0x1000: + frames = symbolicator.symbolicate(pc) + if frames: + for frame in frames: + result.AppendMessage(' [%u] %s' % (frame_idx, frame)) + frame_idx += 1 + else: + result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc)) + frame_idx += 1 + idx = idx + 1 + pc = int(stack_history_entry.frames[idx]) + else: + pc = 0 + if idx >= options.max_frames: + result.AppendMessage('warning: the max number of stack frames (%u) was reached, use the "--max-frames=" option to see more frames' % (options.max_frames)) + + result.AppendMessage('') + +def dump_stack_history_entries(options, result, addr, history): + # malloc_stack_entry *get_stack_history_for_address (const void * addr) + expr_prefix = ''' +typedef int kern_return_t; +typedef struct $malloc_stack_entry { + uint64_t address; + uint64_t argument; + uint32_t type_flags; + uint32_t num_frames; + uint64_t frames[512]; + kern_return_t err; +} $malloc_stack_entry; +''' + single_expr = ''' +#define MAX_FRAMES %u +typedef unsigned task_t; +$malloc_stack_entry stack; +stack.address = 0x%x; +stack.type_flags = 2; +stack.num_frames = 0; +stack.frames[0] = 0; +uint32_t max_stack_frames = MAX_FRAMES; +stack.err = (kern_return_t)__mach_stack_logging_get_frames ( + (task_t)mach_task_self(), + stack.address, + &stack.frames[0], + max_stack_frames, + &stack.num_frames); +if (stack.num_frames < MAX_FRAMES) + stack.frames[stack.num_frames] = 0; +else + stack.frames[MAX_FRAMES-1] = 0; +stack''' % (options.max_frames, addr); + + history_expr = ''' +typedef int kern_return_t; +typedef unsigned task_t; +#define MAX_FRAMES %u +#define MAX_HISTORY %u +typedef struct mach_stack_logging_record_t { + uint32_t type_flags; + uint64_t stack_identifier; + uint64_t argument; + uint64_t address; +} mach_stack_logging_record_t; +typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *); +typedef struct malloc_stack_entry { + uint64_t address; + uint64_t argument; + uint32_t type_flags; + uint32_t num_frames; + uint64_t frames[MAX_FRAMES]; + kern_return_t frames_err; +} malloc_stack_entry; +typedef struct $malloc_stack_history { + task_t task; + unsigned idx; + malloc_stack_entry entries[MAX_HISTORY]; +} $malloc_stack_history; +$malloc_stack_history info = { (task_t)mach_task_self(), 0 }; +uint32_t max_stack_frames = MAX_FRAMES; +enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void { + $malloc_stack_history *info = ($malloc_stack_history *)baton; + if (info->idx < MAX_HISTORY) { + malloc_stack_entry *stack_entry = &(info->entries[info->idx]); + stack_entry->address = stack_record.address; + stack_entry->type_flags = stack_record.type_flags; + stack_entry->argument = stack_record.argument; + stack_entry->num_frames = 0; + stack_entry->frames[0] = 0; + stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack ( + info->task, + stack_record.stack_identifier, + stack_entry->frames, + (uint32_t)MAX_FRAMES, + &stack_entry->num_frames); + // Terminate the frames with zero if there is room + if (stack_entry->num_frames < MAX_FRAMES) + stack_entry->frames[stack_entry->num_frames] = 0; + } + ++info->idx; +}; +(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info); +info''' % (options.max_frames, options.max_history, addr); + + frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + if history: + expr = history_expr + else: + expr = single_expr + expr_options = lldb.SBExpressionOptions() + expr_options.SetIgnoreBreakpoints(True); + expr_options.SetTimeoutInMicroSeconds (5*1000*1000) # 5 second timeout + expr_options.SetTryAllThreads (True) + expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) + expr_options.SetPrefix(expr_prefix) + expr_sbvalue = frame.EvaluateExpression (expr, expr_options) + if options.verbose: + print "expression:" + print expr + print "expression result:" + print expr_sbvalue + if expr_sbvalue.error.Success(): + if history: + malloc_stack_history = lldb.value(expr_sbvalue) + num_stacks = int(malloc_stack_history.idx) + if num_stacks <= options.max_history: + i_max = num_stacks + else: + i_max = options.max_history + for i in range(i_max): + stack_history_entry = malloc_stack_history.entries[i] + dump_stack_history_entry(options, result, stack_history_entry, i) + if num_stacks > options.max_history: + result.AppendMessage('warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks' % (options.max_history, num_stacks)) + else: + stack_history_entry = lldb.value(expr_sbvalue) + dump_stack_history_entry(options, result, stack_history_entry, 0) + + else: + result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error)) + + +def display_match_results (process, result, options, arg_str_description, expr, print_no_matches, expr_prefix = None): + frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + if not frame: + result.AppendMessage('error: invalid frame') + return 0 + expr_options = lldb.SBExpressionOptions() + expr_options.SetIgnoreBreakpoints(True); + expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues); + expr_options.SetTimeoutInMicroSeconds (30*1000*1000) # 30 second timeout + expr_options.SetTryAllThreads (False) + expr_options.SetLanguage (lldb.eLanguageTypeObjC_plus_plus) + if expr_prefix: + expr_options.SetPrefix (expr_prefix) + expr_sbvalue = frame.EvaluateExpression (expr, expr_options) + if options.verbose: + print "expression:" + print expr + print "expression result:" + print expr_sbvalue + if expr_sbvalue.error.Success(): + match_value = lldb.value(expr_sbvalue) + i = 0 + match_idx = 0 + while 1: + print_entry = True + match_entry = match_value[i]; i += 1 + if i > options.max_matches: + result.AppendMessage('warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % (options.max_matches)) + break + malloc_addr = match_entry.addr.sbvalue.unsigned + if malloc_addr == 0: + break + malloc_size = int(match_entry.size) + offset = int(match_entry.offset) + + if options.offset >= 0 and options.offset != offset: + print_entry = False + else: + match_addr = malloc_addr + offset + type_flags = int(match_entry.type) + #result.AppendMessage (hex(malloc_addr + offset)) + if type_flags == 64: + search_stack_old = options.search_stack + search_segments_old = options.search_segments + search_heap_old = options.search_heap + search_vm_regions = options.search_vm_regions + options.search_stack = True + options.search_segments = True + options.search_heap = True + options.search_vm_regions = False + if malloc_info_impl (lldb.debugger, result, options, [hex(malloc_addr + offset)]): + print_entry = False + options.search_stack = search_stack_old + options.search_segments = search_segments_old + options.search_heap = search_heap_old + options.search_vm_regions = search_vm_regions + if print_entry: + description = '%#16.16x: %s' % (match_addr, type_flags_to_description(process, type_flags, malloc_addr, malloc_size, offset, match_addr)) + if options.show_size: + description += ' <%5u>' % (malloc_size) + if options.show_range: + description += ' [%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size) + derefed_dynamic_value = None + dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget) + if dynamic_value.type.name == 'void *': + if options.type == 'pointer' and malloc_size == 4096: + error = lldb.SBError() + process = expr_sbvalue.GetProcess() + target = expr_sbvalue.GetTarget() + data = bytearray(process.ReadMemory(malloc_addr, 16, error)) + if data == '\xa1\xa1\xa1\xa1AUTORELEASE!': + ptr_size = target.addr_size + thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error) + # 4 bytes 0xa1a1a1a1 + # 12 bytes 'AUTORELEASE!' + # ptr bytes autorelease insertion point + # ptr bytes pthread_t + # ptr bytes next colder page + # ptr bytes next hotter page + # 4 bytes this page's depth in the list + # 4 bytes high-water mark + description += ' AUTORELEASE! for pthread_t %#x' % (thread) + # else: + # description += 'malloc(%u)' % (malloc_size) + # else: + # description += 'malloc(%u)' % (malloc_size) + else: + derefed_dynamic_value = dynamic_value.deref + if derefed_dynamic_value: + derefed_dynamic_type = derefed_dynamic_value.type + derefed_dynamic_type_size = derefed_dynamic_type.size + derefed_dynamic_type_name = derefed_dynamic_type.name + description += ' ' + description += derefed_dynamic_type_name + if offset < derefed_dynamic_type_size: + member_list = list(); + get_member_types_for_offset (derefed_dynamic_type, offset, member_list) + if member_list: + member_path = '' + for member in member_list: + member_name = member.name + if member_name: + if member_path: + member_path += '.' + member_path += member_name + if member_path: + if options.ivar_regex_blacklist: + for ivar_regex in options.ivar_regex_blacklist: + if ivar_regex.match(member_path): + print_entry = False + description += '.%s' % (member_path) + else: + description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name) + else: + # strip the "*" from the end of the name since we were unable to dereference this + description += dynamic_value.type.name[0:-1] + if print_entry: + match_idx += 1 + result_output = '' + if description: + result_output += description + if options.print_type and derefed_dynamic_value: + result_output += ' %s' % (derefed_dynamic_value) + if options.print_object_description and dynamic_value: + desc = dynamic_value.GetObjectDescription() + if desc: + result_output += '\n%s' % (desc) + if result_output: + result.AppendMessage(result_output) + if options.memory: + cmd_result = lldb.SBCommandReturnObject() + if options.format == None: + memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size) + else: + memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size) + if options.verbose: + result.AppendMessage(memory_command) + lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result) + result.AppendMessage(cmd_result.GetOutput()) + if options.stack_history: + dump_stack_history_entries(options, result, malloc_addr, 1) + elif options.stack: + dump_stack_history_entries(options, result, malloc_addr, 0) + return i + else: + result.AppendMessage(str(expr_sbvalue.error)) + return 0 + +def get_ptr_refs_options (): + usage = "usage: %prog [options] [EXPR ...]" + description='''Searches all allocations on the heap for pointer values on +darwin user space programs. Any matches that were found will dump the malloc +blocks that contain the pointers and might be able to print what kind of +objects the pointers are contained in using dynamic type information in the +program.''' + parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage) + add_common_options(parser) + return parser + +def find_variable(debugger, command, result, dict): + usage = "usage: %prog [options] [ADDR ...]" + description='''Searches for a local variable in all frames that contains a hex ADDR.''' + command_args = shlex.split(command) + parser = optparse.OptionParser(description=description, prog='find_variable',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + process = debugger.GetSelectedTarget().GetProcess() + if not process: + result.AppendMessage('error: invalid process') + return + + for arg in args: + var_addr = int(arg, 16) + print >>result, "Finding a variable with address %#x..." % (var_addr) + done = False + for thread in process: + for frame in thread: + var = find_variable_containing_address(options.verbose, frame, var_addr) + if var: + print var + done = True + break + if done: + break + +def ptr_refs(debugger, command, result, dict): + command_args = shlex.split(command) + parser = get_ptr_refs_options() + try: + (options, args) = parser.parse_args(command_args) + except: + return + + process = debugger.GetSelectedTarget().GetProcess() + if not process: + result.AppendMessage('error: invalid process') + return + frame = process.GetSelectedThread().GetSelectedFrame() + if not frame: + result.AppendMessage('error: invalid frame') + return + + options.type = 'pointer' + if options.format == None: + options.format = "A" # 'A' is "address" format + + if args: + # When we initialize the expression, we must define any types that + # we will need when looking at every allocation. We must also define + # a type named callback_baton_t and make an instance named "baton" + # and initialize it how ever we want to. The address of "baton" will + # be passed into our range callback. callback_baton_t must contain + # a member named "callback" whose type is "range_callback_t". This + # will be used by our zone callbacks to call the range callback for + # each malloc range. + expr_prefix = ''' +struct $malloc_match { + void *addr; + uintptr_t size; + uintptr_t offset; + uintptr_t type; +}; +''' + user_init_code_format = ''' +#define MAX_MATCHES %u +typedef struct callback_baton_t { + range_callback_t callback; + unsigned num_matches; + $malloc_match matches[MAX_MATCHES]; + void *ptr; +} callback_baton_t; +range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { + callback_baton_t *info = (callback_baton_t *)baton; + typedef void* T; + const unsigned size = sizeof(T); + T *array = (T*)ptr_addr; + for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) { + if (array[idx] == info->ptr) { + if (info->num_matches < MAX_MATCHES) { + info->matches[info->num_matches].addr = (void*)ptr_addr; + info->matches[info->num_matches].size = ptr_size; + info->matches[info->num_matches].offset = idx*sizeof(T); + info->matches[info->num_matches].type = type; + ++info->num_matches; + } + } + } +}; +callback_baton_t baton = { range_callback, 0, {0}, (void *)%s }; +''' + # We must also define a snippet of code to be run that returns + # the result of the expression we run. + # Here we return NULL if our pointer was not found in any malloc blocks, + # and we return the address of the matches array so we can then access + # the matching results + user_return_code = '''if (baton.num_matches < MAX_MATCHES) + baton.matches[baton.num_matches].addr = 0; // Terminate the matches array +baton.matches''' + # Iterate through all of our pointer expressions and display the results + for ptr_expr in args: + user_init_code = user_init_code_format % (options.max_matches, ptr_expr) + expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code) + arg_str_description = 'malloc block containing pointer %s' % ptr_expr + display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix) + else: + result.AppendMessage('error: no pointer arguments were given') + +def get_cstr_refs_options(): + usage = "usage: %prog [options] [CSTR ...]" + description='''Searches all allocations on the heap for C string values on +darwin user space programs. Any matches that were found will dump the malloc +blocks that contain the C strings and might be able to print what kind of +objects the pointers are contained in using dynamic type information in the +program.''' + parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage) + add_common_options(parser) + return parser + +def cstr_refs(debugger, command, result, dict): + command_args = shlex.split(command) + parser = get_cstr_refs_options(); + try: + (options, args) = parser.parse_args(command_args) + except: + return + + process = debugger.GetSelectedTarget().GetProcess() + if not process: + result.AppendMessage('error: invalid process') + return + frame = process.GetSelectedThread().GetSelectedFrame() + if not frame: + result.AppendMessage('error: invalid frame') + return + + + options.type = 'cstr' + if options.format == None: + options.format = "Y" # 'Y' is "bytes with ASCII" format + + if args: + # When we initialize the expression, we must define any types that + # we will need when looking at every allocation. We must also define + # a type named callback_baton_t and make an instance named "baton" + # and initialize it how ever we want to. The address of "baton" will + # be passed into our range callback. callback_baton_t must contain + # a member named "callback" whose type is "range_callback_t". This + # will be used by our zone callbacks to call the range callback for + # each malloc range. + expr_prefix = ''' +struct $malloc_match { + void *addr; + uintptr_t size; + uintptr_t offset; + uintptr_t type; +}; +''' + user_init_code_format = ''' +#define MAX_MATCHES %u +typedef struct callback_baton_t { + range_callback_t callback; + unsigned num_matches; + $malloc_match matches[MAX_MATCHES]; + const char *cstr; + unsigned cstr_len; +} callback_baton_t; +range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { + callback_baton_t *info = (callback_baton_t *)baton; + if (info->cstr_len < ptr_size) { + const char *begin = (const char *)ptr_addr; + const char *end = begin + ptr_size - info->cstr_len; + for (const char *s = begin; s < end; ++s) { + if ((int)memcmp(s, info->cstr, info->cstr_len) == 0) { + if (info->num_matches < MAX_MATCHES) { + info->matches[info->num_matches].addr = (void*)ptr_addr; + info->matches[info->num_matches].size = ptr_size; + info->matches[info->num_matches].offset = s - begin; + info->matches[info->num_matches].type = type; + ++info->num_matches; + } + } + } + } +}; +const char *cstr = "%s"; +callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };''' + # We must also define a snippet of code to be run that returns + # the result of the expression we run. + # Here we return NULL if our pointer was not found in any malloc blocks, + # and we return the address of the matches array so we can then access + # the matching results + user_return_code = '''if (baton.num_matches < MAX_MATCHES) + baton.matches[baton.num_matches].addr = 0; // Terminate the matches array +baton.matches''' + # Iterate through all of our pointer expressions and display the results + for cstr in args: + user_init_code = user_init_code_format % (options.max_matches, cstr) + expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code) + arg_str_description = 'malloc block containing "%s"' % cstr + display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix) + else: + result.AppendMessage('error: command takes one or more C string arguments') + + +def get_malloc_info_options(): + usage = "usage: %prog [options] [EXPR ...]" + description='''Searches the heap a malloc block that contains the addresses +specified as one or more address expressions. Any matches that were found will +dump the malloc blocks that match or contain the specified address. The matching +blocks might be able to show what kind of objects they are using dynamic type +information in the program.''' + parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage) + add_common_options(parser) + return parser + +def malloc_info(debugger, command, result, dict): + command_args = shlex.split(command) + parser = get_malloc_info_options() + try: + (options, args) = parser.parse_args(command_args) + except: + return + malloc_info_impl (debugger, result, options, args) + +def malloc_info_impl (debugger, result, options, args): + # We are specifically looking for something on the heap only + options.type = 'malloc_info' + + process = debugger.GetSelectedTarget().GetProcess() + if not process: + result.AppendMessage('error: invalid process') + return + frame = process.GetSelectedThread().GetSelectedFrame() + if not frame: + result.AppendMessage('error: invalid frame') + return + expr_prefix = ''' +struct $malloc_match { + void *addr; + uintptr_t size; + uintptr_t offset; + uintptr_t type; +}; +''' + + user_init_code_format = ''' +typedef struct callback_baton_t { + range_callback_t callback; + unsigned num_matches; + $malloc_match matches[2]; // Two items so they can be NULL terminated + void *ptr; +} callback_baton_t; +range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { + callback_baton_t *info = (callback_baton_t *)baton; + if (info->num_matches == 0) { + uint8_t *p = (uint8_t *)info->ptr; + uint8_t *lo = (uint8_t *)ptr_addr; + uint8_t *hi = lo + ptr_size; + if (lo <= p && p < hi) { + info->matches[info->num_matches].addr = (void*)ptr_addr; + info->matches[info->num_matches].size = ptr_size; + info->matches[info->num_matches].offset = p - lo; + info->matches[info->num_matches].type = type; + info->num_matches = 1; + } + } +}; +callback_baton_t baton = { range_callback, 0, {0}, (void *)%s }; +baton.matches[0].addr = 0; +baton.matches[1].addr = 0;''' + if args: + total_matches = 0 + for ptr_expr in args: + user_init_code = user_init_code_format % (ptr_expr) + expr = get_iterate_memory_expr(options, process, user_init_code, 'baton.matches') + arg_str_description = 'malloc block that contains %s' % ptr_expr + total_matches += display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix) + return total_matches + else: + result.AppendMessage('error: command takes one or more pointer expressions') + return 0 + +def get_thread_stack_ranges_struct (process): + '''Create code that defines a structure that represents threads stack bounds + for all threads. It returns a static sized array initialized with all of + the tid, base, size structs for all the threads.''' + stack_dicts = list() + if process: + i = 0; + for thread in process: + min_sp = thread.frame[0].sp + max_sp = min_sp + for frame in thread.frames: + sp = frame.sp + if sp < min_sp: min_sp = sp + if sp > max_sp: max_sp = sp + if min_sp < max_sp: + stack_dicts.append ({ 'tid' : thread.GetThreadID(), 'base' : min_sp , 'size' : max_sp-min_sp, 'index' : i }) + i += 1 + stack_dicts_len = len(stack_dicts) + if stack_dicts_len > 0: + result = ''' +#define NUM_STACKS %u +#define STACK_RED_ZONE_SIZE %u +typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t; +thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize()) + for stack_dict in stack_dicts: + result += ''' +stacks[%(index)u].tid = 0x%(tid)x; +stacks[%(index)u].base = 0x%(base)x; +stacks[%(index)u].size = 0x%(size)x;''' % stack_dict + return result + else: + return '' + +def get_sections_ranges_struct (process): + '''Create code that defines a structure that represents all segments that + can contain data for all images in "target". It returns a static sized + array initialized with all of base, size structs for all the threads.''' + target = process.target + segment_dicts = list() + for (module_idx, module) in enumerate(target.modules): + for sect_idx in range(module.GetNumSections()): + section = module.GetSectionAtIndex(sect_idx) + if not section: + break + name = section.name + if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO': + base = section.GetLoadAddress(target) + size = section.GetByteSize() + if base != lldb.LLDB_INVALID_ADDRESS and size > 0: + segment_dicts.append ({ 'base' : base, 'size' : size }) + segment_dicts_len = len(segment_dicts) + if segment_dicts_len > 0: + result = ''' +#define NUM_SEGMENTS %u +typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t; +segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,) + for (idx, segment_dict) in enumerate(segment_dicts): + segment_dict['index'] = idx + result += ''' +segments[%(index)u].base = 0x%(base)x; +segments[%(index)u].size = 0x%(size)x;''' % segment_dict + return result + else: + return '' + +def section_ptr_refs(debugger, command, result, dict): + command_args = shlex.split(command) + usage = "usage: %prog [options] [EXPR ...]" + description='''Searches section contents for pointer values in darwin user space programs.''' + parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage) + add_common_options(parser) + parser.add_option('--section', action='append', type='string', dest='section_names', help='section name to search', default=list()) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + options.type = 'pointer' + + sections = list() + section_modules = list() + if not options.section_names: + result.AppendMessage('error: at least one section must be specified with the --section option') + return + + target = debugger.GetSelectedTarget() + for module in target.modules: + for section_name in options.section_names: + section = module.section[section_name] + if section: + sections.append (section) + section_modules.append (module) + if sections: + dylid_load_err = load_dylib() + if dylid_load_err: + result.AppendMessage(dylid_load_err) + return + frame = target.GetProcess().GetSelectedThread().GetSelectedFrame() + for expr_str in args: + for (idx, section) in enumerate(sections): + expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (section.addr.load_addr, section.size, expr_str) + arg_str_description = 'section %s.%s containing "%s"' % (section_modules[idx].file.fullpath, section.name, expr_str) + num_matches = display_match_results (target.GetProcess(), result, options, arg_str_description, expr, False) + if num_matches: + if num_matches < options.max_matches: + options.max_matches = options.max_matches - num_matches + else: + options.max_matches = 0 + if options.max_matches == 0: + return + else: + result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names))) + +def get_objc_refs_options(): + usage = "usage: %prog [options] [CLASS ...]" + description='''Searches all allocations on the heap for instances of +objective C classes, or any classes that inherit from the specified classes +in darwin user space programs. Any matches that were found will dump the malloc +blocks that contain the C strings and might be able to print what kind of +objects the pointers are contained in using dynamic type information in the +program.''' + parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage) + add_common_options(parser) + return parser + +def objc_refs(debugger, command, result, dict): + command_args = shlex.split(command) + parser = get_objc_refs_options() + try: + (options, args) = parser.parse_args(command_args) + except: + return + + process = debugger.GetSelectedTarget().GetProcess() + if not process: + result.AppendMessage('error: invalid process') + return + frame = process.GetSelectedThread().GetSelectedFrame() + if not frame: + result.AppendMessage('error: invalid frame') + return + + options.type = 'isa' + if options.format == None: + options.format = "A" # 'A' is "address" format + + expr_options = lldb.SBExpressionOptions() + expr_options.SetIgnoreBreakpoints(True); + expr_options.SetTimeoutInMicroSeconds (3*1000*1000) # 3 second infinite timeout + expr_options.SetTryAllThreads (True) + expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) + num_objc_classes_value = frame.EvaluateExpression("(int)objc_getClassList((void *)0, (int)0)", expr_options) + if not num_objc_classes_value.error.Success(): + result.AppendMessage('error: %s' % num_objc_classes_value.error.GetCString()) + return + + num_objc_classes = num_objc_classes_value.GetValueAsUnsigned() + if num_objc_classes == 0: + result.AppendMessage('error: no objective C classes in program') + return + + if args: + # When we initialize the expression, we must define any types that + # we will need when looking at every allocation. We must also define + # a type named callback_baton_t and make an instance named "baton" + # and initialize it how ever we want to. The address of "baton" will + # be passed into our range callback. callback_baton_t must contain + # a member named "callback" whose type is "range_callback_t". This + # will be used by our zone callbacks to call the range callback for + # each malloc range. + expr_prefix = ''' +struct $malloc_match { + void *addr; + uintptr_t size; + uintptr_t offset; + uintptr_t type; +}; +''' + + user_init_code_format = ''' +#define MAX_MATCHES %u +typedef int (*compare_callback_t)(const void *a, const void *b); +typedef struct callback_baton_t { + range_callback_t callback; + compare_callback_t compare_callback; + unsigned num_matches; + $malloc_match matches[MAX_MATCHES]; + void *isa; + Class classes[%u]; +} callback_baton_t; +compare_callback_t compare_callback = [](const void *a, const void *b) -> int { + Class a_ptr = *(Class *)a; + Class b_ptr = *(Class *)b; + if (a_ptr < b_ptr) return -1; + if (a_ptr > b_ptr) return +1; + return 0; +}; +typedef Class (*class_getSuperclass_type)(void *isa); +range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { + class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass; + callback_baton_t *info = (callback_baton_t *)baton; + if (sizeof(Class) <= ptr_size) { + Class *curr_class_ptr = (Class *)ptr_addr; + Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr, + (const void *)info->classes, + sizeof(info->classes)/sizeof(Class), + sizeof(Class), + info->compare_callback); + if (matching_class_ptr) { + bool match = false; + if (info->isa) { + Class isa = *curr_class_ptr; + if (info->isa == isa) + match = true; + else { // if (info->objc.match_superclasses) { + Class super = class_getSuperclass_impl(isa); + while (super) { + if (super == info->isa) { + match = true; + break; + } + super = class_getSuperclass_impl(super); + } + } + } + else + match = true; + if (match) { + if (info->num_matches < MAX_MATCHES) { + info->matches[info->num_matches].addr = (void*)ptr_addr; + info->matches[info->num_matches].size = ptr_size; + info->matches[info->num_matches].offset = 0; + info->matches[info->num_matches].type = type; + ++info->num_matches; + } + } + } + } +}; +callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} }; +int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class)); +(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);''' + # We must also define a snippet of code to be run that returns + # the result of the expression we run. + # Here we return NULL if our pointer was not found in any malloc blocks, + # and we return the address of the matches array so we can then access + # the matching results + user_return_code = '''if (baton.num_matches < MAX_MATCHES) + baton.matches[baton.num_matches].addr = 0; // Terminate the matches array + baton.matches''' + # Iterate through all of our ObjC class name arguments + for class_name in args: + addr_expr_str = "(void *)[%s class]" % class_name + expr_options = lldb.SBExpressionOptions() + expr_options.SetIgnoreBreakpoints(True); + expr_options.SetTimeoutInMicroSeconds (1*1000*1000) # 1 second timeout + expr_options.SetTryAllThreads (True) + expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) + expr_sbvalue = frame.EvaluateExpression (addr_expr_str, expr_options) + if expr_sbvalue.error.Success(): + isa = expr_sbvalue.unsigned + if isa: + options.type = 'isa' + result.AppendMessage('Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % (class_name, isa)) + user_init_code = user_init_code_format % (options.max_matches, num_objc_classes, isa) + expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code) + arg_str_description = 'objective C classes with isa 0x%x' % isa + display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix) + else: + result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name)) + else: + result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error)) + else: + result.AppendMessage('error: command takes one or more C string arguments'); + +if __name__ == '__main__': + lldb.debugger = lldb.SBDebugger.Create() + +# Make the options so we can generate the help text for the new LLDB +# command line command prior to registering it with LLDB below. This way +# if clients in LLDB type "help malloc_info", they will see the exact same +# output as typing "malloc_info --help". +ptr_refs.__doc__ = get_ptr_refs_options().format_help() +cstr_refs.__doc__ = get_cstr_refs_options().format_help() +malloc_info.__doc__ = get_malloc_info_options().format_help() +objc_refs.__doc__ = get_objc_refs_options().format_help() +lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % __name__) +lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % __name__) +lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % __name__) +lldb.debugger.HandleCommand('command script add -f %s.find_variable find_variable' % __name__) +# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name) +# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name) +# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name) +lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % __name__) +print '"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.' + + + + diff --git a/examples/darwin/heap_find/heap/Makefile b/examples/darwin/heap_find/heap/Makefile new file mode 100644 index 000000000000..0e33dc9f8936 --- /dev/null +++ b/examples/darwin/heap_find/heap/Makefile @@ -0,0 +1,33 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +ARCH ?= x86_64 +CFLAGS ?=-arch $(ARCH) -gdwarf-2 -O0 +CXX ?= $(shell xcrun -find clang++) +EXE ?= libheap.dylib +DSYM ?= $(EXE).dSYM + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : heap_find.cpp + $(CXX) $(CFLAGS) -install_name "@executable_path/libheap.dylib" -dynamiclib -lobjc heap_find.cpp -o "$(EXE)" + +#---------------------------------------------------------------------- +# 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. +#---------------------------------------------------------------------- +.PHONY: clean +all: $(EXE) +clean: + rm -rf "$(EXE)" "$(DSYM)" + + + diff --git a/examples/darwin/heap_find/heap/heap_find.cpp b/examples/darwin/heap_find/heap/heap_find.cpp new file mode 100644 index 000000000000..de896775e402 --- /dev/null +++ b/examples/darwin/heap_find/heap/heap_find.cpp @@ -0,0 +1,1071 @@ +//===-- heap_find.c ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file compiles into a dylib and can be used on darwin to find data that +// is contained in active malloc blocks. To use this make the project, then +// load the shared library in a debug session while you are stopped: +// +// (lldb) process load /path/to/libheap.dylib +// +// Now you can use the "find_pointer_in_heap" and "find_cstring_in_heap" +// functions in the expression parser. +// +// This will grep everything in all active allocation blocks and print and +// malloc blocks that contain the pointer 0x112233000000: +// +// (lldb) expression find_pointer_in_heap (0x112233000000) +// +// This will grep everything in all active allocation blocks and print and +// malloc blocks that contain the C string "hello" (as a substring, no +// NULL termination included): +// +// (lldb) expression find_cstring_in_heap ("hello") +// +// The results will be printed to the STDOUT of the inferior program. The +// return value of the "find_pointer_in_heap" function is the number of +// pointer references that were found. A quick example shows +// +// (lldb) expr find_pointer_in_heap(0x0000000104000410) +// (uint32_t) $5 = 0x00000002 +// 0x104000740: 0x0000000104000410 found in malloc block 0x104000730 + 16 (malloc_size = 48) +// 0x100820060: 0x0000000104000410 found in malloc block 0x100820000 + 96 (malloc_size = 4096) +// +// From the above output we see that 0x104000410 was found in the malloc block +// at 0x104000730 and 0x100820000. If we want to see what these blocks are, we +// can display the memory for this block using the "address" ("A" for short) +// format. The address format shows pointers, and if those pointers point to +// objects that have symbols or know data contents, it will display information +// about the pointers: +// +// (lldb) memory read --format address --count 1 0x104000730 +// 0x104000730: 0x0000000100002460 (void *)0x0000000100002488: MyString +// +// We can see that the first block is a "MyString" object that contains our +// pointer value at offset 16. +// +// Looking at the next pointers, are a bit more tricky: +// (lldb) memory read -fA 0x100820000 -c1 +// 0x100820000: 0x4f545541a1a1a1a1 +// (lldb) memory read 0x100820000 +// 0x100820000: a1 a1 a1 a1 41 55 54 4f 52 45 4c 45 41 53 45 21 ....AUTORELEASE! +// 0x100820010: 78 00 82 00 01 00 00 00 60 f9 e8 75 ff 7f 00 00 x.......`..u.... +// +// This is an objective C auto release pool object that contains our pointer. +// C++ classes will show up if they are virtual as something like: +// (lldb) memory read --format address --count 1 0x104008000 +// 0x104008000: 0x109008000 vtable for lldb_private::Process +// +// This is a clue that the 0x104008000 is a "lldb_private::Process *". +//===----------------------------------------------------------------------===// +// C includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C++ includes +#include + +//---------------------------------------------------------------------- +// Redefine private types from "/usr/local/include/stack_logging.h" +//---------------------------------------------------------------------- +typedef struct { + uint32_t type_flags; + uint64_t stack_identifier; + uint64_t argument; + mach_vm_address_t address; +} mach_stack_logging_record_t; + +//---------------------------------------------------------------------- +// Redefine private defines from "/usr/local/include/stack_logging.h" +//---------------------------------------------------------------------- +#define stack_logging_type_free 0 +#define stack_logging_type_generic 1 +#define stack_logging_type_alloc 2 +#define stack_logging_type_dealloc 4 +// This bit is made up by this code +#define stack_logging_type_vm_region 8 + +//---------------------------------------------------------------------- +// Redefine private function prototypes from +// "/usr/local/include/stack_logging.h" +//---------------------------------------------------------------------- +extern "C" kern_return_t +__mach_stack_logging_set_file_path ( + task_t task, + char* file_path +); + +extern "C" kern_return_t +__mach_stack_logging_get_frames ( + task_t task, + mach_vm_address_t address, + mach_vm_address_t *stack_frames_buffer, + uint32_t max_stack_frames, + uint32_t *count +); + +extern "C" kern_return_t +__mach_stack_logging_enumerate_records ( + task_t task, + mach_vm_address_t address, + void enumerator(mach_stack_logging_record_t, void *), + void *context +); + +extern "C" kern_return_t +__mach_stack_logging_frames_for_uniqued_stack ( + task_t task, + uint64_t stack_identifier, + mach_vm_address_t *stack_frames_buffer, + uint32_t max_stack_frames, + uint32_t *count +); + +extern "C" void *gdb_class_getClass (void *objc_class); + +static void +range_info_callback (task_t task, + void *baton, + unsigned type, + uint64_t ptr_addr, + uint64_t ptr_size); + +//---------------------------------------------------------------------- +// Redefine private global variables prototypes from +// "/usr/local/include/stack_logging.h" +//---------------------------------------------------------------------- + +extern "C" int stack_logging_enable_logging; + +//---------------------------------------------------------------------- +// Local defines +//---------------------------------------------------------------------- +#define MAX_FRAMES 1024 + +//---------------------------------------------------------------------- +// Local Typedefs and Types +//---------------------------------------------------------------------- +typedef void range_callback_t (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size); +typedef void zone_callback_t (void *info, const malloc_zone_t *zone); +typedef int (*comare_function_t)(const void *, const void *); +struct range_callback_info_t +{ + zone_callback_t *zone_callback; + range_callback_t *range_callback; + void *baton; + int check_vm_regions; +}; + +enum data_type_t +{ + eDataTypeAddress, + eDataTypeContainsData, + eDataTypeObjC, + eDataTypeHeapInfo +}; + +struct aligned_data_t +{ + const uint8_t *buffer; + uint32_t size; + uint32_t align; +}; + +struct objc_data_t +{ + void *match_isa; // Set to NULL for all objective C objects + bool match_superclasses; +}; + +struct range_contains_data_callback_info_t +{ + data_type_t type; + const void *lookup_addr; + union + { + uintptr_t addr; + aligned_data_t data; + objc_data_t objc; + }; + uint32_t match_count; + bool done; + bool unique; +}; + +struct malloc_match +{ + void *addr; + intptr_t size; + intptr_t offset; + uintptr_t type; +}; + +struct malloc_stack_entry +{ + const void *address; + uint64_t argument; + uint32_t type_flags; + uint32_t num_frames; + mach_vm_address_t frames[MAX_FRAMES]; +}; + +struct malloc_block_contents +{ + union { + Class isa; + void *pointers[2]; + }; +}; + +static int +compare_void_ptr (const void *a, const void *b) +{ + Class a_ptr = *(Class *)a; + Class b_ptr = *(Class *)b; + if (a_ptr < b_ptr) return -1; + if (a_ptr > b_ptr) return +1; + return 0; +} + +class MatchResults +{ + enum { + k_max_entries = 8 * 1024 + }; +public: + MatchResults () : + m_size(0) + { + } + + void + clear() + { + m_size = 0; + bzero (&m_entries, sizeof(m_entries)); + } + + bool + empty() const + { + return m_size == 0; + } + + void + push_back (const malloc_match& m, bool unique = false) + { + if (unique) + { + // Don't add the entry if there is already a match for this address + for (uint32_t i=0; i 0) + { + const int k_page_size = getpagesize(); + const mach_vm_size_t vm_size = ((n_bytes + k_page_size - 1)/k_page_size) * k_page_size; + vm_address_t address = 0; + kern_return_t kerr = vm_allocate (mach_task_self(), &address, vm_size, true); + if (kerr == KERN_SUCCESS) + return (void *)address; + } + return NULL; +} + + +//---------------------------------------------------------------------- +// ObjCClasses +//---------------------------------------------------------------------- +class ObjCClasses +{ +public: + ObjCClasses() : + m_objc_class_ptrs (NULL), + m_size (0) + { + } + + bool + Update() + { + // TODO: find out if class list has changed and update if needed + if (m_objc_class_ptrs == NULL) + { + m_size = objc_getClassList(NULL, 0); + if (m_size > 0) + { + // Allocate the class pointers + m_objc_class_ptrs = (Class *)safe_malloc (m_size * sizeof(Class)); + m_size = objc_getClassList(m_objc_class_ptrs, m_size); + // Sort Class pointers for quick lookup + ::qsort (m_objc_class_ptrs, m_size, sizeof(Class), compare_void_ptr); + } + else + return false; + } + return true; + } + + uint32_t + FindClassIndex (Class isa) + { + Class *matching_class = (Class *)bsearch (&isa, + m_objc_class_ptrs, + m_size, + sizeof(Class), + compare_void_ptr); + if (matching_class) + { + uint32_t idx = matching_class - m_objc_class_ptrs; + return idx; + } + return UINT32_MAX; + } + + Class + GetClassAtIndex (uint32_t idx) const + { + if (idx < m_size) + return m_objc_class_ptrs[idx]; + return NULL; + } + uint32_t + GetSize() const + { + return m_size; + } +private: + Class *m_objc_class_ptrs; + uint32_t m_size; +}; + + + +//---------------------------------------------------------------------- +// Local global variables +//---------------------------------------------------------------------- +MatchResults g_matches; +MallocStackLoggingEntries g_malloc_stack_history; +ObjCClasses g_objc_classes; + +//---------------------------------------------------------------------- +// ObjCClassInfo +//---------------------------------------------------------------------- + +enum HeapInfoSortType +{ + eSortTypeNone, + eSortTypeBytes, + eSortTypeCount +}; + +class ObjCClassInfo +{ +public: + ObjCClassInfo() : + m_entries (NULL), + m_size (0), + m_sort_type (eSortTypeNone) + { + } + + void + Update (const ObjCClasses &objc_classes) + { + m_size = objc_classes.GetSize(); + m_entries = (Entry *)safe_malloc (m_size * sizeof(Entry)); + m_sort_type = eSortTypeNone; + Reset (); + } + + bool + AddInstance (uint32_t idx, uint64_t ptr_size) + { + if (m_size == 0) + Update (g_objc_classes); + // Update the totals for the classes + if (idx < m_size) + { + m_entries[idx].bytes += ptr_size; + ++m_entries[idx].count; + return true; + } + return false; + } + + void + Reset () + { + m_sort_type = eSortTypeNone; + for (uint32_t i=0; i 0) + { + ::qsort (m_entries, m_size, sizeof(Entry), (comare_function_t)compare_bytes); + m_sort_type = eSortTypeBytes; + } + if (print && m_size > 0) + { + puts("Objective C objects by total bytes:"); + puts("Total Bytes Class Name"); + puts("----------- -----------------------------------------------------------------"); + for (uint32_t i=0; i 0; ++i) + { + printf ("%11llu %s\n", m_entries[i].bytes, class_getName (objc_classes.GetClassAtIndex(m_entries[i].idx))); + } + } + } + void + SortByTotalCount (const ObjCClasses &objc_classes, bool print) + { + if (m_sort_type != eSortTypeCount && m_size > 0) + { + ::qsort (m_entries, m_size, sizeof(Entry), (comare_function_t)compare_count); + m_sort_type = eSortTypeCount; + } + if (print && m_size > 0) + { + puts("Objective C objects by total count:"); + puts("Count Class Name"); + puts("-------- -----------------------------------------------------------------"); + for (uint32_t i=0; i 0; ++i) + { + printf ("%8u %s\n", m_entries[i].count, class_getName (objc_classes.GetClassAtIndex(m_entries[i].idx))); + } + } + } +private: + struct Entry + { + uint32_t idx; // Index into the m_objc_class_ptrs[] array + uint32_t count; // Number of object instances that were found + uint64_t bytes; // Total number of bytes for each objc class + }; + + static int + compare_bytes (const Entry *a, const Entry *b) + { + // Reverse the comparison to most bytes entries end up at top of list + if (a->bytes > b->bytes) return -1; + if (a->bytes < b->bytes) return +1; + return 0; + } + + static int + compare_count (const Entry *a, const Entry *b) + { + // Reverse the comparison to most count entries end up at top of list + if (a->count > b->count) return -1; + if (a->count < b->count) return +1; + return 0; + } + + Entry *m_entries; + uint32_t m_size; + HeapInfoSortType m_sort_type; +}; + +ObjCClassInfo g_objc_class_snapshot; + +//---------------------------------------------------------------------- +// task_peek +// +// Reads memory from this tasks address space. This callback is needed +// by the code that iterates through all of the malloc blocks to read +// the memory in this process. +//---------------------------------------------------------------------- +static kern_return_t +task_peek (task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) +{ + *local_memory = (void*) remote_address; + return KERN_SUCCESS; +} + + +static const void +foreach_zone_in_this_process (range_callback_info_t *info) +{ + if (info == NULL || info->zone_callback == NULL) + return; + + vm_address_t *zones = NULL; + unsigned int num_zones = 0; + + kern_return_t err = malloc_get_all_zones (0, task_peek, &zones, &num_zones); + if (KERN_SUCCESS == err) + { + for (unsigned int i=0; izone_callback (info, (const malloc_zone_t *)zones[i]); + } + } + + if (info->check_vm_regions) + { +#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 task = mach_task_self(); + mach_vm_address_t vm_region_base_addr; + mach_vm_size_t vm_region_size; + natural_t vm_region_depth; + RegionInfo vm_region_info; + + ((range_contains_data_callback_info_t *)info->baton)->unique = true; + + for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size) + { + mach_msg_type_number_t vm_region_info_size = kRegionInfoSize; + const kern_return_t err = mach_vm_region_recurse (task, + &vm_region_base_addr, + &vm_region_size, + &vm_region_depth, + (vm_region_recurse_info_t)&vm_region_info, + &vm_region_info_size); + if (err) + break; + // Check all read + write regions. This will cover the thread stacks + // and any regions of memory that aren't covered by the heap + if (vm_region_info.protection & VM_PROT_WRITE && + vm_region_info.protection & VM_PROT_READ) + { + //printf ("checking vm_region: [0x%16.16llx - 0x%16.16llx)\n", (uint64_t)vm_region_base_addr, (uint64_t)vm_region_base_addr + vm_region_size); + range_info_callback (task, + info->baton, + stack_logging_type_vm_region, + vm_region_base_addr, + vm_region_size); + } + } + } +} + +//---------------------------------------------------------------------- +// dump_malloc_block_callback +// +// A simple callback that will dump each malloc block and all available +// info from the enumeration callback perspective. +//---------------------------------------------------------------------- +static void +dump_malloc_block_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size) +{ + printf ("task = 0x%4.4x: baton = %p, type = %u, ptr_addr = 0x%llx + 0x%llu\n", task, baton, type, ptr_addr, ptr_size); +} + +static void +ranges_callback (task_t task, void *baton, unsigned type, vm_range_t *ptrs, unsigned count) +{ + range_callback_info_t *info = (range_callback_info_t *)baton; + while(count--) { + info->range_callback (task, info->baton, type, ptrs->address, ptrs->size); + ptrs++; + } +} + +static void +enumerate_range_in_zone (void *baton, const malloc_zone_t *zone) +{ + range_callback_info_t *info = (range_callback_info_t *)baton; + + if (zone && zone->introspect) + zone->introspect->enumerator (mach_task_self(), + info, + MALLOC_PTR_IN_USE_RANGE_TYPE, + (vm_address_t)zone, + task_peek, + ranges_callback); +} + +static void +range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size) +{ + const uint64_t end_addr = ptr_addr + ptr_size; + + range_contains_data_callback_info_t *info = (range_contains_data_callback_info_t *)baton; + switch (info->type) + { + case eDataTypeAddress: + // Check if the current malloc block contains an address specified by "info->addr" + if (ptr_addr <= info->addr && info->addr < end_addr) + { + ++info->match_count; + malloc_match match = { (void *)ptr_addr, ptr_size, info->addr - ptr_addr, type }; + g_matches.push_back(match, info->unique); + } + break; + + case eDataTypeContainsData: + // Check if the current malloc block contains data specified in "info->data" + { + const uint32_t size = info->data.size; + if (size < ptr_size) // Make sure this block can contain this data + { + uint8_t *ptr_data = NULL; + if (task_peek (task, ptr_addr, ptr_size, (void **)&ptr_data) == KERN_SUCCESS) + { + const void *buffer = info->data.buffer; + assert (ptr_data); + const uint32_t align = info->data.align; + for (uint64_t addr = ptr_addr; + addr < end_addr && ((end_addr - addr) >= size); + addr += align, ptr_data += align) + { + if (memcmp (buffer, ptr_data, size) == 0) + { + ++info->match_count; + malloc_match match = { (void *)ptr_addr, ptr_size, addr - ptr_addr, type }; + g_matches.push_back(match, info->unique); + } + } + } + else + { + printf ("0x%llx: error: couldn't read %llu bytes\n", ptr_addr, ptr_size); + } + } + } + break; + + case eDataTypeObjC: + // Check if the current malloc block contains an objective C object + // of any sort where the first pointer in the object is an OBJC class + // pointer (an isa) + { + malloc_block_contents *block_contents = NULL; + if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS) + { + // We assume that g_objc_classes is up to date + // that the class list was verified to have some classes in it + // before calling this function + const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa); + if (objc_class_idx != UINT32_MAX) + { + bool match = false; + if (info->objc.match_isa == 0) + { + // Match any objective C object + match = true; + } + else + { + // Only match exact isa values in the current class or + // optionally in the super classes + if (info->objc.match_isa == block_contents->isa) + match = true; + else if (info->objc.match_superclasses) + { + Class super = class_getSuperclass(block_contents->isa); + while (super) + { + match = super == info->objc.match_isa; + if (match) + break; + super = class_getSuperclass(super); + } + } + } + if (match) + { + //printf (" success\n"); + ++info->match_count; + malloc_match match = { (void *)ptr_addr, ptr_size, 0, type }; + g_matches.push_back(match, info->unique); + } + else + { + //printf (" error: wrong class: %s\n", dl_info.dli_sname); + } + } + else + { + //printf ("\terror: symbol not objc class: %s\n", dl_info.dli_sname); + return; + } + } + } + break; + + case eDataTypeHeapInfo: + // Check if the current malloc block contains an objective C object + // of any sort where the first pointer in the object is an OBJC class + // pointer (an isa) + { + malloc_block_contents *block_contents = NULL; + if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS) + { + // We assume that g_objc_classes is up to date + // that the class list was verified to have some classes in it + // before calling this function + const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa); + if (objc_class_idx != UINT32_MAX) + { + // This is an objective C object + g_objc_class_snapshot.AddInstance (objc_class_idx, ptr_size); + } + else + { + // Classify other heap info + } + } + } + break; + + } +} + +static void +get_stack_for_address_enumerator(mach_stack_logging_record_t stack_record, void *task_ptr) +{ + malloc_stack_entry *stack_entry = g_malloc_stack_history.next(); + if (stack_entry) + { + stack_entry->address = (void *)stack_record.address; + stack_entry->type_flags = stack_record.type_flags; + stack_entry->argument = stack_record.argument; + stack_entry->num_frames = 0; + stack_entry->frames[0] = 0; + kern_return_t err = __mach_stack_logging_frames_for_uniqued_stack (*(task_t *)task_ptr, + stack_record.stack_identifier, + stack_entry->frames, + MAX_FRAMES, + &stack_entry->num_frames); + // Terminate the frames with zero if there is room + if (stack_entry->num_frames < MAX_FRAMES) + stack_entry->frames[stack_entry->num_frames] = 0; + } +} + +malloc_stack_entry * +get_stack_history_for_address (const void * addr, int history) +{ + if (!stack_logging_enable_logging) + return NULL; + g_malloc_stack_history.clear(); + kern_return_t err; + task_t task = mach_task_self(); + if (history) + { + err = __mach_stack_logging_enumerate_records (task, + (mach_vm_address_t)addr, + get_stack_for_address_enumerator, + &task); + } + else + { + malloc_stack_entry *stack_entry = g_malloc_stack_history.next(); + if (stack_entry) + { + stack_entry->address = addr; + stack_entry->type_flags = stack_logging_type_alloc; + stack_entry->argument = 0; + stack_entry->num_frames = 0; + stack_entry->frames[0] = 0; + err = __mach_stack_logging_get_frames(task, (mach_vm_address_t)addr, stack_entry->frames, MAX_FRAMES, &stack_entry->num_frames); + if (err == 0 && stack_entry->num_frames > 0) + { + // Terminate the frames with zero if there is room + if (stack_entry->num_frames < MAX_FRAMES) + stack_entry->frames[stack_entry->num_frames] = 0; + } + else + { + g_malloc_stack_history.clear(); + } + } + } + // Return data if there is any + return g_malloc_stack_history.data(); +} + +//---------------------------------------------------------------------- +// find_pointer_in_heap +// +// Finds a pointer value inside one or more currently valid malloc +// blocks. +//---------------------------------------------------------------------- +malloc_match * +find_pointer_in_heap (const void * addr, int check_vm_regions) +{ + g_matches.clear(); + // Setup "info" to look for a malloc block that contains data + // that is the pointer + if (addr) + { + range_contains_data_callback_info_t data_info; + data_info.type = eDataTypeContainsData; // Check each block for data + data_info.data.buffer = (uint8_t *)&addr; // What data? The pointer value passed in + data_info.data.size = sizeof(addr); // How many bytes? The byte size of a pointer + data_info.data.align = sizeof(addr); // Align to a pointer byte size + data_info.match_count = 0; // Initialize the match count to zero + data_info.done = false; // Set done to false so searching doesn't stop + data_info.unique = false; // Set to true when iterating on the vm_regions + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; + foreach_zone_in_this_process (&info); + + + } + return g_matches.data(); +} + +//---------------------------------------------------------------------- +// find_pointer_in_memory +// +// Finds a pointer value inside one or more currently valid malloc +// blocks. +//---------------------------------------------------------------------- +malloc_match * +find_pointer_in_memory (uint64_t memory_addr, uint64_t memory_size, const void * addr) +{ + g_matches.clear(); + // Setup "info" to look for a malloc block that contains data + // that is the pointer + range_contains_data_callback_info_t data_info; + data_info.type = eDataTypeContainsData; // Check each block for data + data_info.data.buffer = (uint8_t *)&addr; // What data? The pointer value passed in + data_info.data.size = sizeof(addr); // How many bytes? The byte size of a pointer + data_info.data.align = sizeof(addr); // Align to a pointer byte size + data_info.match_count = 0; // Initialize the match count to zero + data_info.done = false; // Set done to false so searching doesn't stop + data_info.unique = false; // Set to true when iterating on the vm_regions + range_info_callback (mach_task_self(), &data_info, stack_logging_type_generic, memory_addr, memory_size); + return g_matches.data(); +} + +//---------------------------------------------------------------------- +// find_objc_objects_in_memory +// +// Find all instances of ObjC classes 'c', or all ObjC classes if 'c' is +// NULL. If 'c' is non NULL, then also check objects to see if they +// inherit from 'c' +//---------------------------------------------------------------------- +malloc_match * +find_objc_objects_in_memory (void *isa, int check_vm_regions) +{ + g_matches.clear(); + if (g_objc_classes.Update()) + { + // Setup "info" to look for a malloc block that contains data + // that is the pointer + range_contains_data_callback_info_t data_info; + data_info.type = eDataTypeObjC; // Check each block for data + data_info.objc.match_isa = isa; + data_info.objc.match_superclasses = true; + data_info.match_count = 0; // Initialize the match count to zero + data_info.done = false; // Set done to false so searching doesn't stop + data_info.unique = false; // Set to true when iterating on the vm_regions + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; + foreach_zone_in_this_process (&info); + } + return g_matches.data(); +} + +//---------------------------------------------------------------------- +// get_heap_info +// +// Gather information for all allocations on the heap and report +// statistics. +//---------------------------------------------------------------------- + +void +get_heap_info (int sort_type) +{ + if (g_objc_classes.Update()) + { + // Reset all stats + g_objc_class_snapshot.Reset (); + // Setup "info" to look for a malloc block that contains data + // that is the pointer + range_contains_data_callback_info_t data_info; + data_info.type = eDataTypeHeapInfo; // Check each block for data + data_info.match_count = 0; // Initialize the match count to zero + data_info.done = false; // Set done to false so searching doesn't stop + data_info.unique = false; // Set to true when iterating on the vm_regions + const int check_vm_regions = false; + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; + foreach_zone_in_this_process (&info); + + // Sort and print byte total bytes + switch (sort_type) + { + case eSortTypeNone: + default: + case eSortTypeBytes: + g_objc_class_snapshot.SortByTotalBytes(g_objc_classes, true); + break; + + case eSortTypeCount: + g_objc_class_snapshot.SortByTotalCount(g_objc_classes, true); + break; + } + } + else + { + printf ("error: no objective C classes\n"); + } +} + +//---------------------------------------------------------------------- +// find_cstring_in_heap +// +// Finds a C string inside one or more currently valid malloc blocks. +//---------------------------------------------------------------------- +malloc_match * +find_cstring_in_heap (const char *s, int check_vm_regions) +{ + g_matches.clear(); + if (s == NULL || s[0] == '\0') + { + printf ("error: invalid argument (empty cstring)\n"); + return NULL; + } + // Setup "info" to look for a malloc block that contains data + // that is the C string passed in aligned on a 1 byte boundary + range_contains_data_callback_info_t data_info; + data_info.type = eDataTypeContainsData; // Check each block for data + data_info.data.buffer = (uint8_t *)s; // What data? The C string passed in + data_info.data.size = strlen(s); // How many bytes? The length of the C string + data_info.data.align = 1; // Data doesn't need to be aligned, so set the alignment to 1 + data_info.match_count = 0; // Initialize the match count to zero + data_info.done = false; // Set done to false so searching doesn't stop + data_info.unique = false; // Set to true when iterating on the vm_regions + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; + foreach_zone_in_this_process (&info); + return g_matches.data(); +} + +//---------------------------------------------------------------------- +// find_block_for_address +// +// Find the malloc block that whose address range contains "addr". +//---------------------------------------------------------------------- +malloc_match * +find_block_for_address (const void *addr, int check_vm_regions) +{ + g_matches.clear(); + // Setup "info" to look for a malloc block that contains data + // that is the C string passed in aligned on a 1 byte boundary + range_contains_data_callback_info_t data_info; + data_info.type = eDataTypeAddress; // Check each block to see if the block contains the address passed in + data_info.addr = (uintptr_t)addr; // What data? The C string passed in + data_info.match_count = 0; // Initialize the match count to zero + data_info.done = false; // Set done to false so searching doesn't stop + data_info.unique = false; // Set to true when iterating on the vm_regions + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; + foreach_zone_in_this_process (&info); + return g_matches.data(); +} diff --git a/examples/functions/Makefile b/examples/functions/Makefile new file mode 100644 index 000000000000..64ea6e75b332 --- /dev/null +++ b/examples/functions/Makefile @@ -0,0 +1,18 @@ +LEVEL = ../../test/make + +CXX_SOURCES := main.cpp + +EXE := lldb-functions +USE_LIBCPP := 1 + +MY_OS = $(shell uname -s) + +ifeq "$(MY_OS)" "Darwin" + LLDB_BUILD_DIR ?= /Applications/Xcode.app/Contents/SharedFrameworks + LD_EXTRAS ?= -framework LLDB -Wl,-rpath,"$(LLDB_BUILD_DIR)" + FRAMEWORK_INCLUDES=-F"$(LLDB_BUILD_DIR)" +else + LD_EXTRAS ?= $(LLDB_BUILD_DIR)/_lldb.so +endif + +include $(LEVEL)/Makefile.rules diff --git a/examples/functions/main.cpp b/examples/functions/main.cpp new file mode 100644 index 000000000000..4381098e3305 --- /dev/null +++ b/examples/functions/main.cpp @@ -0,0 +1,364 @@ +//===-- 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 +#include + +#if defined(__APPLE__) +#include +#else +#include "LLDB/SBBlock.h" +#include "LLDB/SBCompileUnit.h" +#include "LLDB/SBDebugger.h" +#include "LLDB/SBFunction.h" +#include "LLDB/SBModule.h" +#include "LLDB/SBStream.h" +#include "LLDB/SBSymbol.h" +#include "LLDB/SBTarget.h" +#include "LLDB/SBThread.h" +#include "LLDB/SBProcess.h" +#endif + +#include + +using namespace lldb; + +//---------------------------------------------------------------------- +// This quick sample code shows how to create a debugger instance and +// create an executable target without adding dependent shared +// libraries. It will then set a regular expression breakpoint to get +// breakpoint locations for all functions in the module, and use the +// locations to extract the symbol context for each location. Then it +// dumps all // information about the function: its name, file address +// range, the return type (if any), and all argument types. +// +// To build the program, type (while in this directory): +// +// $ make +// +// then to run this on MacOSX, specify the path to your LLDB.framework +// library using the DYLD_FRAMEWORK_PATH option and run the executable +// +// $ DYLD_FRAMEWORK_PATH=/Volumes/data/lldb/tot/build/Debug ./a.out executable_path1 [executable_path2 ...] +//---------------------------------------------------------------------- +class LLDBSentry +{ +public: + LLDBSentry() { + // Initialize LLDB + SBDebugger::Initialize(); + } + ~LLDBSentry() { + // Terminate LLDB + SBDebugger::Terminate(); + } +}; + +static struct option g_long_options[] = +{ + { "arch", required_argument, NULL, 'a' }, + { "canonical", no_argument, NULL, 'c' }, + { "extern", no_argument, NULL, 'x' }, + { "help", no_argument, NULL, 'h' }, + { "platform", required_argument, NULL, 'p' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +#define PROGRAM_NAME "lldb-functions" +void +usage () +{ + puts ( + "NAME\n" + " " PROGRAM_NAME " -- extract all function signatures from one or more binaries.\n" + "\n" + "SYNOPSIS\n" + " " PROGRAM_NAME " [[--arch=] [--platform=] [--verbose] [--help] [--canonical] --] [....]\n" + "\n" + "DESCRIPTION\n" + " Loads the executable pointed to by and dumps complete signatures for all functions that have debug information.\n" + "\n" + "EXAMPLE\n" + " " PROGRAM_NAME " --arch=x86_64 /usr/lib/dyld\n" + ); + exit(0); +} +int +main (int argc, char const *argv[]) +{ + // Use a sentry object to properly initialize/terminate LLDB. + LLDBSentry sentry; + + SBDebugger debugger (SBDebugger::Create()); + + // Create a debugger instance so we can create a target + if (!debugger.IsValid()) + fprintf (stderr, "error: failed to create a debugger object\n"); + + bool show_usage = false; + bool verbose = false; + bool canonical = false; + bool external_only = false; + const char *arch = NULL; + const char *platform = NULL; + std::string short_options("h?"); + for (const struct option *opt = g_long_options; opt->name; ++opt) + { + if (isprint(opt->val)) + { + short_options.append(1, (char)opt->val); + switch (opt->has_arg) + { + case no_argument: + break; + case required_argument: + short_options.append(1, ':'); + break; + case optional_argument: + short_options.append(2, ':'); + break; + } + } + } +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + char ch; + while ((ch = getopt_long_only(argc, (char * const *)argv, short_options.c_str(), g_long_options, 0)) != -1) + { + switch (ch) + { + case 0: + break; + + case 'a': + if (arch != NULL) + { + fprintf (stderr, "error: the --arch option can only be specified once\n"); + exit(1); + } + arch = optarg; + break; + + case 'c': + canonical = true; + break; + + case 'x': + external_only = true; + break; + + case 'p': + platform = optarg; + break; + + case 'v': + verbose = true; + break; + + case 'h': + case '?': + default: + show_usage = true; + break; + } + } + argc -= optind; + argv += optind; + + const bool add_dependent_libs = false; + SBError error; + for (int arg_idx = 0; arg_idx < argc; ++arg_idx) + { + // The first argument is the file path we want to look something up in + const char *exe_file_path = argv[arg_idx]; + + // Create a target using the executable. + SBTarget target = debugger.CreateTarget (exe_file_path, + arch, + platform, + add_dependent_libs, + error); + + if (error.Success()) + { + if (target.IsValid()) + { + SBFileSpec exe_file_spec (exe_file_path, true); + SBModule module (target.FindModule (exe_file_spec)); + SBFileSpecList comp_unit_list; + + if (module.IsValid()) + { + char command[1024]; + lldb::SBCommandReturnObject command_result; + snprintf (command, sizeof(command), "add-dsym --uuid %s", module.GetUUIDString()); + debugger.GetCommandInterpreter().HandleCommand (command, command_result); + if (!command_result.Succeeded()) + { + fprintf (stderr, "error: couldn't locate debug symbols for '%s'\n", exe_file_path); + exit(1); + } + + SBFileSpecList module_list; + module_list.Append(exe_file_spec); + SBBreakpoint bp = target.BreakpointCreateByRegex (".", module_list, comp_unit_list); + + const size_t num_locations = bp.GetNumLocations(); + for (uint32_t bp_loc_idx=0; bp_loc_idx 0 ? ", " : "", function_arg_type.GetName()); + } + else + { + printf ("%s???", function_arg_idx > 0 ? ", " : ""); + } + } + printf (")\n"); + } + } + } + } + } + } + } + } + } + else + { + fprintf (stderr, "error: %s\n", error.GetCString()); + exit(1); + } + } + + return 0; +} + diff --git a/examples/interposing/darwin/fd_interposing/FDInterposing.cpp b/examples/interposing/darwin/fd_interposing/FDInterposing.cpp new file mode 100644 index 000000000000..e41dc34038ba --- /dev/null +++ b/examples/interposing/darwin/fd_interposing/FDInterposing.cpp @@ -0,0 +1,1152 @@ +//===-- FDInterposing.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file helps with catching double close calls on unix integer file +// descriptors by interposing functions for all file descriptor create and +// close operations. A stack backtrace for every create and close function is +// maintained, and every create and close operation is logged. When a double +// file descriptor close is encountered, it will be logged. +// +// To enable the interposing in a darwin program, set the DYLD_INSERT_LIBRARIES +// environment variable as follows: +// For sh: +// DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib /path/to/executable +// For tcsh: +// (setenv DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib ; /path/to/executable) +// +// Other environment variables that can alter the default actions of this +// interposing shared library include: +// +// "FileDescriptorStackLoggingNoCompact" +// +// With this environment variable set, all file descriptor create and +// delete operations will be permanantly maintained in the event map. +// The default action is to compact the create/delete events by removing +// any previous file descriptor create events that are matched with a +// corresponding file descriptor delete event when the next valid file +// descriptor create event is detected. +// +// "FileDescriptorMinimalLogging" +// +// By default every file descriptor create and delete operation is logged +// (to STDOUT by default, see the "FileDescriptorLogFile"). This can be +// suppressed to only show errors and warnings by setting this environment +// variable (the value in not important). +// +// "FileDescriptorLogFile=" +// +// By default logging goes to STDOUT_FILENO, but this can be changed by +// setting FileDescriptorLogFile. The value is a path to a file that +// will be opened and used for logging. +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for std::tr1::shared_ptr +#include +#include +#include +#include + +//---------------------------------------------------------------------- +/// @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&) + +extern "C" { + int accept$NOCANCEL (int, struct sockaddr * __restrict, socklen_t * __restrict); + int close$NOCANCEL(int); + int open$NOCANCEL(const char *, int, ...); + int __open_extended(const char *, int, uid_t, gid_t, int, struct kauth_filesec *); +} + +namespace fd_interposing { + +//---------------------------------------------------------------------- +// String class so we can get formatted strings without having to worry +// about the memory storage since it will allocate the memory it needs. +//---------------------------------------------------------------------- +class String +{ +public: + String () : + m_str (NULL) + {} + + String (const char *format, ...) : + m_str (NULL) + { + va_list args; + va_start (args, format); + vprintf (format, args); + va_end (args); + } + + ~String() + { + reset(); + } + + void + reset (char *s = NULL) + { + if (m_str) + ::free (m_str); + m_str = s; + } + + const char * + c_str () const + { + return m_str; + } + + void + printf (const char *format, ...) + { + va_list args; + va_start (args, format); + vprintf (format, args); + va_end (args); + } + void + vprintf (const char *format, va_list args) + { + reset(); + ::vasprintf (&m_str, format, args); + } + + void + log (int log_fd) + { + if (m_str && log_fd >= 0) + { + const int len = strlen(m_str); + if (len > 0) + { + write (log_fd, m_str, len); + const char last_char = m_str[len-1]; + if (!(last_char == '\n' || last_char == '\r')) + write (log_fd, "\n", 1); + } + } + } +protected: + char *m_str; + +private: + DISALLOW_COPY_AND_ASSIGN (String); +}; + +//---------------------------------------------------------------------- +// Type definitions +//---------------------------------------------------------------------- +typedef std::vector Frames; +class FDEvent; +typedef std::vector Frames; +typedef std::tr1::shared_ptr FDEventSP; +typedef std::tr1::shared_ptr StringSP; + + +//---------------------------------------------------------------------- +// FDEvent +// +// A class that describes a file desciptor event. +// +// File descriptor events fall into one of two categories: create events +// and delete events. +//---------------------------------------------------------------------- +class FDEvent +{ +public: + FDEvent (int fd, int err, const StringSP &string_sp, bool is_create, const Frames& frames) : + m_string_sp (string_sp), + m_frames (frames.begin(), frames.end()), + m_fd (fd), + m_err (err), + m_is_create (is_create) + {} + + ~FDEvent () {} + + bool + IsCreateEvent() const + { + return m_is_create; + } + + bool + IsDeleteEvent() const + { + return !m_is_create; + } + + Frames & + GetFrames () + { + return m_frames; + } + + const Frames & + GetFrames () const + { + return m_frames; + } + + int + GetFD () const + { + return m_fd; + } + + int + GetError () const + { + return m_err; + } + + void + Dump (int log_fd) const; + + void + SetCreateEvent (FDEventSP &create_event_sp) + { + m_create_event_sp = create_event_sp; + } + +private: + // A shared pointer to a String that describes this event in + // detail (all args and return and error values) + StringSP m_string_sp; + // The frames for the stack backtrace for this event + Frames m_frames; + // If this is a file descriptor delete event, this might contain + // the correspoding file descriptor create event + FDEventSP m_create_event_sp; + // The file descriptor for this event + int m_fd; + // The error code (if any) for this event + int m_err; + // True if this event is a file descriptor create event, false + // if it is a file descriptor delete event + bool m_is_create; +}; + +//---------------------------------------------------------------------- +// Templatized class that will save errno only if the "value" it is +// constructed with is equal to INVALID. When the class goes out of +// scope, it will restore errno if it was saved. +//---------------------------------------------------------------------- +template +class Errno +{ +public: + // Save errno only if we are supposed to + Errno (int value) : + m_saved_errno ((value == INVALID) ? errno : 0), + m_restore (value == INVALID) + { + } + + // Restore errno only if we are supposed to + ~Errno() + { + if (m_restore) + errno = m_saved_errno; + } + + // Accessor for the saved value of errno + int + get_errno() const + { + return m_saved_errno; + } + +protected: + const int m_saved_errno; + const bool m_restore; +}; + +typedef Errno<-1> InvalidFDErrno; +typedef Errno<-1> NegativeErrorErrno; +typedef std::vector FDEventArray; +typedef std::map FDEventMap; + +//---------------------------------------------------------------------- +// Globals +//---------------------------------------------------------------------- +// Global event map that contains all file descriptor events. As file +// descriptor create and close events come in, they will get filled +// into this map (protected by g_mutex). When a file descriptor close +// event is detected, the open event will be removed and placed into +// the close event so if something tries to double close a file +// descriptor we can show the previous close event and the file +// desctiptor event that created it. When a new file descriptor create +// event comes in, we will remove the previous one for that file +// desctiptor unless the environment variable "FileDescriptorStackLoggingNoCompact" +// is set. The file desctiptor history can be accessed using the +// get_fd_history() function. +static FDEventMap g_fd_event_map; +// A mutex to protect access to our data structures in g_fd_event_map +// and also our logging messages +static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; +// Log all file descriptor create and close events by default. Only log +// warnings and erros if the "FileDescriptorMinimalLogging" environment +// variable is set. +static int g_log_all_calls = 1; +// We compact the file descriptor events by default. Set the environment +// varible "FileDescriptorStackLoggingNoCompact" to keep a full history. +static int g_compact = 1; +// The current process ID +static int g_pid = -1; +static bool g_enabled = true; +//---------------------------------------------------------------------- +// Mutex class that will lock a mutex when it is constructed, and unlock +// it when is goes out of scope +//---------------------------------------------------------------------- +class Locker +{ +public: + Locker (pthread_mutex_t *mutex_ptr) : + m_mutex_ptr(mutex_ptr) + { + ::pthread_mutex_lock (m_mutex_ptr); + } + + // This allows clients to test try and acquire the mutex... + Locker (pthread_mutex_t *mutex_ptr, bool &lock_acquired) : + m_mutex_ptr(NULL) + { + lock_acquired = ::pthread_mutex_trylock(mutex_ptr) == 0; + if (lock_acquired) + m_mutex_ptr = mutex_ptr; + } + + ~Locker () + { + if (m_mutex_ptr) + ::pthread_mutex_unlock (m_mutex_ptr); + } +protected: + pthread_mutex_t *m_mutex_ptr; +}; + +static void +log (const char *format, ...) __attribute__ ((format (printf, 1, 2))); + +static void +log (int log_fd, const FDEvent *event, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + +static void +backtrace_log (const char *format, ...) __attribute__ ((format (printf, 1, 2))); + +static void +backtrace_error (const char *format, ...) __attribute__ ((format (printf, 1, 2))); + +static void +log_to_fd (int log_fd, const char *format, ...) __attribute__ ((format (printf, 2, 3))); + +static inline size_t +get_backtrace (Frames &frame_buffer, size_t frames_to_remove) +{ + void *frames[2048]; + int count = ::backtrace (&frames[0], sizeof(frames)/sizeof(void*)); + if (count > frames_to_remove) + frame_buffer.assign (&frames[frames_to_remove], &frames[count]); + else + frame_buffer.assign (&frames[0], &frames[count]); + while (frame_buffer.back() < (void *)1024) + frame_buffer.pop_back(); + return frame_buffer.size(); +} + +static int g_log_fd = STDOUT_FILENO; +static int g_initialized = 0; + +const char * +get_process_fullpath (bool force = false) +{ + static char g_process_fullpath[PATH_MAX] = {0}; + if (force || g_process_fullpath[0] == '\0') + { + // If DST is NULL, then return the number of bytes needed. + uint32_t len = sizeof(g_process_fullpath); + if (_NSGetExecutablePath (g_process_fullpath, &len) != 0) + strncpy (g_process_fullpath, "", sizeof(g_process_fullpath)); + } + return g_process_fullpath; +} + +// Returns the current process ID, or -1 if inserposing not enabled for +// this process +static int +get_interposed_pid() +{ + if (!g_enabled) + return -1; + + const pid_t pid = getpid(); + if (g_pid != pid) + { + if (g_pid == -1) + { + g_pid = pid; + log ("Interposing file descriptor create and delete functions for %s (pid=%i)\n", get_process_fullpath (true), pid); + } + else + { + log ("pid=%i: disabling interposing file descriptor create and delete functions for child process %s (pid=%i)\n", g_pid, get_process_fullpath (true), pid); + g_enabled = false; + return -1; + } + // Log when our process changes + } + return g_pid; +} + +static int +get_logging_fd () +{ + if (!g_enabled) + return -1; + + if (!g_initialized) + { + g_initialized = 1; + + const pid_t pid = get_interposed_pid(); + + if (g_enabled) + { + // Keep all stack info around for all fd create and delete calls. + // Otherwise we will remove the fd create call when a corresponding + // fd delete call is received + if (getenv("FileDescriptorStackLoggingNoCompact")) + g_compact = 0; + + if (getenv("FileDescriptorMinimalLogging")) + g_log_all_calls = 0; + + const char *log_path = getenv ("FileDescriptorLogFile"); + if (log_path) + g_log_fd = ::creat (log_path, 0660); + else + g_log_fd = STDOUT_FILENO; + + // Only let this interposing happen on the first time this matches + // and stop this from happening so any child processes don't also + // log their file descriptors + ::unsetenv ("DYLD_INSERT_LIBRARIES"); + } + else + { + log ("pid=%i: logging disabled\n", getpid()); + } + } + return g_log_fd; +} + +void +log_to_fd (int log_fd, const char *format, va_list args) +{ + if (format && format[0] && log_fd >= 0) + { + char buffer[PATH_MAX]; + const int count = ::vsnprintf (buffer, sizeof(buffer), format, args); + if (count > 0) + write (log_fd, buffer, count); + } +} + +void +log_to_fd (int log_fd, const char *format, ...) +{ + if (format && format[0]) + { + va_list args; + va_start (args, format); + log_to_fd (log_fd, format, args); + va_end (args); + } +} + +void +log (const char *format, va_list args) +{ + log_to_fd (get_logging_fd (), format, args); +} + +void +log (const char *format, ...) +{ + if (format && format[0]) + { + va_list args; + va_start (args, format); + log (format, args); + va_end (args); + } +} + +void +log (int log_fd, const FDEvent *event, const char *format, ...) +{ + if (format && format[0]) + { + va_list args; + va_start (args, format); + log_to_fd (log_fd, format, args); + va_end (args); + } + if (event) + event->Dump(log_fd); +} + +void +FDEvent::Dump (int log_fd) const +{ + if (log_fd >= 0) + { + log_to_fd (log_fd, "%s\n", m_string_sp->c_str()); + if (!m_frames.empty()) + ::backtrace_symbols_fd (m_frames.data(), m_frames.size(), log_fd); + + if (m_create_event_sp) + { + log_to_fd (log_fd, "\nfd=%i was created with this event:\n", m_fd); + m_create_event_sp->Dump (log_fd); + log_to_fd (log_fd, "\n"); + } + } +} + + +void +backtrace_log (const char *format, ...) +{ + const int log_fd = get_logging_fd (); + if (log_fd >= 0) + { + if (format && format[0]) + { + va_list args; + va_start (args, format); + log (format, args); + va_end (args); + } + + Frames frames; + if (get_backtrace(frames, 2)) + ::backtrace_symbols_fd (frames.data(), frames.size(), log_fd); + } + +} + +void +backtrace_error (const char *format, ...) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + const int log_fd = get_logging_fd (); + if (log_fd >= 0) + { + log ("\nerror: %s (pid=%i): ", get_process_fullpath (), pid); + + if (format && format[0]) + { + va_list args; + va_start (args, format); + log (format, args); + va_end (args); + } + + Frames frames; + if (get_backtrace(frames, 2)) + ::backtrace_symbols_fd (frames.data(), frames.size(), log_fd); + } + } +} + +void +save_backtrace (int fd, int err, const StringSP &string_sp, bool is_create) +{ + Frames frames; + get_backtrace(frames, 2); + + FDEventSP fd_event_sp (new FDEvent (fd, err, string_sp, is_create, frames)); + + FDEventMap::iterator pos = g_fd_event_map.find (fd); + + if (pos != g_fd_event_map.end()) + { + // We have history for this fd... + + FDEventArray &event_array = g_fd_event_map[fd]; + if (fd_event_sp->IsCreateEvent()) + { + // The current fd event is a function that creates + // a descriptor, check in case last event was + // a create event. + if (event_array.back()->IsCreateEvent()) + { + const int log_fd = get_logging_fd(); + // Two fd create functions in a row, we missed + // a function that closes a fd... + log (log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor create event fd=%i (we missed a file descriptor close event):\n", fd); + } + else if (g_compact) + { + // We are compacting so we remove previous create event + // when we get the correspinding delete event + event_array.pop_back(); + } + } + else + { + // The current fd event is a function that deletes + // a descriptor, check in case last event for this + // fd was a delete event (double close!) + if (event_array.back()->IsDeleteEvent()) + { + const int log_fd = get_logging_fd(); + // Two fd delete functions in a row, we must + // have missed some function that opened a descriptor + log (log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor close event for fd=%d (we missed the file descriptor create event):\n", fd); + } + else if (g_compact) + { + // Since this is a close event, we want to remember the open event + // that this close if for... + fd_event_sp->SetCreateEvent(event_array.back()); + // We are compacting so we remove previous create event + // when we get the correspinding delete event + event_array.pop_back(); + } + } + + event_array.push_back(fd_event_sp); + } + else + { + g_fd_event_map[fd].push_back(fd_event_sp); + } +} + +//---------------------------------------------------------------------- +// socket() interpose function +//---------------------------------------------------------------------- +extern "C" int +socket$__interposed__ (int domain, int type, int protocol) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int fd = ::socket (domain, type, protocol); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String); + if (fd == -1) + description_sp->printf("pid=%i: socket (domain = %i, type = %i, protocol = %i) => fd=%i errno = %i", pid, domain, type, protocol, fd, fd_errno.get_errno()); + else + description_sp->printf("pid=%i: socket (domain = %i, type = %i, protocol = %i) => fd=%i", pid, domain, type, protocol, fd); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::socket (domain, type, protocol); + } +} + +//---------------------------------------------------------------------- +// socketpair() interpose function +//---------------------------------------------------------------------- +extern "C" int +socketpair$__interposed__ (int domain, int type, int protocol, int fds[2]) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + fds[0] = -1; + fds[1] = -1; + const int err = socketpair (domain, type, protocol, fds); + NegativeErrorErrno err_errno(err); + StringSP description_sp(new String ("pid=%i: socketpair (domain=%i, type=%i, protocol=%i, {fd=%i, fd=%i}) -> err=%i", pid, domain, type, protocol, fds[0], fds[1], err)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fds[0] >= 0) + save_backtrace (fds[0], err_errno.get_errno(), description_sp, true); + if (fds[1] >= 0) + save_backtrace (fds[1], err_errno.get_errno(), description_sp, true); + return err; + } + else + { + return socketpair (domain, type, protocol, fds); + } +} + +//---------------------------------------------------------------------- +// open() interpose function +//---------------------------------------------------------------------- +extern "C" int +open$__interposed__ (const char *path, int oflag, int mode) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + int fd = -2; + StringSP description_sp(new String); + if (oflag & O_CREAT) + { + fd = ::open (path, oflag, mode); + description_sp->printf("pid=%i: open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid, path, oflag, mode, fd); + } + else + { + fd = ::open (path, oflag); + description_sp->printf("pid=%i: open (path = '%s', oflag = %i) -> fd=%i", pid, path, oflag, fd); + } + + InvalidFDErrno fd_errno(fd); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::open (path, oflag, mode); + } +} + +//---------------------------------------------------------------------- +// open$NOCANCEL() interpose function +//---------------------------------------------------------------------- +extern "C" int +open$NOCANCEL$__interposed__ (const char *path, int oflag, int mode) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int fd = ::open$NOCANCEL (path, oflag, mode); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String ("pid=%i: open$NOCANCEL (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid, path, oflag, mode, fd)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::open$NOCANCEL (path, oflag, mode); + } +} + + +//---------------------------------------------------------------------- +// __open_extended() interpose function +//---------------------------------------------------------------------- +extern "C" int +__open_extended$__interposed__ (const char *path, int oflag, uid_t uid, gid_t gid, int mode, struct kauth_filesec *fsacl) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int fd = ::__open_extended (path, oflag, uid, gid, mode, fsacl); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String ("pid=%i: __open_extended (path='%s', oflag=%i, uid=%i, gid=%i, mode=%i, fsacl=%p) -> fd=%i", pid, path, oflag, uid, gid, mode, fsacl, fd)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::__open_extended (path, oflag, uid, gid, mode, fsacl); + } +} + +//---------------------------------------------------------------------- +// kqueue() interpose function +//---------------------------------------------------------------------- +extern "C" int +kqueue$__interposed__ (void) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int fd = ::kqueue (); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String ("pid=%i: kqueue () -> fd=%i", pid, fd)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::kqueue (); + } +} + +//---------------------------------------------------------------------- +// shm_open() interpose function +//---------------------------------------------------------------------- +extern "C" int +shm_open$__interposed__ (const char *path, int oflag, int mode) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int fd = ::shm_open (path, oflag, mode); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String ("pid=%i: shm_open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid, path, oflag, mode, fd)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::shm_open (path, oflag, mode); + } +} + +//---------------------------------------------------------------------- +// accept() interpose function +//---------------------------------------------------------------------- +extern "C" int +accept$__interposed__ (int socket, struct sockaddr *address, socklen_t *address_len) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int fd = ::accept (socket, address, address_len); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String ("pid=%i: accept (socket=%i, ...) -> fd=%i", pid, socket, fd)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::accept (socket, address, address_len); + } +} + + +//---------------------------------------------------------------------- +// accept$NOCANCEL() interpose function +//---------------------------------------------------------------------- +extern "C" int +accept$NOCANCEL$__interposed__ (int socket, struct sockaddr *address, socklen_t *address_len) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int fd = ::accept$NOCANCEL (socket, address, address_len); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String ("pid=%i: accept$NOCANCEL (socket=%i, ...) -> fd=%i", pid, socket, fd)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::accept$NOCANCEL (socket, address, address_len); + } +} + +//---------------------------------------------------------------------- +// dup() interpose function +//---------------------------------------------------------------------- +extern "C" int +dup$__interposed__ (int fd2) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int fd = ::dup (fd2); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String ("pid=%i: dup (fd2=%i) -> fd=%i", pid, fd2, fd)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::dup (fd2); + } +} + +//---------------------------------------------------------------------- +// dup2() interpose function +//---------------------------------------------------------------------- +extern "C" int +dup2$__interposed__ (int fd1, int fd2) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + // If "fd2" is already opened, it will be closed during the + // dup2 call below, so we need to see if we have fd2 in our + // open map and treat it as a close(fd2) + FDEventMap::iterator pos = g_fd_event_map.find (fd2); + StringSP dup2_close_description_sp(new String ("pid=%i: dup2 (fd1=%i, fd2=%i) -> will close (fd=%i)", pid, fd1, fd2, fd2)); + if (pos != g_fd_event_map.end() && pos->second.back()->IsCreateEvent()) + save_backtrace (fd2, 0, dup2_close_description_sp, false); + + const int fd = ::dup2(fd1, fd2); + InvalidFDErrno fd_errno(fd); + StringSP description_sp(new String ("pid=%i: dup2 (fd1=%i, fd2=%i) -> fd=%i", pid, fd1, fd2, fd)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + + if (fd >= 0) + save_backtrace (fd, fd_errno.get_errno(), description_sp, true); + return fd; + } + else + { + return ::dup2(fd1, fd2); + } +} + +//---------------------------------------------------------------------- +// close() interpose function +//---------------------------------------------------------------------- +extern "C" int +close$__interposed__ (int fd) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int err = close(fd); + NegativeErrorErrno err_errno(err); + StringSP description_sp (new String); + if (err == -1) + description_sp->printf("pid=%i: close (fd=%i) => %i errno = %i (%s))", pid, fd, err, err_errno.get_errno(), strerror(err_errno.get_errno())); + else + description_sp->printf("pid=%i: close (fd=%i) => %i", pid, fd, err); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + + if (err == 0) + { + if (fd >= 0) + save_backtrace (fd, err, description_sp, false); + } + else if (err == -1) + { + if (err_errno.get_errno() == EBADF && fd != -1) + { + backtrace_error ("close (fd=%d) resulted in EBADF:\n", fd); + + FDEventMap::iterator pos = g_fd_event_map.find (fd); + if (pos != g_fd_event_map.end()) + { + log (get_logging_fd(), pos->second.back().get(), "\nfd=%d was previously %s with this event:\n", fd, pos->second.back()->IsCreateEvent() ? "opened" : "closed"); + } + } + } + return err; + } + else + { + return close (fd); + } +} + +//---------------------------------------------------------------------- +// close$NOCANCEL() interpose function +//---------------------------------------------------------------------- +extern "C" int +close$NOCANCEL$__interposed__ (int fd) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + const int err = close$NOCANCEL(fd); + NegativeErrorErrno err_errno(err); + StringSP description_sp (new String); + if (err == -1) + description_sp->printf("pid=%i: close$NOCANCEL (fd=%i) => %i errno = %i (%s))", pid, fd, err, err_errno.get_errno(), strerror(err_errno.get_errno())); + else + description_sp->printf("pid=%i: close$NOCANCEL (fd=%i) => %i", pid, fd, err); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + + if (err == 0) + { + if (fd >= 0) + save_backtrace (fd, err, description_sp, false); + } + else if (err == -1) + { + if (err_errno.get_errno() == EBADF && fd != -1) + { + backtrace_error ("close$NOCANCEL (fd=%d) resulted in EBADF\n:", fd); + + FDEventMap::iterator pos = g_fd_event_map.find (fd); + if (pos != g_fd_event_map.end()) + { + log (get_logging_fd(), pos->second.back().get(), "\nfd=%d was previously %s with this event:\n", fd, pos->second.back()->IsCreateEvent() ? "opened" : "closed"); + } + } + } + return err; + } + else + { + return close$NOCANCEL(fd); + } +} + +//---------------------------------------------------------------------- +// pipe() interpose function +//---------------------------------------------------------------------- +extern "C" int +pipe$__interposed__ (int fds[2]) +{ + const int pid = get_interposed_pid(); + if (pid >= 0) + { + Locker locker (&g_mutex); + fds[0] = -1; + fds[1] = -1; + const int err = pipe (fds); + const int saved_errno = errno; + StringSP description_sp(new String ("pid=%i: pipe ({fd=%i, fd=%i}) -> err=%i", pid, fds[0], fds[1], err)); + if (g_log_all_calls) + description_sp->log (get_logging_fd()); + if (fds[0] >= 0) + save_backtrace (fds[0], saved_errno, description_sp, true); + if (fds[1] >= 0) + save_backtrace (fds[1], saved_errno, description_sp, true); + errno = saved_errno; + return err; + } + else + { + return pipe (fds); + } +} + +//---------------------------------------------------------------------- +// get_fd_history() +// +// This function allows runtime access to the file descriptor history. +// +// @param[in] log_fd +// The file descriptor to log to +// +// @param[in] fd +// The file descriptor whose history should be dumped +//---------------------------------------------------------------------- +extern "C" void +get_fd_history (int log_fd, int fd) +{ + // "create" below needs to be outside of the mutex locker scope + if (log_fd >= 0) + { + bool got_lock = false; + Locker locker (&g_mutex, got_lock); + if (got_lock) + { + FDEventMap::iterator pos = g_fd_event_map.find (fd); + log_to_fd (log_fd, "Dumping file descriptor history for fd=%i:\n", fd); + if (pos != g_fd_event_map.end()) + { + FDEventArray &event_array = g_fd_event_map[fd]; + const size_t num_events = event_array.size(); + for (size_t i=0; iDump (log_fd); + } + else + { + log_to_fd (log_fd, "error: no file descriptor events found for fd=%i\n", fd); + } + } + else + { + log_to_fd (log_fd, "error: fd event mutex is locked...\n"); + } + } +} + +//---------------------------------------------------------------------- +// Interposing +//---------------------------------------------------------------------- +// FD creation routines +DYLD_INTERPOSE(accept$__interposed__, accept); +DYLD_INTERPOSE(accept$NOCANCEL$__interposed__, accept$NOCANCEL); +DYLD_INTERPOSE(dup$__interposed__, dup); +DYLD_INTERPOSE(dup2$__interposed__, dup2); +DYLD_INTERPOSE(kqueue$__interposed__, kqueue); +DYLD_INTERPOSE(open$__interposed__, open); +DYLD_INTERPOSE(open$NOCANCEL$__interposed__, open$NOCANCEL); +DYLD_INTERPOSE(__open_extended$__interposed__, __open_extended); +DYLD_INTERPOSE(pipe$__interposed__, pipe); +DYLD_INTERPOSE(shm_open$__interposed__, shm_open); +DYLD_INTERPOSE(socket$__interposed__, socket); +DYLD_INTERPOSE(socketpair$__interposed__, socketpair); + +// FD deleting routines +DYLD_INTERPOSE(close$__interposed__, close); +DYLD_INTERPOSE(close$NOCANCEL$__interposed__, close$NOCANCEL); + +} // namespace fd_interposing + + diff --git a/examples/interposing/darwin/fd_interposing/Makefile b/examples/interposing/darwin/fd_interposing/Makefile new file mode 100644 index 000000000000..20570b1ec320 --- /dev/null +++ b/examples/interposing/darwin/fd_interposing/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../test/make + +DYLIB_NAME := FDInterposing +DYLIB_ONLY := YES +DYLIB_CXX_SOURCES := FDInterposing.cpp + +include $(LEVEL)/Makefile.rules diff --git a/examples/lookup/Makefile b/examples/lookup/Makefile new file mode 100644 index 000000000000..f4429b6e4d92 --- /dev/null +++ b/examples/lookup/Makefile @@ -0,0 +1,17 @@ +LEVEL = ../../test/make + +CXX_SOURCES := main.cpp +EXE := lldb-lookup +USE_LIBCPP := 1 + +MY_OS = $(shell uname -s) + +ifeq "$(MY_OS)" "Darwin" + LLDB_BUILD_DIR ?= /Applications/Xcode.app/Contents/SharedFrameworks + LD_EXTRAS ?= -framework LLDB -Wl,-rpath,"$(LLDB_BUILD_DIR)" + FRAMEWORK_INCLUDES=-F"$(LLDB_BUILD_DIR)" +else + LD_EXTRAS ?= $(LLDB_BUILD_DIR)/_lldb.so +endif + +include $(LEVEL)/Makefile.rules diff --git a/examples/lookup/main.cpp b/examples/lookup/main.cpp new file mode 100644 index 000000000000..fbb908460676 --- /dev/null +++ b/examples/lookup/main.cpp @@ -0,0 +1,235 @@ +//===-- 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 +#include + +#if defined(__APPLE__) +#include +#else +#include "LLDB/SBBlock.h" +#include "LLDB/SBCompileUnit.h" +#include "LLDB/SBDebugger.h" +#include "LLDB/SBFunction.h" +#include "LLDB/SBModule.h" +#include "LLDB/SBStream.h" +#include "LLDB/SBSymbol.h" +#include "LLDB/SBTarget.h" +#include "LLDB/SBThread.h" +#include "LLDB/SBProcess.h" +#endif + +#include + +using namespace lldb; + +//---------------------------------------------------------------------- +// This quick sample code shows how to create a debugger instance and +// create an "i386" executable target. Then we can lookup the executable +// module and resolve a file address into a section offset address, +// and find all symbol context objects (if any) for that address: +// compile unit, function, deepest block, line table entry and the +// symbol. +// +// To build the program, type (while in this directory): +// +// $ make +// +// then (for example): +// +// $ DYLD_FRAMEWORK_PATH=/Volumes/data/lldb/svn/ToT/build/Debug ./a.out executable_path file_address +//---------------------------------------------------------------------- +class LLDBSentry +{ +public: + LLDBSentry() { + // Initialize LLDB + SBDebugger::Initialize(); + } + ~LLDBSentry() { + // Terminate LLDB + SBDebugger::Terminate(); + } +}; + +static struct option g_long_options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "verbose", no_argument, NULL, 'v' }, + { "arch", required_argument, NULL, 'a' }, + { "platform", required_argument, NULL, 'p' }, + { NULL, 0, NULL, 0 } +}; + +#define PROGRAM_NAME "lldb-lookup" +void +usage () +{ + puts ( + "NAME\n" + " " PROGRAM_NAME " -- symbolicate addresses using lldb.\n" + "\n" + "SYNOPSIS\n" + " " PROGRAM_NAME " [[--arch=] [--platform=] [--verbose] [--help] --]
[
....]\n" + "\n" + "DESCRIPTION\n" + " Loads the executable pointed to by and looks up and
\n" + " arguments\n" + "\n" + "EXAMPLE\n" + " " PROGRAM_NAME " --arch=x86_64 -- /usr/lib/dyld 0x100000000\n" + ); + exit(0); +} +int +main (int argc, char const *argv[]) +{ + // Use a sentry object to properly initialize/terminate LLDB. + LLDBSentry sentry; + + SBDebugger debugger (SBDebugger::Create()); + + // Create a debugger instance so we can create a target + if (!debugger.IsValid()) + fprintf (stderr, "error: failed to create a debugger object\n"); + + bool show_usage = false; + bool verbose = false; + const char *arch = NULL; + const char *platform = NULL; + std::string short_options("h?"); + for (const struct option *opt = g_long_options; opt->name; ++opt) + { + if (isprint(opt->val)) + { + short_options.append(1, (char)opt->val); + switch (opt->has_arg) + { + case no_argument: + break; + case required_argument: + short_options.append(1, ':'); + break; + case optional_argument: + short_options.append(2, ':'); + break; + } + } + } +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + char ch; + while ((ch = getopt_long_only(argc, (char * const *)argv, short_options.c_str(), g_long_options, 0)) != -1) + { + switch (ch) + { + case 0: + break; + + case 'a': + if (arch != NULL) + { + fprintf (stderr, "error: the --arch option can only be specified once\n"); + exit(1); + } + arch = optarg; + break; + + case 'p': + platform = optarg; + break; + + case 'v': + verbose = true; + break; + + case 'h': + case '?': + default: + show_usage = true; + break; + } + } + argc -= optind; + argv += optind; + + if (show_usage || argc < 2) + usage(); + + int arg_idx = 0; + // The first argument is the file path we want to look something up in + const char *exe_file_path = argv[arg_idx]; + const char *addr_cstr; + const bool add_dependent_libs = false; + SBError error; + SBStream strm; + strm.RedirectToFileHandle (stdout, false); + + while ((addr_cstr = argv[++arg_idx]) != NULL) + { + // The second argument in the address that we want to lookup + lldb::addr_t file_addr = strtoull (addr_cstr, NULL, 0); + + // Create a target using the executable. + SBTarget target = debugger.CreateTarget (exe_file_path, + arch, + platform, + add_dependent_libs, + error); + if (!error.Success()) + { + fprintf (stderr, "error: %s\n", error.GetCString()); + exit(1); + } + + printf ("%sLooking up 0x%llx in '%s':\n", (arg_idx > 1) ? "\n" : "", file_addr, exe_file_path); + + if (target.IsValid()) + { + // Find the executable module so we can do a lookup inside it + SBFileSpec exe_file_spec (exe_file_path, true); + SBModule module (target.FindModule (exe_file_spec)); + + // Take a file virtual address and resolve it to a section offset + // address that can be used to do a symbol lookup by address + SBAddress addr = module.ResolveFileAddress (file_addr); + bool success = addr.IsValid() && addr.GetSection().IsValid(); + if (success) + { + // We can resolve a section offset address in the module + // and only ask for what we need. You can logical or together + // bits from the SymbolContextItem enumeration found in + // lldb-enumeration.h to request only what you want. Here we + // are asking for everything. + // + // NOTE: the less you ask for, the less LLDB will parse as + // LLDB does partial parsing on just about everything. + SBSymbolContext sc (module.ResolveSymbolContextForAddress (addr, eSymbolContextEverything)); + + strm.Printf (" Address: %s + 0x%llx\n Summary: ", addr.GetSection().GetName (), addr.GetOffset()); + addr.GetDescription (strm); + strm.Printf ("\n"); + if (verbose) + sc.GetDescription (strm); + } + else + { + printf ("error: 0x%llx does not resolve to a valid file address in '%s'\n", file_addr, exe_file_path); + } + } + } + + return 0; +} + diff --git a/examples/plugins/commands/fooplugin.cpp b/examples/plugins/commands/fooplugin.cpp new file mode 100644 index 000000000000..2aaf8ff547e3 --- /dev/null +++ b/examples/plugins/commands/fooplugin.cpp @@ -0,0 +1,56 @@ +//===-- fooplugin.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* +An example plugin for LLDB that provides a new foo command with a child subcommand +Compile this into a dylib foo.dylib and load by placing in appropriate locations on disk or +by typing plugin load foo.dylib at the LLDB command line +*/ + +#include +#include +#include + +namespace lldb { + bool + PluginInitialize (lldb::SBDebugger debugger); +} + +class ChildCommand : public lldb::SBCommandPluginInterface +{ +public: + virtual bool + DoExecute (lldb::SBDebugger debugger, + char** command, + lldb::SBCommandReturnObject &result) + { + if (command) + { + const char* arg = *command; + while (arg) + { + result.Printf("%s\n",arg); + arg = *(++command); + } + return true; + } + return false; + } + +}; + +bool +lldb::PluginInitialize (lldb::SBDebugger debugger) +{ + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + lldb::SBCommand foo = interpreter.AddMultiwordCommand("foo",NULL); + foo.AddCommand("child",new ChildCommand(),"a child of foo"); + return true; +} + diff --git a/examples/python/cmdtemplate.py b/examples/python/cmdtemplate.py new file mode 100644 index 000000000000..ca575362f9b7 --- /dev/null +++ b/examples/python/cmdtemplate.py @@ -0,0 +1,76 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +#---------------------------------------------------------------------- + +import lldb +import commands +import optparse +import shlex + +def create_framestats_options(): + usage = "usage: %prog [options]" + description='''This command is meant to be an example of how to make an LLDB command that +does something useful, follows best practices, and exploits the SB API. +Specifically, this command computes the aggregate and average size of the variables in the current frame +and allows you to tweak exactly which variables are to be accounted in the computation. +''' + parser = optparse.OptionParser(description=description, prog='framestats',usage=usage) + parser.add_option('-i', '--in-scope', action='store_true', dest='inscope', help='in_scope_only = True', default=False) + parser.add_option('-a', '--arguments', action='store_true', dest='arguments', help='arguments = True', default=False) + parser.add_option('-l', '--locals', action='store_true', dest='locals', help='locals = True', default=False) + parser.add_option('-s', '--statics', action='store_true', dest='statics', help='statics = True', default=False) + return parser + +def the_framestats_command(debugger, command, result, dict): + # Use the Shell Lexer to properly parse up command options just like a + # shell would + command_args = shlex.split(command) + parser = create_framestats_options() + try: + (options, args) = parser.parse_args(command_args) + except: + # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + result.SetError ("option parsing failed") + return + + # in a command - the lldb.* convenience variables are not to be used + # and their values (if any) are undefined + # this is the best practice to access those objects from within a command + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + if not frame.IsValid(): + return "no frame here" + # from now on, replace lldb..whatever with .whatever + variables_list = frame.GetVariables(options.arguments, options.locals, options.statics, options.inscope) + variables_count = variables_list.GetSize() + if variables_count == 0: + print >> result, "no variables here" + return + total_size = 0 + for i in range(0,variables_count): + variable = variables_list.GetValueAtIndex(i) + variable_type = variable.GetType() + total_size = total_size + variable_type.GetByteSize() + average_size = float(total_size) / variables_count + print >>result, "Your frame has %d variables. Their total size is %d bytes. The average size is %f bytes" % (variables_count,total_size,average_size) + # not returning anything is akin to returning success + +def __lldb_init_module (debugger, dict): + # This initializer is being run from LLDB in the embedded command interpreter + # Make the options so we can generate the help text for the new LLDB + # command line command prior to registering it with LLDB below + parser = create_framestats_options() + the_framestats_command.__doc__ = parser.format_help() + # Add any commands contained in this module to LLDB + debugger.HandleCommand('command script add -f cmdtemplate.the_framestats_command framestats') + print 'The "framestats" command has been installed, type "help framestats" or "framestats --help" for detailed help.' diff --git a/examples/python/crashlog.py b/examples/python/crashlog.py new file mode 100755 index 000000000000..60a6a1f50f00 --- /dev/null +++ b/examples/python/crashlog.py @@ -0,0 +1,829 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb": +# +# cd /path/containing/crashlog.py +# lldb +# (lldb) script import crashlog +# "crashlog" command installed, type "crashlog --help" for detailed help +# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash +# +# The benefit of running the crashlog command inside lldb in the +# embedded python interpreter is when the command completes, there +# will be a target with all of the files loaded at the locations +# described in the crash log. Only the files that have stack frames +# in the backtrace will be loaded unless the "--load-all" option +# has been specified. This allows users to explore the program in the +# state it was in right at crash time. +# +# On MacOSX csh, tcsh: +# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) +# +# On MacOSX sh, bash: +# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash +#---------------------------------------------------------------------- + +import commands +import cmd +import datetime +import glob +import optparse +import os +import platform +import plistlib +import pprint # pp = pprint.PrettyPrinter(indent=4); pp.pprint(command_args) +import re +import shlex +import string +import sys +import time +import uuid + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + +from lldb.utils import symbolication + +PARSE_MODE_NORMAL = 0 +PARSE_MODE_THREAD = 1 +PARSE_MODE_IMAGES = 2 +PARSE_MODE_THREGS = 3 +PARSE_MODE_SYSTEM = 4 + +class CrashLog(symbolication.Symbolicator): + """Class that does parses darwin crash logs""" + parent_process_regex = re.compile('^Parent Process:\s*(.*)\[(\d+)\]'); + thread_state_regex = re.compile('^Thread ([0-9]+) crashed with') + thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)') + app_backtrace_regex = re.compile('^Application Specific Backtrace ([0-9]+)([^:]*):(.*)') + frame_regex = re.compile('^([0-9]+)\s+([^ ]+)\s+(0x[0-9a-fA-F]+) +(.*)') + image_regex_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^<]+)<([-0-9a-fA-F]+)> (.*)'); + image_regex_no_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^/]+)/(.*)'); + empty_line_regex = re.compile('^$') + + class Thread: + """Class that represents a thread in a darwin crash log""" + def __init__(self, index, app_specific_backtrace): + self.index = index + self.frames = list() + self.idents = list() + self.registers = dict() + self.reason = None + self.queue = None + self.app_specific_backtrace = app_specific_backtrace + + def dump(self, prefix): + if self.app_specific_backtrace: + print "%Application Specific Backtrace[%u] %s" % (prefix, self.index, self.reason) + else: + print "%sThread[%u] %s" % (prefix, self.index, self.reason) + if self.frames: + print "%s Frames:" % (prefix) + for frame in self.frames: + frame.dump(prefix + ' ') + if self.registers: + print "%s Registers:" % (prefix) + for reg in self.registers.keys(): + print "%s %-5s = %#16.16x" % (prefix, reg, self.registers[reg]) + + def dump_symbolicated (self, crash_log, options): + this_thread_crashed = self.app_specific_backtrace + if not this_thread_crashed: + this_thread_crashed = self.did_crash() + if options.crashed_only and this_thread_crashed == False: + return + + print "%s" % self + #prev_frame_index = -1 + display_frame_idx = -1 + for frame_idx, frame in enumerate(self.frames): + disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth; + if frame_idx == 0: + symbolicated_frame_addresses = crash_log.symbolicate (frame.pc & crash_log.addr_mask, options.verbose) + else: + # Any frame above frame zero and we have to subtract one to get the previous line entry + symbolicated_frame_addresses = crash_log.symbolicate ((frame.pc & crash_log.addr_mask) - 1, options.verbose) + + if symbolicated_frame_addresses: + symbolicated_frame_address_idx = 0 + for symbolicated_frame_address in symbolicated_frame_addresses: + display_frame_idx += 1 + print '[%3u] %s' % (frame_idx, symbolicated_frame_address) + if (options.source_all or self.did_crash()) and display_frame_idx < options.source_frames and options.source_context: + source_context = options.source_context + line_entry = symbolicated_frame_address.get_symbol_context().line_entry + if line_entry.IsValid(): + strm = lldb.SBStream() + if line_entry: + lldb.debugger.GetSourceManager().DisplaySourceLinesWithLineNumbers(line_entry.file, line_entry.line, source_context, source_context, "->", strm) + source_text = strm.GetData() + if source_text: + # Indent the source a bit + indent_str = ' ' + join_str = '\n' + indent_str + print '%s%s' % (indent_str, join_str.join(source_text.split('\n'))) + if symbolicated_frame_address_idx == 0: + if disassemble: + instructions = symbolicated_frame_address.get_instructions() + if instructions: + print + symbolication.disassemble_instructions (crash_log.get_target(), + instructions, + frame.pc, + options.disassemble_before, + options.disassemble_after, frame.index > 0) + print + symbolicated_frame_address_idx += 1 + else: + print frame + + def add_ident(self, ident): + if not ident in self.idents: + self.idents.append(ident) + + def did_crash(self): + return self.reason != None + + def __str__(self): + if self.app_specific_backtrace: + s = "Application Specific Backtrace[%u]" % self.index + else: + s = "Thread[%u]" % self.index + if self.reason: + s += ' %s' % self.reason + return s + + + class Frame: + """Class that represents a stack frame in a thread in a darwin crash log""" + def __init__(self, index, pc, description): + self.pc = pc + self.description = description + self.index = index + + def __str__(self): + if self.description: + return "[%3u] 0x%16.16x %s" % (self.index, self.pc, self.description) + else: + return "[%3u] 0x%16.16x" % (self.index, self.pc) + + def dump(self, prefix): + print "%s%s" % (prefix, str(self)) + + class DarwinImage(symbolication.Image): + """Class that represents a binary images in a darwin crash log""" + dsymForUUIDBinary = os.path.expanduser('~rc/bin/dsymForUUID') + if not os.path.exists(dsymForUUIDBinary): + dsymForUUIDBinary = commands.getoutput('which dsymForUUID') + + dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') + + def __init__(self, text_addr_lo, text_addr_hi, identifier, version, uuid, path): + symbolication.Image.__init__(self, path, uuid); + self.add_section (symbolication.Section(text_addr_lo, text_addr_hi, "__TEXT")) + self.identifier = identifier + self.version = version + + def locate_module_and_debug_symbols(self): + # Don't load a module twice... + if self.resolved: + return True + # Mark this as resolved so we don't keep trying + self.resolved = True + uuid_str = self.get_normalized_uuid_string() + print 'Getting symbols for %s %s...' % (uuid_str, self.path), + if os.path.exists(self.dsymForUUIDBinary): + dsym_for_uuid_command = '%s %s' % (self.dsymForUUIDBinary, uuid_str) + s = commands.getoutput(dsym_for_uuid_command) + if s: + plist_root = plistlib.readPlistFromString (s) + if plist_root: + plist = plist_root[uuid_str] + if plist: + if 'DBGArchitecture' in plist: + self.arch = plist['DBGArchitecture'] + if 'DBGDSYMPath' in plist: + self.symfile = os.path.realpath(plist['DBGDSYMPath']) + if 'DBGSymbolRichExecutable' in plist: + self.path = os.path.expanduser (plist['DBGSymbolRichExecutable']) + self.resolved_path = self.path + if not self.resolved_path and os.path.exists(self.path): + dwarfdump_cmd_output = commands.getoutput('dwarfdump --uuid "%s"' % self.path) + self_uuid = self.get_uuid() + for line in dwarfdump_cmd_output.splitlines(): + match = self.dwarfdump_uuid_regex.search (line) + if match: + dwarf_uuid_str = match.group(1) + dwarf_uuid = uuid.UUID(dwarf_uuid_str) + if self_uuid == dwarf_uuid: + self.resolved_path = self.path + self.arch = match.group(2) + break; + if not self.resolved_path: + self.unavailable = True + print "error\n error: unable to locate '%s' with UUID %s" % (self.path, uuid_str) + return False + if (self.resolved_path and os.path.exists(self.resolved_path)) or (self.path and os.path.exists(self.path)): + print 'ok' + # if self.resolved_path: + # print ' exe = "%s"' % self.resolved_path + # if self.symfile: + # print ' dsym = "%s"' % self.symfile + return True + else: + self.unavailable = True + return False + + + + def __init__(self, path): + """CrashLog constructor that take a path to a darwin crash log file""" + symbolication.Symbolicator.__init__(self); + self.path = os.path.expanduser(path); + self.info_lines = list() + self.system_profile = list() + self.threads = list() + self.backtraces = list() # For application specific backtraces + self.idents = list() # A list of the required identifiers for doing all stack backtraces + self.crashed_thread_idx = -1 + self.version = -1 + self.error = None + self.target = None + # With possible initial component of ~ or ~user replaced by that user's home directory. + try: + f = open(self.path) + except IOError: + self.error = 'error: cannot open "%s"' % self.path + return + + self.file_lines = f.read().splitlines() + parse_mode = PARSE_MODE_NORMAL + thread = None + app_specific_backtrace = False + for line in self.file_lines: + # print line + line_len = len(line) + if line_len == 0: + if thread: + if parse_mode == PARSE_MODE_THREAD: + if thread.index == self.crashed_thread_idx: + thread.reason = '' + if self.thread_exception: + thread.reason += self.thread_exception + if self.thread_exception_data: + thread.reason += " (%s)" % self.thread_exception_data + if app_specific_backtrace: + self.backtraces.append(thread) + else: + self.threads.append(thread) + thread = None + else: + # only append an extra empty line if the previous line + # in the info_lines wasn't empty + if len(self.info_lines) > 0 and len(self.info_lines[-1]): + self.info_lines.append(line) + parse_mode = PARSE_MODE_NORMAL + # print 'PARSE_MODE_NORMAL' + elif parse_mode == PARSE_MODE_NORMAL: + if line.startswith ('Process:'): + (self.process_name, pid_with_brackets) = line[8:].strip().split(' [') + self.process_id = pid_with_brackets.strip('[]') + elif line.startswith ('Path:'): + self.process_path = line[5:].strip() + elif line.startswith ('Identifier:'): + self.process_identifier = line[11:].strip() + elif line.startswith ('Version:'): + version_string = line[8:].strip() + matched_pair = re.search("(.+)\((.+)\)", version_string) + if matched_pair: + self.process_version = matched_pair.group(1) + self.process_compatability_version = matched_pair.group(2) + else: + self.process = version_string + self.process_compatability_version = version_string + elif self.parent_process_regex.search(line): + parent_process_match = self.parent_process_regex.search(line) + self.parent_process_name = parent_process_match.group(1) + self.parent_process_id = parent_process_match.group(2) + elif line.startswith ('Exception Type:'): + self.thread_exception = line[15:].strip() + continue + elif line.startswith ('Exception Codes:'): + self.thread_exception_data = line[16:].strip() + continue + elif line.startswith ('Crashed Thread:'): + self.crashed_thread_idx = int(line[15:].strip().split()[0]) + continue + elif line.startswith ('Report Version:'): + self.version = int(line[15:].strip()) + continue + elif line.startswith ('System Profile:'): + parse_mode = PARSE_MODE_SYSTEM + continue + elif (line.startswith ('Interval Since Last Report:') or + line.startswith ('Crashes Since Last Report:') or + line.startswith ('Per-App Interval Since Last Report:') or + line.startswith ('Per-App Crashes Since Last Report:') or + line.startswith ('Sleep/Wake UUID:') or + line.startswith ('Anonymous UUID:')): + # ignore these + continue + elif line.startswith ('Thread'): + thread_state_match = self.thread_state_regex.search (line) + if thread_state_match: + app_specific_backtrace = False + thread_state_match = self.thread_regex.search (line) + thread_idx = int(thread_state_match.group(1)) + parse_mode = PARSE_MODE_THREGS + thread = self.threads[thread_idx] + else: + thread_match = self.thread_regex.search (line) + if thread_match: + app_specific_backtrace = False + parse_mode = PARSE_MODE_THREAD + thread_idx = int(thread_match.group(1)) + thread = CrashLog.Thread(thread_idx, False) + continue + elif line.startswith ('Binary Images:'): + parse_mode = PARSE_MODE_IMAGES + continue + elif line.startswith ('Application Specific Backtrace'): + app_backtrace_match = self.app_backtrace_regex.search (line) + if app_backtrace_match: + parse_mode = PARSE_MODE_THREAD + app_specific_backtrace = True + idx = int(app_backtrace_match.group(1)) + thread = CrashLog.Thread(idx, True) + self.info_lines.append(line.strip()) + elif parse_mode == PARSE_MODE_THREAD: + if line.startswith ('Thread'): + continue + frame_match = self.frame_regex.search(line) + if frame_match: + ident = frame_match.group(2) + thread.add_ident(ident) + if not ident in self.idents: + self.idents.append(ident) + thread.frames.append (CrashLog.Frame(int(frame_match.group(1)), int(frame_match.group(3), 0), frame_match.group(4))) + else: + print 'error: frame regex failed for line: "%s"' % line + elif parse_mode == PARSE_MODE_IMAGES: + image_match = self.image_regex_uuid.search (line) + if image_match: + image = CrashLog.DarwinImage (int(image_match.group(1),0), + int(image_match.group(2),0), + image_match.group(3).strip(), + image_match.group(4).strip(), + uuid.UUID(image_match.group(5)), + image_match.group(6)) + self.images.append (image) + else: + image_match = self.image_regex_no_uuid.search (line) + if image_match: + image = CrashLog.DarwinImage (int(image_match.group(1),0), + int(image_match.group(2),0), + image_match.group(3).strip(), + image_match.group(4).strip(), + None, + image_match.group(5)) + self.images.append (image) + else: + print "error: image regex failed for: %s" % line + + elif parse_mode == PARSE_MODE_THREGS: + stripped_line = line.strip() + # "r12: 0x00007fff6b5939c8 r13: 0x0000000007000006 r14: 0x0000000000002a03 r15: 0x0000000000000c00" + reg_values = re.findall ('([a-zA-Z0-9]+: 0[Xx][0-9a-fA-F]+) *', stripped_line); + for reg_value in reg_values: + #print 'reg_value = "%s"' % reg_value + (reg, value) = reg_value.split(': ') + #print 'reg = "%s"' % reg + #print 'value = "%s"' % value + thread.registers[reg.strip()] = int(value, 0) + elif parse_mode == PARSE_MODE_SYSTEM: + self.system_profile.append(line) + f.close() + + def dump(self): + print "Crash Log File: %s" % (self.path) + if self.backtraces: + print "\nApplication Specific Backtraces:" + for thread in self.backtraces: + thread.dump(' ') + print "\nThreads:" + for thread in self.threads: + thread.dump(' ') + print "\nImages:" + for image in self.images: + image.dump(' ') + + def find_image_with_identifier(self, identifier): + for image in self.images: + if image.identifier == identifier: + return image + regex_text = '^.*\.%s$' % (identifier) + regex = re.compile(regex_text) + for image in self.images: + if regex.match(image.identifier): + return image + return None + + def create_target(self): + #print 'crashlog.create_target()...' + if self.target is None: + self.target = symbolication.Symbolicator.create_target(self) + if self.target: + return self.target + # We weren't able to open the main executable as, but we can still symbolicate + print 'crashlog.create_target()...2' + if self.idents: + for ident in self.idents: + image = self.find_image_with_identifier (ident) + if image: + self.target = image.create_target () + if self.target: + return self.target # success + print 'crashlog.create_target()...3' + for image in self.images: + self.target = image.create_target () + if self.target: + return self.target # success + print 'crashlog.create_target()...4' + print 'error: unable to locate any executables from the crash log' + return self.target + + def get_target(self): + return self.target + +def usage(): + print "Usage: lldb-symbolicate.py [-n name] executable-image" + sys.exit(0) + +class Interactive(cmd.Cmd): + '''Interactive prompt for analyzing one or more Darwin crash logs, type "help" to see a list of supported commands.''' + image_option_parser = None + + def __init__(self, crash_logs): + cmd.Cmd.__init__(self) + self.use_rawinput = False + self.intro = 'Interactive crashlogs prompt, type "help" to see a list of supported commands.' + self.crash_logs = crash_logs + self.prompt = '% ' + + def default(self, line): + '''Catch all for unknown command, which will exit the interpreter.''' + print "uknown command: %s" % line + return True + + def do_q(self, line): + '''Quit command''' + return True + + def do_quit(self, line): + '''Quit command''' + return True + + def do_symbolicate(self, line): + description='''Symbolicate one or more darwin crash log files by index to provide source file and line information, + inlined stack frames back to the concrete functions, and disassemble the location of the crash + for the first frame of the crashed thread.''' + option_parser = CreateSymbolicateCrashLogOptions ('symbolicate', description, False) + command_args = shlex.split(line) + try: + (options, args) = option_parser.parse_args(command_args) + except: + return + + if args: + # We have arguments, they must valid be crash log file indexes + for idx_str in args: + idx = int(idx_str) + if idx < len(self.crash_logs): + SymbolicateCrashLog (self.crash_logs[idx], options) + else: + print 'error: crash log index %u is out of range' % (idx) + else: + # No arguments, symbolicate all crash logs using the options provided + for idx in range(len(self.crash_logs)): + SymbolicateCrashLog (self.crash_logs[idx], options) + + def do_list(self, line=None): + '''Dump a list of all crash logs that are currently loaded. + + USAGE: list''' + print '%u crash logs are loaded:' % len(self.crash_logs) + for (crash_log_idx, crash_log) in enumerate(self.crash_logs): + print '[%u] = %s' % (crash_log_idx, crash_log.path) + + def do_image(self, line): + '''Dump information about one or more binary images in the crash log given an image basename, or all images if no arguments are provided.''' + usage = "usage: %prog [options] [PATH ...]" + description='''Dump information about one or more images in all crash logs. The can be a full path, image basename, or partial path. Searches are done in this order.''' + command_args = shlex.split(line) + if not self.image_option_parser: + self.image_option_parser = optparse.OptionParser(description=description, prog='image',usage=usage) + self.image_option_parser.add_option('-a', '--all', action='store_true', help='show all images', default=False) + try: + (options, args) = self.image_option_parser.parse_args(command_args) + except: + return + + if args: + for image_path in args: + fullpath_search = image_path[0] == '/' + for (crash_log_idx, crash_log) in enumerate(self.crash_logs): + matches_found = 0 + for (image_idx, image) in enumerate(crash_log.images): + if fullpath_search: + if image.get_resolved_path() == image_path: + matches_found += 1 + print '[%u] ' % (crash_log_idx), image + else: + image_basename = image.get_resolved_path_basename() + if image_basename == image_path: + matches_found += 1 + print '[%u] ' % (crash_log_idx), image + if matches_found == 0: + for (image_idx, image) in enumerate(crash_log.images): + resolved_image_path = image.get_resolved_path() + if resolved_image_path and string.find(image.get_resolved_path(), image_path) >= 0: + print '[%u] ' % (crash_log_idx), image + else: + for crash_log in self.crash_logs: + for (image_idx, image) in enumerate(crash_log.images): + print '[%u] %s' % (image_idx, image) + return False + + +def interactive_crashlogs(options, args): + crash_log_files = list() + for arg in args: + for resolved_path in glob.glob(arg): + crash_log_files.append(resolved_path) + + crash_logs = list(); + for crash_log_file in crash_log_files: + #print 'crash_log_file = "%s"' % crash_log_file + crash_log = CrashLog(crash_log_file) + if crash_log.error: + print crash_log.error + continue + if options.debug: + crash_log.dump() + if not crash_log.images: + print 'error: no images in crash log "%s"' % (crash_log) + continue + else: + crash_logs.append(crash_log) + + interpreter = Interactive(crash_logs) + # List all crash logs that were imported + interpreter.do_list() + interpreter.cmdloop() + + +def save_crashlog(debugger, command, result, dict): + usage = "usage: %prog [options] " + description='''Export the state of current target into a crashlog file''' + parser = optparse.OptionParser(description=description, prog='save_crashlog',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(shlex.split(command)) + except: + result.PutCString ("error: invalid options"); + return + if len(args) != 1: + result.PutCString ("error: invalid arguments, a single output file is the only valid argument") + return + out_file = open(args[0], 'w') + if not out_file: + result.PutCString ("error: failed to open file '%s' for writing...", args[0]); + return + target = debugger.GetSelectedTarget() + if target: + identifier = target.executable.basename + if lldb.process: + pid = lldb.process.id + if pid != lldb.LLDB_INVALID_PROCESS_ID: + out_file.write('Process: %s [%u]\n' % (identifier, pid)) + out_file.write('Path: %s\n' % (target.executable.fullpath)) + out_file.write('Identifier: %s\n' % (identifier)) + out_file.write('\nDate/Time: %s\n' % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + out_file.write('OS Version: Mac OS X %s (%s)\n' % (platform.mac_ver()[0], commands.getoutput('sysctl -n kern.osversion'))); + out_file.write('Report Version: 9\n') + for thread_idx in range(lldb.process.num_threads): + thread = lldb.process.thread[thread_idx] + out_file.write('\nThread %u:\n' % (thread_idx)) + for (frame_idx, frame) in enumerate(thread.frames): + frame_pc = frame.pc + frame_offset = 0 + if frame.function: + block = frame.GetFrameBlock() + block_range = block.range[frame.addr] + if block_range: + block_start_addr = block_range[0] + frame_offset = frame_pc - block_start_addr.load_addr + else: + frame_offset = frame_pc - frame.function.addr.load_addr + elif frame.symbol: + frame_offset = frame_pc - frame.symbol.addr.load_addr + out_file.write('%-3u %-32s 0x%16.16x %s' % (frame_idx, frame.module.file.basename, frame_pc, frame.name)) + if frame_offset > 0: + out_file.write(' + %u' % (frame_offset)) + line_entry = frame.line_entry + if line_entry: + if options.verbose: + # This will output the fullpath + line + column + out_file.write(' %s' % (line_entry)) + else: + out_file.write(' %s:%u' % (line_entry.file.basename, line_entry.line)) + column = line_entry.column + if column: + out_file.write(':%u' % (column)) + out_file.write('\n') + + out_file.write('\nBinary Images:\n') + for module in target.modules: + text_segment = module.section['__TEXT'] + if text_segment: + text_segment_load_addr = text_segment.GetLoadAddress(target) + if text_segment_load_addr != lldb.LLDB_INVALID_ADDRESS: + text_segment_end_load_addr = text_segment_load_addr + text_segment.size + identifier = module.file.basename + module_version = '???' + module_version_array = module.GetVersion() + if module_version_array: + module_version = '.'.join(map(str,module_version_array)) + out_file.write (' 0x%16.16x - 0x%16.16x %s (%s - ???) <%s> %s\n' % (text_segment_load_addr, text_segment_end_load_addr, identifier, module_version, module.GetUUIDString(), module.file.fullpath)) + out_file.close() + else: + result.PutCString ("error: invalid target"); + + +def Symbolicate(debugger, command, result, dict): + try: + SymbolicateCrashLogs (shlex.split(command)) + except: + result.PutCString ("error: python exception %s" % sys.exc_info()[0]) + +def SymbolicateCrashLog(crash_log, options): + if crash_log.error: + print crash_log.error + return + if options.debug: + crash_log.dump() + if not crash_log.images: + print 'error: no images in crash log' + return + + if options.dump_image_list: + print "Binary Images:" + for image in crash_log.images: + if options.verbose: + print image.debug_dump() + else: + print image + + target = crash_log.create_target () + if not target: + return + exe_module = target.GetModuleAtIndex(0) + images_to_load = list() + loaded_images = list() + if options.load_all_images: + # --load-all option was specified, load everything up + for image in crash_log.images: + images_to_load.append(image) + else: + # Only load the images found in stack frames for the crashed threads + if options.crashed_only: + for thread in crash_log.threads: + if thread.did_crash(): + for ident in thread.idents: + images = crash_log.find_images_with_identifier (ident) + if images: + for image in images: + images_to_load.append(image) + else: + print 'error: can\'t find image for identifier "%s"' % ident + else: + for ident in crash_log.idents: + images = crash_log.find_images_with_identifier (ident) + if images: + for image in images: + images_to_load.append(image) + else: + print 'error: can\'t find image for identifier "%s"' % ident + + for image in images_to_load: + if not image in loaded_images: + err = image.add_module (target) + if err: + print err + else: + #print 'loaded %s' % image + loaded_images.append(image) + + if crash_log.backtraces: + for thread in crash_log.backtraces: + thread.dump_symbolicated (crash_log, options) + print + + for thread in crash_log.threads: + thread.dump_symbolicated (crash_log, options) + print + + +def CreateSymbolicateCrashLogOptions(command_name, description, add_interactive_options): + usage = "usage: %prog [options] [FILE ...]" + option_parser = optparse.OptionParser(description=description, prog='crashlog',usage=usage) + option_parser.add_option('--verbose' , '-v', action='store_true', dest='verbose', help='display verbose debug info', default=False) + option_parser.add_option('--debug' , '-g', action='store_true', dest='debug', help='display verbose debug logging', default=False) + option_parser.add_option('--load-all' , '-a', action='store_true', dest='load_all_images', help='load all executable images, not just the images found in the crashed stack frames', default=False) + option_parser.add_option('--images' , action='store_true', dest='dump_image_list', help='show image list', default=False) + option_parser.add_option('--debug-delay' , type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0) + option_parser.add_option('--crashed-only' , '-c', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False) + option_parser.add_option('--disasm-depth' , '-d', type='int', dest='disassemble_depth', help='set the depth in stack frames that should be disassembled (default is 1)', default=1) + option_parser.add_option('--disasm-all' , '-D', action='store_true', dest='disassemble_all_threads', help='enabled disassembly of frames on all threads (not just the crashed thread)', default=False) + option_parser.add_option('--disasm-before' , '-B', type='int', dest='disassemble_before', help='the number of instructions to disassemble before the frame PC', default=4) + option_parser.add_option('--disasm-after' , '-A', type='int', dest='disassemble_after', help='the number of instructions to disassemble after the frame PC', default=4) + option_parser.add_option('--source-context', '-C', type='int', metavar='NLINES', dest='source_context', help='show NLINES source lines of source context (default = 4)', default=4) + option_parser.add_option('--source-frames' , type='int', metavar='NFRAMES', dest='source_frames', help='show source for NFRAMES (default = 4)', default=4) + option_parser.add_option('--source-all' , action='store_true', dest='source_all', help='show source for all threads, not just the crashed thread', default=False) + if add_interactive_options: + option_parser.add_option('-i', '--interactive', action='store_true', help='parse all crash logs and enter interactive mode', default=False) + return option_parser + +def SymbolicateCrashLogs(command_args): + description='''Symbolicate one or more darwin crash log files to provide source file and line information, +inlined stack frames back to the concrete functions, and disassemble the location of the crash +for the first frame of the crashed thread. +If this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter +for use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been +created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows +you to explore the program as if it were stopped at the locations described in the crash log and functions can +be disassembled and lookups can be performed using the addresses found in the crash log.''' + option_parser = CreateSymbolicateCrashLogOptions ('crashlog', description, True) + try: + (options, args) = option_parser.parse_args(command_args) + except: + return + + if options.debug: + print 'command_args = %s' % command_args + print 'options', options + print 'args', args + + if options.debug_delay > 0: + print "Waiting %u seconds for debugger to attach..." % options.debug_delay + time.sleep(options.debug_delay) + error = lldb.SBError() + + if args: + if options.interactive: + interactive_crashlogs(options, args) + else: + for crash_log_file in args: + crash_log = CrashLog(crash_log_file) + SymbolicateCrashLog (crash_log, options) +if __name__ == '__main__': + # Create a new debugger instance + lldb.debugger = lldb.SBDebugger.Create() + SymbolicateCrashLogs (sys.argv[1:]) + lldb.SBDebugger.Destroy (lldb.debugger) +elif getattr(lldb, 'debugger', None): + lldb.debugger.HandleCommand('command script add -f lldb.macosx.crashlog.Symbolicate crashlog') + lldb.debugger.HandleCommand('command script add -f lldb.macosx.crashlog.save_crashlog save_crashlog') + print '"crashlog" and "save_crashlog" command installed, use the "--help" option for detailed help' + diff --git a/examples/python/delta.py b/examples/python/delta.py new file mode 100755 index 000000000000..e470de536d85 --- /dev/null +++ b/examples/python/delta.py @@ -0,0 +1,115 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# This module will enable GDB remote packet logging when the +# 'start_gdb_log' command is called with a filename to log to. When the +# 'stop_gdb_log' command is called, it will disable the logging and +# print out statistics about how long commands took to execute and also +# will primnt ou +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command. This can be done from the LLDB command line: +# (lldb) command script import /path/to/gdbremote.py +# Or it can be added to your ~/.lldbinit file so this module is always +# available. +#---------------------------------------------------------------------- + +import commands +import optparse +import os +import shlex +import re +import tempfile + +def start_gdb_log(debugger, command, result, dict): + '''Start logging GDB remote packets by enabling logging with timestamps and + thread safe logging. Follow a call to this function with a call to "stop_gdb_log" + in order to dump out the commands.''' + global log_file + if log_file: + result.PutCString ('error: logging is already in progress with file "%s"', log_file) + else: + args_len = len(args) + if args_len == 0: + log_file = tempfile.mktemp() + elif len(args) == 1: + log_file = args[0] + + if log_file: + debugger.HandleCommand('log enable --threadsafe --timestamp --file "%s" gdb-remote packets' % log_file); + result.PutCString ("GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." % log_file) + return + + result.PutCString ('error: invalid log file path') + result.PutCString (usage) + +def parse_time_log(debugger, command, result, dict): + # Any commands whose names might be followed by more valid C identifier + # characters must be listed here + command_args = shlex.split(command) + parse_time_log_args (command_args) + +def parse_time_log_args(command_args): + usage = "usage: parse_time_log [options] []" + description='''Parse a log file that contains timestamps and convert the timestamps to delta times between log lines.''' + parser = optparse.OptionParser(description=description, prog='parse_time_log',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + for log_file in args: + parse_log_file (log_file, options) + +def parse_log_file(file, options): + '''Parse a log file that was contains timestamps. These logs are typically + generated using: + (lldb) log enable --threadsafe --timestamp --file .... + + This log file will contain timestamps and this function will then normalize + those packets to be relative to the first value timestamp that is found and + show delta times between log lines and also keep track of how long it takes + for GDB remote commands to make a send/receive round trip. This can be + handy when trying to figure out why some operation in the debugger is taking + a long time during a preset set of debugger commands.''' + + print '#----------------------------------------------------------------------' + print "# Log file: '%s'" % file + print '#----------------------------------------------------------------------' + + timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$') + + base_time = 0.0 + last_time = 0.0 + file = open(file) + lines = file.read().splitlines() + for line in lines: + match = timestamp_regex.match (line) + if match: + curr_time = float (match.group(2)) + delta = 0.0 + if base_time: + delta = curr_time - last_time + else: + base_time = curr_time + + print '%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3)) + last_time = curr_time + else: + print line + + + +if __name__ == '__main__': + import sys + parse_time_log_args (sys.argv[1:]) + +else: + import lldb + if lldb.debugger: + # This initializer is being run from LLDB in the embedded command interpreter + # Add any commands contained in this module to LLDB + lldb.debugger.HandleCommand('command script add -f delta.parse_time_log parse_time_log') + print 'The "parse_time_log" command is now installed and ready for use, type "parse_time_log --help" for more information' diff --git a/examples/python/diagnose_nsstring.py b/examples/python/diagnose_nsstring.py new file mode 100644 index 000000000000..aca5c7f220fc --- /dev/null +++ b/examples/python/diagnose_nsstring.py @@ -0,0 +1,171 @@ +# This implements the "diagnose-nsstring" command, usually installed in the debug session like +# command script import lldb.diagnose +# it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the +# decisions it did and providing some useful context information that can be used for improving the formatter + +import lldb + +def read_memory(process,location,size): + data = "" + error = lldb.SBError() + for x in range(0,size-1): + byte = process.ReadUnsignedFromMemory(x+location,1,error) + if error.fail: + data = data + "err%s" % "" if x == size-2 else ":" + else: + try: + data = data + "0x%x" % byte + if byte == 0: + data = data + "(\\0)" + elif byte == 0xa: + data = data + "(\\a)" + elif byte == 0xb: + data = data + "(\\b)" + elif byte == 0xc: + data = data + "(\\c)" + elif byte == '\n': + data = data + "(\\n)" + else: + data = data + "(%s)" % chr(byte) + if x < size-2: + data = data + ":" + except Exception as e: + print e + return data + +def diagnose_nsstring_Command_Impl(debugger,command,result,internal_dict): + """ + A command to diagnose the LLDB NSString data formatter + invoke as + (lldb) diagnose-nsstring + e.g. + (lldb) diagnose-nsstring @"Hello world" + """ + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + if not target.IsValid() or not process.IsValid(): + return "unable to get target/process - cannot proceed" + options = lldb.SBExpressionOptions() + options.SetFetchDynamicValue() + error = lldb.SBError() + if frame.IsValid(): + nsstring = frame.EvaluateExpression(command,options) + else: + nsstring = target.EvaluateExpression(command,options) + print >>result,str(nsstring) + nsstring_address = nsstring.GetValueAsUnsigned(0) + if nsstring_address == 0: + return "unable to obtain the string - cannot proceed" + expression = "\ +struct $__lldb__notInlineMutable {\ + char* buffer;\ + signed long length;\ + signed long capacity;\ + unsigned int hasGap:1;\ + unsigned int isFixedCapacity:1;\ + unsigned int isExternalMutable:1;\ + unsigned int capacityProvidedExternally:1;\n\ +#if __LP64__\n\ + unsigned long desiredCapacity:60;\n\ +#else\n\ + unsigned long desiredCapacity:28;\n\ +#endif\n\ + void* contentsAllocator;\ +};\ +\ +struct $__lldb__CFString {\ + void* _cfisa;\ + uint8_t _cfinfo[4];\ + uint32_t _rc;\ + union {\ + struct __inline1 {\ + signed long length;\ + } inline1;\ + struct __notInlineImmutable1 {\ + char* buffer;\ + signed long length;\ + void* contentsDeallocator;\ + } notInlineImmutable1;\ + struct __notInlineImmutable2 {\ + char* buffer;\ + void* contentsDeallocator;\ + } notInlineImmutable2;\ + struct $__lldb__notInlineMutable notInlineMutable;\ + } variants;\ +};\ +" + + expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address + # print expression + dumped = target.EvaluateExpression(expression,options) + print >>result, str(dumped) + + little_endian = (target.byte_order == lldb.eByteOrderLittle) + ptr_size = target.addr_size + + info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex(0 if little_endian else 3).GetValueAsUnsigned(0) + is_mutable = (info_bits & 1) == 1 + is_inline = (info_bits & 0x60) == 0 + has_explicit_length = (info_bits & (1 | 4)) != 4 + is_unicode = (info_bits & 0x10) == 0x10 + is_special = (nsstring.GetDynamicValue(lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2") + has_null = (info_bits & 8) == 8 + + print >>result,"\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \ + (info_bits, "yes" if is_mutable else "no","yes" if is_inline else "no","yes" if has_explicit_length else "no","yes" if is_unicode else "no","yes" if is_special else "no","yes" if has_null else "no") + + + explicit_length_offset = 0 + if not has_null and has_explicit_length and not is_special: + explicit_length_offset = 2*ptr_size + if is_mutable and not is_inline: + explicit_length_offset = explicit_length_offset + ptr_size + elif is_inline: + pass + elif not is_inline and not is_mutable: + explicit_length_offset = explicit_length_offset + ptr_size + else: + explicit_length_offset = 0 + + if explicit_length_offset == 0: + print >>result,"There is no explicit length marker - skipping this step\n" + else: + explicit_length_offset = nsstring_address + explicit_length_offset + explicit_length = process.ReadUnsignedFromMemory(explicit_length_offset, 4, error) + print >>result,"Explicit length location is at 0x%x - read value is %d\n" % (explicit_length_offset,explicit_length) + + if is_mutable: + location = 2 * ptr_size + nsstring_address + location = process.ReadPointerFromMemory(location,error) + elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable: + location = 3 * ptr_size + nsstring_address + elif is_unicode: + location = 2 * ptr_size + nsstring_address + if is_inline: + if not has_explicit_length: + print >>result,"Unicode & Inline & !Explicit is a new combo - no formula for it" + else: + location += ptr_size + else: + location = process.ReadPointerFromMemory(location,error) + elif is_special: + location = nsstring_address + ptr_size + 4 + elif is_inline: + location = 2 * ptr_size + nsstring_address + if not has_explicit_length: + location += 1 + else: + location = 2 * ptr_size + nsstring_address + location = process.ReadPointerFromMemory(location,error) + print >>result,"Expected data location: 0x%x\n" % (location) + print >>result,"1K of data around location: %s\n" % read_memory(process,location,1024) + print >>result,"5K of data around string pointer: %s\n" % read_memory(process,nsstring_address,1024*5) + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand("command script add -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" % __name__) + print 'The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.' + +__lldb_init_module(lldb.debugger,None) +__lldb_init_module = None \ No newline at end of file diff --git a/examples/python/diagnose_unwind.py b/examples/python/diagnose_unwind.py new file mode 100644 index 000000000000..e977c4ed1b0f --- /dev/null +++ b/examples/python/diagnose_unwind.py @@ -0,0 +1,270 @@ +# This implements the "diagnose-unwind" command, usually installed +# in the debug session like +# command script import lldb.diagnose +# it is used when lldb's backtrace fails -- it collects and prints +# information about the stack frames, and tries an alternate unwind +# algorithm, that will help to understand why lldb's unwind algorithm +# did not succeed. + +import optparse +import lldb +import re +import shlex + +# Print the frame number, pc, frame pointer, module UUID and function name +# Returns the SBModule that contains the PC, if it could be found +def backtrace_print_frame (target, frame_num, addr, fp): + process = target.GetProcess() + addr_for_printing = addr + addr_width = process.GetAddressByteSize() * 2 + if frame_num > 0: + addr = addr - 1 + + sbaddr = lldb.SBAddress() + try: + sbaddr.SetLoadAddress(addr, target) + module_description = "" + if sbaddr.GetModule(): + module_filename = "" + module_uuid_str = sbaddr.GetModule().GetUUIDString() + if module_uuid_str == None: + module_uuid_str = "" + if sbaddr.GetModule().GetFileSpec(): + module_filename = sbaddr.GetModule().GetFileSpec().GetFilename() + if module_filename == None: + module_filename = "" + if module_uuid_str != "" or module_filename != "": + module_description = '%s %s' % (module_filename, module_uuid_str) + except Exception: + print '%2d: pc==0x%-*x fp==0x%-*x' % (frame_num, addr_width, addr_for_printing, addr_width, fp) + return + + sym_ctx = target.ResolveSymbolContextForAddress(sbaddr, lldb.eSymbolContextEverything) + if sym_ctx.IsValid() and sym_ctx.GetSymbol().IsValid(): + function_start = sym_ctx.GetSymbol().GetStartAddress().GetLoadAddress(target) + offset = addr - function_start + print '%2d: pc==0x%-*x fp==0x%-*x %s %s + %d' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description, sym_ctx.GetSymbol().GetName(), offset) + else: + print '%2d: pc==0x%-*x fp==0x%-*x %s' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description) + return sbaddr.GetModule() + +# A simple stack walk algorithm that follows the frame chain. +# Returns a two-element list; the first element is a list of modules +# seen and the second element is a list of addresses seen during the backtrace. +def simple_backtrace(debugger): + target = debugger.GetSelectedTarget() + process = target.GetProcess() + cur_thread = process.GetSelectedThread() + + initial_fp = cur_thread.GetFrameAtIndex(0).GetFP() + + # If the pseudoreg "fp" isn't recognized, on arm hardcode to r7 which is correct for Darwin programs. + if initial_fp == lldb.LLDB_INVALID_ADDRESS and target.triple[0:3] == "arm": + for reggroup in cur_thread.GetFrameAtIndex(1).registers: + if reggroup.GetName() == "General Purpose Registers": + for reg in reggroup: + if reg.GetName() == "r7": + initial_fp = int (reg.GetValue(), 16) + + module_list = [] + address_list = [cur_thread.GetFrameAtIndex(0).GetPC()] + this_module = backtrace_print_frame (target, 0, cur_thread.GetFrameAtIndex(0).GetPC(), initial_fp) + print_stack_frame (process, initial_fp) + print "" + if this_module != None: + module_list.append (this_module) + if cur_thread.GetNumFrames() < 2: + return [module_list, address_list] + + cur_fp = process.ReadPointerFromMemory (initial_fp, lldb.SBError()) + cur_pc = process.ReadPointerFromMemory (initial_fp + process.GetAddressByteSize(), lldb.SBError()) + + frame_num = 1 + + while cur_pc != 0 and cur_fp != 0 and cur_pc != lldb.LLDB_INVALID_ADDRESS and cur_fp != lldb.LLDB_INVALID_ADDRESS: + address_list.append (cur_pc) + this_module = backtrace_print_frame (target, frame_num, cur_pc, cur_fp) + print_stack_frame (process, cur_fp) + print "" + if this_module != None: + module_list.append (this_module) + frame_num = frame_num + 1 + next_pc = 0 + next_fp = 0 + if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386" or target.triple[0:3] == "arm": + error = lldb.SBError() + next_pc = process.ReadPointerFromMemory(cur_fp + process.GetAddressByteSize(), error) + if not error.Success(): + next_pc = 0 + next_fp = process.ReadPointerFromMemory(cur_fp, error) + if not error.Success(): + next_fp = 0 + # Clear the 0th bit for arm frames - this indicates it is a thumb frame + if target.triple[0:3] == "arm" and (next_pc & 1) == 1: + next_pc = next_pc & ~1 + cur_pc = next_pc + cur_fp = next_fp + this_module = backtrace_print_frame (target, frame_num, cur_pc, cur_fp) + print_stack_frame (process, cur_fp) + print "" + if this_module != None: + module_list.append (this_module) + return [module_list, address_list] + +def print_stack_frame(process, fp): + if fp == 0 or fp == lldb.LLDB_INVALID_ADDRESS or fp == 1: + return + addr_size = process.GetAddressByteSize() + addr = fp - (2 * addr_size) + i = 0 + outline = "Stack frame from $fp-%d: " % (2 * addr_size) + error = lldb.SBError() + try: + while i < 5 and error.Success(): + address = process.ReadPointerFromMemory(addr + (i * addr_size), error) + outline += " 0x%x" % address + i += 1 + print outline + except Exception: + return + +def diagnose_unwind(debugger, command, result, dict): + """ +Gather diagnostic information to help debug incorrect unwind (backtrace) +behavior in lldb. When there is a backtrace that doesn't look +correct, run this command with the correct thread selected and a +large amount of diagnostic information will be printed, it is likely +to be helpful when reporting the problem. + """ + + command_args = shlex.split(command) + parser = create_diagnose_unwind_options() + try: + (options, args) = parser.parse_args(command_args) + except: + return + target = debugger.GetSelectedTarget() + if target: + process = target.GetProcess() + if process: + thread = process.GetSelectedThread() + if thread: + lldb_versions_match = re.search(r'[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?', debugger.GetVersionString()) + lldb_version = 0 + lldb_minor = 0 + if len(lldb_versions_match.groups()) >= 1 and lldb_versions_match.groups()[0]: + lldb_major = int(lldb_versions_match.groups()[0]) + if len(lldb_versions_match.groups()) >= 5 and lldb_versions_match.groups()[4]: + lldb_minor = int(lldb_versions_match.groups()[4]) + + modules_seen = [] + addresses_seen = [] + + print 'LLDB version %s' % debugger.GetVersionString() + print 'Unwind diagnostics for thread %d' % thread.GetIndexID() + print "" + print "=============================================================================================" + print "" + print "OS plugin setting:" + debugger.HandleCommand("settings show target.process.python-os-plugin-path") + print "" + print "Live register context:" + thread.SetSelectedFrame(0) + debugger.HandleCommand("register read") + print "" + print "=============================================================================================" + print "" + print "lldb's unwind algorithm:" + print "" + frame_num = 0 + for frame in thread.frames: + if not frame.IsInlined(): + this_module = backtrace_print_frame (target, frame_num, frame.GetPC(), frame.GetFP()) + print_stack_frame (process, frame.GetFP()) + print "" + if this_module != None: + modules_seen.append (this_module) + addresses_seen.append (frame.GetPC()) + frame_num = frame_num + 1 + print "" + print "=============================================================================================" + print "" + print "Simple stack walk algorithm:" + print "" + (module_list, address_list) = simple_backtrace(debugger) + if module_list and module_list != None: + modules_seen += module_list + if address_list and address_list != None: + addresses_seen = set(addresses_seen) + addresses_seen.update(set(address_list)) + + print "" + print "=============================================================================================" + print "" + print "Modules seen in stack walks:" + print "" + modules_already_seen = set() + for module in modules_seen: + if module != None and module.GetFileSpec().GetFilename() != None: + if not module.GetFileSpec().GetFilename() in modules_already_seen: + debugger.HandleCommand('image list %s' % module.GetFileSpec().GetFilename()) + modules_already_seen.add(module.GetFileSpec().GetFilename()) + + print "" + print "=============================================================================================" + print "" + print "Disassembly ofaddresses seen in stack walks:" + print "" + additional_addresses_to_disassemble = addresses_seen + for frame in thread.frames: + if not frame.IsInlined(): + print "--------------------------------------------------------------------------------------" + print "" + print "Disassembly of %s, frame %d, address 0x%x" % (frame.GetFunctionName(), frame.GetFrameID(), frame.GetPC()) + print "" + if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386": + debugger.HandleCommand('disassemble -F att -a 0x%x' % frame.GetPC()) + else: + debugger.HandleCommand('disassemble -a 0x%x' % frame.GetPC()) + if frame.GetPC() in additional_addresses_to_disassemble: + additional_addresses_to_disassemble.remove (frame.GetPC()) + + for address in list(additional_addresses_to_disassemble): + print "--------------------------------------------------------------------------------------" + print "" + print "Disassembly of 0x%x" % address + print "" + if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386": + debugger.HandleCommand('disassemble -F att -a 0x%x' % address) + else: + debugger.HandleCommand('disassemble -a 0x%x' % address) + + print "" + print "=============================================================================================" + print "" + additional_addresses_to_show_unwind = addresses_seen + for frame in thread.frames: + if not frame.IsInlined(): + print "--------------------------------------------------------------------------------------" + print "" + print "Unwind instructions for %s, frame %d" % (frame.GetFunctionName(), frame.GetFrameID()) + print "" + debugger.HandleCommand('image show-unwind -a "0x%x"' % frame.GetPC()) + if frame.GetPC() in additional_addresses_to_show_unwind: + additional_addresses_to_show_unwind.remove (frame.GetPC()) + + for address in list(additional_addresses_to_show_unwind): + print "--------------------------------------------------------------------------------------" + print "" + print "Unwind instructions for 0x%x" % address + print "" + debugger.HandleCommand('image show-unwind -a "0x%x"' % address) + +def create_diagnose_unwind_options(): + usage = "usage: %prog" + description='''Print diagnostic information about a thread backtrace which will help to debug unwind problems''' + parser = optparse.OptionParser(description=description, prog='diagnose_unwind',usage=usage) + return parser + +lldb.debugger.HandleCommand('command script add -f %s.diagnose_unwind diagnose-unwind' % __name__) +print 'The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.' diff --git a/examples/python/dict_utils.py b/examples/python/dict_utils.py new file mode 100755 index 000000000000..7dc5e7a8b56e --- /dev/null +++ b/examples/python/dict_utils.py @@ -0,0 +1,61 @@ + +class LookupDictionary(dict): + """ + a dictionary which can lookup value by key, or keys by value + """ + def __init__(self, items=[]): + """items can be a list of pair_lists or a dictionary""" + dict.__init__(self, items) + + def get_keys_for_value(self, value, fail_value = None): + """find the key(s) as a list given a value""" + list_result = [item[0] for item in self.items() if item[1] == value] + if len(list_result) > 0: + return list_result + return fail_value + + def get_first_key_for_value(self, value, fail_value = None): + """return the first key of this dictionary given the value""" + list_result = [item[0] for item in self.items() if item[1] == value] + if len(list_result) > 0: + return list_result[0] + return fail_value + + def get_value(self, key, fail_value = None): + """find the value given a key""" + if key in self: + return self[key] + return fail_value + + +class Enum(LookupDictionary): + + def __init__(self, initial_value=0, items=[]): + """items can be a list of pair_lists or a dictionary""" + LookupDictionary.__init__(self, items) + self.value = initial_value + + def set_value(self, v): + v_typename = typeof(v).__name__ + if v_typename == 'str': + if str in self: + v = self[v] + else: + v = 0 + else: + self.value = v + + def get_enum_value(self): + return self.value + + def get_enum_name(self): + return self.__str__() + + def __str__(self): + s = self.get_first_key_for_value (self.value, None) + if s == None: + s = "%#8.8x" % self.value + return s + + def __repr__(self): + return self.__str__() \ No newline at end of file diff --git a/examples/python/disasm-stress-test.py b/examples/python/disasm-stress-test.py new file mode 100755 index 000000000000..5aa354dc24cb --- /dev/null +++ b/examples/python/disasm-stress-test.py @@ -0,0 +1,168 @@ +#!/usr/bin/python + +import argparse, datetime, re, subprocess, sys, time + +parser = argparse.ArgumentParser(description="Run an exhaustive test of the LLDB disassembler for a specific architecture.") + +parser.add_argument('--arch', required=True, action='store', help='The architecture whose disassembler is to be tested') +parser.add_argument('--bytes', required=True, action='store', type=int, help='The byte width of instructions for that architecture') +parser.add_argument('--random', required=False, action='store_true', help='Enables non-sequential testing') +parser.add_argument('--start', required=False, action='store', type=int, help='The first instruction value to test') +parser.add_argument('--skip', required=False, action='store', type=int, help='The interval between instructions to test') +parser.add_argument('--log', required=False, action='store', help='A log file to write the most recent instruction being tested') +parser.add_argument('--time', required=False, action='store_true', help='Every 100,000 instructions, print an ETA to standard out') +parser.add_argument('--lldb', required=False, action='store', help='The path to LLDB.framework, if LLDB should be overridden') + +arguments = sys.argv[1:] + +arg_ns = parser.parse_args(arguments) + +def AddLLDBToSysPathOnMacOSX(): + def GetLLDBFrameworkPath(): + lldb_path = subprocess.check_output(["xcrun", "-find", "lldb"]) + re_result = re.match("(.*)/Developer/usr/bin/lldb", lldb_path) + if re_result == None: + return None + xcode_contents_path = re_result.group(1) + return xcode_contents_path + "/SharedFrameworks/LLDB.framework" + + lldb_framework_path = GetLLDBFrameworkPath() + + if lldb_framework_path == None: + print "Couldn't find LLDB.framework" + sys.exit(-1) + + sys.path.append(lldb_framework_path + "/Resources/Python") + +if arg_ns.lldb == None: + AddLLDBToSysPathOnMacOSX() +else: + sys.path.append(arg_ns.lldb + "/Resources/Python") + +import lldb + +debugger = lldb.SBDebugger.Create() + +if debugger.IsValid() == False: + print "Couldn't create an SBDebugger" + sys.exit(-1) + +target = debugger.CreateTargetWithFileAndArch(None, arg_ns.arch) + +if target.IsValid() == False: + print "Couldn't create an SBTarget for architecture " + arg_ns.arch + sys.exit(-1) + +def ResetLogFile(log_file): + if log_file != sys.stdout: + log_file.seek(0) + +def PrintByteArray(log_file, byte_array): + for byte in byte_array: + print >>log_file, hex(byte) + " ", + print >>log_file + +class SequentialInstructionProvider: + def __init__(self, byte_width, log_file, start=0, skip=1): + self.m_byte_width = byte_width + self.m_log_file = log_file + self.m_start = start + self.m_skip = skip + self.m_value = start + self.m_last = (1 << (byte_width * 8)) - 1 + def PrintCurrentState(self, ret): + ResetLogFile(self.m_log_file) + print >>self.m_log_file, self.m_value + PrintByteArray(self.m_log_file, ret) + def GetNextInstruction(self): + if self.m_value > self.m_last: + return None + ret = bytearray(self.m_byte_width) + for i in range(self.m_byte_width): + ret[self.m_byte_width - (i + 1)] = (self.m_value >> (i * 8)) & 255 + self.PrintCurrentState(ret) + self.m_value += self.m_skip + return ret + def GetNumInstructions(self): + return (self.m_last - self.m_start) / self.m_skip + def __iter__(self): + return self + def next(self): + ret = self.GetNextInstruction() + if ret == None: + raise StopIteration + return ret + +class RandomInstructionProvider: + def __init__(self, byte_width, log_file): + self.m_byte_width = byte_width + self.m_log_file = log_file + self.m_random_file = open("/dev/random", 'r') + def PrintCurrentState(self, ret): + ResetLogFile(self.m_log_file) + PrintByteArray(self.m_log_file, ret) + def GetNextInstruction(self): + ret = bytearray(self.m_byte_width) + for i in range(self.m_byte_width): + ret[i] = self.m_random_file.read(1) + self.PrintCurrentState(ret) + return ret + def __iter__(self): + return self + def next(self): + ret = self.GetNextInstruction() + if ret == None: + raise StopIteration + return ret + +log_file = None + +def GetProviderWithArguments(args): + global log_file + if args.log != None: + log_file = open(args.log, 'w') + else: + log_file = sys.stdout + instruction_provider = None + if args.random == True: + instruction_provider = RandomInstructionProvider(args.bytes, log_file) + else: + start = 0 + skip = 1 + if args.start != None: + start = args.start + if args.skip != None: + skip = args.skip + instruction_provider = SequentialInstructionProvider(args.bytes, log_file, start, skip) + return instruction_provider + +instruction_provider = GetProviderWithArguments(arg_ns) + +fake_address = lldb.SBAddress() + +actually_time = arg_ns.time and not arg_ns.random + +if actually_time: + num_instructions_logged = 0 + total_num_instructions = instruction_provider.GetNumInstructions() + start_time = time.time() + +for inst_bytes in instruction_provider: + if actually_time: + if (num_instructions_logged != 0) and (num_instructions_logged % 100000 == 0): + curr_time = time.time() + elapsed_time = curr_time - start_time + remaining_time = float(total_num_instructions - num_instructions_logged) * (float(elapsed_time) / float(num_instructions_logged)) + print str(datetime.timedelta(seconds=remaining_time)) + num_instructions_logged = num_instructions_logged + 1 + inst_list = target.GetInstructions(fake_address, inst_bytes) + if not inst_list.IsValid(): + print >>log_file, "Invalid instruction list" + continue + inst = inst_list.GetInstructionAtIndex(0) + if not inst.IsValid(): + print >>log_file, "Invalid instruction" + continue + instr_output_stream = lldb.SBStream() + inst.GetDescription(instr_output_stream) + print >>log_file, instr_output_stream.GetData() diff --git a/examples/python/disasm.py b/examples/python/disasm.py new file mode 100755 index 000000000000..732cf106b11d --- /dev/null +++ b/examples/python/disasm.py @@ -0,0 +1,119 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Developer/Library/PrivateFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Developer/Library/PrivateFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +import lldb +import os +import sys + +def disassemble_instructions (insts): + for i in insts: + print i + +def usage(): + print "Usage: disasm.py [-n name] executable-image" + print " By default, it breaks at and disassembles the 'main' function." + sys.exit(0) + +if len(sys.argv) == 2: + fname = 'main' + exe = sys.argv[1] +elif len(sys.argv) == 4: + if sys.argv[1] != '-n': + usage() + else: + fname = sys.argv[2] + exe = sys.argv[3] +else: + usage() + +# Create a new debugger instance +debugger = lldb.SBDebugger.Create() + +# When we step or continue, don't return from the function until the process +# stops. We do this by setting the async mode to false. +debugger.SetAsync (False) + +# Create a target from a file and arch +print "Creating a target for '%s'" % exe + +target = debugger.CreateTargetWithFileAndArch (exe, lldb.LLDB_ARCH_DEFAULT) + +if target: + # If the target is valid set a breakpoint at main + main_bp = target.BreakpointCreateByName (fname, target.GetExecutable().GetFilename()); + + print main_bp + + # Launch the process. Since we specified synchronous mode, we won't return + # from this function until we hit the breakpoint at main + process = target.LaunchSimple (None, None, os.getcwd()) + + # Make sure the launch went ok + if process: + # Print some simple process info + state = process.GetState () + print process + if state == lldb.eStateStopped: + # Get the first thread + thread = process.GetThreadAtIndex (0) + if thread: + # Print some simple thread info + print thread + # Get the first frame + frame = thread.GetFrameAtIndex (0) + if frame: + # Print some simple frame info + print frame + function = frame.GetFunction() + # See if we have debug info (a function) + if function: + # We do have a function, print some info for the function + print function + # Now get all instructions for this function and print them + insts = function.GetInstructions(target) + disassemble_instructions (insts) + else: + # See if we have a symbol in the symbol table for where we stopped + symbol = frame.GetSymbol(); + if symbol: + # We do have a symbol, print some info for the symbol + print symbol + # Now get all instructions for this symbol and print them + insts = symbol.GetInstructions(target) + disassemble_instructions (insts) + + registerList = frame.GetRegisters() + print "Frame registers (size of register set = %d):" % registerList.GetSize() + for value in registerList: + #print value + print "%s (number of children = %d):" % (value.GetName(), value.GetNumChildren()) + for child in value: + print "Name: ", child.GetName(), " Value: ", child.GetValue() + + print "Hit the breakpoint at main, enter to continue and wait for program to exit or 'Ctrl-D'/'quit' to terminate the program" + next = sys.stdin.readline() + if not next or next.rstrip('\n') == 'quit': + print "Terminating the inferior process..." + process.Kill() + else: + # Now continue to the program exit + process.Continue() + # When we return from the above function we will hopefully be at the + # program exit. Print out some process info + print process + elif state == lldb.eStateExited: + print "Didn't hit the breakpoint at main, program has exited..." + else: + print "Unexpected process state: %s, killing process..." % debugger.StateAsCString (state) + process.Kill() + + + +lldb.SBDebugger.Terminate() diff --git a/examples/python/file_extract.py b/examples/python/file_extract.py new file mode 100755 index 000000000000..3afc0c3c1a0b --- /dev/null +++ b/examples/python/file_extract.py @@ -0,0 +1,221 @@ +#! /usr/bin/env python + +import string +import struct +import sys + +class FileExtract: + '''Decode binary data from a file''' + + def __init__(self, f, b = '='): + '''Initialize with an open binary file and optional byte order''' + + self.file = f + self.byte_order = b + self.offsets = list() + + def set_byte_order(self, b): + '''Set the byte order, valid values are "big", "little", "swap", "native", "<", ">", "@", "="''' + if b == 'big': + self.byte_order = '>' + elif b == 'little': + self.byte_order = '<' + elif b == 'swap': + # swap what ever the current byte order is + self.byte_order = swap_unpack_char() + elif b == 'native': + self.byte_order = '=' + elif b == '<' or b == '>' or b == '@' or b == '=': + self.byte_order = b + else: + print "error: invalid byte order specified: '%s'" % b + + def is_in_memory(self): + return False + + def seek(self, offset, whence = 0): + if self.file: + return self.file.seek(offset, whence) + raise ValueError + + def tell(self): + if self.file: + return self.file.tell() + raise ValueError + + def read_size (self, byte_size): + s = self.file.read(byte_size) + if len(s) != byte_size: + return None + return s + + def push_offset_and_seek(self, offset): + '''Push the current file offset and seek to "offset"''' + self.offsets.append(self.file.tell()) + self.file.seek(offset, 0) + + def pop_offset_and_seek(self): + '''Pop a previously pushed file offset, or do nothing if there were no previously pushed offsets''' + if len(self.offsets) > 0: + self.file.seek(self.offsets.pop()) + + def get_sint8(self, fail_value=0): + '''Extract a single int8_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(1) + if s: + v, = struct.unpack(self.byte_order + 'b', s) + return v + else: + return fail_value + + def get_uint8(self, fail_value=0): + '''Extract a single uint8_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(1) + if s: + v, = struct.unpack(self.byte_order + 'B', s) + return v + else: + return fail_value + + def get_sint16(self, fail_value=0): + '''Extract a single int16_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(2) + if s: + v, = struct.unpack(self.byte_order + 'h', s) + return v + else: + return fail_value + + def get_uint16(self, fail_value=0): + '''Extract a single uint16_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(2) + if s: + v, = struct.unpack(self.byte_order + 'H', s) + return v + else: + return fail_value + + def get_sint32(self, fail_value=0): + '''Extract a single int32_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(4) + if s: + v, = struct.unpack(self.byte_order + 'i', s) + return v + else: + return fail_value + + def get_uint32(self, fail_value=0): + '''Extract a single uint32_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(4) + if s: + v, = struct.unpack(self.byte_order + 'I', s) + return v + else: + return fail_value + + def get_sint64(self, fail_value=0): + '''Extract a single int64_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(8) + if s: + v, = struct.unpack(self.byte_order + 'q', s) + return v + else: + return fail_value + + def get_uint64(self, fail_value=0): + '''Extract a single uint64_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(8) + if s: + v, = struct.unpack(self.byte_order + 'Q', s) + return v + else: + return fail_value + + def get_fixed_length_c_string(self, n, fail_value='', isprint_only_with_space_padding=False): + '''Extract a single fixed length C string from the binary file at the current file position, returns a single C string''' + s = self.read_size(n) + if s: + cstr, = struct.unpack(self.byte_order + ("%i" % n) + 's', s) + # Strip trialing NULLs + cstr = string.strip(cstr, "\0") + if isprint_only_with_space_padding: + for c in cstr: + if c in string.printable or ord(c) == 0: + continue + return fail_value + return cstr + else: + return fail_value + + def get_c_string(self): + '''Extract a single NULL terminated C string from the binary file at the current file position, returns a single C string''' + cstr = '' + byte = self.get_uint8() + while byte != 0: + cstr += "%c" % byte + byte = self.get_uint8() + return cstr + + def get_n_sint8(self, n, fail_value=0): + '''Extract "n" int8_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'b', s) + else: + return (fail_value,) * n + + def get_n_uint8(self, n, fail_value=0): + '''Extract "n" uint8_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'B', s) + else: + return (fail_value,) * n + + def get_n_sint16(self, n, fail_value=0): + '''Extract "n" int16_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(2*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'h', s) + else: + return (fail_value,) * n + + def get_n_uint16(self, n, fail_value=0): + '''Extract "n" uint16_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(2*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'H', s) + else: + return (fail_value,) * n + + def get_n_sint32(self, n, fail_value=0): + '''Extract "n" int32_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(4*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'i', s) + else: + return (fail_value,) * n + + def get_n_uint32(self, n, fail_value=0): + '''Extract "n" uint32_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(4*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'I', s) + else: + return (fail_value,) * n + + def get_n_sint64(self, n, fail_value=0): + '''Extract "n" int64_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(8*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'q', s) + else: + return (fail_value,) * n + + def get_n_uint64(self, n, fail_value=0): + '''Extract "n" uint64_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(8*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'Q', s) + else: + return (fail_value,) * n diff --git a/examples/python/gdb_disassemble.py b/examples/python/gdb_disassemble.py new file mode 100755 index 000000000000..d9a2f212fc9a --- /dev/null +++ b/examples/python/gdb_disassemble.py @@ -0,0 +1,24 @@ +import lldb + +def disassemble(debugger, command, result, dict): + if lldb.frame.function: + instructions = lldb.frame.function.instructions + start_addr = lldb.frame.function.addr.load_addr + name = lldb.frame.function.name + elif lldb.frame.symbol: + instructions = lldb.frame.symbol.instructions + start_addr = lldb.frame.symbol.addr.load_addr + name = lldb.frame.symbol.name + + for inst in instructions: + inst_addr = inst.addr.load_addr + inst_offset = inst_addr - start_addr + comment = inst.comment + if comment: + print "<%s + %-4u> 0x%x %8s %s ; %s" % (name, inst_offset, inst_addr, inst.mnemonic, inst.operands, comment) + else: + print "<%s + %-4u> 0x%x %8s %s" % (name, inst_offset, inst_addr, inst.mnemonic, inst.operands) + +# Install the command when the module gets imported +lldb.debugger.HandleCommand('command script add -f gdb_disassemble.disassemble gdb-disassemble') +print 'Installed "gdb-disassemble" command for disassembly' \ No newline at end of file diff --git a/examples/python/gdbremote.py b/examples/python/gdbremote.py new file mode 100755 index 000000000000..4cbfdb2ba333 --- /dev/null +++ b/examples/python/gdbremote.py @@ -0,0 +1,1362 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# This module will enable GDB remote packet logging when the +# 'start_gdb_log' command is called with a filename to log to. When the +# 'stop_gdb_log' command is called, it will disable the logging and +# print out statistics about how long commands took to execute and also +# will primnt ou +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command. This can be done from the LLDB command line: +# (lldb) command script import /path/to/gdbremote.py +# Or it can be added to your ~/.lldbinit file so this module is always +# available. +#---------------------------------------------------------------------- + +import binascii +import commands +import json +import math +import optparse +import os +import re +import shlex +import string +import sys +import tempfile +import xml.etree.ElementTree as ET + +#---------------------------------------------------------------------- +# Global variables +#---------------------------------------------------------------------- +g_log_file = '' +g_byte_order = 'little' +g_number_regex = re.compile('^(0x[0-9a-fA-F]+|[0-9]+)') +g_thread_id_regex = re.compile('^(-1|[0-9a-fA-F]+|0)') + +class TerminalColors: + '''Simple terminal colors class''' + def __init__(self, enabled = True): + # TODO: discover terminal type from "file" and disable if + # it can't handle the color codes + self.enabled = enabled + + def reset(self): + '''Reset all terminal colors and formatting.''' + if self.enabled: + return "\x1b[0m"; + return '' + + def bold(self, on = True): + '''Enable or disable bold depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[1m"; + else: + return "\x1b[22m"; + return '' + + def italics(self, on = True): + '''Enable or disable italics depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[3m"; + else: + return "\x1b[23m"; + return '' + + def underline(self, on = True): + '''Enable or disable underline depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[4m"; + else: + return "\x1b[24m"; + return '' + + def inverse(self, on = True): + '''Enable or disable inverse depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[7m"; + else: + return "\x1b[27m"; + return '' + + def strike(self, on = True): + '''Enable or disable strike through depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[9m"; + else: + return "\x1b[29m"; + return '' + + def black(self, fg = True): + '''Set the foreground or background color to black. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[30m"; + else: + return "\x1b[40m"; + return '' + + def red(self, fg = True): + '''Set the foreground or background color to red. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[31m"; + else: + return "\x1b[41m"; + return '' + + def green(self, fg = True): + '''Set the foreground or background color to green. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[32m"; + else: + return "\x1b[42m"; + return '' + + def yellow(self, fg = True): + '''Set the foreground or background color to yellow. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[33m"; + else: + return "\x1b[43m"; + return '' + + def blue(self, fg = True): + '''Set the foreground or background color to blue. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[34m"; + else: + return "\x1b[44m"; + return '' + + def magenta(self, fg = True): + '''Set the foreground or background color to magenta. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[35m"; + else: + return "\x1b[45m"; + return '' + + def cyan(self, fg = True): + '''Set the foreground or background color to cyan. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[36m"; + else: + return "\x1b[46m"; + return '' + + def white(self, fg = True): + '''Set the foreground or background color to white. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[37m"; + else: + return "\x1b[47m"; + return '' + + def default(self, fg = True): + '''Set the foreground or background color to the default. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[39m"; + else: + return "\x1b[49m"; + return '' + + +def start_gdb_log(debugger, command, result, dict): + '''Start logging GDB remote packets by enabling logging with timestamps and + thread safe logging. Follow a call to this function with a call to "stop_gdb_log" + in order to dump out the commands.''' + global g_log_file + command_args = shlex.split(command) + usage = "usage: start_gdb_log [options] []" + description='''The command enables GDB remote packet logging with timestamps. The packets will be logged to if supplied, or a temporary file will be used. Logging stops when stop_gdb_log is called and the packet times will + be aggregated and displayed.''' + parser = optparse.OptionParser(description=description, prog='start_gdb_log',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + if g_log_file: + result.PutCString ('error: logging is already in progress with file "%s"' % g_log_file) + else: + args_len = len(args) + if args_len == 0: + g_log_file = tempfile.mktemp() + elif len(args) == 1: + g_log_file = args[0] + + if g_log_file: + debugger.HandleCommand('log enable --threadsafe --timestamp --file "%s" gdb-remote packets' % g_log_file); + result.PutCString ("GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." % g_log_file) + return + + result.PutCString ('error: invalid log file path') + result.PutCString (usage) + +def stop_gdb_log(debugger, command, result, dict): + '''Stop logging GDB remote packets to the file that was specified in a call + to "start_gdb_log" and normalize the timestamps to be relative to the first + timestamp in the log file. Also print out statistics for how long each + command took to allow performance bottlenecks to be determined.''' + global g_log_file + # Any commands whose names might be followed by more valid C identifier + # characters must be listed here + command_args = shlex.split(command) + usage = "usage: stop_gdb_log [options]" + description='''The command stops a previously enabled GDB remote packet logging command. Packet logging must have been previously enabled with a call to start_gdb_log.''' + parser = optparse.OptionParser(description=description, prog='stop_gdb_log',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='display verbose debug info', default=False) + parser.add_option('-C', '--color', action='store_true', dest='color', help='add terminal colors', default=False) + parser.add_option('-c', '--sort-by-count', action='store_true', dest='sort_count', help='display verbose debug info', default=False) + parser.add_option('-s', '--symbolicate', action='store_true', dest='symbolicate', help='symbolicate addresses in log using current "lldb.target"', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + options.colors = TerminalColors(options.color) + options.symbolicator = None + if options.symbolicate: + if lldb.target: + import lldb.utils.symbolication + options.symbolicator = lldb.utils.symbolication.Symbolicator() + options.symbolicator.target = lldb.target + else: + print "error: can't symbolicate without a target" + + if not g_log_file: + result.PutCString ('error: logging must have been previously enabled with a call to "stop_gdb_log"') + elif os.path.exists (g_log_file): + if len(args) == 0: + debugger.HandleCommand('log disable gdb-remote packets'); + result.PutCString ("GDB packet logging disabled. Logged packets are in '%s'" % g_log_file) + parse_gdb_log_file (g_log_file, options) + else: + result.PutCString (usage) + else: + print 'error: the GDB packet log file "%s" does not exist' % g_log_file + +def is_hex_byte(str): + if len(str) == 2: + return str[0] in string.hexdigits and str[1] in string.hexdigits; + return False + +# global register info list +g_register_infos = list() +g_max_register_info_name_len = 0 + +class RegisterInfo: + """Class that represents register information""" + def __init__(self, kvp): + self.info = dict() + for kv in kvp: + key = kv[0] + value = kv[1] + self.info[key] = value + def name(self): + '''Get the name of the register.''' + if self.info and 'name' in self.info: + return self.info['name'] + return None + + def bit_size(self): + '''Get the size in bits of the register.''' + if self.info and 'bitsize' in self.info: + return int(self.info['bitsize']) + return 0 + + def byte_size(self): + '''Get the size in bytes of the register.''' + return self.bit_size() / 8 + + def get_value_from_hex_string(self, hex_str): + '''Dump the register value given a native byte order encoded hex ASCII byte string.''' + encoding = self.info['encoding'] + bit_size = self.bit_size() + packet = Packet(hex_str) + if encoding == 'uint': + uval = packet.get_hex_uint(g_byte_order) + if bit_size == 8: + return '0x%2.2x' % (uval) + elif bit_size == 16: + return '0x%4.4x' % (uval) + elif bit_size == 32: + return '0x%8.8x' % (uval) + elif bit_size == 64: + return '0x%16.16x' % (uval) + bytes = list(); + uval = packet.get_hex_uint8() + while uval != None: + bytes.append(uval) + uval = packet.get_hex_uint8() + value_str = '0x' + if g_byte_order == 'little': + bytes.reverse() + for byte in bytes: + value_str += '%2.2x' % byte + return '%s' % (value_str) + + def __str__(self): + '''Dump the register info key/value pairs''' + s = '' + for key in self.info.keys(): + if s: + s += ', ' + s += "%s=%s " % (key, self.info[key]) + return s + +class Packet: + """Class that represents a packet that contains string data""" + def __init__(self, packet_str): + self.str = packet_str + + def peek_char(self): + ch = 0 + if self.str: + ch = self.str[0] + return ch + + def get_char(self): + ch = 0 + if self.str: + ch = self.str[0] + self.str = self.str[1:] + return ch + + def skip_exact_string(self, s): + if self.str and self.str.startswith(s): + self.str = self.str[len(s):] + return True + else: + return False + + def get_thread_id(self, fail_value = -1): + match = g_number_regex.match (self.str) + if match: + number_str = match.group(1) + self.str = self.str[len(number_str):] + return int(number_str, 0) + else: + return fail_value + + def get_hex_uint8(self): + if self.str and len(self.str) >= 2 and self.str[0] in string.hexdigits and self.str[1] in string.hexdigits: + uval = int(self.str[0:2], 16) + self.str = self.str[2:] + return uval + return None + + def get_hex_uint16(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + return uval + + def get_hex_uint32(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 24 + return uval + + def get_hex_uint64(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 56 + uval |= self.get_hex_uint8() << 48 + uval |= self.get_hex_uint8() << 40 + uval |= self.get_hex_uint8() << 32 + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 32 + uval |= self.get_hex_uint8() << 40 + uval |= self.get_hex_uint8() << 48 + uval |= self.get_hex_uint8() << 56 + return uval + + def get_number(self, fail_value=-1): + '''Get a number from the packet. The number must be in big endian format and should be parsed + according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with + [1-9] means decimal, etc)''' + match = g_number_regex.match (self.str) + if match: + number_str = match.group(1) + self.str = self.str[len(number_str):] + return int(number_str, 0) + else: + return fail_value + + + def get_hex_ascii_str(self, n=0): + hex_chars = self.get_hex_chars(n) + if hex_chars: + return binascii.unhexlify(hex_chars) + else: + return None + + def get_hex_chars(self, n = 0): + str_len = len(self.str) + if n == 0: + # n was zero, so we need to determine all hex chars and + # stop when we hit the end of the string of a non-hex character + while n < str_len and self.str[n] in string.hexdigits: + n = n + 1 + else: + if n > str_len: + return None # Not enough chars + # Verify all chars are hex if a length was specified + for i in range(n): + if self.str[i] not in string.hexdigits: + return None # Not all hex digits + if n == 0: + return None + hex_str = self.str[0:n] + self.str = self.str[n:] + return hex_str + + def get_hex_uint(self, byte_order, n = 0): + if byte_order == 'big': + hex_str = self.get_hex_chars(n) + if hex_str == None: + return None + return int(hex_str, 16) + else: + uval = self.get_hex_uint8() + if uval == None: + return None + uval_result = 0 + shift = 0 + while uval != None: + uval_result |= (uval << shift) + shift += 8 + uval = self.get_hex_uint8() + return uval_result + + def get_key_value_pairs(self): + kvp = list() + if ';' in self.str: + key_value_pairs = string.split(self.str, ';') + for key_value_pair in key_value_pairs: + if len(key_value_pair): + kvp.append(string.split(key_value_pair, ':')) + return kvp + + def split(self, ch): + return string.split(self.str, ch) + + def split_hex(self, ch, byte_order): + hex_values = list() + strings = string.split(self.str, ch) + for str in strings: + hex_values.append(Packet(str).get_hex_uint(byte_order)) + return hex_values + + def __str__(self): + return self.str + + def __len__(self): + return len(self.str) + +g_thread_suffix_regex = re.compile(';thread:([0-9a-fA-F]+);') +def get_thread_from_thread_suffix(str): + if str: + match = g_thread_suffix_regex.match (str) + if match: + return int(match.group(1), 16) + return None + +def cmd_qThreadStopInfo(options, cmd, args): + packet = Packet(args) + tid = packet.get_hex_uint('big') + print "get_thread_stop_info (tid = 0x%x)" % (tid) + +def cmd_stop_reply(options, cmd, args): + print "get_last_stop_info()" + return False + +def rsp_stop_reply(options, cmd, cmd_args, rsp): + global g_byte_order + packet = Packet(rsp) + stop_type = packet.get_char() + if stop_type == 'T' or stop_type == 'S': + signo = packet.get_hex_uint8() + key_value_pairs = packet.get_key_value_pairs() + for key_value_pair in key_value_pairs: + key = key_value_pair[0] + if is_hex_byte(key): + reg_num = Packet(key).get_hex_uint8() + if reg_num < len(g_register_infos): + reg_info = g_register_infos[reg_num] + key_value_pair[0] = reg_info.name() + key_value_pair[1] = reg_info.get_value_from_hex_string (key_value_pair[1]) + elif key == 'jthreads' or key == 'jstopinfo': + key_value_pair[1] = binascii.unhexlify(key_value_pair[1]) + key_value_pairs.insert(0, ['signal', signo]) + print 'stop_reply():' + dump_key_value_pairs (key_value_pairs) + elif stop_type == 'W': + exit_status = packet.get_hex_uint8() + print 'stop_reply(): exit (status=%i)' % exit_status + elif stop_type == 'O': + print 'stop_reply(): stdout = "%s"' % packet.str + + +def cmd_unknown_packet(options, cmd, args): + if args: + print "cmd: %s, args: %s", cmd, args + else: + print "cmd: %s", cmd + return False + +def cmd_qSymbol(options, cmd, args): + if args == ':': + print 'ready to serve symbols' + else: + packet = Packet(args) + symbol_addr = packet.get_hex_uint('big') + if symbol_addr is None: + if packet.skip_exact_string(':'): + symbol_name = packet.get_hex_ascii_str() + print 'lookup_symbol("%s") -> symbol not available yet' % (symbol_name) + else: + print 'error: bad command format' + else: + if packet.skip_exact_string(':'): + symbol_name = packet.get_hex_ascii_str() + print 'lookup_symbol("%s") -> 0x%x' % (symbol_name, symbol_addr) + else: + print 'error: bad command format' + +def rsp_qSymbol(options, cmd, cmd_args, rsp): + if len(rsp) == 0: + print "Unsupported" + else: + if rsp == "OK": + print "No more symbols to lookup" + else: + packet = Packet(rsp) + if packet.skip_exact_string("qSymbol:"): + symbol_name = packet.get_hex_ascii_str() + print 'lookup_symbol("%s")' % (symbol_name) + else: + print 'error: response string should start with "qSymbol:": respnse is "%s"' % (rsp) + +def cmd_qXfer(options, cmd, args): + # $qXfer:features:read:target.xml:0,1ffff#14 + print "read target special data %s" % (args) + return True + +def rsp_qXfer(options, cmd, cmd_args, rsp): + data = string.split(cmd_args, ':') + if data[0] == 'features': + if data[1] == 'read': + filename, extension = os.path.splitext(data[2]) + if extension == '.xml': + response = Packet(rsp) + xml_string = response.get_hex_ascii_str() + ch = xml_string[0] + if ch == 'l': + xml_string = xml_string[1:] + xml_root = ET.fromstring(xml_string) + for reg_element in xml_root.findall("./feature/reg"): + if not 'value_regnums' in reg_element.attrib: + reg_info = RegisterInfo([]) + if 'name' in reg_element.attrib: + reg_info.info['name'] = reg_element.attrib['name'] + else: + reg_info.info['name'] = 'unspecified' + if 'encoding' in reg_element.attrib: + reg_info.info['encoding'] = reg_element.attrib['encoding'] + else: + reg_info.info['encoding'] = 'uint' + if 'offset' in reg_element.attrib: + reg_info.info['offset'] = reg_element.attrib['offset'] + if 'bitsize' in reg_element.attrib: + reg_info.info['bitsize'] = reg_element.attrib['bitsize'] + g_register_infos.append(reg_info) + print 'XML for "%s":' % (data[2]) + ET.dump(xml_root) + +def cmd_A(options, cmd, args): + print 'launch process:' + packet = Packet(args) + while 1: + arg_len = packet.get_number() + if arg_len == -1: + break + if not packet.skip_exact_string(','): + break + arg_idx = packet.get_number() + if arg_idx == -1: + break + if not packet.skip_exact_string(','): + break; + arg_value = packet.get_hex_ascii_str(arg_len) + print 'argv[%u] = "%s"' % (arg_idx, arg_value) + +def cmd_qC(options, cmd, args): + print "query_current_thread_id()" + +def rsp_qC(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + if packet.skip_exact_string("QC"): + tid = packet.get_thread_id() + print "current_thread_id = %#x" % (tid) + else: + print "current_thread_id = old thread ID" + +def cmd_query_packet(options, cmd, args): + if args: + print "%s%s" % (cmd, args) + else: + print "%s" % (cmd) + return False + +def rsp_ok_error(rsp): + print "rsp: ", rsp + +def rsp_ok_means_supported(options, cmd, cmd_args, rsp): + if rsp == 'OK': + print "%s%s is supported" % (cmd, cmd_args) + elif rsp == '': + print "%s%s is not supported" % (cmd, cmd_args) + else: + print "%s%s -> %s" % (cmd, cmd_args, rsp) + +def rsp_ok_means_success(options, cmd, cmd_args, rsp): + if rsp == 'OK': + print "success" + elif rsp == '': + print "%s%s is not supported" % (cmd, cmd_args) + else: + print "%s%s -> %s" % (cmd, cmd_args, rsp) + +def dump_key_value_pairs(key_value_pairs): + max_key_len = 0 + for key_value_pair in key_value_pairs: + key_len = len(key_value_pair[0]) + if max_key_len < key_len: + max_key_len = key_len + for key_value_pair in key_value_pairs: + key = key_value_pair[0] + value = key_value_pair[1] + print "%*s = %s" % (max_key_len, key, value) + +def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp): + if rsp: + print '%s response:' % (cmd) + packet = Packet(rsp) + key_value_pairs = packet.get_key_value_pairs() + dump_key_value_pairs(key_value_pairs) + else: + print "not supported" + +def cmd_c(options, cmd, args): + print "continue()" + return False + +def cmd_s(options, cmd, args): + print "step()" + return False + +def cmd_vCont(options, cmd, args): + if args == '?': + print "%s: get supported extended continue modes" % (cmd) + else: + got_other_threads = 0 + s = '' + for thread_action in string.split(args[1:], ';'): + (short_action, thread) = string.split(thread_action, ':') + tid = int(thread, 16) + if short_action == 'c': + action = 'continue' + elif short_action == 's': + action = 'step' + elif short_action[0] == 'C': + action = 'continue with signal 0x%s' % (short_action[1:]) + elif short_action == 'S': + action = 'step with signal 0x%s' % (short_action[1:]) + else: + action = short_action + if s: + s += ', ' + if tid == -1: + got_other_threads = 1 + s += 'other-threads:' + else: + s += 'thread 0x%4.4x: %s' % (tid, action) + if got_other_threads: + print "extended_continue (%s)" % (s) + else: + print "extended_continue (%s, other-threads: suspend)" % (s) + return False + +def rsp_vCont(options, cmd, cmd_args, rsp): + if cmd_args == '?': + # Skip the leading 'vCont;' + rsp = rsp[6:] + modes = string.split(rsp, ';') + s = "%s: supported extended continue modes include: " % (cmd) + + for i, mode in enumerate(modes): + if i: + s += ', ' + if mode == 'c': + s += 'continue' + elif mode == 'C': + s += 'continue with signal' + elif mode == 's': + s += 'step' + elif mode == 'S': + s += 'step with signal' + else: + s += 'unrecognized vCont mode: ', mode + print s + elif rsp: + if rsp[0] == 'T' or rsp[0] == 'S' or rsp[0] == 'W' or rsp[0] == 'X': + rsp_stop_reply (options, cmd, cmd_args, rsp) + return + if rsp[0] == 'O': + print "stdout: %s" % (rsp) + return + else: + print "not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd, cmd_args, rsp) + +def cmd_vAttach(options, cmd, args): + (extra_command, args) = string.split(args, ';') + if extra_command: + print "%s%s(%s)" % (cmd, extra_command, args) + else: + print "attach(pid = %u)" % int(args, 16) + return False + + +def cmd_qRegisterInfo(options, cmd, args): + print 'query_register_info(reg_num=%i)' % (int(args, 16)) + return False + +def rsp_qRegisterInfo(options, cmd, cmd_args, rsp): + global g_max_register_info_name_len + print 'query_register_info(reg_num=%i):' % (int(cmd_args, 16)), + if len(rsp) == 3 and rsp[0] == 'E': + g_max_register_info_name_len = 0 + for reg_info in g_register_infos: + name_len = len(reg_info.name()) + if g_max_register_info_name_len < name_len: + g_max_register_info_name_len = name_len + print' DONE' + else: + packet = Packet(rsp) + reg_info = RegisterInfo(packet.get_key_value_pairs()) + g_register_infos.append(reg_info) + print reg_info + return False + +def cmd_qThreadInfo(options, cmd, args): + if cmd == 'qfThreadInfo': + query_type = 'first' + else: + query_type = 'subsequent' + print 'get_current_thread_list(type=%s)' % (query_type) + return False + +def rsp_qThreadInfo(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + response_type = packet.get_char() + if response_type == 'm': + tids = packet.split_hex(';', 'big') + for i, tid in enumerate(tids): + if i: + print ',', + print '0x%x' % (tid), + print + elif response_type == 'l': + print 'END' + +def rsp_hex_big_endian(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + uval = packet.get_hex_uint('big') + print '%s: 0x%x' % (cmd, uval) + +def cmd_read_mem_bin(options, cmd, args): + # x0x7fff5fc39200,0x200 + packet = Packet(args) + addr = packet.get_number() + comma = packet.get_char() + size = packet.get_number() + print 'binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr, size) + return False + +def rsp_mem_bin_bytes(options, cmd, cmd_args, rsp): + packet = Packet(cmd_args) + addr = packet.get_number() + comma = packet.get_char() + size = packet.get_number() + print 'memory:' + if size > 0: + dump_hex_memory_buffer (addr, rsp) + +def cmd_read_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + comma = packet.get_char() + size = packet.get_hex_uint('big') + print 'read_memory (addr = 0x%16.16x, size = %u)' % (addr, size) + return False + +def dump_hex_memory_buffer(addr, hex_byte_str): + packet = Packet(hex_byte_str) + idx = 0 + ascii = '' + uval = packet.get_hex_uint8() + while uval != None: + if ((idx % 16) == 0): + if ascii: + print ' ', ascii + ascii = '' + print '0x%x:' % (addr + idx), + print '%2.2x' % (uval), + if 0x20 <= uval and uval < 0x7f: + ascii += '%c' % uval + else: + ascii += '.' + uval = packet.get_hex_uint8() + idx = idx + 1 + if ascii: + print ' ', ascii + ascii = '' + +def cmd_write_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + if packet.get_char() != ',': + print 'error: invalid write memory command (missing comma after address)' + return + size = packet.get_hex_uint('big') + if packet.get_char() != ':': + print 'error: invalid write memory command (missing colon after size)' + return + print 'write_memory (addr = 0x%16.16x, size = %u, data:' % (addr, size) + dump_hex_memory_buffer (addr, packet.str) + return False + +def cmd_alloc_memory(options, cmd, args): + packet = Packet(args) + byte_size = packet.get_hex_uint('big') + if packet.get_char() != ',': + print 'error: invalid allocate memory command (missing comma after address)' + return + print 'allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size, byte_size, packet.str) + return False + +def rsp_alloc_memory(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + addr = packet.get_hex_uint('big') + print 'addr = 0x%x' % addr + +def cmd_dealloc_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + if packet.get_char() != ',': + print 'error: invalid allocate memory command (missing comma after address)' + else: + print 'deallocate_memory (addr = 0x%x, permissions = %s)' % (addr, packet.str) + return False +def rsp_memory_bytes(options, cmd, cmd_args, rsp): + addr = Packet(cmd_args).get_hex_uint('big') + dump_hex_memory_buffer (addr, rsp) + +def get_register_name_equal_value(options, reg_num, hex_value_str): + if reg_num < len(g_register_infos): + reg_info = g_register_infos[reg_num] + value_str = reg_info.get_value_from_hex_string (hex_value_str) + s = reg_info.name() + ' = ' + if options.symbolicator: + symbolicated_addresses = options.symbolicator.symbolicate (int(value_str, 0)) + if symbolicated_addresses: + s += options.colors.magenta() + s += '%s' % symbolicated_addresses[0] + s += options.colors.reset() + return s + s += value_str + return s + else: + reg_value = Packet(hex_value_str).get_hex_uint(g_byte_order) + return 'reg(%u) = 0x%x' % (reg_num, reg_value) + +def cmd_read_one_reg(options, cmd, args): + packet = Packet(args) + reg_num = packet.get_hex_uint('big') + tid = get_thread_from_thread_suffix (packet.str) + name = None + if reg_num < len(g_register_infos): + name = g_register_infos[reg_num].name () + if packet.str: + packet.get_char() # skip ; + thread_info = packet.get_key_value_pairs() + tid = int(thread_info[0][1], 16) + s = 'read_register (reg_num=%u' % reg_num + if name: + s += ' (%s)' % (name) + if tid != None: + s += ', tid = 0x%4.4x' % (tid) + s += ')' + print s + return False + +def rsp_read_one_reg(options, cmd, cmd_args, rsp): + packet = Packet(cmd_args) + reg_num = packet.get_hex_uint('big') + print get_register_name_equal_value (options, reg_num, rsp) + +def cmd_write_one_reg(options, cmd, args): + packet = Packet(args) + reg_num = packet.get_hex_uint('big') + if packet.get_char() != '=': + print 'error: invalid register write packet' + else: + name = None + hex_value_str = packet.get_hex_chars() + tid = get_thread_from_thread_suffix (packet.str) + s = 'write_register (reg_num=%u' % reg_num + if name: + s += ' (%s)' % (name) + s += ', value = ' + s += get_register_name_equal_value(options, reg_num, hex_value_str) + if tid != None: + s += ', tid = 0x%4.4x' % (tid) + s += ')' + print s + return False + +def dump_all_regs(packet): + for reg_info in g_register_infos: + nibble_size = reg_info.bit_size() / 4 + hex_value_str = packet.get_hex_chars(nibble_size) + if hex_value_str != None: + value = reg_info.get_value_from_hex_string (hex_value_str) + print '%*s = %s' % (g_max_register_info_name_len, reg_info.name(), value) + else: + return + +def cmd_read_all_regs(cmd, cmd_args): + packet = Packet(cmd_args) + packet.get_char() # toss the 'g' command character + tid = get_thread_from_thread_suffix (packet.str) + if tid != None: + print 'read_all_register(thread = 0x%4.4x)' % tid + else: + print 'read_all_register()' + return False + +def rsp_read_all_regs(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + dump_all_regs (packet) + +def cmd_write_all_regs(options, cmd, args): + packet = Packet(args) + print 'write_all_registers()' + dump_all_regs (packet) + return False + +g_bp_types = [ "software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp" ] + +def cmd_bp(options, cmd, args): + if cmd == 'Z': + s = 'set_' + else: + s = 'clear_' + packet = Packet (args) + bp_type = packet.get_hex_uint('big') + packet.get_char() # Skip , + bp_addr = packet.get_hex_uint('big') + packet.get_char() # Skip , + bp_size = packet.get_hex_uint('big') + s += g_bp_types[bp_type] + s += " (addr = 0x%x, size = %u)" % (bp_addr, bp_size) + print s + return False + +def cmd_mem_rgn_info(options, cmd, args): + packet = Packet(args) + packet.get_char() # skip ':' character + addr = packet.get_hex_uint('big') + print 'get_memory_region_info (addr=0x%x)' % (addr) + return False + +def cmd_kill(options, cmd, args): + print 'kill_process()' + return False + +def cmd_jThreadsInfo(options, cmd, args): + print 'jThreadsInfo()' + return False + +def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args): + print 'jGetLoadedDynamicLibrariesInfos()' + return False + +def decode_packet(s, start_index = 0): + #print '\ndecode_packet("%s")' % (s[start_index:]) + index = s.find('}', start_index) + have_escapes = index != -1 + if have_escapes: + normal_s = s[start_index:index] + else: + normal_s = s[start_index:] + #print 'normal_s = "%s"' % (normal_s) + if have_escapes: + escape_char = '%c' % (ord(s[index+1]) ^ 0x20) + #print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char) + return normal_s + escape_char + decode_packet(s, index+2) + else: + return normal_s + +def rsp_json(options, cmd, cmd_args, rsp): + print '%s() reply:' % (cmd) + json_tree = json.loads(rsp) + print json.dumps(json_tree, indent=4, separators=(',', ': ')) + + +def rsp_jGetLoadedDynamicLibrariesInfos(options, cmd, cmd_args, rsp): + if cmd_args: + rsp_json(options, cmd, cmd_args, rsp) + else: + rsp_ok_means_supported(options, cmd, cmd_args, rsp) + +gdb_remote_commands = { + '\\?' : { 'cmd' : cmd_stop_reply , 'rsp' : rsp_stop_reply , 'name' : "stop reply pacpket"}, + 'qThreadStopInfo' : { 'cmd' : cmd_qThreadStopInfo , 'rsp' : rsp_stop_reply , 'name' : "stop reply pacpket"}, + 'QStartNoAckMode' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "query if no ack mode is supported"}, + 'QThreadSuffixSupported' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "query if thread suffix is supported" }, + 'QListThreadsInStopReply' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "query if threads in stop reply packets are supported" }, + 'QSetDetachOnError' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_success , 'name' : "set if we should detach on error" }, + 'QSetDisableASLR' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_success , 'name' : "set if we should disable ASLR" }, + 'qLaunchSuccess' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_success , 'name' : "check on launch success for the A packet" }, + 'A' : { 'cmd' : cmd_A , 'rsp' : rsp_ok_means_success , 'name' : "launch process" }, + 'QLaunchArch' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "set if we should disable ASLR" }, + 'qVAttachOrWaitSupported' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "set the launch architecture" }, + 'qHostInfo' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get host information" }, + 'qC' : { 'cmd' : cmd_qC , 'rsp' : rsp_qC , 'name' : "return the current thread ID" }, + 'vCont' : { 'cmd' : cmd_vCont , 'rsp' : rsp_vCont , 'name' : "extended continue command" }, + 'vAttach' : { 'cmd' : cmd_vAttach , 'rsp' : rsp_stop_reply , 'name' : "attach to process" }, + 'c' : { 'cmd' : cmd_c , 'rsp' : rsp_stop_reply , 'name' : "continue" }, + 's' : { 'cmd' : cmd_s , 'rsp' : rsp_stop_reply , 'name' : "step" }, + 'qRegisterInfo' : { 'cmd' : cmd_qRegisterInfo , 'rsp' : rsp_qRegisterInfo , 'name' : "query register info" }, + 'qfThreadInfo' : { 'cmd' : cmd_qThreadInfo , 'rsp' : rsp_qThreadInfo , 'name' : "get current thread list" }, + 'qsThreadInfo' : { 'cmd' : cmd_qThreadInfo , 'rsp' : rsp_qThreadInfo , 'name' : "get current thread list" }, + 'qShlibInfoAddr' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_hex_big_endian , 'name' : "get shared library info address" }, + 'qMemoryRegionInfo' : { 'cmd' : cmd_mem_rgn_info , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get memory region information" }, + 'qProcessInfo' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get process info" }, + 'qSupported' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "query supported" }, + 'qXfer:' : { 'cmd' : cmd_qXfer , 'rsp' : rsp_qXfer , 'name' : "qXfer" }, + 'qSymbol:' : { 'cmd' : cmd_qSymbol , 'rsp' : rsp_qSymbol , 'name' : "qSymbol" }, + 'x' : { 'cmd' : cmd_read_mem_bin , 'rsp' : rsp_mem_bin_bytes , 'name' : "read memory binary" }, + 'X' : { 'cmd' : cmd_write_memory , 'rsp' : rsp_ok_means_success , 'name' : "write memory binary" }, + 'm' : { 'cmd' : cmd_read_memory , 'rsp' : rsp_memory_bytes , 'name' : "read memory" }, + 'M' : { 'cmd' : cmd_write_memory , 'rsp' : rsp_ok_means_success , 'name' : "write memory" }, + '_M' : { 'cmd' : cmd_alloc_memory , 'rsp' : rsp_alloc_memory , 'name' : "allocate memory" }, + '_m' : { 'cmd' : cmd_dealloc_memory , 'rsp' : rsp_ok_means_success , 'name' : "deallocate memory" }, + 'p' : { 'cmd' : cmd_read_one_reg , 'rsp' : rsp_read_one_reg , 'name' : "read single register" }, + 'P' : { 'cmd' : cmd_write_one_reg , 'rsp' : rsp_ok_means_success , 'name' : "write single register" }, + 'g' : { 'cmd' : cmd_read_all_regs , 'rsp' : rsp_read_all_regs , 'name' : "read all registers" }, + 'G' : { 'cmd' : cmd_write_all_regs , 'rsp' : rsp_ok_means_success , 'name' : "write all registers" }, + 'z' : { 'cmd' : cmd_bp , 'rsp' : rsp_ok_means_success , 'name' : "clear breakpoint or watchpoint" }, + 'Z' : { 'cmd' : cmd_bp , 'rsp' : rsp_ok_means_success , 'name' : "set breakpoint or watchpoint" }, + 'k' : { 'cmd' : cmd_kill , 'rsp' : rsp_stop_reply , 'name' : "kill process" }, + 'jThreadsInfo' : { 'cmd' : cmd_jThreadsInfo , 'rsp' : rsp_json , 'name' : "JSON get all threads info" }, + 'jGetLoadedDynamicLibrariesInfos:' : { 'cmd' : cmd_jGetLoadedDynamicLibrariesInfos, 'rsp' : rsp_jGetLoadedDynamicLibrariesInfos, 'name' : 'JSON get loaded dynamic libraries' }, +} + +def calculate_mean_and_standard_deviation(floats): + sum = 0.0 + count = len(floats) + if count == 0: + return (0.0, 0.0) + for f in floats: + sum += f + mean = sum / count + accum = 0.0 + for f in floats: + delta = f - mean + accum += delta * delta + + std_dev = math.sqrt(accum / (count-1)); + return (mean, std_dev) + +def parse_gdb_log_file(path, options): + f = open(path) + parse_gdb_log(f, options) + f.close() + +def parse_gdb_log(file, options): + '''Parse a GDB log file that was generated by enabling logging with: + (lldb) log enable --threadsafe --timestamp --file gdb-remote packets + This log file will contain timestamps and this function will then normalize + those packets to be relative to the first value timestamp that is found and + show delta times between log lines and also keep track of how long it takes + for GDB remote commands to make a send/receive round trip. This can be + handy when trying to figure out why some operation in the debugger is taking + a long time during a preset set of debugger commands.''' + + tricky_commands = [ 'qRegisterInfo' ] + timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$') + packet_name_regex = re.compile('([A-Za-z_]+)[^a-z]') + packet_transmit_name_regex = re.compile('(?Psend|read) packet: (?P.*)') + packet_contents_name_regex = re.compile('\$([^#]+)#[0-9a-fA-F]{2}') + packet_checksum_regex = re.compile('.*#[0-9a-fA-F]{2}$') + packet_names_regex_str = '(' + '|'.join(gdb_remote_commands.keys()) + ')(.*)'; + packet_names_regex = re.compile(packet_names_regex_str); + + base_time = 0.0 + last_time = 0.0 + packet_send_time = 0.0 + packet_total_times = {} + packet_times = [] + packet_count = {} + lines = file.read().splitlines() + last_command = None + last_command_args = None + last_command_packet = None + hide_next_response = False + num_lines = len(lines) + skip_count = 0 + for (line_index, line) in enumerate(lines): + # See if we need to skip any lines + if skip_count > 0: + skip_count -= 1 + continue + m = packet_transmit_name_regex.search(line) + is_command = False + direction = None + if m: + direction = m.group('direction') + is_command = direction == 'send' + packet = m.group('packet') + sys.stdout.write(options.colors.green()) + if not options.quiet and not hide_next_response: + print '# ', line + sys.stdout.write(options.colors.reset()) + + #print 'direction = "%s", packet = "%s"' % (direction, packet) + + if packet[0] == '+': + if is_command: + print '-->', + else: + print '<--', + if not options.quiet: print 'ACK' + continue + elif packet[0] == '-': + if is_command: + print '-->', + else: + print '<--', + if not options.quiet: print 'NACK' + continue + elif packet[0] == '$': + m = packet_contents_name_regex.match(packet) + if not m and packet[0] == '$': + multiline_packet = packet + idx = line_index + 1 + while idx < num_lines: + if not options.quiet and not hide_next_response: + print '# ', lines[idx] + multiline_packet += lines[idx] + m = packet_contents_name_regex.match(multiline_packet) + if m: + packet = multiline_packet + skip_count = idx - line_index + break + else: + idx += 1 + if m: + if is_command: + print '-->', + else: + print '<--', + contents = decode_packet(m.group(1)) + if is_command: + hide_next_response = False + m = packet_names_regex.match (contents) + if m: + last_command = m.group(1) + if last_command == '?': + last_command = '\\?' + packet_name = last_command + last_command_args = m.group(2) + last_command_packet = contents + hide_next_response = gdb_remote_commands[last_command]['cmd'](options, last_command, last_command_args) + else: + packet_match = packet_name_regex.match (contents) + if packet_match: + packet_name = packet_match.group(1) + for tricky_cmd in tricky_commands: + if packet_name.find (tricky_cmd) == 0: + packet_name = tricky_cmd + else: + packet_name = contents + last_command = None + last_command_args = None + last_command_packet = None + elif last_command: + gdb_remote_commands[last_command]['rsp'](options, last_command, last_command_args, contents) + else: + print 'error: invalid packet: "', packet, '"' + else: + print '???' + else: + print '## ', line + match = timestamp_regex.match (line) + if match: + curr_time = float (match.group(2)) + if last_time and not is_command: + delta = curr_time - last_time + packet_times.append(delta) + delta = 0.0 + if base_time: + delta = curr_time - last_time + else: + base_time = curr_time + + if is_command: + packet_send_time = curr_time + elif line.find('read packet: $') >= 0 and packet_name: + if packet_name in packet_total_times: + packet_total_times[packet_name] += delta + packet_count[packet_name] += 1 + else: + packet_total_times[packet_name] = delta + packet_count[packet_name] = 1 + packet_name = None + + if not options or not options.quiet: + print '%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3)) + last_time = curr_time + # else: + # print line + (average, std_dev) = calculate_mean_and_standard_deviation(packet_times) + if average and std_dev: + print '%u packets with average packet time of %f and standard deviation of %f' % (len(packet_times), average, std_dev) + if packet_total_times: + total_packet_time = 0.0 + total_packet_count = 0 + for key, vvv in packet_total_times.items(): + # print ' key = (%s) "%s"' % (type(key), key) + # print 'value = (%s) %s' % (type(vvv), vvv) + # if type(vvv) == 'float': + total_packet_time += vvv + for key, vvv in packet_count.items(): + total_packet_count += vvv + + print '#---------------------------------------------------' + print '# Packet timing summary:' + print '# Totals: time = %6f, count = %6d' % (total_packet_time, total_packet_count) + print '#---------------------------------------------------' + print '# Packet Time (sec) Percent Count ' + print '#------------------------- ---------- ------- ------' + if options and options.sort_count: + res = sorted(packet_count, key=packet_count.__getitem__, reverse=True) + else: + res = sorted(packet_total_times, key=packet_total_times.__getitem__, reverse=True) + + if last_time > 0.0: + for item in res: + packet_total_time = packet_total_times[item] + packet_percent = (packet_total_time / total_packet_time)*100.0 + if packet_percent >= 10.0: + print " %24s %.6f %.2f%% %6d" % (item, packet_total_time, packet_percent, packet_count[item]) + else: + print " %24s %.6f %.2f%% %6d" % (item, packet_total_time, packet_percent, packet_count[item]) + + + +if __name__ == '__main__': + usage = "usage: gdbremote [options]" + description='''The command disassembles a GDB remote packet log.''' + parser = optparse.OptionParser(description=description, prog='gdbremote',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='display verbose debug info', default=False) + parser.add_option('-C', '--color', action='store_true', dest='color', help='add terminal colors', default=False) + parser.add_option('-c', '--sort-by-count', action='store_true', dest='sort_count', help='display verbose debug info', default=False) + parser.add_option('--crashlog', type='string', dest='crashlog', help='symbolicate using a darwin crash log file', default=False) + try: + (options, args) = parser.parse_args(sys.argv[1:]) + except: + print 'error: argument error' + sys.exit(1) + + options.colors = TerminalColors(options.color) + options.symbolicator = None + if options.crashlog: + import lldb + lldb.debugger = lldb.SBDebugger.Create() + import lldb.macosx.crashlog + options.symbolicator = lldb.macosx.crashlog.CrashLog(options.crashlog) + print '%s' % (options.symbolicator) + + # This script is being run from the command line, create a debugger in case we are + # going to use any debugger functions in our function. + if len(args): + for file in args: + print '#----------------------------------------------------------------------' + print "# GDB remote log file: '%s'" % file + print '#----------------------------------------------------------------------' + parse_gdb_log_file (file, options) + if options.symbolicator: + print '%s' % (options.symbolicator) + else: + parse_gdb_log(sys.stdin, options) + +else: + import lldb + if lldb.debugger: + # This initializer is being run from LLDB in the embedded command interpreter + # Add any commands contained in this module to LLDB + lldb.debugger.HandleCommand('command script add -f gdbremote.start_gdb_log start_gdb_log') + lldb.debugger.HandleCommand('command script add -f gdbremote.stop_gdb_log stop_gdb_log') + print 'The "start_gdb_log" and "stop_gdb_log" commands are now installed and ready for use, type "start_gdb_log --help" or "stop_gdb_log --help" for more information' diff --git a/examples/python/globals.py b/examples/python/globals.py new file mode 100755 index 000000000000..fb2739c8b69c --- /dev/null +++ b/examples/python/globals.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# For the shells csh, tcsh: +# ( setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python ; ./globals.py [ ...]) +# +# For the shells sh, bash: +# PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python ./globals.py [ ...] +#---------------------------------------------------------------------- + +import lldb +import commands +import optparse +import os +import shlex +import sys + +def get_globals(raw_path, options): + error = lldb.SBError() + # Resolve the path if needed + path = os.path.expanduser(raw_path) + # Create a target using path + options + target = lldb.debugger.CreateTarget(path, options.arch, options.platform, False, error) + if target: + # Get the executable module + module = target.module[target.executable.basename] + if module: + # Keep track of which variables we have already looked up + global_names = list() + # Iterate through all symbols in the symbol table and watch for any DATA symbols + for symbol in module.symbols: + if symbol.type == lldb.eSymbolTypeData: + # The symbol is a DATA symbol, lets try and find all global variables + # that match this name and print them + global_name = symbol.name + # Make sure we don't lookup the same variable twice + if global_name not in global_names: + global_names.append(global_name) + # Find all global variables by name + global_variable_list = module.FindGlobalVariables (target, global_name, lldb.UINT32_MAX) + if global_variable_list: + # Print results for anything that matched + for global_variable in global_variable_list: + print 'name = %s' % global_variable.name # returns the global variable name as a string + print 'value = %s' % global_variable.value # Returns the variable value as a string + print 'type = %s' % global_variable.type # Returns an lldb.SBType object + print 'addr = %s' % global_variable.addr # Returns an lldb.SBAddress (section offset address) for this global + print 'file_addr = 0x%x' % global_variable.addr.file_addr # Returns the file virtual address for this global + print 'location = %s' % global_variable.location # returns the global variable value as a string + print 'size = %s' % global_variable.size # Returns the size in bytes of this global variable + print + +def globals(command_args): + '''Extract all globals from any arguments which must be paths to object files.''' + usage = "usage: %prog [options] [PATH ...]" + description='''This command will find all globals in the specified object file and return an list() of lldb.SBValue objects (which might be empty).''' + parser = optparse.OptionParser(description=description, prog='globals',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-a', '--arch', type='string', metavar='arch', dest='arch', help='Specify an architecture (or triple) to use when extracting from a file.') + parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + try: + (options, args) = parser.parse_args(command_args) + except: + return + + for path in args: + get_globals (path, options) + +if __name__ == '__main__': + lldb.debugger = lldb.SBDebugger.Create() + globals (sys.argv[1:]) + diff --git a/examples/python/jump.py b/examples/python/jump.py new file mode 100644 index 000000000000..c904009bb405 --- /dev/null +++ b/examples/python/jump.py @@ -0,0 +1,173 @@ +import lldb, re + +def parse_linespec (linespec, frame, result): + """Handles a subset of GDB-style linespecs. Specifically: + + number - A line in the current file + +offset - The line /offset/ lines after this line + -offset - The line /offset/ lines before this line + filename:number - Line /number/ in file /filename/ + function - The start of /function/ + *address - The pointer target of /address/, which must be a literal (but see `` in LLDB) + + We explicitly do not handle filename:function because it is ambiguous in Objective-C. + + This function returns a list of addresses.""" + + breakpoint = None + target = frame.GetThread().GetProcess().GetTarget() + + matched = False + + if (not matched): + mo = re.match("^([0-9]+)$", linespec) + if (mo != None): + matched = True + #print "Matched " + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation (line_entry.GetFileSpec(), line_number) + + if (not matched): + mo = re.match("^\+([0-9]+)$", linespec) + if (mo != None): + matched = True + #print "Matched +" + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation(line_entry.GetFileSpec(), (line_entry.GetLine() + line_number)) + + if (not matched): + mo = re.match("^\-([0-9]+)$", linespec) + if (mo != None): + matched = True + #print "Matched -" + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation(line_entry.GetFileSpec(), (line_entry.GetLine() - line_number)) + + if (not matched): + mo = re.match("^(.*):([0-9]+)$", linespec) + if (mo != None): + matched = True + #print "Matched :" + file_name = mo.group(1) + line_number = int(mo.group(2)) + breakpoint = target.BreakpointCreateByLocation(file_name, line_number) + + if (not matched): + mo = re.match("\*((0x)?([0-9a-f]+))$", linespec) + if (mo != None): + matched = True + #print "Matched " + address = long(mo.group(1), base=0) + breakpoint = target.BreakpointCreateByAddress(address) + + if (not matched): + #print "Trying " + breakpoint = target.BreakpointCreateByName(linespec) + + num_locations = breakpoint.GetNumLocations() + + if (num_locations == 0): + result.AppendMessage("The line specification provided doesn't resolve to any addresses.") + + addr_list = [] + + for location_index in range(num_locations): + location = breakpoint.GetLocationAtIndex(location_index) + addr_list.append(location.GetAddress()) + + target.BreakpointDelete(breakpoint.GetID()) + + return addr_list + +def usage_string(): + return """ Sets the program counter to a specific address. + +Syntax: jump [] + +Command Options Usage: + jump + jump + + jump - + jump : + jump + jump * + + serves to disambiguate when multiple locations could be meant.""" + +def jump (debugger, command, result, internal_dict): + if (command == ""): + result.AppendMessage(usage_string()) + + args = command.split() + + if not debugger.IsValid(): + result.AppendMessage("Invalid debugger!") + return + + target = debugger.GetSelectedTarget() + if not target.IsValid(): + result.AppendMessage("jump requires a valid target.") + return + + process = target.GetProcess() + if not process.IsValid(): + result.AppendMessage("jump requires a valid process.") + return + + thread = process.GetSelectedThread() + if not thread.IsValid(): + result.AppendMessage("jump requires a valid thread.") + return + + frame = thread.GetSelectedFrame() + if not frame.IsValid(): + result.AppendMessage("jump requires a valid frame.") + return + + addresses = parse_linespec(args[0], frame, result) + + stream = lldb.SBStream() + + if len(addresses) == 0: + return + + desired_address = addresses[0] + + if len(addresses) > 1: + if len(args) == 2: + desired_index = int(args[1]) + if (desired_index >= 0) and (desired_index < len(addresses)): + desired_address = addresses[desired_index] + else: + result.AppendMessage("Desired index " + args[1] + " is not one of the options.") + return + else: + index = 0 + result.AppendMessage("The specified location resolves to multiple targets."); + for address in addresses: + stream.Clear() + address.GetDescription(stream) + result.AppendMessage(" Location ID " + str(index) + ": " + stream.GetData()) + index = index + 1 + result.AppendMessage("Please type 'jump " + command + " ' to choose one.") + return + + frame.SetPC(desired_address.GetLoadAddress(target)) + +if lldb.debugger: + # Module is being run inside the LLDB interpreter + jump.__doc__ = usage_string() + lldb.debugger.HandleCommand('command script add -f jump.jump jump') + print 'The "jump" command has been installed, type "help jump" or "jump " for detailed help.' diff --git a/examples/python/lldb_module_utils.py b/examples/python/lldb_module_utils.py new file mode 100644 index 000000000000..37f33ba416a5 --- /dev/null +++ b/examples/python/lldb_module_utils.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +import lldb +import optparse +import shlex +import string +import sys + +def create_dump_module_line_tables_options (): + usage = "usage: dump_module_line_tables [options] MODULE1 [MODULE2 ...]" + description='''Dumps all line tables from all compile units for any modules specified as arguments. Specifying the --verbose flag will output address ranges for each line entry.''' + parser = optparse.OptionParser(description=description, prog='start_gdb_log',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='Display verbose output.', default=False) + return parser + +def dump_module_line_tables(debugger, command, result, dict): + '''Dumps all line tables from all compile units for any modules specified as arguments.''' + command_args = shlex.split(command) + + parser = create_dump_module_line_tables_options () + try: + (options, args) = parser.parse_args(command_args) + except: + return + if command_args: + target = debugger.GetSelectedTarget() + lldb.target = target + for module_name in command_args: + result.PutCString('Searching for module "%s"' % (module_name,)) + module_fspec = lldb.SBFileSpec (module_name, False) + module = target.FindModule (module_fspec); + if module: + for cu_idx in range (module.GetNumCompileUnits()): + cu = module.GetCompileUnitAtIndex(cu_idx) + result.PutCString("\n%s:" % (cu.file)) + for line_idx in range(cu.GetNumLineEntries()): + line_entry = cu.GetLineEntryAtIndex(line_idx) + start_file_addr = line_entry.addr.file_addr + end_file_addr = line_entry.end_addr.file_addr + # If the two addresses are equal, this line table entry is a termination entry + if options.verbose: + if start_file_addr != end_file_addr: + result.PutCString('[%#x - %#x): %s' % (start_file_addr, end_file_addr, line_entry)) + else: + if start_file_addr == end_file_addr: + result.PutCString('%#x: END' % (start_file_addr)) + else: + result.PutCString('%#x: %s' % (start_file_addr, line_entry)) + if start_file_addr == end_file_addr: + result.Printf("\n") + else: + result.PutCString ("no module for '%s'" % module) + else: + result.PutCString ("error: invalid target") + +parser = create_dump_module_line_tables_options () +dump_module_line_tables.__doc__ = parser.format_help() +lldb.debugger.HandleCommand('command script add -f %s.dump_module_line_tables dump_module_line_tables' % __name__) +print 'Installed "dump_module_line_tables" command' \ No newline at end of file diff --git a/examples/python/lldbtk.py b/examples/python/lldbtk.py new file mode 100644 index 000000000000..7ada9d77b30c --- /dev/null +++ b/examples/python/lldbtk.py @@ -0,0 +1,544 @@ +#!/usr/bin/python + +import lldb +import shlex +import sys +from Tkinter import * +import ttk + +class ValueTreeItemDelegate(object): + def __init__(self, value): + self.value = value + + def get_item_dictionary(self): + name = self.value.name + if name is None: + name = '' + typename = self.value.type + if typename is None: + typename = '' + value = self.value.value + if value is None: + value = '' + summary = self.value.summary + if summary is None: + summary = '' + has_children = self.value.MightHaveChildren() + return { '#0' : name, + 'typename' : typename, + 'value' : value, + 'summary' : summary, + 'children' : has_children, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + for i in range(self.value.num_children): + item_delegate = ValueTreeItemDelegate(self.value.GetChildAtIndex(i)) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class FrameTreeItemDelegate(object): + def __init__(self, frame): + self.frame = frame + + def get_item_dictionary(self): + id = self.frame.GetFrameID() + name = 'frame #%u' % (id); + value = '0x%16.16x' % (self.frame.GetPC()) + stream = lldb.SBStream() + self.frame.GetDescription(stream) + summary = stream.GetData().split("`")[1] + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : self.frame.GetVariables(True, True, True, True).GetSize() > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + variables = self.frame.GetVariables(True, True, True, True) + n = variables.GetSize() + for i in range(n): + item_delegate = ValueTreeItemDelegate(variables[i]) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class ThreadTreeItemDelegate(object): + def __init__(self, thread): + self.thread = thread + + def get_item_dictionary(self): + num_frames = self.thread.GetNumFrames() + name = 'thread #%u' % (self.thread.GetIndexID()) + value = '0x%x' % (self.thread.GetThreadID()) + summary = '%u frames' % (num_frames) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : num_frames > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + for frame in self.thread: + item_delegate = FrameTreeItemDelegate(frame) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class ProcessTreeItemDelegate(object): + def __init__(self, process): + self.process = process + + def get_item_dictionary(self): + id = self.process.GetProcessID() + num_threads = self.process.GetNumThreads() + value = str(self.process.GetProcessID()) + summary = self.process.target.executable.fullpath + return { '#0' : 'process', + 'value': value, + 'summary': summary, + 'children' : num_threads > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + for thread in self.process: + item_delegate = ThreadTreeItemDelegate(thread) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class TargetTreeItemDelegate(object): + def __init__(self, target): + self.target = target + + def get_item_dictionary(self): + value = str(self.target.triple) + summary = self.target.executable.fullpath + return { '#0' : 'target', + 'value': value, + 'summary': summary, + 'children' : True, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + image_item_delegate = TargetImagesTreeItemDelegate(self.target) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class TargetImagesTreeItemDelegate(object): + def __init__(self, target): + self.target = target + + def get_item_dictionary(self): + value = str(self.target.triple) + summary = self.target.executable.fullpath + num_modules = self.target.GetNumModules() + return { '#0' : 'images', + 'value': '', + 'summary': '%u images' % num_modules, + 'children' : num_modules > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + for i in range(self.target.GetNumModules()): + module = self.target.GetModuleAtIndex(i) + image_item_delegate = ModuleTreeItemDelegate(self.target, module, i) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class ModuleTreeItemDelegate(object): + def __init__(self, target, module, index): + self.target = target + self.module = module + self.index = index + + def get_item_dictionary(self): + name = 'module %u' % (self.index) + value = self.module.file.basename + summary = self.module.file.dirname + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : True, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + sections_item_delegate = ModuleSectionsTreeItemDelegate(self.target, self.module) + item_dicts.append(sections_item_delegate.get_item_dictionary()) + + symbols_item_delegate = ModuleSymbolsTreeItemDelegate(self.target, self.module) + item_dicts.append(symbols_item_delegate.get_item_dictionary()) + + comp_units_item_delegate = ModuleCompileUnitsTreeItemDelegate(self.target, self.module) + item_dicts.append(comp_units_item_delegate.get_item_dictionary()) + return item_dicts + +class ModuleSectionsTreeItemDelegate(object): + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'sections' + value = '' + summary = '%u sections' % (self.module.GetNumSections()) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : True, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_sections = self.module.GetNumSections() + for i in range(num_sections): + section = self.module.GetSectionAtIndex(i) + image_item_delegate = SectionTreeItemDelegate(self.target, section) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class SectionTreeItemDelegate(object): + def __init__(self, target, section): + self.target = target + self.section = section + + def get_item_dictionary(self): + name = self.section.name + section_load_addr = self.section.GetLoadAddress(self.target) + if section_load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (section_load_addr) + else: + value = '0x%16.16x *' % (self.section.file_addr) + summary = '' + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : self.section.GetNumSubSections() > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_sections = self.section.GetNumSubSections() + for i in range(num_sections): + section = self.section.GetSubSectionAtIndex(i) + image_item_delegate = SectionTreeItemDelegate(self.target, section) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class ModuleCompileUnitsTreeItemDelegate(object): + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'compile units' + value = '' + summary = '%u compile units' % (self.module.GetNumSections()) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : self.module.GetNumCompileUnits() > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_cus = self.module.GetNumCompileUnits() + for i in range(num_cus): + cu = self.module.GetCompileUnitAtIndex(i) + image_item_delegate = CompileUnitTreeItemDelegate(self.target, cu) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class CompileUnitTreeItemDelegate(object): + def __init__(self, target, cu): + self.target = target + self.cu = cu + + def get_item_dictionary(self): + name = self.cu.GetFileSpec().basename + value = '' + num_lines = self.cu.GetNumLineEntries() + summary = '' + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : num_lines > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + item_delegate = LineTableTreeItemDelegate(self.target, self.cu) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class LineTableTreeItemDelegate(object): + def __init__(self, target, cu): + self.target = target + self.cu = cu + + def get_item_dictionary(self): + name = 'line table' + value = '' + num_lines = self.cu.GetNumLineEntries() + summary = '%u line entries' % (num_lines) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : num_lines > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_lines = self.cu.GetNumLineEntries() + for i in range(num_lines): + line_entry = self.cu.GetLineEntryAtIndex(i) + item_delegate = LineEntryTreeItemDelegate(self.target, line_entry, i) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class LineEntryTreeItemDelegate(object): + def __init__(self, target, line_entry, index): + self.target = target + self.line_entry = line_entry + self.index = index + + def get_item_dictionary(self): + name = str(self.index) + address = self.line_entry.GetStartAddress() + load_addr = address.GetLoadAddress(self.target) + if load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (load_addr) + else: + value = '0x%16.16x *' % (address.file_addr) + summary = self.line_entry.GetFileSpec().fullpath + ':' + str(self.line_entry.line) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : False, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + return item_dicts + +class InstructionTreeItemDelegate(object): + def __init__(self, target, instr): + self.target = target + self.instr = instr + + def get_item_dictionary(self): + address = self.instr.GetAddress() + load_addr = address.GetLoadAddress(self.target) + if load_addr != lldb.LLDB_INVALID_ADDRESS: + name = '0x%16.16x' % (load_addr) + else: + name = '0x%16.16x *' % (address.file_addr) + value = self.instr.GetMnemonic(self.target) + ' ' + self.instr.GetOperands(self.target) + summary = self.instr.GetComment(self.target) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : False, + 'tree-item-delegate' : self } + +class ModuleSymbolsTreeItemDelegate(object): + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'symbols' + value = '' + summary = '%u symbols' % (self.module.GetNumSymbols()) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : True, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_symbols = self.module.GetNumSymbols() + for i in range(num_symbols): + symbol = self.module.GetSymbolAtIndex(i) + image_item_delegate = SymbolTreeItemDelegate(self.target, symbol, i) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class SymbolTreeItemDelegate(object): + def __init__(self, target, symbol, index): + self.target = target + self.symbol = symbol + self.index = index + + def get_item_dictionary(self): + address = self.symbol.GetStartAddress() + name = '[%u]' % self.index + symbol_load_addr = address.GetLoadAddress(self.target) + if symbol_load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (symbol_load_addr) + else: + value = '0x%16.16x *' % (address.file_addr) + summary = self.symbol.name + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : False, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + return item_dicts + + + +class DelegateTree(ttk.Frame): + + def __init__(self, column_dicts, delegate, title, name): + ttk.Frame.__init__(self, name=name) + self.pack(expand=Y, fill=BOTH) + self.master.title(title) + self.delegate = delegate + self.columns_dicts = column_dicts + self.item_id_to_item_dict = dict() + frame = Frame(self) + frame.pack(side=TOP, fill=BOTH, expand=Y) + self._create_treeview(frame) + self._populate_root() + + def _create_treeview(self, parent): + frame = ttk.Frame(parent) + frame.pack(side=TOP, fill=BOTH, expand=Y) + + column_ids = list() + for i in range(1,len(self.columns_dicts)): + column_ids.append(self.columns_dicts[i]['id']) + # create the tree and scrollbars + self.tree = ttk.Treeview(columns=column_ids) + + scroll_bar_v = ttk.Scrollbar(orient=VERTICAL, command= self.tree.yview) + scroll_bar_h = ttk.Scrollbar(orient=HORIZONTAL, command= self.tree.xview) + self.tree['yscroll'] = scroll_bar_v.set + self.tree['xscroll'] = scroll_bar_h.set + + # setup column headings and columns properties + for columns_dict in self.columns_dicts: + self.tree.heading(columns_dict['id'], text=columns_dict['text'], anchor=columns_dict['anchor']) + self.tree.column(columns_dict['id'], stretch=columns_dict['stretch']) + + # add tree and scrollbars to frame + self.tree.grid(in_=frame, row=0, column=0, sticky=NSEW) + scroll_bar_v.grid(in_=frame, row=0, column=1, sticky=NS) + scroll_bar_h.grid(in_=frame, row=1, column=0, sticky=EW) + + # set frame resizing priorities + frame.rowconfigure(0, weight=1) + frame.columnconfigure(0, weight=1) + + # action to perform when a node is expanded + self.tree.bind('<>', self._update_tree) + + def insert_items(self, parent_id, item_dicts): + for item_dict in item_dicts: + name = None + values = list() + first = True + for columns_dict in self.columns_dicts: + if first: + name = item_dict[columns_dict['id']] + first = False + else: + values.append(item_dict[columns_dict['id']]) + item_id = self.tree.insert (parent_id, # root item has an empty name + END, + text=name, + values=values) + self.item_id_to_item_dict[item_id] = item_dict + if item_dict['children']: + self.tree.insert(item_id, END, text='dummy') + + def _populate_root(self): + # use current directory as root node + self.insert_items('', self.delegate.get_child_item_dictionaries()) + + def _update_tree(self, event): + # user expanded a node - build the related directory + item_id = self.tree.focus() # the id of the expanded node + children = self.tree.get_children (item_id) + if len(children): + first_child = children[0] + # if the node only has a 'dummy' child, remove it and + # build new directory; skip if the node is already + # populated + if self.tree.item(first_child, option='text') == 'dummy': + self.tree.delete(first_child) + item_dict = self.item_id_to_item_dict[item_id] + item_dicts = item_dict['tree-item-delegate'].get_child_item_dictionaries() + self.insert_items(item_id, item_dicts) + +@lldb.command("tk-variables") +def tk_variable_display(debugger, command, result, dict): + sys.argv = ['tk-variables'] # needed for tree creation in TK library as it uses sys.argv... + target = debugger.GetSelectedTarget() + if not target: + print >>result, "invalid target" + return + process = target.GetProcess() + if not process: + print >>result, "invalid process" + return + thread = process.GetSelectedThread() + if not thread: + print >>result, "invalid thread" + return + frame = thread.GetSelectedFrame() + if not frame: + print >>result, "invalid frame" + return + # Parse command line args + command_args = shlex.split(command) + column_dicts = [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'typename', 'text' : 'Type' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'summary' , 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }] + tree = DelegateTree(column_dicts, FrameTreeItemDelegate(frame), 'Variables', 'lldb-tk-variables') + tree.mainloop() + +@lldb.command("tk-process") +def tk_process_display(debugger, command, result, dict): + sys.argv = ['tk-process'] # needed for tree creation in TK library as it uses sys.argv... + target = debugger.GetSelectedTarget() + if not target: + print >>result, "invalid target" + return + process = target.GetProcess() + if not process: + print >>result, "invalid process" + return + # Parse command line args + columnd_dicts = [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'summary', 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }]; + command_args = shlex.split(command) + tree = DelegateTree(columnd_dicts, ProcessTreeItemDelegate(process), 'Process', 'lldb-tk-process') + tree.mainloop() + +@lldb.command("tk-target") +def tk_target_display(debugger, command, result, dict): + sys.argv = ['tk-target'] # needed for tree creation in TK library as it uses sys.argv... + target = debugger.GetSelectedTarget() + if not target: + print >>result, "invalid target" + return + # Parse command line args + columnd_dicts = [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'summary', 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }]; + command_args = shlex.split(command) + tree = DelegateTree(columnd_dicts, TargetTreeItemDelegate(target), 'Target', 'lldb-tk-target') + tree.mainloop() + diff --git a/examples/python/mach_o.py b/examples/python/mach_o.py new file mode 100755 index 000000000000..a609b09ed0eb --- /dev/null +++ b/examples/python/mach_o.py @@ -0,0 +1,1687 @@ +#!/usr/bin/python + +import cmd +import dict_utils +import file_extract +import optparse +import re +import struct +import string +import StringIO +import sys +import uuid + +# Mach header "magic" constants +MH_MAGIC = 0xfeedface +MH_CIGAM = 0xcefaedfe +MH_MAGIC_64 = 0xfeedfacf +MH_CIGAM_64 = 0xcffaedfe +FAT_MAGIC = 0xcafebabe +FAT_CIGAM = 0xbebafeca + +# Mach haeder "filetype" constants +MH_OBJECT = 0x00000001 +MH_EXECUTE = 0x00000002 +MH_FVMLIB = 0x00000003 +MH_CORE = 0x00000004 +MH_PRELOAD = 0x00000005 +MH_DYLIB = 0x00000006 +MH_DYLINKER = 0x00000007 +MH_BUNDLE = 0x00000008 +MH_DYLIB_STUB = 0x00000009 +MH_DSYM = 0x0000000a +MH_KEXT_BUNDLE = 0x0000000b + +# Mach haeder "flag" constant bits +MH_NOUNDEFS = 0x00000001 +MH_INCRLINK = 0x00000002 +MH_DYLDLINK = 0x00000004 +MH_BINDATLOAD = 0x00000008 +MH_PREBOUND = 0x00000010 +MH_SPLIT_SEGS = 0x00000020 +MH_LAZY_INIT = 0x00000040 +MH_TWOLEVEL = 0x00000080 +MH_FORCE_FLAT = 0x00000100 +MH_NOMULTIDEFS = 0x00000200 +MH_NOFIXPREBINDING = 0x00000400 +MH_PREBINDABLE = 0x00000800 +MH_ALLMODSBOUND = 0x00001000 +MH_SUBSECTIONS_VIA_SYMBOLS = 0x00002000 +MH_CANONICAL = 0x00004000 +MH_WEAK_DEFINES = 0x00008000 +MH_BINDS_TO_WEAK = 0x00010000 +MH_ALLOW_STACK_EXECUTION = 0x00020000 +MH_ROOT_SAFE = 0x00040000 +MH_SETUID_SAFE = 0x00080000 +MH_NO_REEXPORTED_DYLIBS = 0x00100000 +MH_PIE = 0x00200000 +MH_DEAD_STRIPPABLE_DYLIB = 0x00400000 +MH_HAS_TLV_DESCRIPTORS = 0x00800000 +MH_NO_HEAP_EXECUTION = 0x01000000 + +# Mach load command constants +LC_REQ_DYLD = 0x80000000 +LC_SEGMENT = 0x00000001 +LC_SYMTAB = 0x00000002 +LC_SYMSEG = 0x00000003 +LC_THREAD = 0x00000004 +LC_UNIXTHREAD = 0x00000005 +LC_LOADFVMLIB = 0x00000006 +LC_IDFVMLIB = 0x00000007 +LC_IDENT = 0x00000008 +LC_FVMFILE = 0x00000009 +LC_PREPAGE = 0x0000000a +LC_DYSYMTAB = 0x0000000b +LC_LOAD_DYLIB = 0x0000000c +LC_ID_DYLIB = 0x0000000d +LC_LOAD_DYLINKER = 0x0000000e +LC_ID_DYLINKER = 0x0000000f +LC_PREBOUND_DYLIB = 0x00000010 +LC_ROUTINES = 0x00000011 +LC_SUB_FRAMEWORK = 0x00000012 +LC_SUB_UMBRELLA = 0x00000013 +LC_SUB_CLIENT = 0x00000014 +LC_SUB_LIBRARY = 0x00000015 +LC_TWOLEVEL_HINTS = 0x00000016 +LC_PREBIND_CKSUM = 0x00000017 +LC_LOAD_WEAK_DYLIB = 0x00000018 | LC_REQ_DYLD +LC_SEGMENT_64 = 0x00000019 +LC_ROUTINES_64 = 0x0000001a +LC_UUID = 0x0000001b +LC_RPATH = 0x0000001c | LC_REQ_DYLD +LC_CODE_SIGNATURE = 0x0000001d +LC_SEGMENT_SPLIT_INFO = 0x0000001e +LC_REEXPORT_DYLIB = 0x0000001f | LC_REQ_DYLD +LC_LAZY_LOAD_DYLIB = 0x00000020 +LC_ENCRYPTION_INFO = 0x00000021 +LC_DYLD_INFO = 0x00000022 +LC_DYLD_INFO_ONLY = 0x00000022 | LC_REQ_DYLD +LC_LOAD_UPWARD_DYLIB = 0x00000023 | LC_REQ_DYLD +LC_VERSION_MIN_MACOSX = 0x00000024 +LC_VERSION_MIN_IPHONEOS = 0x00000025 +LC_FUNCTION_STARTS = 0x00000026 +LC_DYLD_ENVIRONMENT = 0x00000027 + +# Mach CPU constants +CPU_ARCH_MASK = 0xff000000 +CPU_ARCH_ABI64 = 0x01000000 +CPU_TYPE_ANY = 0xffffffff +CPU_TYPE_VAX = 1 +CPU_TYPE_MC680x0 = 6 +CPU_TYPE_I386 = 7 +CPU_TYPE_X86_64 = CPU_TYPE_I386 | CPU_ARCH_ABI64 +CPU_TYPE_MIPS = 8 +CPU_TYPE_MC98000 = 10 +CPU_TYPE_HPPA = 11 +CPU_TYPE_ARM = 12 +CPU_TYPE_MC88000 = 13 +CPU_TYPE_SPARC = 14 +CPU_TYPE_I860 = 15 +CPU_TYPE_ALPHA = 16 +CPU_TYPE_POWERPC = 18 +CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64 + +# VM protection constants +VM_PROT_READ = 1 +VM_PROT_WRITE = 2 +VM_PROT_EXECUTE = 4 + +# VM protection constants +N_STAB = 0xe0 +N_PEXT = 0x10 +N_TYPE = 0x0e +N_EXT = 0x01 + +# Values for nlist N_TYPE bits of the "Mach.NList.type" field. +N_UNDF = 0x0 +N_ABS = 0x2 +N_SECT = 0xe +N_PBUD = 0xc +N_INDR = 0xa + +# Section indexes for the "Mach.NList.sect_idx" fields +NO_SECT = 0 +MAX_SECT = 255 + +# Stab defines +N_GSYM = 0x20 +N_FNAME = 0x22 +N_FUN = 0x24 +N_STSYM = 0x26 +N_LCSYM = 0x28 +N_BNSYM = 0x2e +N_OPT = 0x3c +N_RSYM = 0x40 +N_SLINE = 0x44 +N_ENSYM = 0x4e +N_SSYM = 0x60 +N_SO = 0x64 +N_OSO = 0x66 +N_LSYM = 0x80 +N_BINCL = 0x82 +N_SOL = 0x84 +N_PARAMS = 0x86 +N_VERSION = 0x88 +N_OLEVEL = 0x8A +N_PSYM = 0xa0 +N_EINCL = 0xa2 +N_ENTRY = 0xa4 +N_LBRAC = 0xc0 +N_EXCL = 0xc2 +N_RBRAC = 0xe0 +N_BCOMM = 0xe2 +N_ECOMM = 0xe4 +N_ECOML = 0xe8 +N_LENG = 0xfe + +vm_prot_names = [ '---', 'r--', '-w-', 'rw-', '--x', 'r-x', '-wx', 'rwx' ] + +def dump_memory(base_addr, data, hex_bytes_len, num_per_line): + hex_bytes = data.encode('hex') + if hex_bytes_len == -1: + hex_bytes_len = len(hex_bytes) + addr = base_addr + ascii_str = '' + i = 0 + while i < hex_bytes_len: + if ((i/2) % num_per_line) == 0: + if i > 0: + print ' %s' % (ascii_str) + ascii_str = '' + print '0x%8.8x:' % (addr+i), + hex_byte = hex_bytes[i:i+2] + print hex_byte, + int_byte = int (hex_byte, 16) + ascii_char = '%c' % (int_byte) + if int_byte >= 32 and int_byte < 127: + ascii_str += ascii_char + else: + ascii_str += '.' + i = i + 2 + if ascii_str: + if (i/2) % num_per_line: + padding = num_per_line - ((i/2) % num_per_line) + else: + padding = 0 + print '%*s%s' % (padding*3+1,'',ascii_str) + print + + +class TerminalColors: + '''Simple terminal colors class''' + def __init__(self, enabled = True): + # TODO: discover terminal type from "file" and disable if + # it can't handle the color codes + self.enabled = enabled + + def reset(self): + '''Reset all terminal colors and formatting.''' + if self.enabled: + return "\x1b[0m"; + return '' + + def bold(self, on = True): + '''Enable or disable bold depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[1m"; + else: + return "\x1b[22m"; + return '' + + def italics(self, on = True): + '''Enable or disable italics depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[3m"; + else: + return "\x1b[23m"; + return '' + + def underline(self, on = True): + '''Enable or disable underline depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[4m"; + else: + return "\x1b[24m"; + return '' + + def inverse(self, on = True): + '''Enable or disable inverse depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[7m"; + else: + return "\x1b[27m"; + return '' + + def strike(self, on = True): + '''Enable or disable strike through depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[9m"; + else: + return "\x1b[29m"; + return '' + + def black(self, fg = True): + '''Set the foreground or background color to black. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[30m"; + else: + return "\x1b[40m"; + return '' + + def red(self, fg = True): + '''Set the foreground or background color to red. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[31m"; + else: + return "\x1b[41m"; + return '' + + def green(self, fg = True): + '''Set the foreground or background color to green. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[32m"; + else: + return "\x1b[42m"; + return '' + + def yellow(self, fg = True): + '''Set the foreground or background color to yellow. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[43m"; + else: + return "\x1b[33m"; + return '' + + def blue(self, fg = True): + '''Set the foreground or background color to blue. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[34m"; + else: + return "\x1b[44m"; + return '' + + def magenta(self, fg = True): + '''Set the foreground or background color to magenta. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[35m"; + else: + return "\x1b[45m"; + return '' + + def cyan(self, fg = True): + '''Set the foreground or background color to cyan. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[36m"; + else: + return "\x1b[46m"; + return '' + + def white(self, fg = True): + '''Set the foreground or background color to white. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[37m"; + else: + return "\x1b[47m"; + return '' + + def default(self, fg = True): + '''Set the foreground or background color to the default. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[39m"; + else: + return "\x1b[49m"; + return '' + +def swap_unpack_char(): + """Returns the unpack prefix that will for non-native endian-ness.""" + if struct.pack('H', 1).startswith("\x00"): + return '<' + return '>' + + +def dump_hex_bytes(addr, s, bytes_per_line=16): + i = 0 + line = '' + for ch in s: + if (i % bytes_per_line) == 0: + if line: + print line + line = '%#8.8x: ' % (addr + i) + line += "%02X " % ord(ch) + i += 1 + print line + +def dump_hex_byte_string_diff(addr, a, b, bytes_per_line=16): + i = 0 + line = '' + a_len = len(a) + b_len = len(b) + if a_len < b_len: + max_len = b_len + else: + max_len = a_len + tty_colors = TerminalColors (True) + for i in range(max_len): + ch = None + if i < a_len: + ch_a = a[i] + ch = ch_a + else: + ch_a = None + if i < b_len: + ch_b = b[i] + if not ch: + ch = ch_b + else: + ch_b = None + mismatch = ch_a != ch_b + if (i % bytes_per_line) == 0: + if line: + print line + line = '%#8.8x: ' % (addr + i) + if mismatch: line += tty_colors.red() + line += "%02X " % ord(ch) + if mismatch: line += tty_colors.default() + i += 1 + + print line + +class Mach: + """Class that does everything mach-o related""" + + class Arch: + """Class that implements mach-o architectures""" + + def __init__(self, c=0, s=0): + self.cpu=c + self.sub=s + + def set_cpu_type(self, c): + self.cpu=c + def set_cpu_subtype(self, s): + self.sub=s + def set_arch(self, c, s): + self.cpu=c + self.sub=s + def is_64_bit(self): + return (self.cpu & CPU_ARCH_ABI64) != 0 + + cpu_infos = [ + [ "arm" , CPU_TYPE_ARM , CPU_TYPE_ANY ], + [ "arm" , CPU_TYPE_ARM , 0 ], + [ "armv4" , CPU_TYPE_ARM , 5 ], + [ "armv6" , CPU_TYPE_ARM , 6 ], + [ "armv5" , CPU_TYPE_ARM , 7 ], + [ "xscale" , CPU_TYPE_ARM , 8 ], + [ "armv7" , CPU_TYPE_ARM , 9 ], + [ "armv7f" , CPU_TYPE_ARM , 10 ], + [ "armv7s" , CPU_TYPE_ARM , 11 ], + [ "armv7k" , CPU_TYPE_ARM , 12 ], + [ "armv7m" , CPU_TYPE_ARM , 15 ], + [ "armv7em" , CPU_TYPE_ARM , 16 ], + [ "ppc" , CPU_TYPE_POWERPC , CPU_TYPE_ANY ], + [ "ppc" , CPU_TYPE_POWERPC , 0 ], + [ "ppc601" , CPU_TYPE_POWERPC , 1 ], + [ "ppc602" , CPU_TYPE_POWERPC , 2 ], + [ "ppc603" , CPU_TYPE_POWERPC , 3 ], + [ "ppc603e" , CPU_TYPE_POWERPC , 4 ], + [ "ppc603ev" , CPU_TYPE_POWERPC , 5 ], + [ "ppc604" , CPU_TYPE_POWERPC , 6 ], + [ "ppc604e" , CPU_TYPE_POWERPC , 7 ], + [ "ppc620" , CPU_TYPE_POWERPC , 8 ], + [ "ppc750" , CPU_TYPE_POWERPC , 9 ], + [ "ppc7400" , CPU_TYPE_POWERPC , 10 ], + [ "ppc7450" , CPU_TYPE_POWERPC , 11 ], + [ "ppc970" , CPU_TYPE_POWERPC , 100 ], + [ "ppc64" , CPU_TYPE_POWERPC64 , 0 ], + [ "ppc970-64" , CPU_TYPE_POWERPC64 , 100 ], + [ "i386" , CPU_TYPE_I386 , 3 ], + [ "i486" , CPU_TYPE_I386 , 4 ], + [ "i486sx" , CPU_TYPE_I386 , 0x84 ], + [ "i386" , CPU_TYPE_I386 , CPU_TYPE_ANY ], + [ "x86_64" , CPU_TYPE_X86_64 , 3 ], + [ "x86_64" , CPU_TYPE_X86_64 , CPU_TYPE_ANY ], + ] + + def __str__(self): + for info in self.cpu_infos: + if self.cpu == info[1] and (self.sub & 0x00ffffff) == info[2]: + return info[0] + return "{0}.{1}".format(self.cpu,self.sub) + + + class Magic(dict_utils.Enum): + + enum = { + 'MH_MAGIC' : MH_MAGIC, + 'MH_CIGAM' : MH_CIGAM, + 'MH_MAGIC_64' : MH_MAGIC_64, + 'MH_CIGAM_64' : MH_CIGAM_64, + 'FAT_MAGIC' : FAT_MAGIC, + 'FAT_CIGAM' : FAT_CIGAM + } + + def __init__(self, initial_value = 0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + def is_skinny_mach_file(self): + return self.value == MH_MAGIC or self.value == MH_CIGAM or self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64 + + def is_universal_mach_file(self): + return self.value == FAT_MAGIC or self.value == FAT_CIGAM + + def unpack(self, data): + data.set_byte_order('native') + self.value = data.get_uint32(); + + def get_byte_order(self): + if self.value == MH_CIGAM or self.value == MH_CIGAM_64 or self.value == FAT_CIGAM: + return swap_unpack_char() + else: + return '=' + + def is_64_bit(self): + return self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64 + + def __init__(self): + self.magic = Mach.Magic() + self.content = None + self.path = None + + def extract (self, path, extractor): + self.path = path; + self.unpack(extractor) + + def parse(self, path): + self.path = path; + try: + f = open(self.path) + file_extractor = file_extract.FileExtract(f, '=') + self.unpack(file_extractor) + #f.close() + except IOError as (errno, strerror): + print "I/O error({0}): {1}".format(errno, strerror) + except ValueError: + print "Could not convert data to an integer." + except: + print "Unexpected error:", sys.exc_info()[0] + raise + + def compare(self, rhs): + self.content.compare(rhs.content) + + def dump(self, options = None): + self.content.dump(options) + + def dump_header(self, dump_description = True, options = None): + self.content.dump_header(dump_description, options) + + def dump_load_commands(self, dump_description = True, options = None): + self.content.dump_load_commands(dump_description, options) + + def dump_sections(self, dump_description = True, options = None): + self.content.dump_sections(dump_description, options) + + def dump_section_contents(self, options): + self.content.dump_section_contents(options) + + def dump_symtab(self, dump_description = True, options = None): + self.content.dump_symtab(dump_description, options) + + def dump_symbol_names_matching_regex(self, regex, file=None): + self.content.dump_symbol_names_matching_regex(regex, file) + + def description(self): + return self.content.description() + + def unpack(self, data): + self.magic.unpack(data) + if self.magic.is_skinny_mach_file(): + self.content = Mach.Skinny(self.path) + elif self.magic.is_universal_mach_file(): + self.content = Mach.Universal(self.path) + else: + self.content = None + + if self.content != None: + self.content.unpack(data, self.magic) + + def is_valid(self): + return self.content != None + + class Universal: + + def __init__(self, path): + self.path = path + self.type = 'universal' + self.file_off = 0 + self.magic = None + self.nfat_arch = 0 + self.archs = list() + + def description(self): + s = '%#8.8x: %s (' % (self.file_off, self.path) + archs_string = '' + for arch in self.archs: + if len(archs_string): + archs_string += ', ' + archs_string += '%s' % arch.arch + s += archs_string + s += ')' + return s + + def unpack(self, data, magic = None): + self.file_off = data.tell() + if magic is None: + self.magic = Mach.Magic() + self.magic.unpack(data) + else: + self.magic = magic + self.file_off = self.file_off - 4 + # Universal headers are always in big endian + data.set_byte_order('big') + self.nfat_arch = data.get_uint32() + for i in range(self.nfat_arch): + self.archs.append(Mach.Universal.ArchInfo()) + self.archs[i].unpack(data) + for i in range(self.nfat_arch): + self.archs[i].mach = Mach.Skinny(self.path) + data.seek (self.archs[i].offset, 0) + skinny_magic = Mach.Magic() + skinny_magic.unpack (data) + self.archs[i].mach.unpack(data, skinny_magic) + + def compare(self, rhs): + print 'error: comparing two universal files is not supported yet' + return False + + def dump(self, options): + if options.dump_header: + print + print "Universal Mach File: magic = %s, nfat_arch = %u" % (self.magic, self.nfat_arch) + print + if self.nfat_arch > 0: + if options.dump_header: + self.archs[0].dump_header(True, options) + for i in range(self.nfat_arch): + self.archs[i].dump_flat(options) + if options.dump_header: + print + for i in range(self.nfat_arch): + self.archs[i].mach.dump(options) + + def dump_header(self, dump_description = True, options = None): + if dump_description: + print self.description() + for i in range(self.nfat_arch): + self.archs[i].mach.dump_header(True, options) + print + + def dump_load_commands(self, dump_description = True, options = None): + if dump_description: + print self.description() + for i in range(self.nfat_arch): + self.archs[i].mach.dump_load_commands(True, options) + print + + def dump_sections(self, dump_description = True, options = None): + if dump_description: + print self.description() + for i in range(self.nfat_arch): + self.archs[i].mach.dump_sections(True, options) + print + + def dump_section_contents(self, options): + for i in range(self.nfat_arch): + self.archs[i].mach.dump_section_contents(options) + print + + def dump_symtab(self, dump_description = True, options = None): + if dump_description: + print self.description() + for i in range(self.nfat_arch): + self.archs[i].mach.dump_symtab(True, options) + print + + def dump_symbol_names_matching_regex(self, regex, file=None): + for i in range(self.nfat_arch): + self.archs[i].mach.dump_symbol_names_matching_regex(regex, file) + + class ArchInfo: + + def __init__(self): + self.arch = Mach.Arch(0,0) + self.offset = 0 + self.size = 0 + self.align = 0 + self.mach = None + + def unpack(self, data): + # Universal headers are always in big endian + data.set_byte_order('big') + self.arch.cpu, self.arch.sub, self.offset, self.size, self.align = data.get_n_uint32(5) + + def dump_header(self, dump_description = True, options = None): + if options.verbose: + print "CPU SUBTYPE OFFSET SIZE ALIGN" + print "---------- ---------- ---------- ---------- ----------" + else: + print "ARCH FILEOFFSET FILESIZE ALIGN" + print "---------- ---------- ---------- ----------" + def dump_flat(self, options): + if options.verbose: + print "%#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) + else: + print "%-10s %#8.8x %#8.8x %#8.8x" % (self.arch, self.offset, self.size, self.align) + def dump(self): + print " cputype: %#8.8x" % self.arch.cpu + print "cpusubtype: %#8.8x" % self.arch.sub + print " offset: %#8.8x" % self.offset + print " size: %#8.8x" % self.size + print " align: %#8.8x" % self.align + def __str__(self): + return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) + def __repr__(self): + return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) + + class Flags: + + def __init__(self, b): + self.bits = b + + def __str__(self): + s = '' + if self.bits & MH_NOUNDEFS: + s += 'MH_NOUNDEFS | ' + if self.bits & MH_INCRLINK: + s += 'MH_INCRLINK | ' + if self.bits & MH_DYLDLINK: + s += 'MH_DYLDLINK | ' + if self.bits & MH_BINDATLOAD: + s += 'MH_BINDATLOAD | ' + if self.bits & MH_PREBOUND: + s += 'MH_PREBOUND | ' + if self.bits & MH_SPLIT_SEGS: + s += 'MH_SPLIT_SEGS | ' + if self.bits & MH_LAZY_INIT: + s += 'MH_LAZY_INIT | ' + if self.bits & MH_TWOLEVEL: + s += 'MH_TWOLEVEL | ' + if self.bits & MH_FORCE_FLAT: + s += 'MH_FORCE_FLAT | ' + if self.bits & MH_NOMULTIDEFS: + s += 'MH_NOMULTIDEFS | ' + if self.bits & MH_NOFIXPREBINDING: + s += 'MH_NOFIXPREBINDING | ' + if self.bits & MH_PREBINDABLE: + s += 'MH_PREBINDABLE | ' + if self.bits & MH_ALLMODSBOUND: + s += 'MH_ALLMODSBOUND | ' + if self.bits & MH_SUBSECTIONS_VIA_SYMBOLS: + s += 'MH_SUBSECTIONS_VIA_SYMBOLS | ' + if self.bits & MH_CANONICAL: + s += 'MH_CANONICAL | ' + if self.bits & MH_WEAK_DEFINES: + s += 'MH_WEAK_DEFINES | ' + if self.bits & MH_BINDS_TO_WEAK: + s += 'MH_BINDS_TO_WEAK | ' + if self.bits & MH_ALLOW_STACK_EXECUTION: + s += 'MH_ALLOW_STACK_EXECUTION | ' + if self.bits & MH_ROOT_SAFE: + s += 'MH_ROOT_SAFE | ' + if self.bits & MH_SETUID_SAFE: + s += 'MH_SETUID_SAFE | ' + if self.bits & MH_NO_REEXPORTED_DYLIBS: + s += 'MH_NO_REEXPORTED_DYLIBS | ' + if self.bits & MH_PIE: + s += 'MH_PIE | ' + if self.bits & MH_DEAD_STRIPPABLE_DYLIB: + s += 'MH_DEAD_STRIPPABLE_DYLIB | ' + if self.bits & MH_HAS_TLV_DESCRIPTORS: + s += 'MH_HAS_TLV_DESCRIPTORS | ' + if self.bits & MH_NO_HEAP_EXECUTION: + s += 'MH_NO_HEAP_EXECUTION | ' + # Strip the trailing " |" if we have any flags + if len(s) > 0: + s = s[0:-2] + return s + + class FileType(dict_utils.Enum): + + enum = { + 'MH_OBJECT' : MH_OBJECT , + 'MH_EXECUTE' : MH_EXECUTE , + 'MH_FVMLIB' : MH_FVMLIB , + 'MH_CORE' : MH_CORE , + 'MH_PRELOAD' : MH_PRELOAD , + 'MH_DYLIB' : MH_DYLIB , + 'MH_DYLINKER' : MH_DYLINKER , + 'MH_BUNDLE' : MH_BUNDLE , + 'MH_DYLIB_STUB' : MH_DYLIB_STUB , + 'MH_DSYM' : MH_DSYM , + 'MH_KEXT_BUNDLE' : MH_KEXT_BUNDLE + } + + def __init__(self, initial_value = 0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + class Skinny: + + def __init__(self, path): + self.path = path + self.type = 'skinny' + self.data = None + self.file_off = 0 + self.magic = 0 + self.arch = Mach.Arch(0,0) + self.filetype = Mach.FileType(0) + self.ncmds = 0 + self.sizeofcmds = 0 + self.flags = Mach.Flags(0) + self.uuid = None + self.commands = list() + self.segments = list() + self.sections = list() + self.symbols = list() + self.sections.append(Mach.Section()) + + def description(self): + return '%#8.8x: %s (%s)' % (self.file_off, self.path, self.arch) + + def unpack(self, data, magic = None): + self.data = data + self.file_off = data.tell() + if magic is None: + self.magic = Mach.Magic() + self.magic.unpack(data) + else: + self.magic = magic + self.file_off = self.file_off - 4 + data.set_byte_order(self.magic.get_byte_order()) + self.arch.cpu, self.arch.sub, self.filetype.value, self.ncmds, self.sizeofcmds, bits = data.get_n_uint32(6) + self.flags.bits = bits + + if self.is_64_bit(): + data.get_uint32() # Skip reserved word in mach_header_64 + + for i in range(0,self.ncmds): + lc = self.unpack_load_command (data) + self.commands.append (lc) + + def get_data(self): + if self.data: + self.data.set_byte_order(self.magic.get_byte_order()) + return self.data + return None + + def unpack_load_command (self, data): + lc = Mach.LoadCommand() + lc.unpack (self, data) + lc_command = lc.command.get_enum_value(); + if (lc_command == LC_SEGMENT or + lc_command == LC_SEGMENT_64): + lc = Mach.SegmentLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_LOAD_DYLIB or + lc_command == LC_ID_DYLIB or + lc_command == LC_LOAD_WEAK_DYLIB or + lc_command == LC_REEXPORT_DYLIB): + lc = Mach.DylibLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_LOAD_DYLINKER or + lc_command == LC_SUB_FRAMEWORK or + lc_command == LC_SUB_CLIENT or + lc_command == LC_SUB_UMBRELLA or + lc_command == LC_SUB_LIBRARY or + lc_command == LC_ID_DYLINKER or + lc_command == LC_RPATH): + lc = Mach.LoadDYLDLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_DYLD_INFO_ONLY): + lc = Mach.DYLDInfoOnlyLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_SYMTAB): + lc = Mach.SymtabLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_DYSYMTAB): + lc = Mach.DYLDSymtabLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_UUID): + lc = Mach.UUIDLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_CODE_SIGNATURE or + lc_command == LC_SEGMENT_SPLIT_INFO or + lc_command == LC_FUNCTION_STARTS): + lc = Mach.DataBlobLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_UNIXTHREAD): + lc = Mach.UnixThreadLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_ENCRYPTION_INFO): + lc = Mach.EncryptionInfoLoadCommand(lc) + lc.unpack(self, data) + lc.skip(data) + return lc + + def compare(self, rhs): + print "\nComparing:" + print "a) %s %s" % (self.arch, self.path) + print "b) %s %s" % (rhs.arch, rhs.path) + result = True + if self.type == rhs.type: + for lhs_section in self.sections[1:]: + rhs_section = rhs.get_section_by_section(lhs_section) + if rhs_section: + print 'comparing %s.%s...' % (lhs_section.segname, lhs_section.sectname), + sys.stdout.flush() + lhs_data = lhs_section.get_contents (self) + rhs_data = rhs_section.get_contents (rhs) + if lhs_data and rhs_data: + if lhs_data == rhs_data: + print 'ok' + else: + lhs_data_len = len(lhs_data) + rhs_data_len = len(rhs_data) + # if lhs_data_len < rhs_data_len: + # if lhs_data == rhs_data[0:lhs_data_len]: + # print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len) + # else: + # # TODO: check padding + # result = False + # elif lhs_data_len > rhs_data_len: + # if lhs_data[0:rhs_data_len] == rhs_data: + # print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len) + # else: + # # TODO: check padding + # result = False + # else: + result = False + print 'error: sections differ' + #print 'a) %s' % (lhs_section) + # dump_hex_byte_string_diff(0, lhs_data, rhs_data) + #print 'b) %s' % (rhs_section) + # dump_hex_byte_string_diff(0, rhs_data, lhs_data) + elif lhs_data and not rhs_data: + print 'error: section data missing from b:' + print 'a) %s' % (lhs_section) + print 'b) %s' % (rhs_section) + result = False + elif not lhs_data and rhs_data: + print 'error: section data missing from a:' + print 'a) %s' % (lhs_section) + print 'b) %s' % (rhs_section) + result = False + elif lhs_section.offset or rhs_section.offset: + print 'error: section data missing for both a and b:' + print 'a) %s' % (lhs_section) + print 'b) %s' % (rhs_section) + result = False + else: + print 'ok' + else: + result = False + print 'error: section %s is missing in %s' % (lhs_section.sectname, rhs.path) + else: + print 'error: comaparing a %s mach-o file with a %s mach-o file is not supported' % (self.type, rhs.type) + result = False + if not result: + print 'error: mach files differ' + return result + def dump_header(self, dump_description = True, options = None): + if options.verbose: + print "MAGIC CPU SUBTYPE FILETYPE NUM CMDS SIZE CMDS FLAGS" + print "---------- ---------- ---------- ---------- -------- ---------- ----------" + else: + print "MAGIC ARCH FILETYPE NUM CMDS SIZE CMDS FLAGS" + print "------------ ---------- -------------- -------- ---------- ----------" + + def dump_flat(self, options): + if options.verbose: + print "%#8.8x %#8.8x %#8.8x %#8.8x %#8u %#8.8x %#8.8x" % (self.magic, self.arch.cpu , self.arch.sub, self.filetype.value, self.ncmds, self.sizeofcmds, self.flags.bits) + else: + print "%-12s %-10s %-14s %#8u %#8.8x %s" % (self.magic, self.arch, self.filetype, self.ncmds, self.sizeofcmds, self.flags) + + def dump(self, options): + if options.dump_header: + self.dump_header(True, options) + if options.dump_load_commands: + self.dump_load_commands(False, options) + if options.dump_sections: + self.dump_sections(False, options) + if options.section_names: + self.dump_section_contents(options) + if options.dump_symtab: + self.get_symtab() + if len(self.symbols): + self.dump_sections(False, options) + else: + print "No symbols" + if options.find_mangled: + self.dump_symbol_names_matching_regex (re.compile('^_?_Z')) + + def dump_header(self, dump_description = True, options = None): + if dump_description: + print self.description() + print "Mach Header" + print " magic: %#8.8x %s" % (self.magic.value, self.magic) + print " cputype: %#8.8x %s" % (self.arch.cpu, self.arch) + print " cpusubtype: %#8.8x" % self.arch.sub + print " filetype: %#8.8x %s" % (self.filetype.get_enum_value(), self.filetype.get_enum_name()) + print " ncmds: %#8.8x %u" % (self.ncmds, self.ncmds) + print " sizeofcmds: %#8.8x" % self.sizeofcmds + print " flags: %#8.8x %s" % (self.flags.bits, self.flags) + + def dump_load_commands(self, dump_description = True, options = None): + if dump_description: + print self.description() + for lc in self.commands: + print lc + + def get_section_by_name (self, name): + for section in self.sections: + if section.sectname and section.sectname == name: + return section + return None + + def get_section_by_section (self, other_section): + for section in self.sections: + if section.sectname == other_section.sectname and section.segname == other_section.segname: + return section + return None + + def dump_sections(self, dump_description = True, options = None): + if dump_description: + print self.description() + num_sections = len(self.sections) + if num_sections > 1: + self.sections[1].dump_header() + for sect_idx in range(1,num_sections): + print "%s" % self.sections[sect_idx] + + def dump_section_contents(self, options): + saved_section_to_disk = False + for sectname in options.section_names: + section = self.get_section_by_name(sectname) + if section: + sect_bytes = section.get_contents (self) + if options.outfile: + if not saved_section_to_disk: + outfile = open(options.outfile, 'w') + if options.extract_modules: + #print "Extracting modules from mach file..." + data = file_extract.FileExtract(StringIO.StringIO(sect_bytes), self.data.byte_order) + version = data.get_uint32() + num_modules = data.get_uint32() + #print "version = %u, num_modules = %u" % (version, num_modules) + for i in range(num_modules): + data_offset = data.get_uint64() + data_size = data.get_uint64() + name_offset = data.get_uint32() + language = data.get_uint32() + flags = data.get_uint32() + data.seek (name_offset) + module_name = data.get_c_string() + #print "module[%u] data_offset = %#16.16x, data_size = %#16.16x, name_offset = %#16.16x (%s), language = %u, flags = %#x" % (i, data_offset, data_size, name_offset, module_name, language, flags) + data.seek (data_offset) + outfile.write(data.read_size (data_size)) + else: + print "Saving section %s to '%s'" % (sectname, options.outfile) + outfile.write(sect_bytes) + outfile.close() + saved_section_to_disk = True + else: + print "error: you can only save a single section to disk at a time, skipping section '%s'" % (sectname) + else: + print 'section %s:\n' % (sectname) + section.dump_header() + print '%s\n' % (section) + dump_memory (0, sect_bytes, options.max_count, 16) + else: + print 'error: no section named "%s" was found' % (sectname) + + def get_segment(self, segname): + if len(self.segments) == 1 and self.segments[0].segname == '': + return self.segments[0] + for segment in self.segments: + if segment.segname == segname: + return segment + return None + + def get_first_load_command(self, lc_enum_value): + for lc in self.commands: + if lc.command.value == lc_enum_value: + return lc + return None + + def get_symtab(self): + if self.data and not self.symbols: + lc_symtab = self.get_first_load_command (LC_SYMTAB) + if lc_symtab: + symtab_offset = self.file_off + if self.data.is_in_memory(): + linkedit_segment = self.get_segment('__LINKEDIT') + if linkedit_segment: + linkedit_vmaddr = linkedit_segment.vmaddr + linkedit_fileoff = linkedit_segment.fileoff + symtab_offset = linkedit_vmaddr + lc_symtab.symoff - linkedit_fileoff + symtab_offset = linkedit_vmaddr + lc_symtab.stroff - linkedit_fileoff + else: + symtab_offset += lc_symtab.symoff + + self.data.seek (symtab_offset) + is_64 = self.is_64_bit() + for i in range(lc_symtab.nsyms): + nlist = Mach.NList() + nlist.unpack (self, self.data, lc_symtab) + self.symbols.append(nlist) + else: + print "no LC_SYMTAB" + + def dump_symtab(self, dump_description = True, options = None): + self.get_symtab() + if dump_description: + print self.description() + for i, symbol in enumerate(self.symbols): + print '[%5u] %s' % (i, symbol) + + def dump_symbol_names_matching_regex(self, regex, file=None): + self.get_symtab() + for symbol in self.symbols: + if symbol.name and regex.search (symbol.name): + print symbol.name + if file: + file.write('%s\n' % (symbol.name)) + + def is_64_bit(self): + return self.magic.is_64_bit() + + class LoadCommand: + class Command(dict_utils.Enum): + enum = { + 'LC_SEGMENT' : LC_SEGMENT, + 'LC_SYMTAB' : LC_SYMTAB, + 'LC_SYMSEG' : LC_SYMSEG, + 'LC_THREAD' : LC_THREAD, + 'LC_UNIXTHREAD' : LC_UNIXTHREAD, + 'LC_LOADFVMLIB' : LC_LOADFVMLIB, + 'LC_IDFVMLIB' : LC_IDFVMLIB, + 'LC_IDENT' : LC_IDENT, + 'LC_FVMFILE' : LC_FVMFILE, + 'LC_PREPAGE' : LC_PREPAGE, + 'LC_DYSYMTAB' : LC_DYSYMTAB, + 'LC_LOAD_DYLIB' : LC_LOAD_DYLIB, + 'LC_ID_DYLIB' : LC_ID_DYLIB, + 'LC_LOAD_DYLINKER' : LC_LOAD_DYLINKER, + 'LC_ID_DYLINKER' : LC_ID_DYLINKER, + 'LC_PREBOUND_DYLIB' : LC_PREBOUND_DYLIB, + 'LC_ROUTINES' : LC_ROUTINES, + 'LC_SUB_FRAMEWORK' : LC_SUB_FRAMEWORK, + 'LC_SUB_UMBRELLA' : LC_SUB_UMBRELLA, + 'LC_SUB_CLIENT' : LC_SUB_CLIENT, + 'LC_SUB_LIBRARY' : LC_SUB_LIBRARY, + 'LC_TWOLEVEL_HINTS' : LC_TWOLEVEL_HINTS, + 'LC_PREBIND_CKSUM' : LC_PREBIND_CKSUM, + 'LC_LOAD_WEAK_DYLIB' : LC_LOAD_WEAK_DYLIB, + 'LC_SEGMENT_64' : LC_SEGMENT_64, + 'LC_ROUTINES_64' : LC_ROUTINES_64, + 'LC_UUID' : LC_UUID, + 'LC_RPATH' : LC_RPATH, + 'LC_CODE_SIGNATURE' : LC_CODE_SIGNATURE, + 'LC_SEGMENT_SPLIT_INFO' : LC_SEGMENT_SPLIT_INFO, + 'LC_REEXPORT_DYLIB' : LC_REEXPORT_DYLIB, + 'LC_LAZY_LOAD_DYLIB' : LC_LAZY_LOAD_DYLIB, + 'LC_ENCRYPTION_INFO' : LC_ENCRYPTION_INFO, + 'LC_DYLD_INFO' : LC_DYLD_INFO, + 'LC_DYLD_INFO_ONLY' : LC_DYLD_INFO_ONLY, + 'LC_LOAD_UPWARD_DYLIB' : LC_LOAD_UPWARD_DYLIB, + 'LC_VERSION_MIN_MACOSX' : LC_VERSION_MIN_MACOSX, + 'LC_VERSION_MIN_IPHONEOS' : LC_VERSION_MIN_IPHONEOS, + 'LC_FUNCTION_STARTS' : LC_FUNCTION_STARTS, + 'LC_DYLD_ENVIRONMENT' : LC_DYLD_ENVIRONMENT + } + + def __init__(self, initial_value = 0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + + def __init__(self, c=None, l=0,o=0): + if c != None: + self.command = c + else: + self.command = Mach.LoadCommand.Command(0) + self.length = l + self.file_off = o + + def unpack(self, mach_file, data): + self.file_off = data.tell() + self.command.value, self.length = data.get_n_uint32(2) + + def skip(self, data): + data.seek (self.file_off + self.length, 0) + + def __str__(self): + lc_name = self.command.get_enum_name() + return '%#8.8x: <%#4.4x> %-24s' % (self.file_off, self.length, lc_name) + + class Section: + + def __init__(self): + self.index = 0 + self.is_64 = False + self.sectname = None + self.segname = None + self.addr = 0 + self.size = 0 + self.offset = 0 + self.align = 0 + self.reloff = 0 + self.nreloc = 0 + self.flags = 0 + self.reserved1 = 0 + self.reserved2 = 0 + self.reserved3 = 0 + + def unpack(self, is_64, data): + self.is_64 = is_64 + self.sectname = data.get_fixed_length_c_string (16, '', True) + self.segname = data.get_fixed_length_c_string (16, '', True) + if self.is_64: + self.addr, self.size = data.get_n_uint64(2) + self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.reserved3 = data.get_n_uint32(8) + else: + self.addr, self.size = data.get_n_uint32(2) + self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2 = data.get_n_uint32(7) + + def dump_header(self): + if self.is_64: + print "INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 RESERVED3 NAME"; + print "===== ------------------ ------------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------"; + else: + print "INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 NAME"; + print "===== ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------"; + + def __str__(self): + if self.is_64: + return "[%3u] %#16.16x %#16.16x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s" % (self.index, self.addr, self.size, self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.reserved3, self.segname, self.sectname) + else: + return "[%3u] %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s" % (self.index, self.addr, self.size, self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.segname, self.sectname) + + def get_contents(self, mach_file): + '''Get the section contents as a python string''' + if self.size > 0 and mach_file.get_segment(self.segname).filesize > 0: + data = mach_file.get_data() + if data: + section_data_offset = mach_file.file_off + self.offset + #print '%s.%s is at offset 0x%x with size 0x%x' % (self.segname, self.sectname, section_data_offset, self.size) + data.push_offset_and_seek (section_data_offset) + bytes = data.read_size(self.size) + data.pop_offset_and_seek() + return bytes + return None + + class DylibLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.name = None + self.timestamp = 0 + self.current_version = 0 + self.compatibility_version = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + name_offset, self.timestamp, self.current_version, self.compatibility_version = data.get_n_uint32(4) + data.seek(self.file_off + name_offset, 0) + self.name = data.get_fixed_length_c_string(self.length - 24) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "%#8.8x %#8.8x %#8.8x " % (self.timestamp, self.current_version, self.compatibility_version) + s += self.name + return s + + class LoadDYLDLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.name = None + + def unpack(self, mach_file, data): + data.get_uint32() + self.name = data.get_fixed_length_c_string(self.length - 12) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "%s" % self.name + return s + + class UnixThreadLoadCommand(LoadCommand): + class ThreadState: + def __init__(self): + self.flavor = 0 + self.count = 0 + self.register_values = list() + + def unpack(self, data): + self.flavor, self.count = data.get_n_uint32(2) + self.register_values = data.get_n_uint32(self.count) + + def __str__(self): + s = "flavor = %u, count = %u, regs =" % (self.flavor, self.count) + i = 0 + for register_value in self.register_values: + if i % 8 == 0: + s += "\n " + s += " %#8.8x" % register_value + i += 1 + return s + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.reg_sets = list() + + def unpack(self, mach_file, data): + reg_set = Mach.UnixThreadLoadCommand.ThreadState() + reg_set.unpack (data) + self.reg_sets.append(reg_set) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + for reg_set in self.reg_sets: + s += "%s" % reg_set + return s + + class DYLDInfoOnlyLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.rebase_off = 0 + self.rebase_size = 0 + self.bind_off = 0 + self.bind_size = 0 + self.weak_bind_off = 0 + self.weak_bind_size = 0 + self.lazy_bind_off = 0 + self.lazy_bind_size = 0 + self.export_off = 0 + self.export_size = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.rebase_off, self.rebase_size, self.bind_off, self.bind_size, self.weak_bind_off, self.weak_bind_size, self.lazy_bind_off, self.lazy_bind_size, self.export_off, self.export_size = data.get_n_uint32(10) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "rebase_off = %#8.8x, rebase_size = %u, " % (self.rebase_off, self.rebase_size) + s += "bind_off = %#8.8x, bind_size = %u, " % (self.bind_off, self.bind_size) + s += "weak_bind_off = %#8.8x, weak_bind_size = %u, " % (self.weak_bind_off, self.weak_bind_size) + s += "lazy_bind_off = %#8.8x, lazy_bind_size = %u, " % (self.lazy_bind_off, self.lazy_bind_size) + s += "export_off = %#8.8x, export_size = %u, " % (self.export_off, self.export_size) + return s + + class DYLDSymtabLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.ilocalsym = 0 + self.nlocalsym = 0 + self.iextdefsym = 0 + self.nextdefsym = 0 + self.iundefsym = 0 + self.nundefsym = 0 + self.tocoff = 0 + self.ntoc = 0 + self.modtaboff = 0 + self.nmodtab = 0 + self.extrefsymoff = 0 + self.nextrefsyms = 0 + self.indirectsymoff = 0 + self.nindirectsyms = 0 + self.extreloff = 0 + self.nextrel = 0 + self.locreloff = 0 + self.nlocrel = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.ilocalsym, self.nlocalsym, self.iextdefsym, self.nextdefsym, self.iundefsym, self.nundefsym, self.tocoff, self.ntoc, self.modtaboff, self.nmodtab, self.extrefsymoff, self.nextrefsyms, self.indirectsymoff, self.nindirectsyms, self.extreloff, self.nextrel, self.locreloff, self.nlocrel = data.get_n_uint32(18) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + # s += "ilocalsym = %u, nlocalsym = %u, " % (self.ilocalsym, self.nlocalsym) + # s += "iextdefsym = %u, nextdefsym = %u, " % (self.iextdefsym, self.nextdefsym) + # s += "iundefsym %u, nundefsym = %u, " % (self.iundefsym, self.nundefsym) + # s += "tocoff = %#8.8x, ntoc = %u, " % (self.tocoff, self.ntoc) + # s += "modtaboff = %#8.8x, nmodtab = %u, " % (self.modtaboff, self.nmodtab) + # s += "extrefsymoff = %#8.8x, nextrefsyms = %u, " % (self.extrefsymoff, self.nextrefsyms) + # s += "indirectsymoff = %#8.8x, nindirectsyms = %u, " % (self.indirectsymoff, self.nindirectsyms) + # s += "extreloff = %#8.8x, nextrel = %u, " % (self.extreloff, self.nextrel) + # s += "locreloff = %#8.8x, nlocrel = %u" % (self.locreloff, self.nlocrel) + s += "ilocalsym = %-10u, nlocalsym = %u\n" % (self.ilocalsym, self.nlocalsym) + s += " iextdefsym = %-10u, nextdefsym = %u\n" % (self.iextdefsym, self.nextdefsym) + s += " iundefsym = %-10u, nundefsym = %u\n" % (self.iundefsym, self.nundefsym) + s += " tocoff = %#8.8x, ntoc = %u\n" % (self.tocoff, self.ntoc) + s += " modtaboff = %#8.8x, nmodtab = %u\n" % (self.modtaboff, self.nmodtab) + s += " extrefsymoff = %#8.8x, nextrefsyms = %u\n" % (self.extrefsymoff, self.nextrefsyms) + s += " indirectsymoff = %#8.8x, nindirectsyms = %u\n" % (self.indirectsymoff, self.nindirectsyms) + s += " extreloff = %#8.8x, nextrel = %u\n" % (self.extreloff, self.nextrel) + s += " locreloff = %#8.8x, nlocrel = %u" % (self.locreloff, self.nlocrel) + return s + + class SymtabLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.symoff = 0 + self.nsyms = 0 + self.stroff = 0 + self.strsize = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.symoff, self.nsyms, self.stroff, self.strsize = data.get_n_uint32(4) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "symoff = %#8.8x, nsyms = %u, stroff = %#8.8x, strsize = %u" % (self.symoff, self.nsyms, self.stroff, self.strsize) + return s + + + class UUIDLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.uuid = None + + def unpack(self, mach_file, data): + uuid_data = data.get_n_uint8(16) + uuid_str = '' + for byte in uuid_data: + uuid_str += '%2.2x' % byte + self.uuid = uuid.UUID(uuid_str) + mach_file.uuid = self.uuid + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += self.uuid.__str__() + return s + + class DataBlobLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.dataoff = 0 + self.datasize = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.dataoff, self.datasize = data.get_n_uint32(2) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "dataoff = %#8.8x, datasize = %u" % (self.dataoff, self.datasize) + return s + + class EncryptionInfoLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.cryptoff = 0 + self.cryptsize = 0 + self.cryptid = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.cryptoff, self.cryptsize, self.cryptid = data.get_n_uint32(3) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "file-range = [%#8.8x - %#8.8x), cryptsize = %u, cryptid = %u" % (self.cryptoff, self.cryptoff + self.cryptsize, self.cryptsize, self.cryptid) + return s + + class SegmentLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.segname = None + self.vmaddr = 0 + self.vmsize = 0 + self.fileoff = 0 + self.filesize = 0 + self.maxprot = 0 + self.initprot = 0 + self.nsects = 0 + self.flags = 0 + + def unpack(self, mach_file, data): + is_64 = self.command.get_enum_value() == LC_SEGMENT_64; + self.segname = data.get_fixed_length_c_string (16, '', True) + if is_64: + self.vmaddr, self.vmsize, self.fileoff, self.filesize = data.get_n_uint64(4) + else: + self.vmaddr, self.vmsize, self.fileoff, self.filesize = data.get_n_uint32(4) + self.maxprot, self.initprot, self.nsects, self.flags = data.get_n_uint32(4) + mach_file.segments.append(self) + for i in range(self.nsects): + section = Mach.Section() + section.unpack(is_64, data) + section.index = len (mach_file.sections) + mach_file.sections.append(section) + + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + if self.command.get_enum_value() == LC_SEGMENT: + s += "%#8.8x %#8.8x %#8.8x %#8.8x " % (self.vmaddr, self.vmsize, self.fileoff, self.filesize) + else: + s += "%#16.16x %#16.16x %#16.16x %#16.16x " % (self.vmaddr, self.vmsize, self.fileoff, self.filesize) + s += "%s %s %3u %#8.8x" % (vm_prot_names[self.maxprot], vm_prot_names[self.initprot], self.nsects, self.flags) + s += ' ' + self.segname + return s + + class NList: + class Type: + class Stab(dict_utils.Enum): + enum = { + 'N_GSYM' : N_GSYM , + 'N_FNAME' : N_FNAME , + 'N_FUN' : N_FUN , + 'N_STSYM' : N_STSYM , + 'N_LCSYM' : N_LCSYM , + 'N_BNSYM' : N_BNSYM , + 'N_OPT' : N_OPT , + 'N_RSYM' : N_RSYM , + 'N_SLINE' : N_SLINE , + 'N_ENSYM' : N_ENSYM , + 'N_SSYM' : N_SSYM , + 'N_SO' : N_SO , + 'N_OSO' : N_OSO , + 'N_LSYM' : N_LSYM , + 'N_BINCL' : N_BINCL , + 'N_SOL' : N_SOL , + 'N_PARAMS' : N_PARAMS , + 'N_VERSION' : N_VERSION , + 'N_OLEVEL' : N_OLEVEL , + 'N_PSYM' : N_PSYM , + 'N_EINCL' : N_EINCL , + 'N_ENTRY' : N_ENTRY , + 'N_LBRAC' : N_LBRAC , + 'N_EXCL' : N_EXCL , + 'N_RBRAC' : N_RBRAC , + 'N_BCOMM' : N_BCOMM , + 'N_ECOMM' : N_ECOMM , + 'N_ECOML' : N_ECOML , + 'N_LENG' : N_LENG + } + + def __init__(self, magic = 0): + dict_utils.Enum.__init__(self, magic, self.enum) + + def __init__(self, t = 0): + self.value = t + + def __str__(self): + n_type = self.value + if n_type & N_STAB: + stab = Mach.NList.Type.Stab(self.value) + return '%s' % stab + else: + type = self.value & N_TYPE + type_str = '' + if type == N_UNDF: + type_str = 'N_UNDF' + elif type == N_ABS: + type_str = 'N_ABS ' + elif type == N_SECT: + type_str = 'N_SECT' + elif type == N_PBUD: + type_str = 'N_PBUD' + elif type == N_INDR: + type_str = 'N_INDR' + else: + type_str = "??? (%#2.2x)" % type + if n_type & N_PEXT: + type_str += ' | PEXT' + if n_type & N_EXT: + type_str += ' | EXT ' + return type_str + + + def __init__(self): + self.index = 0 + self.name_offset = 0 + self.name = 0 + self.type = Mach.NList.Type() + self.sect_idx = 0 + self.desc = 0 + self.value = 0 + + def unpack(self, mach_file, data, symtab_lc): + self.index = len(mach_file.symbols) + self.name_offset = data.get_uint32() + self.type.value, self.sect_idx = data.get_n_uint8(2) + self.desc = data.get_uint16() + if mach_file.is_64_bit(): + self.value = data.get_uint64() + else: + self.value = data.get_uint32() + data.push_offset_and_seek (mach_file.file_off + symtab_lc.stroff + self.name_offset) + #print "get string for symbol[%u]" % self.index + self.name = data.get_c_string() + data.pop_offset_and_seek() + + def __str__(self): + name_display = '' + if len(self.name): + name_display = ' "%s"' % self.name + return '%#8.8x %#2.2x (%-20s) %#2.2x %#4.4x %16.16x%s' % (self.name_offset, self.type.value, self.type, self.sect_idx, self.desc, self.value, name_display) + + + class Interactive(cmd.Cmd): + '''Interactive command interpreter to mach-o files.''' + + def __init__(self, mach, options): + cmd.Cmd.__init__(self) + self.intro = 'Interactive mach-o command interpreter' + self.prompt = 'mach-o: %s %% ' % mach.path + self.mach = mach + self.options = options + + def default(self, line): + '''Catch all for unknown command, which will exit the interpreter.''' + print "uknown command: %s" % line + return True + + def do_q(self, line): + '''Quit command''' + return True + + def do_quit(self, line): + '''Quit command''' + return True + + def do_header(self, line): + '''Dump mach-o file headers''' + self.mach.dump_header(True, self.options) + return False + + def do_load(self, line): + '''Dump all mach-o load commands''' + self.mach.dump_load_commands(True, self.options) + return False + + def do_sections(self, line): + '''Dump all mach-o sections''' + self.mach.dump_sections(True, self.options) + return False + + def do_symtab(self, line): + '''Dump all mach-o symbols in the symbol table''' + self.mach.dump_symtab(True, self.options) + return False + +if __name__ == '__main__': + parser = optparse.OptionParser(description='A script that parses skinny and universal mach-o files.') + parser.add_option('--arch', '-a', type='string', metavar='arch', dest='archs', action='append', help='specify one or more architectures by name') + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-H', '--header', action='store_true', dest='dump_header', help='dump the mach-o file header', default=False) + parser.add_option('-l', '--load-commands', action='store_true', dest='dump_load_commands', help='dump the mach-o load commands', default=False) + parser.add_option('-s', '--symtab', action='store_true', dest='dump_symtab', help='dump the mach-o symbol table', default=False) + parser.add_option('-S', '--sections', action='store_true', dest='dump_sections', help='dump the mach-o sections', default=False) + parser.add_option('--section', type='string', metavar='sectname', dest='section_names', action='append', help='Specify one or more section names to dump', default=[]) + parser.add_option('-o', '--out', type='string', dest='outfile', help='Used in conjunction with the --section=NAME option to save a single section\'s data to disk.', default=False) + parser.add_option('-i', '--interactive', action='store_true', dest='interactive', help='enable interactive mode', default=False) + parser.add_option('-m', '--mangled', action='store_true', dest='find_mangled', help='dump all mangled names in a mach file', default=False) + parser.add_option('-c', '--compare', action='store_true', dest='compare', help='compare two mach files', default=False) + parser.add_option('-M', '--extract-modules', action='store_true', dest='extract_modules', help='Extract modules from file', default=False) + parser.add_option('-C', '--count', type='int', dest='max_count', help='Sets the max byte count when dumping section data', default=-1) + + (options, mach_files) = parser.parse_args() + if options.extract_modules: + if options.section_names: + print "error: can't use --section option with the --extract-modules option" + exit(1) + if not options.outfile: + print "error: the --output=FILE option must be specified with the --extract-modules option" + exit(1) + options.section_names.append("__apple_ast") + if options.compare: + if len(mach_files) == 2: + mach_a = Mach() + mach_b = Mach() + mach_a.parse(mach_files[0]) + mach_b.parse(mach_files[1]) + mach_a.compare(mach_b) + else: + print 'error: --compare takes two mach files as arguments' + else: + if not (options.dump_header or options.dump_load_commands or options.dump_symtab or options.dump_sections or options.find_mangled or options.section_names): + options.dump_header = True + options.dump_load_commands = True + if options.verbose: + print 'options', options + print 'mach_files', mach_files + for path in mach_files: + mach = Mach() + mach.parse(path) + if options.interactive: + interpreter = Mach.Interactive(mach, options) + interpreter.cmdloop() + else: + mach.dump(options) + diff --git a/examples/python/memory.py b/examples/python/memory.py new file mode 100755 index 000000000000..ae78e24e2e29 --- /dev/null +++ b/examples/python/memory.py @@ -0,0 +1,181 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +#---------------------------------------------------------------------- + +import commands +import platform +import os +import re +import sys + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + +import commands +import optparse +import shlex +import string +import struct +import time + +def append_data_callback(option, opt_str, value, parser): + if opt_str == "--uint8": + int8 = int(value, 0) + parser.values.data += struct.pack('1B',int8) + if opt_str == "--uint16": + int16 = int(value, 0) + parser.values.data += struct.pack('1H',int16) + if opt_str == "--uint32": + int32 = int(value, 0) + parser.values.data += struct.pack('1I',int32) + if opt_str == "--uint64": + int64 = int(value, 0) + parser.values.data += struct.pack('1Q',int64) + if opt_str == "--int8": + int8 = int(value, 0) + parser.values.data += struct.pack('1b',int8) + if opt_str == "--int16": + int16 = int(value, 0) + parser.values.data += struct.pack('1h',int16) + if opt_str == "--int32": + int32 = int(value, 0) + parser.values.data += struct.pack('1i',int32) + if opt_str == "--int64": + int64 = int(value, 0) + parser.values.data += struct.pack('1q',int64) + +def create_memfind_options(): + usage = "usage: %prog [options] STARTADDR [ENDADDR]" + description='''This command can find data in a specified address range. +Options are used to specify the data that is to be looked for and the options +can be specified multiple times to look for longer streams of data. +''' + parser = optparse.OptionParser(description=description, prog='memfind',usage=usage) + parser.add_option('-s', '--size', type='int', metavar='BYTESIZE', dest='size', help='Specify the byte size to search.', default=0) + parser.add_option('--int8', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 8 bit signed integer value to search for in memory.', default='') + parser.add_option('--int16', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 16 bit signed integer value to search for in memory.', default='') + parser.add_option('--int32', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 32 bit signed integer value to search for in memory.', default='') + parser.add_option('--int64', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 64 bit signed integer value to search for in memory.', default='') + parser.add_option('--uint8', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 8 bit unsigned integer value to search for in memory.', default='') + parser.add_option('--uint16', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 16 bit unsigned integer value to search for in memory.', default='') + parser.add_option('--uint32', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 32 bit unsigned integer value to search for in memory.', default='') + parser.add_option('--uint64', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 64 bit unsigned integer value to search for in memory.', default='') + return parser + +def memfind_command (debugger, command, result, dict): + # Use the Shell Lexer to properly parse up command options just like a + # shell would + command_args = shlex.split(command) + parser = create_memfind_options() + (options, args) = parser.parse_args(command_args) + # try: + # (options, args) = parser.parse_args(command_args) + # except: + # # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + # result.SetStatus (lldb.eReturnStatusFailed) + # print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string + # return + memfind (debugger.GetSelectedTarget(), options, args, result) + +def print_error(str, show_usage, result): + print >>result, str + if show_usage: + print >>result, create_memfind_options().format_help() + +def memfind (target, options, args, result): + num_args = len(args) + start_addr = 0 + if num_args == 1: + if options.size > 0: + print_error ("error: --size must be specified if there is no ENDADDR argument", True, result) + return + start_addr = int(args[0], 0) + elif num_args == 2: + if options.size != 0: + print_error ("error: --size can't be specified with an ENDADDR argument", True, result) + return + start_addr = int(args[0], 0) + end_addr = int(args[1], 0) + if start_addr >= end_addr: + print_error ("error: inavlid memory range [%#x - %#x)" % (start_addr, end_addr), True, result) + return + options.size = end_addr - start_addr + else: + print_error ("error: memfind takes 1 or 2 arguments", True, result) + return + + if not options.data: + print >>result, 'error: no data specified to search for' + return + + if not target: + print >>result, 'error: invalid target' + return + process = target.process + if not process: + print >>result, 'error: invalid process' + return + + error = lldb.SBError() + bytes = process.ReadMemory (start_addr, options.size, error) + if error.Success(): + num_matches = 0 + print >>result, "Searching memory range [%#x - %#x) for" % (start_addr, end_addr), + for byte in options.data: + print >>result, '%2.2x' % ord(byte), + print >>result + + match_index = string.find(bytes, options.data) + while match_index != -1: + num_matches = num_matches + 1 + print >>result, '%#x: %#x + %u' % (start_addr + match_index, start_addr, match_index) + match_index = string.find(bytes, options.data, match_index + 1) + + if num_matches == 0: + print >>result, "error: no matches found" + else: + print >>result, 'error: %s' % (error.GetCString()) + + +if __name__ == '__main__': + print 'error: this script is designed to be used within the embedded script interpreter in LLDB' +elif getattr(lldb, 'debugger', None): + memfind_command.__doc__ = create_memfind_options().format_help() + lldb.debugger.HandleCommand('command script add -f memory.memfind_command memfind') + print '"memfind" command installed, use the "--help" option for detailed help' diff --git a/examples/python/operating_system.py b/examples/python/operating_system.py new file mode 100644 index 000000000000..49cd5ff34398 --- /dev/null +++ b/examples/python/operating_system.py @@ -0,0 +1,104 @@ +#!/usr/bin/python + +import lldb +import struct + +class OperatingSystemPlugIn(object): + """Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class""" + + def __init__(self, process): + '''Initialization needs a valid.SBProcess object. + + This plug-in will get created after a live process is valid and has stopped for the + first time.''' + self.process = None + self.registers = None + self.threads = None + if type(process) is lldb.SBProcess and process.IsValid(): + self.process = process + self.threads = None # Will be an dictionary containing info for each thread + + def get_target(self): + # NOTE: Don't use "lldb.target" when trying to get your target as the "lldb.target" + # tracks the current target in the LLDB command interpreter which isn't the + # correct thing to use for this plug-in. + return self.process.target + + def create_thread(self, tid, context): + if tid == 0x444444444: + thread_info = { 'tid' : tid, 'name' : 'four' , 'queue' : 'queue4', 'state' : 'stopped', 'stop_reason' : 'none' } + self.threads.append(thread_info) + return thread_info + return None + + def get_thread_info(self): + if not self.threads: + # The sample dictionary below shows the values that can be returned for a thread + # tid => thread ID (mandatory) + # name => thread name (optional key/value pair) + # queue => thread dispatch queue name (optional key/value pair) + # state => thred state (mandatory, set to 'stopped' for now) + # stop_reason => thread stop reason. (mandatory, usually set to 'none') + # Possible values include: + # 'breakpoint' if the thread is stopped at a breakpoint + # 'none' thread is just stopped because the process is stopped + # 'trace' the thread just single stepped + # The usual value for this while threads are in memory is 'none' + # register_data_addr => the address of the register data in memory (optional key/value pair) + # Specifying this key/value pair for a thread will avoid a call to get_register_data() + # and can be used when your registers are in a thread context structure that is contiguous + # in memory. Don't specify this if your register layout in memory doesn't match the layout + # described by the dictionary returned from a call to the get_register_info() method. + self.threads = [ + { 'tid' : 0x111111111, 'name' : 'one' , 'queue' : 'queue1', 'state' : 'stopped', 'stop_reason' : 'breakpoint'}, + { 'tid' : 0x222222222, 'name' : 'two' , 'queue' : 'queue2', 'state' : 'stopped', 'stop_reason' : 'none' }, + { 'tid' : 0x333333333, 'name' : 'three', 'queue' : 'queue3', 'state' : 'stopped', 'stop_reason' : 'trace' , 'register_data_addr' : 0x100000000 } + ] + return self.threads + + def get_register_info(self): + if self.registers == None: + self.registers = dict() + triple = self.process.target.triple + if triple: + arch = triple.split('-')[0] + if arch == 'x86_64': + self.registers['sets'] = ['GPR', 'FPU', 'EXC'] + self.registers['registers'] = [ + { 'name':'rax' , 'bitsize' : 64, 'offset' : 0, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 0, 'dwarf' : 0}, + { 'name':'rbx' , 'bitsize' : 64, 'offset' : 8, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 3, 'dwarf' : 3}, + { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 2, 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, + { 'name':'rdx' , 'bitsize' : 64, 'offset' : 24, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 1, 'dwarf' : 1, 'generic':'arg3', 'alt-name':'arg3', }, + { 'name':'rdi' , 'bitsize' : 64, 'offset' : 32, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 5, 'dwarf' : 5, 'generic':'arg1', 'alt-name':'arg1', }, + { 'name':'rsi' , 'bitsize' : 64, 'offset' : 40, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 4, 'dwarf' : 4, 'generic':'arg2', 'alt-name':'arg2', }, + { 'name':'rbp' , 'bitsize' : 64, 'offset' : 48, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 6, 'dwarf' : 6, 'generic':'fp' , 'alt-name':'fp', }, + { 'name':'rsp' , 'bitsize' : 64, 'offset' : 56, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 7, 'dwarf' : 7, 'generic':'sp' , 'alt-name':'sp', }, + { 'name':'r8' , 'bitsize' : 64, 'offset' : 64, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 8, 'dwarf' : 8, 'generic':'arg5', 'alt-name':'arg5', }, + { 'name':'r9' , 'bitsize' : 64, 'offset' : 72, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 9, 'dwarf' : 9, 'generic':'arg6', 'alt-name':'arg6', }, + { 'name':'r10' , 'bitsize' : 64, 'offset' : 80, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 10, 'dwarf' : 10}, + { 'name':'r11' , 'bitsize' : 64, 'offset' : 88, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 11, 'dwarf' : 11}, + { 'name':'r12' , 'bitsize' : 64, 'offset' : 96, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 12, 'dwarf' : 12}, + { 'name':'r13' , 'bitsize' : 64, 'offset' : 104, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 13, 'dwarf' : 13}, + { 'name':'r14' , 'bitsize' : 64, 'offset' : 112, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 14, 'dwarf' : 14}, + { 'name':'r15' , 'bitsize' : 64, 'offset' : 120, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 15, 'dwarf' : 15}, + { 'name':'rip' , 'bitsize' : 64, 'offset' : 128, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 16, 'dwarf' : 16, 'generic':'pc', 'alt-name':'pc' }, + { 'name':'rflags' , 'bitsize' : 64, 'offset' : 136, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'generic':'flags', 'alt-name':'flags' }, + { 'name':'cs' , 'bitsize' : 64, 'offset' : 144, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + { 'name':'fs' , 'bitsize' : 64, 'offset' : 152, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + { 'name':'gs' , 'bitsize' : 64, 'offset' : 160, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + ] + return self.registers + + def get_register_data(self, tid): + if tid == 0x111111111: + return struct.pack('21Q',1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21); + elif tid == 0x222222222: + return struct.pack('21Q',11,12,13,14,15,16,17,18,19,110,111,112,113,114,115,116,117,118,119,120,121); + elif tid == 0x333333333: + return struct.pack('21Q',21,22,23,24,25,26,27,28,29,210,211,212,213,214,215,216,217,218,219,220,221); + elif tid == 0x444444444: + return struct.pack('21Q',31,32,33,34,35,36,37,38,39,310,311,312,313,314,315,316,317,318,319,320,321); + else: + return struct.pack('21Q',41,42,43,44,45,46,47,48,49,410,411,412,413,414,415,416,417,418,419,420,421); + return None + diff --git a/examples/python/performance.py b/examples/python/performance.py new file mode 100755 index 000000000000..a225d7b731e0 --- /dev/null +++ b/examples/python/performance.py @@ -0,0 +1,335 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +import commands +import optparse +import os +import platform +import re +import resource +import sys +import time +import types + +#---------------------------------------------------------------------- +# Code that auto imports LLDB +#---------------------------------------------------------------------- +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + + +class Timer: + def __enter__(self): + self.start = time.clock() + return self + + def __exit__(self, *args): + self.end = time.clock() + self.interval = self.end - self.start + +class Action(object): + """Class that encapsulates actions to take when a thread stops for a reason.""" + def __init__(self, callback = None, callback_owner = None): + self.callback = callback + self.callback_owner = callback_owner + def ThreadStopped (self, thread): + assert False, "performance.Action.ThreadStopped(self, thread) must be overridden in a subclass" + +class PlanCompleteAction (Action): + def __init__(self, callback = None, callback_owner = None): + Action.__init__(self, callback, callback_owner) + def ThreadStopped (self, thread): + if thread.GetStopReason() == lldb.eStopReasonPlanComplete: + if self.callback: + if self.callback_owner: + self.callback (self.callback_owner, thread) + else: + self.callback (thread) + return True + return False + + +class BreakpointAction (Action): + def __init__(self, callback = None, callback_owner = None, name = None, module = None, file = None, line = None, breakpoint = None): + Action.__init__(self, callback, callback_owner) + self.modules = lldb.SBFileSpecList() + self.files = lldb.SBFileSpecList() + self.breakpoints = list() + # "module" can be a list or a string + if breakpoint: + self.breakpoints.append(breakpoint) + else: + if module: + if isinstance(module, types.ListType): + for module_path in module: + self.modules.Append(lldb.SBFileSpec(module_path, False)) + elif isinstance(module, types.StringTypes): + self.modules.Append(lldb.SBFileSpec(module, False)) + if name: + # "file" can be a list or a string + if file: + if isinstance(file, types.ListType): + self.files = lldb.SBFileSpecList() + for f in file: + self.files.Append(lldb.SBFileSpec(f, False)) + elif isinstance(file, types.StringTypes): + self.files.Append(lldb.SBFileSpec(file, False)) + self.breakpoints.append (self.target.BreakpointCreateByName(name, self.modules, self.files)) + elif file and line: + self.breakpoints.append (self.target.BreakpointCreateByLocation(file, line)) + def ThreadStopped (self, thread): + if thread.GetStopReason() == lldb.eStopReasonBreakpoint: + for bp in self.breakpoints: + if bp.GetID() == thread.GetStopReasonDataAtIndex(0): + if self.callback: + if self.callback_owner: + self.callback (self.callback_owner, thread) + else: + self.callback (thread) + return True + return False +class TestCase: + """Class that aids in running performance tests.""" + def __init__(self): + self.verbose = False + self.debugger = lldb.SBDebugger.Create() + self.target = None + self.process = None + self.thread = None + self.launch_info = None + self.done = False + self.listener = self.debugger.GetListener() + self.user_actions = list() + self.builtin_actions = list() + self.bp_id_to_dict = dict() + + def Setup(self, args): + self.launch_info = lldb.SBLaunchInfo(args) + + def Run (self, args): + assert False, "performance.TestCase.Run(self, args) must be subclassed" + + def Launch(self): + if self.target: + error = lldb.SBError() + self.process = self.target.Launch (self.launch_info, error) + if not error.Success(): + print "error: %s" % error.GetCString() + if self.process: + self.process.GetBroadcaster().AddListener(self.listener, lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitInterrupt) + return True + return False + + def WaitForNextProcessEvent (self): + event = None + if self.process: + while event is None: + process_event = lldb.SBEvent() + if self.listener.WaitForEvent (lldb.UINT32_MAX, process_event): + state = lldb.SBProcess.GetStateFromEvent (process_event) + if self.verbose: + print "event = %s" % (lldb.SBDebugger.StateAsCString(state)) + if lldb.SBProcess.GetRestartedFromEvent(process_event): + continue + if state == lldb.eStateInvalid or state == lldb.eStateDetached or state == lldb.eStateCrashed or state == lldb.eStateUnloaded or state == lldb.eStateExited: + event = process_event + self.done = True + elif state == lldb.eStateConnected or state == lldb.eStateAttaching or state == lldb.eStateLaunching or state == lldb.eStateRunning or state == lldb.eStateStepping or state == lldb.eStateSuspended: + continue + elif state == lldb.eStateStopped: + event = process_event + call_test_step = True + fatal = False + selected_thread = False + for thread in self.process: + frame = thread.GetFrameAtIndex(0) + select_thread = False + + stop_reason = thread.GetStopReason() + if self.verbose: + print "tid = %#x pc = %#x " % (thread.GetThreadID(),frame.GetPC()), + if stop_reason == lldb.eStopReasonNone: + if self.verbose: + print "none" + elif stop_reason == lldb.eStopReasonTrace: + select_thread = True + if self.verbose: + print "trace" + elif stop_reason == lldb.eStopReasonPlanComplete: + select_thread = True + if self.verbose: + print "plan complete" + elif stop_reason == lldb.eStopReasonThreadExiting: + if self.verbose: + print "thread exiting" + elif stop_reason == lldb.eStopReasonExec: + if self.verbose: + print "exec" + elif stop_reason == lldb.eStopReasonInvalid: + if self.verbose: + print "invalid" + elif stop_reason == lldb.eStopReasonException: + select_thread = True + if self.verbose: + print "exception" + fatal = True + elif stop_reason == lldb.eStopReasonBreakpoint: + select_thread = True + bp_id = thread.GetStopReasonDataAtIndex(0) + bp_loc_id = thread.GetStopReasonDataAtIndex(1) + if self.verbose: + print "breakpoint id = %d.%d" % (bp_id, bp_loc_id) + elif stop_reason == lldb.eStopReasonWatchpoint: + select_thread = True + if self.verbose: + print "watchpoint id = %d" % (thread.GetStopReasonDataAtIndex(0)) + elif stop_reason == lldb.eStopReasonSignal: + select_thread = True + if self.verbose: + print "signal %d" % (thread.GetStopReasonDataAtIndex(0)) + + if select_thread and not selected_thread: + self.thread = thread + selected_thread = self.process.SetSelectedThread(thread) + + for action in self.user_actions: + action.ThreadStopped (thread) + + + if fatal: + # if self.verbose: + # Xcode.RunCommand(self.debugger,"bt all",true) + sys.exit(1) + return event + +class Measurement: + '''A class that encapsulates a measurement''' + def __init__(self): + object.__init__(self) + def Measure(self): + assert False, "performance.Measurement.Measure() must be subclassed" + +class MemoryMeasurement(Measurement): + '''A class that can measure memory statistics for a process.''' + def __init__(self, pid): + Measurement.__init__(self) + self.pid = pid + self.stats = ["rprvt","rshrd","rsize","vsize","vprvt","kprvt","kshrd","faults","cow","pageins"] + self.command = "top -l 1 -pid %u -stats %s" % (self.pid, ",".join(self.stats)) + self.value = dict() + + def Measure(self): + output = commands.getoutput(self.command).split("\n")[-1] + values = re.split('[-+\s]+', output) + for (idx, stat) in enumerate(values): + multiplier = 1 + if stat: + if stat[-1] == 'K': + multiplier = 1024 + stat = stat[:-1] + elif stat[-1] == 'M': + multiplier = 1024*1024 + stat = stat[:-1] + elif stat[-1] == 'G': + multiplier = 1024*1024*1024 + elif stat[-1] == 'T': + multiplier = 1024*1024*1024*1024 + stat = stat[:-1] + self.value[self.stats[idx]] = int (stat) * multiplier + + def __str__(self): + '''Dump the MemoryMeasurement current value''' + s = '' + for key in self.value.keys(): + if s: + s += "\n" + s += "%8s = %s" % (key, self.value[key]) + return s + + +class TesterTestCase(TestCase): + def __init__(self): + TestCase.__init__(self) + self.verbose = True + self.num_steps = 5 + + def BreakpointHit (self, thread): + bp_id = thread.GetStopReasonDataAtIndex(0) + loc_id = thread.GetStopReasonDataAtIndex(1) + print "Breakpoint %i.%i hit: %s" % (bp_id, loc_id, thread.process.target.FindBreakpointByID(bp_id)) + thread.StepOver() + + def PlanComplete (self, thread): + if self.num_steps > 0: + thread.StepOver() + self.num_steps = self.num_steps - 1 + else: + thread.process.Kill() + + def Run (self, args): + self.Setup(args) + with Timer() as total_time: + self.target = self.debugger.CreateTarget(args[0]) + if self.target: + with Timer() as breakpoint_timer: + bp = self.target.BreakpointCreateByName("main") + print('Breakpoint time = %.03f sec.' % breakpoint_timer.interval) + + self.user_actions.append (BreakpointAction(breakpoint=bp, callback=TesterTestCase.BreakpointHit, callback_owner=self)) + self.user_actions.append (PlanCompleteAction(callback=TesterTestCase.PlanComplete, callback_owner=self)) + + if self.Launch(): + while not self.done: + self.WaitForNextProcessEvent() + else: + print "error: failed to launch process" + else: + print "error: failed to create target with '%s'" % (args[0]) + print('Total time = %.03f sec.' % total_time.interval) + + +if __name__ == '__main__': + lldb.SBDebugger.Initialize() + test = TesterTestCase() + test.Run (sys.argv[1:]) + mem = MemoryMeasurement(os.getpid()) + mem.Measure() + print str(mem) + lldb.SBDebugger.Terminate() + # print "sleeeping for 100 seconds" + # time.sleep(100) diff --git a/examples/python/process_events.py b/examples/python/process_events.py new file mode 100755 index 000000000000..e8ccc5f90230 --- /dev/null +++ b/examples/python/process_events.py @@ -0,0 +1,278 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +import commands +import optparse +import os +import platform +import sys + +#---------------------------------------------------------------------- +# Code that auto imports LLDB +#---------------------------------------------------------------------- +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + +def print_threads(process, options): + if options.show_threads: + for thread in process: + print '%s %s' % (thread, thread.GetFrameAtIndex(0)) + +def run_commands(command_interpreter, commands): + return_obj = lldb.SBCommandReturnObject() + for command in commands: + command_interpreter.HandleCommand( command, return_obj ) + if return_obj.Succeeded(): + print return_obj.GetOutput() + else: + print return_obj + if options.stop_on_error: + break + +def main(argv): + description='''Debugs a program using the LLDB python API and uses asynchronous broadcast events to watch for process state changes.''' + epilog='''Examples: + +#---------------------------------------------------------------------- +# Run "/bin/ls" with the arguments "-lAF /tmp/", and set a breakpoint +# at "malloc" and backtrace and read all registers each time we stop +#---------------------------------------------------------------------- +% ./process_events.py --breakpoint malloc --stop-command bt --stop-command 'register read' -- /bin/ls -lAF /tmp/ + +''' + optparse.OptionParser.format_epilog = lambda self, formatter: self.epilog + parser = optparse.OptionParser(description=description, prog='process_events',usage='usage: process_events [options] program [arg1 arg2]', epilog=epilog) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help="Enable verbose logging.", default=False) + parser.add_option('-b', '--breakpoint', action='append', type='string', metavar='BPEXPR', dest='breakpoints', help='Breakpoint commands to create after the target has been created, the values will be sent to the "_regexp-break" command which supports breakpoints by name, file:line, and address.') + parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) + parser.add_option('--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".', default=None) + parser.add_option('-l', '--launch-command', action='append', type='string', metavar='CMD', dest='launch_commands', help='LLDB command interpreter commands to run once after the process has launched. This option can be specified more than once.', default=[]) + parser.add_option('-s', '--stop-command', action='append', type='string', metavar='CMD', dest='stop_commands', help='LLDB command interpreter commands to run each time the process stops. This option can be specified more than once.', default=[]) + parser.add_option('-c', '--crash-command', action='append', type='string', metavar='CMD', dest='crash_commands', help='LLDB command interpreter commands to run in case the process crashes. This option can be specified more than once.', default=[]) + parser.add_option('-x', '--exit-command', action='append', type='string', metavar='CMD', dest='exit_commands', help='LLDB command interpreter commands to run once after the process has exited. This option can be specified more than once.', default=[]) + parser.add_option('-T', '--no-threads', action='store_false', dest='show_threads', help="Don't show threads when process stops.", default=True) + parser.add_option('--ignore-errors', action='store_false', dest='stop_on_error', help="Don't stop executing LLDB commands if the command returns an error. This applies to all of the LLDB command interpreter commands that get run for launch, stop, crash and exit.", default=True) + parser.add_option('-n', '--run-count', type='int', dest='run_count', metavar='N', help='How many times to run the process in case the process exits.', default=1) + parser.add_option('-t', '--event-timeout', type='int', dest='event_timeout', metavar='SEC', help='Specify the timeout in seconds to wait for process state change events.', default=lldb.UINT32_MAX) + parser.add_option('-e', '--environment', action='append', type='string', metavar='ENV', dest='env_vars', help='Environment variables to set in the inferior process when launching a process.') + parser.add_option('-d', '--working-dir', type='string', metavar='DIR', dest='working_dir', help='The the current working directory when launching a process.', default=None) + parser.add_option('-p', '--attach-pid', type='int', dest='attach_pid', metavar='PID', help='Specify a process to attach to by process ID.', default=-1) + parser.add_option('-P', '--attach-name', type='string', dest='attach_name', metavar='PROCESSNAME', help='Specify a process to attach to by name.', default=None) + parser.add_option('-w', '--attach-wait', action='store_true', dest='attach_wait', help='Wait for the next process to launch when attaching to a process by name.', default=False) + try: + (options, args) = parser.parse_args(argv) + except: + return + + attach_info = None + launch_info = None + exe = None + if args: + exe = args.pop(0) + launch_info = lldb.SBLaunchInfo (args) + if options.env_vars: + launch_info.SetEnvironmentEntries(options.env_vars, True) + if options.working_dir: + launch_info.SetWorkingDirectory(options.working_dir) + elif options.attach_pid != -1: + if options.run_count == 1: + attach_info = lldb.SBAttachInfo (options.attach_pid) + else: + print "error: --run-count can't be used with the --attach-pid option" + sys.exit(1) + elif not options.attach_name is None: + if options.run_count == 1: + attach_info = lldb.SBAttachInfo (options.attach_name, options.attach_wait) + else: + print "error: --run-count can't be used with the --attach-name option" + sys.exit(1) + else: + print 'error: a program path for a program to debug and its arguments are required' + sys.exit(1) + + + + # Create a new debugger instance + debugger = lldb.SBDebugger.Create() + debugger.SetAsync (True) + command_interpreter = debugger.GetCommandInterpreter() + # Create a target from a file and arch + + if exe: + print "Creating a target for '%s'" % exe + error = lldb.SBError() + target = debugger.CreateTarget (exe, options.arch, options.platform, True, error) + + if target: + + # Set any breakpoints that were specified in the args if we are launching. We use the + # command line command to take advantage of the shorthand breakpoint creation + if launch_info and options.breakpoints: + for bp in options.breakpoints: + debugger.HandleCommand( "_regexp-break %s" % (bp)) + run_commands(command_interpreter, ['breakpoint list']) + + for run_idx in range(options.run_count): + # Launch the process. Since we specified synchronous mode, we won't return + # from this function until we hit the breakpoint at main + error = lldb.SBError() + + if launch_info: + if options.run_count == 1: + print 'Launching "%s"...' % (exe) + else: + print 'Launching "%s"... (launch %u of %u)' % (exe, run_idx + 1, options.run_count) + + process = target.Launch (launch_info, error) + else: + if options.attach_pid != -1: + print 'Attaching to process %i...' % (options.attach_pid) + else: + if options.attach_wait: + print 'Waiting for next to process named "%s" to launch...' % (options.attach_name) + else: + print 'Attaching to existing process named "%s"...' % (options.attach_name) + process = target.Attach (attach_info, error) + + # Make sure the launch went ok + if process and process.GetProcessID() != lldb.LLDB_INVALID_PROCESS_ID: + + pid = process.GetProcessID() + print 'Process is %i' % (pid) + if attach_info: + # continue process if we attached as we won't get an initial event + process.Continue() + + listener = debugger.GetListener() + # sign up for process state change events + stop_idx = 0 + done = False + while not done: + event = lldb.SBEvent() + if listener.WaitForEvent (options.event_timeout, event): + if lldb.SBProcess.EventIsProcessEvent(event): + state = lldb.SBProcess.GetStateFromEvent (event) + if state == lldb.eStateInvalid: + # Not a state event + print 'process event = %s' % (event) + else: + print "process state changed event: %s" % (lldb.SBDebugger.StateAsCString(state)) + if state == lldb.eStateStopped: + if stop_idx == 0: + if launch_info: + print "process %u launched" % (pid) + run_commands(command_interpreter, ['breakpoint list']) + else: + print "attached to process %u" % (pid) + for m in target.modules: + print m + if options.breakpoints: + for bp in options.breakpoints: + debugger.HandleCommand( "_regexp-break %s" % (bp)) + run_commands(command_interpreter, ['breakpoint list']) + run_commands (command_interpreter, options.launch_commands) + else: + if options.verbose: + print "process %u stopped" % (pid) + run_commands (command_interpreter, options.stop_commands) + stop_idx += 1 + print_threads (process, options) + print "continuing process %u" % (pid) + process.Continue() + elif state == lldb.eStateExited: + exit_desc = process.GetExitDescription() + if exit_desc: + print "process %u exited with status %u: %s" % (pid, process.GetExitStatus (), exit_desc) + else: + print "process %u exited with status %u" % (pid, process.GetExitStatus ()) + run_commands (command_interpreter, options.exit_commands) + done = True + elif state == lldb.eStateCrashed: + print "process %u crashed" % (pid) + print_threads (process, options) + run_commands (command_interpreter, options.crash_commands) + done = True + elif state == lldb.eStateDetached: + print "process %u detached" % (pid) + done = True + elif state == lldb.eStateRunning: + # process is running, don't say anything, we will always get one of these after resuming + if options.verbose: + print "process %u resumed" % (pid) + elif state == lldb.eStateUnloaded: + print "process %u unloaded, this shouldn't happen" % (pid) + done = True + elif state == lldb.eStateConnected: + print "process connected" + elif state == lldb.eStateAttaching: + print "process attaching" + elif state == lldb.eStateLaunching: + print "process launching" + else: + print 'event = %s' % (event) + else: + # timeout waiting for an event + print "no process event for %u seconds, killing the process..." % (options.event_timeout) + done = True + # Now that we are done dump the stdout and stderr + process_stdout = process.GetSTDOUT(1024) + if process_stdout: + print "Process STDOUT:\n%s" % (process_stdout) + while process_stdout: + process_stdout = process.GetSTDOUT(1024) + print process_stdout + process_stderr = process.GetSTDERR(1024) + if process_stderr: + print "Process STDERR:\n%s" % (process_stderr) + while process_stderr: + process_stderr = process.GetSTDERR(1024) + print process_stderr + process.Kill() # kill the process + else: + if error: + print error + else: + if launch_info: + print 'error: launch failed' + else: + print 'error: attach failed' + + lldb.SBDebugger.Terminate() + +if __name__ == '__main__': + main(sys.argv[1:]) \ No newline at end of file diff --git a/examples/python/pytracer.py b/examples/python/pytracer.py new file mode 100644 index 000000000000..61bbceefe057 --- /dev/null +++ b/examples/python/pytracer.py @@ -0,0 +1,328 @@ +import sys +import inspect +from collections import OrderedDict + +class TracebackFancy: + def __init__(self,traceback): + self.t = traceback + + def getFrame(self): + return FrameFancy(self.t.tb_frame) + + def getLineNumber(self): + return self.t.tb_lineno if self.t != None else None + + def getNext(self): + return TracebackFancy(self.t.tb_next) + + def __str__(self): + if self.t == None: + return "" + str_self = "%s @ %s" % (self.getFrame().getName(), self.getLineNumber()) + return str_self + "\n" + self.getNext().__str__() + +class ExceptionFancy: + def __init__(self,frame): + self.etraceback = frame.f_exc_traceback + self.etype = frame.exc_type + self.evalue = frame.f_exc_value + + def __init__(self,tb,ty,va): + self.etraceback = tb + self.etype = ty + self.evalue = va + + def getTraceback(self): + return TracebackFancy(self.etraceback) + + def __nonzero__(self): + return self.etraceback != None or self.etype != None or self.evalue != None + + def getType(self): + return str(self.etype) + + def getValue(self): + return self.evalue + +class CodeFancy: + def __init__(self,code): + self.c = code + + def getArgCount(self): + return self.c.co_argcount if self.c != None else 0 + + def getFilename(self): + return self.c.co_filename if self.c != None else "" + + def getVariables(self): + return self.c.co_varnames if self.c != None else [] + + def getName(self): + return self.c.co_name if self.c != None else "" + + def getFileName(self): + return self.c.co_filename if self.c != None else "" + +class ArgsFancy: + def __init__(self,frame,arginfo): + self.f = frame + self.a = arginfo + + def __str__(self): + args, varargs, kwargs = self.getArgs(), self.getVarArgs(), self.getKWArgs() + ret = "" + count = 0 + size = len(args) + for arg in args: + ret = ret + ("%s = %s" % (arg, args[arg])) + count = count + 1 + if count < size: + ret = ret + ", " + if varargs: + if size > 0: + ret = ret + " " + ret = ret + "varargs are " + str(varargs) + if kwargs: + if size > 0: + ret = ret + " " + ret = ret + "kwargs are " + str(kwargs) + return ret + + def getNumArgs(wantVarargs = False, wantKWArgs=False): + args, varargs, keywords, values = self.a + size = len(args) + if varargs and wantVarargs: + size = size+len(self.getVarArgs()) + if keywords and wantKWArgs: + size = size+len(self.getKWArgs()) + return size + + def getArgs(self): + args, _, _, values = self.a + argWValues = OrderedDict() + for arg in args: + argWValues[arg] = values[arg] + return argWValues + + def getVarArgs(self): + _, vargs, _, _ = self.a + if vargs: + return self.f.f_locals[vargs] + return () + + def getKWArgs(self): + _, _, kwargs, _ = self.a + if kwargs: + return self.f.f_locals[kwargs] + return {} + +class FrameFancy: + def __init__(self,frame): + self.f = frame + + def getCaller(self): + return FrameFancy(self.f.f_back) + + def getLineNumber(self): + return self.f.f_lineno if self.f != None else 0 + + def getCodeInformation(self): + return CodeFancy(self.f.f_code) if self.f != None else None + + def getExceptionInfo(self): + return ExceptionFancy(self.f) if self.f != None else None + + def getName(self): + return self.getCodeInformation().getName() if self.f != None else "" + + def getFileName(self): + return self.getCodeInformation().getFileName() if self.f != None else "" + + def getLocals(self): + return self.f.f_locals if self.f != None else {} + + def getArgumentInfo(self): + return ArgsFancy(self.f,inspect.getargvalues(self.f)) if self.f != None else None + +class TracerClass: + def callEvent(self,frame): + pass + + def lineEvent(self,frame): + pass + + def returnEvent(self,frame,retval): + pass + + def exceptionEvent(self,frame,exception,value,traceback): + pass + + def cCallEvent(self,frame,cfunct): + pass + + def cReturnEvent(self,frame,cfunct): + pass + + def cExceptionEvent(self,frame,cfunct): + pass + +tracer_impl = TracerClass() + + +def the_tracer_entrypoint(frame,event,args): + if tracer_impl == None: + return None + if event == "call": + call_retval = tracer_impl.callEvent(FrameFancy(frame)) + if call_retval == False: + return None + return the_tracer_entrypoint + elif event == "line": + line_retval = tracer_impl.lineEvent(FrameFancy(frame)) + if line_retval == False: + return None + return the_tracer_entrypoint + elif event == "return": + tracer_impl.returnEvent(FrameFancy(frame),args) + elif event == "exception": + exty,exva,extb = args + exception_retval = tracer_impl.exceptionEvent(FrameFancy(frame),ExceptionFancy(extb,exty,exva)) + if exception_retval == False: + return None + return the_tracer_entrypoint + elif event == "c_call": + tracer_impl.cCallEvent(FrameFancy(frame),args) + elif event == "c_return": + tracer_impl.cReturnEvent(FrameFancy(frame),args) + elif event == "c_exception": + tracer_impl.cExceptionEvent(FrameFancy(frame),args) + return None + +def enable(t=None): + global tracer_impl + if t: + tracer_impl = t + sys.settrace(the_tracer_entrypoint) + +def disable(): + sys.settrace(None) + +class LoggingTracer: + def callEvent(self,frame): + print "call " + frame.getName() + " from " + frame.getCaller().getName() + " @ " + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo()) + + def lineEvent(self,frame): + print "running " + frame.getName() + " @ " + str(frame.getLineNumber()) + " locals are " + str(frame.getLocals()) + " in " + frame.getFileName() + + def returnEvent(self,frame,retval): + print "return from " + frame.getName() + " value is " + str(retval) + " locals are " + str(frame.getLocals()) + + def exceptionEvent(self,frame,exception): + print "exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber()) + print "tb: " + str(exception.getTraceback()) + +# the same functionality as LoggingTracer, but with a little more lldb-specific smarts +class LLDBAwareTracer: + def callEvent(self,frame): + if frame.getName() == "": + return + if frame.getName() == "run_one_line": + print "call run_one_line(%s)" % (frame.getArgumentInfo().getArgs()["input_string"]) + return + if "Python.framework" in frame.getFileName(): + print "call into Python at " + frame.getName() + return + if frame.getName() == "__init__" and frame.getCaller().getName() == "run_one_line" and frame.getCaller().getLineNumber() == 101: + return False + strout = "call " + frame.getName() + if (frame.getCaller().getFileName() == ""): + strout += " from LLDB - args are " + args = frame.getArgumentInfo().getArgs() + for arg in args: + if arg == "dict" or arg == "internal_dict": + continue + strout = strout + ("%s = %s " % (arg,args[arg])) + else: + strout += " from " + frame.getCaller().getName() + " @ " + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo()) + print strout + + def lineEvent(self,frame): + if frame.getName() == "": + return + if frame.getName() == "run_one_line": + print "running run_one_line(%s) @ %s" % (frame.getArgumentInfo().getArgs()["input_string"],frame.getLineNumber()) + return + if "Python.framework" in frame.getFileName(): + print "running into Python at " + frame.getName() + " @ " + str(frame.getLineNumber()) + return + strout = "running " + frame.getName() + " @ " + str(frame.getLineNumber()) + " locals are " + if (frame.getCaller().getFileName() == ""): + locals = frame.getLocals() + for local in locals: + if local == "dict" or local == "internal_dict": + continue + strout = strout + ("%s = %s " % (local,locals[local])) + else: + strout = strout + str(frame.getLocals()) + strout = strout + " in " + frame.getFileName() + print strout + + def returnEvent(self,frame,retval): + if frame.getName() == "": + return + if frame.getName() == "run_one_line": + print "return from run_one_line(%s) return value is %s" % (frame.getArgumentInfo().getArgs()["input_string"],retval) + return + if "Python.framework" in frame.getFileName(): + print "return from Python at " + frame.getName() + " return value is " + str(retval) + return + strout = "return from " + frame.getName() + " return value is " + str(retval) + " locals are " + if (frame.getCaller().getFileName() == ""): + locals = frame.getLocals() + for local in locals: + if local == "dict" or local == "internal_dict": + continue + strout = strout + ("%s = %s " % (local,locals[local])) + else: + strout = strout + str(frame.getLocals()) + strout = strout + " in " + frame.getFileName() + print strout + + def exceptionEvent(self,frame,exception): + if frame.getName() == "": + return + print "exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber()) + print "tb: " + str(exception.getTraceback()) + +def f(x,y=None): + if x > 0: + return 2 + f(x-2) + return 35 + +def g(x): + return 1.134 / x + +def print_keyword_args(**kwargs): + # kwargs is a dict of the keyword args passed to the function + for key, value in kwargs.iteritems(): + print "%s = %s" % (key, value) + +def total(initial=5, *numbers, **keywords): + count = initial + for number in numbers: + count += number + for key in keywords: + count += keywords[key] + return count + +if __name__ == "__main__": + enable(LoggingTracer()) + f(5) + f(5,1) + print_keyword_args(first_name="John", last_name="Doe") + total(10, 1, 2, 3, vegetables=50, fruits=100) + try: + g(0) + except: + pass + disable() diff --git a/examples/python/sbvalue.py b/examples/python/sbvalue.py new file mode 100755 index 000000000000..59c0b61e5528 --- /dev/null +++ b/examples/python/sbvalue.py @@ -0,0 +1,255 @@ +#!/usr/bin/python + +import lldb + +class value(object): + '''A class that wraps an lldb.SBValue object and returns an object that + can be used as an object with attribytes:\n + argv = a.value(lldb.frame.FindVariable('argv'))\n + argv.name - return the name of the value that this object contains\n + argv.type - return the lldb.SBType for this value + argv.type_name - return the name of the type + argv.size - return the byte size of this value + argv.is_in_scope - return true if this value is currently in scope + argv.is_pointer - return true if this value is a pointer + argv.format - return the current format for this value + argv.value - return the value's value as a string + argv.summary - return a summary of this value's value + argv.description - return the runtime description for this value + argv.location - return a string that represents the values location (address, register, etc) + argv.target - return the lldb.SBTarget for this value + argv.process - return the lldb.SBProcess for this value + argv.thread - return the lldb.SBThread for this value + argv.frame - return the lldb.SBFrame for this value + argv.num_children - return the number of children this value has + argv.children - return a list of sbvalue objects that represents all of the children of this value + ''' + def __init__(self, sbvalue): + self.sbvalue = sbvalue + + def __nonzero__(self): + return self.sbvalue.__nonzero__() + + def __repr__(self): + return self.sbvalue.__repr__() + + def __str__(self): + return self.sbvalue.__str__() + + def __getitem__(self, key): + if type(key) is int: + return value(self.sbvalue.GetChildAtIndex(key, lldb.eNoDynamicValues, True)) + raise TypeError + + def __getattr__(self, name): + if name == 'name': + return self.sbvalue.GetName() + if name == 'type': + return self.sbvalue.GetType() + if name == 'type_name': + return self.sbvalue.GetTypeName() + if name == 'size': + return self.sbvalue.GetByteSize() + if name == 'is_in_scope': + return self.sbvalue.IsInScope() + if name == 'is_pointer': + return self.sbvalue.TypeIsPointerType() + if name == 'format': + return self.sbvalue.GetFormat () + if name == 'value': + return self.sbvalue.GetValue () + if name == 'summary': + return self.sbvalue.GetSummary () + if name == 'description': + return self.sbvalue.GetObjectDescription () + if name == 'location': + return self.sbvalue.GetLocation () + if name == 'target': + return self.sbvalue.GetTarget() + if name == 'process': + return self.sbvalue.GetProcess() + if name == 'thread': + return self.sbvalue.GetThread() + if name == 'frame': + return self.sbvalue.GetFrame() + if name == 'num_children': + return self.sbvalue.GetNumChildren() + if name == 'children': + # Returns an array of sbvalue objects, one for each child of + # the value for the lldb.SBValue + children = [] + for i in range (self.sbvalue.GetNumChildren()): + children.append(value(self.sbvalue.GetChildAtIndex(i, lldb.eNoDynamicValues, True))) + return children + raise AttributeError + +class variable(object): + '''A class that treats a lldb.SBValue and allows it to be used just as + a variable would be in code. So if you have a Point structure variable + in your code, you would be able to do: "pt.x + pt.y"''' + def __init__(self, sbvalue): + self.sbvalue = sbvalue + + def __nonzero__(self): + return self.sbvalue.__nonzero__() + + def __repr__(self): + return self.sbvalue.__repr__() + + def __str__(self): + return self.sbvalue.__str__() + + def __getitem__(self, key): + # Allow array access if this value has children... + if type(key) is int: + return variable(self.sbvalue.GetValueForExpressionPath("[%i]" % key)) + raise TypeError + + def __getattr__(self, name): + child_sbvalue = self.sbvalue.GetChildMemberWithName (name) + if child_sbvalue: + return variable(child_sbvalue) + raise AttributeError + + def __add__(self, other): + return int(self) + int(other) + + def __sub__(self, other): + return int(self) - int(other) + + def __mul__(self, other): + return int(self) * int(other) + + def __floordiv__(self, other): + return int(self) // int(other) + + def __mod__(self, other): + return int(self) % int(other) + + def __divmod__(self, other): + return int(self) % int(other) + + def __pow__(self, other): + return int(self) ** int(other) + + def __lshift__(self, other): + return int(self) << int(other) + + def __rshift__(self, other): + return int(self) >> int(other) + + def __and__(self, other): + return int(self) & int(other) + + def __xor__(self, other): + return int(self) ^ int(other) + + def __or__(self, other): + return int(self) | int(other) + + def __div__(self, other): + return int(self) / int(other) + + def __truediv__(self, other): + return int(self) / int(other) + + def __iadd__(self, other): + result = self.__add__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __isub__(self, other): + result = self.__sub__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __imul__(self, other): + result = self.__mul__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __idiv__(self, other): + result = self.__div__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __itruediv__(self, other): + result = self.__truediv__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ifloordiv__(self, other): + result = self.__floordiv__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __imod__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ipow__(self, other): + result = self.__pow__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ipow__(self, other, modulo): + result = self.__pow__(self, other, modulo) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ilshift__(self, other): + result = self.__lshift__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __irshift__(self, other): + result = self.__rshift__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __iand__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ixor__(self, other): + result = self.__xor__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ior__(self, other): + result = self.__ior__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __neg__(self): + return -int(self) + + def __pos__(self): + return +int(self) + + def __abs__(self): + return abs(int(self)) + + def __invert__(self): + return ~int(self) + + def __complex__(self): + return complex (int(self)) + + def __int__(self): + return self.sbvalue.GetValueAsSigned() + + def __long__(self): + return self.sbvalue.GetValueAsSigned() + + def __float__(self): + return float (self.sbvalue.GetValueAsSigned()) + + def __oct__(self): + return '0%o' % self.sbvalue.GetValueAsSigned() + + def __hex__(self): + return '0x%x' % self.sbvalue.GetValueAsSigned() + \ No newline at end of file diff --git a/examples/python/scripted_step.py b/examples/python/scripted_step.py new file mode 100644 index 000000000000..8affb9e83220 --- /dev/null +++ b/examples/python/scripted_step.py @@ -0,0 +1,186 @@ +############################################################################# +# This script contains two trivial examples of simple "scripted step" classes. +# To fully understand how the lldb "Thread Plan" architecture works, read the +# comments at the beginning of ThreadPlan.h in the lldb sources. The python +# interface is a reduced version of the full internal mechanism, but captures +# most of the power with a much simpler interface. +# +# But I'll attempt a brief summary here. +# Stepping in lldb is done independently for each thread. Moreover, the stepping +# operations are stackable. So for instance if you did a "step over", and in +# the course of stepping over you hit a breakpoint, stopped and stepped again, +# the first "step-over" would be suspended, and the new step operation would +# be enqueued. Then if that step over caused the program to hit another breakpoint, +# lldb would again suspend the second step and return control to the user, so +# now there are two pending step overs. Etc. with all the other stepping +# operations. Then if you hit "continue" the bottom-most step-over would complete, +# and another continue would complete the first "step-over". +# +# lldb represents this system with a stack of "Thread Plans". Each time a new +# stepping operation is requested, a new plan is pushed on the stack. When the +# operation completes, it is pushed off the stack. +# +# The bottom-most plan in the stack is the immediate controller of stepping, +# most importantly, when the process resumes, the bottom most plan will get +# asked whether to set the program running freely, or to instruction-single-step +# the current thread. In the scripted interface, you indicate this by returning +# False or True respectively from the should_step method. +# +# Each time the process stops the thread plan stack for each thread that stopped +# "for a reason", Ii.e. a single-step completed on that thread, or a breakpoint +# was hit), is queried to determine how to proceed, starting from the most +# recently pushed plan, in two stages: +# +# 1) Each plan is asked if it "explains" the stop. The first plan to claim the +# stop wins. In scripted Thread Plans, this is done by returning True from +# the "explains_stop method. This is how, for instance, control is returned +# to the User when the "step-over" plan hits a breakpoint. The step-over +# plan doesn't explain the breakpoint stop, so it returns false, and the +# breakpoint hit is propagated up the stack to the "base" thread plan, which +# is the one that handles random breakpoint hits. +# +# 2) Then the plan that won the first round is asked if the process should stop. +# This is done in the "should_stop" method. The scripted plans actually do +# three jobs in should_stop: +# a) They determine if they have completed their job or not. If they have +# they indicate that by calling SetPlanComplete on their thread plan. +# b) They decide whether they want to return control to the user or not. +# They do this by returning True or False respectively. +# c) If they are not done, they set up whatever machinery they will use +# the next time the thread continues. +# +# Note that deciding to return control to the user, and deciding your plan +# is done, are orthgonal operations. You could set up the next phase of +# stepping, and then return True from should_stop, and when the user next +# "continued" the process your plan would resume control. Of course, the +# user might also "step-over" or some other operation that would push a +# different plan, which would take control till it was done. +# +# One other detail you should be aware of, if the plan below you on the +# stack was done, then it will be popped and the next plan will take control +# and its "should_stop" will be called. +# +# Note also, there should be another method called when your plan is popped, +# to allow you to do whatever cleanup is required. I haven't gotten to that +# yet. For now you should do that at the same time you mark your plan complete. +# +# Both examples show stepping through an address range for 20 bytes from the +# current PC. The first one does it by single stepping and checking a condition. +# It doesn't, however handle the case where you step into another frame while +# still in the current range in the starting frame. +# +# That is better handled in the second example by using the built-in StepOverRange +# thread plan. +# +# To use these stepping modes, you would do: +# +# (lldb) command script import scripted_step.py +# (lldb) thread step-scripted -C scripted_step.SimpleStep +# or +# +# (lldb) thread step-scripted -C scripted_step.StepWithPlan + +import lldb + +class SimpleStep: + def __init__ (self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPC() + + def explains_stop (self, event): + # We are stepping, so if we stop for any other reason, it isn't + # because of us. + if self.thread_plan.GetThread().GetStopReason()== lldb.eStopReasonTrace: + return True + else: + return False + + def should_stop (self, event): + cur_pc = self.thread_plan.GetThread().GetFrameAtIndex(0).GetPC() + + if cur_pc < self.start_address or cur_pc >= self.start_address + 20: + self.thread_plan.SetPlanComplete(True) + return True + else: + return False + + def should_step (self): + return True + +class StepWithPlan: + def __init__ (self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPCAddress() + self.step_thread_plan =thread_plan.QueueThreadPlanForStepOverRange(self.start_address, 20); + + def explains_stop (self, event): + # Since all I'm doing is running a plan, I will only ever get askedthis + # if myplan doesn't explain the stop, and in that caseI don'teither. + return False + + def should_stop (self, event): + if self.step_thread_plan.IsPlanComplete(): + self.thread_plan.SetPlanComplete(True) + return True + else: + return False + + def should_step (self): + return False + +# Here's another example which does "step over" through the current function, +# and when it stops at each line, it checks some condition (in this example the +# value of a variable) and stops if that condition is true. + +class StepCheckingCondition: + def __init__ (self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_frame = thread_plan.GetThread().GetFrameAtIndex(0) + self.queue_next_plan() + + def queue_next_plan (self): + cur_frame = self.thread_plan.GetThread().GetFrameAtIndex(0) + cur_line_entry = cur_frame.GetLineEntry() + start_address = cur_line_entry.GetStartAddress() + end_address = cur_line_entry.GetEndAddress() + line_range = end_address.GetFileAddress() - start_address.GetFileAddress() + self.step_thread_plan = self.thread_plan.QueueThreadPlanForStepOverRange(start_address, line_range) + + def explains_stop (self, event): + # We are stepping, so if we stop for any other reason, it isn't + # because of us. + return False + + def should_stop (self, event): + if not self.step_thread_plan.IsPlanComplete(): + return False + + frame = self.thread_plan.GetThread().GetFrameAtIndex(0) + if not self.start_frame.IsEqual(frame): + self.thread_plan.SetPlanComplete(True) + return True + + # This part checks the condition. In this case we are expecting + # some integer variable called "a", and will stop when it is 20. + a_var = frame.FindVariable("a") + + if not a_var.IsValid(): + print "A was not valid." + return True + + error = lldb.SBError() + a_value = a_var.GetValueAsSigned (error) + if not error.Success(): + print "A value was not good." + return True + + if a_value == 20: + self.thread_plan.SetPlanComplete(True) + return True + else: + self.queue_next_plan() + return False + + def should_step (self): + return True + diff --git a/examples/python/sources.py b/examples/python/sources.py new file mode 100644 index 000000000000..0eb5858805be --- /dev/null +++ b/examples/python/sources.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +import lldb +import shlex + +def dump_module_sources(module, result): + if module: + print >> result, "Module: %s" % (module.file) + for compile_unit in module.compile_units: + if compile_unit.file: + print >> result, " %s" % (compile_unit.file) + +def info_sources(debugger, command, result, dict): + description='''This command will dump all compile units in any modules that are listed as arguments, or for all modules if no arguments are supplied.''' + module_names = shlex.split(command) + target = debugger.GetSelectedTarget() + if module_names: + for module_name in module_names: + dump_module_sources(target.module[module_name], result) + else: + for module in target.modules: + dump_module_sources(module, result) + + +def __lldb_init_module (debugger, dict): + # Add any commands contained in this module to LLDB + debugger.HandleCommand('command script add -f sources.info_sources info_sources') + print 'The "info_sources" command has been installed, type "help info_sources" or "info_sources --help" for detailed help.' diff --git a/examples/python/stacks.py b/examples/python/stacks.py new file mode 100755 index 000000000000..06907e159d7f --- /dev/null +++ b/examples/python/stacks.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +import lldb +import commands +import optparse +import shlex + +def stack_frames(debugger, command, result, dict): + command_args = shlex.split(command) + usage = "usage: %prog [options] [PATH ...]" + description='''This command will enumerate all stack frames, print the stack size for each, and print an aggregation of which functions have the largest stack frame sizes at the end.''' + parser = optparse.OptionParser(description=description, prog='ls',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + target = debugger.GetSelectedTarget() + process = target.GetProcess() + + frame_info = {} + for thread in process: + last_frame = None + print "thread %u" % (thread.id) + for frame in thread.frames: + if last_frame: + frame_size = 0 + if frame.idx == 1: + if frame.fp == last_frame.fp: + # No frame one the first frame (might be right at the entry point) + first_frame_size = 0 + frame_size = frame.fp - frame.sp + else: + # First frame that has a valid size + first_frame_size = last_frame.fp - last_frame.sp + print "<%#7x> %s" % (first_frame_size, last_frame) + if first_frame_size: + name = last_frame.name + if name not in frame_info: + frame_info[name] = first_frame_size + else: + frame_info[name] += first_frame_size + else: + # Second or higher frame + frame_size = frame.fp - last_frame.fp + print "<%#7x> %s" % (frame_size, frame) + if frame_size > 0: + name = frame.name + if name not in frame_info: + frame_info[name] = frame_size + else: + frame_info[name] += frame_size + last_frame = frame + print frame_info + + +lldb.debugger.HandleCommand("command script add -f stacks.stack_frames stack_frames") +print "A new command called 'stack_frames' was added, type 'stack_frames --help' for more information." \ No newline at end of file diff --git a/examples/python/symbolication.py b/examples/python/symbolication.py new file mode 100755 index 000000000000..2f2a274dbc41 --- /dev/null +++ b/examples/python/symbolication.py @@ -0,0 +1,640 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb": +# +# cd /path/containing/crashlog.py +# lldb +# (lldb) script import crashlog +# "crashlog" command installed, type "crashlog --help" for detailed help +# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash +# +# The benefit of running the crashlog command inside lldb in the +# embedded python interpreter is when the command completes, there +# will be a target with all of the files loaded at the locations +# described in the crash log. Only the files that have stack frames +# in the backtrace will be loaded unless the "--load-all" option +# has been specified. This allows users to explore the program in the +# state it was in right at crash time. +# +# On MacOSX csh, tcsh: +# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) +# +# On MacOSX sh, bash: +# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash +#---------------------------------------------------------------------- + +import lldb +import commands +import optparse +import os +import plistlib +import re +import shlex +import sys +import time +import uuid + +class Address: + """Class that represents an address that will be symbolicated""" + def __init__(self, target, load_addr): + self.target = target + self.load_addr = load_addr # The load address that this object represents + self.so_addr = None # the resolved lldb.SBAddress (if any), named so_addr for section/offset address + self.sym_ctx = None # The cached symbol context for this address + self.description = None # Any original textual description of this address to be used as a backup in case symbolication fails + self.symbolication = None # The cached symbolicated string that describes this address + self.inlined = False + def __str__(self): + s = "%#16.16x" % (self.load_addr) + if self.symbolication: + s += " %s" % (self.symbolication) + elif self.description: + s += " %s" % (self.description) + elif self.so_addr: + s += " %s" % (self.so_addr) + return s + + def resolve_addr(self): + if self.so_addr == None: + self.so_addr = self.target.ResolveLoadAddress (self.load_addr) + return self.so_addr + + def is_inlined(self): + return self.inlined + + def get_symbol_context(self): + if self.sym_ctx == None: + sb_addr = self.resolve_addr() + if sb_addr: + self.sym_ctx = self.target.ResolveSymbolContextForAddress (sb_addr, lldb.eSymbolContextEverything) + else: + self.sym_ctx = lldb.SBSymbolContext() + return self.sym_ctx + + def get_instructions(self): + sym_ctx = self.get_symbol_context() + if sym_ctx: + function = sym_ctx.GetFunction() + if function: + return function.GetInstructions(self.target) + return sym_ctx.GetSymbol().GetInstructions(self.target) + return None + + def symbolicate(self, verbose = False): + if self.symbolication == None: + self.symbolication = '' + self.inlined = False + sym_ctx = self.get_symbol_context() + if sym_ctx: + module = sym_ctx.GetModule() + if module: + # Print full source file path in verbose mode + if verbose: + self.symbolication += str(module.GetFileSpec()) + '`' + else: + self.symbolication += module.GetFileSpec().GetFilename() + '`' + function_start_load_addr = -1 + function = sym_ctx.GetFunction() + block = sym_ctx.GetBlock() + line_entry = sym_ctx.GetLineEntry() + symbol = sym_ctx.GetSymbol() + inlined_block = block.GetContainingInlinedBlock(); + if function: + self.symbolication += function.GetName() + + if inlined_block: + self.inlined = True + self.symbolication += ' [inlined] ' + inlined_block.GetInlinedName(); + block_range_idx = inlined_block.GetRangeIndexForBlockAddress (self.so_addr) + if block_range_idx < lldb.UINT32_MAX: + block_range_start_addr = inlined_block.GetRangeStartAddress (block_range_idx) + function_start_load_addr = block_range_start_addr.GetLoadAddress (self.target) + if function_start_load_addr == -1: + function_start_load_addr = function.GetStartAddress().GetLoadAddress (self.target) + elif symbol: + self.symbolication += symbol.GetName() + function_start_load_addr = symbol.GetStartAddress().GetLoadAddress (self.target) + else: + self.symbolication = '' + return False + + # Dump the offset from the current function or symbol if it is non zero + function_offset = self.load_addr - function_start_load_addr + if function_offset > 0: + self.symbolication += " + %u" % (function_offset) + elif function_offset < 0: + self.symbolication += " %i (invalid negative offset, file a bug) " % function_offset + + # Print out any line information if any is available + if line_entry.GetFileSpec(): + # Print full source file path in verbose mode + if verbose: + self.symbolication += ' at %s' % line_entry.GetFileSpec() + else: + self.symbolication += ' at %s' % line_entry.GetFileSpec().GetFilename() + self.symbolication += ':%u' % line_entry.GetLine () + column = line_entry.GetColumn() + if column > 0: + self.symbolication += ':%u' % column + return True + return False + +class Section: + """Class that represents an load address range""" + sect_info_regex = re.compile('(?P[^=]+)=(?P.*)') + addr_regex = re.compile('^\s*(?P0x[0-9A-Fa-f]+)\s*$') + range_regex = re.compile('^\s*(?P0x[0-9A-Fa-f]+)\s*(?P[-+])\s*(?P0x[0-9A-Fa-f]+)\s*$') + + def __init__(self, start_addr = None, end_addr = None, name = None): + self.start_addr = start_addr + self.end_addr = end_addr + self.name = name + + @classmethod + def InitWithSBTargetAndSBSection(cls, target, section): + sect_load_addr = section.GetLoadAddress(target) + if sect_load_addr != lldb.LLDB_INVALID_ADDRESS: + obj = cls(sect_load_addr, sect_load_addr + section.size, section.name) + return obj + else: + return None + + def contains(self, addr): + return self.start_addr <= addr and addr < self.end_addr; + + def set_from_string(self, s): + match = self.sect_info_regex.match (s) + if match: + self.name = match.group('name') + range_str = match.group('range') + addr_match = self.addr_regex.match(range_str) + if addr_match: + self.start_addr = int(addr_match.group('start'), 16) + self.end_addr = None + return True + + range_match = self.range_regex.match(range_str) + if range_match: + self.start_addr = int(range_match.group('start'), 16) + self.end_addr = int(range_match.group('end'), 16) + op = range_match.group('op') + if op == '+': + self.end_addr += self.start_addr + return True + print 'error: invalid section info string "%s"' % s + print 'Valid section info formats are:' + print 'Format Example Description' + print '--------------------- -----------------------------------------------' + print '= __TEXT=0x123000 Section from base address only' + print '=- __TEXT=0x123000-0x124000 Section from base address and end address' + print '=+ __TEXT=0x123000+0x1000 Section from base address and size' + return False + + def __str__(self): + if self.name: + if self.end_addr != None: + if self.start_addr != None: + return "%s=[0x%16.16x - 0x%16.16x)" % (self.name, self.start_addr, self.end_addr) + else: + if self.start_addr != None: + return "%s=0x%16.16x" % (self.name, self.start_addr) + return self.name + return "" + +class Image: + """A class that represents an executable image and any associated data""" + + def __init__(self, path, uuid = None): + self.path = path + self.resolved_path = None + self.resolved = False + self.unavailable = False + self.uuid = uuid + self.section_infos = list() + self.identifier = None + self.version = None + self.arch = None + self.module = None + self.symfile = None + self.slide = None + + @classmethod + def InitWithSBTargetAndSBModule(cls, target, module): + '''Initialize this Image object with a module from a target.''' + obj = cls(module.file.fullpath, module.uuid) + obj.resolved_path = module.platform_file.fullpath + obj.resolved = True + obj.arch = module.triple + for section in module.sections: + symb_section = Section.InitWithSBTargetAndSBSection(target, section) + if symb_section: + obj.section_infos.append (symb_section) + obj.arch = module.triple + obj.module = module + obj.symfile = None + obj.slide = None + return obj + + def dump(self, prefix): + print "%s%s" % (prefix, self) + + def debug_dump(self): + print 'path = "%s"' % (self.path) + print 'resolved_path = "%s"' % (self.resolved_path) + print 'resolved = %i' % (self.resolved) + print 'unavailable = %i' % (self.unavailable) + print 'uuid = %s' % (self.uuid) + print 'section_infos = %s' % (self.section_infos) + print 'identifier = "%s"' % (self.identifier) + print 'version = %s' % (self.version) + print 'arch = %s' % (self.arch) + print 'module = %s' % (self.module) + print 'symfile = "%s"' % (self.symfile) + print 'slide = %i (0x%x)' % (self.slide, self.slide) + + def __str__(self): + s = '' + if self.uuid: + s += "%s " % (self.get_uuid()) + if self.arch: + s += "%s " % (self.arch) + if self.version: + s += "%s " % (self.version) + resolved_path = self.get_resolved_path() + if resolved_path: + s += "%s " % (resolved_path) + for section_info in self.section_infos: + s += ", %s" % (section_info) + if self.slide != None: + s += ', slide = 0x%16.16x' % self.slide + return s + + def add_section(self, section): + #print "added '%s' to '%s'" % (section, self.path) + self.section_infos.append (section) + + def get_section_containing_load_addr (self, load_addr): + for section_info in self.section_infos: + if section_info.contains(load_addr): + return section_info + return None + + def get_resolved_path(self): + if self.resolved_path: + return self.resolved_path + elif self.path: + return self.path + return None + + def get_resolved_path_basename(self): + path = self.get_resolved_path() + if path: + return os.path.basename(path) + return None + + def symfile_basename(self): + if self.symfile: + return os.path.basename(self.symfile) + return None + + def has_section_load_info(self): + return self.section_infos or self.slide != None + + def load_module(self, target): + if self.unavailable: + return None # We already warned that we couldn't find this module, so don't return an error string + # Load this module into "target" using the section infos to + # set the section load addresses + if self.has_section_load_info(): + if target: + if self.module: + if self.section_infos: + num_sections_loaded = 0 + for section_info in self.section_infos: + if section_info.name: + section = self.module.FindSection (section_info.name) + if section: + error = target.SetSectionLoadAddress (section, section_info.start_addr) + if error.Success(): + num_sections_loaded += 1 + else: + return 'error: %s' % error.GetCString() + else: + return 'error: unable to find the section named "%s"' % section_info.name + else: + return 'error: unable to find "%s" section in "%s"' % (range.name, self.get_resolved_path()) + if num_sections_loaded == 0: + return 'error: no sections were successfully loaded' + else: + err = target.SetModuleLoadAddress(self.module, self.slide) + if err.Fail(): + return err.GetCString() + return None + else: + return 'error: invalid module' + else: + return 'error: invalid target' + else: + return 'error: no section infos' + + def add_module(self, target): + '''Add the Image described in this object to "target" and load the sections if "load" is True.''' + if target: + # Try and find using UUID only first so that paths need not match up + uuid_str = self.get_normalized_uuid_string() + if uuid_str: + self.module = target.AddModule (None, None, uuid_str) + if not self.module: + self.locate_module_and_debug_symbols () + if self.unavailable: + return None + resolved_path = self.get_resolved_path() + self.module = target.AddModule (resolved_path, self.arch, uuid_str, self.symfile) + if not self.module: + return 'error: unable to get module for (%s) "%s"' % (self.arch, self.get_resolved_path()) + if self.has_section_load_info(): + return self.load_module(target) + else: + return None # No sections, the module was added to the target, so success + else: + return 'error: invalid target' + + def locate_module_and_debug_symbols (self): + # By default, just use the paths that were supplied in: + # self.path + # self.resolved_path + # self.module + # self.symfile + # Subclasses can inherit from this class and override this function + self.resolved = True + return True + + def get_uuid(self): + if not self.uuid and self.module: + self.uuid = uuid.UUID(self.module.GetUUIDString()) + return self.uuid + + def get_normalized_uuid_string(self): + if self.uuid: + return str(self.uuid).upper() + return None + + def create_target(self): + '''Create a target using the information in this Image object.''' + if self.unavailable: + return None + + if self.locate_module_and_debug_symbols (): + resolved_path = self.get_resolved_path(); + path_spec = lldb.SBFileSpec (resolved_path) + #result.PutCString ('plist[%s] = %s' % (uuid, self.plist)) + error = lldb.SBError() + target = lldb.debugger.CreateTarget (resolved_path, self.arch, None, False, error); + if target: + self.module = target.FindModule(path_spec) + if self.has_section_load_info(): + err = self.load_module(target) + if err: + print 'ERROR: ', err + return target + else: + print 'error: unable to create a valid target for (%s) "%s"' % (self.arch, self.path) + else: + print 'error: unable to locate main executable (%s) "%s"' % (self.arch, self.path) + return None + +class Symbolicator: + + def __init__(self): + """A class the represents the information needed to symbolicate addresses in a program""" + self.target = None + self.images = list() # a list of images to be used when symbolicating + self.addr_mask = 0xffffffffffffffff + + @classmethod + def InitWithSBTarget(cls, target): + obj = cls() + obj.target = target + obj.images = list(); + triple = target.triple + if triple: + arch = triple.split('-')[0] + if "arm" in arch: + obj.addr_mask = 0xfffffffffffffffe + + for module in target.modules: + image = Image.InitWithSBTargetAndSBModule(target, module) + obj.images.append(image) + return obj + + def __str__(self): + s = "Symbolicator:\n" + if self.target: + s += "Target = '%s'\n" % (self.target) + s += "Target modules:\n" + for m in self.target.modules: + s += str(m) + "\n" + s += "Images:\n" + for image in self.images: + s += ' %s\n' % (image) + return s + + def find_images_with_identifier(self, identifier): + images = list() + for image in self.images: + if image.identifier == identifier: + images.append(image) + if len(images) == 0: + regex_text = '^.*\.%s$' % (identifier) + regex = re.compile(regex_text) + for image in self.images: + if regex.match(image.identifier): + images.append(image) + return images + + def find_image_containing_load_addr(self, load_addr): + for image in self.images: + if image.get_section_containing_load_addr (load_addr): + return image + return None + + def create_target(self): + if self.target: + return self.target + + if self.images: + for image in self.images: + self.target = image.create_target () + if self.target: + if self.target.GetAddressByteSize() == 4: + triple = self.target.triple + if triple: + arch = triple.split('-')[0] + if "arm" in arch: + self.addr_mask = 0xfffffffffffffffe + return self.target + return None + + + def symbolicate(self, load_addr, verbose = False): + if not self.target: + self.create_target() + if self.target: + live_process = False + process = self.target.process + if process: + state = process.state + if state > lldb.eStateUnloaded and state < lldb.eStateDetached: + live_process = True + # If we don't have a live process, we can attempt to find the image + # that a load address belongs to and lazily load its module in the + # target, but we shouldn't do any of this if we have a live process + if not live_process: + image = self.find_image_containing_load_addr (load_addr) + if image: + image.add_module (self.target) + symbolicated_address = Address(self.target, load_addr) + if symbolicated_address.symbolicate (verbose): + if symbolicated_address.so_addr: + symbolicated_addresses = list() + symbolicated_addresses.append(symbolicated_address) + # See if we were able to reconstruct anything? + while 1: + inlined_parent_so_addr = lldb.SBAddress() + inlined_parent_sym_ctx = symbolicated_address.sym_ctx.GetParentOfInlinedScope (symbolicated_address.so_addr, inlined_parent_so_addr) + if not inlined_parent_sym_ctx: + break + if not inlined_parent_so_addr: + break + + symbolicated_address = Address(self.target, inlined_parent_so_addr.GetLoadAddress(self.target)) + symbolicated_address.sym_ctx = inlined_parent_sym_ctx + symbolicated_address.so_addr = inlined_parent_so_addr + symbolicated_address.symbolicate (verbose) + + # push the new frame onto the new frame stack + symbolicated_addresses.append (symbolicated_address) + + if symbolicated_addresses: + return symbolicated_addresses + else: + print 'error: no target in Symbolicator' + return None + + +def disassemble_instructions (target, instructions, pc, insts_before_pc, insts_after_pc, non_zeroeth_frame): + lines = list() + pc_index = -1 + comment_column = 50 + for inst_idx, inst in enumerate(instructions): + inst_pc = inst.GetAddress().GetLoadAddress(target); + if pc == inst_pc: + pc_index = inst_idx + mnemonic = inst.GetMnemonic (target) + operands = inst.GetOperands (target) + comment = inst.GetComment (target) + #data = inst.GetData (target) + lines.append ("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands)) + if comment: + line_len = len(lines[-1]) + if line_len < comment_column: + lines[-1] += ' ' * (comment_column - line_len) + lines[-1] += "; %s" % comment + + if pc_index >= 0: + # If we are disassembling the non-zeroeth frame, we need to backup the PC by 1 + if non_zeroeth_frame and pc_index > 0: + pc_index = pc_index - 1 + if insts_before_pc == -1: + start_idx = 0 + else: + start_idx = pc_index - insts_before_pc + if start_idx < 0: + start_idx = 0 + if insts_before_pc == -1: + end_idx = inst_idx + else: + end_idx = pc_index + insts_after_pc + if end_idx > inst_idx: + end_idx = inst_idx + for i in range(start_idx, end_idx+1): + if i == pc_index: + print ' -> ', lines[i] + else: + print ' ', lines[i] + +def print_module_section_data (section): + print section + section_data = section.GetSectionData() + if section_data: + ostream = lldb.SBStream() + section_data.GetDescription (ostream, section.GetFileAddress()) + print ostream.GetData() + +def print_module_section (section, depth): + print section + if depth > 0: + num_sub_sections = section.GetNumSubSections() + for sect_idx in range(num_sub_sections): + print_module_section (section.GetSubSectionAtIndex(sect_idx), depth - 1) + +def print_module_sections (module, depth): + for sect in module.section_iter(): + print_module_section (sect, depth) + +def print_module_symbols (module): + for sym in module: + print sym + +def Symbolicate(command_args): + + usage = "usage: %prog [options] [addr2 ...]" + description='''Symbolicate one or more addresses using LLDB's python scripting API..''' + parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + parser.add_option('-f', '--file', type='string', metavar='file', dest='file', help='Specify a file to use when symbolicating') + parser.add_option('-a', '--arch', type='string', metavar='arch', dest='arch', help='Specify a architecture to use when symbolicating') + parser.add_option('-s', '--slide', type='int', metavar='slide', dest='slide', help='Specify the slide to use on the file specified with the --file option', default=None) + parser.add_option('--section', type='string', action='append', dest='section_strings', help='specify = or =-') + try: + (options, args) = parser.parse_args(command_args) + except: + return + symbolicator = Symbolicator() + images = list(); + if options.file: + image = Image(options.file); + image.arch = options.arch + # Add any sections that were specified with one or more --section options + if options.section_strings: + for section_str in options.section_strings: + section = Section() + if section.set_from_string (section_str): + image.add_section (section) + else: + sys.exit(1) + if options.slide != None: + image.slide = options.slide + symbolicator.images.append(image) + + target = symbolicator.create_target() + if options.verbose: + print symbolicator + if target: + for addr_str in args: + addr = int(addr_str, 0) + symbolicated_addrs = symbolicator.symbolicate(addr, options.verbose) + for symbolicated_addr in symbolicated_addrs: + print symbolicated_addr + print + else: + print 'error: no target for %s' % (symbolicator) + +if __name__ == '__main__': + # Create a new debugger instance + lldb.debugger = lldb.SBDebugger.Create() + Symbolicate (sys.argv[1:]) diff --git a/examples/python/types.py b/examples/python/types.py new file mode 100755 index 000000000000..60ea7514c13a --- /dev/null +++ b/examples/python/types.py @@ -0,0 +1,265 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +#---------------------------------------------------------------------- + +import commands +import platform +import os +import re +import signal +import sys + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + +import commands +import optparse +import shlex +import time + +def regex_option_callback(option, opt_str, value, parser): + if opt_str == "--std": + value = '^std::' + regex = re.compile(value) + parser.values.skip_type_regexes.append (regex) + +def create_types_options(for_lldb_command): + if for_lldb_command: + usage = "usage: %prog [options]" + description='''This command will help check for padding in between +base classes and members in structs and classes. It will summarize the types +and how much padding was found. If no types are specified with the --types TYPENAME +option, all structure and class types will be verified. If no modules are +specified with the --module option, only the target's main executable will be +searched. +''' + else: + usage = "usage: %prog [options] EXEPATH [EXEPATH ...]" + description='''This command will help check for padding in between +base classes and members in structures and classes. It will summarize the types +and how much padding was found. One or more paths to executable files must be +specified and targets will be created with these modules. If no types are +specified with the --types TYPENAME option, all structure and class types will +be verified in all specified modules. +''' + parser = optparse.OptionParser(description=description, prog='framestats',usage=usage) + if not for_lldb_command: + parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) + parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + parser.add_option('-m', '--module', action='append', type='string', metavar='MODULE', dest='modules', help='Specify one or more modules which will be used to verify the types.', default=[]) + parser.add_option('-d', '--debug', action='store_true', dest='debug', help='Pause 10 seconds to wait for a debugger to attach.', default=False) + parser.add_option('-t', '--type', action='append', type='string', metavar='TYPENAME', dest='typenames', help='Specify one or more type names which should be verified. If no type names are specified, all class and struct types will be verified.', default=[]) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='Enable verbose logging and information.', default=False) + parser.add_option('-s', '--skip-type-regex', action="callback", callback=regex_option_callback, type='string', metavar='REGEX', dest='skip_type_regexes', help='Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.', default=[]) + parser.add_option('--std', action="callback", callback=regex_option_callback, metavar='REGEX', dest='skip_type_regexes', help="Don't' recurse into types in the std namespace.", default=[]) + return parser + +def verify_type (target, options, type): + print type + typename = type.GetName() + # print 'type: %s' % (typename) + (end_offset, padding) = verify_type_recursive (target, options, type, None, 0, 0, 0) + byte_size = type.GetByteSize() + # if end_offset < byte_size: + # last_member_padding = byte_size - end_offset + # print '%+4u <%u> padding' % (end_offset, last_member_padding) + # padding += last_member_padding + print 'Total byte size: %u' % (byte_size) + print 'Total pad bytes: %u' % (padding) + if padding > 0: + print 'Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0) + print + +def verify_type_recursive (target, options, type, member_name, depth, base_offset, padding): + prev_end_offset = base_offset + typename = type.GetName() + byte_size = type.GetByteSize() + if member_name and member_name != typename: + print '%+4u <%3u> %s%s %s;' % (base_offset, byte_size, ' ' * depth, typename, member_name) + else: + print '%+4u {%3u} %s%s' % (base_offset, byte_size, ' ' * depth, typename) + + for type_regex in options.skip_type_regexes: + match = type_regex.match (typename) + if match: + return (base_offset + byte_size, padding) + + members = type.members + if members: + for member_idx, member in enumerate(members): + member_type = member.GetType() + member_canonical_type = member_type.GetCanonicalType() + member_type_class = member_canonical_type.GetTypeClass() + member_name = member.GetName() + member_offset = member.GetOffsetInBytes() + member_total_offset = member_offset + base_offset + member_byte_size = member_type.GetByteSize() + member_is_class_or_struct = False + if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass: + member_is_class_or_struct = True + if member_idx == 0 and member_offset == target.GetAddressByteSize() and type.IsPolymorphicClass(): + ptr_size = target.GetAddressByteSize() + print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1)) + prev_end_offset = ptr_size + else: + if prev_end_offset < member_total_offset: + member_padding = member_total_offset - prev_end_offset + padding = padding + member_padding + print '%+4u <%3u> %s' % (prev_end_offset, member_padding, ' ' * (depth + 1)) + + if member_is_class_or_struct: + (prev_end_offset, padding) = verify_type_recursive (target, options, member_canonical_type, member_name, depth + 1, member_total_offset, padding) + else: + prev_end_offset = member_total_offset + member_byte_size + member_typename = member_type.GetName() + if member.IsBitfield(): + print '%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member.GetBitfieldSizeInBits(), member_name) + else: + print '%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member_name) + + if prev_end_offset < byte_size: + last_member_padding = byte_size - prev_end_offset + print '%+4u <%3u> %s' % (prev_end_offset, last_member_padding, ' ' * (depth + 1)) + padding += last_member_padding + else: + if type.IsPolymorphicClass(): + ptr_size = target.GetAddressByteSize() + print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1)) + prev_end_offset = ptr_size + prev_end_offset = base_offset + byte_size + + return (prev_end_offset, padding) + +def check_padding_command (debugger, command, result, dict): + # Use the Shell Lexer to properly parse up command options just like a + # shell would + command_args = shlex.split(command) + parser = create_types_options(True) + try: + (options, args) = parser.parse_args(command_args) + except: + # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + result.SetStatus (lldb.eReturnStatusFailed) + return "option parsing failed" # returning a string is the same as returning an error whose description is the string + verify_types(debugger.GetSelectedTarget(), options) + +@lldb.command("parse_all_struct_class_types") +def parse_all_struct_class_types (debugger, command, result, dict): + command_args = shlex.split(command) + for f in command_args: + error = lldb.SBError() + target = debugger.CreateTarget (f, None, None, False, error) + module = target.GetModuleAtIndex(0) + print "Parsing all types in '%s'" % (module) + types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) + for t in types: + print t + print "" + + +def verify_types (target, options): + + if not target: + print 'error: invalid target' + return + + modules = list() + if len(options.modules) == 0: + # Append just the main executable if nothing was specified + module = target.modules[0] + if module: + modules.append(module) + else: + for module_name in options.modules: + module = lldb.target.module[module_name] + if module: + modules.append(module) + + if modules: + for module in modules: + print 'module: %s' % (module.file) + if options.typenames: + for typename in options.typenames: + types = module.FindTypes(typename) + if types.GetSize(): + print 'Found %u types matching "%s" in "%s"' % (len(types), typename, module.file) + for type in types: + verify_type (target, options, type) + else: + print 'error: no type matches "%s" in "%s"' % (typename, module.file) + else: + types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) + print 'Found %u types in "%s"' % (len(types), module.file) + for type in types: + verify_type (target, options, type) + else: + print 'error: no modules' + +if __name__ == '__main__': + debugger = lldb.SBDebugger.Create() + parser = create_types_options(False) + + # try: + (options, args) = parser.parse_args(sys.argv[1:]) + # except: + # print "error: option parsing failed" + # sys.exit(1) + + if options.debug: + print "Waiting for debugger to attach to process %d" % os.getpid() + os.kill(os.getpid(), signal.SIGSTOP) + + for path in args: + # in a command - the lldb.* convenience variables are not to be used + # and their values (if any) are undefined + # this is the best practice to access those objects from within a command + error = lldb.SBError() + target = debugger.CreateTarget (path, + options.arch, + options.platform, + True, + error) + if error.Fail(): + print error.GetCString() + continue + verify_types (target, options) + +elif getattr(lldb, 'debugger', None): + lldb.debugger.HandleCommand('command script add -f types.check_padding_command check_padding') + print '"check_padding" command installed, use the "--help" option for detailed help' \ No newline at end of file diff --git a/examples/python/x86_64_linux_target_definition.py b/examples/python/x86_64_linux_target_definition.py new file mode 100644 index 000000000000..06cbe4c82963 --- /dev/null +++ b/examples/python/x86_64_linux_target_definition.py @@ -0,0 +1,353 @@ +#!/usr/bin/python +#===-- x86_64_linux_target_definition.py -----------------------------*- C++ -*-===// +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the GDB server +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the darwin version of +# GDB and allows you to connect to servers that use this register set. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_linux_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB server. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax' : 0 , + 'rdx' : 1 , + 'rcx' : 2 , + 'rbx' : 3 , + 'rsi' : 4 , + 'rdi' : 5 , + 'rbp' : 6 , + 'rsp' : 7 , + 'r8' : 8 , + 'r9' : 9 , + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'xmm0' : 17, + 'xmm1' : 18, + 'xmm2' : 19, + 'xmm3' : 20, + 'xmm4' : 21, + 'xmm5' : 22, + 'xmm6' : 23, + 'xmm7' : 24, + 'xmm8' : 25, + 'xmm9' : 26, + 'xmm10' : 27, + 'xmm11' : 28, + 'xmm12' : 29, + 'xmm13' : 30, + 'xmm14' : 31, + 'xmm15' : 32, + 'stmm0' : 33, + 'stmm1' : 34, + 'stmm2' : 35, + 'stmm3' : 36, + 'stmm4' : 37, + 'stmm5' : 38, + 'stmm6' : 39, + 'stmm7' : 30, + 'ymm0' : 41, + 'ymm1' : 42, + 'ymm2' : 43, + 'ymm3' : 44, + 'ymm4' : 45, + 'ymm5' : 46, + 'ymm6' : 47, + 'ymm7' : 48, + 'ymm8' : 49, + 'ymm9' : 40, + 'ymm10' : 41, + 'ymm11' : 42, + 'ymm12' : 43, + 'ymm13' : 44, + 'ymm14' : 45, + 'ymm15' : 46 +}; + +name_to_gdb_regnum = { + 'rax' : 0, + 'rbx' : 1, + 'rcx' : 2, + 'rdx' : 3, + 'rsi' : 4, + 'rdi' : 5, + 'rbp' : 6, + 'rsp' : 7, + 'r8' : 8, + 'r9' : 9, + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'rflags': 17, + 'cs' : 18, + 'ss' : 19, + 'ds' : 20, + 'es' : 21, + 'fs' : 22, + 'gs' : 23, + 'stmm0' : 24, + 'stmm1' : 25, + 'stmm2' : 26, + 'stmm3' : 27, + 'stmm4' : 28, + 'stmm5' : 29, + 'stmm6' : 30, + 'stmm7' : 31, + 'fctrl' : 32, + 'fstat' : 33, + 'ftag' : 34, + 'fiseg' : 35, + 'fioff' : 36, + 'foseg' : 37, + 'fooff' : 38, + 'fop' : 39, + 'xmm0' : 40, + 'xmm1' : 41, + 'xmm2' : 42, + 'xmm3' : 43, + 'xmm4' : 44, + 'xmm5' : 45, + 'xmm6' : 46, + 'xmm7' : 47, + 'xmm8' : 48, + 'xmm9' : 49, + 'xmm10' : 50, + 'xmm11' : 51, + 'xmm12' : 52, + 'xmm13' : 53, + 'xmm14' : 54, + 'xmm15' : 55, + 'mxcsr' : 56, + 'ymm0' : 57, + 'ymm1' : 58, + 'ymm2' : 59, + 'ymm3' : 60, + 'ymm4' : 61, + 'ymm5' : 62, + 'ymm6' : 63, + 'ymm7' : 64, + 'ymm8' : 65, + 'ymm9' : 66, + 'ymm10' : 67, + 'ymm11' : 68, + 'ymm12' : 69, + 'ymm13' : 70, + 'ymm14' : 71, + 'ymm15' : 72 +}; + +name_to_generic_regnum = { + 'rip' : LLDB_REGNUM_GENERIC_PC, + 'rsp' : LLDB_REGNUM_GENERIC_SP, + 'rbp' : LLDB_REGNUM_GENERIC_FP, + 'rdi' : LLDB_REGNUM_GENERIC_ARG1, + 'rsi' : LLDB_REGNUM_GENERIC_ARG2, + 'rdx' : LLDB_REGNUM_GENERIC_ARG3, + 'rcx' : LLDB_REGNUM_GENERIC_ARG4, + 'r8' : LLDB_REGNUM_GENERIC_ARG5, + 'r9' : LLDB_REGNUM_GENERIC_ARG6 +}; + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ +{ 'name':'rax' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rbx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rcx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg4' }, +{ 'name':'rdx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg3' }, +{ 'name':'rsi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg2' }, +{ 'name':'rdi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg1' }, +{ 'name':'rbp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'fp' }, +{ 'name':'rsp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'sp' }, +{ 'name':'r8' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg5' }, +{ 'name':'r9' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg6' }, +{ 'name':'r10' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r11' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r12' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r13' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r14' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r15' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rip' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'pc' }, +{ 'name':'rflags', 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'cs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ss' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ds' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'es' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'gs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'stmm0' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm1' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm2' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm3' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm4' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm5' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm6' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm7' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'fctrl' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fstat' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ftag' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fiseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fioff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'foseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fooff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fop' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'xmm0' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm1' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm2' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm3' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm4' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm5' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm6' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm7' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm8' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm9' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm10' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm11' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm12' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm13' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm14' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm15' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'mxcsr' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'orig_rax' , 'set':1, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatHex }, +# Registers that are contained in or composed of one of more other registers +{ 'name':'eax' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[31:0]' }, +{ 'name':'ebx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[31:0]' }, +{ 'name':'ecx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[31:0]' }, +{ 'name':'edx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[31:0]' }, +{ 'name':'edi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[31:0]' }, +{ 'name':'esi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[31:0]' }, +{ 'name':'ebp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[31:0]' }, +{ 'name':'esp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[31:0]' }, +{ 'name':'r8d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[31:0]' }, +{ 'name':'r9d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[31:0]' }, +{ 'name':'r10d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[31:0]' }, +{ 'name':'r11d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[31:0]' }, +{ 'name':'r12d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[31:0]' }, +{ 'name':'r13d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[31:0]' }, +{ 'name':'r14d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[31:0]' }, +{ 'name':'r15d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[31:0]' }, + +{ 'name':'ax' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:0]' }, +{ 'name':'bx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:0]' }, +{ 'name':'cx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:0]' }, +{ 'name':'dx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:0]' }, +{ 'name':'di' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[15:0]' }, +{ 'name':'si' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[15:0]' }, +{ 'name':'bp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[15:0]' }, +{ 'name':'sp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[15:0]' }, +{ 'name':'r8w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[15:0]' }, +{ 'name':'r9w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[15:0]' }, +{ 'name':'r10w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[15:0]' }, +{ 'name':'r11w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[15:0]' }, +{ 'name':'r12w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[15:0]' }, +{ 'name':'r13w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[15:0]' }, +{ 'name':'r14w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[15:0]' }, +{ 'name':'r15w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[15:0]' }, + +{ 'name':'ah' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:8]' }, +{ 'name':'bh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:8]' }, +{ 'name':'ch' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:8]' }, +{ 'name':'dh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:8]' }, + +{ 'name':'al' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[7:0]' }, +{ 'name':'bl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[7:0]' }, +{ 'name':'cl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[7:0]' }, +{ 'name':'dl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[7:0]' }, +{ 'name':'dil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[7:0]' }, +{ 'name':'sil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[7:0]' }, +{ 'name':'bpl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[7:0]' }, +{ 'name':'spl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[7:0]' }, +{ 'name':'r8l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[7:0]' }, +{ 'name':'r9l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[7:0]' }, +{ 'name':'r10l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[7:0]' }, +{ 'name':'r11l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[7:0]' }, +{ 'name':'r12l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[7:0]' }, +{ 'name':'r13l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[7:0]' }, +{ 'name':'r14l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[7:0]' }, +{ 'name':'r15l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[7:0]' }, +]; + +g_target_definition = None + +def get_target_definition (): + global g_target_definition + if g_target_definition == None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize']/8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = ['General Purpose Registers', 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition['host-info'] = { 'triple' : 'x86_64-*-linux', 'endian': eByteOrderLittle } + g_target_definition['g-packet-size'] = offset + g_target_definition['breakpoint-pc-offset'] = -1 + return g_target_definition + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition() \ No newline at end of file diff --git a/examples/python/x86_64_qemu_target_definition.py b/examples/python/x86_64_qemu_target_definition.py new file mode 100644 index 000000000000..7b246896d8bf --- /dev/null +++ b/examples/python/x86_64_qemu_target_definition.py @@ -0,0 +1,352 @@ +#!/usr/bin/python +#===-- x86_64_qemu_target_definition.py -----------------------------*- C++ -*-===// +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the remote stub +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the user mode qemu on linux. +# The only difference with the Linux file is the absense of orig_rax register. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_qemu_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB stub. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax' : 0 , + 'rdx' : 1 , + 'rcx' : 2 , + 'rbx' : 3 , + 'rsi' : 4 , + 'rdi' : 5 , + 'rbp' : 6 , + 'rsp' : 7 , + 'r8' : 8 , + 'r9' : 9 , + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'xmm0' : 17, + 'xmm1' : 18, + 'xmm2' : 19, + 'xmm3' : 20, + 'xmm4' : 21, + 'xmm5' : 22, + 'xmm6' : 23, + 'xmm7' : 24, + 'xmm8' : 25, + 'xmm9' : 26, + 'xmm10' : 27, + 'xmm11' : 28, + 'xmm12' : 29, + 'xmm13' : 30, + 'xmm14' : 31, + 'xmm15' : 32, + 'stmm0' : 33, + 'stmm1' : 34, + 'stmm2' : 35, + 'stmm3' : 36, + 'stmm4' : 37, + 'stmm5' : 38, + 'stmm6' : 39, + 'stmm7' : 30, + 'ymm0' : 41, + 'ymm1' : 42, + 'ymm2' : 43, + 'ymm3' : 44, + 'ymm4' : 45, + 'ymm5' : 46, + 'ymm6' : 47, + 'ymm7' : 48, + 'ymm8' : 49, + 'ymm9' : 40, + 'ymm10' : 41, + 'ymm11' : 42, + 'ymm12' : 43, + 'ymm13' : 44, + 'ymm14' : 45, + 'ymm15' : 46 +}; + +name_to_gdb_regnum = { + 'rax' : 0, + 'rbx' : 1, + 'rcx' : 2, + 'rdx' : 3, + 'rsi' : 4, + 'rdi' : 5, + 'rbp' : 6, + 'rsp' : 7, + 'r8' : 8, + 'r9' : 9, + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'rflags': 17, + 'cs' : 18, + 'ss' : 19, + 'ds' : 20, + 'es' : 21, + 'fs' : 22, + 'gs' : 23, + 'stmm0' : 24, + 'stmm1' : 25, + 'stmm2' : 26, + 'stmm3' : 27, + 'stmm4' : 28, + 'stmm5' : 29, + 'stmm6' : 30, + 'stmm7' : 31, + 'fctrl' : 32, + 'fstat' : 33, + 'ftag' : 34, + 'fiseg' : 35, + 'fioff' : 36, + 'foseg' : 37, + 'fooff' : 38, + 'fop' : 39, + 'xmm0' : 40, + 'xmm1' : 41, + 'xmm2' : 42, + 'xmm3' : 43, + 'xmm4' : 44, + 'xmm5' : 45, + 'xmm6' : 46, + 'xmm7' : 47, + 'xmm8' : 48, + 'xmm9' : 49, + 'xmm10' : 50, + 'xmm11' : 51, + 'xmm12' : 52, + 'xmm13' : 53, + 'xmm14' : 54, + 'xmm15' : 55, + 'mxcsr' : 56, + 'ymm0' : 57, + 'ymm1' : 58, + 'ymm2' : 59, + 'ymm3' : 60, + 'ymm4' : 61, + 'ymm5' : 62, + 'ymm6' : 63, + 'ymm7' : 64, + 'ymm8' : 65, + 'ymm9' : 66, + 'ymm10' : 67, + 'ymm11' : 68, + 'ymm12' : 69, + 'ymm13' : 70, + 'ymm14' : 71, + 'ymm15' : 72 +}; + +name_to_generic_regnum = { + 'rip' : LLDB_REGNUM_GENERIC_PC, + 'rsp' : LLDB_REGNUM_GENERIC_SP, + 'rbp' : LLDB_REGNUM_GENERIC_FP, + 'rdi' : LLDB_REGNUM_GENERIC_ARG1, + 'rsi' : LLDB_REGNUM_GENERIC_ARG2, + 'rdx' : LLDB_REGNUM_GENERIC_ARG3, + 'rcx' : LLDB_REGNUM_GENERIC_ARG4, + 'r8' : LLDB_REGNUM_GENERIC_ARG5, + 'r9' : LLDB_REGNUM_GENERIC_ARG6 +}; + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ +{ 'name':'rax' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rbx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rcx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg4' }, +{ 'name':'rdx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg3' }, +{ 'name':'rsi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg2' }, +{ 'name':'rdi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg1' }, +{ 'name':'rbp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'fp' }, +{ 'name':'rsp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'sp' }, +{ 'name':'r8' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg5' }, +{ 'name':'r9' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg6' }, +{ 'name':'r10' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r11' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r12' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r13' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r14' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r15' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rip' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'pc' }, +{ 'name':'rflags', 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'cs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ss' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ds' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'es' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'gs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'stmm0' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm1' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm2' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm3' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm4' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm5' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm6' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm7' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'fctrl' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fstat' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ftag' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fiseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fioff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'foseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fooff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fop' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'xmm0' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm1' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm2' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm3' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm4' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm5' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm6' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm7' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm8' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm9' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm10' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm11' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm12' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm13' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm14' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm15' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'mxcsr' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +# Registers that are contained in or composed of one of more other registers +{ 'name':'eax' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[31:0]' }, +{ 'name':'ebx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[31:0]' }, +{ 'name':'ecx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[31:0]' }, +{ 'name':'edx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[31:0]' }, +{ 'name':'edi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[31:0]' }, +{ 'name':'esi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[31:0]' }, +{ 'name':'ebp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[31:0]' }, +{ 'name':'esp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[31:0]' }, +{ 'name':'r8d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[31:0]' }, +{ 'name':'r9d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[31:0]' }, +{ 'name':'r10d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[31:0]' }, +{ 'name':'r11d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[31:0]' }, +{ 'name':'r12d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[31:0]' }, +{ 'name':'r13d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[31:0]' }, +{ 'name':'r14d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[31:0]' }, +{ 'name':'r15d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[31:0]' }, + +{ 'name':'ax' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:0]' }, +{ 'name':'bx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:0]' }, +{ 'name':'cx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:0]' }, +{ 'name':'dx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:0]' }, +{ 'name':'di' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[15:0]' }, +{ 'name':'si' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[15:0]' }, +{ 'name':'bp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[15:0]' }, +{ 'name':'sp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[15:0]' }, +{ 'name':'r8w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[15:0]' }, +{ 'name':'r9w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[15:0]' }, +{ 'name':'r10w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[15:0]' }, +{ 'name':'r11w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[15:0]' }, +{ 'name':'r12w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[15:0]' }, +{ 'name':'r13w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[15:0]' }, +{ 'name':'r14w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[15:0]' }, +{ 'name':'r15w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[15:0]' }, + +{ 'name':'ah' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:8]' }, +{ 'name':'bh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:8]' }, +{ 'name':'ch' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:8]' }, +{ 'name':'dh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:8]' }, + +{ 'name':'al' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[7:0]' }, +{ 'name':'bl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[7:0]' }, +{ 'name':'cl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[7:0]' }, +{ 'name':'dl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[7:0]' }, +{ 'name':'dil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[7:0]' }, +{ 'name':'sil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[7:0]' }, +{ 'name':'bpl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[7:0]' }, +{ 'name':'spl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[7:0]' }, +{ 'name':'r8l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[7:0]' }, +{ 'name':'r9l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[7:0]' }, +{ 'name':'r10l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[7:0]' }, +{ 'name':'r11l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[7:0]' }, +{ 'name':'r12l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[7:0]' }, +{ 'name':'r13l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[7:0]' }, +{ 'name':'r14l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[7:0]' }, +{ 'name':'r15l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[7:0]' }, +]; + +g_target_definition = None + +def get_target_definition (): + global g_target_definition + if g_target_definition == None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize']/8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = ['General Purpose Registers', 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition['host-info'] = { 'triple' : 'x86_64-*-linux', 'endian': eByteOrderLittle } + g_target_definition['g-packet-size'] = offset + g_target_definition['breakpoint-pc-offset'] = -1 + return g_target_definition + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition() diff --git a/examples/python/x86_64_target_definition.py b/examples/python/x86_64_target_definition.py new file mode 100644 index 000000000000..3a1290b62f80 --- /dev/null +++ b/examples/python/x86_64_target_definition.py @@ -0,0 +1,357 @@ +#!/usr/bin/python +#===-- x86_64_target_definition.py -----------------------------*- C++ -*-===// +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the GDB server +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the darwin version of +# GDB and allows you to connect to servers that use this register set. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB server. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax' : 0 , + 'rdx' : 1 , + 'rcx' : 2 , + 'rbx' : 3 , + 'rsi' : 4 , + 'rdi' : 5 , + 'rbp' : 6 , + 'rsp' : 7 , + 'r8' : 8 , + 'r9' : 9 , + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'xmm0' : 17, + 'xmm1' : 18, + 'xmm2' : 19, + 'xmm3' : 20, + 'xmm4' : 21, + 'xmm5' : 22, + 'xmm6' : 23, + 'xmm7' : 24, + 'xmm8' : 25, + 'xmm9' : 26, + 'xmm10' : 27, + 'xmm11' : 28, + 'xmm12' : 29, + 'xmm13' : 30, + 'xmm14' : 31, + 'xmm15' : 32, + 'stmm0' : 33, + 'stmm1' : 34, + 'stmm2' : 35, + 'stmm3' : 36, + 'stmm4' : 37, + 'stmm5' : 38, + 'stmm6' : 39, + 'stmm7' : 30, + 'ymm0' : 41, + 'ymm1' : 42, + 'ymm2' : 43, + 'ymm3' : 44, + 'ymm4' : 45, + 'ymm5' : 46, + 'ymm6' : 47, + 'ymm7' : 48, + 'ymm8' : 49, + 'ymm9' : 40, + 'ymm10' : 41, + 'ymm11' : 42, + 'ymm12' : 43, + 'ymm13' : 44, + 'ymm14' : 45, + 'ymm15' : 46 +}; + +name_to_gdb_regnum = { + 'rax' : 0, + 'rbx' : 1, + 'rcx' : 2, + 'rdx' : 3, + 'rsi' : 4, + 'rdi' : 5, + 'rbp' : 6, + 'rsp' : 7, + 'r8' : 8, + 'r9' : 9, + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'rflags': 17, + 'cs' : 18, + 'ss' : 19, + 'ds' : 20, + 'es' : 21, + 'fs' : 22, + 'gs' : 23, + 'stmm0' : 24, + 'stmm1' : 25, + 'stmm2' : 26, + 'stmm3' : 27, + 'stmm4' : 28, + 'stmm5' : 29, + 'stmm6' : 30, + 'stmm7' : 31, + 'fctrl' : 32, + 'fstat' : 33, + 'ftag' : 34, + 'fiseg' : 35, + 'fioff' : 36, + 'foseg' : 37, + 'fooff' : 38, + 'fop' : 39, + 'xmm0' : 40, + 'xmm1' : 41, + 'xmm2' : 42, + 'xmm3' : 43, + 'xmm4' : 44, + 'xmm5' : 45, + 'xmm6' : 46, + 'xmm7' : 47, + 'xmm8' : 48, + 'xmm9' : 49, + 'xmm10' : 50, + 'xmm11' : 51, + 'xmm12' : 52, + 'xmm13' : 53, + 'xmm14' : 54, + 'xmm15' : 55, + 'mxcsr' : 56, + 'ymm0' : 57, + 'ymm1' : 58, + 'ymm2' : 59, + 'ymm3' : 60, + 'ymm4' : 61, + 'ymm5' : 62, + 'ymm6' : 63, + 'ymm7' : 64, + 'ymm8' : 65, + 'ymm9' : 66, + 'ymm10' : 67, + 'ymm11' : 68, + 'ymm12' : 69, + 'ymm13' : 70, + 'ymm14' : 71, + 'ymm15' : 72 +}; + +name_to_generic_regnum = { + 'rip' : LLDB_REGNUM_GENERIC_PC, + 'rsp' : LLDB_REGNUM_GENERIC_SP, + 'rbp' : LLDB_REGNUM_GENERIC_FP, + 'rdi' : LLDB_REGNUM_GENERIC_ARG1, + 'rsi' : LLDB_REGNUM_GENERIC_ARG2, + 'rdx' : LLDB_REGNUM_GENERIC_ARG3, + 'rcx' : LLDB_REGNUM_GENERIC_ARG4, + 'r8' : LLDB_REGNUM_GENERIC_ARG5, + 'r9' : LLDB_REGNUM_GENERIC_ARG6 +}; + + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ +{ 'name':'rax' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rbx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rcx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg4' }, +{ 'name':'rdx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg3' }, +{ 'name':'rsi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg2' }, +{ 'name':'rdi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg1' }, +{ 'name':'rbp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'fp' }, +{ 'name':'rsp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'sp' }, +{ 'name':'r8' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg5' }, +{ 'name':'r9' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg6' }, +{ 'name':'r10' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r11' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r12' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r13' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r14' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r15' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rip' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'pc' }, +{ 'name':'rflags', 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'cs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ss' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ds' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'es' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'gs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'stmm0' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm1' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm2' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm3' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm4' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm5' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm6' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm7' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'fctrl' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fstat' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ftag' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fiseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fioff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'foseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fooff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fop' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'xmm0' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm1' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm2' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm3' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm4' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm5' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm6' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm7' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm8' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm9' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm10' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm11' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm12' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm13' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm14' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm15' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'mxcsr' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +# Registers that are contained in or composed of one of more other registers +{ 'name':'eax' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[31:0]' }, +{ 'name':'ebx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[31:0]' }, +{ 'name':'ecx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[31:0]' }, +{ 'name':'edx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[31:0]' }, +{ 'name':'edi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[31:0]' }, +{ 'name':'esi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[31:0]' }, +{ 'name':'ebp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[31:0]' }, +{ 'name':'esp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[31:0]' }, +{ 'name':'r8d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[31:0]' }, +{ 'name':'r9d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[31:0]' }, +{ 'name':'r10d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[31:0]' }, +{ 'name':'r11d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[31:0]' }, +{ 'name':'r12d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[31:0]' }, +{ 'name':'r13d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[31:0]' }, +{ 'name':'r14d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[31:0]' }, +{ 'name':'r15d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[31:0]' }, + +{ 'name':'ax' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:0]' }, +{ 'name':'bx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:0]' }, +{ 'name':'cx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:0]' }, +{ 'name':'dx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:0]' }, +{ 'name':'di' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[15:0]' }, +{ 'name':'si' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[15:0]' }, +{ 'name':'bp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[15:0]' }, +{ 'name':'sp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[15:0]' }, +{ 'name':'r8w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[15:0]' }, +{ 'name':'r9w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[15:0]' }, +{ 'name':'r10w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[15:0]' }, +{ 'name':'r11w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[15:0]' }, +{ 'name':'r12w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[15:0]' }, +{ 'name':'r13w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[15:0]' }, +{ 'name':'r14w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[15:0]' }, +{ 'name':'r15w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[15:0]' }, + +{ 'name':'ah' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:8]' }, +{ 'name':'bh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:8]' }, +{ 'name':'ch' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:8]' }, +{ 'name':'dh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:8]' }, + +{ 'name':'al' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[7:0]' }, +{ 'name':'bl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[7:0]' }, +{ 'name':'cl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[7:0]' }, +{ 'name':'dl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[7:0]' }, +{ 'name':'dil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[7:0]' }, +{ 'name':'sil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[7:0]' }, +{ 'name':'bpl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[7:0]' }, +{ 'name':'spl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[7:0]' }, +{ 'name':'r8l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[7:0]' }, +{ 'name':'r9l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[7:0]' }, +{ 'name':'r10l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[7:0]' }, +{ 'name':'r11l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[7:0]' }, +{ 'name':'r12l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[7:0]' }, +{ 'name':'r13l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[7:0]' }, +{ 'name':'r14l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[7:0]' }, +{ 'name':'r15l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[7:0]' }, +]; + +g_target_definition = None + +def get_target_definition (): + global g_target_definition + if g_target_definition == None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize']/8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = ['General Purpose Registers', 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition['host-info'] = { 'triple' : 'x86_64-apple-macosx', 'endian': eByteOrderLittle } + g_target_definition['g-packet-size'] = offset + return g_target_definition + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition() \ No newline at end of file diff --git a/examples/scripting/dictionary.c b/examples/scripting/dictionary.c new file mode 100644 index 000000000000..a7e1390ccebd --- /dev/null +++ b/examples/scripting/dictionary.c @@ -0,0 +1,200 @@ +//===-- dictionary.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 + +typedef struct tree_node +{ + const char *word; + struct tree_node *left; + struct tree_node *right; +} tree_node; + +/* Given a char*, returns a substring that starts at the first + alphabet character and ends at the last alphabet character, i.e. it + strips off beginning or ending quotes, punctuation, etc. */ + +char * +strip (char **word) +{ + char *start = *word; + int len = strlen (start); + char *end = start + len - 1; + + while ((start < end) && (!isalpha (start[0]))) + start++; + + while ((end > start) && (!isalpha (end[0]))) + end--; + + if (start > end) + return NULL; + + end[1] = '\0'; + *word = start; + + return start; +} + +/* Given a binary search tree (sorted alphabetically by the word at + each node), and a new word, inserts the word at the appropriate + place in the tree. */ + +void +insert (tree_node *root, char *word) +{ + if (root == NULL) + return; + + int compare_value = strcmp (word, root->word); + + if (compare_value == 0) + return; + + if (compare_value < 0) + { + if (root->left != NULL) + insert (root->left, word); + else + { + tree_node *new_node = (tree_node *) malloc (sizeof (tree_node)); + new_node->word = strdup (word); + new_node->left = NULL; + new_node->right = NULL; + root->left = new_node; + } + } + else + { + if (root->right != NULL) + insert (root->right, word); + else + { + tree_node *new_node = (tree_node *) malloc (sizeof (tree_node)); + new_node->word = strdup (word); + new_node->left = NULL; + new_node->right = NULL; + root->right = new_node; + } + } +} + +/* Read in a text file and storea all the words from the file in a + binary search tree. */ + +void +populate_dictionary (tree_node **dictionary, char *filename) +{ + FILE *in_file; + char word[1024]; + + in_file = fopen (filename, "r"); + if (in_file) + { + while (fscanf (in_file, "%s", word) == 1) + { + char *new_word = (strdup (word)); + new_word = strip (&new_word); + if (*dictionary == NULL) + { + tree_node *new_node = (tree_node *) malloc (sizeof (tree_node)); + new_node->word = new_word; + new_node->left = NULL; + new_node->right = NULL; + *dictionary = new_node; + } + else + insert (*dictionary, new_word); + } + } +} + +/* Given a binary search tree and a word, search for the word + in the binary search tree. */ + +int +find_word (tree_node *dictionary, char *word) +{ + if (!word || !dictionary) + return 0; + + int compare_value = strcmp (word, dictionary->word); + + if (compare_value == 0) + return 1; + else if (compare_value < 0) + return find_word (dictionary->left, word); + else + return find_word (dictionary->right, word); +} + +/* Print out the words in the binary search tree, in sorted order. */ + +void +print_tree (tree_node *dictionary) +{ + if (!dictionary) + return; + + if (dictionary->left) + print_tree (dictionary->left); + + printf ("%s\n", dictionary->word); + + + if (dictionary->right) + print_tree (dictionary->right); +} + + +int +main (int argc, char **argv) +{ + tree_node *dictionary = NULL; + char buffer[1024]; + char *filename; + int done = 0; + + if (argc == 2) + filename = argv[1]; + + if (!filename) + return -1; + + populate_dictionary (&dictionary, filename); + fprintf (stdout, "Dictionary loaded.\nEnter search word: "); + while (!done && fgets (buffer, sizeof(buffer), stdin)) + { + char *word = buffer; + int len = strlen (word); + int i; + + for (i = 0; i < len; ++i) + word[i] = tolower (word[i]); + + if ((len > 0) && (word[len-1] == '\n')) + { + word[len-1] = '\0'; + len = len - 1; + } + + if (find_word (dictionary, word)) + fprintf (stdout, "Yes!\n"); + else + fprintf (stdout, "No!\n"); + + fprintf (stdout, "Enter search word: "); + } + + fprintf (stdout, "\n"); + return 0; +} + diff --git a/examples/scripting/tree_utils.py b/examples/scripting/tree_utils.py new file mode 100755 index 000000000000..e83f516ab580 --- /dev/null +++ b/examples/scripting/tree_utils.py @@ -0,0 +1,118 @@ +""" +# ===-- tree_utils.py ---------------------------------------*- Python -*-===// +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +# ===---------------------------------------------------------------------===// + +tree_utils.py - A set of functions for examining binary +search trees, based on the example search tree defined in +dictionary.c. These functions contain calls to LLDB API +functions, and assume that the LLDB Python module has been +imported. + +For a thorough explanation of how the DFS function works, and +for more information about dictionary.c go to +http://lldb.llvm.org/scripting.html +""" + + +def DFS (root, word, cur_path): + """ + Recursively traverse a binary search tree containing + words sorted alphabetically, searching for a particular + word in the tree. Also maintains a string representing + the path from the root of the tree to the current node. + If the word is found in the tree, return the path string. + Otherwise return an empty string. + + This function assumes the binary search tree is + the one defined in dictionary.c It uses LLDB API + functions to examine and traverse the tree nodes. + """ + + # Get pointer field values out of node 'root' + + root_word_ptr = root.GetChildMemberWithName ("word") + left_child_ptr = root.GetChildMemberWithName ("left") + right_child_ptr = root.GetChildMemberWithName ("right") + + # Get the word out of the word pointer and strip off + # surrounding quotes (added by call to GetSummary). + + root_word = root_word_ptr.GetSummary() + end = len (root_word) - 1 + if root_word[0] == '"' and root_word[end] == '"': + root_word = root_word[1:end] + end = len (root_word) - 1 + if root_word[0] == '\'' and root_word[end] == '\'': + root_word = root_word[1:end] + + # Main depth first search + + if root_word == word: + return cur_path + elif word < root_word: + + # Check to see if left child is NULL + + if left_child_ptr.GetValue() == None: + return "" + else: + cur_path = cur_path + "L" + return DFS (left_child_ptr, word, cur_path) + else: + + # Check to see if right child is NULL + + if right_child_ptr.GetValue() == None: + return "" + else: + cur_path = cur_path + "R" + return DFS (right_child_ptr, word, cur_path) + + +def tree_size (root): + """ + Recursively traverse a binary search tree, counting + the nodes in the tree. Returns the final count. + + This function assumes the binary search tree is + the one defined in dictionary.c It uses LLDB API + functions to examine and traverse the tree nodes. + """ + if (root.GetValue == None): + return 0 + + if (int (root.GetValue(), 16) == 0): + return 0 + + left_size = tree_size (root.GetChildAtIndex(1)); + right_size = tree_size (root.GetChildAtIndex(2)); + + total_size = left_size + right_size + 1 + return total_size + + +def print_tree (root): + """ + Recursively traverse a binary search tree, printing out + the words at the nodes in alphabetical order (the + search order for the binary tree). + + This function assumes the binary search tree is + the one defined in dictionary.c It uses LLDB API + functions to examine and traverse the tree nodes. + """ + if (root.GetChildAtIndex(1).GetValue() != None) and (int (root.GetChildAtIndex(1).GetValue(), 16) != 0): + print_tree (root.GetChildAtIndex(1)) + + print root.GetChildAtIndex(0).GetSummary() + + if (root.GetChildAtIndex(2).GetValue() != None) and (int (root.GetChildAtIndex(2).GetValue(), 16) != 0): + print_tree (root.GetChildAtIndex(2)) + + diff --git a/examples/summaries/cocoa/CFArray.py b/examples/summaries/cocoa/CFArray.py new file mode 100644 index 000000000000..5068875b5b38 --- /dev/null +++ b/examples/summaries/cocoa/CFArray.py @@ -0,0 +1,204 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NSArray +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# much less functional than the other two cases below +# just runs code to get to the count and then returns +# no children +class NSArrayKVC_SynthProvider: + + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, dict, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]"); + if num_children_vo.IsValid(): + return num_children_vo.GetValueAsUnsigned(0) + return "" + +# much less functional than the other two cases below +# just runs code to get to the count and then returns +# no children +class NSArrayCF_SynthProvider: + + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, dict, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not (self.sys_params.types_cache.ulong): + self.sys_params.types_cache.ulong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + num_children_vo = self.valobj.CreateChildAtOffset("count", + self.sys_params.cfruntime_size, + self.sys_params.types_cache.ulong) + return num_children_vo.GetValueAsUnsigned(0) + +class NSArrayI_SynthProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, dict, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.long): + self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong) + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # skip the isa pointer and get at the size + def num_children(self): + logger = lldb.formatters.Logger.Logger() + count = self.valobj.CreateChildAtOffset("count", + self.sys_params.pointer_size, + self.sys_params.types_cache.long); + return count.GetValueAsUnsigned(0) + +class NSArrayM_SynthProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, dict, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.long): + self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong) + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # skip the isa pointer and get at the size + def num_children(self): + logger = lldb.formatters.Logger.Logger() + count = self.valobj.CreateChildAtOffset("count", + self.sys_params.pointer_size, + self.sys_params.types_cache.long); + return count.GetValueAsUnsigned(0) + +# this is the actual synth provider, but is just a wrapper that checks +# whether valobj is an instance of __NSArrayI or __NSArrayM and sets up an +# appropriate backend layer to do the computations +class NSArray_SynthProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, dict): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.adjust_for_architecture() + self.error = False + self.wrapper = self.make_wrapper() + self.invalid = (self.wrapper == None) + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + if self.wrapper == None: + return 0; + return self.wrapper.num_children() + + def update(self): + logger = lldb.formatters.Logger.Logger() + if self.wrapper == None: + return + self.wrapper.update() + + # this code acts as our defense against NULL and uninitialized + # NSArray pointers, which makes it much longer than it would be otherwise + def make_wrapper(self): + logger = lldb.formatters.Logger.Logger() + if self.valobj.GetValueAsUnsigned() == 0: + self.error = True + return lldb.runtime.objc.objc_runtime.InvalidPointer_Description(True) + else: + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(self.valobj,statistics) + if wrapper: + self.error = True + return wrapper + + name_string = class_data.class_name() + + logger >> "Class name is " + str(name_string) + + if name_string == '__NSArrayI': + wrapper = NSArrayI_SynthProvider(self.valobj, dict, class_data.sys_params) + statistics.metric_hit('code_notrun',self.valobj.GetName()) + elif name_string == '__NSArrayM': + wrapper = NSArrayM_SynthProvider(self.valobj, dict, class_data.sys_params) + statistics.metric_hit('code_notrun',self.valobj.GetName()) + elif name_string == '__NSCFArray': + wrapper = NSArrayCF_SynthProvider(self.valobj, dict, class_data.sys_params) + statistics.metric_hit('code_notrun',self.valobj.GetName()) + else: + wrapper = NSArrayKVC_SynthProvider(self.valobj, dict, class_data.sys_params) + statistics.metric_hit('unknown_class',str(self.valobj.GetName()) + " seen as " + name_string) + return wrapper; + +def CFArray_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = NSArray_SynthProvider(valobj,dict); + if provider.invalid == False: + if provider.error == True: + return provider.wrapper.message() + try: + summary = int(provider.num_children()); + except: + summary = None + logger >> "provider gave me " + str(summary) + if summary == None: + summary = '' + elif isinstance(summary,basestring): + pass + else: + # we format it like it were a CFString to make it look the same as the summary from Xcode + summary = '@"' + str(summary) + (" objects" if summary != 1 else " object") + '"' + return summary + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F CFArray.CFArray_SummaryProvider NSArray CFArrayRef CFMutableArrayRef") diff --git a/examples/summaries/cocoa/CFBag.py b/examples/summaries/cocoa/CFBag.py new file mode 100644 index 000000000000..37d14a432d52 --- /dev/null +++ b/examples/summaries/cocoa/CFBag.py @@ -0,0 +1,146 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for CFBag +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the length for an CFBag, so they need not +# obey the interface specification for synthetic children providers +class CFBagRef_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # 12 bytes on i386 + # 20 bytes on x64 + # most probably 2 pointers and 4 bytes of data + def offset(self): + logger = lldb.formatters.Logger.Logger() + if self.sys_params.is_64_bit: + return 20 + else: + return 12 + + def length(self): + logger = lldb.formatters.Logger.Logger() + size = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + return size.GetValueAsUnsigned(0) + + +class CFBagUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def length(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + num_children_vo = self.valobj.CreateValueFromExpression("count","(int)CFBagGetCount(" + stream.GetData() + " )") + if num_children_vo.IsValid(): + return num_children_vo.GetValueAsUnsigned(0) + return "" + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + actual_name = name_string + + logger >> "name string got was " + str(name_string) + " but actual name is " + str(actual_name) + + if class_data.is_cftype(): + # CFBag does not expose an actual NSWrapper type, so we have to check that this is + # an NSCFType and then check we are a pointer-to __CFBag + valobj_type = valobj.GetType() + if valobj_type.IsValid() and valobj_type.IsPointerType(): + valobj_type = valobj_type.GetPointeeType() + if valobj_type.IsValid(): + actual_name = valobj_type.GetName() + if actual_name == '__CFBag' or \ + actual_name == 'const struct __CFBag': + wrapper = CFBagRef_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + return wrapper + wrapper = CFBagUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + actual_name) + return wrapper; + +def CFBag_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.length(); + except: + summary = None + logger >> "summary got from provider: " + str(summary) + # for some reason, one needs to clear some bits for the count + # to be correct when using CF(Mutable)BagRef on x64 + # the bit mask was derived through experimentation + # (if counts start looking weird, then most probably + # the mask needs to be changed) + if summary == None: + summary = '' + elif isinstance(summary,basestring): + pass + else: + if provider.sys_params.is_64_bit: + summary = summary & ~0x1fff000000000000 + if summary == 1: + summary = '@"1 value"' + else: + summary = '@"' + str(summary) + ' values"' + return summary + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F CFBag.CFBag_SummaryProvider CFBagRef CFMutableBagRef") diff --git a/examples/summaries/cocoa/CFBinaryHeap.py b/examples/summaries/cocoa/CFBinaryHeap.py new file mode 100644 index 000000000000..2348a8971815 --- /dev/null +++ b/examples/summaries/cocoa/CFBinaryHeap.py @@ -0,0 +1,142 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for CFBinaryHeap +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the length for an CFBinaryHeap, so they need not +# obey the interface specification for synthetic children providers +class CFBinaryHeapRef_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # 8 bytes on i386 + # 16 bytes on x64 + # most probably 2 pointers + def offset(self): + logger = lldb.formatters.Logger.Logger() + return 2 * self.sys_params.pointer_size + + def length(self): + logger = lldb.formatters.Logger.Logger() + size = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + return size.GetValueAsUnsigned(0) + + +class CFBinaryHeapUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def length(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + num_children_vo = self.valobj.CreateValueFromExpression("count","(int)CFBinaryHeapGetCount(" + stream.GetData() + " )"); + if num_children_vo.IsValid(): + return num_children_vo.GetValueAsUnsigned(0) + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + actual_name = class_data.class_name() + + logger >> "name string got was " + str(name_string) + " but actual name is " + str(actual_name) + + if class_data.is_cftype(): + # CFBinaryHeap does not expose an actual NSWrapper type, so we have to check that this is + # an NSCFType and then check we are a pointer-to CFBinaryHeap + valobj_type = valobj.GetType() + if valobj_type.IsValid() and valobj_type.IsPointerType(): + valobj_type = valobj_type.GetPointeeType() + if valobj_type.IsValid(): + actual_name = valobj_type.GetName() + if actual_name == '__CFBinaryHeap': + wrapper = CFBinaryHeapRef_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + return wrapper + wrapper = CFBinaryHeapUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def CFBinaryHeap_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.length(); + except: + summary = None + logger >> "summary got from provider: " + str(summary) + # for some reason, one needs to clear some bits for the count + # to be correct when using CF(Mutable)BagRef on x64 + # the bit mask was derived through experimentation + # (if counts start looking weird, then most probably + # the mask needs to be changed) + if summary == None: + summary = '' + elif isinstance(summary,basestring): + pass + else: + if provider.sys_params.is_64_bit: + summary = summary & ~0x1fff000000000000 + if summary == 1: + return '@"1 item"' + else: + summary = '@"' + str(summary) + ' items"' + return summary + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F CFBinaryHeap.CFBinaryHeap_SummaryProvider CFBinaryHeapRef") diff --git a/examples/summaries/cocoa/CFBitVector.py b/examples/summaries/cocoa/CFBitVector.py new file mode 100644 index 000000000000..b0c9e7912106 --- /dev/null +++ b/examples/summaries/cocoa/CFBitVector.py @@ -0,0 +1,175 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# summary provider for CF(Mutable)BitVector +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import lldb.formatters.Logger + +# first define some utility functions +def byte_index(abs_pos): + logger = lldb.formatters.Logger.Logger() + return abs_pos/8 + +def bit_index(abs_pos): + logger = lldb.formatters.Logger.Logger() + return abs_pos & 7 + +def get_bit(byte,index): + logger = lldb.formatters.Logger.Logger() + if index < 0 or index > 7: + return None + return (byte >> (7-index)) & 1 + +def grab_array_item_data(pointer,index): + logger = lldb.formatters.Logger.Logger() + return pointer.GetPointeeData(index,1) + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but a summary for a CF*BitVector, so they need not +# obey the interface specification for synthetic children providers +class CFBitVectorKnown_SummaryProvider: + def adjust_for_architecture(self): + logger = lldb.formatters.Logger.Logger() + self.uiint_size = self.sys_params.types_cache.NSUInteger.GetByteSize() + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + if not(self.sys_params.types_cache.charptr): + self.sys_params.types_cache.charptr = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType() + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # we skip the CFRuntimeBase + # then the next CFIndex is the count + # then we skip another CFIndex and then we get at a byte array + # that wraps the individual bits + + def contents(self): + logger = lldb.formatters.Logger.Logger() + count_vo = self.valobj.CreateChildAtOffset("count",self.sys_params.cfruntime_size, + self.sys_params.types_cache.NSUInteger) + count = count_vo.GetValueAsUnsigned(0) + if count == 0: + return '(empty)' + + array_vo = self.valobj.CreateChildAtOffset("data", + self.sys_params.cfruntime_size+2*self.uiint_size, + self.sys_params.types_cache.charptr) + + data_list = [] + cur_byte_pos = None + for i in range(0,count): + if cur_byte_pos == None: + cur_byte_pos = byte_index(i) + cur_byte = grab_array_item_data(array_vo,cur_byte_pos) + cur_byte_val = cur_byte.uint8[0] + else: + byte_pos = byte_index(i) + # do not fetch the pointee data every single time through + if byte_pos != cur_byte_pos: + cur_byte_pos = byte_pos + cur_byte = grab_array_item_data(array_vo,cur_byte_pos) + cur_byte_val = cur_byte.uint8[0] + bit = get_bit(cur_byte_val,bit_index(i)) + if (i % 4) == 0: + data_list.append(' ') + if bit == 1: + data_list.append('1') + else: + data_list.append('0') + return ''.join(data_list) + + +class CFBitVectorUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def contents(self): + logger = lldb.formatters.Logger.Logger() + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + actual_name = name_string + + logger >> "name string got was " + str(name_string) + " but actual name is " + str(actual_name) + + if class_data.is_cftype(): + # CFBitVectorRef does not expose an actual NSWrapper type, so we have to check that this is + # an NSCFType and then check we are a pointer-to CFBitVectorRef + valobj_type = valobj.GetType() + if valobj_type.IsValid() and valobj_type.IsPointerType(): + valobj_type = valobj_type.GetPointeeType() + if valobj_type.IsValid(): + actual_name = valobj_type.GetName() + if actual_name == '__CFBitVector' or actual_name == '__CFMutableBitVector': + wrapper = CFBitVectorKnown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = CFBitVectorUnknown_SummaryProvider(valobj, class_data.sys_params) + print actual_name + else: + wrapper = CFBitVectorUnknown_SummaryProvider(valobj, class_data.sys_params) + print name_string + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def CFBitVector_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.contents(); + except: + summary = None + logger >> "summary got from provider: " + str(summary) + if summary == None or summary == '': + summary = '' + return summary + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F CFBitVector.CFBitVector_SummaryProvider CFBitVectorRef CFMutableBitVectorRef") diff --git a/examples/summaries/cocoa/CFDictionary.py b/examples/summaries/cocoa/CFDictionary.py new file mode 100644 index 000000000000..061f5c56f9d6 --- /dev/null +++ b/examples/summaries/cocoa/CFDictionary.py @@ -0,0 +1,234 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NSDictionary +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the count for an NSDictionary, so they need not +# obey the interface specification for synthetic children providers +class NSCFDictionary_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # empirically determined on both 32 and 64bit desktop Mac OS X + # probably boils down to 2 pointers and 4 bytes of data, but + # the description of __CFDictionary is not readily available so most + # of this is guesswork, plain and simple + def offset(self): + logger = lldb.formatters.Logger.Logger() + if self.sys_params.is_64_bit: + return 20 + else: + return 12 + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + num_children_vo = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + return num_children_vo.GetValueAsUnsigned(0) + + +class NSDictionaryI_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # we just need to skip the ISA and the count immediately follows + def offset(self): + logger = lldb.formatters.Logger.Logger() + return self.sys_params.pointer_size + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + num_children_vo = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + value = num_children_vo.GetValueAsUnsigned(0) + if value != None: + # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity + # not sure if it is a bug or some weird sort of feature, but masking that out + # gets the count right + if self.sys_params.is_64_bit: + value = value & ~0xFC00000000000000 + else: + value = value & ~0xFC000000 + return value + +class NSDictionaryM_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # we just need to skip the ISA and the count immediately follows + def offset(self): + return self.sys_params.pointer_size + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + num_children_vo = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + value = num_children_vo.GetValueAsUnsigned(0) + if value != None: + # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity + # not sure if it is a bug or some weird sort of feature, but masking that out + # gets the count right + if self.sys_params.is_64_bit: + value = value & ~0xFC00000000000000 + else: + value = value & ~0xFC000000 + return value + +class NSDictionaryUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]"); + if num_children_vo.IsValid(): + return num_children_vo.GetValueAsUnsigned(0) + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + + logger >> "class name is: " + str(name_string) + + if name_string == '__NSCFDictionary': + wrapper = NSCFDictionary_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + elif name_string == '__NSDictionaryI': + wrapper = NSDictionaryI_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + elif name_string == '__NSDictionaryM': + wrapper = NSDictionaryM_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSDictionaryUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def CFDictionary_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.num_children(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None: + return '' + if isinstance(summary,basestring): + return summary + return str(summary) + (" key/value pairs" if summary != 1 else " key/value pair") + return 'Summary Unavailable' + +def CFDictionary_SummaryProvider2 (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.num_children(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None: + summary = '' + if isinstance(summary,basestring): + return summary + else: + # needed on OSX Mountain Lion + if provider.sys_params.is_64_bit: + summary = summary & ~0x0f1f000000000000 + summary = '@"' + str(summary) + (' entries"' if summary != 1 else ' entry"') + return summary + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary") + debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef") diff --git a/examples/summaries/cocoa/CFString.py b/examples/summaries/cocoa/CFString.py new file mode 100644 index 000000000000..570fd8280e0c --- /dev/null +++ b/examples/summaries/cocoa/CFString.py @@ -0,0 +1,325 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example synthetic children and summary provider for CFString (and related NSString class) +# the real code is part of the LLDB core +import lldb +import lldb.runtime.objc.objc_runtime +import lldb.formatters.Logger + +def CFString_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = CFStringSynthProvider(valobj,dict); + if provider.invalid == False: + try: + summary = provider.get_child_at_index(provider.get_child_index("content")) + if type(summary) == lldb.SBValue: + summary = summary.GetSummary() + else: + summary = '"' + summary + '"' + except: + summary = None + if summary == None: + summary = '' + return '@'+summary + return '' + +def CFAttributedString_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + offset = valobj.GetTarget().GetProcess().GetAddressByteSize() + pointee = valobj.GetValueAsUnsigned(0) + summary = '' + if pointee != None and pointee != 0: + pointee = pointee + offset + child_ptr = valobj.CreateValueFromAddress("string_ptr",pointee,valobj.GetType()) + child = child_ptr.CreateValueFromAddress("string_data",child_ptr.GetValueAsUnsigned(),valobj.GetType()).AddressOf() + provider = CFStringSynthProvider(child,dict); + if provider.invalid == False: + try: + summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary(); + except: + summary = '' + if summary == None: + summary = '' + return '@'+summary + + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef") + debugger.HandleCommand("type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString") + +class CFStringSynthProvider: + def __init__(self,valobj,dict): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.update() + + # children other than "content" are for debugging only and must not be used in production code + def num_children(self): + logger = lldb.formatters.Logger.Logger() + if self.invalid: + return 0; + return 6; + + def read_unicode(self, pointer,max_len=2048): + logger = lldb.formatters.Logger.Logger() + process = self.valobj.GetTarget().GetProcess() + error = lldb.SBError() + pystr = u'' + # cannot do the read at once because the length value has + # a weird encoding. better play it safe here + while max_len > 0: + content = process.ReadMemory(pointer, 2, error) + new_bytes = bytearray(content) + b0 = new_bytes[0] + b1 = new_bytes[1] + pointer = pointer + 2 + if b0 == 0 and b1 == 0: + break + # rearrange bytes depending on endianness + # (do we really need this or is Cocoa going to + # use Windows-compatible little-endian even + # if the target is big endian?) + if self.is_little: + value = b1 * 256 + b0 + else: + value = b0 * 256 + b1 + pystr = pystr + unichr(value) + # read max_len unicode values, not max_len bytes + max_len = max_len - 1 + return pystr + + # handle the special case strings + # only use the custom code for the tested LP64 case + def handle_special(self): + logger = lldb.formatters.Logger.Logger() + if self.is_64_bit == False: + # for 32bit targets, use safe ObjC code + return self.handle_unicode_string_safe() + offset = 12 + pointer = self.valobj.GetValueAsUnsigned(0) + offset + pystr = self.read_unicode(pointer) + return self.valobj.CreateValueFromExpression("content", + "(char*)\"" + pystr.encode('utf-8') + "\"") + + # last resort call, use ObjC code to read; the final aim is to + # be able to strip this call away entirely and only do the read + # ourselves + def handle_unicode_string_safe(self): + return self.valobj.CreateValueFromExpression("content", + "(char*)\"" + self.valobj.GetObjectDescription() + "\""); + + def handle_unicode_string(self): + logger = lldb.formatters.Logger.Logger() + # step 1: find offset + if self.inline: + pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base(); + if self.explicit == False: + # untested, use the safe code path + return self.handle_unicode_string_safe(); + else: + # a full pointer is skipped here before getting to the live data + pointer = pointer + self.pointer_size + else: + pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base() + # read 8 bytes here and make an address out of them + try: + char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType() + vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type); + pointer = vopointer.GetValueAsUnsigned(0) + except: + return self.valobj.CreateValueFromExpression("content", + '(char*)"@\"invalid NSString\""') + # step 2: read Unicode data at pointer + pystr = self.read_unicode(pointer) + # step 3: return it + return pystr.encode('utf-8') + + def handle_inline_explicit(self): + logger = lldb.formatters.Logger.Logger() + offset = 3*self.pointer_size + offset = offset + self.valobj.GetValueAsUnsigned(0) + return self.valobj.CreateValueFromExpression("content", + "(char*)(" + str(offset) + ")") + + def handle_mutable_string(self): + logger = lldb.formatters.Logger.Logger() + offset = 2 * self.pointer_size + data = self.valobj.CreateChildAtOffset("content", + offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()); + data_value = data.GetValueAsUnsigned(0) + if self.explicit and self.unicode: + return self.read_unicode(data_value).encode('utf-8') + else: + data_value = data_value + 1 + return self.valobj.CreateValueFromExpression("content", "(char*)(" + str(data_value) + ")") + + def handle_UTF8_inline(self): + logger = lldb.formatters.Logger.Logger() + offset = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base(); + if self.explicit == False: + offset = offset + 1; + return self.valobj.CreateValueFromAddress("content", + offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)).AddressOf(); + + def handle_UTF8_not_inline(self): + logger = lldb.formatters.Logger.Logger() + offset = self.size_of_cfruntime_base(); + return self.valobj.CreateChildAtOffset("content", + offset,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()); + + def get_child_at_index(self,index): + logger = lldb.formatters.Logger.Logger() + logger >> "Querying for child [" + str(index) + "]" + if index == 0: + return self.valobj.CreateValueFromExpression("mutable", + str(int(self.mutable))); + if index == 1: + return self.valobj.CreateValueFromExpression("inline", + str(int(self.inline))); + if index == 2: + return self.valobj.CreateValueFromExpression("explicit", + str(int(self.explicit))); + if index == 3: + return self.valobj.CreateValueFromExpression("unicode", + str(int(self.unicode))); + if index == 4: + return self.valobj.CreateValueFromExpression("special", + str(int(self.special))); + if index == 5: + # we are handling the several possible combinations of flags. + # for each known combination we have a function that knows how to + # go fetch the data from memory instead of running code. if a string is not + # correctly displayed, one should start by finding a combination of flags that + # makes it different from these known cases, and provide a new reader function + # if this is not possible, a new flag might have to be made up (like the "special" flag + # below, which is not a real flag in CFString), or alternatively one might need to use + # the ObjC runtime helper to detect the new class and deal with it accordingly + #print 'mutable = ' + str(self.mutable) + #print 'inline = ' + str(self.inline) + #print 'explicit = ' + str(self.explicit) + #print 'unicode = ' + str(self.unicode) + #print 'special = ' + str(self.special) + if self.mutable == True: + return self.handle_mutable_string() + elif self.inline == True and self.explicit == True and \ + self.unicode == False and self.special == False and \ + self.mutable == False: + return self.handle_inline_explicit() + elif self.unicode == True: + return self.handle_unicode_string(); + elif self.special == True: + return self.handle_special(); + elif self.inline == True: + return self.handle_UTF8_inline(); + else: + return self.handle_UTF8_not_inline(); + + def get_child_index(self,name): + logger = lldb.formatters.Logger.Logger() + logger >> "Querying for child ['" + str(name) + "']" + if name == "content": + return self.num_children() - 1; + if name == "mutable": + return 0; + if name == "inline": + return 1; + if name == "explicit": + return 2; + if name == "unicode": + return 3; + if name == "special": + return 4; + + # CFRuntimeBase is defined as having an additional + # 4 bytes (padding?) on LP64 architectures + # to get its size we add up sizeof(pointer)+4 + # and then add 4 more bytes if we are on a 64bit system + def size_of_cfruntime_base(self): + logger = lldb.formatters.Logger.Logger() + return self.pointer_size+4+(4 if self.is_64_bit else 0) + + # the info bits are part of the CFRuntimeBase structure + # to get at them we have to skip a uintptr_t and then get + # at the least-significant byte of a 4 byte array. If we are + # on big-endian this means going to byte 3, if we are on + # little endian (OSX & iOS), this means reading byte 0 + def offset_of_info_bits(self): + logger = lldb.formatters.Logger.Logger() + offset = self.pointer_size + if self.is_little == False: + offset = offset + 3; + return offset; + + def read_info_bits(self): + logger = lldb.formatters.Logger.Logger() + cfinfo = self.valobj.CreateChildAtOffset("cfinfo", + self.offset_of_info_bits(), + self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)); + cfinfo.SetFormat(11) + info = cfinfo.GetValue(); + if info != None: + self.invalid = False; + return int(info,0); + else: + self.invalid = True; + return None; + + # calculating internal flag bits of the CFString object + # this stuff is defined and discussed in CFString.c + def is_mutable(self): + logger = lldb.formatters.Logger.Logger() + return (self.info_bits & 1) == 1; + + def is_inline(self): + logger = lldb.formatters.Logger.Logger() + return (self.info_bits & 0x60) == 0; + + # this flag's name is ambiguous, it turns out + # we must skip a length byte to get at the data + # when this flag is False + def has_explicit_length(self): + logger = lldb.formatters.Logger.Logger() + return (self.info_bits & (1 | 4)) != 4; + + # probably a subclass of NSString. obtained this from [str pathExtension] + # here info_bits = 0 and Unicode data at the start of the padding word + # in the long run using the isa value might be safer as a way to identify this + # instead of reading the info_bits + def is_special_case(self): + logger = lldb.formatters.Logger.Logger() + return self.info_bits == 0; + + def is_unicode(self): + logger = lldb.formatters.Logger.Logger() + return (self.info_bits & 0x10) == 0x10; + + # preparing ourselves to read into memory + # by adjusting architecture-specific info + def adjust_for_architecture(self): + logger = lldb.formatters.Logger.Logger() + self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() + self.is_64_bit = self.pointer_size == 8 + self.is_little = self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle + + # reading info bits out of the CFString and computing + # useful values to get at the real data + def compute_flags(self): + logger = lldb.formatters.Logger.Logger() + self.info_bits = self.read_info_bits(); + if self.info_bits == None: + return; + self.mutable = self.is_mutable(); + self.inline = self.is_inline(); + self.explicit = self.has_explicit_length(); + self.unicode = self.is_unicode(); + self.special = self.is_special_case(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + self.compute_flags(); diff --git a/examples/summaries/cocoa/Class.py b/examples/summaries/cocoa/Class.py new file mode 100644 index 000000000000..9c9dda858ac1 --- /dev/null +++ b/examples/summaries/cocoa/Class.py @@ -0,0 +1,21 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +import lldb +import lldb.runtime.objc.objc_runtime +import lldb.formatters.Logger + +def Class_Summary(valobj,dict): + logger = lldb.formatters.Logger.Logger() + runtime =lldb.runtime.objc.objc_runtime.ObjCRuntime.runtime_from_isa(valobj) + if runtime == None or not runtime.is_valid(): + return '' + class_data = runtime.read_class_data() + if class_data == None or not class_data.is_valid(): + return '' + return class_data.class_name() + diff --git a/examples/summaries/cocoa/Logger.py b/examples/summaries/cocoa/Logger.py new file mode 100644 index 000000000000..91d503c31210 --- /dev/null +++ b/examples/summaries/cocoa/Logger.py @@ -0,0 +1,122 @@ +from __future__ import print_function +import sys +import os.path +import inspect + +class NopLogger: + def __init__(self): + pass + + def write(self,data): + pass + + def flush(self): + pass + + def close(self): + pass + + +class StdoutLogger: + def __init__(self): + pass + + def write(self,data): + print(data) + + def flush(self): + pass + + def close(self): + pass + +class FileLogger: + def __init__(self, name): + self.file = None + try: + name = os.path.abspath(name) + self.file = open(name,'a') + except: + try: + self.file = open('formatters.log','a') + except: + pass + + def write(self,data): + if self.file != None: + print(data,file=self.file) + else: + print(data) + + def flush(self): + if self.file != None: + self.file.flush() + + def close(self): + if self.file != None: + self.file.close() + self.file = None + +# to enable logging: +# define lldb.formatters.Logger._lldb_formatters_debug_level to any number greater than 0 +# if you define it to any value greater than 1, the log will be automatically flushed after each write (slower but should make sure most of the stuff makes it to the log even if we crash) +# if you define it to any value greater than 2, the calling function's details will automatically be logged (even slower, but provides additional details) +# if you need the log to go to a file instead of on screen, define lldb.formatters.Logger._lldb_formatters_debug_filename to a valid filename +class Logger: + def __init__(self,autoflush=False,logcaller=False): + global _lldb_formatters_debug_level + global _lldb_formatters_debug_filename + self.autoflush = autoflush + want_log = False + try: + want_log = (_lldb_formatters_debug_level > 0) + except: + pass + if not (want_log): + self.impl = NopLogger() + return + want_file = False + try: + want_file = (_lldb_formatters_debug_filename != None and _lldb_formatters_debug_filename != '' and _lldb_formatters_debug_filename != 0) + except: + pass + if want_file: + self.impl = FileLogger(_lldb_formatters_debug_filename) + else: + self.impl = StdoutLogger() + try: + self.autoflush = (_lldb_formatters_debug_level > 1) + except: + self.autoflush = autoflush + want_caller_info = False + try: + want_caller_info = (_lldb_formatters_debug_level > 2) + except: + pass + if want_caller_info: + self._log_caller() + + def _log_caller(self): + caller = inspect.stack()[2] + try: + if caller != None and len(caller) > 3: + self.write('Logging from function ' + str(caller)) + else: + self.write('Caller info not available - Required caller logging not possible') + finally: + del caller # needed per Python docs to avoid keeping objects alive longer than we care + + def write(self,data): + self.impl.write(data) + if self.autoflush: + self.flush() + + def __rshift__(self,data): + self.write(data) + + def flush(self): + self.impl.flush() + + def close(self): + self.impl.close() + diff --git a/examples/summaries/cocoa/NSBundle.py b/examples/summaries/cocoa/NSBundle.py new file mode 100644 index 000000000000..5fd83f8e89f1 --- /dev/null +++ b/examples/summaries/cocoa/NSBundle.py @@ -0,0 +1,127 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NSBundle +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import NSURL +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but a summary for an NSURL, so they need not +# obey the interface specification for synthetic children providers +class NSBundleKnown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSString): + self.sys_params.types_cache.NSString = self.valobj.GetTarget().FindFirstType('NSString').GetPointerType() + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # we need to skip the ISA, plus four other values + # that are luckily each a pointer in size + # which makes our computation trivial :-) + def offset(self): + logger = lldb.formatters.Logger.Logger() + return 5 * self.sys_params.pointer_size + + def url_text(self): + logger = lldb.formatters.Logger.Logger() + global statistics + text = self.valobj.CreateChildAtOffset("text", + self.offset(), + self.sys_params.types_cache.NSString) + my_string = text.GetSummary() + if (my_string == None) or (my_string == ''): + statistics.metric_hit('unknown_class',str(self.valobj.GetName()) + " triggered unknown pointer location") + return NSBundleUnknown_SummaryProvider(self.valobj, self.sys_params).url_text() + else: + statistics.metric_hit('code_notrun',self.valobj) + return my_string + + +class NSBundleUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def url_text(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + expr = "(NSString*)[" + stream.GetData() + " bundlePath]" + url_text_vo = self.valobj.CreateValueFromExpression("path",expr); + if url_text_vo.IsValid(): + return url_text_vo.GetSummary() + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == 'NSBundle': + wrapper = NSBundleKnown_SummaryProvider(valobj, class_data.sys_params) + # [NSBundle mainBundle] does return an object that is + # not correctly filled out for our purposes, so we still + # end up having to run code in that case + #statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSBundleUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def NSBundle_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.url_text(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None or summary == '': + summary = '' + return summary + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSBundle.NSBundle_SummaryProvider NSBundle") diff --git a/examples/summaries/cocoa/NSData.py b/examples/summaries/cocoa/NSData.py new file mode 100644 index 000000000000..3aa30b29f54b --- /dev/null +++ b/examples/summaries/cocoa/NSData.py @@ -0,0 +1,163 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NSData +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the length for an NSData, so they need not +# obey the interface specification for synthetic children providers +class NSConcreteData_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + logger >> "NSConcreteData_SummaryProvider __init__" + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + self.adjust_for_architecture(); + + # one pointer is the ISA + # then there are 32 bit worth of flags and other data + # however, on 64bit systems these are padded to be a full + # machine word long, which means we actually have two pointers + # worth of data to skip + def offset(self): + return 2 * self.sys_params.pointer_size + + def length(self): + logger = lldb.formatters.Logger.Logger() + logger >> "NSConcreteData_SummaryProvider length" + size = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + logger >> str(size) + logger >> str(size.GetValueAsUnsigned(0)) + return size.GetValueAsUnsigned(0) + + +class NSDataUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + logger >> "NSDataUnknown_SummaryProvider __init__" + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + self.adjust_for_architecture(); + + def length(self): + logger = lldb.formatters.Logger.Logger() + logger >> "NSDataUnknown_SummaryProvider length" + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + logger >> stream.GetData() + num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " length]"); + logger >> "still in after expression: " + str(num_children_vo) + if num_children_vo.IsValid(): + logger >> "wow - expr output is valid: " + str(num_children_vo.GetValueAsUnsigned()) + return num_children_vo.GetValueAsUnsigned(0) + logger >> "invalid expr output - too bad" + return '' + + +def GetSummary_Impl(valobj): + global statistics + logger = lldb.formatters.Logger.Logger() + logger >> "NSData GetSummary_Impl" + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + logger >> "got a wrapper summary - using it" + return wrapper + + name_string = class_data.class_name() + logger >> "class name: " + name_string + if name_string == 'NSConcreteData' or \ + name_string == 'NSConcreteMutableData' or \ + name_string == '__NSCFData': + wrapper = NSConcreteData_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSDataUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def NSData_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + logger >> "NSData_SummaryProvider" + provider = GetSummary_Impl(valobj); + logger >> "found a summary provider, it is: " + str(provider) + if provider != None: + try: + summary = provider.length(); + except: + summary = None + logger >> "got a summary: it is " + str(summary) + if summary == None: + summary = '' + elif isinstance(summary,basestring): + pass + else: + if summary == 1: + summary = '1 byte' + else: + summary = str(summary) + ' bytes' + return summary + return 'Summary Unavailable' + +def NSData_SummaryProvider2 (valobj,dict): + logger = lldb.formatters.Logger.Logger() + logger >> "NSData_SummaryProvider2" + provider = GetSummary_Impl(valobj); + logger >> "found a summary provider, it is: " + str(provider) + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.length(); + except: + summary = None + logger >> "got a summary: it is " + str(summary) + if summary == None: + summary = '' + elif isinstance(summary,basestring): + pass + else: + if summary == 1: + summary = '@"1 byte"' + else: + summary = '@"' + str(summary) + ' bytes"' + return summary + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSData.NSData_SummaryProvider NSData") + debugger.HandleCommand("type summary add -F NSData.NSData_SummaryProvider2 CFDataRef CFMutableDataRef") diff --git a/examples/summaries/cocoa/NSDate.py b/examples/summaries/cocoa/NSDate.py new file mode 100644 index 000000000000..4dd63b4a5c32 --- /dev/null +++ b/examples/summaries/cocoa/NSDate.py @@ -0,0 +1,269 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NSDate +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import struct +import time +import datetime +import CFString +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# Python promises to start counting time at midnight on Jan 1st on the epoch year +# hence, all we need to know is the epoch year +python_epoch = time.gmtime(0).tm_year + +osx_epoch = datetime.date(2001,1,1).timetuple() + +def mkgmtime(t): + logger = lldb.formatters.Logger.Logger() + return time.mktime(t)-time.timezone + +osx_epoch = mkgmtime(osx_epoch) + +def osx_to_python_time(osx): + logger = lldb.formatters.Logger.Logger() + if python_epoch <= 2001: + return osx + osx_epoch + else: + return osx - osx_epoch + +# represent a struct_time as a string in the format used by Xcode +def xcode_format_time(X): + logger = lldb.formatters.Logger.Logger() + return time.strftime('%Y-%m-%d %H:%M:%S %Z',X) + +# represent a count-since-epoch as a string in the format used by Xcode +def xcode_format_count(X): + logger = lldb.formatters.Logger.Logger() + return xcode_format_time(time.localtime(X)) + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the summary for NSDate, so they need not +# obey the interface specification for synthetic children providers +class NSTaggedDate_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, info_bits, data, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + # NSDate is not using its info_bits for info like NSNumber is + # so we need to regroup info_bits and data + self.data = ((data << 8) | (info_bits << 4)) + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def value(self): + logger = lldb.formatters.Logger.Logger() + # the value of the date-time object is wrapped into the pointer value + # unfortunately, it is made as a time-delta after Jan 1 2001 midnight GMT + # while all Python knows about is the "epoch", which is a platform-dependent + # year (1970 of *nix) whose Jan 1 at midnight is taken as reference + value_double = struct.unpack('d', struct.pack('Q', self.data))[0] + if value_double == -63114076800.0: + return '0001-12-30 00:00:00 +0000' + return xcode_format_count(osx_to_python_time(value_double)) + + +class NSUntaggedDate_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not (self.sys_params.types_cache.double): + self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble) + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def offset(self): + logger = lldb.formatters.Logger.Logger() + return self.sys_params.pointer_size + + def value(self): + logger = lldb.formatters.Logger.Logger() + value = self.valobj.CreateChildAtOffset("value", + self.offset(), + self.sys_params.types_cache.double) + value_double = struct.unpack('d', struct.pack('Q', value.GetData().uint64[0]))[0] + if value_double == -63114076800.0: + return '0001-12-30 00:00:00 +0000' + return xcode_format_count(osx_to_python_time(value_double)) + +class NSCalendarDate_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not (self.sys_params.types_cache.double): + self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble) + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def offset(self): + logger = lldb.formatters.Logger.Logger() + return 2*self.sys_params.pointer_size + + def value(self): + logger = lldb.formatters.Logger.Logger() + value = self.valobj.CreateChildAtOffset("value", + self.offset(), + self.sys_params.types_cache.double) + value_double = struct.unpack('d', struct.pack('Q', value.GetData().uint64[0]))[0] + return xcode_format_count(osx_to_python_time(value_double)) + +class NSTimeZoneClass_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not (self.sys_params.types_cache.voidptr): + self.sys_params.types_cache.voidptr = self.valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType() + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def offset(self): + logger = lldb.formatters.Logger.Logger() + return self.sys_params.pointer_size + + def timezone(self): + logger = lldb.formatters.Logger.Logger() + tz_string = self.valobj.CreateChildAtOffset("tz_name", + self.offset(), + self.sys_params.types_cache.voidptr) + return CFString.CFString_SummaryProvider(tz_string,None) + +class NSUnknownDate_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def value(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + expr = "(NSString*)[" + stream.GetData() + " description]" + num_children_vo = self.valobj.CreateValueFromExpression("str",expr); + if num_children_vo.IsValid(): + return num_children_vo.GetSummary() + return '' + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == 'NSDate' or name_string == '__NSDate' or name_string == '__NSTaggedDate': + if class_data.is_tagged(): + wrapper = NSTaggedDate_SummaryProvider(valobj,class_data.info_bits(),class_data.value(), class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSUntaggedDate_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + elif name_string == 'NSCalendarDate': + wrapper = NSCalendarDate_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + elif name_string == '__NSTimeZone': + wrapper = NSTimeZoneClass_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSUnknownDate_SummaryProvider(valobj) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + + +def NSDate_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.value(); + except: + summary = None + if summary == None: + summary = '' + return str(summary) + return 'Summary Unavailable' + +def NSTimeZone_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.timezone(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None: + summary = '' + return str(summary) + return 'Summary Unavailable' + + +def CFAbsoluteTime_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + try: + value_double = struct.unpack('d', struct.pack('Q', valobj.GetData().uint64[0]))[0] + return xcode_format_count(osx_to_python_time(value_double)) + except: + return 'Summary Unavailable' + + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSDate.NSDate_SummaryProvider NSDate") + debugger.HandleCommand("type summary add -F NSDate.CFAbsoluteTime_SummaryProvider CFAbsoluteTime") + debugger.HandleCommand("type summary add -F NSDate.NSTimeZone_SummaryProvider NSTimeZone CFTimeZoneRef") + diff --git a/examples/summaries/cocoa/NSException.py b/examples/summaries/cocoa/NSException.py new file mode 100644 index 000000000000..72bf895bdbc3 --- /dev/null +++ b/examples/summaries/cocoa/NSException.py @@ -0,0 +1,114 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# summary provider for class NSException +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import CFString +import lldb +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +class NSKnownException_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not (self.sys_params.types_cache.id): + self.sys_params.types_cache.id = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def offset_name(self): + logger = lldb.formatters.Logger.Logger() + return self.sys_params.pointer_size + def offset_reason(self): + logger = lldb.formatters.Logger.Logger() + return 2*self.sys_params.pointer_size + + def description(self): + logger = lldb.formatters.Logger.Logger() + name_ptr = self.valobj.CreateChildAtOffset("name", + self.offset_name(), + self.sys_params.types_cache.id) + reason_ptr = self.valobj.CreateChildAtOffset("reason", + self.offset_reason(), + self.sys_params.types_cache.id) + return 'name:' + CFString.CFString_SummaryProvider(name_ptr,None) + ' reason:' + CFString.CFString_SummaryProvider(reason_ptr,None) + +class NSUnknownException_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def description(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + name_vo = self.valobj.CreateValueFromExpression("name","(NSString*)[" + stream.GetData() + " name]"); + reason_vo = self.valobj.CreateValueFromExpression("reason","(NSString*)[" + stream.GetData() + " reason]"); + if name_vo.IsValid() and reason_vo.IsValid(): + return CFString.CFString_SummaryProvider(name_vo,None) + ' ' + CFString.CFString_SummaryProvider(reason_vo,None) + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == 'NSException': + wrapper = NSKnownException_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSUnknownException_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def NSException_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.description(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None: + summary = '' + return str(summary) + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSException.NSException_SummaryProvider NSException") diff --git a/examples/summaries/cocoa/NSIndexSet.py b/examples/summaries/cocoa/NSIndexSet.py new file mode 100644 index 000000000000..011d58dd773d --- /dev/null +++ b/examples/summaries/cocoa/NSIndexSet.py @@ -0,0 +1,150 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NS(Mutable)IndexSet +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the count of values for an NSIndexSet, so they need not +# obey the interface specification for synthetic children providers +class NSIndexSetClass_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + if not(self.sys_params.types_cache.uint32): + self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # NS(Mutable)IndexSet works in one of two modes: when having a compact block of data (e.g. a Range) + # the count is stored in the set itself, 3 pointers into it + # otherwise, it will store a pointer to an additional data structure (2 pointers into itself) and this + # additional structure will contain the count two pointers deep + # a bunch of flags allow us to detect an empty set, vs. a one-range set, vs. a multi-range set + def count(self): + logger = lldb.formatters.Logger.Logger() + mode_chooser_vo = self.valobj.CreateChildAtOffset("mode_chooser", + self.sys_params.pointer_size, + self.sys_params.types_cache.uint32) + mode_chooser = mode_chooser_vo.GetValueAsUnsigned(0) + if self.sys_params.is_64_bit: + mode_chooser = mode_chooser & 0x00000000FFFFFFFF + # empty set + if mode_chooser & 0x01 == 1: + return 0 + # single range + if mode_chooser & 0x02 == 2: + mode = 1 + # multi range + else: + mode = 2 + if mode == 1: + count_vo = self.valobj.CreateChildAtOffset("count", + 3*self.sys_params.pointer_size, + self.sys_params.types_cache.NSUInteger) + else: + count_ptr = self.valobj.CreateChildAtOffset("count_ptr", + 2*self.sys_params.pointer_size, + self.sys_params.types_cache.NSUInteger) + count_vo = self.valobj.CreateValueFromAddress("count", + count_ptr.GetValueAsUnsigned()+2*self.sys_params.pointer_size, + self.sys_params.types_cache.NSUInteger) + return count_vo.GetValueAsUnsigned(0) + + +class NSIndexSetUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def count(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + expr = "(int)[" + stream.GetData() + " count]" + num_children_vo = self.valobj.CreateValueFromExpression("count",expr) + if num_children_vo.IsValid(): + return num_children_vo.GetValueAsUnsigned(0) + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == 'NSIndexSet' or name_string == 'NSMutableIndexSet': + wrapper = NSIndexSetClass_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSIndexSetUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + + +def NSIndexSet_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.count(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None: + summary = '' + if isinstance(summary, basestring): + return summary + else: + summary = str(summary) + (' indexes' if summary != 1 else ' index') + return summary + return 'Summary Unavailable' + + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSIndexSet.NSIndexSet_SummaryProvider NSIndexSet NSMutableIndexSet") diff --git a/examples/summaries/cocoa/NSMachPort.py b/examples/summaries/cocoa/NSMachPort.py new file mode 100644 index 000000000000..554d2ca7785a --- /dev/null +++ b/examples/summaries/cocoa/NSMachPort.py @@ -0,0 +1,123 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NSMachPort +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the port number of an NSMachPort, so they need not +# obey the interface specification for synthetic children providers +class NSMachPortKnown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # one pointer is the ISA + # then we have one other internal pointer, plus + # 4 bytes worth of flags. hence, these values + def offset(self): + logger = lldb.formatters.Logger.Logger() + if self.sys_params.is_64_bit: + return 20 + else: + return 12 + + def port(self): + logger = lldb.formatters.Logger.Logger() + vport = self.valobj.CreateChildAtOffset("port", + self.offset(), + self.sys_params.types_cache.NSUInteger) + return vport.GetValueAsUnsigned(0) + + +class NSMachPortUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def port(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + num_children_vo = self.valobj.CreateValueFromExpression("port","(int)[" + stream.GetData() + " machPort]") + if num_children_vo.IsValid(): + return num_children_vo.GetValueAsUnsigned(0) + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == 'NSMachPort': + wrapper = NSMachPortKnown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSMachPortUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def NSMachPort_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.port(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None: + summary = '' + if isinstance(summary, basestring): + return summay + return 'mach port: ' + str(summary) + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSMachPort.NSMachPort_SummaryProvider NSMachPort") diff --git a/examples/summaries/cocoa/NSNotification.py b/examples/summaries/cocoa/NSNotification.py new file mode 100644 index 000000000000..33c20065346f --- /dev/null +++ b/examples/summaries/cocoa/NSNotification.py @@ -0,0 +1,110 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NSNotification +# the real summary is now C++ code built into LLDB +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import CFString +import lldb +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +class NSConcreteNotification_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not (self.sys_params.types_cache.id): + self.sys_params.types_cache.id = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # skip the ISA and go to the name pointer + def offset(self): + logger = lldb.formatters.Logger.Logger() + return self.sys_params.pointer_size + + def name(self): + logger = lldb.formatters.Logger.Logger() + string_ptr = self.valobj.CreateChildAtOffset("name", + self.offset(), + self.sys_params.types_cache.id) + return CFString.CFString_SummaryProvider(string_ptr,None) + + +class NSNotificationUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def name(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + name_vo = self.valobj.CreateValueFromExpression("name","(NSString*)[" + stream.GetData() + " name]") + if name_vo.IsValid(): + return CFString.CFString_SummaryProvider(name_vo,None) + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == 'NSConcreteNotification': + wrapper = NSConcreteNotification_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSNotificationUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def NSNotification_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.name(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None: + summary = '' + return str(summary) + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSNotification.NSNotification_SummaryProvider NSNotification") diff --git a/examples/summaries/cocoa/NSNumber.py b/examples/summaries/cocoa/NSNumber.py new file mode 100644 index 000000000000..7edd33803f9c --- /dev/null +++ b/examples/summaries/cocoa/NSNumber.py @@ -0,0 +1,235 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# example summary provider for NSNumber +# the real summary is now C++ code built into LLDB +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import struct +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the port number of an NSNumber, so they need not +# obey the interface specification for synthetic children providers +class NSTaggedNumber_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, info_bits, data, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.info_bits = info_bits + self.data = data + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def value(self): + logger = lldb.formatters.Logger.Logger() + # in spite of the plenty of types made available by the public NSNumber API + # only a bunch of these are actually used in the internal implementation + # unfortunately, the original type information appears to be lost + # so we try to at least recover the proper magnitude of the data + if self.info_bits == 0: + return '(char)' + str(ord(ctypes.c_char(chr(self.data % 256)).value)) + if self.info_bits == 4: + return '(short)' + str(ctypes.c_short(self.data % (256*256)).value) + if self.info_bits == 8: + return '(int)' + str(ctypes.c_int(self.data % (256*256*256*256)).value) + if self.info_bits == 12: + return '(long)' + str(ctypes.c_long(self.data).value) + else: + return 'unexpected value:(info=' + str(self.info_bits) + ", value = " + str(self.data) + ')' + + +class NSUntaggedNumber_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.char): + self.sys_params.types_cache.char = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar) + if not(self.sys_params.types_cache.short): + self.sys_params.types_cache.short = self.valobj.GetType().GetBasicType(lldb.eBasicTypeShort) + if not(self.sys_params.types_cache.ushort): + self.sys_params.types_cache.ushort = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedShort) + if not(self.sys_params.types_cache.int): + self.sys_params.types_cache.int = self.valobj.GetType().GetBasicType(lldb.eBasicTypeInt) + if not(self.sys_params.types_cache.long): + self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong) + if not(self.sys_params.types_cache.ulong): + self.sys_params.types_cache.ulong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + if not(self.sys_params.types_cache.longlong): + self.sys_params.types_cache.longlong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLongLong) + if not(self.sys_params.types_cache.ulonglong): + self.sys_params.types_cache.ulonglong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLongLong) + if not(self.sys_params.types_cache.float): + self.sys_params.types_cache.float = self.valobj.GetType().GetBasicType(lldb.eBasicTypeFloat) + if not(self.sys_params.types_cache.double): + self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def value(self): + logger = lldb.formatters.Logger.Logger() + global statistics + # we need to skip the ISA, then the next byte tells us what to read + # we then skip one other full pointer worth of data and then fetch the contents + # if we are fetching an int64 value, one more pointer must be skipped to get at our data + data_type_vo = self.valobj.CreateChildAtOffset("dt", + self.sys_params.pointer_size, + self.sys_params.types_cache.char) + data_type = ((data_type_vo.GetValueAsUnsigned(0) % 256) & 0x1F) + data_offset = 2 * self.sys_params.pointer_size + if data_type == 0B00001: + data_vo = self.valobj.CreateChildAtOffset("data", + data_offset, + self.sys_params.types_cache.char) + statistics.metric_hit('code_notrun',self.valobj) + return '(char)' + str(ord(ctypes.c_char(chr(data_vo.GetValueAsUnsigned(0))).value)) + elif data_type == 0B0010: + data_vo = self.valobj.CreateChildAtOffset("data", + data_offset, + self.sys_params.types_cache.short) + statistics.metric_hit('code_notrun',self.valobj) + return '(short)' + str(ctypes.c_short(data_vo.GetValueAsUnsigned(0) % (256*256)).value) + # IF tagged pointers are possible on 32bit+v2 runtime + # (of which the only existing instance should be iOS) + # then values of this type might be tagged + elif data_type == 0B0011: + data_vo = self.valobj.CreateChildAtOffset("data", + data_offset, + self.sys_params.types_cache.int) + statistics.metric_hit('code_notrun',self.valobj) + return '(int)' + str(ctypes.c_int(data_vo.GetValueAsUnsigned(0)% (256*256*256*256)).value) + # apparently, on is_64_bit architectures, these are the only values that will ever + # be represented by a non tagged pointers + elif data_type == 0B10001: + data_offset = data_offset + 8 # 8 is needed even if we are on 32bit + data_vo = self.valobj.CreateChildAtOffset("data", + data_offset, + self.sys_params.types_cache.longlong) + statistics.metric_hit('code_notrun',self.valobj) + return '(long)' + str(ctypes.c_long(data_vo.GetValueAsUnsigned(0)).value) + elif data_type == 0B0100: + if self.sys_params.is_64_bit: + data_offset = data_offset + self.sys_params.pointer_size + data_vo = self.valobj.CreateChildAtOffset("data", + data_offset, + self.sys_params.types_cache.longlong) + statistics.metric_hit('code_notrun',self.valobj) + return '(long)' + str(ctypes.c_long(data_vo.GetValueAsUnsigned(0)).value) + elif data_type == 0B0101: + data_vo = self.valobj.CreateChildAtOffset("data", + data_offset, + self.sys_params.types_cache.longlong) + data_plain = int(str(data_vo.GetValueAsUnsigned(0) & 0x00000000FFFFFFFF)) + packed = struct.pack('I', data_plain) + data_float = struct.unpack('f', packed)[0] + statistics.metric_hit('code_notrun',self.valobj) + return '(float)' + str(data_float) + elif data_type == 0B0110: + data_vo = self.valobj.CreateChildAtOffset("data", + data_offset, + self.sys_params.types_cache.longlong) + data_plain = data_vo.GetValueAsUnsigned(0) + data_double = struct.unpack('d', struct.pack('Q', data_plain))[0] + statistics.metric_hit('code_notrun',self.valobj) + return '(double)' + str(data_double) + statistics.metric_hit('unknown_class',str(valobj.GetName()) + " had unknown data_type " + str(data_type)) + return 'unexpected: dt = ' + str(data_type) + + +class NSUnknownNumber_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def value(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + expr = "(NSString*)[" + stream.GetData() + " stringValue]" + num_children_vo = self.valobj.CreateValueFromExpression("str",expr) + if num_children_vo.IsValid(): + return num_children_vo.GetSummary() + return '' + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == 'NSNumber' or name_string == '__NSCFNumber': + if class_data.is_tagged(): + wrapper = NSTaggedNumber_SummaryProvider(valobj,class_data.info_bits(),class_data.value(), class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + # the wrapper might be unable to decipher what is into the NSNumber + # and then have to run code on it + wrapper = NSUntaggedNumber_SummaryProvider(valobj, class_data.sys_params) + else: + wrapper = NSUnknownNumber_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + + +def NSNumber_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.value(); + except Exception as foo: + print foo +# except: + summary = None + logger >> "got summary " + str(summary) + if summary == None: + summary = '' + return str(summary) + return 'Summary Unavailable' + + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSNumber.NSNumber_SummaryProvider NSNumber") + debugger.HandleCommand("type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFBoolean") + debugger.HandleCommand("type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFNumber") + diff --git a/examples/summaries/cocoa/NSSet.py b/examples/summaries/cocoa/NSSet.py new file mode 100644 index 000000000000..71665dbc9f79 --- /dev/null +++ b/examples/summaries/cocoa/NSSet.py @@ -0,0 +1,263 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# summary provider for NSSet +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import CFBag +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but the port number of an NSMachPort, so they need not +# obey the interface specification for synthetic children providers +class NSCFSet_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # one pointer is the ISA + # then we have one other internal pointer, plus + # 4 bytes worth of flags. hence, these values + def offset(self): + logger = lldb.formatters.Logger.Logger() + if self.sys_params.is_64_bit: + return 20 + else: + return 12 + + def count(self): + logger = lldb.formatters.Logger.Logger() + vcount = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + return vcount.GetValueAsUnsigned(0) + + +class NSSetUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def count(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + expr = "(int)[" + stream.GetData() + " count]" + num_children_vo = self.valobj.CreateValueFromExpression("count",expr) + if num_children_vo.IsValid(): + return num_children_vo.GetValueAsUnsigned(0) + return '' + +class NSSetI_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # we just need to skip the ISA and the count immediately follows + def offset(self): + logger = lldb.formatters.Logger.Logger() + return self.sys_params.pointer_size + + def count(self): + logger = lldb.formatters.Logger.Logger() + num_children_vo = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + value = num_children_vo.GetValueAsUnsigned(0) + if value != None: + # the MSB on immutable sets seems to be taken by some other data + # not sure if it is a bug or some weird sort of feature, but masking it out + # gets the count right (unless, of course, someone's dictionaries grow + # too large - but I have not tested this) + if self.sys_params.is_64_bit: + value = value & ~0xFF00000000000000 + else: + value = value & ~0xFF000000 + return value + +class NSSetM_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSUInteger): + if self.sys_params.is_64_bit: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + else: + self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # we just need to skip the ISA and the count immediately follows + def offset(self): + logger = lldb.formatters.Logger.Logger() + return self.sys_params.pointer_size + + def count(self): + logger = lldb.formatters.Logger.Logger() + num_children_vo = self.valobj.CreateChildAtOffset("count", + self.offset(), + self.sys_params.types_cache.NSUInteger) + return num_children_vo.GetValueAsUnsigned(0) + + +class NSCountedSet_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not (self.sys_params.types_cache.voidptr): + self.sys_params.types_cache.voidptr = self.valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType() + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # an NSCountedSet is implemented using a CFBag whose pointer just follows the ISA + def offset(self): + logger = lldb.formatters.Logger.Logger() + return self.sys_params.pointer_size + + def count(self): + logger = lldb.formatters.Logger.Logger() + cfbag_vo = self.valobj.CreateChildAtOffset("bag_impl", + self.offset(), + self.sys_params.types_cache.voidptr) + return CFBag.CFBagRef_SummaryProvider(cfbag_vo,self.sys_params).length() + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == '__NSCFSet': + wrapper = NSCFSet_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + elif name_string == '__NSSetI': + wrapper = NSSetI_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + elif name_string == '__NSSetM': + wrapper = NSSetM_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + elif name_string == 'NSCountedSet': + wrapper = NSCountedSet_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSSetUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + + +def NSSet_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + try: + summary = provider.count(); + except: + summary = None + if summary == None: + summary = '' + if isinstance(summary, basestring): + return summary + else: + summary = str(summary) + (' objects' if summary != 1 else ' object') + return summary + return 'Summary Unavailable' + +def NSSet_SummaryProvider2 (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.count(); + except: + summary = None + logger >> "got summary " + str(summary) + # for some reason, one needs to clear some bits for the count returned + # to be correct when using directly CF*SetRef as compared to NS*Set + # this only happens on 64bit, and the bit mask was derived through + # experimentation (if counts start looking weird, then most probably + # the mask needs to be changed) + if summary == None: + summary = '' + if isinstance(summary, basestring): + return summary + else: + if provider.sys_params.is_64_bit: + summary = summary & ~0x1fff000000000000 + summary = '@"' + str(summary) + (' values"' if summary != 1 else ' value"') + return summary + return 'Summary Unavailable' + + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider NSSet") + debugger.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef") diff --git a/examples/summaries/cocoa/NSURL.py b/examples/summaries/cocoa/NSURL.py new file mode 100644 index 000000000000..ac47be365e5a --- /dev/null +++ b/examples/summaries/cocoa/NSURL.py @@ -0,0 +1,137 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +# summary provider for NSURL +import lldb +import ctypes +import lldb.runtime.objc.objc_runtime +import lldb.formatters.metrics +import CFString +import lldb.formatters.Logger + +statistics = lldb.formatters.metrics.Metrics() +statistics.add_metric('invalid_isa') +statistics.add_metric('invalid_pointer') +statistics.add_metric('unknown_class') +statistics.add_metric('code_notrun') + +# despite the similary to synthetic children providers, these classes are not +# trying to provide anything but a summary for an NSURL, so they need not +# obey the interface specification for synthetic children providers +class NSURLKnown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + if not(self.sys_params.types_cache.NSString): + self.sys_params.types_cache.NSString = self.valobj.GetTarget().FindFirstType('NSString').GetPointerType() + if not(self.sys_params.types_cache.NSURL): + self.sys_params.types_cache.NSURL = self.valobj.GetTarget().FindFirstType('NSURL').GetPointerType() + self.update(); + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + # one pointer is the ISA + # then there is one more pointer and 8 bytes of plain data + # (which are also present on a 32-bit system) + # then there is a pointer to an NSString which is the url text + # optionally, the next pointer is another NSURL which is the "base" + # of this one when doing NSURLs composition (incidentally, NSURLs can + # recurse the base+text mechanism to any desired depth) + def offset_text(self): + logger = lldb.formatters.Logger.Logger() + return 24 if self.sys_params.is_64_bit else 16 + def offset_base(self): + logger = lldb.formatters.Logger.Logger() + return self.offset_text()+self.sys_params.pointer_size + + def url_text(self): + logger = lldb.formatters.Logger.Logger() + text = self.valobj.CreateChildAtOffset("text", + self.offset_text(), + self.sys_params.types_cache.NSString) + base = self.valobj.CreateChildAtOffset("base", + self.offset_base(), + self.sys_params.types_cache.NSURL) + my_string = CFString.CFString_SummaryProvider(text,None) + if len(my_string) > 0 and base.GetValueAsUnsigned(0) != 0: + # remove final " from myself + my_string = my_string[0:len(my_string)-1] + my_string = my_string + ' -- ' + my_base_string = NSURL_SummaryProvider(base,None) + if len(my_base_string) > 2: + # remove @" marker from base URL string + my_base_string = my_base_string[2:] + my_string = my_string + my_base_string + return my_string + + +class NSURLUnknown_SummaryProvider: + def adjust_for_architecture(self): + pass + + def __init__(self, valobj, params): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.sys_params = params + self.update() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(); + + def url_text(self): + logger = lldb.formatters.Logger.Logger() + stream = lldb.SBStream() + self.valobj.GetExpressionPath(stream) + url_text_vo = self.valobj.CreateValueFromExpression("url","(NSString*)[" + stream.GetData() + " description]") + if url_text_vo.IsValid(): + return CFString.CFString_SummaryProvider(url_text_vo,None) + return '' + + +def GetSummary_Impl(valobj): + logger = lldb.formatters.Logger.Logger() + global statistics + class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics) + if wrapper: + return wrapper + + name_string = class_data.class_name() + logger >> "class name is: " + str(name_string) + + if name_string == 'NSURL': + wrapper = NSURLKnown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('code_notrun',valobj) + else: + wrapper = NSURLUnknown_SummaryProvider(valobj, class_data.sys_params) + statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string) + return wrapper; + +def NSURL_SummaryProvider (valobj,dict): + logger = lldb.formatters.Logger.Logger() + provider = GetSummary_Impl(valobj); + if provider != None: + if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description): + return provider.message() + try: + summary = provider.url_text(); + except: + summary = None + logger >> "got summary " + str(summary) + if summary == None or summary == '': + summary = '' + return summary + return 'Summary Unavailable' + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand("type summary add -F NSURL.NSURL_SummaryProvider NSURL CFURLRef") diff --git a/examples/summaries/cocoa/Selector.py b/examples/summaries/cocoa/Selector.py new file mode 100644 index 000000000000..d0505204bf22 --- /dev/null +++ b/examples/summaries/cocoa/Selector.py @@ -0,0 +1,14 @@ +""" +LLDB AppKit formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +import lldb + +def SEL_Summary(valobj,dict): + return valobj.Cast(valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()).GetSummary() + +def SELPointer_Summary(valobj,dict): + return valobj.CreateValueFromAddress('text',valobj.GetValueAsUnsigned(0),valobj.GetType().GetBasicType(lldb.eBasicTypeChar)).AddressOf().GetSummary() diff --git a/examples/summaries/cocoa/attrib_fromdict.py b/examples/summaries/cocoa/attrib_fromdict.py new file mode 100644 index 000000000000..86964d602b5b --- /dev/null +++ b/examples/summaries/cocoa/attrib_fromdict.py @@ -0,0 +1,38 @@ +""" +Objective-C runtime wrapper for use by LLDB Python formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +class AttributesDictionary: + def __init__(self, allow_reset = True): + self.__dict__['_dictionary'] = {} # need to do it this way to prevent endless recursion + self.__dict__['_allow_reset'] = allow_reset + + def __getattr__(self,name): + if not self._check_exists(name): + return None + value = self._dictionary[name] + return value + + def _set_impl(self,name,value): + self._dictionary[name] = value + + def _check_exists(self,name): + return name in self._dictionary + + def __setattr__(self,name,value): + if self._allow_reset: + self._set_impl(name,value) + else: + self.set_if_necessary(name,value) + + def set_if_necessary(self,name,value): + if not self._check_exists(name): + self._set_impl(name,value) + return True + return False + + def __len__(self): + return len(self._dictionary) \ No newline at end of file diff --git a/examples/summaries/cocoa/cache.py b/examples/summaries/cocoa/cache.py new file mode 100644 index 000000000000..066829d80d4f --- /dev/null +++ b/examples/summaries/cocoa/cache.py @@ -0,0 +1,35 @@ +""" +Objective-C runtime wrapper for use by LLDB Python formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +import lldb.formatters.metrics + +class Cache: + def __init__(self): + self.data = {} + self.statistics = lldb.formatters.metrics.Metrics() + self.statistics.add_metric('hit') + self.statistics.add_metric('miss') + + def look_for_key(self,key): + if key in self.data: + return True + return False + + def add_item(self,key,value,ok_to_replace=True): + if not(ok_to_replace) and self.look_for_key(key): + return False + self.data[key] = value + return True + + def get_value(self,key,default=None): + if self.look_for_key(key): + self.statistics.metric_hit('hit',key) + return self.data[key] + else: + self.statistics.metric_hit('miss',key) + return default + diff --git a/examples/summaries/cocoa/metrics.py b/examples/summaries/cocoa/metrics.py new file mode 100644 index 000000000000..6b82ff3b3015 --- /dev/null +++ b/examples/summaries/cocoa/metrics.py @@ -0,0 +1,94 @@ +""" +Objective-C runtime wrapper for use by LLDB Python formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +import lldb +import time, datetime +import inspect + +class TimeMetrics: + @staticmethod + def generate(label=None): + return TimeMetrics(label) + + def __init__(self,lbl=None): + self.label = "" if lbl is None else lbl + pass + + def __enter__(self): + caller = inspect.stack()[1] + self.function = str(caller) + self.enter_time = time.clock() + + def __exit__(self, a,b,c): + self.exit_time = time.clock() + print("It took " + str(self.exit_time - self.enter_time) + " time units to run through " + self.function + self.label) + return False + +class Counter: + def __init__(self): + self.count = 0 + self.list = [] + def update(self,name): + self.count = self.count + 1 + # avoid getting the full dump of this ValueObject just to save its metrics + if isinstance(name,lldb.SBValue): + self.list.append(name.GetName()) + else: + self.list.append(str(name)) + def __str__(self): + return str(self.count) + " times, for items [" + str(self.list) + "]" + +class MetricsPrinter_Verbose: + def __init__(self,metrics): + self.metrics = metrics + def __str__(self): + string = "" + for key,value in self.metrics.metrics.items(): + string = string + "metric " + str(key) + ": " + str(value) + "\n" + return string + +class MetricsPrinter_Compact: + def __init__(self,metrics): + self.metrics = metrics + def __str__(self): + string = "" + for key,value in self.metrics.metrics.items(): + string = string + "metric " + str(key) + " was hit " + str(value.count) + " times\n" + return string + +class Metrics: + def __init__(self): + self.metrics = {} + + def add_metric(self,name): + self.metrics[name] = Counter() + + def metric_hit(self,metric,trigger): + self.metrics[metric].update(trigger) + + def __getitem__(self,key): + return self.metrics[key] + + def __getattr__(self,name): + if name == 'compact': + return MetricsPrinter_Compact(self) + if name == 'verbose': + return MetricsPrinter_Verbose(self) + raise AttributeError("%r object has no attribute %r" % + (type(self).__name__, name)) + + def __str__(self): + return str(self.verbose) + + def metric_success(self,metric): + total_count = 0 + metric_count = self[metric].count + for key,value in self.metrics.items(): + total_count = total_count + value.count + if total_count > 0: + return metric_count / float(total_count) + return 0 diff --git a/examples/summaries/cocoa/objc_runtime.py b/examples/summaries/cocoa/objc_runtime.py new file mode 100644 index 000000000000..8b5debccb824 --- /dev/null +++ b/examples/summaries/cocoa/objc_runtime.py @@ -0,0 +1,781 @@ +""" +Objective-C runtime wrapper for use by LLDB Python formatters + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" +import lldb +import lldb.formatters.cache +import lldb.formatters.attrib_fromdict +import functools +import lldb.formatters.Logger + +class Utilities: + @staticmethod + def read_ascii(process, pointer,max_len=128): + logger = lldb.formatters.Logger.Logger() + error = lldb.SBError() + content = None + try: + content = process.ReadCStringFromMemory(pointer,max_len,error) + except: + pass + if content is None or len(content) == 0 or error.fail: + return None + return content + + @staticmethod + def is_valid_pointer(pointer, pointer_size, allow_tagged=0, allow_NULL=0): + logger = lldb.formatters.Logger.Logger() + if pointer is None: + return 0 + if pointer == 0: + return allow_NULL + if allow_tagged and (pointer % 2) == 1: + return 1 + return ((pointer % pointer_size) == 0) + + # Objective-C runtime has a rule that pointers in a class_t will only have bits 0 thru 46 set + # so if any pointer has bits 47 thru 63 high we know that this is not a valid isa + @staticmethod + def is_allowed_pointer(pointer): + logger = lldb.formatters.Logger.Logger() + if pointer is None: + return 0 + return ((pointer & 0xFFFF800000000000) == 0) + + @staticmethod + def read_child_of(valobj,offset,type): + logger = lldb.formatters.Logger.Logger() + if offset == 0 and type.GetByteSize() == valobj.GetByteSize(): + return valobj.GetValueAsUnsigned() + child = valobj.CreateChildAtOffset("childUNK",offset,type) + if child is None or child.IsValid() == 0: + return None; + return child.GetValueAsUnsigned() + + @staticmethod + def is_valid_identifier(name): + logger = lldb.formatters.Logger.Logger() + if name is None: + return None + if len(name) == 0: + return None + # technically, the ObjC runtime does not enforce any rules about what name a class can have + # in practice, the commonly used byte values for a class name are the letters, digits and some + # symbols: $, %, -, _, . + # WARNING: this means that you cannot use this runtime implementation if you need to deal + # with class names that use anything but what is allowed here + ok_values = dict.fromkeys("$%_.-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890") + return all(c in ok_values for c in name) + + @staticmethod + def check_is_osx_lion(target): + logger = lldb.formatters.Logger.Logger() + # assume the only thing that has a Foundation.framework is a Mac + # assume anything < Lion does not even exist + try: + mod = target.module['Foundation'] + except: + mod = None + if mod is None or mod.IsValid() == 0: + return None + ver = mod.GetVersion() + if ver is None or ver == []: + return None + return (ver[0] < 900) + + # a utility method that factors out code common to almost all the formatters + # takes in an SBValue and a metrics object + # returns a class_data and a wrapper (or None, if the runtime alone can't decide on a wrapper) + @staticmethod + def prepare_class_detection(valobj,statistics): + logger = lldb.formatters.Logger.Logger() + class_data = ObjCRuntime(valobj) + if class_data.is_valid() == 0: + statistics.metric_hit('invalid_pointer',valobj) + wrapper = InvalidPointer_Description(valobj.GetValueAsUnsigned(0) == 0) + return class_data,wrapper + class_data = class_data.read_class_data() + if class_data.is_valid() == 0: + statistics.metric_hit('invalid_isa',valobj) + wrapper = InvalidISA_Description() + return class_data,wrapper + if class_data.is_kvo(): + class_data = class_data.get_superclass() + if class_data.class_name() == '_NSZombie_OriginalClass': + wrapper = ThisIsZombie_Description() + return class_data,wrapper + return class_data,None + + +class RoT_Data: + def __init__(self,rot_pointer,params): + logger = lldb.formatters.Logger.Logger() + if (Utilities.is_valid_pointer(rot_pointer.GetValueAsUnsigned(),params.pointer_size, allow_tagged=0)): + self.sys_params = params + self.valobj = rot_pointer + #self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t) + #self.instanceStart = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t) + self.instanceSize = None # lazy fetching + offset = 24 if self.sys_params.is_64_bit else 16 + #self.ivarLayoutPtr = Utilities.read_child_of(self.valobj,offset,self.sys_params.addr_ptr_type) + self.namePointer = Utilities.read_child_of(self.valobj,offset,self.sys_params.types_cache.addr_ptr_type) + self.valid = 1 # self.check_valid() + else: + logger >> "Marking as invalid - rot is invalid" + self.valid = 0 + if self.valid: + self.name = Utilities.read_ascii(self.valobj.GetTarget().GetProcess(),self.namePointer) + if not(Utilities.is_valid_identifier(self.name)): + logger >> "Marking as invalid - name is invalid" + self.valid = 0 + + # perform sanity checks on the contents of this class_ro_t + def check_valid(self): + self.valid = 1 + # misaligned pointers seem to be possible for this field + #if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=0)): + # self.valid = 0 + # pass + + def __str__(self): + logger = lldb.formatters.Logger.Logger() + return \ + "instanceSize = " + hex(self.instance_size()) + "\n" + \ + "namePointer = " + hex(self.namePointer) + " --> " + self.name + + def is_valid(self): + return self.valid + + def instance_size(self,align=0): + logger = lldb.formatters.Logger.Logger() + if self.is_valid() == 0: + return None + if self.instanceSize is None: + self.instanceSize = Utilities.read_child_of(self.valobj,8,self.sys_params.types_cache.uint32_t) + if align: + unalign = self.instance_size(0) + if self.sys_params.is_64_bit: + return ((unalign + 7) & ~7) % 0x100000000 + else: + return ((unalign + 3) & ~3) % 0x100000000 + else: + return self.instanceSize + +class RwT_Data: + def __init__(self,rwt_pointer,params): + logger = lldb.formatters.Logger.Logger() + if (Utilities.is_valid_pointer(rwt_pointer.GetValueAsUnsigned(),params.pointer_size, allow_tagged=0)): + self.sys_params = params + self.valobj = rwt_pointer + #self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t) + #self.version = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t) + self.roPointer = Utilities.read_child_of(self.valobj,8,self.sys_params.types_cache.addr_ptr_type) + self.check_valid() + else: + logger >> "Marking as invalid - rwt is invald" + self.valid = 0 + if self.valid: + self.rot = self.valobj.CreateValueFromData("rot",lldb.SBData.CreateDataFromUInt64Array(self.sys_params.endianness, self.sys_params.pointer_size, [self.roPointer]),self.sys_params.types_cache.addr_ptr_type) +# self.rot = self.valobj.CreateValueFromAddress("rot",self.roPointer,self.sys_params.types_cache.addr_ptr_type).AddressOf() + self.data = RoT_Data(self.rot,self.sys_params) + + # perform sanity checks on the contents of this class_rw_t + def check_valid(self): + logger = lldb.formatters.Logger.Logger() + self.valid = 1 + if not(Utilities.is_valid_pointer(self.roPointer,self.sys_params.pointer_size,allow_tagged=0)): + logger >> "Marking as invalid - ropointer is invalid" + self.valid = 0 + + def __str__(self): + logger = lldb.formatters.Logger.Logger() + return \ + "roPointer = " + hex(self.roPointer) + + def is_valid(self): + logger = lldb.formatters.Logger.Logger() + if self.valid: + return self.data.is_valid() + return 0 + +class Class_Data_V2: + def __init__(self,isa_pointer,params): + logger = lldb.formatters.Logger.Logger() + if (isa_pointer != None) and (Utilities.is_valid_pointer(isa_pointer.GetValueAsUnsigned(),params.pointer_size, allow_tagged=0)): + self.sys_params = params + self.valobj = isa_pointer + self.check_valid() + else: + logger >> "Marking as invalid - isa is invalid or None" + self.valid = 0 + if self.valid: + self.rwt = self.valobj.CreateValueFromData("rwt",lldb.SBData.CreateDataFromUInt64Array(self.sys_params.endianness, self.sys_params.pointer_size, [self.dataPointer]),self.sys_params.types_cache.addr_ptr_type) +# self.rwt = self.valobj.CreateValueFromAddress("rwt",self.dataPointer,self.sys_params.types_cache.addr_ptr_type).AddressOf() + self.data = RwT_Data(self.rwt,self.sys_params) + + # perform sanity checks on the contents of this class_t + # this call tries to minimize the amount of data fetched- as soon as we have "proven" + # that we have an invalid object, we stop reading + def check_valid(self): + logger = lldb.formatters.Logger.Logger() + self.valid = 1 + + self.isaPointer = Utilities.read_child_of(self.valobj,0,self.sys_params.types_cache.addr_ptr_type) + if not(Utilities.is_valid_pointer(self.isaPointer,self.sys_params.pointer_size,allow_tagged=0)): + logger >> "Marking as invalid - isaPointer is invalid" + self.valid = 0 + return + if not(Utilities.is_allowed_pointer(self.isaPointer)): + logger >> "Marking as invalid - isaPointer is not allowed" + self.valid = 0 + return + + self.cachePointer = Utilities.read_child_of(self.valobj,2*self.sys_params.pointer_size,self.sys_params.types_cache.addr_ptr_type) + if not(Utilities.is_valid_pointer(self.cachePointer,self.sys_params.pointer_size,allow_tagged=0)): + logger >> "Marking as invalid - cachePointer is invalid" + self.valid = 0 + return + if not(Utilities.is_allowed_pointer(self.cachePointer)): + logger >> "Marking as invalid - cachePointer is not allowed" + self.valid = 0 + return + self.dataPointer = Utilities.read_child_of(self.valobj,4*self.sys_params.pointer_size,self.sys_params.types_cache.addr_ptr_type) + if not(Utilities.is_valid_pointer(self.dataPointer,self.sys_params.pointer_size,allow_tagged=0)): + logger >> "Marking as invalid - dataPointer is invalid" + self.valid = 0 + return + if not(Utilities.is_allowed_pointer(self.dataPointer)): + logger >> "Marking as invalid - dataPointer is not allowed" + self.valid = 0 + return + + self.superclassIsaPointer = Utilities.read_child_of(self.valobj,1*self.sys_params.pointer_size,self.sys_params.types_cache.addr_ptr_type) + if not(Utilities.is_valid_pointer(self.superclassIsaPointer,self.sys_params.pointer_size,allow_tagged=0, allow_NULL=1)): + logger >> "Marking as invalid - superclassIsa is invalid" + self.valid = 0 + return + if not(Utilities.is_allowed_pointer(self.superclassIsaPointer)): + logger >> "Marking as invalid - superclassIsa is not allowed" + self.valid = 0 + return + + # in general, KVO is implemented by transparently subclassing + # however, there could be exceptions where a class does something else + # internally to implement the feature - this method will have no clue that a class + # has been KVO'ed unless the standard implementation technique is used + def is_kvo(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + if self.class_name().startswith("NSKVONotifying_"): + return 1 + return 0 + + # some CF classes have a valid ObjC isa in their CFRuntimeBase + # but instead of being class-specific this isa points to a match-'em-all class + # which is __NSCFType (the versions without __ also exists and we are matching to it + # just to be on the safe side) + def is_cftype(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + return self.class_name() == '__NSCFType' or self.class_name() == 'NSCFType' + + def get_superclass(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + parent_isa_pointer = self.valobj.CreateChildAtOffset("parent_isa", + self.sys_params.pointer_size, + self.sys_params.addr_ptr_type) + return Class_Data_V2(parent_isa_pointer,self.sys_params) + else: + return None + + def class_name(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + return self.data.data.name + else: + return None + + def is_valid(self): + logger = lldb.formatters.Logger.Logger() + if self.valid: + return self.data.is_valid() + return 0 + + def __str__(self): + logger = lldb.formatters.Logger.Logger() + return 'isaPointer = ' + hex(self.isaPointer) + "\n" + \ + "superclassIsaPointer = " + hex(self.superclassIsaPointer) + "\n" + \ + "cachePointer = " + hex(self.cachePointer) + "\n" + \ + "data = " + hex(self.dataPointer) + + def is_tagged(self): + return 0 + + def instance_size(self,align=0): + logger = lldb.formatters.Logger.Logger() + if self.is_valid() == 0: + return None + return self.rwt.rot.instance_size(align) + +# runtime v1 is much less intricate than v2 and stores relevant information directly in the class_t object +class Class_Data_V1: + def __init__(self,isa_pointer,params): + logger = lldb.formatters.Logger.Logger() + if (isa_pointer != None) and (Utilities.is_valid_pointer(isa_pointer.GetValueAsUnsigned(),params.pointer_size, allow_tagged=0)): + self.valid = 1 + self.sys_params = params + self.valobj = isa_pointer + self.check_valid() + else: + logger >> "Marking as invalid - isaPointer is invalid or None" + self.valid = 0 + if self.valid: + self.name = Utilities.read_ascii(self.valobj.GetTarget().GetProcess(),self.namePointer) + if not(Utilities.is_valid_identifier(self.name)): + logger >> "Marking as invalid - name is not valid" + self.valid = 0 + + # perform sanity checks on the contents of this class_t + def check_valid(self): + logger = lldb.formatters.Logger.Logger() + self.valid = 1 + + self.isaPointer = Utilities.read_child_of(self.valobj,0,self.sys_params.types_cache.addr_ptr_type) + if not(Utilities.is_valid_pointer(self.isaPointer,self.sys_params.pointer_size,allow_tagged=0)): + logger >> "Marking as invalid - isaPointer is invalid" + self.valid = 0 + return + + self.superclassIsaPointer = Utilities.read_child_of(self.valobj,1*self.sys_params.pointer_size,self.sys_params.types_cache.addr_ptr_type) + if not(Utilities.is_valid_pointer(self.superclassIsaPointer,self.sys_params.pointer_size,allow_tagged=0,allow_NULL=1)): + logger >> "Marking as invalid - superclassIsa is invalid" + self.valid = 0 + return + + self.namePointer = Utilities.read_child_of(self.valobj,2*self.sys_params.pointer_size,self.sys_params.types_cache.addr_ptr_type) + #if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=0,allow_NULL=0)): + # self.valid = 0 + # return + + # in general, KVO is implemented by transparently subclassing + # however, there could be exceptions where a class does something else + # internally to implement the feature - this method will have no clue that a class + # has been KVO'ed unless the standard implementation technique is used + def is_kvo(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + if self.class_name().startswith("NSKVONotifying_"): + return 1 + return 0 + + # some CF classes have a valid ObjC isa in their CFRuntimeBase + # but instead of being class-specific this isa points to a match-'em-all class + # which is __NSCFType (the versions without __ also exists and we are matching to it + # just to be on the safe side) + def is_cftype(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + return self.class_name() == '__NSCFType' or self.class_name() == 'NSCFType' + + def get_superclass(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + parent_isa_pointer = self.valobj.CreateChildAtOffset("parent_isa", + self.sys_params.pointer_size, + self.sys_params.addr_ptr_type) + return Class_Data_V1(parent_isa_pointer,self.sys_params) + else: + return None + + def class_name(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + return self.name + else: + return None + + def is_valid(self): + return self.valid + + def __str__(self): + logger = lldb.formatters.Logger.Logger() + return 'isaPointer = ' + hex(self.isaPointer) + "\n" + \ + "superclassIsaPointer = " + hex(self.superclassIsaPointer) + "\n" + \ + "namePointer = " + hex(self.namePointer) + " --> " + self.name + \ + "instanceSize = " + hex(self.instanceSize()) + "\n" + + def is_tagged(self): + return 0 + + def instance_size(self,align=0): + logger = lldb.formatters.Logger.Logger() + if self.is_valid() == 0: + return None + if self.instanceSize is None: + self.instanceSize = Utilities.read_child_of(self.valobj,5*self.sys_params.pointer_size,self.sys_params.types_cache.addr_ptr_type) + if align: + unalign = self.instance_size(0) + if self.sys_params.is_64_bit: + return ((unalign + 7) & ~7) % 0x100000000 + else: + return ((unalign + 3) & ~3) % 0x100000000 + else: + return self.instanceSize + +# these are the only tagged pointers values for current versions +# of OSX - they might change in future OS releases, and no-one is +# advised to rely on these values, or any of the bitmasking formulas +# in TaggedClass_Data. doing otherwise is at your own risk +TaggedClass_Values_Lion = {1 : 'NSNumber', \ + 5: 'NSManagedObject', \ + 6: 'NSDate', \ + 7: 'NSDateTS' }; +TaggedClass_Values_NMOS = {0: 'NSAtom', \ + 3 : 'NSNumber', \ + 4: 'NSDateTS', \ + 5: 'NSManagedObject', \ + 6: 'NSDate' }; + +class TaggedClass_Data: + def __init__(self,pointer,params): + logger = lldb.formatters.Logger.Logger() + global TaggedClass_Values_Lion,TaggedClass_Values_NMOS + self.valid = 1 + self.name = None + self.sys_params = params + self.valobj = pointer + self.val = (pointer & ~0x0000000000000000FF) >> 8 + self.class_bits = (pointer & 0xE) >> 1 + self.i_bits = (pointer & 0xF0) >> 4 + + if self.sys_params.is_lion: + if self.class_bits in TaggedClass_Values_Lion: + self.name = TaggedClass_Values_Lion[self.class_bits] + else: + logger >> "Marking as invalid - not a good tagged pointer for Lion" + self.valid = 0 + else: + if self.class_bits in TaggedClass_Values_NMOS: + self.name = TaggedClass_Values_NMOS[self.class_bits] + else: + logger >> "Marking as invalid - not a good tagged pointer for NMOS" + self.valid = 0 + + + def is_valid(self): + return self.valid + + def class_name(self): + logger = lldb.formatters.Logger.Logger() + if self.is_valid(): + return self.name + else: + return 0 + + def value(self): + return self.val if self.is_valid() else None + + def info_bits(self): + return self.i_bits if self.is_valid() else None + + def is_kvo(self): + return 0 + + def is_cftype(self): + return 0 + + # we would need to go around looking for the superclass or ask the runtime + # for now, we seem not to require support for this operation so we will merrily + # pretend to be at a root point in the hierarchy + def get_superclass(self): + return None + + # anything that is handled here is tagged + def is_tagged(self): + return 1 + + # it seems reasonable to say that a tagged pointer is the size of a pointer + def instance_size(self,align=0): + logger = lldb.formatters.Logger.Logger() + if self.is_valid() == 0: + return None + return self.sys_params.pointer_size + + +class InvalidClass_Data: + def __init__(self): + pass + def is_valid(self): + return 0 + + +class Version: + def __init__(self, major, minor, release, build_string): + self._major = major + self._minor = minor + self._release = release + self._build_string = build_string + + def get_major(self): + return self._major + def get_minor(self): + return self._minor + def get_release(self): + return self._release + def get_build_string(self): + return self._build_string + + major = property(get_major,None) + minor = property(get_minor,None) + release = property(get_release,None) + build_string = property(get_build_string,None) + + def __lt__(self,other): + if (self.major < other.major): + return 1 + if (self.minor < other.minor): + return 1 + if (self.release < other.release): + return 1 + # build strings are not compared since they are heavily platform-dependent and might not always + # be available + return 0 + + def __eq__(self,other): + return (self.major == other.major) and \ + (self.minor == other.minor) and \ + (self.release == other.release) and \ + (self.build_string == other.build_string) + + # Python 2.6 doesn't have functools.total_ordering, so we have to implement + # other comparators + def __gt__(self, other): + return other < self + + def __le__(self, other): + return not other < self + + def __ge__(self, other): + return not self < other + + +runtime_version = lldb.formatters.cache.Cache() +os_version = lldb.formatters.cache.Cache() +types_caches = lldb.formatters.cache.Cache() +isa_caches = lldb.formatters.cache.Cache() + +class SystemParameters: + def __init__(self,valobj): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture(valobj) + self.adjust_for_process(valobj) + + def adjust_for_process(self, valobj): + logger = lldb.formatters.Logger.Logger() + global runtime_version + global os_version + global types_caches + global isa_caches + + process = valobj.GetTarget().GetProcess() + self.pid = process.GetUniqueID() # using the unique ID for added guarantees (see svn revision 172628 for further details) + + if runtime_version.look_for_key(self.pid): + self.runtime_version = runtime_version.get_value(self.pid) + else: + self.runtime_version = ObjCRuntime.runtime_version(process) + runtime_version.add_item(self.pid,self.runtime_version) + + if os_version.look_for_key(self.pid): + self.is_lion = os_version.get_value(self.pid) + else: + self.is_lion = Utilities.check_is_osx_lion(valobj.GetTarget()) + os_version.add_item(self.pid,self.is_lion) + + if types_caches.look_for_key(self.pid): + self.types_cache = types_caches.get_value(self.pid) + else: + self.types_cache = lldb.formatters.attrib_fromdict.AttributesDictionary(allow_reset=0) + self.types_cache.addr_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + self.types_cache.addr_ptr_type = self.types_cache.addr_type.GetPointerType() + self.types_cache.uint32_t = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt) + types_caches.add_item(self.pid,self.types_cache) + + if isa_caches.look_for_key(self.pid): + self.isa_cache = isa_caches.get_value(self.pid) + else: + self.isa_cache = lldb.formatters.cache.Cache() + isa_caches.add_item(self.pid,self.isa_cache) + + def adjust_for_architecture(self,valobj): + process = valobj.GetTarget().GetProcess() + self.pointer_size = process.GetAddressByteSize() + self.is_64_bit = (self.pointer_size == 8) + self.endianness = process.GetByteOrder() + self.is_little = (self.endianness == lldb.eByteOrderLittle) + self.cfruntime_size = 16 if self.is_64_bit else 8 + + # a simple helper function that makes it more explicit that one is calculating + # an offset that is made up of X pointers and Y bytes of additional data + # taking into account pointer size - if you know there is going to be some padding + # you can pass that in and it will be taken into account (since padding may be different between + # 32 and 64 bit versions, you can pass padding value for both, the right one will be used) + def calculate_offset(self, num_pointers = 0, bytes_count = 0, padding32 = 0, padding64 = 0): + value = bytes_count + num_pointers*self.pointer_size + return value + padding64 if self.is_64_bit else value + padding32 + +class ObjCRuntime: + + # the ObjC runtime has no explicit "version" field that we can use + # instead, we discriminate v1 from v2 by looking for the presence + # of a well-known section only present in v1 + @staticmethod + def runtime_version(process): + logger = lldb.formatters.Logger.Logger() + if process.IsValid() == 0: + logger >> "No process - bailing out" + return None + target = process.GetTarget() + num_modules = target.GetNumModules() + module_objc = None + for idx in range(num_modules): + module = target.GetModuleAtIndex(idx) + if module.GetFileSpec().GetFilename() == 'libobjc.A.dylib': + module_objc = module + break + if module_objc is None or module_objc.IsValid() == 0: + logger >> "no libobjc - bailing out" + return None + num_sections = module.GetNumSections() + section_objc = None + for idx in range(num_sections): + section = module.GetSectionAtIndex(idx) + if section.GetName() == '__OBJC': + section_objc = section + break + if section_objc != None and section_objc.IsValid(): + logger >> "found __OBJC: v1" + return 1 + logger >> "no __OBJC: v2" + return 2 + + @staticmethod + def runtime_from_isa(isa): + logger = lldb.formatters.Logger.Logger() + runtime = ObjCRuntime(isa) + runtime.isa = isa + return runtime + + def __init__(self,valobj): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj + self.adjust_for_architecture() + self.sys_params = SystemParameters(self.valobj) + self.unsigned_value = self.valobj.GetValueAsUnsigned() + self.isa_value = None + + def adjust_for_architecture(self): + pass + +# an ObjC pointer can either be tagged or must be aligned + def is_tagged(self): + logger = lldb.formatters.Logger.Logger() + if self.valobj is None: + return 0 + return (Utilities.is_valid_pointer(self.unsigned_value,self.sys_params.pointer_size, allow_tagged=1) and \ + not(Utilities.is_valid_pointer(self.unsigned_value,self.sys_params.pointer_size, allow_tagged=0))) + + def is_valid(self): + logger = lldb.formatters.Logger.Logger() + if self.valobj is None: + return 0 + if self.valobj.IsInScope() == 0: + return 0 + return Utilities.is_valid_pointer(self.unsigned_value,self.sys_params.pointer_size, allow_tagged=1) + + def is_nil(self): + return self.unsigned_value == 0 + + def read_isa(self): + logger = lldb.formatters.Logger.Logger() + if self.isa_value != None: + logger >> "using cached isa" + return self.isa_value + self.isa_pointer = self.valobj.CreateChildAtOffset("cfisa", + 0, + self.sys_params.types_cache.addr_ptr_type) + if self.isa_pointer is None or self.isa_pointer.IsValid() == 0: + logger >> "invalid isa - bailing out" + return None; + self.isa_value = self.isa_pointer.GetValueAsUnsigned(1) + if self.isa_value == 1: + logger >> "invalid isa value - bailing out" + return None; + return Ellipsis + + def read_class_data(self): + logger = lldb.formatters.Logger.Logger() + global isa_cache + if self.is_tagged(): + # tagged pointers only exist in ObjC v2 + if self.sys_params.runtime_version == 2: + logger >> "on v2 and tagged - maybe" + # not every odd-valued pointer is actually tagged. most are just plain wrong + # we could try and predetect this before even creating a TaggedClass_Data object + # but unless performance requires it, this seems a cleaner way to tackle the task + tentative_tagged = TaggedClass_Data(self.unsigned_value,self.sys_params) + if tentative_tagged.is_valid(): + logger >> "truly tagged" + return tentative_tagged + else: + logger >> "not tagged - error" + return InvalidClass_Data() + else: + logger >> "on v1 and tagged - error" + return InvalidClass_Data() + if self.is_valid() == 0 or self.read_isa() is None: + return InvalidClass_Data() + data = self.sys_params.isa_cache.get_value(self.isa_value,default=None) + if data != None: + return data + if self.sys_params.runtime_version == 2: + data = Class_Data_V2(self.isa_pointer,self.sys_params) + else: + data = Class_Data_V1(self.isa_pointer,self.sys_params) + if data is None: + return InvalidClass_Data() + if data.is_valid(): + self.sys_params.isa_cache.add_item(self.isa_value,data,ok_to_replace=1) + return data + +# these classes below can be used by the data formatters to provide a consistent message that describes a given runtime-generated situation +class SpecialSituation_Description: + def message(self): + return '' + +class InvalidPointer_Description(SpecialSituation_Description): + + def __init__(self,nil): + self.is_nil = nil + + def message(self): + if self.is_nil: + return '@""' + else: + return '' + +class InvalidISA_Description(SpecialSituation_Description): + + def __init__(self): + pass + + def message(self): + return '' + +class ThisIsZombie_Description(SpecialSituation_Description): + def message(self): + return '' \ No newline at end of file diff --git a/examples/summaries/essentials b/examples/summaries/essentials new file mode 100644 index 000000000000..85e87e23457e --- /dev/null +++ b/examples/summaries/essentials @@ -0,0 +1,5 @@ +type summary add -s "${var._M_dataplus._M_p}" std::string std::basic_string "std::basic_string,std::allocator >" +type summary add -s "\"${var%@}\"" "NSString *" +type summary add -s "${svar%#} items" -e -x std::map< +type summary add -s "${svar%#} items" -e -x std::vector< +type summary add -s "${svar%#} items" -e -x std::list< diff --git a/examples/summaries/lldb b/examples/summaries/lldb new file mode 100644 index 000000000000..b6b2bf3d41c5 --- /dev/null +++ b/examples/summaries/lldb @@ -0,0 +1,28 @@ +type summary add -w lldb lldb_private::Error -s "Type: ${var.m_type%E}, Code: ${var.m_code}, Message: ${var.m_string}" +type summary add -w lldb lldb_private::ConstString -s "${var.m_string}" +type summary add -w lldb lldb_private::Language -s "${var.m_language%E}" +type summary add -w lldb lldb_private::RegularExpression -s "${var.m_re}" +type summary add -w lldb lldb_private::UserID -s "UserID(${var.m_uid})" +type summary add -w lldb lldb_private::ValueObject -s "${var.m_name}" +type summary add -w lldb lldb_private::ValueObjectSP -s "${var.ptr_.m_name}" +type summary add -w lldb lldb_private::ValueObjectRegister -s "${var.m_reg_info.name}" +type summary add -w lldb lldb_private::ClangExpression -s "{${var.m_expr_text}}" +type summary add -w lldb lldb_private::CommandObject -s "Command name: ${var.m_cmd_name}" +type summary add -w lldb lldb_private::Variable -s "${var.m_type.m_name} ${var.m_name}" +type summary add -w lldb lldb_private::StopInfo -s "ID: ${var.m_stop_id}, ${var.m_description}" +type summary add -w lldb lldb_private::FileSpec -s "file: ${var.m_filename%S} dir: ${var.m_directory%S}" +type summary add -w -v lldb lldb::ConnectionStatus -s "[enum=${var%E} val=${var%i}]" +# Where '-v' tells type summary not to show the value itself, but just use the summary format. + +type summary add -w lldb "lldb_private::ThreadSafeValue" -s "${var.m_value}" +type summary add -w lldb lldb_private::CompileUnit -s "file: ${var.m_filename%S} dir: ${var.m_directory%S}" +type summary add -w lldb "lldb_private::Module" -s "${var.m_file%S}" +type summary add -w lldb "lldb_private::ModuleSpec" -s "${var.m_file%S}" +type summary add -w lldb "lldb_private::ModuleList" -s "${var.m_modules%S}" +type summary add -w lldb "lldb::ModuleSP" -s "${var._M_ptr%S}" +type summary add -w lldb "lldb_private::Process" -s "Public: ${var.m_public_state%S} Private: ${var.m_private_state%S}" +type summary add -w lldb "DynamicLoaderMacOSXDYLD::DYLDImageInfo" -s "${var.file_spec%S}" + +type format add -f x lldb::addr_t + +type category enable lldb diff --git a/examples/summaries/objc.py b/examples/summaries/objc.py new file mode 100644 index 000000000000..75a4572add7a --- /dev/null +++ b/examples/summaries/objc.py @@ -0,0 +1,16 @@ +# Summaries for common ObjC types that require Python scripting +# to be generated fit into this file + +def BOOL_SummaryProvider (valobj,dict): + if not (valobj.IsValid()): + return "" + if valobj.GetValueAsUnsigned() == 0: + return "NO" + else: + return "YES" + +def BOOLRef_SummaryProvider (valobj, dict): + return BOOL_SummaryProvider (valobj.GetChildAtIndex(0),dict) + +def BOOLPtr_SummaryProvider (valobj,dict): + return BOOL_SummaryProvider (valobj.Dereference(),dict) diff --git a/examples/summaries/pysummary.py b/examples/summaries/pysummary.py new file mode 100644 index 000000000000..71414fdaeb78 --- /dev/null +++ b/examples/summaries/pysummary.py @@ -0,0 +1,18 @@ +import lldb + +def pyobj_summary (value,unused): + if value == None or value.IsValid() == False or value.GetValueAsUnsigned(0) == 0: + return "" + refcnt = value.GetChildMemberWithName("ob_refcnt") + expr = "(char*)PyString_AsString( (PyObject*)PyObject_Str( (PyObject*)0x%x) )" % (value.GetValueAsUnsigned(0)) + expr_summary = value.target.EvaluateExpression(expr,lldb.SBExpressionOptions()).GetSummary() + refcnt_value = "rc = %d" % (refcnt.GetValueAsUnsigned(0)) + return "%s (%s)" % (expr_summary,refcnt_value) + +def __lldb_init_module(debugger, unused): + debugger.HandleCommand("type summary add PyObject --python-function pysummary.pyobj_summary") + debugger.HandleCommand("type summary add lldb_private::PythonObject -s ${var.m_py_obj%S}") + debugger.HandleCommand("type summary add lldb_private::PythonDictionary -s ${var.m_py_obj%S}") + debugger.HandleCommand("type summary add lldb_private::PythonString -s ${var.m_py_obj%S}") + + diff --git a/examples/summaries/sp_cp.py b/examples/summaries/sp_cp.py new file mode 100644 index 000000000000..9aa0a2f4fb93 --- /dev/null +++ b/examples/summaries/sp_cp.py @@ -0,0 +1,61 @@ +""" +Summary and synthetic providers for LLDB-specific shared pointers + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" + +class SharedPtr_SyntheticChildrenProvider: + def __init__(self,valobj,dict): + self.valobj = valobj + self.update() + def update(self): + pass + def num_children(self): + return 1 + def get_child_index(self,name): + if name == "ptr": + return 0 + if name == "count": + return 1 + return None + def get_child_at_index(self,index): + if index == 0: + return self.valobj.GetChildMemberWithName('_M_ptr') + if index == 1: + return self.valobj.GetChildMemberWithName('_M_refcount').GetChildMemberWithName('_M_pi').GetChildMemberWithName('_M_use_count') + return None + +def SharedPtr_SummaryProvider (valobj,dict): + return 'use = ' + str(valobj.GetChildMemberWithName("count").GetValueAsUnsigned()) + +class ValueObjectSP_SyntheticChildrenProvider: + def __init__(self,valobj,dict): + self.valobj = valobj + self.update() + def update(self): + pass + def num_children(self): + return 1 + def get_child_index(self,name): + if name == "ptr": + return 0 + if name == "count": + return 1 + return None + def get_child_at_index(self,index): + if index == 0: + return self.valobj.GetChildMemberWithName('ptr_') + if index == 1: + return self.valobj.GetChildMemberWithName('cntrl_').GetChildMemberWithName('shared_owners_') + return None + +def ValueObjectSP_SummaryProvider (valobj,dict): + return 'use = ' + str(1 + valobj.GetChildMemberWithName("count").GetValueAsUnsigned()) + +def __lldb_init_module(debugger, dict): + debugger.HandleCommand('type summary add -x ".*ValueObjectSP" --expand -F sp_cp.ValueObjectSP_SummaryProvider') + debugger.HandleCommand('type synthetic add -x ".*ValueObjectSP" -l sp_cp.ValueObjectSP_SyntheticChildrenProvider') + debugger.HandleCommand('type summary add -x ".*SP" --expand -F sp_cp.SharedPtr_SummaryProvider') + debugger.HandleCommand('type synthetic add -x ".*SP" -l sp_cp.SharedPtr_SyntheticChildrenProvider') diff --git a/examples/summaries/unicode_strings.py b/examples/summaries/unicode_strings.py new file mode 100644 index 000000000000..319433ff3c15 --- /dev/null +++ b/examples/summaries/unicode_strings.py @@ -0,0 +1,48 @@ +""" +Example data formatters for strings represented as (pointer,length) pairs +encoded in UTF8/16/32 for use with the LLDB debugger + +To use in your projects, tweak the children names as appropriate for your data structures +and use as summaries for your data types + +part of The LLVM Compiler Infrastructure +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. +""" + +import lldb +def utf8_summary(value,unused): + pointer = value.GetChildMemberWithName("first").GetValueAsUnsigned(0) + length = value.GetChildMemberWithName("second").GetValueAsUnsigned(0) + if pointer == 0: + return False + if length == 0: + return '""' + error = lldb.SBError() + string_data = value.process.ReadMemory(pointer, length, error) + return '"%s"' % (string_data) # utf8 is safe to emit as-is on OSX + +def utf16_summary(value,unused): + pointer = value.GetChildMemberWithName("first").GetValueAsUnsigned(0) + length = value.GetChildMemberWithName("second").GetValueAsUnsigned(0) + # assume length is in bytes - if in UTF16 chars, just multiply by 2 + if pointer == 0: + return False + if length == 0: + return '""' + error = lldb.SBError() + string_data = value.process.ReadMemory(pointer, length, error) + return '"%s"' % (string_data.decode('utf-16').encode('utf-8')) # utf8 is safe to emit as-is on OSX + +def utf32_summary(value,unused): + pointer = value.GetChildMemberWithName("first").GetValueAsUnsigned(0) + length = value.GetChildMemberWithName("second").GetValueAsUnsigned(0) + # assume length is in bytes - if in UTF32 chars, just multiply by 4 + if pointer == 0: + return False + if length == 0: + return '""' + error = lldb.SBError() + string_data = value.process.ReadMemory(pointer, length, error) + return '"%s"' % (string_data.decode('utf-32').encode('utf-8')) # utf8 is safe to emit as-is on OSX + diff --git a/examples/synthetic/bitfield/example.py b/examples/synthetic/bitfield/example.py new file mode 100644 index 000000000000..7995919a4904 --- /dev/null +++ b/examples/synthetic/bitfield/example.py @@ -0,0 +1,100 @@ +# Synthetic children provider example for class MaskedData +# to use me: +# command script import ./example.py --allow-reload +# type synthetic add MaskedData --python-class example.MaskedData_SyntheticChildrenProvider +class MaskedData_SyntheticChildrenProvider: + def __init__(self, valobj, dict): + self.valobj = valobj # remember the SBValue since you will not have another chance to get it :-) + + def num_children(self): + # you could perform calculations involving the SBValue and/or its children to determine this value + # here, we have an hardcoded value - but since you have stored the SBValue you could use it to + # help figure out the correct thing to return here. if you return a number N, you should be prepared to + # answer questions about N children + return 4 + + def has_children(self): + # we simply say True here because we know we have 4 children + # in general, you want to make this calculation as simple as possible + # and return True if in doubt (you can always return num_children == 0 later) + return True + + def get_child_index(self,name): + # given a name, return its index + # you can return None if you don't know the answer for a given name + if name == "value": + return 0 + # here, we are using a reserved C++ keyword as a child name - we could not do that in the source code + # but we are free to use the names we like best in the synthetic children provider class + # we are also not respecting the order of declaration in the C++ class itself - as long as + # we are consistent, we can do that freely + if name == "operator": + return 1 + if name == "mask": + return 2 + # this member does not exist in the original class - we will compute its value and show it to the user + # when returning synthetic children, there is no need to only stick to what already exists in memory + if name == "apply()": + return 3 + return None # no clue, just say none + + def get_child_at_index(self,index): + # precautionary measures + if index < 0: + return None + if index > self.num_children(): + return None + if self.valobj.IsValid() == False: + return None + if index == 0: + return self.valobj.GetChildMemberWithName("value") + if index == 1: + # fetch the value of the operator + op_chosen = self.valobj.GetChildMemberWithName("oper").GetValueAsUnsigned() + # if it is a known value, return a descriptive string for it + # we are not doing this in the most efficient possible way, but the code is very readable + # and easy to maintain - if you change the values on the C++ side, the same changes must be made here + if op_chosen == 0: + return self.valobj.CreateValueFromExpression("operator",'(const char*)"none"') + elif op_chosen == 1: + return self.valobj.CreateValueFromExpression("operator",'(const char*)"AND"') + elif op_chosen == 2: + return self.valobj.CreateValueFromExpression("operator",'(const char*)"OR"') + elif op_chosen == 3: + return self.valobj.CreateValueFromExpression("operator",'(const char*)"XOR"') + elif op_chosen == 4: + return self.valobj.CreateValueFromExpression("operator",'(const char*)"NAND"') + elif op_chosen == 5: + return self.valobj.CreateValueFromExpression("operator",'(const char*)"NOR"') + else: + return self.valobj.CreateValueFromExpression("operator",'(const char*)"unknown"') # something else + if index == 2: + return self.valobj.GetChildMemberWithName("mask") + if index == 3: + # for this, we must fetch all the other elements + # in an efficient implementation, we would be caching this data for efficiency + value = self.valobj.GetChildMemberWithName("value").GetValueAsUnsigned() + operator = self.valobj.GetChildMemberWithName("oper").GetValueAsUnsigned() + mask = self.valobj.GetChildMemberWithName("mask").GetValueAsUnsigned() + # compute the masked value according to the operator + if operator == 1: + value = value & mask + elif operator == 2: + value = value | mask + elif operator == 3: + value = value ^ mask + elif operator == 4: + value = ~(value & mask) + elif operator == 5: + value = ~(value | mask) + else: + pass + value &= 0xFFFFFFFF # make sure Python does not extend our values to 64-bits + # return it - again, not the most efficient possible way. we should actually be pushing the computed value + # into an SBData, and using the SBData to create an SBValue - this has the advantage of readability + return self.valobj.CreateValueFromExpression("apply()",'(uint32_t)(' + str(value) + ')') + + def update(self): + # we do not do anything special in update - but this would be the right place to lookup + # the data we use in get_child_at_index and cache it + pass diff --git a/examples/synthetic/bitfield/program.cpp b/examples/synthetic/bitfield/program.cpp new file mode 100644 index 000000000000..5276824a2fb4 --- /dev/null +++ b/examples/synthetic/bitfield/program.cpp @@ -0,0 +1,74 @@ +typedef unsigned int uint32_t; + +enum MaskingOperator +{ + eMaskingOperatorDefault = 0, + eMaskingOperatorAnd = 1, + eMaskingOperatorOr = 2, + eMaskingOperatorXor = 3, + eMaskingOperatorNand = 4, + eMaskingOperatorNor = 5 +}; + +class MaskedData +{ +private: + uint32_t value; + uint32_t mask; + MaskingOperator oper; +public: + MaskedData( uint32_t V = 0, + uint32_t M = 0, + MaskingOperator P = eMaskingOperatorDefault) : + value(V), + mask(M), + oper(P) + { + } + + uint32_t apply() + { + switch(oper) + { + case eMaskingOperatorAnd: + return value & mask; + case eMaskingOperatorOr: + return value | mask; + case eMaskingOperatorXor: + return value ^ mask; + case eMaskingOperatorNand: + return ~(value & mask); + case eMaskingOperatorNor: + return ~(value | mask); + case eMaskingOperatorDefault: // fall through + default: + return value; + } + } + + void setValue(uint32_t V) + { + value = V; + } + + void setMask (uint32_t M) + { + mask = M; + } + + void setOperator(MaskingOperator P) + { + oper = P; + } +}; + +int main() +{ + MaskedData data_1(0xFF0F,0xA01F,eMaskingOperatorAnd); + MaskedData data_2(data_1.apply(),0x1AFC,eMaskingOperatorXor); + MaskedData data_3(data_2.apply(),0xFFCF,eMaskingOperatorOr); + MaskedData data_4(data_3.apply(),0xAABC,eMaskingOperatorAnd); + MaskedData data_5(data_4.apply(),0xFFAC,eMaskingOperatorNor); + MaskedData data_6(data_5.apply(),0x0000BEEF,eMaskingOperatorAnd); + return data_6.apply(); // <-- what comes out of here? +} \ No newline at end of file diff --git a/examples/synthetic/gnu_libstdcpp.py b/examples/synthetic/gnu_libstdcpp.py new file mode 100644 index 000000000000..b6bf42235acd --- /dev/null +++ b/examples/synthetic/gnu_libstdcpp.py @@ -0,0 +1,451 @@ +import re +import lldb.formatters.Logger + +# C++ STL formatters for LLDB +# These formatters are based upon the version of the GNU libstdc++ +# as it ships with Mac OS X 10.6.8 thru 10.8.0 +# You are encouraged to look at the STL implementation for your platform +# before relying on these formatters to do the right thing for your setup + +class StdListSynthProvider: + + def __init__(self, valobj, dict): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj + self.count = None + logger >> "Providing synthetic children for a list named " + str(valobj.GetName()) + + def next_node(self,node): + logger = lldb.formatters.Logger.Logger() + return node.GetChildMemberWithName('_M_next') + + def is_valid(self,node): + logger = lldb.formatters.Logger.Logger() + valid = self.value(self.next_node(node)) != self.node_address + if valid: + logger >> "%s is valid" % str(self.valobj.GetName()) + else: + logger >> "synthetic value is not valid" + return valid + + def value(self,node): + logger = lldb.formatters.Logger.Logger() + value = node.GetValueAsUnsigned() + logger >> "synthetic value for {}: {}".format(str(self.valobj.GetName()), value) + return value + + # Floyd's cycle-finding algorithm + # try to detect if this list has a loop + def has_loop(self): + global _list_uses_loop_detector + logger = lldb.formatters.Logger.Logger() + if _list_uses_loop_detector == False: + logger >> "Asked not to use loop detection" + return False + slow = self.next + fast1 = self.next + fast2 = self.next + while self.is_valid(slow): + slow_value = self.value(slow) + fast1 = self.next_node(fast2) + fast2 = self.next_node(fast1) + if self.value(fast1) == slow_value or self.value(fast2) == slow_value: + return True + slow = self.next_node(slow) + return False + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + if self.count is None: + # libstdc++ 6.0.21 added dedicated count field. + count_child = self.node.GetChildMemberWithName('_M_data') + if count_child and count_child.IsValid(): + self.count = count_child.GetValueAsUnsigned(0) + if self.count is None: + self.count = self.num_children_impl() + return self.count + + def num_children_impl(self): + logger = lldb.formatters.Logger.Logger() + try: + next_val = self.next.GetValueAsUnsigned(0) + prev_val = self.prev.GetValueAsUnsigned(0) + # After a std::list has been initialized, both next and prev will be non-NULL + if next_val == 0 or prev_val == 0: + return 0 + if next_val == self.node_address: + return 0 + if next_val == prev_val: + return 1 + if self.has_loop(): + return 0 + size = 2 + current = self.next + while current.GetChildMemberWithName('_M_next').GetValueAsUnsigned(0) != self.node_address: + size = size + 1 + current = current.GetChildMemberWithName('_M_next') + return (size - 1) + except: + return 0; + + def get_child_index(self,name): + logger = lldb.formatters.Logger.Logger() + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self,index): + logger = lldb.formatters.Logger.Logger() + logger >> "Fetching child " + str(index) + if index < 0: + return None; + if index >= self.num_children(): + return None; + try: + offset = index + current = self.next + while offset > 0: + current = current.GetChildMemberWithName('_M_next') + offset = offset - 1 + return current.CreateChildAtOffset('['+str(index)+']',2*current.GetType().GetByteSize(),self.data_type) + except: + return None + + def extract_type(self): + logger = lldb.formatters.Logger.Logger() + list_type = self.valobj.GetType().GetUnqualifiedType() + if list_type.IsReferenceType(): + list_type = list_type.GetDereferencedType() + if list_type.GetNumberOfTemplateArguments() > 0: + data_type = list_type.GetTemplateArgumentType(0) + else: + data_type = None + return data_type + + def update(self): + logger = lldb.formatters.Logger.Logger() + # preemptively setting this to None - we might end up changing our mind later + self.count = None + try: + impl = self.valobj.GetChildMemberWithName('_M_impl') + self.node = impl.GetChildMemberWithName('_M_node') + self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) + self.next = self.node.GetChildMemberWithName('_M_next') + self.prev = self.node.GetChildMemberWithName('_M_prev') + self.data_type = self.extract_type() + self.data_size = self.data_type.GetByteSize() + except: + pass + + def has_children(self): + return True + +class StdVectorSynthProvider: + + class StdVectorImplementation(object): + def __init__(self, valobj): + self.valobj = valobj + self.count = None + + def num_children(self): + if self.count == None: + self.count = self.num_children_impl() + return self.count + + def num_children_impl(self): + try: + start_val = self.start.GetValueAsUnsigned(0) + finish_val = self.finish.GetValueAsUnsigned(0) + end_val = self.end.GetValueAsUnsigned(0) + # Before a vector has been constructed, it will contain bad values + # so we really need to be careful about the length we return since + # uninitialized data can cause us to return a huge number. We need + # to also check for any of the start, finish or end of storage values + # being zero (NULL). If any are, then this vector has not been + # initialized yet and we should return zero + + # Make sure nothing is NULL + if start_val == 0 or finish_val == 0 or end_val == 0: + return 0 + # Make sure start is less than finish + if start_val >= finish_val: + return 0 + # Make sure finish is less than or equal to end of storage + if finish_val > end_val: + return 0 + + # if we have a struct (or other data type that the compiler pads to native word size) + # this check might fail, unless the sizeof() we get is itself incremented to take the + # padding bytes into account - on current clang it looks like this is the case + num_children = (finish_val-start_val) + if (num_children % self.data_size) != 0: + return 0 + else: + num_children = num_children/self.data_size + return num_children + except: + return 0; + + def get_child_at_index(self, index): + logger = lldb.formatters.Logger.Logger() + logger >> "Retrieving child " + str(index) + if index < 0: + return None; + if index >= self.num_children(): + return None; + try: + offset = index * self.data_size + return self.start.CreateChildAtOffset('['+str(index)+']',offset,self.data_type) + except: + return None + + def update(self): + # preemptively setting this to None - we might end up changing our mind later + self.count = None + try: + impl = self.valobj.GetChildMemberWithName('_M_impl') + self.start = impl.GetChildMemberWithName('_M_start') + self.finish = impl.GetChildMemberWithName('_M_finish') + self.end = impl.GetChildMemberWithName('_M_end_of_storage') + self.data_type = self.start.GetType().GetPointeeType() + self.data_size = self.data_type.GetByteSize() + # if any of these objects is invalid, it means there is no point in trying to fetch anything + if self.start.IsValid() and self.finish.IsValid() and self.end.IsValid() and self.data_type.IsValid(): + self.count = None + else: + self.count = 0 + except: + pass + return True + + class StdVBoolImplementation(object): + def __init__(self, valobj, bool_type): + self.valobj = valobj + self.bool_type = bool_type + self.valid = False + + def num_children(self): + if self.valid: + start = self.start_p.GetValueAsUnsigned(0) + finish = self.finish_p.GetValueAsUnsigned(0) + offset = self.offset.GetValueAsUnsigned(0) + if finish >= start: + return (finish - start) * 8 + offset + return 0 + + def get_child_at_index(self, index): + if index >= self.num_children(): + return None + byte_offset = index / 8 + bit_offset = index % 8 + element_size = self.start_p.GetType().GetPointeeType().GetByteSize() + data = self.start_p.GetPointeeData(byte_offset / element_size) + bit = data.GetUnsignedInt8(lldb.SBError(), byte_offset % element_size) & (1 << bit_offset) + if bit != 0: + value_expr = "(bool)true" + else: + value_expr = "(bool)false" + return self.valobj.CreateValueFromExpression("[%d]" % index, value_expr) + + def update(self): + try: + m_impl = self.valobj.GetChildMemberWithName('_M_impl') + self.m_start = m_impl.GetChildMemberWithName('_M_start') + self.m_finish = m_impl.GetChildMemberWithName('_M_finish') + self.start_p = self.m_start.GetChildMemberWithName('_M_p') + self.finish_p = self.m_finish.GetChildMemberWithName('_M_p') + self.offset = self.m_finish.GetChildMemberWithName('_M_offset') + self.valid = True + except: + self.valid = False + return True + + def __init__(self, valobj, dict): + logger = lldb.formatters.Logger.Logger() + first_template_arg_type = valobj.GetType().GetTemplateArgumentType(0) + if str(first_template_arg_type.GetName()) == "bool": + self.impl = self.StdVBoolImplementation(valobj, first_template_arg_type) + else: + self.impl = self.StdVectorImplementation(valobj) + logger >> "Providing synthetic children for a vector named " + str(valobj.GetName()) + + def num_children(self): + return self.impl.num_children() + + def get_child_index(self,name): + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self, index): + return self.impl.get_child_at_index(index) + + def update(self): + return self.impl.update() + + def has_children(self): + return True + + +class StdMapSynthProvider: + + def __init__(self, valobj, dict): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.count = None + logger >> "Providing synthetic children for a map named " + str(valobj.GetName()) + + # we need this function as a temporary workaround for rdar://problem/10801549 + # which prevents us from extracting the std::pair SBType out of the template + # arguments for _Rep_Type _M_t in the map itself - because we have to make up the + # typename and then find it, we may hit the situation were std::string has multiple + # names but only one is actually referenced in the debug information. hence, we need + # to replace the longer versions of std::string with the shorter one in order to be able + # to find the type name + def fixup_class_name(self, class_name): + logger = lldb.formatters.Logger.Logger() + if class_name == 'std::basic_string, std::allocator >': + return 'std::basic_string',True + if class_name == 'basic_string, std::allocator >': + return 'std::basic_string',True + if class_name == 'std::basic_string, std::allocator >': + return 'std::basic_string',True + if class_name == 'basic_string, std::allocator >': + return 'std::basic_string',True + return class_name,False + + def update(self): + logger = lldb.formatters.Logger.Logger() + # preemptively setting this to None - we might end up changing our mind later + self.count = None + try: + # we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree + # if this gets set to True, then we will merrily return None for any child from that moment on + self.garbage = False + self.Mt = self.valobj.GetChildMemberWithName('_M_t') + self.Mimpl = self.Mt.GetChildMemberWithName('_M_impl') + self.Mheader = self.Mimpl.GetChildMemberWithName('_M_header') + + map_type = self.valobj.GetType() + if map_type.IsReferenceType(): + logger >> "Dereferencing type" + map_type = map_type.GetDereferencedType() + + # Get the type of std::pair. It is the first template + # argument type of the 4th template argument to std::map. + allocator_type = map_type.GetTemplateArgumentType(3) + self.data_type = allocator_type.GetTemplateArgumentType(0) + if not self.data_type: + # GCC does not emit DW_TAG_template_type_parameter for + # std::allocator<...>. For such a case, get the type of + # std::pair from a member of std::map. + rep_type = self.valobj.GetChildMemberWithName('_M_t').GetType() + self.data_type = rep_type.GetTypedefedType().GetTemplateArgumentType(1) + + # from libstdc++ implementation of _M_root for rbtree + self.Mroot = self.Mheader.GetChildMemberWithName('_M_parent') + self.data_size = self.data_type.GetByteSize() + self.skip_size = self.Mheader.GetType().GetByteSize() + except: + pass + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + if self.count == None: + self.count = self.num_children_impl() + return self.count + + def num_children_impl(self): + logger = lldb.formatters.Logger.Logger() + try: + root_ptr_val = self.node_ptr_value(self.Mroot) + if root_ptr_val == 0: + return 0; + count = self.Mimpl.GetChildMemberWithName('_M_node_count').GetValueAsUnsigned(0) + logger >> "I have " + str(count) + " children available" + return count + except: + return 0; + + def get_child_index(self,name): + logger = lldb.formatters.Logger.Logger() + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self,index): + logger = lldb.formatters.Logger.Logger() + logger >> "Being asked to fetch child[" + str(index) + "]" + if index < 0: + return None + if index >= self.num_children(): + return None; + if self.garbage: + logger >> "Returning None since we are a garbage tree" + return None + try: + offset = index + current = self.left(self.Mheader); + while offset > 0: + current = self.increment_node(current) + offset = offset - 1; + # skip all the base stuff and get at the data + return current.CreateChildAtOffset('['+str(index)+']',self.skip_size,self.data_type) + except: + return None + + # utility functions + def node_ptr_value(self,node): + logger = lldb.formatters.Logger.Logger() + return node.GetValueAsUnsigned(0) + + def right(self,node): + logger = lldb.formatters.Logger.Logger() + return node.GetChildMemberWithName("_M_right"); + + def left(self,node): + logger = lldb.formatters.Logger.Logger() + return node.GetChildMemberWithName("_M_left"); + + def parent(self,node): + logger = lldb.formatters.Logger.Logger() + return node.GetChildMemberWithName("_M_parent"); + + # from libstdc++ implementation of iterator for rbtree + def increment_node(self,node): + logger = lldb.formatters.Logger.Logger() + max_steps = self.num_children() + if self.node_ptr_value(self.right(node)) != 0: + x = self.right(node); + max_steps -= 1 + while self.node_ptr_value(self.left(x)) != 0: + x = self.left(x); + max_steps -= 1 + logger >> str(max_steps) + " more to go before giving up" + if max_steps <= 0: + self.garbage = True + return None + return x; + else: + x = node; + y = self.parent(x) + max_steps -= 1 + while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))): + x = y; + y = self.parent(y); + max_steps -= 1 + logger >> str(max_steps) + " more to go before giving up" + if max_steps <= 0: + self.garbage = True + return None + if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y): + x = y; + return x; + + def has_children(self): + return True + +_list_uses_loop_detector = True diff --git a/examples/synthetic/libcxx.py b/examples/synthetic/libcxx.py new file mode 100644 index 000000000000..6623fea097c4 --- /dev/null +++ b/examples/synthetic/libcxx.py @@ -0,0 +1,787 @@ +import lldb +import lldb.formatters.Logger + +# libcxx STL formatters for LLDB +# These formatters are based upon the implementation of libc++ that +# ships with current releases of OS X - They will not work for other implementations +# of the standard C++ library - and they are bound to use the libc++-specific namespace + +# the std::string summary is just an example for your convenience +# the actual summary that LLDB uses is C++ code inside the debugger's own core + +# this could probably be made more efficient but since it only reads a handful of bytes at a time +# we probably don't need to worry too much about this for the time being +def make_string(F,L): + strval = '' + G = F.GetData().uint8 + for X in range(L): + V = G[X] + if V == 0: + break + strval = strval + chr(V % 256) + return '"' + strval + '"' + +# if we ever care about big-endian, these two functions might need to change +def is_short_string(value): + return True if (value & 1) == 0 else False +def extract_short_size(value): + return ((value >> 1) % 256) + +# some of the members of libc++ std::string are anonymous or have internal names that convey +# no external significance - we access them by index since this saves a name lookup that would add +# no information for readers of the code, but when possible try to use meaningful variable names +def stdstring_SummaryProvider(valobj,dict): + logger = lldb.formatters.Logger.Logger() + r = valobj.GetChildAtIndex(0) + B = r.GetChildAtIndex(0) + first = B.GetChildAtIndex(0) + D = first.GetChildAtIndex(0) + l = D.GetChildAtIndex(0) + s = D.GetChildAtIndex(1) + D20 = s.GetChildAtIndex(0) + size_mode = D20.GetChildAtIndex(0).GetValueAsUnsigned(0) + if is_short_string(size_mode): + size = extract_short_size(size_mode) + return make_string(s.GetChildAtIndex(1),size) + else: + data_ptr = l.GetChildAtIndex(2) + size_vo = l.GetChildAtIndex(1) + size = size_vo.GetValueAsUnsigned(0)+1 # the NULL terminator must be accounted for + if size <= 1 or size == None: # should never be the case + return '""' + try: + data = data_ptr.GetPointeeData(0,size) + except: + return '""' + error = lldb.SBError() + strval = data.GetString(error,0) + if error.Fail(): + return '' + else: + return '"' + strval + '"' + +class stdvector_SynthProvider: + + def __init__(self, valobj, dict): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + + def num_children(self): + logger = lldb.formatters.Logger.Logger() + try: + start_val = self.start.GetValueAsUnsigned(0) + finish_val = self.finish.GetValueAsUnsigned(0) + # Before a vector has been constructed, it will contain bad values + # so we really need to be careful about the length we return since + # uninitialized data can cause us to return a huge number. We need + # to also check for any of the start, finish or end of storage values + # being zero (NULL). If any are, then this vector has not been + # initialized yet and we should return zero + + # Make sure nothing is NULL + if start_val == 0 or finish_val == 0: + return 0 + # Make sure start is less than finish + if start_val >= finish_val: + return 0 + + num_children = (finish_val-start_val) + if (num_children % self.data_size) != 0: + return 0 + else: + num_children = num_children/self.data_size + return num_children + except: + return 0; + + def get_child_index(self,name): + logger = lldb.formatters.Logger.Logger() + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self,index): + logger = lldb.formatters.Logger.Logger() + logger >> "Retrieving child " + str(index) + if index < 0: + return None; + if index >= self.num_children(): + return None; + try: + offset = index * self.data_size + return self.start.CreateChildAtOffset('['+str(index)+']',offset,self.data_type) + except: + return None + + def update(self): + logger = lldb.formatters.Logger.Logger() + try: + self.start = self.valobj.GetChildMemberWithName('__begin_') + self.finish = self.valobj.GetChildMemberWithName('__end_') + # the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector + # if this ends up not being correct, we can use the APIs to get at template arguments + data_type_finder = self.valobj.GetChildMemberWithName('__end_cap_').GetChildMemberWithName('__first_') + self.data_type = data_type_finder.GetType().GetPointeeType() + self.data_size = self.data_type.GetByteSize() + except: + pass + + def has_children(self): + return True + +# Just an example: the actual summary is produced by a summary string: size=${svar%#} +def stdvector_SummaryProvider(valobj,dict): + prov = stdvector_SynthProvider(valobj,None) + return 'size=' + str(prov.num_children()) + +class stdlist_entry: + + def __init__(self,entry): + logger = lldb.formatters.Logger.Logger() + self.entry = entry + + def _next_impl(self): + logger = lldb.formatters.Logger.Logger() + return stdlist_entry(self.entry.GetChildMemberWithName('__next_')) + + def _prev_impl(self): + logger = lldb.formatters.Logger.Logger() + return stdlist_entry(self.entry.GetChildMemberWithName('__prev_')) + + def _value_impl(self): + logger = lldb.formatters.Logger.Logger() + return self.entry.GetValueAsUnsigned(0) + + def _isnull_impl(self): + logger = lldb.formatters.Logger.Logger() + return self._value_impl() == 0 + + def _sbvalue_impl(self): + logger = lldb.formatters.Logger.Logger() + return self.entry + + next = property(_next_impl,None) + value = property(_value_impl,None) + is_null = property(_isnull_impl,None) + sbvalue = property(_sbvalue_impl,None) + +class stdlist_iterator: + + def increment_node(self,node): + logger = lldb.formatters.Logger.Logger() + if node.is_null: + return None + return node.next + + def __init__(self,node): + logger = lldb.formatters.Logger.Logger() + self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry + + def value(self): + logger = lldb.formatters.Logger.Logger() + return self.node.sbvalue # and return the SBValue back on exit + + def next(self): + logger = lldb.formatters.Logger.Logger() + node = self.increment_node(self.node) + if node != None and node.sbvalue.IsValid() and not(node.is_null): + self.node = node + return self.value() + else: + return None + + def advance(self,N): + logger = lldb.formatters.Logger.Logger() + if N < 0: + return None + if N == 0: + return self.value() + if N == 1: + return self.next() + while N > 0: + self.next() + N = N - 1 + return self.value() + + +class stdlist_SynthProvider: + def __init__(self, valobj, dict): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj + self.count = None + + def next_node(self,node): + logger = lldb.formatters.Logger.Logger() + return node.GetChildMemberWithName('__next_') + + def value(self,node): + logger = lldb.formatters.Logger.Logger() + return node.GetValueAsUnsigned() + + # Floyd's cycle-finding algorithm + # try to detect if this list has a loop + def has_loop(self): + global _list_uses_loop_detector + logger = lldb.formatters.Logger.Logger() + if _list_uses_loop_detector == False: + logger >> "Asked not to use loop detection" + return False + slow = stdlist_entry(self.head) + fast1 = stdlist_entry(self.head) + fast2 = stdlist_entry(self.head) + while slow.next.value != self.node_address: + slow_value = slow.value + fast1 = fast2.next + fast2 = fast1.next + if fast1.value == slow_value or fast2.value == slow_value: + return True + slow = slow.next + return False + + def num_children(self): + global _list_capping_size + logger = lldb.formatters.Logger.Logger() + if self.count == None: + self.count = self.num_children_impl() + if self.count > _list_capping_size: + self.count = _list_capping_size + return self.count + + def num_children_impl(self): + global _list_capping_size + logger = lldb.formatters.Logger.Logger() + try: + next_val = self.head.GetValueAsUnsigned(0) + prev_val = self.tail.GetValueAsUnsigned(0) + # After a std::list has been initialized, both next and prev will be non-NULL + if next_val == 0 or prev_val == 0: + return 0 + if next_val == self.node_address: + return 0 + if next_val == prev_val: + return 1 + if self.has_loop(): + return 0 + size = 2 + current = stdlist_entry(self.head) + while current.next.value != self.node_address: + size = size + 1 + current = current.next + if size > _list_capping_size: + return _list_capping_size + return (size - 1) + except: + return 0; + + def get_child_index(self,name): + logger = lldb.formatters.Logger.Logger() + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self,index): + logger = lldb.formatters.Logger.Logger() + logger >> "Fetching child " + str(index) + if index < 0: + return None; + if index >= self.num_children(): + return None; + try: + current = stdlist_iterator(self.head) + current = current.advance(index) + # we do not return __value_ because then all our children would be named __value_ + # we need to make a copy of __value__ with the right name - unfortunate + obj = current.GetChildMemberWithName('__value_') + obj_data = obj.GetData() + return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type) + except: + return None + + def extract_type(self): + logger = lldb.formatters.Logger.Logger() + list_type = self.valobj.GetType().GetUnqualifiedType() + if list_type.IsReferenceType(): + list_type = list_type.GetDereferencedType() + if list_type.GetNumberOfTemplateArguments() > 0: + data_type = list_type.GetTemplateArgumentType(0) + else: + data_type = None + return data_type + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.count = None + try: + impl = self.valobj.GetChildMemberWithName('__end_') + self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) + self.head = impl.GetChildMemberWithName('__next_') + self.tail = impl.GetChildMemberWithName('__prev_') + self.data_type = self.extract_type() + self.data_size = self.data_type.GetByteSize() + except: + pass + + def has_children(self): + return True + + +# Just an example: the actual summary is produced by a summary string: size=${svar%#} +def stdlist_SummaryProvider(valobj,dict): + prov = stdlist_SynthProvider(valobj,None) + return 'size=' + str(prov.num_children()) + +# a tree node - this class makes the syntax in the actual iterator nicer to read and maintain +class stdmap_iterator_node: + def _left_impl(self): + logger = lldb.formatters.Logger.Logger() + return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_")) + + def _right_impl(self): + logger = lldb.formatters.Logger.Logger() + return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_")) + + def _parent_impl(self): + logger = lldb.formatters.Logger.Logger() + return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_")) + + def _value_impl(self): + logger = lldb.formatters.Logger.Logger() + return self.node.GetValueAsUnsigned(0) + + def _sbvalue_impl(self): + logger = lldb.formatters.Logger.Logger() + return self.node + + def _null_impl(self): + logger = lldb.formatters.Logger.Logger() + return self.value == 0 + + def __init__(self,node): + logger = lldb.formatters.Logger.Logger() + self.node = node + + left = property(_left_impl,None) + right = property(_right_impl,None) + parent = property(_parent_impl,None) + value = property(_value_impl,None) + is_null = property(_null_impl,None) + sbvalue = property(_sbvalue_impl,None) + +# a Python implementation of the tree iterator used by libc++ +class stdmap_iterator: + + def tree_min(self,x): + logger = lldb.formatters.Logger.Logger() + steps = 0 + if x.is_null: + return None + while (not x.left.is_null): + x = x.left + steps += 1 + if steps > self.max_count: + logger >> "Returning None - we overflowed" + return None + return x + + def tree_max(self,x): + logger = lldb.formatters.Logger.Logger() + if x.is_null: + return None + while (not x.right.is_null): + x = x.right + return x + + def tree_is_left_child(self,x): + logger = lldb.formatters.Logger.Logger() + if x.is_null: + return None + return True if x.value == x.parent.left.value else False + + def increment_node(self,node): + logger = lldb.formatters.Logger.Logger() + if node.is_null: + return None + if not node.right.is_null: + return self.tree_min(node.right) + steps = 0 + while (not self.tree_is_left_child(node)): + steps += 1 + if steps > self.max_count: + logger >> "Returning None - we overflowed" + return None + node = node.parent + return node.parent + + def __init__(self,node,max_count=0): + logger = lldb.formatters.Logger.Logger() + self.node = stdmap_iterator_node(node) # we convert the SBValue to an internal node object on entry + self.max_count = max_count + + def value(self): + logger = lldb.formatters.Logger.Logger() + return self.node.sbvalue # and return the SBValue back on exit + + def next(self): + logger = lldb.formatters.Logger.Logger() + node = self.increment_node(self.node) + if node != None and node.sbvalue.IsValid() and not(node.is_null): + self.node = node + return self.value() + else: + return None + + def advance(self,N): + logger = lldb.formatters.Logger.Logger() + if N < 0: + return None + if N == 0: + return self.value() + if N == 1: + return self.next() + while N > 0: + if self.next() == None: + return None + N = N - 1 + return self.value() + +class stdmap_SynthProvider: + + def __init__(self, valobj, dict): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj; + self.pointer_size = self.valobj.GetProcess().GetAddressByteSize() + self.count = None + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.count = None + try: + # we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree + # if this gets set to True, then we will merrily return None for any child from that moment on + self.garbage = False + self.tree = self.valobj.GetChildMemberWithName('__tree_') + self.root_node = self.tree.GetChildMemberWithName('__begin_node_') + # this data is either lazily-calculated, or cannot be inferred at this moment + # we still need to mark it as None, meaning "please set me ASAP" + self.data_type = None + self.data_size = None + self.skip_size = None + except: + pass + + def num_children(self): + global _map_capping_size + logger = lldb.formatters.Logger.Logger() + if self.count == None: + self.count = self.num_children_impl() + if self.count > _map_capping_size: + self.count = _map_capping_size + return self.count + + def num_children_impl(self): + logger = lldb.formatters.Logger.Logger() + try: + return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned() + except: + return 0; + + def has_children(self): + return True + + def get_data_type(self): + logger = lldb.formatters.Logger.Logger() + if self.data_type == None or self.data_size == None: + if self.num_children() == 0: + return False + deref = self.root_node.Dereference() + if not(deref.IsValid()): + return False + value = deref.GetChildMemberWithName('__value_') + if not(value.IsValid()): + return False + self.data_type = value.GetType() + self.data_size = self.data_type.GetByteSize() + self.skip_size = None + return True + else: + return True + + def get_value_offset(self,node): + logger = lldb.formatters.Logger.Logger() + if self.skip_size == None: + node_type = node.GetType() + fields_count = node_type.GetNumberOfFields() + for i in range(fields_count): + field = node_type.GetFieldAtIndex(i) + if field.GetName() == '__value_': + self.skip_size = field.GetOffsetInBytes() + break + return (self.skip_size != None) + + def get_child_index(self,name): + logger = lldb.formatters.Logger.Logger() + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self,index): + logger = lldb.formatters.Logger.Logger() + logger >> "Retrieving child " + str(index) + if index < 0: + return None + if index >= self.num_children(): + return None; + if self.garbage: + logger >> "Returning None since this tree is garbage" + return None + try: + iterator = stdmap_iterator(self.root_node,max_count=self.num_children()) + # the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type + # out of which we can grab the information we need - every other node has a less informative + # type which omits all value information and only contains housekeeping information for the RB tree + # hence, we need to know if we are at a node != 0, so that we can still get at the data + need_to_skip = (index > 0) + current = iterator.advance(index) + if current == None: + logger >> "Tree is garbage - returning None" + self.garbage = True + return None + if self.get_data_type(): + if not(need_to_skip): + current = current.Dereference() + obj = current.GetChildMemberWithName('__value_') + obj_data = obj.GetData() + self.get_value_offset(current) # make sure we have a valid offset for the next items + # we do not return __value_ because then we would end up with a child named + # __value_ instead of [0] + return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type) + else: + # FIXME we need to have accessed item 0 before accessing any other item! + if self.skip_size == None: + logger >> "You asked for item > 0 before asking for item == 0, I will fetch 0 now then retry" + if self.get_child_at_index(0): + return self.get_child_at_index(index) + else: + logger >> "item == 0 could not be found. sorry, nothing can be done here." + return None + return current.CreateChildAtOffset('[' + str(index) + ']',self.skip_size,self.data_type) + else: + logger >> "Unable to infer data-type - returning None (should mark tree as garbage here?)" + return None + except Exception as err: + logger >> "Hit an exception: " + str(err) + return None + +# Just an example: the actual summary is produced by a summary string: size=${svar%#} +def stdmap_SummaryProvider(valobj,dict): + prov = stdmap_SynthProvider(valobj,None) + return 'size=' + str(prov.num_children()) + +class stddeque_SynthProvider: + def __init__(self, valobj, d): + logger = lldb.formatters.Logger.Logger() + logger.write("init") + self.valobj = valobj + self.pointer_size = self.valobj.GetProcess().GetAddressByteSize() + self.count = None + try: + self.find_block_size() + except: + self.block_size = -1 + self.element_size = -1 + logger.write("block_size=%d, element_size=%d" % (self.block_size, self.element_size)) + + def find_block_size(self): + # in order to use the deque we must have the block size, or else + # it's impossible to know what memory addresses are valid + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) + self.element_size = self.element_type.GetByteSize() + # The code says this, but there must be a better way: + # template + # class __deque_base { + # static const difference_type __block_size = sizeof(value_type) < 256 ? 4096 / sizeof(value_type) : 16; + # } + if self.element_size < 256: + self.block_size = 4096 / self.element_size + else: + self.block_size = 16 + + def num_children(self): + global _deque_capping_size + logger = lldb.formatters.Logger.Logger() + if self.count is None: + return 0 + return min(self.count, _deque_capping_size) + + def has_children(self): + return True + + def get_child_index(self,name): + logger = lldb.formatters.Logger.Logger() + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self,index): + logger = lldb.formatters.Logger.Logger() + logger.write("Fetching child " + str(index)) + if index < 0 or self.count is None: + return None; + if index >= self.num_children(): + return None; + try: + i, j = divmod(self.start+index, self.block_size) + return self.first.CreateValueFromExpression('[' + str(index) + ']', + '*(*(%s + %d) + %d)' % (self.first.get_expr_path(), i, j)) + except: + return None + + def update(self): + logger = lldb.formatters.Logger.Logger() + try: + # A deque is effectively a two-dim array, with fixed width. + # 'map' contains pointers to the rows of this array. The + # full memory area allocated by the deque is delimited + # by 'first' and 'end_cap'. However, only a subset of this + # memory contains valid data since a deque may have some slack + # at the front and back in order to have O(1) insertion at + # both ends. The rows in active use are delimited by + # 'begin' and 'end'. + # + # To find the elements that are actually constructed, the 'start' + # variable tells which element in this NxM array is the 0th + # one, and the 'size' element gives the number of elements + # in the deque. + count = self.valobj.GetChildMemberWithName('__size_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0) + # give up now if we cant access memory reliably + if self.block_size < 0: + logger.write("block_size < 0") + return + map_ = self.valobj.GetChildMemberWithName('__map_') + start = self.valobj.GetChildMemberWithName('__start_').GetValueAsUnsigned(0) + first = map_.GetChildMemberWithName('__first_') + map_first = first.GetValueAsUnsigned(0) + map_begin = map_.GetChildMemberWithName('__begin_').GetValueAsUnsigned(0) + map_end = map_.GetChildMemberWithName('__end_').GetValueAsUnsigned(0) + map_endcap= map_.GetChildMemberWithName('__end_cap_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0) + # check consistency + if not map_first <= map_begin <= map_end <= map_endcap: + logger.write("map pointers are not monotonic") + return + total_rows, junk = divmod(map_endcap - map_first, self.pointer_size) + if junk: + logger.write("endcap-first doesnt align correctly") + return + active_rows, junk = divmod(map_end - map_begin, self.pointer_size) + if junk: + logger.write("end-begin doesnt align correctly") + return + start_row, junk = divmod(map_begin - map_first, self.pointer_size) + if junk: + logger.write("begin-first doesnt align correctly") + return + if not start_row*self.block_size <= start < (start_row+1)*self.block_size: + logger.write("0th element must be in the 'begin' row") + return + end_row = start_row + active_rows + if not count: + if active_rows: + logger.write("empty deque but begin!=end") + return + elif not (end_row-1)*self.block_size <= start+count < end_row*self.block_size: + logger.write("nth element must be before the 'end' row") + return + logger.write("update success: count=%r, start=%r, first=%r" % (count,start,first)) + # if consistent, save all we really need: + self.count = count + self.start = start + self.first = first + except: + self.count = None + self.start = None + self.map_first = None + self.map_begin = None + +class stdsharedptr_SynthProvider: + def __init__(self, valobj, d): + logger = lldb.formatters.Logger.Logger() + logger.write("init") + self.valobj = valobj + #self.element_ptr_type = self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType() + self.ptr = None + self.cntrl = None + process = valobj.GetProcess() + self.endianness = process.GetByteOrder() + self.pointer_size = process.GetAddressByteSize() + self.count_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) + + def num_children(self): + return 1 + + def has_children(self): + return True + + def get_child_index(self,name): + if name=="__ptr_": + return 0 + if name=="count": + return 1 + if name=="weak_count": + return 2 + return -1 + + def get_child_at_index(self,index): + if index == 0: + return self.ptr + if index == 1: + if self.cntrl == None: + count = 0 + else: + count = 1 + self.cntrl.GetChildMemberWithName('__shared_owners_').GetValueAsSigned() + return self.valobj.CreateValueFromData("count", + lldb.SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [count]), + self.count_type) + if index == 2: + if self.cntrl == None: + count = 0 + else: + count = 1 + self.cntrl.GetChildMemberWithName('__shared_weak_owners_').GetValueAsSigned() + return self.valobj.CreateValueFromData("weak_count", + lldb.SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [count]), + self.count_type) + return None + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.ptr = self.valobj.GetChildMemberWithName('__ptr_')#.Cast(self.element_ptr_type) + cntrl = self.valobj.GetChildMemberWithName('__cntrl_') + if cntrl.GetValueAsUnsigned(0): + self.cntrl = cntrl.Dereference() + else: + self.cntrl = None + +# we can use two different categories for old and new formatters - type names are different enough that we should make no confusion +# talking with libc++ developer: "std::__1::class_name is set in stone until we decide to change the ABI. That shouldn't happen within a 5 year time frame" +def __lldb_init_module(debugger,dict): + debugger.HandleCommand('type summary add -F libcxx.stdstring_SummaryProvider "std::__1::string" -w libcxx') + debugger.HandleCommand('type summary add -F libcxx.stdstring_SummaryProvider "std::__1::basic_string, class std::__1::allocator >" -w libcxx') + debugger.HandleCommand('type synthetic add -l libcxx.stdvector_SynthProvider -x "^(std::__1::)vector<.+>$" -w libcxx') + debugger.HandleCommand('type summary add -F libcxx.stdvector_SummaryProvider -e -x "^(std::__1::)vector<.+>$" -w libcxx') + debugger.HandleCommand('type synthetic add -l libcxx.stdlist_SynthProvider -x "^(std::__1::)list<.+>$" -w libcxx') + debugger.HandleCommand('type summary add -F libcxx.stdlist_SummaryProvider -e -x "^(std::__1::)list<.+>$" -w libcxx') + debugger.HandleCommand('type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -w libcxx') + debugger.HandleCommand('type summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx') + debugger.HandleCommand("type category enable libcxx") + debugger.HandleCommand('type synthetic add -l libcxx.stddeque_SynthProvider -x "^(std::__1::)deque<.+>$" -w libcxx') + debugger.HandleCommand('type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)shared_ptr<.+>$" -w libcxx') + # turns out the structs look the same, so weak_ptr can be handled the same! + debugger.HandleCommand('type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)weak_ptr<.+>$" -w libcxx') + +_map_capping_size = 255 +_list_capping_size = 255 +_list_uses_loop_detector = True +_deque_capping_size = 255 diff --git a/examples/synthetic/unordered_multi.py b/examples/synthetic/unordered_multi.py new file mode 100644 index 000000000000..3389a01aea30 --- /dev/null +++ b/examples/synthetic/unordered_multi.py @@ -0,0 +1,110 @@ +import lldb + +_map_capping_size = 255 + +class libcxx_hash_table_SynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj + self.num_elements = None + self.next_element = None + self.bucket_count = None + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.num_elements = None + self.next_element = None + self.bucket_count = None + try: + # unordered_map is made up of a hash_map, which has 4 pieces in it: + # bucket list : + # array of buckets + # p1 (pair): + # first - pointer to first loaded element + # p2 (pair): + # first - number of elements + # second - hash function + # p3 (pair): + # first - max_load_factor + # second - equality operator function + # + # For display, we actually don't need to go inside the buckets, since 'p1' has a way to iterate over all + # the elements directly. + # + # We will calculate other values about the map because they will be useful for the summary. + # + table = self.valobj.GetChildMemberWithName('__table_') + + bl_ptr = table.GetChildMemberWithName('__bucket_list_').GetChildMemberWithName('__ptr_') + self.bucket_array_ptr = bl_ptr.GetChildMemberWithName('__first_').GetValueAsUnsigned(0) + self.bucket_count = bl_ptr.GetChildMemberWithName('__second_').GetChildMemberWithName('__data_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0) + logger >> "Bucket count = %r" % self.bucket_count + + self.begin_ptr = table.GetChildMemberWithName('__p1_').GetChildMemberWithName('__first_').GetChildMemberWithName('__next_') + + self.num_elements = table.GetChildMemberWithName('__p2_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0) + self.max_load_factor = table.GetChildMemberWithName('__p3_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0) + logger >> "Num elements = %r" % self.num_elements + + # save the pointers as we get them + # -- don't access this first element if num_element==0! + self.elements_cache = [] + if self.num_elements: + self.next_element = self.begin_ptr + else: + self.next_element = None + except Exception as e: + logger >> "Caught exception: %r" % e + pass + + def num_children(self): + global _map_capping_size + num_elements = self.num_elements + if num_elements is not None: + if num_elements > _map_capping_size: + num_elements = _map_capping_size + return num_elements + + def has_children(self): + return True + + def get_child_index(self,name): + logger = lldb.formatters.Logger.Logger() + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self,index): + logger = lldb.formatters.Logger.Logger() + logger >> "Retrieving child " + str(index) + if index < 0: + return None + if index >= self.num_children(): + return None + + # extend + logger >> " : cache size starts with %d elements" % len(self.elements_cache) + while index >= len(self.elements_cache): + # if we hit the end before we get the index, give up: + if not self.next_element: + logger >> " : hit end of list" + return None + + node = self.next_element.Dereference() + + value = node.GetChildMemberWithName('__value_') + hash_value = node.GetChildMemberWithName('__hash_').GetValueAsUnsigned() + self.elements_cache.append((value, hash_value)) + + self.next_element = node.GetChildMemberWithName('__next_') + if not self.next_element.GetValueAsUnsigned(0): + self.next_element = None + + # hit the index! so we have the value + logger >> " : cache size ends with %d elements" % len(self.elements_cache) + value, hash_value = self.elements_cache[index] + return self.valobj.CreateValueFromData('[%d] '%(index,hash_value), value.GetData(), value.GetType()) + + +def __lldb_init_module(debugger,dict): + debugger.HandleCommand('type synthetic add -l unordered_multi.libcxx_hash_table_SynthProvider -x "^(std::__1::)unordered_(multi)?(map|set)<.+> >$" -w libcxx') diff --git a/examples/test/.lldb-loggings b/examples/test/.lldb-loggings new file mode 100644 index 000000000000..9c92bd958479 --- /dev/null +++ b/examples/test/.lldb-loggings @@ -0,0 +1,20 @@ +def pre_flight(self): + import os + import lldb + import lldbtest + + dname = os.path.join(os.environ["LLDB_TEST"], + os.environ["LLDB_SESSION_DIRNAME"]) + if not os.path.isdir(dname): + os.mkdir(dname) + dest = os.path.join(dname, "lldb_log-%s-%s-%s.txt" % (self.getArchitecture(), self.getCompiler(), self.id())) + print "\nEnabling lldb logging for test case:", self + print "with log destination:", dest + self.runCmd("log enable -f %s gdb-remote packets process" % dest) + +#def post_flight(test): +# __import__("lldb") +# __import__("lldbtest") +# print "\nRunning post-flight function:" +# print "for test case:", test + diff --git a/examples/test/.lldb-pre-post-flight b/examples/test/.lldb-pre-post-flight new file mode 100644 index 000000000000..c1568a7295a7 --- /dev/null +++ b/examples/test/.lldb-pre-post-flight @@ -0,0 +1,12 @@ +def pre_flight(test): + __import__("lldb") + __import__("lldbtest") + print "\nRunning pre-flight function:" + print "for test case:", test + +def post_flight(test): + __import__("lldb") + __import__("lldbtest") + print "\nRunning post-flight function:" + print "for test case:", test + diff --git a/examples/test/.lldb-pre-post-flight.bad b/examples/test/.lldb-pre-post-flight.bad new file mode 100644 index 000000000000..0e17f3cdc95b --- /dev/null +++ b/examples/test/.lldb-pre-post-flight.bad @@ -0,0 +1,8 @@ +pre_flight = "I am not callable" + +def post_flight(test): + __import__("lldb") + __import__("lldbtest") + print "\nRunning post-flight function:" + print "for test case:", test + diff --git a/examples/test/.lldbtest-config b/examples/test/.lldbtest-config new file mode 100644 index 000000000000..31b489207778 --- /dev/null +++ b/examples/test/.lldbtest-config @@ -0,0 +1,6 @@ +sys.stderr = open("/tmp/lldbtest-stderr", "w") +sys.stdout = open("/tmp/lldbtest-stdout", "w") +compilers = ["gcc", "llvm-gcc"] +archs = ["x86_64", "i386"] +split_stderr = True # This will split the stderr into configuration-specific file +split_stdout = True # This will split the stdout into configuration-specific file diff --git a/examples/test/.lldbtest-config2 b/examples/test/.lldbtest-config2 new file mode 100644 index 000000000000..bf44726fd7f2 --- /dev/null +++ b/examples/test/.lldbtest-config2 @@ -0,0 +1,19 @@ +# Example config file for running the test suite for both 64 and 32-bit +# architectures. +# +# I use the following command to invoke the test driver: +# +# /Volumes/data/lldb/svn/trunk/test $ ./dotest.py -r /Volumes/data/lldb-test/archs -s session -c ../examples/test/.lldbtest-config2 -v -w . 2> ~/Developer/Log/lldbtest.log +# +# The '-r' option tells the driver to relocate the test execution to +# /Volumes/data/lldb-test/archs which must not exist before the run. +# +# Test failures/errors will be recorded into the 'session' directory due to the +# '-s' option, e.g., /Volumes/data/lldb-test/archs.arch=i386/test/session could +# contain the following three session info files: +# +# -rw-r--r-- 1 johnny admin 1737 Oct 25 13:25 TestArrayTypes.ArrayTypesTestCase.test_with_dwarf_and_run_command.log +# -rw-r--r-- 1 johnny admin 1733 Oct 25 13:25 TestClassTypes.ClassTypesTestCase.test_with_dwarf_and_run_command.log +# -rw-r--r-- 1 johnny admin 4677 Oct 25 13:26 TestObjCMethods.FoundationTestCase.test_data_type_and_expr_with_dsym.log + +archs = ["x86_64", "i386"] diff --git a/examples/test/lldbtest-stderr b/examples/test/lldbtest-stderr new file mode 100644 index 000000000000..7934d92835cc --- /dev/null +++ b/examples/test/lldbtest-stderr @@ -0,0 +1,39 @@ +---------------------------------------------------------------------- +Collected 1 test + + +Configuration: arch=x86_64 compiler=gcc +test_persistent_variables (TestPersistentVariables.PersistentVariablesTestCase) +Test that lldb persistent variables works correctly. ... ok + +---------------------------------------------------------------------- +Ran 1 test in 1.397s + +OK + +Configuration: arch=x86_64 compiler=llvm-gcc +test_persistent_variables (TestPersistentVariables.PersistentVariablesTestCase) +Test that lldb persistent variables works correctly. ... ok + +---------------------------------------------------------------------- +Ran 1 test in 1.282s + +OK + +Configuration: arch=i386 compiler=gcc +test_persistent_variables (TestPersistentVariables.PersistentVariablesTestCase) +Test that lldb persistent variables works correctly. ... ok + +---------------------------------------------------------------------- +Ran 1 test in 1.297s + +OK + +Configuration: arch=i386 compiler=llvm-gcc +test_persistent_variables (TestPersistentVariables.PersistentVariablesTestCase) +Test that lldb persistent variables works correctly. ... ok + +---------------------------------------------------------------------- +Ran 1 test in 1.269s + +OK diff --git a/examples/test/lldbtest-stdout b/examples/test/lldbtest-stdout new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/test/tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dsym.txt b/examples/test/tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dsym.txt new file mode 100644 index 000000000000..c1448cd844c2 --- /dev/null +++ b/examples/test/tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dsym.txt @@ -0,0 +1,55 @@ +com.apple.main-thread /Volumes/data/lldb/svn/ToT/build/Debug/LLDB.framework/Versions/A/Resources/debugserver arguments: +argv[0]="/Volumes/data/lldb/svn/ToT/build/Debug/LLDB.framework/Versions/A/Resources/debugserver" +argv[1]="localhost:14953" +argv[2]="--native-regs" +argv[3]="--setsid" +argv[4]=NULL + + +com.apple.main-thread Host::LaunchProcess (launch_info) => pid=55237, path='/Volumes/data/lldb/svn/ToT/build/Debug/LLDB.framework/Versions/A/Resources/debugserver' err = 0x00000000 +com.apple.main-thread ProcessGDBRemote::StartAsyncThread () + ProcessGDBRemote::AsyncThread (arg = 0x7fabc1883400, pid = 0) thread starting... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc1883400, pid = 0) listener.WaitForEvent (NULL, event_sp)... +com.apple.main-thread < 1> send packet: + +com.apple.main-thread history[1] tid=0x1307 < 1> send packet: + +com.apple.main-thread < 19> send packet: $QStartNoAckMode#b0 +com.apple.main-thread < 1> read packet: + +com.apple.main-thread < 6> read packet: $OK#9a +com.apple.main-thread < 1> send packet: + +com.apple.main-thread < 26> send packet: $QThreadSuffixSupported#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread < 27> send packet: $QListThreadsInStopReply#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread < 13> send packet: $qHostInfo#00 +com.apple.main-thread < 122> read packet: $cputype:16777223;cpusubtype:3;ostype:macosx;watchpoint_exceptions_received:after;vendor:apple;endian:little;ptrsize:8;#00 +com.apple.main-thread < 10> send packet: $vCont?#00 +com.apple.main-thread < 17> read packet: $vCont;c;C;s;S#00 +com.apple.main-thread < 27> send packet: $qVAttachOrWaitSupported#00 +com.apple.main-thread < 6> read packet: $OK#00 + +... + +com.apple.main-thread ProcessGDBRemote::DoDestroy() +com.apple.main-thread < 5> send packet: $k#00 +com.apple.main-thread error: failed to get response for 'k' +com.apple.main-thread ProcessGDBRemote::DoDestroy - failed to send k packet +com.apple.main-thread ProcessGDBRemote::StopAsyncThread () + ProcessGDBRemote::AsyncThread (arg = 0x7fabc185e200, pid = 55239) Got an event of type: 2... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc185e200, pid = 55239) got eBroadcastBitAsyncThreadShouldExit... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc185e200, pid = 55239) thread exiting... +com.apple.root.default-priority ProcessGDBRemote::MonitorDebugserverProcess (baton=0x7fabc185e200, pid=55240, signo=2 (0x2), exit_status=-1) +com.apple.main-thread < 18> send packet: $z0,100000e37,1#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread < 21> send packet: $z0,7fff5fc0d6e5,1#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread < 21> send packet: $z0,7fff8b132187,1#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread ProcessGDBRemote::DoDestroy() +com.apple.main-thread < 5> send packet: $k#00 +com.apple.main-thread error: failed to get response for 'k' +com.apple.main-thread ProcessGDBRemote::DoDestroy - failed to send k packet +com.apple.main-thread ProcessGDBRemote::StopAsyncThread () + ProcessGDBRemote::AsyncThread (arg = 0x7fabc18f8600, pid = 55243) Got an event of type: 2... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc18f8600, pid = 55243) got eBroadcastBitAsyncThreadShouldExit... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc18f8600, pid = 55243) thread exiting... +com.apple.root.default-priority ProcessGDBRemote::MonitorDebugserverProcess (baton=0x7fabc18f8600, pid=55244, signo=2 (0x2), exit_status=-1) diff --git a/examples/test/tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dwarf.txt b/examples/test/tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dwarf.txt new file mode 100644 index 000000000000..87cfddb29329 --- /dev/null +++ b/examples/test/tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dwarf.txt @@ -0,0 +1,55 @@ +com.apple.main-thread /Volumes/data/lldb/svn/ToT/build/Debug/LLDB.framework/Versions/A/Resources/debugserver arguments: +argv[0]="/Volumes/data/lldb/svn/ToT/build/Debug/LLDB.framework/Versions/A/Resources/debugserver" +argv[1]="localhost:33231" +argv[2]="--native-regs" +argv[3]="--setsid" +argv[4]=NULL + + +com.apple.main-thread Host::LaunchProcess (launch_info) => pid=55287, path='/Volumes/data/lldb/svn/ToT/build/Debug/LLDB.framework/Versions/A/Resources/debugserver' err = 0x00000000 +com.apple.main-thread ProcessGDBRemote::StartAsyncThread () + ProcessGDBRemote::AsyncThread (arg = 0x7fabc0997600, pid = 0) thread starting... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc0997600, pid = 0) listener.WaitForEvent (NULL, event_sp)... +com.apple.main-thread < 1> send packet: + +com.apple.main-thread history[1] tid=0x1307 < 1> send packet: + +com.apple.main-thread < 19> send packet: $QStartNoAckMode#b0 +com.apple.main-thread < 1> read packet: + +com.apple.main-thread < 6> read packet: $OK#9a +com.apple.main-thread < 1> send packet: + +com.apple.main-thread < 26> send packet: $QThreadSuffixSupported#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread < 27> send packet: $QListThreadsInStopReply#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread < 13> send packet: $qHostInfo#00 +com.apple.main-thread < 122> read packet: $cputype:16777223;cpusubtype:3;ostype:macosx;watchpoint_exceptions_received:after;vendor:apple;endian:little;ptrsize:8;#00 +com.apple.main-thread < 10> send packet: $vCont?#00 +com.apple.main-thread < 17> read packet: $vCont;c;C;s;S#00 +com.apple.main-thread < 27> send packet: $qVAttachOrWaitSupported#00 +com.apple.main-thread < 6> read packet: $OK#00 + +... + +com.apple.main-thread ProcessGDBRemote::DoDestroy() +com.apple.main-thread < 5> send packet: $k#00 +com.apple.main-thread error: failed to get response for 'k' +com.apple.main-thread ProcessGDBRemote::DoDestroy - failed to send k packet +com.apple.main-thread ProcessGDBRemote::StopAsyncThread () + ProcessGDBRemote::AsyncThread (arg = 0x7fabc1882000, pid = 55289) Got an event of type: 2... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc1882000, pid = 55289) got eBroadcastBitAsyncThreadShouldExit... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc1882000, pid = 55289) thread exiting... +com.apple.root.default-priority ProcessGDBRemote::MonitorDebugserverProcess (baton=0x7fabc1882000, pid=55290, signo=2 (0x2), exit_status=-1) +com.apple.main-thread < 18> send packet: $z0,100000e37,1#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread < 21> send packet: $z0,7fff5fc0d6e5,1#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread < 21> send packet: $z0,7fff8b132187,1#00 +com.apple.main-thread < 6> read packet: $OK#00 +com.apple.main-thread ProcessGDBRemote::DoDestroy() +com.apple.main-thread < 5> send packet: $k#00 +com.apple.main-thread error: failed to get response for 'k' +com.apple.main-thread ProcessGDBRemote::DoDestroy - failed to send k packet +com.apple.main-thread ProcessGDBRemote::StopAsyncThread () + ProcessGDBRemote::AsyncThread (arg = 0x7fabc0bed200, pid = 55292) Got an event of type: 2... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc0bed200, pid = 55292) got eBroadcastBitAsyncThreadShouldExit... + ProcessGDBRemote::AsyncThread (arg = 0x7fabc0bed200, pid = 55292) thread exiting... +com.apple.root.default-priority ProcessGDBRemote::MonitorDebugserverProcess (baton=0x7fabc0bed200, pid=55293, signo=2 (0x2), exit_status=-1) diff --git a/examples/test/usage-config b/examples/test/usage-config new file mode 100644 index 000000000000..4f3d3b222f17 --- /dev/null +++ b/examples/test/usage-config @@ -0,0 +1,10 @@ +# This is an example of using the "-c" option to source a config file to +# reassign the system stderr and stdout and to exercise different combinations +# of architectures and compilers. +# +# The config file is checked in as .lldbtest-config and the redirected stderr +# and stdout are checked in as lldbtest-stderr and lldbtest-stdout, all in the +# the same directory as this file. + +[15:36:32] johnny:/Volumes/data/lldb/svn/trunk/test $ ./dotest.py -v -c ~/.lldbtest-config persistent_variables +[15:40:55] johnny:/Volumes/data/lldb/svn/trunk/test $ diff --git a/examples/test/usage-lldb-loggings b/examples/test/usage-lldb-loggings new file mode 100644 index 000000000000..b7d7e2e58fc1 --- /dev/null +++ b/examples/test/usage-lldb-loggings @@ -0,0 +1,125 @@ +# +# The following example shows how to utilize the pre-flight config file to route the lldb gdb-remote log messages +# into individual log destinations. +# +# See also .lldb-loggings in this directory as well as the tmp dir which contains the two log files abridged due +# to their log sizes. +# + +[11:31:34] johnny:/Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 -v -c ../examples/test/.lldb-loggings functionalities/breakpoint/breakpoint_command +config: {'pre_flight': } +LLDB build dir: /Volumes/data/lldb/svn/ToT/build/Debug +LLDB-165 +Path: /Volumes/data/lldb/svn/ToT +URL: https://johnny@llvm.org/svn/llvm-project/lldb/trunk +Repository Root: https://johnny@llvm.org/svn/llvm-project +Repository UUID: 91177308-0d34-0410-b5e6-96231b3b80d8 +Revision: 162231 +Node Kind: directory +Schedule: normal +Last Changed Author: johnny +Last Changed Rev: 162228 +Last Changed Date: 2012-08-20 14:16:02 -0700 (Mon, 20 Aug 2012) + + +lldb.pre_flight: def pre_flight(self): + import os + import lldb + import lldbtest + + dest = os.path.join("/tmp", "lldb_log-%s-%s-%s.txt" % (self.getArchitecture(), self.getCompiler(), self.id())) + print "\nEnabling lldb logging for test case:", self + print "with log destination:", dest + self.runCmd("log enable -f %s gdb-remote packets process" % dest) + +lldb.post_flight: None + +Session logs for test failures/errors/unexpected successes will go into directory '2012-08-22-11_36_37' +Command invoked: python ./dotest.py -A x86_64 -v -c ../examples/test/.lldb-loggings functionalities/breakpoint/breakpoint_command +compilers=['clang'] + +Configuration: arch=x86_64 compiler=clang +---------------------------------------------------------------------- +Collected 2 tests + +1: test_with_dsym (TestBreakpointCommand.BreakpointCommandTestCase) + Test a sequence of breakpoint command add, list, and delete. ... +Enabling lldb logging for test case: test_with_dsym (TestBreakpointCommand.BreakpointCommandTestCase) +with log destination: /tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dsym.txt +ok +2: test_with_dwarf (TestBreakpointCommand.BreakpointCommandTestCase) + Test a sequence of breakpoint command add, list, and delete. ... +Enabling lldb logging for test case: test_with_dwarf (TestBreakpointCommand.BreakpointCommandTestCase) +with log destination: /tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dwarf.txt +ok + +---------------------------------------------------------------------- +Ran 2 tests in 7.826s + +OK +[11:36:44] johnny:/Volumes/data/lldb/svn/ToT/test $ ls -l /tmp/lldb_log* +-rw-r----- 1 johnny wheel 614614 Aug 22 11:36 /tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dsym.txt +-rw-r----- 1 johnny wheel 614614 Aug 22 11:36 /tmp/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dwarf.txt +[11:37:09] johnny:/Volumes/data/lldb/svn/ToT/test $ + +# +# And this shows the log files go into the session directory. +# Note that the .lldb-loggings file is modified to get the session directory now. +# + +[11:37:09] johnny:/Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 -v -c ../examples/test/.lldb-loggings functionalities/breakpoint/breakpoint_command +config: {'pre_flight': } +LLDB build dir: /Volumes/data/lldb/svn/ToT/build/Debug +LLDB-165 +Path: /Volumes/data/lldb/svn/ToT +URL: https://johnny@llvm.org/svn/llvm-project/lldb/trunk +Repository Root: https://johnny@llvm.org/svn/llvm-project +Repository UUID: 91177308-0d34-0410-b5e6-96231b3b80d8 +Revision: 162231 +Node Kind: directory +Schedule: normal +Last Changed Author: johnny +Last Changed Rev: 162228 +Last Changed Date: 2012-08-20 14:16:02 -0700 (Mon, 20 Aug 2012) + + +lldb.pre_flight: def pre_flight(self): + import os + import lldb + import lldbtest + + dname = os.path.join(os.environ["LLDB_TEST"], + os.environ["LLDB_SESSION_DIRNAME"]) + if not os.path.isdir(dname): + os.mkdir(dname) + dest = os.path.join(dname, "lldb_log-%s-%s-%s.txt" % (self.getArchitecture(), self.getCompiler(), self.id())) + print "\nEnabling lldb logging for test case:", self + print "with log destination:", dest + self.runCmd("log enable -f %s gdb-remote packets process" % dest) + +lldb.post_flight: None + +Session logs for test failures/errors/unexpected successes will go into directory '2012-08-22-13_21_46' +Command invoked: python ./dotest.py -A x86_64 -v -c ../examples/test/.lldb-loggings functionalities/breakpoint/breakpoint_command +compilers=['clang'] + +Configuration: arch=x86_64 compiler=clang +---------------------------------------------------------------------- +Collected 2 tests + +1: test_with_dsym (TestBreakpointCommand.BreakpointCommandTestCase) + Test a sequence of breakpoint command add, list, and delete. ... +Enabling lldb logging for test case: test_with_dsym (TestBreakpointCommand.BreakpointCommandTestCase) +with log destination: /Volumes/data/lldb/svn/ToT/test/2012-08-22-13_21_46/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dsym.txt +ok +2: test_with_dwarf (TestBreakpointCommand.BreakpointCommandTestCase) + Test a sequence of breakpoint command add, list, and delete. ... +Enabling lldb logging for test case: test_with_dwarf (TestBreakpointCommand.BreakpointCommandTestCase) +with log destination: /Volumes/data/lldb/svn/ToT/test/2012-08-22-13_21_46/lldb_log-x86_64-clang-TestBreakpointCommand.BreakpointCommandTestCase.test_with_dwarf.txt +ok + +---------------------------------------------------------------------- +Ran 2 tests in 8.575s + +OK +[13:21:55] johnny:/Volumes/data/lldb/svn/ToT/test $ diff --git a/examples/test/usage-pre-post-flight b/examples/test/usage-pre-post-flight new file mode 100644 index 000000000000..da6860724258 --- /dev/null +++ b/examples/test/usage-pre-post-flight @@ -0,0 +1,65 @@ +# +# The following examples first show a bad pre/post flight config file followed by a good pre/post config file. +# + +[11:31:19] johnny:/Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 -v -c ../examples/test/.lldb-pre-post-flight.bad functionalities/watchpoint/hello_watchpoint +config: {'pre_flight': 'I am not callable', 'post_flight': } +fatal error: pre_flight is not callable, exiting. +[11:32:48] johnny:/Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 -v -c ../examples/test/.lldb-pre-post-flight functionalities/watchpoint/hello_watchpoint +config: {'pre_flight': , 'post_flight': } +LLDB build dir: /Volumes/data/lldb/svn/ToT/build/Debug +LLDB-139 +Path: /Volumes/data/lldb/svn/ToT +URL: https://johnny@llvm.org/svn/llvm-project/lldb/trunk +Repository Root: https://johnny@llvm.org/svn/llvm-project +Repository UUID: 91177308-0d34-0410-b5e6-96231b3b80d8 +Revision: 154753 +Node Kind: directory +Schedule: normal +Last Changed Author: gclayton +Last Changed Rev: 154730 +Last Changed Date: 2012-04-13 18:42:46 -0700 (Fri, 13 Apr 2012) + + +lldb.pre_flight: def pre_flight(test): + __import__("lldb") + __import__("lldbtest") + print "\nRunning pre-flight function:" + print "for test case:", test + +lldb.post_flight: def post_flight(test): + __import__("lldb") + __import__("lldbtest") + print "\nRunning post-flight function:" + print "for test case:", test + + +Session logs for test failures/errors/unexpected successes will go into directory '2012-04-16-11_34_08' +Command invoked: python ./dotest.py -A x86_64 -v -c ../examples/test/.lldb-pre-post-flight functionalities/watchpoint/hello_watchpoint +compilers=['clang'] + +Configuration: arch=x86_64 compiler=clang +---------------------------------------------------------------------- +Collected 2 tests + +1: test_hello_watchpoint_with_dsym_using_watchpoint_set (TestMyFirstWatchpoint.HelloWatchpointTestCase) + Test a simple sequence of watchpoint creation and watchpoint hit. ... +Running pre-flight function: +for test case: test_hello_watchpoint_with_dsym_using_watchpoint_set (TestMyFirstWatchpoint.HelloWatchpointTestCase) + +Running post-flight function: +for test case: test_hello_watchpoint_with_dsym_using_watchpoint_set (TestMyFirstWatchpoint.HelloWatchpointTestCase) +ok +2: test_hello_watchpoint_with_dwarf_using_watchpoint_set (TestMyFirstWatchpoint.HelloWatchpointTestCase) + Test a simple sequence of watchpoint creation and watchpoint hit. ... +Running pre-flight function: +for test case: test_hello_watchpoint_with_dwarf_using_watchpoint_set (TestMyFirstWatchpoint.HelloWatchpointTestCase) + +Running post-flight function: +for test case: test_hello_watchpoint_with_dwarf_using_watchpoint_set (TestMyFirstWatchpoint.HelloWatchpointTestCase) +ok + +---------------------------------------------------------------------- +Ran 2 tests in 1.584s + +OK \ No newline at end of file diff --git a/include/Makefile b/include/Makefile new file mode 100644 index 000000000000..02acdce10271 --- /dev/null +++ b/include/Makefile @@ -0,0 +1,13 @@ +##===- include/Makefile ------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := .. +DIRS := lldb + +include $(LLDB_LEVEL)/Makefile diff --git a/include/lldb/Core/StringList.h b/include/lldb/Core/StringList.h index 3e341b994075..53bdb6994a56 100644 --- a/include/lldb/Core/StringList.h +++ b/include/lldb/Core/StringList.h @@ -132,9 +132,16 @@ class StringList StringList& operator << (const char* str); + StringList& + operator << (const std::string &s); + StringList& operator << (StringList strings); + // Copy assignment for a vector of strings + StringList& + operator = (const std::vector &rhs); + // This string list contains a list of valid auto completion // strings, and the "s" is passed in. "matches" is filled in // with zero or more string values that start with "s", and @@ -147,6 +154,23 @@ class StringList StringList &matches, size_t &exact_matches_idx) const; + // Dump the StringList to the given lldb_private::Log, `log`, one item per line. + // If given, `name` will be used to identify the start and end of the list in the output. + virtual void LogDump(Log *log, const char *name = nullptr); + + // Static helper to convert an iterable of strings to a StringList, and then + // dump it with the semantics of the `LogDump` method. + template static void LogDump(Log *log, T s_iterable, const char *name = nullptr) + { + if (!log) + return; + // Make a copy of the iterable as a StringList + StringList l{}; + for (const auto &s : s_iterable) + l << s; + + l.LogDump(log, name); + } private: STLStringArray m_strings; }; diff --git a/include/lldb/Host/android/Android.h b/include/lldb/Host/android/Android.h new file mode 100644 index 000000000000..8efc1a53b01f --- /dev/null +++ b/include/lldb/Host/android/Android.h @@ -0,0 +1,31 @@ +//===-- lldb-android.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_android_h_ +#define LLDB_lldb_android_h_ + +#include +#include +#include + +#define _isatty isatty +#define SYS_tgkill __NR_tgkill + +namespace std +{ + template + std::string to_string(T value) + { + std::ostringstream os ; + os << value ; + return os.str() ; + } +} + +#endif // LLDB_lldb_android_h_ diff --git a/include/lldb/Host/android/Config.h b/include/lldb/Host/android/Config.h new file mode 100644 index 000000000000..f16ed86cabb8 --- /dev/null +++ b/include/lldb/Host/android/Config.h @@ -0,0 +1,28 @@ +//===-- Config.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//---------------------------------------------------------------------- +// LLDB currently doesn't have a dynamic configuration mechanism, so we +// are going to hardcode things for now. Eventually these files will +// be auto generated by some configuration script that can detect +// platform functionality availability. +//---------------------------------------------------------------------- + +#ifndef liblldb_Platform_Config_h_ +#define liblldb_Platform_Config_h_ + +#define LLDB_CONFIG_TERMIOS_SUPPORTED 1 + +//#define LLDB_CONFIG_TILDE_RESOLVES_TO_USER 1 + +//#define LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED 1 + +//#define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 + +#endif // #ifndef liblldb_Platform_Config_h_ diff --git a/include/lldb/Host/android/HostInfoAndroid.h b/include/lldb/Host/android/HostInfoAndroid.h new file mode 100644 index 000000000000..08eb1abf3b51 --- /dev/null +++ b/include/lldb/Host/android/HostInfoAndroid.h @@ -0,0 +1,33 @@ +//===-- HostInfoAndroid.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_Host_android_HostInfoAndroid_h_ +#define lldb_Host_android_HostInfoAndroid_h_ + +#include "lldb/Host/linux/HostInfoLinux.h" + +namespace lldb_private +{ + +class HostInfoAndroid : public HostInfoLinux +{ + friend class HostInfoBase; + + public: + static FileSpec GetDefaultShell(); + static FileSpec ResolveLibraryPath (const std::string& path, const ArchSpec& arch); + + protected: + static void ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64); + static bool ComputeTempFileBaseDirectory(FileSpec &file_spec); +}; + +} // end of namespace lldb_private + +#endif // #ifndef lldb_Host_android_HostInfoAndroid_h_ diff --git a/include/lldb/Host/android/ProcessLauncherAndroid.h b/include/lldb/Host/android/ProcessLauncherAndroid.h new file mode 100644 index 000000000000..8d4847810028 --- /dev/null +++ b/include/lldb/Host/android/ProcessLauncherAndroid.h @@ -0,0 +1,26 @@ +//===-- ProcessLauncherAndroid.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_Host_android_ProcessLauncherAndroid_h_ +#define lldb_Host_android_ProcessLauncherAndroid_h_ + +#include "lldb/Host/ProcessLauncher.h" + +namespace lldb_private +{ + +class ProcessLauncherAndroid : public ProcessLauncher +{ + public: + virtual HostProcess LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error); +}; + +} // end of namespace lldb_private + +#endif diff --git a/include/lldb/Host/linux/AbstractSocket.h b/include/lldb/Host/linux/AbstractSocket.h new file mode 100644 index 000000000000..7814410a04d7 --- /dev/null +++ b/include/lldb/Host/linux/AbstractSocket.h @@ -0,0 +1,28 @@ +//===-- AbstractSocket.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AbstractSocket_h_ +#define liblldb_AbstractSocket_h_ + +#include "lldb/Host/posix/DomainSocket.h" + +namespace lldb_private +{ + class AbstractSocket: public DomainSocket + { + public: + AbstractSocket(bool child_processes_inherit, Error &error); + + protected: + size_t GetNameOffset() const override; + void DeleteSocketFile(llvm::StringRef name) override; + }; +} + +#endif // ifndef liblldb_AbstractSocket_h_ diff --git a/include/lldb/Host/linux/Config.h b/include/lldb/Host/linux/Config.h new file mode 100644 index 000000000000..49d97dd5793c --- /dev/null +++ b/include/lldb/Host/linux/Config.h @@ -0,0 +1,28 @@ +//===-- Config.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//---------------------------------------------------------------------- +// LLDB currently doesn't have a dynamic configuration mechanism, so we +// are going to hardcode things for now. Eventually these files will +// be auto generated by some configuration script that can detect +// platform functionality availability. +//---------------------------------------------------------------------- + +#ifndef liblldb_Platform_Config_h_ +#define liblldb_Platform_Config_h_ + +#define LLDB_CONFIG_TERMIOS_SUPPORTED 1 + +#define LLDB_CONFIG_TILDE_RESOLVES_TO_USER 1 + +//#define LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED 1 + +//#define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 + +#endif // #ifndef liblldb_Platform_Config_h_ diff --git a/include/lldb/Host/linux/HostInfoLinux.h b/include/lldb/Host/linux/HostInfoLinux.h new file mode 100644 index 000000000000..e4b22075325d --- /dev/null +++ b/include/lldb/Host/linux/HostInfoLinux.h @@ -0,0 +1,50 @@ +//===-- HostInfoLinux.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_Host_linux_HostInfoLinux_h_ +#define lldb_Host_linux_HostInfoLinux_h_ + +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/posix/HostInfoPosix.h" + +#include "llvm/ADT/StringRef.h" + +#include + +namespace lldb_private +{ + +class HostInfoLinux : public HostInfoPosix +{ + friend class HostInfoBase; + + private: + // Static class, unconstructable. + HostInfoLinux(); + ~HostInfoLinux(); + + public: + static void Initialize(); + static uint32_t GetMaxThreadNameLength(); + + static bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update); + static bool GetOSBuildString(std::string &s); + static bool GetOSKernelDescription(std::string &s); + static llvm::StringRef GetDistributionId(); + static FileSpec GetProgramFileSpec(); + + protected: + static bool ComputeSupportExeDirectory(FileSpec &file_spec); + static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); + static bool ComputeUserPluginsDirectory(FileSpec &file_spec); + static void ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64); +}; +} + +#endif diff --git a/include/lldb/Host/linux/HostThreadLinux.h b/include/lldb/Host/linux/HostThreadLinux.h new file mode 100644 index 000000000000..9091f41dae47 --- /dev/null +++ b/include/lldb/Host/linux/HostThreadLinux.h @@ -0,0 +1,32 @@ +//===-- HostThreadLinux.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_Host_linux_HostThreadLinux_h_ +#define lldb_Host_linux_HostThreadLinux_h_ + +#include "lldb/Host/posix/HostThreadPosix.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallString.h" + +namespace lldb_private +{ + +class HostThreadLinux : public HostThreadPosix +{ + public: + HostThreadLinux(); + HostThreadLinux(lldb::thread_t thread); + + static void SetName(lldb::thread_t thread, llvm::StringRef name); + static void GetName(lldb::thread_t thread, llvm::SmallVectorImpl &name); +}; +} + +#endif diff --git a/include/lldb/Host/linux/Personality.h b/include/lldb/Host/linux/Personality.h new file mode 100644 index 000000000000..48fc2e2bdd4c --- /dev/null +++ b/include/lldb/Host/linux/Personality.h @@ -0,0 +1,25 @@ +//===-- Personality.h -------------------------------------------*- 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 personality functions & structures + +#ifndef liblldb_Host_linux_Personality_h_ +#define liblldb_Host_linux_Personality_h_ + +#ifdef __ANDROID_NDK__ +#include +#endif + +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 +#include +#else +#include +#endif + +#endif // liblldb_Host_linux_Personality_h_ diff --git a/include/lldb/Host/linux/Ptrace.h b/include/lldb/Host/linux/Ptrace.h new file mode 100644 index 000000000000..b28bb37715d6 --- /dev/null +++ b/include/lldb/Host/linux/Ptrace.h @@ -0,0 +1,66 @@ +//===-- Ptrace.h ------------------------------------------------*- 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 ptrace functions & structures + +#ifndef liblldb_Host_linux_Ptrace_h_ +#define liblldb_Host_linux_Ptrace_h_ + +#include + +#ifdef __ANDROID_NDK__ +#define PT_DETACH PTRACE_DETACH +typedef int __ptrace_request; +#endif + +#define DEBUG_PTRACE_MAXBYTES 20 + +// Support ptrace extensions even when compiled without required kernel support +#ifndef PT_GETREGS + #ifndef PTRACE_GETREGS + #define PTRACE_GETREGS 12 + #endif +#endif +#ifndef PT_SETREGS + #ifndef PTRACE_SETREGS + #define PTRACE_SETREGS 13 + #endif +#endif +#ifndef PT_GETFPREGS + #ifndef PTRACE_GETFPREGS + #define PTRACE_GETFPREGS 14 + #endif +#endif +#ifndef PT_SETFPREGS + #ifndef PTRACE_SETFPREGS + #define PTRACE_SETFPREGS 15 + #endif +#endif +#ifndef PTRACE_GETREGSET + #define PTRACE_GETREGSET 0x4204 +#endif +#ifndef PTRACE_SETREGSET + #define PTRACE_SETREGSET 0x4205 +#endif +#ifndef PTRACE_GET_THREAD_AREA + #define PTRACE_GET_THREAD_AREA 25 +#endif +#ifndef PTRACE_ARCH_PRCTL + #define PTRACE_ARCH_PRCTL 30 +#endif +#ifndef ARCH_GET_FS + #define ARCH_SET_GS 0x1001 + #define ARCH_SET_FS 0x1002 + #define ARCH_GET_FS 0x1003 + #define ARCH_GET_GS 0x1004 +#endif + +#define LLDB_PTRACE_NT_ARM_TLS 0x401 // ARM TLS register + +#endif // liblldb_Host_linux_Ptrace_h_ diff --git a/include/lldb/Host/linux/Signalfd.h b/include/lldb/Host/linux/Signalfd.h new file mode 100644 index 000000000000..cf50e87097fb --- /dev/null +++ b/include/lldb/Host/linux/Signalfd.h @@ -0,0 +1,54 @@ +//===-- Signalfd.h ----------------------------------------------*- 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 signalfd functions & structures + +#ifndef liblldb_Host_linux_Signalfd_h_ +#define liblldb_Host_linux_Signalfd_h_ + +#ifdef __ANDROID_NDK__ +#include +#endif + +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 + +#include +#include + +#define SFD_CLOEXEC O_CLOEXEC +#define SFD_NONBLOCK O_NONBLOCK + +struct signalfd_siginfo { + __u32 ssi_signo; + __s32 ssi_errno; + __s32 ssi_code; + __u32 ssi_pid; + __u32 ssi_uid; + __s32 ssi_fd; + __u32 ssi_tid; + __u32 ssi_band; + __u32 ssi_overrun; + __u32 ssi_trapno; + __s32 ssi_status; + __s32 ssi_int; + __u64 ssi_ptr; + __u64 ssi_utime; + __u64 ssi_stime; + __u64 ssi_addr; + __u16 ssi_addr_lsb; + __u8 __pad[46]; +}; + +int signalfd (int fd, const sigset_t *mask, int flags); + +#else +#include +#endif + +#endif // liblldb_Host_linux_Signalfd_h_ diff --git a/include/lldb/Host/linux/Uio.h b/include/lldb/Host/linux/Uio.h new file mode 100644 index 000000000000..08894c0a2ca8 --- /dev/null +++ b/include/lldb/Host/linux/Uio.h @@ -0,0 +1,23 @@ +//===-- Uio.h ---------------------------------------------------*- 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_linux_Uio_h_ +#define liblldb_Host_linux_Uio_h_ + +#include + +// We shall provide our own implementation of process_vm_readv if it is not present +#ifndef HAVE_PROCESS_VM_READV +ssize_t process_vm_readv(::pid_t pid, + const struct iovec *local_iov, unsigned long liovcnt, + const struct iovec *remote_iov, unsigned long riovcnt, + unsigned long flags); +#endif + +#endif // liblldb_Host_linux_Uio_h_ diff --git a/include/lldb/Host/macosx/Config.h b/include/lldb/Host/macosx/Config.h new file mode 100644 index 000000000000..c8add4498f27 --- /dev/null +++ b/include/lldb/Host/macosx/Config.h @@ -0,0 +1,28 @@ +//===-- Config.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//---------------------------------------------------------------------- +// LLDB currently doesn't have a dynamic configuration mechanism, so we +// are going to hardcode things for now. Eventually these files will +// be auto generated by some configuration script that can detect +// platform functionality availability. +//---------------------------------------------------------------------- + +#ifndef liblldb_Platform_Config_h_ +#define liblldb_Platform_Config_h_ + +#define LLDB_CONFIG_TERMIOS_SUPPORTED 1 + +#define LLDB_CONFIG_TILDE_RESOLVES_TO_USER 1 + +#define LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED 1 + +#define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 + +#endif // #ifndef liblldb_Platform_Config_h_ diff --git a/include/lldb/Host/macosx/HostInfoMacOSX.h b/include/lldb/Host/macosx/HostInfoMacOSX.h new file mode 100644 index 000000000000..3b62c7fb9061 --- /dev/null +++ b/include/lldb/Host/macosx/HostInfoMacOSX.h @@ -0,0 +1,47 @@ +//===-- HostInfoMacOSX.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_Host_macosx_HostInfoMacOSX_h_ +#define lldb_Host_macosx_HostInfoMacOSX_h_ + +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/posix/HostInfoPosix.h" + +namespace lldb_private +{ + +class ArchSpec; + +class HostInfoMacOSX : public HostInfoPosix +{ + friend class HostInfoBase; + + private: + // Static class, unconstructable. + HostInfoMacOSX(); + ~HostInfoMacOSX(); + + public: + static bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update); + static bool GetOSBuildString(std::string &s); + static bool GetOSKernelDescription(std::string &s); + static FileSpec GetProgramFileSpec(); + + protected: + static bool ComputeSupportExeDirectory(FileSpec &file_spec); + static void ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64); + static bool ComputeHeaderDirectory(FileSpec &file_spec); + static bool ComputePythonDirectory(FileSpec &file_spec); + static bool ComputeClangDirectory(FileSpec &file_spec); + static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); + static bool ComputeUserPluginsDirectory(FileSpec &file_spec); +}; +} + +#endif diff --git a/include/lldb/Host/macosx/HostThreadMacOSX.h b/include/lldb/Host/macosx/HostThreadMacOSX.h new file mode 100644 index 000000000000..9173aaf92f32 --- /dev/null +++ b/include/lldb/Host/macosx/HostThreadMacOSX.h @@ -0,0 +1,31 @@ +//===-- HostThreadMacOSX.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_Host_macosx_HostThreadMacOSX_h_ +#define lldb_Host_macosx_HostThreadMacOSX_h_ + +#include "lldb/Host/posix/HostThreadPosix.h" + +namespace lldb_private +{ + +class HostThreadMacOSX : public HostThreadPosix +{ + friend class ThreadLauncher; + + public: + HostThreadMacOSX(); + HostThreadMacOSX(lldb::thread_t thread); + + protected: + static lldb::thread_result_t ThreadCreateTrampoline(lldb::thread_arg_t arg); +}; +} + +#endif diff --git a/include/lldb/Host/mingw/Config.h b/include/lldb/Host/mingw/Config.h new file mode 100644 index 000000000000..cdf6f21e0988 --- /dev/null +++ b/include/lldb/Host/mingw/Config.h @@ -0,0 +1,30 @@ +//===-- Config.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//---------------------------------------------------------------------- +// LLDB currently doesn't have a dynamic configuration mechanism, so we +// are going to hardcode things for now. Eventually these files will +// be auto generated by some configuration script that can detect +// platform functionality availability. +//---------------------------------------------------------------------- + +#ifndef liblldb_Platform_Config_h_ +#define liblldb_Platform_Config_h_ + +#define LLDB_DISABLE_POSIX + +//#define LLDB_CONFIG_TERMIOS_SUPPORTED 1 + +//#define LLDB_CONFIG_TILDE_RESOLVES_TO_USER 1 + +//#define LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED 1 + +//#define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 + +#endif // #ifndef liblldb_Platform_Config_h_ diff --git a/include/lldb/Host/msvc/Config.h b/include/lldb/Host/msvc/Config.h new file mode 100644 index 000000000000..4a4ed5799c53 --- /dev/null +++ b/include/lldb/Host/msvc/Config.h @@ -0,0 +1,37 @@ +//===-- Config.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//---------------------------------------------------------------------- +// LLDB currently doesn't have a dynamic configuration mechanism, so we +// are going to hardcode things for now. Eventually these files will +// be auto generated by some configuration script that can detect +// platform functionality availability. +//---------------------------------------------------------------------- + +#ifndef liblldb_host_msvc_Config_h_ +#define liblldb_host_msvc_Config_h_ + +#define LLDB_DISABLE_POSIX + +//#define LLDB_CONFIG_TERMIOS_SUPPORTED 1 + +//#define LLDB_CONFIG_TILDE_RESOLVES_TO_USER 1 + +//#define LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED 1 + +//#define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 + +#if _HAS_EXCEPTIONS == 0 +// Due to a bug in , when _HAS_EXCEPTIONS == 0 the header will try to call +// uncaught_exception() without having a declaration for it. The fix for this is +// to manually #include , which contains this declaration. +#include +#endif + +#endif // #ifndef liblldb_Platform_Config_h_ diff --git a/include/lldb/Host/windows/AutoHandle.h b/include/lldb/Host/windows/AutoHandle.h new file mode 100644 index 000000000000..04411c47d9e2 --- /dev/null +++ b/include/lldb/Host/windows/AutoHandle.h @@ -0,0 +1,40 @@ +//===-- AutoHandle.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_Host_windows_AutoHandle_h_ +#define LLDB_lldb_Host_windows_AutoHandle_h_ + +namespace lldb_private { + +class AutoHandle { +public: + AutoHandle(HANDLE handle, HANDLE invalid_value = INVALID_HANDLE_VALUE) + : m_handle(handle) + , m_invalid_value(invalid_value) + { + } + + ~AutoHandle() + { + if (m_handle != m_invalid_value) + ::CloseHandle(m_handle); + } + + bool IsValid() const { return m_handle != m_invalid_value; } + + HANDLE get() const { return m_handle; } +private: + HANDLE m_handle; + HANDLE m_invalid_value; +}; + +} + +#endif + diff --git a/include/lldb/Host/windows/ConnectionGenericFileWindows.h b/include/lldb/Host/windows/ConnectionGenericFileWindows.h new file mode 100644 index 000000000000..bfe9b2e0c7b8 --- /dev/null +++ b/include/lldb/Host/windows/ConnectionGenericFileWindows.h @@ -0,0 +1,68 @@ +//===-- ConnectionGenericFileWindows.h --------------------------*- 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_windows_ConnectionGenericFileWindows_h_ +#define liblldb_Host_windows_ConnectionGenericFileWindows_h_ + +#include "lldb/Core/Connection.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/lldb-types.h" + +namespace lldb_private +{ + +class Error; + +class ConnectionGenericFile : public lldb_private::Connection +{ + public: + ConnectionGenericFile(); + + ConnectionGenericFile(lldb::file_t file, bool owns_file); + + ~ConnectionGenericFile() override; + + bool IsConnected() const override; + + lldb::ConnectionStatus Connect(const char *s, Error *error_ptr) override; + + lldb::ConnectionStatus Disconnect(Error *error_ptr) override; + + size_t Read(void *dst, size_t dst_len, uint32_t timeout_usec, lldb::ConnectionStatus &status, Error *error_ptr) override; + + size_t Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr) override; + + std::string GetURI() override; + + bool InterruptRead() override; + + protected: + OVERLAPPED m_overlapped; + HANDLE m_file; + HANDLE m_event_handles[2]; + bool m_owns_file; + LARGE_INTEGER m_file_position; + + enum + { + kBytesAvailableEvent, + kInterruptEvent + }; + + private: + void InitializeEventHandles(); + void IncrementFilePointer(DWORD amount); + + std::string m_uri; + + DISALLOW_COPY_AND_ASSIGN(ConnectionGenericFile); +}; +} + +#endif diff --git a/include/lldb/Host/windows/HostInfoWindows.h b/include/lldb/Host/windows/HostInfoWindows.h new file mode 100644 index 000000000000..022e15533d31 --- /dev/null +++ b/include/lldb/Host/windows/HostInfoWindows.h @@ -0,0 +1,46 @@ +//===-- HostInfoWindows.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_Host_windows_HostInfoWindows_h_ +#define lldb_Host_windows_HostInfoWindows_h_ + +#include "lldb/Host/HostInfoBase.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private +{ + +class HostInfoWindows : public HostInfoBase +{ + friend class HostInfoBase; + + private: + // Static class, unconstructable. + HostInfoWindows(); + ~HostInfoWindows(); + + public: + static size_t GetPageSize(); + + static bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update); + static bool GetOSBuildString(std::string &s); + static bool GetOSKernelDescription(std::string &s); + static bool GetHostname(std::string &s); + static FileSpec GetProgramFileSpec(); + static FileSpec GetDefaultShell(); + + protected: + static bool ComputePythonDirectory(FileSpec &file_spec); + + private: + static FileSpec m_program_filespec; +}; +} + +#endif diff --git a/include/lldb/Host/windows/HostProcessWindows.h b/include/lldb/Host/windows/HostProcessWindows.h new file mode 100644 index 000000000000..6f8ad3dc4d40 --- /dev/null +++ b/include/lldb/Host/windows/HostProcessWindows.h @@ -0,0 +1,47 @@ +//===-- HostProcessWindows.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_Host_HostProcessWindows_h_ +#define lldb_Host_HostProcessWindows_h_ + +#include "lldb/Host/HostNativeProcessBase.h" +#include "lldb/lldb-types.h" + +namespace lldb_private +{ + +class FileSpec; + +class HostProcessWindows : public HostNativeProcessBase +{ + public: + HostProcessWindows(); + explicit HostProcessWindows(lldb::process_t process); + ~HostProcessWindows(); + + void SetOwnsHandle(bool owns); + + virtual Error Terminate(); + virtual Error GetMainModule(FileSpec &file_spec) const; + + virtual lldb::pid_t GetProcessId() const; + virtual bool IsRunning() const; + + virtual HostThread StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals); + + private: + static lldb::thread_result_t MonitorThread(void *thread_arg); + + void Close(); + + bool m_owns_handle; +}; +} + +#endif diff --git a/include/lldb/Host/windows/HostThreadWindows.h b/include/lldb/Host/windows/HostThreadWindows.h new file mode 100644 index 000000000000..e0c78c37d69a --- /dev/null +++ b/include/lldb/Host/windows/HostThreadWindows.h @@ -0,0 +1,42 @@ +//===-- HostThreadWindows.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_Host_windows_HostThreadWindows_h_ +#define lldb_Host_windows_HostThreadWindows_h_ + +#include "lldb/Host/HostNativeThreadBase.h" + +#include "llvm/ADT/SmallString.h" + +namespace lldb_private +{ + +class HostThreadWindows : public HostNativeThreadBase +{ + DISALLOW_COPY_AND_ASSIGN(HostThreadWindows); + + public: + HostThreadWindows(); + HostThreadWindows(lldb::thread_t thread); + virtual ~HostThreadWindows(); + + void SetOwnsHandle(bool owns); + + virtual Error Join(lldb::thread_result_t *result); + virtual Error Cancel(); + virtual void Reset(); + + lldb::tid_t GetThreadId() const; + + private: + bool m_owns_handle; +}; +} + +#endif diff --git a/include/lldb/Host/windows/LockFileWindows.h b/include/lldb/Host/windows/LockFileWindows.h new file mode 100644 index 000000000000..b2b8896f18c4 --- /dev/null +++ b/include/lldb/Host/windows/LockFileWindows.h @@ -0,0 +1,49 @@ +//===-- LockFileWindows.h ---------------------------------------*- 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_posix_LockFileWindows_h_ +#define liblldb_Host_posix_LockFileWindows_h_ + +#include "lldb/Host/LockFileBase.h" +#include "lldb/Host/windows/windows.h" + +namespace lldb_private { + +class LockFileWindows : public LockFileBase +{ +public: + explicit LockFileWindows (int fd); + ~LockFileWindows (); + +protected: + Error + DoWriteLock (const uint64_t start, const uint64_t len) override; + + Error + DoTryWriteLock (const uint64_t start, const uint64_t len) override; + + Error + DoReadLock (const uint64_t start, const uint64_t len) override; + + Error + DoTryReadLock (const uint64_t start, const uint64_t len) override; + + Error + DoUnlock () override; + + bool + IsValidFile () const override; + +private: + HANDLE m_file; +}; + +} // namespace lldb_private + +#endif // liblldb_Host_posix_LockFileWindows_h_ diff --git a/include/lldb/Host/windows/PipeWindows.h b/include/lldb/Host/windows/PipeWindows.h new file mode 100644 index 000000000000..7170c7c36815 --- /dev/null +++ b/include/lldb/Host/windows/PipeWindows.h @@ -0,0 +1,74 @@ +//===-- PipePosix.h ---------------------------------------------*- 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_windows_PipeWindows_h_ +#define liblldb_Host_windows_PipeWindows_h_ + +#include "lldb/Host/PipeBase.h" +#include "lldb/Host/windows/windows.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class Pipe PipeWindows.h "lldb/Host/windows/PipeWindows.h" +/// @brief A windows-based implementation of Pipe, a class that abtracts +/// unix style pipes. +/// +/// A class that abstracts the LLDB core from host pipe functionality. +//---------------------------------------------------------------------- +class PipeWindows : public PipeBase +{ + public: + PipeWindows(); + ~PipeWindows() override; + + Error CreateNew(bool child_process_inherit) override; + Error CreateNew(llvm::StringRef name, bool child_process_inherit) override; + Error CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl& name) override; + Error OpenAsReader(llvm::StringRef name, bool child_process_inherit) override; + Error OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout) override; + + bool CanRead() const override; + bool CanWrite() const override; + + int GetReadFileDescriptor() const override; + int GetWriteFileDescriptor() const override; + int ReleaseReadFileDescriptor() override; + int ReleaseWriteFileDescriptor() override; + void CloseReadFileDescriptor() override; + void CloseWriteFileDescriptor() override; + + void Close() override; + + Error Delete(llvm::StringRef name) override; + + Error Write(const void *buf, size_t size, size_t &bytes_written) override; + Error ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) override; + + // PipeWindows specific methods. These allow access to the underlying OS handle. + HANDLE GetReadNativeHandle(); + HANDLE GetWriteNativeHandle(); + + private: + Error OpenNamedPipe(llvm::StringRef name, bool child_process_inherit, bool is_read); + + HANDLE m_read; + HANDLE m_write; + + int m_read_fd; + int m_write_fd; + + OVERLAPPED m_read_overlapped; + OVERLAPPED m_write_overlapped; +}; + +} // namespace lldb_private + +#endif // liblldb_Host_posix_PipePosix_h_ diff --git a/include/lldb/Host/windows/ProcessLauncherWindows.h b/include/lldb/Host/windows/ProcessLauncherWindows.h new file mode 100644 index 000000000000..a2fe665dcfaf --- /dev/null +++ b/include/lldb/Host/windows/ProcessLauncherWindows.h @@ -0,0 +1,31 @@ +//===-- ProcessLauncherWindows.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_Host_windows_ProcessLauncherWindows_h_ +#define lldb_Host_windows_ProcessLauncherWindows_h_ + +#include "lldb/Host/ProcessLauncher.h" +#include "lldb/Host/windows/windows.h" + +namespace lldb_private +{ + +class ProcessLaunchInfo; + +class ProcessLauncherWindows : public ProcessLauncher +{ + public: + virtual HostProcess LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error); + + protected: + HANDLE GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd); +}; +} + +#endif diff --git a/include/lldb/Host/windows/editlinewin.h b/include/lldb/Host/windows/editlinewin.h new file mode 100644 index 000000000000..907ef373a372 --- /dev/null +++ b/include/lldb/Host/windows/editlinewin.h @@ -0,0 +1,123 @@ +//===-- ELWrapper.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include + +// EditLine editor function return codes. +// For user-defined function interface +#define CC_NORM 0 +#define CC_NEWLINE 1 +#define CC_EOF 2 +#define CC_ARGHACK 3 +#define CC_REFRESH 4 +#define CC_CURSOR 5 +#define CC_ERROR 6 +#define CC_FATAL 7 +#define CC_REDISPLAY 8 +#define CC_REFRESH_BEEP 9 + +// el_set/el_get parameters +#define EL_PROMPT 0 // , el_pfunc_t +#define EL_TERMINAL 1 // , const char * +#define EL_EDITOR 2 // , const char * +#define EL_SIGNAL 3 // , int); +#define EL_BIND 4 // , const char *, ..., NULL +#define EL_TELLTC 5 // , const char *, ..., NULL +#define EL_SETTC 6 // , const char *, ..., NULL +#define EL_ECHOTC 7 // , const char *, ..., NULL +#define EL_SETTY 8 // , const char *, ..., NULL +#define EL_ADDFN 9 // , const char *, const char *, el_func_t +#define EL_HIST 10 // , hist_fun_t, const char * +#define EL_EDITMODE 11 // , int +#define EL_RPROMPT 12 // , el_pfunc_t +#define EL_GETCFN 13 // , el_rfunc_t +#define EL_CLIENTDATA 14 // , void * +#define EL_UNBUFFERED 15 // , int +#define EL_PREP_TERM 16 // , int +#define EL_GETTC 17 // , const char *, ..., NULL +#define EL_GETFP 18 // , int, FILE ** +#define EL_SETFP 19 // , int, FILE * +#define EL_REFRESH 20 // , void +#define EL_PROMPT_ESC 21 // , prompt_func, Char); set/get + +#define EL_BUILTIN_GETCFN (NULL) + +// history defines +#define H_FUNC 0 // , UTSL +#define H_SETSIZE 1 // , const int +#define H_GETSIZE 2 // , void +#define H_FIRST 3 // , void +#define H_LAST 4 // , void +#define H_PREV 5 // , void +#define H_NEXT 6 // , void +#define H_CURR 8 // , const int +#define H_SET 7 // , int +#define H_ADD 9 // , const char * +#define H_ENTER 10 // , const char * +#define H_APPEND 11 // , const char * +#define H_END 12 // , void +#define H_NEXT_STR 13 // , const char * +#define H_PREV_STR 14 // , const char * +#define H_NEXT_EVENT 15 // , const int +#define H_PREV_EVENT 16 // , const int +#define H_LOAD 17 // , const char * +#define H_SAVE 18 // , const char * +#define H_CLEAR 19 // , void +#define H_SETUNIQUE 20 // , int +#define H_GETUNIQUE 21 // , void +#define H_DEL 22 // , int + +struct EditLine +{ +}; + +struct LineInfo +{ + const char *buffer; + const char *cursor; + const char *lastchar; +}; + +struct History +{ +}; + +struct HistEvent +{ + int num; + const char *str; +}; + +extern "C" +{ + // edit line API + EditLine *el_init ( const char *, FILE *, FILE *, FILE * ); + const char *el_gets ( EditLine *, int * ); + int el_set ( EditLine *, int, ... ); + + void el_end ( EditLine * ); + void el_reset ( EditLine * ); + int el_getc ( EditLine *, char * ); + void el_push ( EditLine *, const char * ); + void el_beep ( EditLine * ); + int el_parse ( EditLine *, int, const char ** ); + int el_get ( EditLine *, int, ... ); + int el_source ( EditLine *, const char * ); + void el_resize ( EditLine * ); + const LineInfo *el_line ( EditLine * ); + int el_insertstr( EditLine *, const char * ); + void el_deletestr( EditLine *, int ); + + // history API + History *history_init( void ); + void history_end ( History * ); + int history ( History *, HistEvent *, int, ... ); +}; \ No newline at end of file diff --git a/include/lldb/Host/windows/win32.h b/include/lldb/Host/windows/win32.h new file mode 100644 index 000000000000..2789a4b84f07 --- /dev/null +++ b/include/lldb/Host/windows/win32.h @@ -0,0 +1,107 @@ +//===-- lldb-win32.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_win32_h_ +#define LLDB_lldb_win32_h_ + +#include +#include + +// posix utilities +int vasprintf(char **ret, const char *fmt, va_list ap); +char * strcasestr(const char *s, const char* find); +char* realpath(const char * name, char * resolved); + +#ifndef PATH_MAX +#define PATH_MAX 32768 +#endif + +#define O_NOCTTY 0 +#define O_NONBLOCK 0 +#define SIGTRAP 5 +#define SIGKILL 9 +#define SIGSTOP 20 + +#if defined(_MSC_VER) +# define S_IRUSR S_IREAD /* read, user */ +# define S_IWUSR S_IWRITE /* write, user */ +# define S_IXUSR 0 /* execute, user */ +#endif +#define S_IRGRP 0 /* read, group */ +#define S_IWGRP 0 /* write, group */ +#define S_IXGRP 0 /* execute, group */ +#define S_IROTH 0 /* read, others */ +#define S_IWOTH 0 /* write, others */ +#define S_IXOTH 0 /* execute, others */ +#define S_IRWXU 0 +#define S_IRWXG 0 +#define S_IRWXO 0 + +#ifdef _MSC_VER + +#include +#include +#include +typedef unsigned short mode_t; + +#ifdef LLDB_DISABLE_PYTHON +typedef uint32_t pid_t; +#endif // LLDB_DISABLE_PYTHON + +int usleep(uint32_t useconds); + +char* getcwd(char* path, int max); +int chdir(const char* path); +char* basename(char *path); +char *dirname(char *path); + +int strcasecmp(const char* s1, const char* s2); +int strncasecmp(const char* s1, const char* s2, size_t n); + +#if _MSC_VER < 1900 +namespace lldb_private { +int vsnprintf(char *buffer, size_t count, const char *format, va_list argptr); +} + +// inline to avoid linkage conflicts +int inline snprintf(char *buffer, size_t count, const char *format, ...) +{ + va_list argptr; + va_start(argptr, format); + int r = lldb_private::vsnprintf(buffer, count, format, argptr); + va_end(argptr); + return r; +} +#endif + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#define __PRETTY_FUNCTION__ __FUNCSIG__ + +#define S_IFDIR _S_IFDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) + +#endif // _MSC_VER + +// timespec +// MSVC 2015 and higher have timespec. Otherwise we need to define it ourselves. +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#include +#else +struct timespec +{ + time_t tv_sec; + long tv_nsec; +}; +#endif + + +#endif // LLDB_lldb_win32_h_ diff --git a/include/lldb/Host/windows/windows.h b/include/lldb/Host/windows/windows.h new file mode 100644 index 000000000000..124e8de1dc90 --- /dev/null +++ b/include/lldb/Host/windows/windows.h @@ -0,0 +1,29 @@ +//===-- lldb-windows.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_windows_h_ +#define LLDB_lldb_windows_h_ + +#define NTDDI_VERSION NTDDI_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#define NOMINMAX +#include +#undef GetUserName +#undef LoadImage +#undef CreateProcess +#undef far +#undef near +#undef FAR +#undef NEAR +#define FAR +#define NEAR + +#endif // LLDB_lldb_windows_h_ diff --git a/include/lldb/Makefile b/include/lldb/Makefile new file mode 100644 index 000000000000..6066298fce4a --- /dev/null +++ b/include/lldb/Makefile @@ -0,0 +1,31 @@ +LEVEL = ../../../.. +DIRS := + +include $(LEVEL)/Makefile.common + +install-local:: + $(Echo) Installing LLDB include files + $(Verb) $(MKDIR) $(DESTDIR)$(PROJ_includedir) + $(Verb) if test -d "$(PROJ_SRC_ROOT)/tools/lldb/include/lldb" ; then \ + cd $(PROJ_SRC_ROOT)/tools/lldb/include && \ + for hdr in `find lldb -type f '!' '(' -name '*~' \ + -o -name '.#*' -o -name '*.in' -o -name '*.txt' \ + -o -name 'Makefile' -o -name '*.td' -o -name '*.orig' ')' -print \ + | grep -v CVS | grep -v .svn | grep -v .dir` ; do \ + instdir=$(DESTDIR)`dirname "$(PROJ_includedir)/$$hdr"` ; \ + if test \! -d "$$instdir" ; then \ + $(EchoCmd) Making install directory $$instdir ; \ + $(MKDIR) $$instdir ;\ + fi ; \ + $(DataInstall) $$hdr $(DESTDIR)$(PROJ_includedir)/$$hdr ; \ + done ; \ + fi +ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT)) + $(Verb) if test -d "$(PROJ_OBJ_ROOT)/tools/lldb/include/lldb" ; then \ + cd $(PROJ_OBJ_ROOT)/tools/lldb/include && \ + for hdr in `find lldb -type f '!' '(' -name 'Makefile' ')' -print \ + | grep -v CVS | grep -v .tmp | grep -v .dir` ; do \ + $(DataInstall) $$hdr $(DESTDIR)$(PROJ_includedir)/$$hdr ; \ + done ; \ + fi +endif diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 000000000000..e2388e05833e --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,217 @@ +##===- source/Makefile -------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../../.. +LLDB_LEVEL := .. + +LIBRARYNAME = lldb + +#EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/resources/lldb-framework-exports +NO_BUILD_ARCHIVE = 1 +LINK_LIBS_IN_SHARED = 1 +SHARED_LIBRARY = 1 + +ifeq (,$(findstring -DLLDB_DISABLE_PYTHON,$(CXXFLAGS))) +PYTHON_CONFIG?= python-config +PYTHON_BUILD_FLAGS = $(shell $(PYTHON_CONFIG) --ldflags) +endif + +# Include all archives in liblldb.so file +USEDLIBS = lldbAPI.a \ + lldbBreakpoint.a \ + lldbCommands.a \ + lldbCore.a \ + lldbDataFormatters.a \ + lldbExpression.a \ + lldbInitialization.a \ + lldbHost.a \ + lldbBase.a \ + lldbInterpreter.a \ + lldbPluginABIMacOSX_arm.a \ + lldbPluginABIMacOSX_arm64.a \ + lldbPluginABIMacOSX_i386.a \ + lldbPluginABISysV_arm.a \ + lldbPluginABISysV_arm64.a \ + lldbPluginABISysV_ppc.a \ + lldbPluginABISysV_ppc64.a \ + lldbPluginABISysV_mips.a \ + lldbPluginABISysV_mips64.a \ + lldbPluginABISysV_i386.a \ + lldbPluginABISysV_x86_64.a \ + lldbPluginABISysV_hexagon.a \ + lldbPluginDisassemblerLLVM.a \ + lldbPluginDynamicLoaderStatic.a \ + lldbPluginDynamicLoaderPosixDYLD.a \ + lldbPluginDynamicLoaderHexagon.a \ + lldbPluginDynamicLoaderMacOSXDYLD.a \ + lldbPluginDynamicLoaderWindowsDYLD.a \ + lldbPluginExpressionParserClang.a \ + lldbPluginExpressionParserGo.a \ + lldbPluginInstructionARM.a \ + lldbPluginInstructionARM64.a \ + lldbPluginInstructionMIPS.a \ + lldbPluginInstructionMIPS64.a \ + lldbPluginInstrumentationRuntimeAddressSanitizer.a \ + lldbPluginCXXItaniumABI.a \ + lldbPluginAppleObjCRuntime.a \ + lldbPluginRenderScriptRuntime.a \ + lldbPluginMemoryHistoryASan.a \ + lldbPluginCPlusPlusLanguage.a \ + lldbPluginLanguageRuntimeGo.a \ + lldbPluginObjCLanguage.a \ + lldbPluginObjCPlusPlusLanguage.a \ + lldbPluginGoLanguage.a \ + lldbPluginObjectContainerBSDArchive.a \ + lldbPluginObjectContainerMachOArchive.a \ + lldbPluginObjectFileELF.a \ + lldbPluginObjectFileJIT.a \ + lldbPluginSymbolVendorELF.a \ + lldbPluginObjectFilePECOFF.a \ + lldbPluginOSGo.a \ + lldbPluginOSPython.a \ + lldbPluginPlatformGDB.a \ + lldbPluginProcessElfCore.a \ + lldbPluginProcessGDBRemote.a \ + lldbPluginSymbolFileDWARF.a \ + lldbPluginSymbolFileSymtab.a \ + lldbPluginSystemRuntimeMacOSX.a \ + lldbPluginUnwindAssemblyInstEmulation.a \ + lldbPluginUnwindAssemblyX86.a \ + lldbPluginProcessUtility.a \ + lldbSymbol.a \ + lldbTarget.a \ + lldbUtility.a \ + clangAnalysis.a \ + clangAST.a \ + clangBasic.a \ + clangCodeGen.a \ + clangFrontend.a \ + clangDriver.a \ + clangEdit.a \ + clangLex.a \ + clangParse.a \ + clangSema.a \ + clangSerialization.a \ + LLVMMCDisassembler.a \ + LLVMProfileData.a \ + LLVMObjCARCOpts.a \ + lldbPluginPlatformMacOSX.a \ + lldbPluginPlatformLinux.a \ + lldbPluginPlatformWindows.a \ + lldbPluginPlatformFreeBSD.a \ + lldbPluginPlatformNetBSD.a \ + lldbPluginPlatformPOSIX.a \ + lldbPluginPlatformKalimba.a \ + lldbPluginPlatformAndroid.a \ + lldbPluginJITLoaderGDB.a \ + lldbPluginScriptInterpreterNone.a \ + lldbPluginScriptInterpreterPython.a + +# Because GCC requires RTTI enabled for lldbCore (see source/Core/Makefile) it is +# necessary to also link the clang rewriter libraries so vtable references can +# be resolved correctly, if we are building with GCC. +ifeq (g++,$(shell basename $(CXX) | cut -c 1-4)) + USEDLIBS += clangRewrite.a \ + clangRewriteFrontend.a +endif + +include $(LLDB_LEVEL)/../../Makefile.config + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader bitwriter codegen \ + instrumentation ipo irreader selectiondag mc mcjit \ + linker option + +ifeq ($(HOST_OS),Darwin) + USEDLIBS += lldbPluginDynamicLoaderDarwinKernel.a \ + lldbPluginObjectFileMachO.a \ + lldbPluginSymbolVendorMacOSX.a \ + lldbPluginProcessDarwin.a \ + lldbPluginProcessMachCore.a +endif + +ifeq ($(HOST_OS),Linux) + USEDLIBS += lldbPluginProcessLinux.a \ + lldbPluginProcessPOSIX.a +endif + +ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD)) + USEDLIBS += lldbPluginProcessPOSIX.a \ + lldbPluginProcessFreeBSD.a +endif + +ifeq ($(HOST_OS),NetBSD) + USEDLIBS += lldbPluginProcessPOSIX.a +endif + +include $(LEVEL)/Makefile.common + +ifeq ($(HOST_OS),MingW) + LLVMLibsOptions += -lws2_32 + # Include everything from the .a's into the shared library. + ProjLibsOptions := -Wl,--whole-archive $(ProjLibsOptions) \ + -Wl,--no-whole-archive +endif + +ifeq ($(HOST_OS),Darwin) + LLVMLibsOptions += -Wl,-all_load + # set dylib internal version number to llvmCore submission number + ifdef LLDB_SUBMIT_VERSION + LLVMLibsOptions += -Wl,-current_version \ + -Wl,$(LLDB_SUBMIT_VERSION).$(LLDB_SUBMIT_SUBVERSION) \ + -Wl,-compatibility_version -Wl,1 + endif + # extra options to override libtool defaults + LLVMLibsOptions += -F/System/Library/Frameworks -F/System/Library/PrivateFrameworks + LLVMLibsOptions += -framework Foundation -framework CoreFoundation + LLVMLibsOptions += -framework CoreServices -framework Carbon -framework Security + LLVMLibsOptions += -framework DebugSymbols $(PYTHON_BUILD_FLAGS) -lobjc + LLVMLibsOptions += -lxml2 -ledit -lpanel -lcurses + ifneq ($(EXPORTED_SYMBOL_FILE),) + LLVMLibsOptions += -Wl,-exported_symbols_list -Wl,"$(EXPORTED_SYMBOL_FILE)" + endif + # Mac OS X 10.4 and earlier tools do not allow a second -install_name on command line + DARWIN_VERS := $(shell echo $(TARGET_TRIPLE) | sed 's/.*darwin\([0-9]*\).*/\1/') + ifneq ($(DARWIN_VERS),8) + LLVMLibsOptions += -Wl,-install_name \ + -Wl,"@executable_path/../lib/lib$(LIBRARYNAME)$(SHLIBEXT)" + endif +endif + +ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux GNU GNU/kFreeBSD)) + # Include everything from the .a's into the shared library. + ProjLibsOptions := -Wl,--whole-archive $(ProjLibsOptions) \ + -Wl,--no-whole-archive + # Don't allow unresolved symbols. + LLVMLibsOptions += -Wl,--no-undefined + # Link in python + LLVMLibsOptions += $(PYTHON_BUILD_FLAGS) -lrt -ledit -lncurses -lpanel -lpthread + LLVMLibsOptions += -Wl,--soname,lib$(LIBRARYNAME)$(SHLIBEXT) +endif + +ifeq ($(HOST_OS),FreeBSD) + # Include everything from the .a's into the shared library. + ProjLibsOptions := -Wl,--whole-archive $(ProjLibsOptions) \ + -Wl,--no-whole-archive + # Allow unresolved symbols. + LLVMLibsOptions += -Wl,--allow-shlib-undefined + # Link in python + LLVMLibsOptions += $(PYTHON_BUILD_FLAGS) -lrt -L/usr/local/lib -lexecinfo \ + -ledit -lncurses -lpanel -lpthread +endif + +ifeq ($(HOST_OS),NetBSD) + # Include everything from the .a's into the shared library. + ProjLibsOptions := -Wl,--whole-archive $(ProjLibsOptions) \ + -Wl,--no-whole-archive + # Allow unresolved symbols. + LLVMLibsOptions += -Wl,--allow-shlib-undefined + # Link in python + LLVMLibsOptions += $(PYTHON_BUILD_FLAGS) -lrt -L/usr/pkg/lib -lexecinfo \ + -ledit -lcurses -lpthread -lkvm -Wl,-rpath,/usr/pkg/lib +endif diff --git a/lit/CMakeLists.txt b/lit/CMakeLists.txt new file mode 100644 index 000000000000..108fc9368732 --- /dev/null +++ b/lit/CMakeLists.txt @@ -0,0 +1,36 @@ +set(LLVM_SOURCE_DIR "${LLVM_MAIN_SRC_DIR}") +set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}") +set(LLVM_BUILD_MODE "%(build_mode)s") +set(LLVM_TOOLS_DIR "${LLVM_TOOLS_BINARY_DIR}/%(build_config)s") +set(LLVM_LIBS_DIR "${LLVM_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/%(build_config)s") +set(CLANG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") +set(CLANG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..") +if(BUILD_SHARED_LIBS) + set(ENABLE_SHARED 1) +else() + set(ENABLE_SHARED 0) +endif(BUILD_SHARED_LIBS) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + ) + +set(LLDB_TEST_DEPS + LLDBUnitTests + ) +set(LLDB_TEST_PARAMS + lldb_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + +add_lit_testsuite(check-lldb-unit "Running lldb unit test suite" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS lldb_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + lldb_unit_site_config=${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + DEPENDS ${LLDB_TEST_DEPS} + ) + +set_target_properties(check-lldb-unit PROPERTIES FOLDER "lldb tests") diff --git a/lit/Unit/lit.cfg b/lit/Unit/lit.cfg new file mode 100644 index 000000000000..3d295475d564 --- /dev/null +++ b/lit/Unit/lit.cfg @@ -0,0 +1,23 @@ +# -*- Python -*- + +# Configuration file for the 'lit' test runner. + +import os + +import lit.formats + +# name: The name of this test suite. +config.name = 'lldb-Unit' + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = [] + +# test_source_root: The root path where unit test binaries are located. +# test_exec_root: The root path where tests should be run. +config.test_source_root = os.path.join(config.lldb_obj_root, 'unittests') +config.test_exec_root = config.test_source_root + +# testFormat: The test format to use to interpret tests. +if not hasattr(config, 'llvm_build_mode'): + lit_config.fatal("unable to find llvm_build_mode value on config") +config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, 'Tests') diff --git a/lit/Unit/lit.site.cfg.in b/lit/Unit/lit.site.cfg.in new file mode 100644 index 000000000000..61c8179af39e --- /dev/null +++ b/lit/Unit/lit.site.cfg.in @@ -0,0 +1,25 @@ +## Autogenerated by LLVM/lld configuration. +# Do not edit! +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.llvm_libs_dir = "@LLVM_LIBS_DIR@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" +config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" +config.lldb_obj_root = "@LLDB_BINARY_DIR@" +config.lldb_src_root = "@LLDB_SOURCE_DIR@" +config.target_triple = "@TARGET_TRIPLE@" +config.python_executable = "@PYTHON_EXECUTABLE@" + +# Support substitution of the tools and libs dirs with user parameters. This is +# used when we can't determine the tool dir at configuration time. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params + config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params + config.llvm_build_mode = config.llvm_build_mode % lit_config.params +except KeyError as e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) + +# Let the main config do the real work. +lit_config.load_config(config, "@LLDB_SOURCE_DIR@/lit/Unit/lit.cfg") diff --git a/lit/lit.cfg b/lit/lit.cfg new file mode 100644 index 000000000000..3f1a90c7f4fa --- /dev/null +++ b/lit/lit.cfg @@ -0,0 +1,152 @@ +# -*- Python -*- + +import os +import platform +import re +import subprocess +import locale + +import lit.formats +import lit.util + +# Configuration file for the 'lit' test runner. + +# name: The name of this test suite. +config.name = 'lldb' + +# testFormat: The test format to use to interpret tests. +# +# For now we require '&&' between commands, until they get globally killed and +# the test runner updated. +execute_external = (platform.system() != 'Windows' + or lit_config.getBashPath() not in [None, ""]) +config.test_format = lit.formats.ShTest(execute_external) + +# suffixes: We only support unit tests +config.suffixes = [] + +# test_source_root: The root path where tests are located. +config.test_source_root = os.path.dirname(__file__) + +# test_exec_root: The root path where tests should be run. +lldb_obj_root = getattr(config, 'lldb_obj_root', None) +if lldb_obj_root is not None: + config.test_exec_root = os.path.join(lldb_obj_root, 'lit') + +# Set llvm_{src,obj}_root for use by others. +config.llvm_src_root = getattr(config, 'llvm_src_root', None) +config.llvm_obj_root = getattr(config, 'llvm_obj_root', None) + +# Tweak the PATH to include the tools dir and the scripts dir. +if lldb_obj_root is not None: + llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) + if not llvm_tools_dir: + lit_config.fatal('No LLVM tools dir set!') + path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH'])) + path = os.path.pathsep.join((os.path.join(getattr(config, 'llvm_src_root', None),'test','Scripts'),path)) + + config.environment['PATH'] = path + + llvm_libs_dir = getattr(config, 'llvm_libs_dir', None) + if not llvm_libs_dir: + lit_config.fatal('No LLVM libs dir set!') + path = os.path.pathsep.join((llvm_libs_dir, + config.environment.get('LD_LIBRARY_PATH',''))) + config.environment['LD_LIBRARY_PATH'] = path + + # Propagate LLVM_SRC_ROOT into the environment. + config.environment['LLVM_SRC_ROOT'] = getattr(config, 'llvm_src_root', '') + + # Propagate PYTHON_EXECUTABLE into the environment + config.environment['PYTHON_EXECUTABLE'] = getattr(config, 'python_executable', + '') +### + +# Check that the object root is known. +if config.test_exec_root is None: + # Otherwise, we haven't loaded the site specific configuration (the user is + # probably trying to run on a test file directly, and either the site + # configuration hasn't been created by the build system, or we are in an + # out-of-tree build situation). + + # Check for 'lldb_site_config' user parameter, and use that if available. + site_cfg = lit_config.params.get('lldb_site_config', None) + if site_cfg and os.path.exists(site_cfg): + lit_config.load_config(config, site_cfg) + raise SystemExit + + # Try to detect the situation where we are using an out-of-tree build by + # looking for 'llvm-config'. + # + # FIXME: I debated (i.e., wrote and threw away) adding logic to + # automagically generate the lit.site.cfg if we are in some kind of fresh + # build situation. This means knowing how to invoke the build system though, + # and I decided it was too much magic. We should solve this by just having + # the .cfg files generated during the configuration step. + + llvm_config = lit.util.which('llvm-config', config.environment['PATH']) + if not llvm_config: + lit_config.fatal('No site specific configuration available!') + + # Get the source and object roots. + llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip() + llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip() + lldb_src_root = os.path.join(llvm_src_root, "tools", "lldb") + lldb_obj_root = os.path.join(llvm_obj_root, "tools", "lldb") + + # Validate that we got a tree which points to here, using the standard + # tools/lldb layout. + this_src_root = os.path.dirname(config.test_source_root) + if os.path.realpath(lldb_src_root) != os.path.realpath(this_src_root): + lit_config.fatal('No site specific configuration available!') + + # Check that the site specific configuration exists. + site_cfg = os.path.join(lldb_obj_root, 'test', 'lit.site.cfg') + if not os.path.exists(site_cfg): + lit_config.fatal( + 'No site specific configuration available! You may need to ' + 'run "make test" in your lldb build directory.') + + # Okay, that worked. Notify the user of the automagic, and reconfigure. + lit_config.note('using out-of-tree build at %r' % lldb_obj_root) + lit_config.load_config(config, site_cfg) + raise SystemExit + +# Shell execution +if platform.system() not in ['Windows'] or lit_config.getBashPath() != '': + config.available_features.add('shell') + +# Running on Darwin OS +if platform.system() in ['Darwin']: + config.available_features.add('system-linker-mach-o') + +# Running on ELF based *nix +if platform.system() in ['FreeBSD', 'Linux']: + config.available_features.add('system-linker-elf') + +# llvm-config knows whether it is compiled with asserts (and) +# whether we are operating in release/debug mode. +import subprocess +try: + llvm_config_cmd = \ + subprocess.Popen([os.path.join(llvm_tools_dir, 'llvm-config'), + '--build-mode', '--assertion-mode', '--targets-built'], + stdout = subprocess.PIPE) +except OSError as why: + print("Could not find llvm-config in " + llvm_tools_dir) + exit(42) + +llvm_config_output = llvm_config_cmd.stdout.read().decode('utf_8') +llvm_config_output_list = llvm_config_output.split("\n") + +if re.search(r'DEBUG', llvm_config_output_list[0]): + config.available_features.add('debug') +if re.search(r'ON', llvm_config_output_list[1]): + config.available_features.add('asserts') +if re.search(r'ARM', llvm_config_output_list[2]): + config.available_features.add('arm') +if re.search(r'Mips', llvm_config_output_list[2]): + config.available_features.add('mips') +if re.search(r'X86', llvm_config_output_list[2]): + config.available_features.add('x86') +llvm_config_cmd.wait() diff --git a/lit/lit.site.cfg.in b/lit/lit.site.cfg.in new file mode 100644 index 000000000000..10f3033da4af --- /dev/null +++ b/lit/lit.site.cfg.in @@ -0,0 +1,22 @@ +## Autogenerated by LLVM/lldb configuration. +# Do not edit! +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.llvm_libs_dir = "@LLVM_LIBS_DIR@" +config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" +config.lldb_obj_root = "@LLDB_BINARY_DIR@" +config.target_triple = "@TARGET_TRIPLE@" +config.python_executable = "@PYTHON_EXECUTABLE@" + +# Support substitution of the tools and libs dirs with user parameters. This is +# used when we can't determine the tool dir at configuration time. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params + config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params +except KeyError as e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) + +# Let the main config do the real work. +lit_config.load_config(config, "@LLDB_SOURCE_DIR@/lit/lit.cfg") diff --git a/lldb.xcodeproj/project.pbxproj b/lldb.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..6c73ccd0801e --- /dev/null +++ b/lldb.xcodeproj/project.pbxproj @@ -0,0 +1,9546 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + 26CEF3A914FD58BF007286B2 /* desktop_no_xpc */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 26CEF3AD14FD58BF007286B2 /* Build configuration list for PBXAggregateTarget "desktop_no_xpc" */; + buildPhases = ( + AF90106415AB7D2900FF120D /* CopyFiles */, + ); + dependencies = ( + 26B391F11A6DCCBE00456239 /* PBXTargetDependency */, + 26CEF3B014FD591F007286B2 /* PBXTargetDependency */, + 2687EACD1508115900DD8C2E /* PBXTargetDependency */, + ); + name = desktop_no_xpc; + productName = snowleopard; + }; + 26CEF3B114FD592B007286B2 /* desktop */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 26CEF3B214FD592B007286B2 /* Build configuration list for PBXAggregateTarget "desktop" */; + buildPhases = ( + AF90106415AB7D2900FF120D /* CopyFiles */, + ); + dependencies = ( + 26CEF3BB14FD595B007286B2 /* PBXTargetDependency */, + 26B391EF1A6DCCAF00456239 /* PBXTargetDependency */, + 2687EACB1508115000DD8C2E /* PBXTargetDependency */, + ); + name = desktop; + productName = desktop; + }; + 26CEF3BC14FD596A007286B2 /* ios */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 26CEF3BD14FD596A007286B2 /* Build configuration list for PBXAggregateTarget "ios" */; + buildPhases = ( + AFF87C85150FF5CC000E1742 /* CopyFiles */, + AF3059151B4B390800E25622 /* Run Script - remove unneeded Resources and Swift dirs from iOS LLDB.framework bundle */, + ); + dependencies = ( + 26CEF3C214FD5973007286B2 /* PBXTargetDependency */, + 2687EACF1508116300DD8C2E /* PBXTargetDependency */, + ); + name = ios; + productName = ios; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 23042D121976CA1D00621B2C /* PlatformKalimba.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23042D101976CA0A00621B2C /* PlatformKalimba.cpp */; }; + 23059A0719532B96007B8189 /* LinuxSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23059A0519532B96007B8189 /* LinuxSignals.cpp */; }; + 23059A101958B319007B8189 /* SBUnixSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23059A0F1958B319007B8189 /* SBUnixSignals.cpp */; }; + 23059A121958B3B2007B8189 /* SBUnixSignals.h in Headers */ = {isa = PBXBuildFile; fileRef = 23059A111958B37B007B8189 /* SBUnixSignals.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2326CF441BDD643700A5CEAC /* liblldb-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2689FFCA13353D7A00698AC0 /* liblldb-core.a */; }; + 2326CF491BDD67D800A5CEAC /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2326CF471BDD67C100A5CEAC /* libncurses.dylib */; }; + 2326CF4B1BDD681800A5CEAC /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2326CF4A1BDD681800A5CEAC /* libz.dylib */; }; + 2326CF4D1BDD684B00A5CEAC /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2326CF4C1BDD684B00A5CEAC /* libedit.dylib */; }; + 2326CF4F1BDD687800A5CEAC /* libpanel.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2326CF4E1BDD687800A5CEAC /* libpanel.dylib */; }; + 2326CF521BDD693B00A5CEAC /* EditlineTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2326CF511BDD693B00A5CEAC /* EditlineTest.cpp */; }; + 232CB615191E00CD00EF39FC /* NativeBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 232CB60B191E00CC00EF39FC /* NativeBreakpoint.cpp */; }; + 232CB617191E00CD00EF39FC /* NativeBreakpointList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 232CB60D191E00CC00EF39FC /* NativeBreakpointList.cpp */; }; + 232CB619191E00CD00EF39FC /* NativeProcessProtocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 232CB60F191E00CC00EF39FC /* NativeProcessProtocol.cpp */; }; + 232CB61B191E00CD00EF39FC /* NativeThreadProtocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 232CB611191E00CC00EF39FC /* NativeThreadProtocol.cpp */; }; + 232CB61D191E00CD00EF39FC /* SoftwareBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 232CB613191E00CC00EF39FC /* SoftwareBreakpoint.cpp */; }; + 233B007D1960C9F90090E598 /* ProcessInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B007B1960C9E60090E598 /* ProcessInfo.cpp */; }; + 233B007F1960CB280090E598 /* ProcessLaunchInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B007E1960CB280090E598 /* ProcessLaunchInfo.cpp */; }; + 236124A41986B4E2004EFC37 /* IOObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 236124A21986B4E2004EFC37 /* IOObject.cpp */; }; + 236124A51986B4E2004EFC37 /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 236124A31986B4E2004EFC37 /* Socket.cpp */; }; + 2377C2F819E613C100737875 /* PipePosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2377C2F719E613C100737875 /* PipePosix.cpp */; }; + 239504DE1BDD453200963CEA /* SocketAddressTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F9391BDD332400BA9A93 /* SocketAddressTest.cpp */; }; + 239504DF1BDD453200963CEA /* SocketTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F93A1BDD332400BA9A93 /* SocketTest.cpp */; }; + 239504E01BDD453200963CEA /* SymbolsTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F93B1BDD332400BA9A93 /* SymbolsTest.cpp */; }; + 239504E11BDD453E00963CEA /* TestArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F93E1BDD33CE00BA9A93 /* TestArgs.cpp */; }; + 239504E21BDD454500963CEA /* PythonDataObjectsTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F94D1BDD360F00BA9A93 /* PythonDataObjectsTests.cpp */; }; + 239504E31BDD454B00963CEA /* StringExtractorTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F9441BDD346100BA9A93 /* StringExtractorTest.cpp */; }; + 239504E41BDD454B00963CEA /* TaskPoolTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F9451BDD346100BA9A93 /* TaskPoolTest.cpp */; }; + 239504E51BDD454B00963CEA /* UriParserTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2321F9461BDD346100BA9A93 /* UriParserTest.cpp */; }; + 23D4007D1C2101F2000C3885 /* DWARFDebugMacro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23E77CD61C20F29F007192AD /* DWARFDebugMacro.cpp */; }; + 23D4007E1C210201000C3885 /* DebugMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23E77CDB1C20F2F2007192AD /* DebugMacros.cpp */; }; + 23DDF226196C3EE600BB8417 /* CommandOptionValidators.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */; }; + 23EFE389193D1ABC00E54E54 /* SBTypeEnumMember.h in Headers */ = {isa = PBXBuildFile; fileRef = 23EFE388193D1ABC00E54E54 /* SBTypeEnumMember.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 23EFE38B193D1AEC00E54E54 /* SBTypeEnumMember.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23EFE38A193D1AEC00E54E54 /* SBTypeEnumMember.cpp */; }; + 23F4034D1926E0F60046DC9B /* NativeRegisterContextRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23F403481926CC250046DC9B /* NativeRegisterContextRegisterInfo.cpp */; }; + 250D6AE31A9679440049CC70 /* FileSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 250D6AE11A9679270049CC70 /* FileSystem.cpp */; }; + 25420ECD1A6490B8009ADBCB /* OptionValueChar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25420ECC1A6490B8009ADBCB /* OptionValueChar.cpp */; }; + 25420ED21A649D88009ADBCB /* PipeBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25420ED11A649D88009ADBCB /* PipeBase.cpp */; }; + 254FBB951A81AA7F00BD6378 /* SBLaunchInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 254FBB941A81AA7F00BD6378 /* SBLaunchInfo.cpp */; }; + 254FBB971A81B03100BD6378 /* SBLaunchInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 254FBB961A81B03100BD6378 /* SBLaunchInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 254FBBA31A9166F100BD6378 /* SBAttachInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 254FBBA21A9166F100BD6378 /* SBAttachInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 254FBBA51A91670E00BD6378 /* SBAttachInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 254FBBA41A91670E00BD6378 /* SBAttachInfo.cpp */; }; + 255EFF741AFABA720069F277 /* LockFileBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 255EFF731AFABA720069F277 /* LockFileBase.cpp */; }; + 255EFF761AFABA950069F277 /* LockFilePosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 255EFF751AFABA950069F277 /* LockFilePosix.cpp */; }; + 256CBDB11ADD0E1700BC6CDC /* NativeRegisterContextLinux_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 256CBDAB1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm.cpp */; }; + 256CBDB41ADD0EFD00BC6CDC /* RegisterContextPOSIXCore_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 256CBDB21ADD0EFD00BC6CDC /* RegisterContextPOSIXCore_arm.cpp */; }; + 256CBDBA1ADD107200BC6CDC /* RegisterContextLinux_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 256CBDB61ADD107200BC6CDC /* RegisterContextLinux_arm.cpp */; }; + 256CBDBC1ADD107200BC6CDC /* RegisterContextLinux_mips64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 256CBDB81ADD107200BC6CDC /* RegisterContextLinux_mips64.cpp */; }; + 256CBDC01ADD11C000BC6CDC /* RegisterContextPOSIX_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 256CBDBE1ADD11C000BC6CDC /* RegisterContextPOSIX_arm.cpp */; }; + 2579065C1BD0488100178368 /* TCPSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2579065A1BD0488100178368 /* TCPSocket.cpp */; }; + 2579065D1BD0488100178368 /* UDPSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2579065B1BD0488100178368 /* UDPSocket.cpp */; }; + 2579065F1BD0488D00178368 /* DomainSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2579065E1BD0488D00178368 /* DomainSocket.cpp */; }; + 257906641BD5AFD000178368 /* Acceptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 257906621BD5AFD000178368 /* Acceptor.cpp */; }; + 257906651BD5AFD000178368 /* Acceptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 257906631BD5AFD000178368 /* Acceptor.h */; }; + 257E47171AA56C2000A62F81 /* ModuleCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 257E47151AA56C2000A62F81 /* ModuleCache.cpp */; }; + 25EF23781AC09B3700908DF0 /* AdbClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25EF23751AC09AD800908DF0 /* AdbClient.cpp */; }; + 260157C61885F51C00F875CF /* libpanel.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 260157C41885F4FF00F875CF /* libpanel.dylib */; }; + 260157C81885F53100F875CF /* libpanel.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 260157C41885F4FF00F875CF /* libpanel.dylib */; }; + 2606EDDF184E68A10034641B /* liblldb-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2689FFCA13353D7A00698AC0 /* liblldb-core.a */; }; + 260A63171861008E00FECF8E /* IOHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 260A63161861008E00FECF8E /* IOHandler.h */; }; + 260A63191861009E00FECF8E /* IOHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260A63181861009E00FECF8E /* IOHandler.cpp */; }; + 260CC64815D0440D002BF2E0 /* OptionValueArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC63B15D0440D002BF2E0 /* OptionValueArgs.cpp */; }; + 260CC64915D0440D002BF2E0 /* OptionValueArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC63C15D0440D002BF2E0 /* OptionValueArray.cpp */; }; + 260CC64A15D0440D002BF2E0 /* OptionValueBoolean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC63D15D0440D002BF2E0 /* OptionValueBoolean.cpp */; }; + 260CC64B15D0440D002BF2E0 /* OptionValueProperties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC63E15D0440D002BF2E0 /* OptionValueProperties.cpp */; }; + 260CC64C15D0440D002BF2E0 /* OptionValueDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC63F15D0440D002BF2E0 /* OptionValueDictionary.cpp */; }; + 260CC64D15D0440D002BF2E0 /* OptionValueEnumeration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC64015D0440D002BF2E0 /* OptionValueEnumeration.cpp */; }; + 260CC64E15D0440D002BF2E0 /* OptionValueFileSpec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC64115D0440D002BF2E0 /* OptionValueFileSpec.cpp */; }; + 260CC64F15D0440D002BF2E0 /* OptionValueFileSpecLIst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC64215D0440D002BF2E0 /* OptionValueFileSpecLIst.cpp */; }; + 260CC65015D0440D002BF2E0 /* OptionValueFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC64315D0440D002BF2E0 /* OptionValueFormat.cpp */; }; + 260CC65115D0440D002BF2E0 /* OptionValueSInt64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC64415D0440D002BF2E0 /* OptionValueSInt64.cpp */; }; + 260CC65215D0440D002BF2E0 /* OptionValueString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC64515D0440D002BF2E0 /* OptionValueString.cpp */; }; + 260CC65315D0440D002BF2E0 /* OptionValueUInt64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC64615D0440D002BF2E0 /* OptionValueUInt64.cpp */; }; + 260CC65415D0440D002BF2E0 /* OptionValueUUID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260CC64715D0440D002BF2E0 /* OptionValueUUID.cpp */; }; + 260E07C6136FA69E00CF21D3 /* OptionGroupUUID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E07C5136FA69E00CF21D3 /* OptionGroupUUID.cpp */; }; + 260E07C8136FAB9200CF21D3 /* OptionGroupFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E07C7136FAB9200CF21D3 /* OptionGroupFile.cpp */; }; + 2613F6C81B17B82F00D4DB85 /* CxaDemangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2613F6C71B17B82F00D4DB85 /* CxaDemangle.cpp */; }; + 26151DC31B41E4A200FF7F1C /* SharingPtr.h in Headers */ = {isa = PBXBuildFile; fileRef = 261B5A5311C3F2AD00AABD0A /* SharingPtr.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 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, ); }; }; + 262173A318395D4600C52091 /* SectionLoadHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 262173A218395D4600C52091 /* SectionLoadHistory.cpp */; }; + 26274FA714030F79006BA130 /* DynamicLoaderDarwinKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26274FA514030F79006BA130 /* DynamicLoaderDarwinKernel.cpp */; }; + 2628A4D513D4977900F5487A /* ThreadKDP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2628A4D313D4977900F5487A /* ThreadKDP.cpp */; }; + 262CFC7711A4510000946C6C /* debugserver in Resources */ = {isa = PBXBuildFile; fileRef = 26CE05A0115C31E50022F371 /* debugserver */; }; + 262ED0081631FA3A00879631 /* OptionGroupString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 262ED0071631FA3A00879631 /* OptionGroupString.cpp */; }; + 262F12B51835468600AEB384 /* SBPlatform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 262F12B41835468600AEB384 /* SBPlatform.cpp */; }; + 262F12B71835469C00AEB384 /* SBPlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = 262F12B61835469C00AEB384 /* SBPlatform.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2635879417822FC2004C30BA /* SymbolVendorELF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2635879017822E56004C30BA /* SymbolVendorELF.cpp */; }; + 263641191B34AEE200145B2F /* ABISysV_mips64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263641151B34AEE200145B2F /* ABISysV_mips64.cpp */; }; + 26368A3C126B697600E8659F /* darwin-debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26368A3B126B697600E8659F /* darwin-debug.cpp */; }; + 26368AF7126B960500E8659F /* darwin-debug in Resources */ = {isa = PBXBuildFile; fileRef = 26579F68126A25920007C5CB /* darwin-debug */; }; + 263C4938178B50C40070F12D /* SBModuleSpec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263C4937178B50C40070F12D /* SBModuleSpec.cpp */; }; + 263C493A178B50CF0070F12D /* SBModuleSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 263C4939178B50CF0070F12D /* SBModuleSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 263E949F13661AEA00E7D1CE /* UnwindAssembly-x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263E949D13661AE400E7D1CE /* UnwindAssembly-x86.cpp */; }; + 263FDE601A79A01500E68013 /* FormatEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263FDE5F1A79A01500E68013 /* FormatEntity.cpp */; }; + 2640E19F15DC78FD00F23B50 /* Property.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2640E19E15DC78FD00F23B50 /* Property.cpp */; }; + 2642FBAE13D003B400ED6808 /* CommunicationKDP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2642FBA813D003B400ED6808 /* CommunicationKDP.cpp */; }; + 2642FBB013D003B400ED6808 /* ProcessKDP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2642FBAA13D003B400ED6808 /* ProcessKDP.cpp */; }; + 2642FBB213D003B400ED6808 /* ProcessKDPLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2642FBAC13D003B400ED6808 /* ProcessKDPLog.cpp */; }; + 26474CA818D0CB070073DEBA /* RegisterContextFreeBSD_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CA218D0CB070073DEBA /* RegisterContextFreeBSD_i386.cpp */; }; + 26474CAA18D0CB070073DEBA /* RegisterContextFreeBSD_mips64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CA418D0CB070073DEBA /* RegisterContextFreeBSD_mips64.cpp */; }; + 26474CAC18D0CB070073DEBA /* RegisterContextFreeBSD_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CA618D0CB070073DEBA /* RegisterContextFreeBSD_x86_64.cpp */; }; + 26474CB218D0CB180073DEBA /* RegisterContextLinux_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CAE18D0CB180073DEBA /* RegisterContextLinux_i386.cpp */; }; + 26474CB418D0CB180073DEBA /* RegisterContextLinux_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CB018D0CB180073DEBA /* RegisterContextLinux_x86_64.cpp */; }; + 26474CBC18D0CB2D0073DEBA /* RegisterContextMach_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CB618D0CB2D0073DEBA /* RegisterContextMach_arm.cpp */; }; + 26474CBE18D0CB2D0073DEBA /* RegisterContextMach_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CB818D0CB2D0073DEBA /* RegisterContextMach_i386.cpp */; }; + 26474CC018D0CB2D0073DEBA /* RegisterContextMach_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CBA18D0CB2D0073DEBA /* RegisterContextMach_x86_64.cpp */; }; + 26474CC918D0CB5B0073DEBA /* RegisterContextMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CC218D0CB5B0073DEBA /* RegisterContextMemory.cpp */; }; + 26474CCB18D0CB5B0073DEBA /* RegisterContextPOSIX_mips64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CC418D0CB5B0073DEBA /* RegisterContextPOSIX_mips64.cpp */; }; + 26474CCD18D0CB5B0073DEBA /* RegisterContextPOSIX_x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26474CC618D0CB5B0073DEBA /* RegisterContextPOSIX_x86.cpp */; }; + 26491E3E15E1DB9F00CBFFC2 /* OptionValueRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26491E3D15E1DB9F00CBFFC2 /* OptionValueRegex.cpp */; }; + 264A12FC1372522000875C42 /* EmulateInstructionARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264A12FA1372522000875C42 /* EmulateInstructionARM64.cpp */; }; + 264A1300137252C700875C42 /* ARM64_DWARF_Registers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264A12FE137252C700875C42 /* ARM64_DWARF_Registers.cpp */; }; + 264A58EE1A7DBCAD00A6B1B0 /* OptionValueFormatEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264A58ED1A7DBCAD00A6B1B0 /* OptionValueFormatEntity.cpp */; }; + 264A97BF133918BC0017F0BE /* PlatformRemoteGDBServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264A97BD133918BC0017F0BE /* PlatformRemoteGDBServer.cpp */; }; + 264D8D5013661BD7003A368F /* UnwindAssembly.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D8D4F13661BD7003A368F /* UnwindAssembly.cpp */; }; + 265192C61BA8E905002F08F6 /* CompilerDecl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 265192C51BA8E905002F08F6 /* CompilerDecl.cpp */; }; + 265205A813D3E3F700132FE2 /* RegisterContextKDP_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 265205A213D3E3F700132FE2 /* RegisterContextKDP_arm.cpp */; }; + 265205AA13D3E3F700132FE2 /* RegisterContextKDP_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 265205A413D3E3F700132FE2 /* RegisterContextKDP_i386.cpp */; }; + 265205AC13D3E3F700132FE2 /* RegisterContextKDP_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 265205A613D3E3F700132FE2 /* RegisterContextKDP_x86_64.cpp */; }; + 2656BBC31AE0739C00441749 /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C32A10F3DFDD009D5894 /* libedit.dylib */; }; + 2656BBC41AE073A800441749 /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2670F8111862B44A006B332C /* libncurses.dylib */; }; + 2656BBC51AE073AD00441749 /* libpanel.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 260157C41885F4FF00F875CF /* libpanel.dylib */; }; + 2656BBC61AE073B500441749 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 966C6B7818E6A56A0093F5EC /* libz.dylib */; }; + 2657AFB71B86910100958979 /* CompilerDeclContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2657AFB61B86910100958979 /* CompilerDeclContext.cpp */; }; + 2660AAB914622483003A9694 /* LLDBWrapPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A4EEB511682AAC007A372A /* LLDBWrapPython.cpp */; settings = {COMPILER_FLAGS = "-Dregister="; }; }; + 26651A18133BF9E0005B64B7 /* Opcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26651A17133BF9DF005B64B7 /* Opcode.cpp */; }; + 266603CA1345B5A8004DA8B6 /* ConnectionSharedMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266603C91345B5A8004DA8B6 /* ConnectionSharedMemory.cpp */; }; + 2666ADC61B3CB675001FAFD3 /* DynamicLoaderHexagonDYLD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2666ADC11B3CB675001FAFD3 /* DynamicLoaderHexagonDYLD.cpp */; }; + 2666ADC81B3CB675001FAFD3 /* HexagonDYLDRendezvous.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2666ADC31B3CB675001FAFD3 /* HexagonDYLDRendezvous.cpp */; }; + 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, ); }; }; + 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 */; }; + 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 */; }; + 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 */; }; + 266942001A6DC2AC0063BE93 /* MICmdArgContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941601A6DC2AB0063BE93 /* MICmdArgContext.cpp */; }; + 266942011A6DC2AC0063BE93 /* MICmdArgSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941621A6DC2AB0063BE93 /* MICmdArgSet.cpp */; }; + 266942021A6DC2AC0063BE93 /* MICmdArgValBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941641A6DC2AB0063BE93 /* MICmdArgValBase.cpp */; }; + 266942031A6DC2AC0063BE93 /* MICmdArgValConsume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941661A6DC2AB0063BE93 /* MICmdArgValConsume.cpp */; }; + 266942041A6DC2AC0063BE93 /* MICmdArgValFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941681A6DC2AB0063BE93 /* MICmdArgValFile.cpp */; }; + 266942051A6DC2AC0063BE93 /* MICmdArgValListBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669416A1A6DC2AC0063BE93 /* MICmdArgValListBase.cpp */; }; + 266942061A6DC2AC0063BE93 /* MICmdArgValListOfN.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669416C1A6DC2AC0063BE93 /* MICmdArgValListOfN.cpp */; }; + 266942071A6DC2AC0063BE93 /* MICmdArgValNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669416E1A6DC2AC0063BE93 /* MICmdArgValNumber.cpp */; }; + 266942081A6DC2AC0063BE93 /* MICmdArgValOptionLong.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941701A6DC2AC0063BE93 /* MICmdArgValOptionLong.cpp */; }; + 266942091A6DC2AC0063BE93 /* MICmdArgValOptionShort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941721A6DC2AC0063BE93 /* MICmdArgValOptionShort.cpp */; }; + 2669420A1A6DC2AC0063BE93 /* MICmdArgValString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941741A6DC2AC0063BE93 /* MICmdArgValString.cpp */; }; + 2669420B1A6DC2AC0063BE93 /* MICmdArgValThreadGrp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941761A6DC2AC0063BE93 /* MICmdArgValThreadGrp.cpp */; }; + 2669420C1A6DC2AC0063BE93 /* MICmdBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941781A6DC2AC0063BE93 /* MICmdBase.cpp */; }; + 2669420D1A6DC2AC0063BE93 /* MICmdCmd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669417A1A6DC2AC0063BE93 /* MICmdCmd.cpp */; }; + 2669420E1A6DC2AC0063BE93 /* MICmdCmdBreak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669417C1A6DC2AC0063BE93 /* MICmdCmdBreak.cpp */; }; + 2669420F1A6DC2AC0063BE93 /* MICmdCmdData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669417E1A6DC2AC0063BE93 /* MICmdCmdData.cpp */; }; + 266942101A6DC2AC0063BE93 /* MICmdCmdEnviro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941801A6DC2AC0063BE93 /* MICmdCmdEnviro.cpp */; }; + 266942111A6DC2AC0063BE93 /* MICmdCmdExec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941821A6DC2AC0063BE93 /* MICmdCmdExec.cpp */; }; + 266942121A6DC2AC0063BE93 /* MICmdCmdFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941841A6DC2AC0063BE93 /* MICmdCmdFile.cpp */; }; + 266942131A6DC2AC0063BE93 /* MICmdCmdGdbInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941861A6DC2AC0063BE93 /* MICmdCmdGdbInfo.cpp */; }; + 266942141A6DC2AC0063BE93 /* MICmdCmdGdbSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941881A6DC2AC0063BE93 /* MICmdCmdGdbSet.cpp */; }; + 266942151A6DC2AC0063BE93 /* MICmdCmdGdbThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669418A1A6DC2AC0063BE93 /* MICmdCmdGdbThread.cpp */; }; + 266942161A6DC2AC0063BE93 /* MICmdCmdMiscellanous.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669418C1A6DC2AC0063BE93 /* MICmdCmdMiscellanous.cpp */; }; + 266942171A6DC2AC0063BE93 /* MICmdCmdStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669418E1A6DC2AC0063BE93 /* MICmdCmdStack.cpp */; }; + 266942181A6DC2AC0063BE93 /* MICmdCmdSupportInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941901A6DC2AC0063BE93 /* MICmdCmdSupportInfo.cpp */; }; + 266942191A6DC2AC0063BE93 /* MICmdCmdSupportList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941921A6DC2AC0063BE93 /* MICmdCmdSupportList.cpp */; }; + 2669421A1A6DC2AC0063BE93 /* MICmdCmdTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941941A6DC2AC0063BE93 /* MICmdCmdTarget.cpp */; }; + 2669421B1A6DC2AC0063BE93 /* MICmdCmdThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941961A6DC2AC0063BE93 /* MICmdCmdThread.cpp */; }; + 2669421C1A6DC2AC0063BE93 /* MICmdCmdTrace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941981A6DC2AC0063BE93 /* MICmdCmdTrace.cpp */; }; + 2669421D1A6DC2AC0063BE93 /* MICmdCmdVar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669419A1A6DC2AC0063BE93 /* MICmdCmdVar.cpp */; }; + 2669421E1A6DC2AC0063BE93 /* MICmdCommands.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669419C1A6DC2AC0063BE93 /* MICmdCommands.cpp */; }; + 2669421F1A6DC2AC0063BE93 /* MICmdData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2669419E1A6DC2AC0063BE93 /* MICmdData.cpp */; }; + 266942201A6DC2AC0063BE93 /* MICmdFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941A01A6DC2AC0063BE93 /* MICmdFactory.cpp */; }; + 266942211A6DC2AC0063BE93 /* MICmdInterpreter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941A21A6DC2AC0063BE93 /* MICmdInterpreter.cpp */; }; + 266942221A6DC2AC0063BE93 /* MICmdInvoker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941A41A6DC2AC0063BE93 /* MICmdInvoker.cpp */; }; + 266942231A6DC2AC0063BE93 /* MICmdMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941A61A6DC2AC0063BE93 /* MICmdMgr.cpp */; }; + 266942241A6DC2AC0063BE93 /* MICmdMgrSetCmdDeleteCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941A81A6DC2AC0063BE93 /* MICmdMgrSetCmdDeleteCallback.cpp */; }; + 266942251A6DC2AC0063BE93 /* MICmnBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941AA1A6DC2AC0063BE93 /* MICmnBase.cpp */; }; + 266942261A6DC2AC0063BE93 /* MICmnLLDBBroadcaster.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941AD1A6DC2AC0063BE93 /* MICmnLLDBBroadcaster.cpp */; }; + 266942271A6DC2AC0063BE93 /* MICmnLLDBDebugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941AF1A6DC2AC0063BE93 /* MICmnLLDBDebugger.cpp */; }; + 266942281A6DC2AC0063BE93 /* MICmnLLDBDebuggerHandleEvents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941B11A6DC2AC0063BE93 /* MICmnLLDBDebuggerHandleEvents.cpp */; }; + 266942291A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941B31A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfo.cpp */; }; + 2669422A1A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfoVarObj.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941B51A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfoVarObj.cpp */; }; + 2669422B1A6DC2AC0063BE93 /* MICmnLLDBProxySBValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941B71A6DC2AC0063BE93 /* MICmnLLDBProxySBValue.cpp */; }; + 2669422C1A6DC2AC0063BE93 /* MICmnLLDBUtilSBValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941B91A6DC2AC0063BE93 /* MICmnLLDBUtilSBValue.cpp */; }; + 2669422D1A6DC2AC0063BE93 /* MICmnLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941BB1A6DC2AC0063BE93 /* MICmnLog.cpp */; }; + 2669422E1A6DC2AC0063BE93 /* MICmnLogMediumFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941BD1A6DC2AC0063BE93 /* MICmnLogMediumFile.cpp */; }; + 2669422F1A6DC2AC0063BE93 /* MICmnMIOutOfBandRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941BF1A6DC2AC0063BE93 /* MICmnMIOutOfBandRecord.cpp */; }; + 266942301A6DC2AC0063BE93 /* MICmnMIResultRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941C11A6DC2AC0063BE93 /* MICmnMIResultRecord.cpp */; }; + 266942311A6DC2AC0063BE93 /* MICmnMIValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941C31A6DC2AC0063BE93 /* MICmnMIValue.cpp */; }; + 266942321A6DC2AC0063BE93 /* MICmnMIValueConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941C51A6DC2AC0063BE93 /* MICmnMIValueConst.cpp */; }; + 266942331A6DC2AC0063BE93 /* MICmnMIValueList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941C71A6DC2AC0063BE93 /* MICmnMIValueList.cpp */; }; + 266942341A6DC2AC0063BE93 /* MICmnMIValueResult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941C91A6DC2AC0063BE93 /* MICmnMIValueResult.cpp */; }; + 266942351A6DC2AC0063BE93 /* MICmnMIValueTuple.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941CB1A6DC2AC0063BE93 /* MICmnMIValueTuple.cpp */; }; + 266942361A6DC2AC0063BE93 /* MICmnResources.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941CD1A6DC2AC0063BE93 /* MICmnResources.cpp */; }; + 266942371A6DC2AC0063BE93 /* MICmnStreamStderr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941CF1A6DC2AC0063BE93 /* MICmnStreamStderr.cpp */; }; + 266942381A6DC2AC0063BE93 /* MICmnStreamStdin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941D11A6DC2AC0063BE93 /* MICmnStreamStdin.cpp */; }; + 2669423B1A6DC2AC0063BE93 /* MICmnStreamStdout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941D71A6DC2AC0063BE93 /* MICmnStreamStdout.cpp */; }; + 2669423C1A6DC2AC0063BE93 /* MICmnThreadMgrStd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941D91A6DC2AC0063BE93 /* MICmnThreadMgrStd.cpp */; }; + 2669423D1A6DC2AC0063BE93 /* MIDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941DC1A6DC2AC0063BE93 /* MIDriver.cpp */; }; + 2669423E1A6DC2AC0063BE93 /* MIDriverBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941DE1A6DC2AC0063BE93 /* MIDriverBase.cpp */; }; + 2669423F1A6DC2AC0063BE93 /* MIDriverMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941E01A6DC2AC0063BE93 /* MIDriverMain.cpp */; }; + 266942401A6DC2AC0063BE93 /* MIDriverMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941E11A6DC2AC0063BE93 /* MIDriverMgr.cpp */; }; + 266942411A6DC2AC0063BE93 /* MIUtilDateTimeStd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941E41A6DC2AC0063BE93 /* MIUtilDateTimeStd.cpp */; }; + 266942421A6DC2AC0063BE93 /* MIUtilDebug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941E61A6DC2AC0063BE93 /* MIUtilDebug.cpp */; }; + 266942431A6DC2AC0063BE93 /* MIUtilFileStd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941E81A6DC2AC0063BE93 /* MIUtilFileStd.cpp */; }; + 266942441A6DC2AC0063BE93 /* MIUtilMapIdToVariant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941EA1A6DC2AC0063BE93 /* MIUtilMapIdToVariant.cpp */; }; + 266942451A6DC2AC0063BE93 /* MIUtilString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941EE1A6DC2AC0063BE93 /* MIUtilString.cpp */; }; + 2669424A1A6DC2AC0063BE93 /* MIUtilThreadBaseStd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941F81A6DC2AC0063BE93 /* MIUtilThreadBaseStd.cpp */; }; + 2669424B1A6DC2AC0063BE93 /* MIUtilVariant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941FA1A6DC2AC0063BE93 /* MIUtilVariant.cpp */; }; + 2669424C1A6DC2AC0063BE93 /* Platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941FC1A6DC2AC0063BE93 /* Platform.cpp */; }; + 2669424D1A6DC32B0063BE93 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26680207115FD0ED008E1FE4 /* LLDB.framework */; }; + 266DFE9713FD656E00D0C574 /* OperatingSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266DFE9613FD656E00D0C574 /* OperatingSystem.cpp */; }; + 266E82971B8CE3AC008FCA06 /* DWARFDIE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266E82961B8CE3AC008FCA06 /* DWARFDIE.cpp */; }; + 266E829D1B8E542C008FCA06 /* DWARFAttribute.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266E829C1B8E542C008FCA06 /* DWARFAttribute.cpp */; }; + 2670F8121862B44A006B332C /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2670F8111862B44A006B332C /* libncurses.dylib */; }; + 2671A0D013482601003A87BB /* ConnectionMachPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2671A0CF13482601003A87BB /* ConnectionMachPort.cpp */; }; + 26744EF11338317700EF765A /* GDBRemoteCommunicationClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26744EED1338317700EF765A /* GDBRemoteCommunicationClient.cpp */; }; + 26744EF31338317700EF765A /* GDBRemoteCommunicationServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26744EEF1338317700EF765A /* GDBRemoteCommunicationServer.cpp */; }; + 26780C611867C33D00234593 /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2670F8111862B44A006B332C /* libncurses.dylib */; }; + 267A47FB1B1411C40021A5BC /* NativeRegisterContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 267A47FA1B1411C40021A5BC /* NativeRegisterContext.cpp */; }; + 267A47FF1B1411D90021A5BC /* NativeWatchpointList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 267A47FE1B1411D90021A5BC /* NativeWatchpointList.cpp */; }; + 267A48011B1411E40021A5BC /* XML.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 267A48001B1411E40021A5BC /* XML.cpp */; }; + 267C012B136880DF006E963E /* OptionGroupValueObjectDisplay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 267C012A136880DF006E963E /* OptionGroupValueObjectDisplay.cpp */; }; + 267C01371368C49C006E963E /* OptionGroupOutputFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BCFC531368B3E4006DC050 /* OptionGroupOutputFile.cpp */; }; + 267DFB461B06752A00000FB7 /* MICmdArgValPrintValues.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 267DFB441B06752A00000FB7 /* MICmdArgValPrintValues.cpp */; }; + 268648C416531BF800F04704 /* com.apple.debugserver.posix.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 268648C116531BF800F04704 /* com.apple.debugserver.posix.plist */; }; + 268648C516531BF800F04704 /* com.apple.debugserver.applist.internal.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 268648C216531BF800F04704 /* com.apple.debugserver.applist.internal.plist */; }; + 268648C616531BF800F04704 /* com.apple.debugserver.internal.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 268648C316531BF800F04704 /* com.apple.debugserver.internal.plist */; }; + 2686536C1370ACB200D186A3 /* OptionGroupBoolean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2686536B1370ACB200D186A3 /* OptionGroupBoolean.cpp */; }; + 268653701370AE7200D186A3 /* OptionGroupUInt64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2686536F1370AE7200D186A3 /* OptionGroupUInt64.cpp */; }; + 2689000113353DB600698AC0 /* BreakpointResolverAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D0DD5310FE555900271C65 /* BreakpointResolverAddress.cpp */; }; + 2689000313353DB600698AC0 /* BreakpointResolverFileLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D0DD5410FE555900271C65 /* BreakpointResolverFileLine.cpp */; }; + 2689000513353DB600698AC0 /* BreakpointResolverName.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D0DD5510FE555900271C65 /* BreakpointResolverName.cpp */; }; + 2689000713353DB600698AC0 /* BreakpointSite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1310F1B83100F91463 /* BreakpointSite.cpp */; }; + 2689000913353DB600698AC0 /* BreakpointSiteList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1410F1B83100F91463 /* BreakpointSiteList.cpp */; }; + 2689000B13353DB600698AC0 /* Stoppoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1610F1B83100F91463 /* Stoppoint.cpp */; }; + 2689000D13353DB600698AC0 /* StoppointCallbackContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0910F1B83100F91463 /* StoppointCallbackContext.cpp */; }; + 2689000F13353DB600698AC0 /* StoppointLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1710F1B83100F91463 /* StoppointLocation.cpp */; }; + 2689001113353DB600698AC0 /* Watchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1810F1B83100F91463 /* Watchpoint.cpp */; }; + 2689001213353DDE00698AC0 /* CommandObjectApropos.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9637911B6E99A00780E28 /* CommandObjectApropos.cpp */; }; + 2689001313353DDE00698AC0 /* CommandObjectArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 499F381F11A5B3F300F5CE02 /* CommandObjectArgs.cpp */; }; + 2689001413353DDE00698AC0 /* CommandObjectBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E2D10F1B84700F91463 /* CommandObjectBreakpoint.cpp */; }; + 2689001513353DDE00698AC0 /* CommandObjectBreakpointCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A42976211861AA600FE05CD /* CommandObjectBreakpointCommand.cpp */; }; + 2689001613353DDE00698AC0 /* CommandObjectCommands.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5DBBC611E3FEC60035160F /* CommandObjectCommands.cpp */; }; + 2689001713353DDE00698AC0 /* CommandObjectDisassemble.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3010F1B84700F91463 /* CommandObjectDisassemble.cpp */; }; + 2689001813353DDE00698AC0 /* CommandObjectExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3110F1B84700F91463 /* CommandObjectExpression.cpp */; }; + 2689001A13353DDE00698AC0 /* CommandObjectFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672D8461189055500FF4019 /* CommandObjectFrame.cpp */; }; + 2689001B13353DDE00698AC0 /* CommandObjectHelp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3310F1B84700F91463 /* CommandObjectHelp.cpp */; }; + 2689001D13353DDE00698AC0 /* CommandObjectLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264AD83711095BA600E0B039 /* CommandObjectLog.cpp */; }; + 2689001E13353DDE00698AC0 /* CommandObjectMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3610F1B84700F91463 /* CommandObjectMemory.cpp */; }; + 2689001F13353DDE00698AC0 /* CommandObjectPlatform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26879CE71333F58B0012C1F8 /* CommandObjectPlatform.cpp */; }; + 2689002013353DDE00698AC0 /* CommandObjectProcess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3810F1B84700F91463 /* CommandObjectProcess.cpp */; }; + 2689002113353DDE00698AC0 /* CommandObjectQuit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3910F1B84700F91463 /* CommandObjectQuit.cpp */; }; + 2689002213353DDE00698AC0 /* CommandObjectRegister.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3B10F1B84700F91463 /* CommandObjectRegister.cpp */; }; + 2689002313353DDE00698AC0 /* CommandObjectScript.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3D10F1B84700F91463 /* CommandObjectScript.cpp */; }; + 2689002413353DDE00698AC0 /* CommandObjectSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4010F1B84700F91463 /* CommandObjectSettings.cpp */; }; + 2689002513353DDE00698AC0 /* CommandObjectSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4210F1B84700F91463 /* CommandObjectSource.cpp */; }; + 2689002613353DDE00698AC0 /* CommandObjectSyntax.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4510F1B84700F91463 /* CommandObjectSyntax.cpp */; }; + 2689002713353DDE00698AC0 /* CommandObjectTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 269416AD119A024800FF2715 /* CommandObjectTarget.cpp */; }; + 2689002813353DDE00698AC0 /* CommandObjectThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4610F1B84700F91463 /* CommandObjectThread.cpp */; }; + 2689002913353DDE00698AC0 /* CommandObjectVersion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B296983412C2FB2B002D92C3 /* CommandObjectVersion.cpp */; }; + 2689002A13353E0400698AC0 /* Address.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6910F1B85900F91463 /* Address.cpp */; }; + 2689002B13353E0400698AC0 /* AddressRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6A10F1B85900F91463 /* AddressRange.cpp */; }; + 2689002C13353E0400698AC0 /* AddressResolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC7034011752C6B0086C050 /* AddressResolver.cpp */; }; + 2689002D13353E0400698AC0 /* AddressResolverFileLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC7034211752C720086C050 /* AddressResolverFileLine.cpp */; }; + 2689002E13353E0400698AC0 /* AddressResolverName.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC7034411752C790086C050 /* AddressResolverName.cpp */; }; + 2689002F13353E0400698AC0 /* ArchSpec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6B10F1B85900F91463 /* ArchSpec.cpp */; }; + 2689003013353E0400698AC0 /* Baton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A0604811A5D03C00F75969 /* Baton.cpp */; }; + 2689003113353E0400698AC0 /* Broadcaster.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6D10F1B85900F91463 /* Broadcaster.cpp */; }; + 2689003213353E0400698AC0 /* Communication.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6E10F1B85900F91463 /* Communication.cpp */; }; + 2689003313353E0400698AC0 /* Connection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6F10F1B85900F91463 /* Connection.cpp */; }; + 2689003513353E0400698AC0 /* ConstString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9410F1B85900F91463 /* ConstString.cpp */; }; + 2689003613353E0400698AC0 /* DataBufferHeap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7210F1B85900F91463 /* DataBufferHeap.cpp */; }; + 2689003713353E0400698AC0 /* DataBufferMemoryMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7310F1B85900F91463 /* DataBufferMemoryMap.cpp */; }; + 2689003813353E0400698AC0 /* DataExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7110F1B85900F91463 /* DataExtractor.cpp */; }; + 2689003913353E0400698AC0 /* Debugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263664921140A4930075843B /* Debugger.cpp */; }; + 2689003A13353E0400698AC0 /* Disassembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7610F1B85900F91463 /* Disassembler.cpp */; }; + 2689003B13353E0400698AC0 /* EmulateInstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D9FDC812F784FD0003F2EE /* EmulateInstruction.cpp */; }; + 2689003C13353E0400698AC0 /* Error.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7810F1B85900F91463 /* Error.cpp */; }; + 2689003D13353E0400698AC0 /* Event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7910F1B85900F91463 /* Event.cpp */; }; + 2689003E13353E0400698AC0 /* FileSpecList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7B10F1B85900F91463 /* FileSpecList.cpp */; }; + 2689004113353E0400698AC0 /* Listener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7E10F1B85900F91463 /* Listener.cpp */; }; + 2689004213353E0400698AC0 /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7F10F1B85900F91463 /* Log.cpp */; }; + 2689004313353E0400698AC0 /* Mangled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8010F1B85900F91463 /* Mangled.cpp */; }; + 2689004413353E0400698AC0 /* Module.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8110F1B85900F91463 /* Module.cpp */; }; + 2689004513353E0400698AC0 /* ModuleChild.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8210F1B85900F91463 /* ModuleChild.cpp */; }; + 2689004613353E0400698AC0 /* ModuleList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8310F1B85900F91463 /* ModuleList.cpp */; }; + 2689004713353E0400698AC0 /* PluginManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8A10F1B85900F91463 /* PluginManager.cpp */; }; + 2689004813353E0400698AC0 /* RegularExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8C10F1B85900F91463 /* RegularExpression.cpp */; }; + 2689004913353E0400698AC0 /* Scalar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8D10F1B85900F91463 /* Scalar.cpp */; }; + 2689004A13353E0400698AC0 /* SearchFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1510F1B83100F91463 /* SearchFilter.cpp */; }; + 2689004B13353E0400698AC0 /* Section.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8E10F1B85900F91463 /* Section.cpp */; }; + 2689004C13353E0400698AC0 /* SourceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8F10F1B85900F91463 /* SourceManager.cpp */; }; + 2689004D13353E0400698AC0 /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9010F1B85900F91463 /* State.cpp */; }; + 2689004E13353E0400698AC0 /* Stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9110F1B85900F91463 /* Stream.cpp */; }; + 2689004F13353E0400698AC0 /* StreamFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9210F1B85900F91463 /* StreamFile.cpp */; }; + 2689005013353E0400698AC0 /* StreamString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9310F1B85900F91463 /* StreamString.cpp */; }; + 2689005113353E0400698AC0 /* StringList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A35765F116E76B900E8ED2F /* StringList.cpp */; }; + 2689005213353E0400698AC0 /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9610F1B85900F91463 /* Timer.cpp */; }; + 2689005313353E0400698AC0 /* UserID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9810F1B85900F91463 /* UserID.cpp */; }; + 2689005413353E0400698AC0 /* UserSettingsController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A4633DC11F65D9A00955CE1 /* UserSettingsController.cpp */; }; + 2689005513353E0400698AC0 /* UUID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C81CA511335651004BDC5A /* UUID.cpp */; }; + 2689005613353E0400698AC0 /* Value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9910F1B85900F91463 /* Value.cpp */; }; + 2689005713353E0400698AC0 /* ValueObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9A10F1B85900F91463 /* ValueObject.cpp */; }; + 2689005813353E0400698AC0 /* ValueObjectChild.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9B10F1B85900F91463 /* ValueObjectChild.cpp */; }; + 2689005913353E0400698AC0 /* ValueObjectConstResult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26424E3C125986CB0016D82C /* ValueObjectConstResult.cpp */; }; + 2689005A13353E0400698AC0 /* ValueObjectList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9C10F1B85900F91463 /* ValueObjectList.cpp */; }; + 2689005B13353E0400698AC0 /* ValueObjectRegister.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264334381110F63100CDB6C6 /* ValueObjectRegister.cpp */; }; + 2689005C13353E0400698AC0 /* ValueObjectVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9D10F1B85900F91463 /* ValueObjectVariable.cpp */; }; + 2689005D13353E0400698AC0 /* VMRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9E10F1B85900F91463 /* VMRange.cpp */; }; + 2689005E13353E0E00698AC0 /* ClangASTSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49D7072811B5AD11001AD875 /* ClangASTSource.cpp */; }; + 2689005F13353E0E00698AC0 /* ClangFunctionCaller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C98D3DA118FB96F00E575D0 /* ClangFunctionCaller.cpp */; }; + 2689006013353E0E00698AC0 /* ClangExpressionDeclMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49F1A74511B3388F003ED505 /* ClangExpressionDeclMap.cpp */; }; + 2689006113353E0E00698AC0 /* ClangExpressionParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49445C2512245E3600C11A81 /* ClangExpressionParser.cpp */; }; + 2689006313353E0E00698AC0 /* ClangPersistentVariables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49D4FE871210B61C00CDB854 /* ClangPersistentVariables.cpp */; }; + 2689006413353E0E00698AC0 /* ClangUserExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7ED510F1B86700F91463 /* ClangUserExpression.cpp */; }; + 2689006513353E0E00698AC0 /* ClangUtilityFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 497C86BD122823D800B54702 /* ClangUtilityFunction.cpp */; }; + 2689006613353E0E00698AC0 /* DWARFExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7ED810F1B86700F91463 /* DWARFExpression.cpp */; }; + 2689006713353E0E00698AC0 /* ASTDumper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4906FD4012F2255300A2A77C /* ASTDumper.cpp */; }; + 2689006813353E0E00698AC0 /* ASTResultSynthesizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A8A39F11D568A300AD3B68 /* ASTResultSynthesizer.cpp */; }; + 2689006913353E0E00698AC0 /* ASTStructExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 491193501226386000578B7F /* ASTStructExtractor.cpp */; }; + 2689006A13353E0E00698AC0 /* IRDynamicChecks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49CF9829122C70BD007A0B96 /* IRDynamicChecks.cpp */; }; + 2689006B13353E0E00698AC0 /* IRForTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49307AAD11DEA4D90081F992 /* IRForTarget.cpp */; }; + 2689006D13353E0E00698AC0 /* IRExecutionUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C98D3DB118FB96F00E575D0 /* IRExecutionUnit.cpp */; }; + 2689006E13353E1A00698AC0 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C6EA213011581005E16B0 /* File.cpp */; }; + 2689006F13353E1A00698AC0 /* FileSpec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26FA43171301048600E71120 /* FileSpec.cpp */; }; + 2689007013353E1A00698AC0 /* Condition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69A01E1B1236C5D400C660B5 /* Condition.cpp */; }; + 2689007113353E1A00698AC0 /* Host.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69A01E1C1236C5D400C660B5 /* Host.cpp */; }; + 2689007213353E1A00698AC0 /* Mutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69A01E1E1236C5D400C660B5 /* Mutex.cpp */; }; + 2689007313353E1A00698AC0 /* Symbols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69A01E1F1236C5D400C660B5 /* Symbols.cpp */; }; + 2689007413353E1A00698AC0 /* Terminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 268DA873130095ED00C9483A /* Terminal.cpp */; }; + 2689007513353E1A00698AC0 /* TimeValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69A01E201236C5D400C660B5 /* TimeValue.cpp */; }; + 2689007613353E1A00698AC0 /* CFCBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EED10F1B8AD00F91463 /* CFCBundle.cpp */; }; + 2689007713353E1A00698AC0 /* CFCData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EEF10F1B8AD00F91463 /* CFCData.cpp */; }; + 2689007813353E1A00698AC0 /* CFCMutableArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EF110F1B8AD00F91463 /* CFCMutableArray.cpp */; }; + 2689007913353E1A00698AC0 /* CFCMutableDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EF310F1B8AD00F91463 /* CFCMutableDictionary.cpp */; }; + 2689007A13353E1A00698AC0 /* CFCMutableSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EF510F1B8AD00F91463 /* CFCMutableSet.cpp */; }; + 2689007B13353E1A00698AC0 /* CFCString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EF810F1B8AD00F91463 /* CFCString.cpp */; }; + 2689007C13353E1A00698AC0 /* Symbols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2689B0B5113EE47E00A4AEDB /* Symbols.cpp */; }; + 2689007D13353E2200698AC0 /* Args.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6C10F1B85900F91463 /* Args.cpp */; }; + 2689007F13353E2200698AC0 /* CommandCompletions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CB74116BD98B00C7A725 /* CommandCompletions.cpp */; }; + 2689008013353E2200698AC0 /* CommandInterpreter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0810F1B8DD00F91463 /* CommandInterpreter.cpp */; }; + 2689008113353E2200698AC0 /* CommandObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0910F1B8DD00F91463 /* CommandObject.cpp */; }; + 2689008313353E2200698AC0 /* CommandObjectMultiword.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DFBC58113B48F300DD817F /* CommandObjectMultiword.cpp */; }; + 2689008413353E2200698AC0 /* CommandObjectRegexCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DFBC59113B48F300DD817F /* CommandObjectRegexCommand.cpp */; }; + 2689008513353E2200698AC0 /* CommandReturnObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0A10F1B8DD00F91463 /* CommandReturnObject.cpp */; }; + 2689008613353E2200698AC0 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8610F1B85900F91463 /* Options.cpp */; }; + 2689008713353E2200698AC0 /* ScriptInterpreter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A82010B10FFB49800182560 /* ScriptInterpreter.cpp */; }; + 2689008D13353E4200698AC0 /* DynamicLoaderMacOSXDYLD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C897A10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLD.cpp */; }; + 2689008E13353E4200698AC0 /* DynamicLoaderStatic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 268A683D1321B53B000E3FB8 /* DynamicLoaderStatic.cpp */; }; + 2689009613353E4200698AC0 /* ObjectContainerBSDArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A3B4AC1181454800381BC2 /* ObjectContainerBSDArchive.cpp */; }; + 2689009713353E4200698AC0 /* ObjectContainerUniversalMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C898010F57C5600BB2B04 /* ObjectContainerUniversalMachO.cpp */; }; + 2689009813353E4200698AC0 /* ELFHeader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D27C9D11ED3A4E0024D721 /* ELFHeader.cpp */; }; + 2689009913353E4200698AC0 /* ObjectFileELF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C898510F57C5600BB2B04 /* ObjectFileELF.cpp */; }; + 2689009A13353E4200698AC0 /* ObjectFileMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C898810F57C5600BB2B04 /* ObjectFileMachO.cpp */; }; + 2689009B13353E4200698AC0 /* PlatformMacOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C5577B132575AD008FD8FE /* PlatformMacOSX.cpp */; }; + 2689009C13353E4200698AC0 /* PlatformRemoteiOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675F6FE1332BE690067997B /* PlatformRemoteiOS.cpp */; }; + 2689009D13353E4200698AC0 /* GDBRemoteCommunication.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2618EE5B1315B29C001D6D71 /* GDBRemoteCommunication.cpp */; }; + 2689009E13353E4200698AC0 /* GDBRemoteRegisterContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2618EE5D1315B29C001D6D71 /* GDBRemoteRegisterContext.cpp */; }; + 2689009F13353E4200698AC0 /* ProcessGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2618EE5F1315B29C001D6D71 /* ProcessGDBRemote.cpp */; }; + 268900A013353E4200698AC0 /* ProcessGDBRemoteLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2618EE611315B29C001D6D71 /* ProcessGDBRemoteLog.cpp */; }; + 268900A113353E4200698AC0 /* ThreadGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2618EE631315B29C001D6D71 /* ThreadGDBRemote.cpp */; }; + 268900AF13353E5000698AC0 /* UnwindLLDB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF68D32F1255A110002FF25B /* UnwindLLDB.cpp */; }; + 268900B013353E5000698AC0 /* RegisterContextLLDB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF68D2541255416E002FF25B /* RegisterContextLLDB.cpp */; }; + 268900B413353E5000698AC0 /* RegisterContextMacOSXFrameBackchain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E3EEF711A994E800FBADB6 /* RegisterContextMacOSXFrameBackchain.cpp */; }; + 268900B513353E5000698AC0 /* StopInfoMachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2615DBC81208B5FC0021781D /* StopInfoMachException.cpp */; }; + 268900B613353E5000698AC0 /* UnwindMacOSXFrameBackchain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E3EEE311A9901300FBADB6 /* UnwindMacOSXFrameBackchain.cpp */; }; + 268900B713353E5F00698AC0 /* DWARFAbbreviationDeclaration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89B310F57C5600BB2B04 /* DWARFAbbreviationDeclaration.cpp */; }; + 268900B813353E5F00698AC0 /* DWARFCompileUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89B710F57C5600BB2B04 /* DWARFCompileUnit.cpp */; }; + 268900B913353E5F00698AC0 /* DWARFDebugAbbrev.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89B910F57C5600BB2B04 /* DWARFDebugAbbrev.cpp */; }; + 268900BA13353E5F00698AC0 /* DWARFDebugAranges.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89BB10F57C5600BB2B04 /* DWARFDebugAranges.cpp */; }; + 268900BB13353E5F00698AC0 /* DWARFDebugArangeSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89BD10F57C5600BB2B04 /* DWARFDebugArangeSet.cpp */; }; + 268900BC13353E5F00698AC0 /* DWARFDebugInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89BF10F57C5600BB2B04 /* DWARFDebugInfo.cpp */; }; + 268900BD13353E5F00698AC0 /* DWARFDebugInfoEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C110F57C5600BB2B04 /* DWARFDebugInfoEntry.cpp */; }; + 268900BE13353E5F00698AC0 /* DWARFDebugLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C310F57C5600BB2B04 /* DWARFDebugLine.cpp */; }; + 268900BF13353E5F00698AC0 /* DWARFDebugMacinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C510F57C5600BB2B04 /* DWARFDebugMacinfo.cpp */; }; + 268900C013353E5F00698AC0 /* DWARFDebugMacinfoEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C710F57C5600BB2B04 /* DWARFDebugMacinfoEntry.cpp */; }; + 268900C113353E5F00698AC0 /* DWARFDebugPubnames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C910F57C5600BB2B04 /* DWARFDebugPubnames.cpp */; }; + 268900C213353E5F00698AC0 /* DWARFDebugPubnamesSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89CB10F57C5600BB2B04 /* DWARFDebugPubnamesSet.cpp */; }; + 268900C313353E5F00698AC0 /* DWARFDebugRanges.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89CD10F57C5600BB2B04 /* DWARFDebugRanges.cpp */; }; + 268900C413353E5F00698AC0 /* DWARFDefines.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89CF10F57C5600BB2B04 /* DWARFDefines.cpp */; }; + 268900C513353E5F00698AC0 /* DWARFDIECollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89D110F57C5600BB2B04 /* DWARFDIECollection.cpp */; }; + 268900C613353E5F00698AC0 /* DWARFFormValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89D310F57C5600BB2B04 /* DWARFFormValue.cpp */; }; + 268900C913353E5F00698AC0 /* NameToDIE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2618D9EA12406FE600F2B8FE /* NameToDIE.cpp */; }; + 268900CA13353E5F00698AC0 /* SymbolFileDWARF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89D910F57C5600BB2B04 /* SymbolFileDWARF.cpp */; }; + 268900CB13353E5F00698AC0 /* LogChannelDWARF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26109B3B1155D70100CC3529 /* LogChannelDWARF.cpp */; }; + 268900CC13353E5F00698AC0 /* SymbolFileDWARFDebugMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89DB10F57C5600BB2B04 /* SymbolFileDWARFDebugMap.cpp */; }; + 268900CD13353E5F00698AC0 /* UniqueDWARFASTType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26B8B42312EEC52A00A831B2 /* UniqueDWARFASTType.cpp */; }; + 268900CE13353E5F00698AC0 /* SymbolFileSymtab.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89DE10F57C5600BB2B04 /* SymbolFileSymtab.cpp */; }; + 268900CF13353E5F00698AC0 /* SymbolVendorMacOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89E210F57C5600BB2B04 /* SymbolVendorMacOSX.cpp */; }; + 268900D013353E6F00698AC0 /* Block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1310F1B8EC00F91463 /* Block.cpp */; }; + 268900D113353E6F00698AC0 /* ClangASTContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1410F1B8EC00F91463 /* ClangASTContext.cpp */; }; + 268900D213353E6F00698AC0 /* CompilerType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49E45FAD11F660FE008F7B28 /* CompilerType.cpp */; }; + 268900D313353E6F00698AC0 /* ClangExternalASTSourceCallbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E69030129C6BEF00DDECD9 /* ClangExternalASTSourceCallbacks.cpp */; }; + 268900D513353E6F00698AC0 /* CompileUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1510F1B8EC00F91463 /* CompileUnit.cpp */; }; + 268900D613353E6F00698AC0 /* Declaration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1610F1B8EC00F91463 /* Declaration.cpp */; }; + 268900D713353E6F00698AC0 /* DWARFCallFrameInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1710F1B8EC00F91463 /* DWARFCallFrameInfo.cpp */; }; + 268900D813353E6F00698AC0 /* Function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1810F1B8EC00F91463 /* Function.cpp */; }; + 268900D913353E6F00698AC0 /* FuncUnwinders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 961FABB81235DE1600F93A47 /* FuncUnwinders.cpp */; }; + 268900DA13353E6F00698AC0 /* LineEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1910F1B8EC00F91463 /* LineEntry.cpp */; }; + 268900DB13353E6F00698AC0 /* LineTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1A10F1B8EC00F91463 /* LineTable.cpp */; }; + 268900DC13353E6F00698AC0 /* ObjectFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F4C10F1BC1A00F91463 /* ObjectFile.cpp */; }; + 268900DD13353E6F00698AC0 /* Symbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1B10F1B8EC00F91463 /* Symbol.cpp */; }; + 268900DE13353E6F00698AC0 /* SymbolContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1C10F1B8EC00F91463 /* SymbolContext.cpp */; }; + 268900DF13353E6F00698AC0 /* SymbolFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1D10F1B8EC00F91463 /* SymbolFile.cpp */; }; + 268900E013353E6F00698AC0 /* SymbolVendor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF94005711C03F6500085DB9 /* SymbolVendor.cpp */; }; + 268900E113353E6F00698AC0 /* Symtab.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1F10F1B8EC00F91463 /* Symtab.cpp */; }; + 268900E213353E6F00698AC0 /* Type.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F2010F1B8EC00F91463 /* Type.cpp */; }; + 268900E313353E6F00698AC0 /* TypeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F2110F1B8EC00F91463 /* TypeList.cpp */; }; + 268900E413353E6F00698AC0 /* UnwindPlan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 961FABB91235DE1600F93A47 /* UnwindPlan.cpp */; }; + 268900E513353E6F00698AC0 /* UnwindTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 961FABBA1235DE1600F93A47 /* UnwindTable.cpp */; }; + 268900E613353E6F00698AC0 /* Variable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F2210F1B8EC00F91463 /* Variable.cpp */; }; + 268900E713353E6F00698AC0 /* VariableList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F2310F1B8EC00F91463 /* VariableList.cpp */; }; + 268900E813353E6F00698AC0 /* ABI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 497E7B9D1188F6690065CCA1 /* ABI.cpp */; }; + 268900E913353E6F00698AC0 /* CPPLanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CB443BC1249920C00C13DC2 /* CPPLanguageRuntime.cpp */; }; + 268900EA13353E6F00698AC0 /* DynamicLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7710F1B85900F91463 /* DynamicLoader.cpp */; }; + 268900EB13353E6F00698AC0 /* ExecutionContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3510F1B90C00F91463 /* ExecutionContext.cpp */; }; + 268900EC13353E6F00698AC0 /* LanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CB4430A12491DDA00C13DC2 /* LanguageRuntime.cpp */; }; + 268900ED13353E6F00698AC0 /* ObjCLanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CB443F212499B5000C13DC2 /* ObjCLanguageRuntime.cpp */; }; + 268900EE13353E6F00698AC0 /* PathMappingList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 495BBACB119A0DBE00418BEA /* PathMappingList.cpp */; }; + 268900EF13353E6F00698AC0 /* Platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264A43BD1320BCEB005B4096 /* Platform.cpp */; }; + 268900F013353E6F00698AC0 /* Process.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3610F1B90C00F91463 /* Process.cpp */; }; + 268900F113353E6F00698AC0 /* RegisterContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3710F1B90C00F91463 /* RegisterContext.cpp */; }; + 268900F213353E6F00698AC0 /* SectionLoadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2618D7911240116900F2B8FE /* SectionLoadList.cpp */; }; + 268900F313353E6F00698AC0 /* StackFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3810F1B90C00F91463 /* StackFrame.cpp */; }; + 268900F413353E6F00698AC0 /* StackFrameList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3910F1B90C00F91463 /* StackFrameList.cpp */; }; + 268900F513353E6F00698AC0 /* StackID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3A10F1B90C00F91463 /* StackID.cpp */; }; + 268900F613353E6F00698AC0 /* StopInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2615DB861208A9E40021781D /* StopInfo.cpp */; }; + 268900F713353E6F00698AC0 /* Target.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3B10F1B90C00F91463 /* Target.cpp */; }; + 268900F813353E6F00698AC0 /* TargetList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3C10F1B90C00F91463 /* TargetList.cpp */; }; + 268900F913353E6F00698AC0 /* Thread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3D10F1B90C00F91463 /* Thread.cpp */; }; + 268900FA13353E6F00698AC0 /* ThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3E10F1B90C00F91463 /* ThreadList.cpp */; }; + 268900FB13353E6F00698AC0 /* ThreadPlan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3F10F1B90C00F91463 /* ThreadPlan.cpp */; }; + 268900FC13353E6F00698AC0 /* ThreadPlanBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847110F50EFC00BB2B04 /* ThreadPlanBase.cpp */; }; + 268900FD13353E6F00698AC0 /* ThreadPlanCallFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49EC3E98118F90AC00B1265E /* ThreadPlanCallFunction.cpp */; }; + 268900FE13353E6F00698AC0 /* ThreadPlanCallUserExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7CF7E51295E12B00B4FBB5 /* ThreadPlanCallUserExpression.cpp */; }; + 268900FF13353E6F00698AC0 /* ThreadPlanShouldStopHere.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C43DEFA110641F300E55CBF /* ThreadPlanShouldStopHere.cpp */; }; + 2689010013353E6F00698AC0 /* ThreadPlanStepInstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847210F50EFC00BB2B04 /* ThreadPlanStepInstruction.cpp */; }; + 2689010113353E6F00698AC0 /* ThreadPlanStepOut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847310F50EFC00BB2B04 /* ThreadPlanStepOut.cpp */; }; + 2689010213353E6F00698AC0 /* ThreadPlanStepOverBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847410F50EFC00BB2B04 /* ThreadPlanStepOverBreakpoint.cpp */; }; + 2689010313353E6F00698AC0 /* ThreadPlanStepRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847610F50EFC00BB2B04 /* ThreadPlanStepRange.cpp */; }; + 2689010413353E6F00698AC0 /* ThreadPlanStepInRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C43DF8911069C3200E55CBF /* ThreadPlanStepInRange.cpp */; }; + 2689010513353E6F00698AC0 /* ThreadPlanStepOverRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C43DF8A11069C3200E55CBF /* ThreadPlanStepOverRange.cpp */; }; + 2689010613353E6F00698AC0 /* ThreadPlanRunToAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CAFCE031101218900CA63DB /* ThreadPlanRunToAddress.cpp */; }; + 2689010713353E6F00698AC0 /* ThreadPlanStepThrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847510F50EFC00BB2B04 /* ThreadPlanStepThrough.cpp */; }; + 2689010813353E6F00698AC0 /* ThreadPlanStepUntil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9FE11922A7F00958FBD /* ThreadPlanStepUntil.cpp */; }; + 2689010A13353E6F00698AC0 /* ThreadPlanTracer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CC2A148128C73ED001531C4 /* ThreadPlanTracer.cpp */; }; + 2689010B13353E6F00698AC0 /* ThreadSpec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C08CDE711C81EF8001610A8 /* ThreadSpec.cpp */; }; + 2689010C13353E6F00698AC0 /* UnixSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C00987011500B4300F316B0 /* UnixSignals.cpp */; }; + 2689011013353E8200698AC0 /* SharingPtr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 261B5A5211C3F2AD00AABD0A /* SharingPtr.cpp */; }; + 2689011113353E8200698AC0 /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9F611922A1300958FBD /* StringExtractor.cpp */; }; + 2689011213353E8200698AC0 /* StringExtractorGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2676A093119C93C8008A98EF /* StringExtractorGDBRemote.cpp */; }; + 2689011313353E8200698AC0 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.cpp */; }; + 268901161335BBC300698AC0 /* liblldb-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2689FFCA13353D7A00698AC0 /* liblldb-core.a */; }; + 2689FFDA13353D9D00698AC0 /* lldb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7410F1B85900F91463 /* lldb.cpp */; }; + 2689FFEF13353DB600698AC0 /* Breakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0A10F1B83100F91463 /* Breakpoint.cpp */; }; + 2689FFF113353DB600698AC0 /* BreakpointID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0B10F1B83100F91463 /* BreakpointID.cpp */; }; + 2689FFF313353DB600698AC0 /* BreakpointIDList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0C10F1B83100F91463 /* BreakpointIDList.cpp */; }; + 2689FFF513353DB600698AC0 /* BreakpointList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0D10F1B83100F91463 /* BreakpointList.cpp */; }; + 2689FFF713353DB600698AC0 /* BreakpointLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0E10F1B83100F91463 /* BreakpointLocation.cpp */; }; + 2689FFF913353DB600698AC0 /* BreakpointLocationCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0F10F1B83100F91463 /* BreakpointLocationCollection.cpp */; }; + 2689FFFB13353DB600698AC0 /* BreakpointLocationList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1010F1B83100F91463 /* BreakpointLocationList.cpp */; }; + 2689FFFD13353DB600698AC0 /* BreakpointOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1110F1B83100F91463 /* BreakpointOptions.cpp */; }; + 2689FFFF13353DB600698AC0 /* BreakpointResolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1210F1B83100F91463 /* BreakpointResolver.cpp */; }; + 268F9D53123AA15200B91E9B /* SBSymbolContextList.h in Headers */ = {isa = PBXBuildFile; fileRef = 268F9D52123AA15200B91E9B /* SBSymbolContextList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 268F9D55123AA16600B91E9B /* SBSymbolContextList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 268F9D54123AA16600B91E9B /* SBSymbolContextList.cpp */; }; + 2690B3711381D5C300ECFBAE /* Memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2690B3701381D5C300ECFBAE /* Memory.cpp */; }; + 2692BA15136610C100F9E14D /* UnwindAssemblyInstEmulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2692BA13136610C100F9E14D /* UnwindAssemblyInstEmulation.cpp */; }; + 2694E99D14FC0BB30076DE67 /* PlatformFreeBSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2694E99A14FC0BB30076DE67 /* PlatformFreeBSD.cpp */; }; + 2694E9A414FC0BBD0076DE67 /* PlatformLinux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2694E9A114FC0BBD0076DE67 /* PlatformLinux.cpp */; }; + 26954EBE1401EE8B00294D09 /* DynamicRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26954EBC1401EE8B00294D09 /* DynamicRegisterInfo.cpp */; }; + 26957D9813D381C900670048 /* RegisterContextDarwin_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26957D9213D381C900670048 /* RegisterContextDarwin_arm.cpp */; }; + 26957D9A13D381C900670048 /* RegisterContextDarwin_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26957D9413D381C900670048 /* RegisterContextDarwin_i386.cpp */; }; + 26957D9C13D381C900670048 /* RegisterContextDarwin_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26957D9613D381C900670048 /* RegisterContextDarwin_x86_64.cpp */; }; + 2697A39315E404B1003E682C /* OptionValueArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2697A39215E404B1003E682C /* OptionValueArch.cpp */; }; + 2697A54D133A6305004E4240 /* PlatformDarwin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2697A54B133A6305004E4240 /* PlatformDarwin.cpp */; }; + 2698699B15E6CBD0002415FF /* OperatingSystemPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2698699815E6CBD0002415FF /* OperatingSystemPython.cpp */; }; + 269DDD4A1B8FD1C300D0DBD8 /* DWARFASTParserClang.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 269DDD481B8FD1C300D0DBD8 /* DWARFASTParserClang.cpp */; }; + 26A527C114E24F5F00F3A14A /* ProcessMachCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A527BD14E24F5F00F3A14A /* ProcessMachCore.cpp */; }; + 26A527C314E24F5F00F3A14A /* ThreadMachCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A527BF14E24F5F00F3A14A /* ThreadMachCore.cpp */; }; + 26A69C5F137A17A500262477 /* RegisterValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C6886E137880C400407EDF /* RegisterValue.cpp */; }; + 26A7A035135E6E4200FB369E /* OptionValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A7A034135E6E4200FB369E /* OptionValue.cpp */; }; + 26AB92121819D74600E63F3E /* DWARFDataExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26AB92101819D74600E63F3E /* DWARFDataExtractor.cpp */; }; + 26B1EFAE154638AF00E2DAC7 /* DWARFDeclContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26B1EFAC154638AF00E2DAC7 /* DWARFDeclContext.cpp */; }; + 26B1FCC21338115F002886E2 /* Host.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EE810F1B88F00F91463 /* Host.mm */; }; + 26B42C4D1187ABA50079C8C8 /* LLDB.h in Headers */ = {isa = PBXBuildFile; fileRef = 26B42C4C1187ABA50079C8C8 /* LLDB.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26B7564E14F89356008D9CB3 /* PlatformiOSSimulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26B7564C14F89356008D9CB3 /* PlatformiOSSimulator.cpp */; }; + 26B75B441AD6E29A001F7A57 /* MipsLinuxSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26B75B421AD6E29A001F7A57 /* MipsLinuxSignals.cpp */; }; + 26B8283D142D01E9002DBC64 /* SBSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 26B8283C142D01E9002DBC64 /* SBSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26B82840142D020F002DBC64 /* SBSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26B8283F142D020F002DBC64 /* SBSection.cpp */; }; + 26BC179918C7F2B300D2196D /* JITLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC179718C7F2B300D2196D /* JITLoader.cpp */; }; + 26BC179A18C7F2B300D2196D /* JITLoaderList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC179818C7F2B300D2196D /* JITLoaderList.cpp */; }; + 26BC17AB18C7F4CB00D2196D /* ProcessElfCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC17A218C7F4CB00D2196D /* ProcessElfCore.cpp */; }; + 26BC17AD18C7F4CB00D2196D /* RegisterContextPOSIXCore_mips64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC17A418C7F4CB00D2196D /* RegisterContextPOSIXCore_mips64.cpp */; }; + 26BC17AF18C7F4CB00D2196D /* RegisterContextPOSIXCore_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC17A618C7F4CB00D2196D /* RegisterContextPOSIXCore_x86_64.cpp */; }; + 26BC17B118C7F4CB00D2196D /* ThreadElfCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC17A818C7F4CB00D2196D /* ThreadElfCore.cpp */; }; + 26BCFC521368AE38006DC050 /* OptionGroupFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BCFC511368AE38006DC050 /* OptionGroupFormat.cpp */; }; + 26BD407F135D2AE000237D80 /* FileLineResolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BD407E135D2ADF00237D80 /* FileLineResolver.cpp */; }; + 26BF51F31B3C754400016294 /* ABISysV_hexagon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BF51EA1B3C754400016294 /* ABISysV_hexagon.cpp */; }; + 26BF51F61B3C754400016294 /* ABISysV_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BF51EF1B3C754400016294 /* ABISysV_i386.cpp */; }; + 26C72C94124322890068DC16 /* SBStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 26C72C93124322890068DC16 /* SBStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26C72C961243229A0068DC16 /* SBStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C72C951243229A0068DC16 /* SBStream.cpp */; }; + 26C7C4831BFFEA7E009BD01F /* WindowsMiniDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C7C4811BFFEA7E009BD01F /* WindowsMiniDump.cpp */; }; + 26C7C4841BFFEA7E009BD01F /* WindowsMiniDump.h in Headers */ = {isa = PBXBuildFile; fileRef = 26C7C4821BFFEA7E009BD01F /* WindowsMiniDump.h */; }; + 26CA97A1172B1FD5005DC71B /* RegisterContextThreadMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CA979F172B1FD5005DC71B /* RegisterContextThreadMemory.cpp */; }; + 26CEB5EF18761CB2008F575A /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C32A10F3DFDD009D5894 /* libedit.dylib */; }; + 26CEB5F218762056008F575A /* CommandObjectGUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CEB5F018762056008F575A /* CommandObjectGUI.cpp */; }; + 26CFDCA3186163A4000E63E5 /* Editline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CFDCA2186163A4000E63E5 /* Editline.cpp */; }; + 26CFDCA818616473000E63E5 /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C32A10F3DFDD009D5894 /* libedit.dylib */; }; + 26D1803E16CEBFD300EDFB5B /* KQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D1803C16CEBFD300EDFB5B /* KQueue.cpp */; }; + 26D1804216CEDF0700EDFB5B /* TimeSpecTimeout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D1804016CEDF0700EDFB5B /* TimeSpecTimeout.cpp */; }; + 26D265BC136B4269002EEE45 /* lldb-public.h in Headers */ = {isa = PBXBuildFile; fileRef = 26651A14133BEC76005B64B7 /* lldb-public.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26D52C1F1A980FE300E5D2FB /* MICmdCmdSymbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D52C1D1A980FE300E5D2FB /* MICmdCmdSymbol.cpp */; }; + 26D55235159A7DB100708D8D /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26D55234159A7DB100708D8D /* libxml2.dylib */; }; + 26D5E15F135BAEA2006EA0A7 /* OptionGroupArchitecture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D5E15E135BAEA2006EA0A7 /* OptionGroupArchitecture.cpp */; }; + 26D5E163135BB054006EA0A7 /* OptionGroupPlatform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D5E162135BB054006EA0A7 /* OptionGroupPlatform.cpp */; }; + 26D7E45D13D5E30A007FD12B /* SocketAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D7E45C13D5E30A007FD12B /* SocketAddress.cpp */; }; + 26DAED6315D327C200E15819 /* OptionValuePathMappings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DAED6215D327C200E15819 /* OptionValuePathMappings.cpp */; }; + 26DB3E161379E7AD0080DC73 /* ABIMacOSX_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DB3E071379E7AD0080DC73 /* ABIMacOSX_arm.cpp */; }; + 26DB3E191379E7AD0080DC73 /* ABIMacOSX_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DB3E0B1379E7AD0080DC73 /* ABIMacOSX_arm64.cpp */; }; + 26DB3E1C1379E7AD0080DC73 /* ABIMacOSX_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DB3E0F1379E7AD0080DC73 /* ABIMacOSX_i386.cpp */; }; + 26DB3E1F1379E7AD0080DC73 /* ABISysV_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DB3E131379E7AD0080DC73 /* ABISysV_x86_64.cpp */; }; + 26DC6A1D1337FECA00FF7998 /* lldb-platform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DC6A1C1337FECA00FF7998 /* lldb-platform.cpp */; }; + 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 */; }; + 26E152261419CAD4007967D0 /* ObjectFilePECOFF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E152231419CACA007967D0 /* ObjectFilePECOFF.cpp */; }; + 26ECA04313665FED008D1F18 /* ARM_DWARF_Registers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26ECA04213665FED008D1F18 /* ARM_DWARF_Registers.cpp */; }; + 26ED3D6D13C563810017D45E /* OptionGroupVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26ED3D6C13C563810017D45E /* OptionGroupVariable.cpp */; }; + 26EFB61B1BFE8D3E00544801 /* PlatformNetBSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26EFB6181BFE8D3E00544801 /* PlatformNetBSD.cpp */; }; + 26EFB61C1BFE8D3E00544801 /* PlatformNetBSD.h in Headers */ = {isa = PBXBuildFile; fileRef = 26EFB6191BFE8D3E00544801 /* PlatformNetBSD.h */; }; + 26EFC4CD18CFAF0D00865D87 /* ObjectFileJIT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26EFC4CA18CFAF0D00865D87 /* ObjectFileJIT.cpp */; }; + 26F006561B4DD86700B872E5 /* DynamicLoaderWindowsDYLD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F006541B4DD86700B872E5 /* DynamicLoaderWindowsDYLD.cpp */; }; + 26F4A21C13FBA31A0064B613 /* ThreadMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F4A21A13FBA31A0064B613 /* ThreadMemory.cpp */; }; + 26F5C27710F3D9E4009D5894 /* Driver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F5C27310F3D9E4009D5894 /* Driver.cpp */; }; + 26F5C32D10F3DFDD009D5894 /* libtermcap.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C32B10F3DFDD009D5894 /* libtermcap.dylib */; }; + 26F73062139D8FDB00FD51C7 /* History.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F73061139D8FDB00FD51C7 /* History.cpp */; }; + 26FFC19914FC072100087D58 /* AuxVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26FFC19314FC072100087D58 /* AuxVector.cpp */; }; + 26FFC19B14FC072100087D58 /* DYLDRendezvous.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26FFC19514FC072100087D58 /* DYLDRendezvous.cpp */; }; + 26FFC19D14FC072100087D58 /* DynamicLoaderPOSIXDYLD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26FFC19714FC072100087D58 /* DynamicLoaderPOSIXDYLD.cpp */; }; + 30DED5DE1B4ECB49004CC508 /* MainLoopPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 30DED5DC1B4ECB17004CC508 /* MainLoopPosix.cpp */; }; + 332CCB181AFF41620034D4C4 /* SBLanguageRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = 3392EBB71AFF402200858B9F /* SBLanguageRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 33E5E8421A672A240024ED68 /* StringConvert.cpp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 33E5E8411A672A240024ED68 /* StringConvert.cpp */; }; + 33E5E8461A6736D30024ED68 /* StringConvert.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 33E5E8451A6736D30024ED68 /* StringConvert.h */; }; + 33E5E8471A674FB60024ED68 /* StringConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33E5E8411A672A240024ED68 /* StringConvert.cpp */; }; + 3F8160A61AB9F7DD001DA9DF /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F8160A51AB9F7DD001DA9DF /* Logging.cpp */; }; + 3F8169191ABA2419001DA9DF /* ConvertEnum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F8169171ABA2419001DA9DF /* ConvertEnum.cpp */; }; + 3F81691A1ABA2419001DA9DF /* NameMatches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F8169181ABA2419001DA9DF /* NameMatches.cpp */; }; + 3F81692C1ABB7A1E001DA9DF /* SystemInitializerFull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F81692A1ABB7A16001DA9DF /* SystemInitializerFull.cpp */; }; + 3F8169311ABB7A6D001DA9DF /* SystemInitializer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F81692E1ABB7A6D001DA9DF /* SystemInitializer.cpp */; }; + 3F8169321ABB7A6D001DA9DF /* SystemInitializerCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F81692F1ABB7A6D001DA9DF /* SystemInitializerCommon.cpp */; }; + 3F8169331ABB7A6D001DA9DF /* SystemLifetimeManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F8169301ABB7A6D001DA9DF /* SystemLifetimeManager.cpp */; }; + 3FA093151BF65D3A0037DD08 /* PythonExceptionStateTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FA093141BF65D3A0037DD08 /* PythonExceptionStateTests.cpp */; }; + 3FBA69DF1B6067020008F44A /* ScriptInterpreterNone.cpp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3FBA69DD1B6067020008F44A /* ScriptInterpreterNone.cpp */; }; + 3FBA69E01B6067020008F44A /* ScriptInterpreterNone.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3FBA69DE1B6067020008F44A /* ScriptInterpreterNone.h */; }; + 3FBA69E11B6067120008F44A /* ScriptInterpreterNone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FBA69DD1B6067020008F44A /* ScriptInterpreterNone.cpp */; }; + 3FBA69E71B60672A0008F44A /* lldb-python.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3FBA69E21B60672A0008F44A /* lldb-python.h */; }; + 3FBA69E91B60672A0008F44A /* PythonDataObjects.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3FBA69E41B60672A0008F44A /* PythonDataObjects.h */; }; + 3FBA69EB1B60672A0008F44A /* ScriptInterpreterPython.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3FBA69E61B60672A0008F44A /* ScriptInterpreterPython.h */; }; + 3FBA69EC1B6067430008F44A /* PythonDataObjects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FBA69E31B60672A0008F44A /* PythonDataObjects.cpp */; }; + 3FBA69ED1B60674B0008F44A /* ScriptInterpreterPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FBA69E51B60672A0008F44A /* ScriptInterpreterPython.cpp */; }; + 3FDFDDBD199C3A06009756A7 /* FileAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFDDBC199C3A06009756A7 /* FileAction.cpp */; }; + 3FDFDDBF199D345E009756A7 /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFDDBE199D345E009756A7 /* FileCache.cpp */; }; + 3FDFDDC6199D37ED009756A7 /* FileSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFDDC5199D37ED009756A7 /* FileSystem.cpp */; }; + 3FDFE52C19A2917A009756A7 /* HostInfoMacOSX.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFE52B19A2917A009756A7 /* HostInfoMacOSX.mm */; }; + 3FDFE53119A292F0009756A7 /* HostInfoPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFE53019A292F0009756A7 /* HostInfoPosix.cpp */; }; + 3FDFE53519A29327009756A7 /* HostInfoBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFE53419A29327009756A7 /* HostInfoBase.cpp */; }; + 3FDFE56C19AF9C44009756A7 /* HostProcessPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFE56A19AF9C44009756A7 /* HostProcessPosix.cpp */; }; + 3FDFE56D19AF9C44009756A7 /* HostThreadPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFE56B19AF9C44009756A7 /* HostThreadPosix.cpp */; }; + 3FDFED0B19B7C8DE009756A7 /* HostThreadMacOSX.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFED0519B7C898009756A7 /* HostThreadMacOSX.mm */; }; + 3FDFED0C19B7C8E7009756A7 /* ThisThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFED0619B7C898009756A7 /* ThisThread.cpp */; }; + 3FDFED0F19B7D269009756A7 /* ThisThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFED0D19B7D269009756A7 /* ThisThread.cpp */; }; + 3FDFED2719BA6D96009756A7 /* HostNativeThreadBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFED2419BA6D96009756A7 /* HostNativeThreadBase.cpp */; }; + 3FDFED2819BA6D96009756A7 /* HostThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFED2519BA6D96009756A7 /* HostThread.cpp */; }; + 3FDFED2919BA6D96009756A7 /* ThreadLauncher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFED2619BA6D96009756A7 /* ThreadLauncher.cpp */; }; + 3FDFED2D19C257A0009756A7 /* HostProcess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFED2C19C257A0009756A7 /* HostProcess.cpp */; }; + 449ACC98197DEA0B008D175E /* FastDemangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 449ACC96197DE9EC008D175E /* FastDemangle.cpp */; }; + 490A36C0180F0E6F00BA31F8 /* PlatformWindows.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 490A36BD180F0E6F00BA31F8 /* PlatformWindows.cpp */; }; + 490A966B1628C3BF00F0002E /* SBDeclaration.h in Headers */ = {isa = PBXBuildFile; fileRef = 9452573816262CEF00325455 /* SBDeclaration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4939EA8D1BD56B6D00084382 /* REPL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4939EA8C1BD56B6D00084382 /* REPL.cpp */; }; + 494260DA14579144003C1C78 /* VerifyDecl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 494260D914579144003C1C78 /* VerifyDecl.cpp */; }; + 4959511F1A1BC4BC00F6F8FC /* ClangModulesDeclVendor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4959511E1A1BC4BC00F6F8FC /* ClangModulesDeclVendor.cpp */; }; + 4966DCC4148978A10028481B /* ClangExternalASTSourceCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4966DCC3148978A10028481B /* ClangExternalASTSourceCommon.cpp */; }; + 49684D7B1BAB37E400E6D5D5 /* MIUtilParse.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 49684D791BAB37E400E6D5D5 /* MIUtilParse.h */; }; + 49684D7C1BAB37F200E6D5D5 /* MIUtilParse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49684D781BAB37E400E6D5D5 /* MIUtilParse.cpp */; }; + 49724D991AD6ED390033C538 /* RenderScriptRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49724D971AD6ED390033C538 /* RenderScriptRuntime.cpp */; }; + 4984BA131B978C55008658D4 /* ClangExpressionVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4984BA0E1B978C3E008658D4 /* ClangExpressionVariable.cpp */; }; + 4984BA161B979973008658D4 /* ExpressionVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4984BA151B979973008658D4 /* ExpressionVariable.cpp */; }; + 4984BA181B979C08008658D4 /* ExpressionVariable.h in Headers */ = {isa = PBXBuildFile; fileRef = 4984BA171B979C08008658D4 /* ExpressionVariable.h */; }; + 49A1CAC51430E8DE00306AC9 /* ExpressionSourceCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A1CAC31430E8BD00306AC9 /* ExpressionSourceCode.cpp */; }; + 49A71FE7141FFA5C00D59478 /* IRInterpreter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 496B01581406DE8900F830D5 /* IRInterpreter.cpp */; }; + 49A71FE8141FFACF00D59478 /* DataEncoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 268ED0A4140FF54200DE830F /* DataEncoder.cpp */; }; + 49D8FB3913B5598F00411094 /* ClangASTImporter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49D8FB3513B558DE00411094 /* ClangASTImporter.cpp */; }; + 49DA65031485C92A005FF180 /* AppleObjCDeclVendor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49DA65021485C92A005FF180 /* AppleObjCDeclVendor.cpp */; }; + 49DCF6FE170E6B4A0092F75E /* IRMemoryMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49DCF6FD170E6B4A0092F75E /* IRMemoryMap.cpp */; }; + 49DCF702170E70120092F75E /* Materializer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49DCF700170E70120092F75E /* Materializer.cpp */; }; + 4C0083401B9F9BA900D5CF24 /* UtilityFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C00833F1B9F9BA900D5CF24 /* UtilityFunction.cpp */; }; + 4C2479BD1BA39295009C9A7B /* FunctionCaller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C0083321B9A5DE200D5CF24 /* FunctionCaller.cpp */; }; + 4C3ADCD61810D88B00357218 /* BreakpointResolverFileRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CAA56141422D986001FFA01 /* BreakpointResolverFileRegex.cpp */; }; + 4C56543119D1EFAA002E9C44 /* ThreadPlanPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C56543019D1EFAA002E9C44 /* ThreadPlanPython.cpp */; }; + 4C56543519D2297A002E9C44 /* SBThreadPlan.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C56543419D2297A002E9C44 /* SBThreadPlan.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4C56543719D22B32002E9C44 /* SBThreadPlan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C56543619D22B32002E9C44 /* SBThreadPlan.cpp */; }; + 4C6649A314EEE81000B0316F /* StreamCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C6649A214EEE81000B0316F /* StreamCallback.cpp */; }; + 4C88BC2A1BA3722B00AA0964 /* Expression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C88BC291BA3722B00AA0964 /* Expression.cpp */; }; + 4C88BC2D1BA391B000AA0964 /* UserExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C0083331B9A5DE200D5CF24 /* UserExpression.cpp */; }; + 4CABA9E0134A8BCD00539BDD /* ValueObjectMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CABA9DF134A8BCD00539BDD /* ValueObjectMemory.cpp */; }; + 4CCA644D13B40B82003BDF98 /* ItaniumABILanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CCA643D13B40B82003BDF98 /* ItaniumABILanguageRuntime.cpp */; }; + 4CCA645013B40B82003BDF98 /* AppleObjCRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CCA644213B40B82003BDF98 /* AppleObjCRuntime.cpp */; }; + 4CCA645213B40B82003BDF98 /* AppleObjCRuntimeV1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CCA644413B40B82003BDF98 /* AppleObjCRuntimeV1.cpp */; }; + 4CCA645413B40B82003BDF98 /* AppleObjCRuntimeV2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CCA644613B40B82003BDF98 /* AppleObjCRuntimeV2.cpp */; }; + 4CCA645613B40B82003BDF98 /* AppleObjCTrampolineHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CCA644813B40B82003BDF98 /* AppleObjCTrampolineHandler.cpp */; }; + 4CCA645813B40B82003BDF98 /* AppleThreadPlanStepThroughObjCTrampoline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CCA644A13B40B82003BDF98 /* AppleThreadPlanStepThroughObjCTrampoline.cpp */; }; + 4CD0BD0F134BFADF00CB44D4 /* ValueObjectDynamicValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD0BD0E134BFADF00CB44D4 /* ValueObjectDynamicValue.cpp */; }; + 4CE4F673162C971A00F75CB3 /* SBExpressionOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CE4F672162C971A00F75CB3 /* SBExpressionOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4CE4F675162C973F00F75CB3 /* SBExpressionOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F674162C973F00F75CB3 /* SBExpressionOptions.cpp */; }; + 4CF3D80C15AF4DC800845BF3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDB919B414F6F10D008FF64B /* Security.framework */; }; + 4CF52AF51428291E0051E832 /* SBFileSpecList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CF52AF41428291E0051E832 /* SBFileSpecList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4CF52AF8142829390051E832 /* SBFileSpecList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CF52AF7142829390051E832 /* SBFileSpecList.cpp */; }; + 6D55B2901A8A806200A70529 /* GDBRemoteCommunicationServerCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D55B28D1A8A806200A70529 /* GDBRemoteCommunicationServerCommon.cpp */; }; + 6D55B2911A8A806200A70529 /* GDBRemoteCommunicationServerLLGS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D55B28E1A8A806200A70529 /* GDBRemoteCommunicationServerLLGS.cpp */; }; + 6D55B2921A8A806200A70529 /* GDBRemoteCommunicationServerPlatform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D55B28F1A8A806200A70529 /* GDBRemoteCommunicationServerPlatform.cpp */; }; + 6D55BAED1A8CD0A800A70529 /* PlatformAndroid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D55BAE91A8CD08C00A70529 /* PlatformAndroid.cpp */; }; + 6D55BAEE1A8CD0B200A70529 /* PlatformAndroidRemoteGDBServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D55BAEB1A8CD08C00A70529 /* PlatformAndroidRemoteGDBServer.cpp */; }; + 6D762BEE1B1605D2006C929D /* LLDBServerUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D762BEC1B1605CD006C929D /* LLDBServerUtilities.cpp */; }; + 6D86CEA01B440F8500A7FBFA /* CommandObjectBugreport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D86CE9E1B440F6B00A7FBFA /* CommandObjectBugreport.cpp */; }; + 6D95DC001B9DC057000E318A /* DIERef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D95DBFD1B9DC057000E318A /* DIERef.cpp */; }; + 6D95DC011B9DC057000E318A /* HashedNameToDIE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D95DBFE1B9DC057000E318A /* HashedNameToDIE.cpp */; }; + 6D95DC021B9DC057000E318A /* SymbolFileDWARFDwo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D95DBFF1B9DC057000E318A /* SymbolFileDWARFDwo.cpp */; }; + 6D99A3631BBC2F3200979793 /* ArmUnwindInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D99A3621BBC2F3200979793 /* ArmUnwindInfo.cpp */; }; + 6D9AB3DD1BB2B74E003F2289 /* TypeMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6D9AB3DC1BB2B74E003F2289 /* TypeMap.cpp */; }; + 6DEC6F391BD66D750091ABA6 /* TaskPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6DEC6F381BD66D750091ABA6 /* TaskPool.cpp */; }; + 8C2D6A53197A1EAF006989C9 /* MemoryHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */; }; + 8C2D6A5E197A250F006989C9 /* MemoryHistoryASan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */; }; + 8CCB017E19BA28A80009FD44 /* ThreadCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CCB017A19BA283D0009FD44 /* ThreadCollection.cpp */; }; + 8CCB018219BA4E270009FD44 /* SBThreadCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCB018119BA4E210009FD44 /* SBThreadCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8CCB018319BA51BF0009FD44 /* SBThreadCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CCB017F19BA4DD00009FD44 /* SBThreadCollection.cpp */; }; + 8CF02AE919DCC01900B14BE0 /* InstrumentationRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */; }; + 8CF02AEA19DCC02100B14BE0 /* AddressSanitizerRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CF02AE519DCBF8400B14BE0 /* AddressSanitizerRuntime.cpp */; }; + 8CF02AEF19DD16B100B14BE0 /* InstrumentationRuntimeStopInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CF02AED19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.cpp */; }; + 9404957A1BEC497E00926025 /* NSError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940495781BEC497E00926025 /* NSError.cpp */; }; + 9404957B1BEC497E00926025 /* NSException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940495791BEC497E00926025 /* NSException.cpp */; }; + 94094C6B163B6F840083A547 /* ValueObjectCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94094C69163B6CD90083A547 /* ValueObjectCast.cpp */; }; + 940B02F619DC96E700AD0F52 /* SBExecutionContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940B02F519DC96E700AD0F52 /* SBExecutionContext.cpp */; }; + 940B04D91A8984FF0045D5F7 /* argdumper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940B04D81A8984FF0045D5F7 /* argdumper.cpp */; }; + 940B04E41A8987680045D5F7 /* lldb-argdumper in CopyFiles */ = {isa = PBXBuildFile; fileRef = 942829C01A89835300521B30 /* lldb-argdumper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 94145431175E63B500284436 /* lldb-versioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 94145430175D7FDE00284436 /* lldb-versioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9418EBCD1AA910910058B02E /* VectorType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9418EBCC1AA910910058B02E /* VectorType.cpp */; }; + 941BCC7F14E48C4000BB969C /* SBTypeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568614E355F2003A195C /* SBTypeFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 941BCC8014E48C4000BB969C /* SBTypeFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568714E355F2003A195C /* SBTypeFormat.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 941BCC8114E48C4000BB969C /* SBTypeSummary.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568814E355F2003A195C /* SBTypeSummary.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 941BCC8214E48C4000BB969C /* SBTypeSynthetic.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568914E355F2003A195C /* SBTypeSynthetic.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 94235B9E1A8D667400EB2EED /* SBVariablesOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94235B9B1A8D5FF300EB2EED /* SBVariablesOptions.cpp */; }; + 94235B9F1A8D66D600EB2EED /* SBVariablesOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 94235B9A1A8D5FD800EB2EED /* SBVariablesOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 942612F71B95000000EF842E /* LanguageCategory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 942612F61B95000000EF842E /* LanguageCategory.cpp */; }; + 942612F81B952C9B00EF842E /* ObjCLanguage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94B6385E1B8FB7A2004FE1E4 /* ObjCLanguage.cpp */; }; + 942829561A89614C00521B30 /* JSON.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 942829551A89614C00521B30 /* JSON.cpp */; }; + 942829CC1A89839300521B30 /* liblldb-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2689FFCA13353D7A00698AC0 /* liblldb-core.a */; }; + 94380B8219940B0A00BFE4A8 /* StringLexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94380B8119940B0A00BFE4A8 /* StringLexer.cpp */; }; + 943BDEFE1AA7B2F800789CE8 /* LLDBAssert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 943BDEFD1AA7B2F800789CE8 /* LLDBAssert.cpp */; }; + 944372DC171F6B4300E57C32 /* RegisterContextDummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 944372DA171F6B4300E57C32 /* RegisterContextDummy.cpp */; }; + 9443B122140C18C40013457C /* SBData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9443B121140C18C10013457C /* SBData.cpp */; }; + 9443B123140C26AB0013457C /* SBData.h in Headers */ = {isa = PBXBuildFile; fileRef = 9443B120140C18A90013457C /* SBData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9447DE431BD5963300E67212 /* DumpValueObjectOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9447DE421BD5963300E67212 /* DumpValueObjectOptions.cpp */; }; + 945215DF17F639EE00521C0B /* ValueObjectPrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945215DE17F639EE00521C0B /* ValueObjectPrinter.cpp */; }; + 9452573A16262D0200325455 /* SBDeclaration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9452573916262D0200325455 /* SBDeclaration.cpp */; }; + 945261BF1B9A11FC00BF138D /* CxxStringTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261B31B9A11E800BF138D /* CxxStringTypes.cpp */; }; + 945261C01B9A11FC00BF138D /* LibCxx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261B51B9A11E800BF138D /* LibCxx.cpp */; }; + 945261C11B9A11FC00BF138D /* LibCxxInitializerList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261B71B9A11E800BF138D /* LibCxxInitializerList.cpp */; }; + 945261C21B9A11FC00BF138D /* LibCxxList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261B81B9A11E800BF138D /* LibCxxList.cpp */; }; + 945261C31B9A11FC00BF138D /* LibCxxMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261B91B9A11E800BF138D /* LibCxxMap.cpp */; }; + 945261C41B9A11FC00BF138D /* LibCxxUnorderedMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261BA1B9A11E800BF138D /* LibCxxUnorderedMap.cpp */; }; + 945261C51B9A11FC00BF138D /* LibCxxVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261BB1B9A11E800BF138D /* LibCxxVector.cpp */; }; + 945261C61B9A11FC00BF138D /* LibStdcpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261BC1B9A11E800BF138D /* LibStdcpp.cpp */; }; + 945261C81B9A14D300BF138D /* CXXFunctionPointer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261C71B9A14D300BF138D /* CXXFunctionPointer.cpp */; }; + 9455630F1BEAD0600073F75F /* PlatformAppleSimulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9455630A1BEAD0570073F75F /* PlatformAppleSimulator.cpp */; }; + 945563101BEAD0650073F75F /* PlatformiOSSimulatorCoreSimulatorSupport.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9455630D1BEAD0570073F75F /* PlatformiOSSimulatorCoreSimulatorSupport.mm */; }; + 945759671534941F005A9070 /* PlatformPOSIX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945759651534941F005A9070 /* PlatformPOSIX.cpp */; }; + 945E8D80152F6AB40019BCCD /* StreamGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945E8D7F152F6AB40019BCCD /* StreamGDBRemote.cpp */; }; + 9461569A14E358A6003A195C /* SBTypeFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9461568A14E35621003A195C /* SBTypeFilter.cpp */; }; + 9461569B14E358A6003A195C /* SBTypeFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9461568B14E35621003A195C /* SBTypeFormat.cpp */; }; + 9461569C14E358A6003A195C /* SBTypeSummary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9461568C14E35621003A195C /* SBTypeSummary.cpp */; }; + 9461569D14E358A6003A195C /* SBTypeSynthetic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9461568D14E35621003A195C /* SBTypeSynthetic.cpp */; }; + 946216C21A97C080006E19CC /* OptionValueLanguage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946216C11A97C080006E19CC /* OptionValueLanguage.cpp */; }; + 9463D4CD13B1798800C230D4 /* CommandObjectType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9463D4CC13B1798800C230D4 /* CommandObjectType.cpp */; }; + 9475C18814E5E9FA001BFC6D /* SBTypeCategory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9475C18714E5E9FA001BFC6D /* SBTypeCategory.cpp */; }; + 9475C18914E5EA08001BFC6D /* SBTypeCategory.h in Headers */ = {isa = PBXBuildFile; fileRef = 9475C18514E5E9C5001BFC6D /* SBTypeCategory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9475C18E14E5F834001BFC6D /* SBTypeNameSpecifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9475C18D14E5F834001BFC6D /* SBTypeNameSpecifier.cpp */; }; + 9475C18F14E5F858001BFC6D /* SBTypeNameSpecifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 9475C18C14E5F826001BFC6D /* SBTypeNameSpecifier.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 947A1D641616476B0017C8D1 /* CommandObjectPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 947A1D621616476A0017C8D1 /* CommandObjectPlugin.cpp */; }; + 949ADF031406F648004833E1 /* ValueObjectConstResultImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949ADF021406F648004833E1 /* ValueObjectConstResultImpl.cpp */; }; + 949EEDA01BA74B6D008C63CF /* CoreMedia.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949EED9E1BA74B64008C63CF /* CoreMedia.cpp */; }; + 949EEDA31BA76577008C63CF /* Cocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949EEDA11BA76571008C63CF /* Cocoa.cpp */; }; + 949EEDAE1BA7671C008C63CF /* CF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949EEDAC1BA76719008C63CF /* CF.cpp */; }; + 949EEDAF1BA76729008C63CF /* NSArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949EEDA41BA765B5008C63CF /* NSArray.cpp */; }; + 949EEDB11BA7672D008C63CF /* NSDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949EEDA51BA765B5008C63CF /* NSDictionary.cpp */; }; + 949EEDB21BA76731008C63CF /* NSIndexPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949EEDA61BA765B5008C63CF /* NSIndexPath.cpp */; }; + 949EEDB31BA76736008C63CF /* NSSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949EEDA71BA765B5008C63CF /* NSSet.cpp */; }; + 94A5B3971AB9FE8D00A5EE7F /* EmulateInstructionMIPS64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94A5B3951AB9FE8300A5EE7F /* EmulateInstructionMIPS64.cpp */; }; + 94B638531B8F8E6C004FE1E4 /* Language.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94B638521B8F8E6C004FE1E4 /* Language.cpp */; }; + 94B6385D1B8FB178004FE1E4 /* CPlusPlusLanguage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94B6385B1B8FB174004FE1E4 /* CPlusPlusLanguage.cpp */; }; + 94B638631B8FB7F1004FE1E4 /* ObjCPlusPlusLanguage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94B638621B8FB7F1004FE1E4 /* ObjCPlusPlusLanguage.cpp */; }; + 94B6E76213D88365005F417F /* ValueObjectSyntheticFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */; }; + 94B9E5121BBF20F4000A48DC /* NSString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94B9E5111BBF20F4000A48DC /* NSString.cpp */; }; + 94BA8B6D176F8C9B005A91B5 /* Range.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94BA8B6C176F8C9B005A91B5 /* Range.cpp */; }; + 94BA8B70176F97CE005A91B5 /* CommandHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94BA8B6F176F97CE005A91B5 /* CommandHistory.cpp */; }; + 94CB255C16B069770059775D /* DataVisualization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB255816B069770059775D /* DataVisualization.cpp */; }; + 94CB255D16B069770059775D /* FormatClasses.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB255916B069770059775D /* FormatClasses.cpp */; }; + 94CB255E16B069770059775D /* FormatManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB255A16B069770059775D /* FormatManager.cpp */; }; + 94CB256616B096F10059775D /* TypeCategory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB256416B096F10059775D /* TypeCategory.cpp */; }; + 94CB256716B096F10059775D /* TypeCategoryMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB256516B096F10059775D /* TypeCategoryMap.cpp */; }; + 94CB257016B0A4270059775D /* TypeFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB256D16B0A4260059775D /* TypeFormat.cpp */; }; + 94CB257116B0A4270059775D /* TypeSummary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB256E16B0A4260059775D /* TypeSummary.cpp */; }; + 94CB257216B0A4270059775D /* TypeSynthetic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB256F16B0A4270059775D /* TypeSynthetic.cpp */; }; + 94CB257416B1D3880059775D /* FormatCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB257316B1D3870059775D /* FormatCache.cpp */; }; + 94CD131A19BA33B400DB7BED /* TypeValidator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CD131919BA33B400DB7BED /* TypeValidator.cpp */; }; + 94CD7D0919A3FBA300908B7C /* AppleObjCClassDescriptorV2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CD7D0819A3FBA300908B7C /* AppleObjCClassDescriptorV2.cpp */; }; + 94CD7D0C19A3FBCE00908B7C /* AppleObjCTypeEncodingParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CD7D0B19A3FBCE00908B7C /* AppleObjCTypeEncodingParser.cpp */; }; + 94D0858C1B9675B8000D24BD /* FormattersHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94D0858B1B9675B8000D24BD /* FormattersHelpers.cpp */; }; + 94E829CA152D33C1006F96A3 /* lldb-server in Resources */ = {isa = PBXBuildFile; fileRef = 26DC6A101337FE6900FF7998 /* lldb-server */; }; + 94F48F251A01C687005C0EC6 /* StringPrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94F48F241A01C687005C0EC6 /* StringPrinter.cpp */; }; + 94FA3DE01405D50400833217 /* ValueObjectConstResultChild.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94FA3DDF1405D50300833217 /* ValueObjectConstResultChild.cpp */; }; + 964463EC1A330C0500154ED8 /* CompactUnwindInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 964463EB1A330C0500154ED8 /* CompactUnwindInfo.cpp */; }; + 966C6B7918E6A56A0093F5EC /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 966C6B7818E6A56A0093F5EC /* libz.dylib */; }; + 966C6B7A18E6A56A0093F5EC /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 966C6B7818E6A56A0093F5EC /* libz.dylib */; }; + 966C6B7C18E6A56A0093F5EC /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 966C6B7818E6A56A0093F5EC /* libz.dylib */; }; + 9694FA711B32AA64005EBB16 /* ABISysV_mips.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9694FA6F1B32AA64005EBB16 /* ABISysV_mips.cpp */; }; + 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 */; }; + 9A22A161135E30370024DDC3 /* EmulateInstructionARM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A15D135E30370024DDC3 /* EmulateInstructionARM.cpp */; }; + 9A22A163135E30370024DDC3 /* EmulationStateARM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A15F135E30370024DDC3 /* EmulationStateARM.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 */; }; + 9A4F35101368A51A00823F52 /* StreamAsynchronousIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A4F350F1368A51A00823F52 /* StreamAsynchronousIO.cpp */; }; + 9AA69DA61188F52100D753A0 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.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 */; }; + A36FF33C17D8E94600244D40 /* OptionParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A36FF33B17D8E94600244D40 /* OptionParser.cpp */; }; + AE44FB301BB07EB20033EB62 /* GoUserExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB2C1BB07DD80033EB62 /* GoUserExpression.cpp */; }; + AE44FB311BB07EB80033EB62 /* GoLexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB2A1BB07DD80033EB62 /* GoLexer.cpp */; }; + AE44FB321BB07EBC0033EB62 /* GoParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB2B1BB07DD80033EB62 /* GoParser.cpp */; }; + AE44FB3E1BB485960033EB62 /* GoLanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB3D1BB485960033EB62 /* GoLanguageRuntime.cpp */; }; + AE44FB471BB4BB090033EB62 /* GoLanguage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB451BB4BB090033EB62 /* GoLanguage.cpp */; }; + AE44FB4C1BB4BB540033EB62 /* GoFormatterFunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB4A1BB4BB540033EB62 /* GoFormatterFunctions.cpp */; }; + AE6897281B94F6DE0018845D /* DWARFASTParserGo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE6897261B94F6DE0018845D /* DWARFASTParserGo.cpp */; }; + AE7F56291B8FE418001377A8 /* GoASTContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEFFBA7C1AC4835D0087B932 /* GoASTContext.cpp */; }; + AE8F624919EF3E1E00326B21 /* OperatingSystemGo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE8F624719EF3E1E00326B21 /* OperatingSystemGo.cpp */; }; + AEB0E4591BD6E9F800B24093 /* LLVMUserExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEB0E4581BD6E9F800B24093 /* LLVMUserExpression.cpp */; }; + AEC6FFA01BE970A2007882C1 /* GoParserTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEC6FF9F1BE970A2007882C1 /* GoParserTest.cpp */; }; + AEEA34051AC88A7400AB639D /* TypeSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEEA34041AC88A7400AB639D /* TypeSystem.cpp */; }; + AF061F87182C97ED00B6A19C /* RegisterContextHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF061F85182C97ED00B6A19C /* RegisterContextHistory.cpp */; }; + AF0C112818580CD800C4C45B /* QueueItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF0C112718580CD800C4C45B /* QueueItem.cpp */; }; + AF0E22F018A09FB20009B7D1 /* AppleGetItemInfoHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF0E22EE18A09FB20009B7D1 /* AppleGetItemInfoHandler.cpp */; }; + AF0EBBE8185940FB0059E52F /* SBQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF0EBBE6185940FB0059E52F /* SBQueue.cpp */; }; + AF0EBBE9185940FB0059E52F /* SBQueueItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF0EBBE7185940FB0059E52F /* SBQueueItem.cpp */; }; + AF0EBBEC185941360059E52F /* SBQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = AF0EBBEA185941360059E52F /* SBQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF0EBBED185941360059E52F /* SBQueueItem.h in Headers */ = {isa = PBXBuildFile; fileRef = AF0EBBEB185941360059E52F /* SBQueueItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF0F6E501739A76D009180FE /* RegisterContextKDP_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF0F6E4E1739A76D009180FE /* RegisterContextKDP_arm64.cpp */; }; + AF1729D6182C907200E0AB97 /* HistoryThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF1729D4182C907200E0AB97 /* HistoryThread.cpp */; }; + AF1729D7182C907200E0AB97 /* HistoryUnwind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF1729D5182C907200E0AB97 /* HistoryUnwind.cpp */; }; + AF1D88691B575E8D003CB899 /* ValueObjectConstResultCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF94726E1B575E430063D65C /* ValueObjectConstResultCast.cpp */; }; + AF1F7B07189C904B0087DB9C /* AppleGetPendingItemsHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF1F7B05189C904B0087DB9C /* AppleGetPendingItemsHandler.cpp */; }; + AF1FA88A1A60A69500272AFC /* RegisterNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF1FA8891A60A69500272AFC /* RegisterNumber.cpp */; }; + AF20F7661AF18F8500751A6E /* ABISysV_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F7641AF18F8500751A6E /* ABISysV_arm.cpp */; }; + AF20F76A1AF18F9000751A6E /* ABISysV_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F7681AF18F9000751A6E /* ABISysV_arm64.cpp */; }; + AF20F76D1AF18FC700751A6E /* SBLanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F76C1AF18FC700751A6E /* SBLanguageRuntime.cpp */; }; + AF20F7701AF1902900751A6E /* RegisterContextLinux_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF20F76E1AF1902900751A6E /* RegisterContextLinux_arm64.cpp */; }; + AF23B4DB19009C66003E2A58 /* FreeBSDSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */; }; + AF254E31170CCC33007AE5C9 /* PlatformDarwinKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */; }; + AF25AB26188F685C0030DEC3 /* AppleGetQueuesHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */; }; + AF26703A1852D01E00B6CC36 /* Queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF2670381852D01E00B6CC36 /* Queue.cpp */; }; + AF26703B1852D01E00B6CC36 /* QueueList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF2670391852D01E00B6CC36 /* QueueList.cpp */; }; + AF2BA6EC1A707E3400C5248A /* UriParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33064C991A5C7A330033D415 /* UriParser.cpp */; }; + AF2BCA6C18C7EFDE005B4526 /* JITLoaderGDB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF2BCA6918C7EFDE005B4526 /* JITLoaderGDB.cpp */; }; + AF33B4BE1C1FA441001B28D9 /* NetBSDSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF33B4BC1C1FA441001B28D9 /* NetBSDSignals.cpp */; }; + AF33B4BF1C1FA441001B28D9 /* NetBSDSignals.h in Headers */ = {isa = PBXBuildFile; fileRef = AF33B4BD1C1FA441001B28D9 /* NetBSDSignals.h */; }; + AF37E10A17C861F20061E18E /* ProcessRunLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF37E10917C861F20061E18E /* ProcessRunLock.cpp */; }; + AF45E1FE1BF57C8D000563EB /* PythonTestSuite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF45E1FC1BF57C8D000563EB /* PythonTestSuite.cpp */; }; + AF45FDE518A1F3AC0007051C /* AppleGetThreadItemInfoHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF45FDE318A1F3AC0007051C /* AppleGetThreadItemInfoHandler.cpp */; }; + AF77E08F1A033C700096C0EA /* ABISysV_ppc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF77E08D1A033C700096C0EA /* ABISysV_ppc.cpp */; }; + AF77E0931A033C7F0096C0EA /* ABISysV_ppc64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF77E0911A033C7F0096C0EA /* ABISysV_ppc64.cpp */; }; + AF77E0A11A033D360096C0EA /* RegisterContextFreeBSD_powerpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF77E09A1A033D360096C0EA /* RegisterContextFreeBSD_powerpc.cpp */; }; + AF77E0A41A033D360096C0EA /* RegisterContextPOSIX_powerpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF77E09D1A033D360096C0EA /* RegisterContextPOSIX_powerpc.cpp */; }; + AF77E0A91A033D740096C0EA /* RegisterContextPOSIXCore_powerpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF77E0A71A033D740096C0EA /* RegisterContextPOSIXCore_powerpc.cpp */; }; + AF81DEFA1828A23F0042CF19 /* SystemRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF81DEF91828A23F0042CF19 /* SystemRuntime.cpp */; }; + AF8AD62E1BEC28A400150209 /* PlatformAppleTVSimulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF8AD62A1BEC28A400150209 /* PlatformAppleTVSimulator.cpp */; }; + AF8AD62F1BEC28A400150209 /* PlatformAppleTVSimulator.h in Headers */ = {isa = PBXBuildFile; fileRef = AF8AD62B1BEC28A400150209 /* PlatformAppleTVSimulator.h */; }; + AF8AD6301BEC28A400150209 /* PlatformAppleWatchSimulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF8AD62C1BEC28A400150209 /* PlatformAppleWatchSimulator.cpp */; }; + AF8AD6311BEC28A400150209 /* PlatformAppleWatchSimulator.h in Headers */ = {isa = PBXBuildFile; fileRef = AF8AD62D1BEC28A400150209 /* PlatformAppleWatchSimulator.h */; }; + AF8AD6371BEC28C400150209 /* PlatformRemoteAppleTV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF8AD6331BEC28C400150209 /* PlatformRemoteAppleTV.cpp */; }; + AF8AD6381BEC28C400150209 /* PlatformRemoteAppleTV.h in Headers */ = {isa = PBXBuildFile; fileRef = AF8AD6341BEC28C400150209 /* PlatformRemoteAppleTV.h */; }; + AF8AD6391BEC28C400150209 /* PlatformRemoteAppleWatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF8AD6351BEC28C400150209 /* PlatformRemoteAppleWatch.cpp */; }; + AF8AD63A1BEC28C400150209 /* PlatformRemoteAppleWatch.h in Headers */ = {isa = PBXBuildFile; fileRef = AF8AD6361BEC28C400150209 /* PlatformRemoteAppleWatch.h */; }; + AF90106515AB7D3600FF120D /* lldb.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = AF90106315AB7C5700FF120D /* lldb.1 */; }; + AF9107EE168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF9107EC168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp */; }; + AF9107EF168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF9107EC168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp */; }; + AF9B8F33182DB52900DA866F /* SystemRuntimeMacOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF9B8F31182DB52900DA866F /* SystemRuntimeMacOSX.cpp */; }; + AFB3D2801AC262AB003B4B30 /* MICmdCmdGdbShow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFB3D27E1AC262AB003B4B30 /* MICmdCmdGdbShow.cpp */; }; + AFC234081AF85CE000CDE8B6 /* CommandObjectLanguage.cpp in CopyFiles */ = {isa = PBXBuildFile; fileRef = AFC234061AF85CE000CDE8B6 /* CommandObjectLanguage.cpp */; }; + AFC234091AF85CE100CDE8B6 /* CommandObjectLanguage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFC234061AF85CE000CDE8B6 /* CommandObjectLanguage.cpp */; }; + AFCB2BBD1BF577F40018B553 /* PythonExceptionState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFCB2BBB1BF577F40018B553 /* PythonExceptionState.cpp */; }; + AFCB2BBE1BF577F40018B553 /* PythonExceptionState.h in Headers */ = {isa = PBXBuildFile; fileRef = AFCB2BBC1BF577F40018B553 /* PythonExceptionState.h */; }; + AFDCDBCB19DD0F42005EA55E /* SBExecutionContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 940B02F419DC96CB00AD0F52 /* SBExecutionContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFDFDFD119E34D3400EAE509 /* ConnectionFileDescriptorPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFDFDFD019E34D3400EAE509 /* ConnectionFileDescriptorPosix.cpp */; }; + AFEC3362194A8ABA00FF05C6 /* StructuredData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */; }; + AFF87C87150FF669000E1742 /* com.apple.debugserver.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = AFF87C86150FF669000E1742 /* com.apple.debugserver.plist */; }; + AFF87C8F150FF688000E1742 /* com.apple.debugserver.applist.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = AFF87C8E150FF688000E1742 /* com.apple.debugserver.applist.plist */; }; + B207C4931429607D00F36E4E /* CommandObjectWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B207C4921429607D00F36E4E /* CommandObjectWatchpoint.cpp */; }; + B2462247141AD37D00F3D409 /* OptionGroupWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2462246141AD37D00F3D409 /* OptionGroupWatchpoint.cpp */; }; + B27318421416AC12006039C8 /* WatchpointList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B27318411416AC12006039C8 /* WatchpointList.cpp */; }; + B28058A1139988B0002D96D0 /* InferiorCallPOSIX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B28058A0139988B0002D96D0 /* InferiorCallPOSIX.cpp */; }; + B299580B14F2FA1400050A04 /* DisassemblerLLVMC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B299580A14F2FA1400050A04 /* DisassemblerLLVMC.cpp */; }; + B2A58722143119810092BFBA /* SBWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = B2A58721143119810092BFBA /* SBWatchpoint.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B2A58724143119D50092BFBA /* SBWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2A58723143119D50092BFBA /* SBWatchpoint.cpp */; }; + B2B7CCEB15D1BD6700EEFB57 /* CommandObjectWatchpointCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2B7CCEA15D1BD6600EEFB57 /* CommandObjectWatchpointCommand.cpp */; }; + B2B7CCF015D1C20F00EEFB57 /* WatchpointOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2B7CCEF15D1C20F00EEFB57 /* WatchpointOptions.cpp */; }; + B5EFAE861AE53B1D007059F3 /* RegisterContextFreeBSD_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5EFAE841AE53B1D007059F3 /* RegisterContextFreeBSD_arm.cpp */; }; + E769331C1A94D15400C73337 /* lldb-gdbserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D6F3F4183E7F9300194858 /* lldb-gdbserver.cpp */; }; + E769331E1A94D18100C73337 /* lldb-server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E769331D1A94D18100C73337 /* lldb-server.cpp */; }; + E7723D441AC4A7FB002BA082 /* RegisterContextPOSIXCore_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7723D421AC4A7FB002BA082 /* RegisterContextPOSIXCore_arm64.cpp */; }; + E7723D481AC4A8C8002BA082 /* RegisterContextFreeBSD_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7723D461AC4A8C8002BA082 /* RegisterContextFreeBSD_arm64.cpp */; }; + E7723D4C1AC4A944002BA082 /* RegisterContextPOSIX_arm64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7723D4A1AC4A944002BA082 /* RegisterContextPOSIX_arm64.cpp */; }; + E778E9A21B062D1700247609 /* EmulateInstructionMIPS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E778E99F1B062D1700247609 /* EmulateInstructionMIPS.cpp */; }; + E7E94ABC1B54961F00D0AE30 /* GDBRemoteSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E73A15A41B548EC500786197 /* GDBRemoteSignals.cpp */; }; + EB8375E71B553DE800BA907D /* ThreadPlanCallFunctionUsingABI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EB8375E61B553DE800BA907D /* ThreadPlanCallFunctionUsingABI.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 239504C41BDD3FD700963CEA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 456F67721AD46CE9002850C2; + remoteInfo = "debugserver-mini"; + }; + 23AB8B6A1BDF513B008BF3B0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2689FFC913353D7A00698AC0; + remoteInfo = "lldb-core"; + }; + 262CFC7111A450CB00946C6C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 26CE0593115C31C20022F371; + remoteInfo = debugserver; + }; + 26368AF5126B95FA00E8659F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26579F67126A25920007C5CB; + remoteInfo = "darwin-debug"; + }; + 266803611160110D008E1FE4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26680206115FD0ED008E1FE4; + remoteInfo = LLDB; + }; + 2687EACA1508115000DD8C2E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2687EAC51508110B00DD8C2E; + remoteInfo = "install-headers"; + }; + 2687EACC1508115900DD8C2E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2687EAC51508110B00DD8C2E; + remoteInfo = "install-headers"; + }; + 2687EACE1508116300DD8C2E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2687EAC51508110B00DD8C2E; + remoteInfo = "install-headers"; + }; + 2689011413353E9B00698AC0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2689FFC913353D7A00698AC0; + remoteInfo = "lldb-core"; + }; + 26B391EE1A6DCCAF00456239 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2690CD161A6DC0D000E717C8; + remoteInfo = "lldb-mi"; + }; + 26B391F01A6DCCBE00456239 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2690CD161A6DC0D000E717C8; + remoteInfo = "lldb-mi"; + }; + 26CE059F115C31E50022F371 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 26CE0594115C31C20022F371; + remoteInfo = "lldb-debugserver"; + }; + 26CEF3AF14FD591F007286B2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26F5C26910F3D9A4009D5894; + remoteInfo = "lldb-tool"; + }; + 26CEF3BA14FD595B007286B2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26F5C26910F3D9A4009D5894; + remoteInfo = "lldb-tool"; + }; + 26CEF3C114FD5973007286B2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26F5C26910F3D9A4009D5894; + remoteInfo = "lldb-tool"; + }; + 26DC6A151337FE7300FF7998 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2689FFC913353D7A00698AC0; + remoteInfo = "lldb-core"; + }; + 26DF745F1A6DCDB300B85563 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26680206115FD0ED008E1FE4; + remoteInfo = LLDB; + }; + 942829C91A89836A00521B30 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2689FFC913353D7A00698AC0; + remoteInfo = "lldb-core"; + }; + 942829CD1A89842900521B30 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 942829BF1A89835300521B30; + remoteInfo = argdumper; + }; + 94E829C8152D33B4006F96A3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26DC6A0F1337FE6900FF7998; + remoteInfo = "lldb-server"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 239504D21BDD451400963CEA /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 940B04E31A89875C0045D5F7 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 7; + files = ( + 940B04E41A8987680045D5F7 /* lldb-argdumper in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 942829BE1A89835300521B30 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + AF90106415AB7D2900FF120D /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(DEVELOPER_DIR)/usr/share/man/man1/"; + dstSubfolderSpec = 0; + files = ( + 3FBA69E91B60672A0008F44A /* PythonDataObjects.h in CopyFiles */, + AF90106515AB7D3600FF120D /* lldb.1 in CopyFiles */, + 3FBA69E71B60672A0008F44A /* lldb-python.h in CopyFiles */, + 3FBA69DF1B6067020008F44A /* ScriptInterpreterNone.cpp in CopyFiles */, + 3FBA69E01B6067020008F44A /* ScriptInterpreterNone.h in CopyFiles */, + 33E5E8461A6736D30024ED68 /* StringConvert.h in CopyFiles */, + 3FBA69EB1B60672A0008F44A /* ScriptInterpreterPython.h in CopyFiles */, + AFC234081AF85CE000CDE8B6 /* CommandObjectLanguage.cpp in CopyFiles */, + 33E5E8421A672A240024ED68 /* StringConvert.cpp in CopyFiles */, + 49684D7B1BAB37E400E6D5D5 /* MIUtilParse.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + AFF87C85150FF5CC000E1742 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /Developer/Library/Lockdown/ServiceAgents/; + dstSubfolderSpec = 0; + files = ( + 268648C416531BF800F04704 /* com.apple.debugserver.posix.plist in CopyFiles */, + 268648C516531BF800F04704 /* com.apple.debugserver.applist.internal.plist in CopyFiles */, + 268648C616531BF800F04704 /* com.apple.debugserver.internal.plist in CopyFiles */, + AFF87C87150FF669000E1742 /* com.apple.debugserver.plist in CopyFiles */, + AFF87C8F150FF688000E1742 /* com.apple.debugserver.applist.plist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 23042D101976CA0A00621B2C /* PlatformKalimba.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformKalimba.cpp; sourceTree = ""; }; + 23042D111976CA0A00621B2C /* PlatformKalimba.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformKalimba.h; sourceTree = ""; }; + 23059A0519532B96007B8189 /* LinuxSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinuxSignals.cpp; path = Utility/LinuxSignals.cpp; sourceTree = ""; }; + 23059A0619532B96007B8189 /* LinuxSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinuxSignals.h; path = Utility/LinuxSignals.h; sourceTree = ""; }; + 23059A0F1958B319007B8189 /* SBUnixSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBUnixSignals.cpp; path = source/API/SBUnixSignals.cpp; sourceTree = ""; }; + 23059A111958B37B007B8189 /* SBUnixSignals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBUnixSignals.h; path = include/lldb/API/SBUnixSignals.h; sourceTree = ""; }; + 23173F8B192BA93F005C708F /* lldb-x86-register-enums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-x86-register-enums.h"; path = "Utility/lldb-x86-register-enums.h"; sourceTree = ""; }; + 2321F9381BDD332400BA9A93 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 2321F9391BDD332400BA9A93 /* SocketAddressTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SocketAddressTest.cpp; sourceTree = ""; }; + 2321F93A1BDD332400BA9A93 /* SocketTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SocketTest.cpp; sourceTree = ""; }; + 2321F93B1BDD332400BA9A93 /* SymbolsTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolsTest.cpp; sourceTree = ""; }; + 2321F93D1BDD33CE00BA9A93 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 2321F93E1BDD33CE00BA9A93 /* TestArgs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TestArgs.cpp; sourceTree = ""; }; + 2321F9401BDD340D00BA9A93 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 2321F9431BDD346100BA9A93 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 2321F9441BDD346100BA9A93 /* StringExtractorTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringExtractorTest.cpp; sourceTree = ""; }; + 2321F9451BDD346100BA9A93 /* TaskPoolTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TaskPoolTest.cpp; sourceTree = ""; }; + 2321F9461BDD346100BA9A93 /* UriParserTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UriParserTest.cpp; sourceTree = ""; }; + 2321F94C1BDD360F00BA9A93 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 2321F94D1BDD360F00BA9A93 /* PythonDataObjectsTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PythonDataObjectsTests.cpp; sourceTree = ""; }; + 2326CF3F1BDD613E00A5CEAC /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = System/Library/Frameworks/Python.framework; sourceTree = SDKROOT; }; + 2326CF451BDD647400A5CEAC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 2326CF471BDD67C100A5CEAC /* libncurses.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libncurses.dylib; path = ../../../../../../usr/lib/libncurses.dylib; sourceTree = ""; }; + 2326CF4A1BDD681800A5CEAC /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = ../../../../../../usr/lib/libz.dylib; sourceTree = ""; }; + 2326CF4C1BDD684B00A5CEAC /* libedit.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libedit.dylib; path = ../../../../../../usr/lib/libedit.dylib; sourceTree = ""; }; + 2326CF4E1BDD687800A5CEAC /* libpanel.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpanel.dylib; path = ../../../../../../usr/lib/libpanel.dylib; sourceTree = ""; }; + 2326CF511BDD693B00A5CEAC /* EditlineTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditlineTest.cpp; sourceTree = ""; }; + 232CB60B191E00CC00EF39FC /* NativeBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeBreakpoint.cpp; path = source/Host/common/NativeBreakpoint.cpp; sourceTree = ""; }; + 232CB60D191E00CC00EF39FC /* NativeBreakpointList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeBreakpointList.cpp; path = source/Host/common/NativeBreakpointList.cpp; sourceTree = ""; }; + 232CB60F191E00CC00EF39FC /* NativeProcessProtocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = NativeProcessProtocol.cpp; path = source/Host/common/NativeProcessProtocol.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 232CB611191E00CC00EF39FC /* NativeThreadProtocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeThreadProtocol.cpp; path = source/Host/common/NativeThreadProtocol.cpp; sourceTree = ""; }; + 232CB613191E00CC00EF39FC /* SoftwareBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SoftwareBreakpoint.cpp; path = source/Host/common/SoftwareBreakpoint.cpp; sourceTree = ""; }; + 232CB62B19213AC200EF39FC /* NativeProcessLinux.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NativeProcessLinux.cpp; sourceTree = ""; }; + 232CB62C19213AC200EF39FC /* NativeProcessLinux.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; path = NativeProcessLinux.h; sourceTree = ""; }; + 232CB62D19213AC200EF39FC /* NativeThreadLinux.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NativeThreadLinux.cpp; sourceTree = ""; }; + 232CB62E19213AC200EF39FC /* NativeThreadLinux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeThreadLinux.h; sourceTree = ""; }; + 233B007919609DB40090E598 /* ProcessLaunchInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ProcessLaunchInfo.h; path = include/lldb/Target/ProcessLaunchInfo.h; sourceTree = ""; }; + 233B007A1960A0440090E598 /* ProcessInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ProcessInfo.h; path = include/lldb/Target/ProcessInfo.h; sourceTree = ""; }; + 233B007B1960C9E60090E598 /* ProcessInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProcessInfo.cpp; path = source/Target/ProcessInfo.cpp; sourceTree = ""; }; + 233B007E1960CB280090E598 /* ProcessLaunchInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProcessLaunchInfo.cpp; path = source/Target/ProcessLaunchInfo.cpp; sourceTree = ""; }; + 233B009D19610D6B0090E598 /* Host.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Host.cpp; sourceTree = ""; }; + 233B00A1196113730090E598 /* ProcFileReader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProcFileReader.cpp; sourceTree = ""; }; + 233B00A2196113730090E598 /* ProcFileReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProcFileReader.h; sourceTree = ""; }; + 233B00A919622F3F0090E598 /* NativeRegisterContextLinux_x86_64.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NativeRegisterContextLinux_x86_64.cpp; sourceTree = ""; }; + 233B00AA19622F3F0090E598 /* NativeRegisterContextLinux_x86_64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeRegisterContextLinux_x86_64.h; sourceTree = ""; }; + 2360092C193FB21500189DB1 /* MemoryRegionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MemoryRegionInfo.h; path = include/lldb/Target/MemoryRegionInfo.h; sourceTree = ""; }; + 236124A21986B4E2004EFC37 /* IOObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IOObject.cpp; sourceTree = ""; }; + 236124A31986B4E2004EFC37 /* Socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Socket.cpp; sourceTree = ""; }; + 236124A61986B50E004EFC37 /* IOObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = IOObject.h; path = include/lldb/Host/IOObject.h; sourceTree = ""; }; + 236124A71986B50E004EFC37 /* Socket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Socket.h; path = include/lldb/Host/Socket.h; sourceTree = ""; }; + 2377C2F719E613C100737875 /* PipePosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PipePosix.cpp; sourceTree = ""; }; + 237C577A19AF9D9F00213D59 /* HostInfoLinux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostInfoLinux.h; path = include/lldb/Host/linux/HostInfoLinux.h; sourceTree = SOURCE_ROOT; }; + 239504C21BDD3FD600963CEA /* gtest_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gtest_common.h; sourceTree = ""; }; + 239504C61BDD3FF300963CEA /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 239504D41BDD451400963CEA /* lldb-gtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-gtest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 23AB052D199FF639003B8084 /* FreeBSDThread.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FreeBSDThread.cpp; sourceTree = ""; }; + 23AB052E199FF639003B8084 /* FreeBSDThread.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FreeBSDThread.h; sourceTree = ""; }; + 23AB052F199FF639003B8084 /* ProcessFreeBSD.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessFreeBSD.cpp; sourceTree = ""; }; + 23AB0530199FF639003B8084 /* ProcessFreeBSD.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProcessFreeBSD.h; sourceTree = ""; }; + 23AB0531199FF639003B8084 /* ProcessMonitor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessMonitor.cpp; sourceTree = ""; }; + 23AB0532199FF639003B8084 /* ProcessMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProcessMonitor.h; sourceTree = ""; }; + 23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandOptionValidators.cpp; path = source/Interpreter/CommandOptionValidators.cpp; sourceTree = ""; }; + 23E77CD61C20F29F007192AD /* DWARFDebugMacro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugMacro.cpp; sourceTree = ""; }; + 23E77CD71C20F29F007192AD /* DWARFDebugMacro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugMacro.h; sourceTree = ""; }; + 23E77CDB1C20F2F2007192AD /* DebugMacros.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DebugMacros.cpp; path = source/Symbol/DebugMacros.cpp; sourceTree = ""; }; + 23EDE3301926839700F6A132 /* NativeRegisterContext.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContext.cpp; path = source/Host/common/NativeRegisterContext.cpp; sourceTree = ""; }; + 23EDE3311926843600F6A132 /* NativeRegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContext.h; path = include/lldb/Host/common/NativeRegisterContext.h; sourceTree = ""; }; + 23EDE3371926AAD500F6A132 /* RegisterInfoInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RegisterInfoInterface.h; path = Utility/RegisterInfoInterface.h; sourceTree = ""; }; + 23EFE388193D1ABC00E54E54 /* SBTypeEnumMember.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBTypeEnumMember.h; path = include/lldb/API/SBTypeEnumMember.h; sourceTree = ""; }; + 23EFE38A193D1AEC00E54E54 /* SBTypeEnumMember.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SBTypeEnumMember.cpp; path = source/API/SBTypeEnumMember.cpp; sourceTree = ""; }; + 23F403471926C8D50046DC9B /* NativeRegisterContextRegisterInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContextRegisterInfo.h; path = include/lldb/Host/common/NativeRegisterContextRegisterInfo.h; sourceTree = ""; }; + 23F403481926CC250046DC9B /* NativeRegisterContextRegisterInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContextRegisterInfo.cpp; path = source/Host/common/NativeRegisterContextRegisterInfo.cpp; sourceTree = ""; }; + 250D6AE11A9679270049CC70 /* FileSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileSystem.cpp; sourceTree = ""; }; + 25420ECC1A6490B8009ADBCB /* OptionValueChar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueChar.cpp; path = source/Interpreter/OptionValueChar.cpp; sourceTree = ""; }; + 25420ECE1A64911B009ADBCB /* OptionValueChar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionValueChar.h; path = include/lldb/Interpreter/OptionValueChar.h; sourceTree = ""; }; + 25420ED11A649D88009ADBCB /* PipeBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PipeBase.cpp; sourceTree = ""; }; + 254FBB921A81AA5200BD6378 /* SBLaunchInfo.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBLaunchInfo.i; sourceTree = ""; }; + 254FBB941A81AA7F00BD6378 /* SBLaunchInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBLaunchInfo.cpp; path = source/API/SBLaunchInfo.cpp; sourceTree = ""; }; + 254FBB961A81B03100BD6378 /* SBLaunchInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBLaunchInfo.h; path = include/lldb/API/SBLaunchInfo.h; sourceTree = ""; }; + 254FBBA21A9166F100BD6378 /* SBAttachInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBAttachInfo.h; path = include/lldb/API/SBAttachInfo.h; sourceTree = ""; }; + 254FBBA41A91670E00BD6378 /* SBAttachInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBAttachInfo.cpp; path = source/API/SBAttachInfo.cpp; sourceTree = ""; }; + 254FBBA61A91672800BD6378 /* SBAttachInfo.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBAttachInfo.i; sourceTree = ""; }; + 255EFF6F1AFABA320069F277 /* LockFileWindows.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LockFileWindows.h; path = include/lldb/Host/windows/LockFileWindows.h; sourceTree = ""; }; + 255EFF701AFABA320069F277 /* PipeWindows.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PipeWindows.h; path = include/lldb/Host/windows/PipeWindows.h; sourceTree = ""; }; + 255EFF711AFABA4D0069F277 /* LockFileWindows.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LockFileWindows.cpp; path = source/Host/windows/LockFileWindows.cpp; sourceTree = ""; }; + 255EFF731AFABA720069F277 /* LockFileBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LockFileBase.cpp; sourceTree = ""; }; + 255EFF751AFABA950069F277 /* LockFilePosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LockFilePosix.cpp; sourceTree = ""; }; + 256CBDAB1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NativeRegisterContextLinux_arm.cpp; sourceTree = ""; }; + 256CBDAC1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeRegisterContextLinux_arm.h; sourceTree = ""; }; + 256CBDAD1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm64.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NativeRegisterContextLinux_arm64.cpp; sourceTree = ""; }; + 256CBDAE1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeRegisterContextLinux_arm64.h; sourceTree = ""; }; + 256CBDAF1ADD0DB600BC6CDC /* NativeRegisterContextLinux_mips64.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NativeRegisterContextLinux_mips64.cpp; sourceTree = ""; }; + 256CBDB01ADD0DB600BC6CDC /* NativeRegisterContextLinux_mips64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeRegisterContextLinux_mips64.h; sourceTree = ""; }; + 256CBDB21ADD0EFD00BC6CDC /* RegisterContextPOSIXCore_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXCore_arm.cpp; sourceTree = ""; }; + 256CBDB31ADD0EFD00BC6CDC /* RegisterContextPOSIXCore_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXCore_arm.h; sourceTree = ""; }; + 256CBDB61ADD107200BC6CDC /* RegisterContextLinux_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextLinux_arm.cpp; path = Utility/RegisterContextLinux_arm.cpp; sourceTree = ""; }; + 256CBDB71ADD107200BC6CDC /* RegisterContextLinux_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLinux_arm.h; path = Utility/RegisterContextLinux_arm.h; sourceTree = ""; }; + 256CBDB81ADD107200BC6CDC /* RegisterContextLinux_mips64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextLinux_mips64.cpp; path = Utility/RegisterContextLinux_mips64.cpp; sourceTree = ""; }; + 256CBDB91ADD107200BC6CDC /* RegisterContextLinux_mips64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLinux_mips64.h; path = Utility/RegisterContextLinux_mips64.h; sourceTree = ""; }; + 256CBDBE1ADD11C000BC6CDC /* RegisterContextPOSIX_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextPOSIX_arm.cpp; path = Utility/RegisterContextPOSIX_arm.cpp; sourceTree = ""; }; + 256CBDBF1ADD11C000BC6CDC /* RegisterContextPOSIX_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextPOSIX_arm.h; path = Utility/RegisterContextPOSIX_arm.h; sourceTree = ""; }; + 2579065A1BD0488100178368 /* TCPSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TCPSocket.cpp; sourceTree = ""; }; + 2579065B1BD0488100178368 /* UDPSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UDPSocket.cpp; sourceTree = ""; }; + 2579065E1BD0488D00178368 /* DomainSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DomainSocket.cpp; sourceTree = ""; }; + 257906621BD5AFD000178368 /* Acceptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Acceptor.cpp; path = "tools/lldb-server/Acceptor.cpp"; sourceTree = ""; }; + 257906631BD5AFD000178368 /* Acceptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Acceptor.h; path = "tools/lldb-server/Acceptor.h"; sourceTree = ""; }; + 257E47151AA56C2000A62F81 /* ModuleCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ModuleCache.cpp; path = source/Utility/ModuleCache.cpp; sourceTree = ""; }; + 257E47161AA56C2000A62F81 /* ModuleCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ModuleCache.h; path = source/Utility/ModuleCache.h; sourceTree = ""; }; + 25EF23751AC09AD800908DF0 /* AdbClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AdbClient.cpp; sourceTree = ""; }; + 25EF23761AC09AD800908DF0 /* AdbClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdbClient.h; sourceTree = ""; }; + 260157C41885F4FF00F875CF /* libpanel.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpanel.dylib; path = /usr/lib/libpanel.dylib; sourceTree = ""; }; + 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 = ""; }; + 260A248D15D06C4F009981B0 /* OptionValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValues.h; path = include/lldb/Interpreter/OptionValues.h; sourceTree = ""; }; + 260A39A519647A3A004B4130 /* Pipe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Pipe.h; path = include/lldb/Host/Pipe.h; sourceTree = ""; }; + 260A63111860FDB600FECF8E /* Queue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Queue.h; path = include/lldb/Target/Queue.h; sourceTree = ""; }; + 260A63121860FDBD00FECF8E /* QueueItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = QueueItem.h; path = include/lldb/Target/QueueItem.h; sourceTree = ""; }; + 260A63131860FDC700FECF8E /* QueueList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = QueueList.h; path = include/lldb/Target/QueueList.h; sourceTree = ""; }; + 260A63161861008E00FECF8E /* IOHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOHandler.h; path = include/lldb/Core/IOHandler.h; sourceTree = ""; }; + 260A63181861009E00FECF8E /* IOHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IOHandler.cpp; path = source/Core/IOHandler.cpp; sourceTree = ""; }; + 260C6EA013011578005E16B0 /* File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = File.h; path = include/lldb/Host/File.h; sourceTree = ""; }; + 260C6EA213011581005E16B0 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = File.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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; path = DWARFDefines.cpp; 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 = ""; }; + 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 = ""; }; + 260CC62115D04377002BF2E0 /* OptionValueArgs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueArgs.h; path = include/lldb/Interpreter/OptionValueArgs.h; sourceTree = ""; }; + 260CC62215D04377002BF2E0 /* OptionValueArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueArray.h; path = include/lldb/Interpreter/OptionValueArray.h; sourceTree = ""; }; + 260CC62315D04377002BF2E0 /* OptionValueBoolean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueBoolean.h; path = include/lldb/Interpreter/OptionValueBoolean.h; sourceTree = ""; }; + 260CC62415D04377002BF2E0 /* OptionValueProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueProperties.h; path = include/lldb/Interpreter/OptionValueProperties.h; sourceTree = ""; }; + 260CC62515D04377002BF2E0 /* OptionValueDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueDictionary.h; path = include/lldb/Interpreter/OptionValueDictionary.h; sourceTree = ""; }; + 260CC62615D04377002BF2E0 /* OptionValueEnumeration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueEnumeration.h; path = include/lldb/Interpreter/OptionValueEnumeration.h; sourceTree = ""; }; + 260CC62715D04377002BF2E0 /* OptionValueFileSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueFileSpec.h; path = include/lldb/Interpreter/OptionValueFileSpec.h; sourceTree = ""; }; + 260CC62815D04377002BF2E0 /* OptionValueFileSpecList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueFileSpecList.h; path = include/lldb/Interpreter/OptionValueFileSpecList.h; sourceTree = ""; }; + 260CC62915D04377002BF2E0 /* OptionValueFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueFormat.h; path = include/lldb/Interpreter/OptionValueFormat.h; sourceTree = ""; }; + 260CC62A15D04377002BF2E0 /* OptionValueSInt64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueSInt64.h; path = include/lldb/Interpreter/OptionValueSInt64.h; sourceTree = ""; }; + 260CC62B15D04377002BF2E0 /* OptionValueString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueString.h; path = include/lldb/Interpreter/OptionValueString.h; sourceTree = ""; }; + 260CC62C15D04377002BF2E0 /* OptionValueUInt64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueUInt64.h; path = include/lldb/Interpreter/OptionValueUInt64.h; sourceTree = ""; }; + 260CC62D15D04377002BF2E0 /* OptionValueUUID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueUUID.h; path = include/lldb/Interpreter/OptionValueUUID.h; sourceTree = ""; }; + 260CC63B15D0440D002BF2E0 /* OptionValueArgs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueArgs.cpp; path = source/Interpreter/OptionValueArgs.cpp; sourceTree = ""; }; + 260CC63C15D0440D002BF2E0 /* OptionValueArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueArray.cpp; path = source/Interpreter/OptionValueArray.cpp; sourceTree = ""; }; + 260CC63D15D0440D002BF2E0 /* OptionValueBoolean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueBoolean.cpp; path = source/Interpreter/OptionValueBoolean.cpp; sourceTree = ""; }; + 260CC63E15D0440D002BF2E0 /* OptionValueProperties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueProperties.cpp; path = source/Interpreter/OptionValueProperties.cpp; sourceTree = ""; }; + 260CC63F15D0440D002BF2E0 /* OptionValueDictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueDictionary.cpp; path = source/Interpreter/OptionValueDictionary.cpp; sourceTree = ""; }; + 260CC64015D0440D002BF2E0 /* OptionValueEnumeration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueEnumeration.cpp; path = source/Interpreter/OptionValueEnumeration.cpp; sourceTree = ""; }; + 260CC64115D0440D002BF2E0 /* OptionValueFileSpec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueFileSpec.cpp; path = source/Interpreter/OptionValueFileSpec.cpp; sourceTree = ""; }; + 260CC64215D0440D002BF2E0 /* OptionValueFileSpecLIst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueFileSpecLIst.cpp; path = source/Interpreter/OptionValueFileSpecLIst.cpp; sourceTree = ""; }; + 260CC64315D0440D002BF2E0 /* OptionValueFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueFormat.cpp; path = source/Interpreter/OptionValueFormat.cpp; sourceTree = ""; }; + 260CC64415D0440D002BF2E0 /* OptionValueSInt64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueSInt64.cpp; path = source/Interpreter/OptionValueSInt64.cpp; sourceTree = ""; }; + 260CC64515D0440D002BF2E0 /* OptionValueString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueString.cpp; path = source/Interpreter/OptionValueString.cpp; sourceTree = ""; }; + 260CC64615D0440D002BF2E0 /* OptionValueUInt64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueUInt64.cpp; path = source/Interpreter/OptionValueUInt64.cpp; sourceTree = ""; }; + 260CC64715D0440D002BF2E0 /* OptionValueUUID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueUUID.cpp; path = source/Interpreter/OptionValueUUID.cpp; sourceTree = ""; }; + 260D9B2615EC369500960137 /* ModuleSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ModuleSpec.h; path = include/lldb/Core/ModuleSpec.h; sourceTree = ""; }; + 260E07C3136FA68900CF21D3 /* OptionGroupUUID.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupUUID.h; path = include/lldb/Interpreter/OptionGroupUUID.h; sourceTree = ""; }; + 260E07C5136FA69E00CF21D3 /* OptionGroupUUID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupUUID.cpp; path = source/Interpreter/OptionGroupUUID.cpp; sourceTree = ""; }; + 260E07C7136FAB9200CF21D3 /* OptionGroupFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupFile.cpp; path = source/Interpreter/OptionGroupFile.cpp; sourceTree = ""; }; + 260E07C9136FABAC00CF21D3 /* OptionGroupFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupFile.h; path = include/lldb/Interpreter/OptionGroupFile.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 = ""; }; + 2611FEEF142D83060017FEA3 /* SBAddress.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBAddress.i; sourceTree = ""; }; + 2611FEF0142D83060017FEA3 /* SBBlock.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBBlock.i; sourceTree = ""; }; + 2611FEF1142D83060017FEA3 /* SBBreakpoint.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBBreakpoint.i; sourceTree = ""; }; + 2611FEF2142D83060017FEA3 /* SBBreakpointLocation.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBBreakpointLocation.i; sourceTree = ""; }; + 2611FEF3142D83060017FEA3 /* SBBroadcaster.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBBroadcaster.i; sourceTree = ""; }; + 2611FEF4142D83060017FEA3 /* SBCommandInterpreter.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBCommandInterpreter.i; sourceTree = ""; }; + 2611FEF5142D83060017FEA3 /* SBCommandReturnObject.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBCommandReturnObject.i; sourceTree = ""; }; + 2611FEF6142D83060017FEA3 /* SBCommunication.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBCommunication.i; sourceTree = ""; }; + 2611FEF7142D83060017FEA3 /* SBCompileUnit.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBCompileUnit.i; sourceTree = ""; }; + 2611FEF8142D83060017FEA3 /* SBData.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBData.i; sourceTree = ""; }; + 2611FEF9142D83060017FEA3 /* SBDebugger.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBDebugger.i; sourceTree = ""; }; + 2611FEFA142D83060017FEA3 /* SBError.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBError.i; sourceTree = ""; }; + 2611FEFB142D83060017FEA3 /* SBEvent.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBEvent.i; sourceTree = ""; }; + 2611FEFC142D83060017FEA3 /* SBFileSpec.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBFileSpec.i; sourceTree = ""; }; + 2611FEFD142D83060017FEA3 /* SBFileSpecList.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBFileSpecList.i; sourceTree = ""; }; + 2611FEFE142D83060017FEA3 /* SBFrame.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBFrame.i; sourceTree = ""; }; + 2611FEFF142D83060017FEA3 /* SBFunction.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBFunction.i; sourceTree = ""; }; + 2611FF00142D83060017FEA3 /* SBHostOS.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBHostOS.i; sourceTree = ""; }; + 2611FF02142D83060017FEA3 /* SBInstruction.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBInstruction.i; sourceTree = ""; }; + 2611FF03142D83060017FEA3 /* SBInstructionList.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBInstructionList.i; sourceTree = ""; }; + 2611FF04142D83060017FEA3 /* SBLineEntry.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBLineEntry.i; sourceTree = ""; }; + 2611FF05142D83060017FEA3 /* SBListener.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBListener.i; sourceTree = ""; }; + 2611FF06142D83060017FEA3 /* SBModule.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBModule.i; sourceTree = ""; }; + 2611FF07142D83060017FEA3 /* SBProcess.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBProcess.i; sourceTree = ""; }; + 2611FF08142D83060017FEA3 /* SBSection.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBSection.i; sourceTree = ""; }; + 2611FF09142D83060017FEA3 /* SBSourceManager.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBSourceManager.i; sourceTree = ""; }; + 2611FF0A142D83060017FEA3 /* SBStream.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBStream.i; sourceTree = ""; }; + 2611FF0B142D83060017FEA3 /* SBStringList.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBStringList.i; sourceTree = ""; }; + 2611FF0C142D83060017FEA3 /* SBSymbol.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBSymbol.i; sourceTree = ""; }; + 2611FF0D142D83060017FEA3 /* SBSymbolContext.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBSymbolContext.i; sourceTree = ""; }; + 2611FF0E142D83060017FEA3 /* SBSymbolContextList.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBSymbolContextList.i; sourceTree = ""; }; + 2611FF0F142D83060017FEA3 /* SBTarget.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBTarget.i; sourceTree = ""; }; + 2611FF10142D83060017FEA3 /* SBThread.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBThread.i; sourceTree = ""; }; + 2611FF11142D83060017FEA3 /* SBType.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBType.i; sourceTree = ""; }; + 2611FF12142D83060017FEA3 /* SBValue.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBValue.i; sourceTree = ""; }; + 2611FF13142D83060017FEA3 /* SBValueList.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBValueList.i; sourceTree = ""; }; + 2613F6C71B17B82F00D4DB85 /* CxaDemangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CxaDemangle.cpp; path = source/Core/CxaDemangle.cpp; sourceTree = ""; }; + 2613F6C91B17B84500D4DB85 /* CxaDemangle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CxaDemangle.h; path = include/lldb/Core/CxaDemangle.h; sourceTree = ""; }; + 2613F6CA1B17B85400D4DB85 /* FastDemangle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FastDemangle.h; path = include/lldb/Core/FastDemangle.h; sourceTree = ""; }; + 2615DB841208A9C90021781D /* StopInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StopInfo.h; path = include/lldb/Target/StopInfo.h; sourceTree = ""; }; + 2615DB861208A9E40021781D /* StopInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StopInfo.cpp; path = source/Target/StopInfo.cpp; sourceTree = ""; }; + 2615DBC81208B5FC0021781D /* StopInfoMachException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StopInfoMachException.cpp; path = Utility/StopInfoMachException.cpp; sourceTree = ""; }; + 2615DBC91208B5FC0021781D /* StopInfoMachException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StopInfoMachException.h; path = Utility/StopInfoMachException.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 = ""; }; + 2618D78F1240115500F2B8FE /* SectionLoadList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SectionLoadList.h; path = include/lldb/Target/SectionLoadList.h; sourceTree = ""; }; + 2618D7911240116900F2B8FE /* SectionLoadList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SectionLoadList.cpp; path = source/Target/SectionLoadList.cpp; sourceTree = ""; }; + 2618D957124056C700F2B8FE /* NameToDIE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NameToDIE.h; sourceTree = ""; }; + 2618D9EA12406FE600F2B8FE /* NameToDIE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NameToDIE.cpp; sourceTree = ""; }; + 2618EE5B1315B29C001D6D71 /* GDBRemoteCommunication.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GDBRemoteCommunication.cpp; sourceTree = ""; }; + 2618EE5C1315B29C001D6D71 /* GDBRemoteCommunication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GDBRemoteCommunication.h; sourceTree = ""; }; + 2618EE5D1315B29C001D6D71 /* GDBRemoteRegisterContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GDBRemoteRegisterContext.cpp; sourceTree = ""; }; + 2618EE5E1315B29C001D6D71 /* GDBRemoteRegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GDBRemoteRegisterContext.h; sourceTree = ""; }; + 2618EE5F1315B29C001D6D71 /* ProcessGDBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessGDBRemote.cpp; sourceTree = ""; }; + 2618EE601315B29C001D6D71 /* ProcessGDBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessGDBRemote.h; sourceTree = ""; }; + 2618EE611315B29C001D6D71 /* ProcessGDBRemoteLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessGDBRemoteLog.cpp; sourceTree = ""; }; + 2618EE621315B29C001D6D71 /* ProcessGDBRemoteLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessGDBRemoteLog.h; sourceTree = ""; }; + 2618EE631315B29C001D6D71 /* ThreadGDBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadGDBRemote.cpp; sourceTree = ""; }; + 2618EE641315B29C001D6D71 /* ThreadGDBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadGDBRemote.h; sourceTree = ""; }; + 261B5A5211C3F2AD00AABD0A /* SharingPtr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SharingPtr.cpp; path = source/Utility/SharingPtr.cpp; sourceTree = ""; }; + 261B5A5311C3F2AD00AABD0A /* SharingPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharingPtr.h; path = include/lldb/Utility/SharingPtr.h; sourceTree = ""; }; + 262173A018395D3800C52091 /* SectionLoadHistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SectionLoadHistory.h; path = include/lldb/Target/SectionLoadHistory.h; sourceTree = ""; }; + 262173A218395D4600C52091 /* SectionLoadHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SectionLoadHistory.cpp; path = source/Target/SectionLoadHistory.cpp; sourceTree = ""; }; + 26217930133BC8640083B112 /* lldb-private-types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "lldb-private-types.h"; path = "include/lldb/lldb-private-types.h"; sourceTree = ""; }; + 26217932133BCB850083B112 /* lldb-private-enumerations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "lldb-private-enumerations.h"; path = "include/lldb/lldb-private-enumerations.h"; sourceTree = ""; }; + 2623096E13D0EFFB006381D9 /* StreamBuffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StreamBuffer.h; path = include/lldb/Core/StreamBuffer.h; sourceTree = ""; }; + 2626B6AD143E1BEA00EF935C /* RangeMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RangeMap.h; path = include/lldb/Core/RangeMap.h; sourceTree = ""; }; + 26274FA514030F79006BA130 /* DynamicLoaderDarwinKernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicLoaderDarwinKernel.cpp; sourceTree = ""; }; + 26274FA614030F79006BA130 /* DynamicLoaderDarwinKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicLoaderDarwinKernel.h; sourceTree = ""; }; + 2628A4D313D4977900F5487A /* ThreadKDP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadKDP.cpp; sourceTree = ""; }; + 2628A4D413D4977900F5487A /* ThreadKDP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadKDP.h; sourceTree = ""; }; + 262D24E413FB8710002D1960 /* RegisterContextMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextMemory.cpp; path = Utility/RegisterContextMemory.cpp; sourceTree = ""; }; + 262D24E513FB8710002D1960 /* RegisterContextMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextMemory.h; path = Utility/RegisterContextMemory.h; sourceTree = ""; }; + 262ED0041631FA2800879631 /* OptionGroupString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionGroupString.h; path = include/lldb/Interpreter/OptionGroupString.h; sourceTree = ""; }; + 262ED0071631FA3A00879631 /* OptionGroupString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupString.cpp; path = source/Interpreter/OptionGroupString.cpp; sourceTree = ""; }; + 262F12B41835468600AEB384 /* SBPlatform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBPlatform.cpp; path = source/API/SBPlatform.cpp; sourceTree = ""; }; + 262F12B61835469C00AEB384 /* SBPlatform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBPlatform.h; path = include/lldb/API/SBPlatform.h; sourceTree = ""; }; + 262F12B8183546C900AEB384 /* SBPlatform.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBPlatform.i; sourceTree = ""; }; + 2635879017822E56004C30BA /* SymbolVendorELF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolVendorELF.cpp; sourceTree = ""; }; + 2635879117822E56004C30BA /* SymbolVendorELF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolVendorELF.h; sourceTree = ""; }; + 263641151B34AEE200145B2F /* ABISysV_mips64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ABISysV_mips64.cpp; sourceTree = ""; }; + 263641161B34AEE200145B2F /* ABISysV_mips64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ABISysV_mips64.h; sourceTree = ""; }; + 263664921140A4930075843B /* Debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = Debugger.cpp; path = source/Core/Debugger.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 263664941140A4C10075843B /* Debugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = Debugger.h; path = include/lldb/Core/Debugger.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 26368A3B126B697600E8659F /* darwin-debug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "darwin-debug.cpp"; path = "tools/darwin-debug/darwin-debug.cpp"; sourceTree = ""; }; + 263C4937178B50C40070F12D /* SBModuleSpec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBModuleSpec.cpp; path = source/API/SBModuleSpec.cpp; sourceTree = ""; }; + 263C4939178B50CF0070F12D /* SBModuleSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBModuleSpec.h; path = include/lldb/API/SBModuleSpec.h; sourceTree = ""; }; + 263C493B178B61CC0070F12D /* SBModuleSpec.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBModuleSpec.i; sourceTree = ""; }; + 263E949D13661AE400E7D1CE /* UnwindAssembly-x86.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "UnwindAssembly-x86.cpp"; sourceTree = ""; }; + 263E949E13661AE400E7D1CE /* UnwindAssembly-x86.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UnwindAssembly-x86.h"; sourceTree = ""; }; + 263FDE5D1A799F2D00E68013 /* FormatEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FormatEntity.h; path = include/lldb/Core/FormatEntity.h; sourceTree = ""; }; + 263FDE5F1A79A01500E68013 /* FormatEntity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FormatEntity.cpp; path = source/Core/FormatEntity.cpp; sourceTree = ""; }; + 263FEDA5112CC1DA00E4C208 /* ThreadSafeSTLMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadSafeSTLMap.h; path = include/lldb/Core/ThreadSafeSTLMap.h; sourceTree = ""; }; + 2640E19E15DC78FD00F23B50 /* Property.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Property.cpp; path = source/Interpreter/Property.cpp; sourceTree = ""; }; + 26424E3C125986CB0016D82C /* ValueObjectConstResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectConstResult.cpp; path = source/Core/ValueObjectConstResult.cpp; sourceTree = ""; }; + 26424E3E125986D30016D82C /* ValueObjectConstResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueObjectConstResult.h; path = include/lldb/Core/ValueObjectConstResult.h; sourceTree = ""; }; + 2642FBA813D003B400ED6808 /* CommunicationKDP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommunicationKDP.cpp; sourceTree = ""; }; + 2642FBA913D003B400ED6808 /* CommunicationKDP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommunicationKDP.h; sourceTree = ""; }; + 2642FBAA13D003B400ED6808 /* ProcessKDP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessKDP.cpp; sourceTree = ""; }; + 2642FBAB13D003B400ED6808 /* ProcessKDP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessKDP.h; sourceTree = ""; }; + 2642FBAC13D003B400ED6808 /* ProcessKDPLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessKDPLog.cpp; sourceTree = ""; }; + 2642FBAD13D003B400ED6808 /* ProcessKDPLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessKDPLog.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 = ""; }; + 264723A511FA076E00DE380C /* CleanUp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CleanUp.h; path = include/lldb/Utility/CleanUp.h; sourceTree = ""; }; + 26474C9E18D0CAEC0073DEBA /* RegisterContext_mips64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContext_mips64.h; path = Utility/RegisterContext_mips64.h; sourceTree = ""; }; + 26474C9F18D0CAEC0073DEBA /* RegisterContext_x86.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContext_x86.h; path = Utility/RegisterContext_x86.h; sourceTree = ""; }; + 26474CA218D0CB070073DEBA /* RegisterContextFreeBSD_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextFreeBSD_i386.cpp; path = Utility/RegisterContextFreeBSD_i386.cpp; sourceTree = ""; }; + 26474CA318D0CB070073DEBA /* RegisterContextFreeBSD_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextFreeBSD_i386.h; path = Utility/RegisterContextFreeBSD_i386.h; sourceTree = ""; }; + 26474CA418D0CB070073DEBA /* RegisterContextFreeBSD_mips64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextFreeBSD_mips64.cpp; path = Utility/RegisterContextFreeBSD_mips64.cpp; sourceTree = ""; }; + 26474CA518D0CB070073DEBA /* RegisterContextFreeBSD_mips64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextFreeBSD_mips64.h; path = Utility/RegisterContextFreeBSD_mips64.h; sourceTree = ""; }; + 26474CA618D0CB070073DEBA /* RegisterContextFreeBSD_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextFreeBSD_x86_64.cpp; path = Utility/RegisterContextFreeBSD_x86_64.cpp; sourceTree = ""; }; + 26474CA718D0CB070073DEBA /* RegisterContextFreeBSD_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextFreeBSD_x86_64.h; path = Utility/RegisterContextFreeBSD_x86_64.h; sourceTree = ""; }; + 26474CAE18D0CB180073DEBA /* RegisterContextLinux_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextLinux_i386.cpp; path = Utility/RegisterContextLinux_i386.cpp; sourceTree = ""; }; + 26474CAF18D0CB180073DEBA /* RegisterContextLinux_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLinux_i386.h; path = Utility/RegisterContextLinux_i386.h; sourceTree = ""; }; + 26474CB018D0CB180073DEBA /* RegisterContextLinux_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextLinux_x86_64.cpp; path = Utility/RegisterContextLinux_x86_64.cpp; sourceTree = ""; }; + 26474CB118D0CB180073DEBA /* RegisterContextLinux_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLinux_x86_64.h; path = Utility/RegisterContextLinux_x86_64.h; sourceTree = ""; }; + 26474CB618D0CB2D0073DEBA /* RegisterContextMach_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextMach_arm.cpp; path = Utility/RegisterContextMach_arm.cpp; sourceTree = ""; }; + 26474CB718D0CB2D0073DEBA /* RegisterContextMach_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextMach_arm.h; path = Utility/RegisterContextMach_arm.h; sourceTree = ""; }; + 26474CB818D0CB2D0073DEBA /* RegisterContextMach_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextMach_i386.cpp; path = Utility/RegisterContextMach_i386.cpp; sourceTree = ""; }; + 26474CB918D0CB2D0073DEBA /* RegisterContextMach_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextMach_i386.h; path = Utility/RegisterContextMach_i386.h; sourceTree = ""; }; + 26474CBA18D0CB2D0073DEBA /* RegisterContextMach_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextMach_x86_64.cpp; path = Utility/RegisterContextMach_x86_64.cpp; sourceTree = ""; }; + 26474CBB18D0CB2D0073DEBA /* RegisterContextMach_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextMach_x86_64.h; path = Utility/RegisterContextMach_x86_64.h; sourceTree = ""; }; + 26474CC218D0CB5B0073DEBA /* RegisterContextMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextMemory.cpp; path = Utility/RegisterContextMemory.cpp; sourceTree = ""; }; + 26474CC318D0CB5B0073DEBA /* RegisterContextMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextMemory.h; path = Utility/RegisterContextMemory.h; sourceTree = ""; }; + 26474CC418D0CB5B0073DEBA /* RegisterContextPOSIX_mips64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextPOSIX_mips64.cpp; path = Utility/RegisterContextPOSIX_mips64.cpp; sourceTree = ""; }; + 26474CC518D0CB5B0073DEBA /* RegisterContextPOSIX_mips64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextPOSIX_mips64.h; path = Utility/RegisterContextPOSIX_mips64.h; sourceTree = ""; }; + 26474CC618D0CB5B0073DEBA /* RegisterContextPOSIX_x86.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextPOSIX_x86.cpp; path = Utility/RegisterContextPOSIX_x86.cpp; sourceTree = ""; }; + 26474CC718D0CB5B0073DEBA /* RegisterContextPOSIX_x86.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextPOSIX_x86.h; path = Utility/RegisterContextPOSIX_x86.h; sourceTree = ""; }; + 26474CC818D0CB5B0073DEBA /* RegisterContextPOSIX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextPOSIX.h; path = Utility/RegisterContextPOSIX.h; sourceTree = ""; }; + 26474CD018D0CB700073DEBA /* RegisterInfos_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterInfos_i386.h; path = Utility/RegisterInfos_i386.h; sourceTree = ""; }; + 26474CD118D0CB710073DEBA /* RegisterInfos_mips64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterInfos_mips64.h; path = Utility/RegisterInfos_mips64.h; sourceTree = ""; }; + 26474CD218D0CB710073DEBA /* RegisterInfos_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterInfos_x86_64.h; path = Utility/RegisterInfos_x86_64.h; sourceTree = ""; }; + 26491E3A15E1DB8600CBFFC2 /* OptionValueRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueRegex.h; path = include/lldb/Interpreter/OptionValueRegex.h; sourceTree = ""; }; + 26491E3D15E1DB9F00CBFFC2 /* OptionValueRegex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueRegex.cpp; path = source/Interpreter/OptionValueRegex.cpp; sourceTree = ""; }; + 264A12FA1372522000875C42 /* EmulateInstructionARM64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EmulateInstructionARM64.cpp; sourceTree = ""; }; + 264A12FB1372522000875C42 /* EmulateInstructionARM64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmulateInstructionARM64.h; sourceTree = ""; }; + 264A12FE137252C700875C42 /* ARM64_DWARF_Registers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ARM64_DWARF_Registers.cpp; path = source/Utility/ARM64_DWARF_Registers.cpp; sourceTree = ""; }; + 264A12FF137252C700875C42 /* ARM64_DWARF_Registers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARM64_DWARF_Registers.h; path = source/Utility/ARM64_DWARF_Registers.h; sourceTree = ""; }; + 264A43BB1320B3B4005B4096 /* Platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Platform.h; path = include/lldb/Target/Platform.h; sourceTree = ""; }; + 264A43BD1320BCEB005B4096 /* Platform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Platform.cpp; path = source/Target/Platform.cpp; sourceTree = ""; }; + 264A58EB1A7DBC8C00A6B1B0 /* OptionValueFormatEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueFormatEntity.h; path = include/lldb/Interpreter/OptionValueFormatEntity.h; sourceTree = ""; }; + 264A58ED1A7DBCAD00A6B1B0 /* OptionValueFormatEntity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueFormatEntity.cpp; path = source/Interpreter/OptionValueFormatEntity.cpp; sourceTree = ""; }; + 264A97BD133918BC0017F0BE /* PlatformRemoteGDBServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PlatformRemoteGDBServer.cpp; path = "gdb-server/PlatformRemoteGDBServer.cpp"; sourceTree = ""; }; + 264A97BE133918BC0017F0BE /* PlatformRemoteGDBServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlatformRemoteGDBServer.h; path = "gdb-server/PlatformRemoteGDBServer.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 = ""; }; + 264D8D4E13661BCC003A368F /* UnwindAssembly.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = UnwindAssembly.h; path = include/lldb/Target/UnwindAssembly.h; sourceTree = ""; }; + 264D8D4F13661BD7003A368F /* UnwindAssembly.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindAssembly.cpp; path = source/Target/UnwindAssembly.cpp; sourceTree = ""; }; + 265192C41BA8E8F8002F08F6 /* CompilerDecl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CompilerDecl.h; path = include/lldb/Symbol/CompilerDecl.h; sourceTree = ""; }; + 265192C51BA8E905002F08F6 /* CompilerDecl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CompilerDecl.cpp; path = source/Symbol/CompilerDecl.cpp; sourceTree = ""; }; + 265205A213D3E3F700132FE2 /* RegisterContextKDP_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextKDP_arm.cpp; sourceTree = ""; }; + 265205A313D3E3F700132FE2 /* RegisterContextKDP_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextKDP_arm.h; sourceTree = ""; }; + 265205A413D3E3F700132FE2 /* RegisterContextKDP_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextKDP_i386.cpp; sourceTree = ""; }; + 265205A513D3E3F700132FE2 /* RegisterContextKDP_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextKDP_i386.h; sourceTree = ""; }; + 265205A613D3E3F700132FE2 /* RegisterContextKDP_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextKDP_x86_64.cpp; sourceTree = ""; }; + 265205A713D3E3F700132FE2 /* RegisterContextKDP_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextKDP_x86_64.h; sourceTree = ""; }; + 26579F68126A25920007C5CB /* darwin-debug */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "darwin-debug"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2657AFB51B8690EC00958979 /* CompilerDeclContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CompilerDeclContext.h; path = include/lldb/Symbol/CompilerDeclContext.h; sourceTree = ""; }; + 2657AFB61B86910100958979 /* CompilerDeclContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CompilerDeclContext.cpp; path = source/Symbol/CompilerDeclContext.cpp; 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 = ""; }; + 26651A14133BEC76005B64B7 /* lldb-public.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "lldb-public.h"; path = "include/lldb/lldb-public.h"; sourceTree = ""; }; + 26651A15133BF9CC005B64B7 /* Opcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Opcode.h; path = include/lldb/Core/Opcode.h; sourceTree = ""; }; + 26651A17133BF9DF005B64B7 /* Opcode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Opcode.cpp; path = source/Core/Opcode.cpp; sourceTree = ""; }; + 2665CD0D15080846002C8FAE /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 266603C91345B5A8004DA8B6 /* ConnectionSharedMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ConnectionSharedMemory.cpp; path = source/Core/ConnectionSharedMemory.cpp; sourceTree = ""; }; + 266603CC1345B5C0004DA8B6 /* ConnectionSharedMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConnectionSharedMemory.h; path = include/lldb/Core/ConnectionSharedMemory.h; sourceTree = ""; }; + 2666ADC11B3CB675001FAFD3 /* DynamicLoaderHexagonDYLD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicLoaderHexagonDYLD.cpp; sourceTree = ""; }; + 2666ADC21B3CB675001FAFD3 /* DynamicLoaderHexagonDYLD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicLoaderHexagonDYLD.h; sourceTree = ""; }; + 2666ADC31B3CB675001FAFD3 /* HexagonDYLDRendezvous.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HexagonDYLDRendezvous.cpp; sourceTree = ""; }; + 2666ADC41B3CB675001FAFD3 /* HexagonDYLDRendezvous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HexagonDYLDRendezvous.h; sourceTree = ""; }; + 26680207115FD0ED008E1FE4 /* LLDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LLDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2669415B1A6DC2AB0063BE93 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = "tools/lldb-mi/CMakeLists.txt"; sourceTree = SOURCE_ROOT; }; + 2669415E1A6DC2AB0063BE93 /* lldb-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "lldb-Info.plist"; path = "tools/lldb-mi/lldb-Info.plist"; sourceTree = SOURCE_ROOT; }; + 2669415F1A6DC2AB0063BE93 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = "tools/lldb-mi/Makefile"; sourceTree = SOURCE_ROOT; }; + 266941601A6DC2AB0063BE93 /* MICmdArgContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgContext.cpp; path = "tools/lldb-mi/MICmdArgContext.cpp"; sourceTree = SOURCE_ROOT; }; + 266941611A6DC2AB0063BE93 /* MICmdArgContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgContext.h; path = "tools/lldb-mi/MICmdArgContext.h"; sourceTree = SOURCE_ROOT; }; + 266941621A6DC2AB0063BE93 /* MICmdArgSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgSet.cpp; path = "tools/lldb-mi/MICmdArgSet.cpp"; sourceTree = SOURCE_ROOT; }; + 266941631A6DC2AB0063BE93 /* MICmdArgSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgSet.h; path = "tools/lldb-mi/MICmdArgSet.h"; sourceTree = SOURCE_ROOT; }; + 266941641A6DC2AB0063BE93 /* MICmdArgValBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValBase.cpp; path = "tools/lldb-mi/MICmdArgValBase.cpp"; sourceTree = SOURCE_ROOT; }; + 266941651A6DC2AB0063BE93 /* MICmdArgValBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValBase.h; path = "tools/lldb-mi/MICmdArgValBase.h"; sourceTree = SOURCE_ROOT; }; + 266941661A6DC2AB0063BE93 /* MICmdArgValConsume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValConsume.cpp; path = "tools/lldb-mi/MICmdArgValConsume.cpp"; sourceTree = SOURCE_ROOT; }; + 266941671A6DC2AB0063BE93 /* MICmdArgValConsume.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValConsume.h; path = "tools/lldb-mi/MICmdArgValConsume.h"; sourceTree = SOURCE_ROOT; }; + 266941681A6DC2AB0063BE93 /* MICmdArgValFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValFile.cpp; path = "tools/lldb-mi/MICmdArgValFile.cpp"; sourceTree = SOURCE_ROOT; }; + 266941691A6DC2AB0063BE93 /* MICmdArgValFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValFile.h; path = "tools/lldb-mi/MICmdArgValFile.h"; sourceTree = SOURCE_ROOT; }; + 2669416A1A6DC2AC0063BE93 /* MICmdArgValListBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValListBase.cpp; path = "tools/lldb-mi/MICmdArgValListBase.cpp"; sourceTree = SOURCE_ROOT; }; + 2669416B1A6DC2AC0063BE93 /* MICmdArgValListBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValListBase.h; path = "tools/lldb-mi/MICmdArgValListBase.h"; sourceTree = SOURCE_ROOT; }; + 2669416C1A6DC2AC0063BE93 /* MICmdArgValListOfN.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValListOfN.cpp; path = "tools/lldb-mi/MICmdArgValListOfN.cpp"; sourceTree = SOURCE_ROOT; }; + 2669416D1A6DC2AC0063BE93 /* MICmdArgValListOfN.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValListOfN.h; path = "tools/lldb-mi/MICmdArgValListOfN.h"; sourceTree = SOURCE_ROOT; }; + 2669416E1A6DC2AC0063BE93 /* MICmdArgValNumber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValNumber.cpp; path = "tools/lldb-mi/MICmdArgValNumber.cpp"; sourceTree = SOURCE_ROOT; }; + 2669416F1A6DC2AC0063BE93 /* MICmdArgValNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValNumber.h; path = "tools/lldb-mi/MICmdArgValNumber.h"; sourceTree = SOURCE_ROOT; }; + 266941701A6DC2AC0063BE93 /* MICmdArgValOptionLong.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValOptionLong.cpp; path = "tools/lldb-mi/MICmdArgValOptionLong.cpp"; sourceTree = SOURCE_ROOT; }; + 266941711A6DC2AC0063BE93 /* MICmdArgValOptionLong.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValOptionLong.h; path = "tools/lldb-mi/MICmdArgValOptionLong.h"; sourceTree = SOURCE_ROOT; }; + 266941721A6DC2AC0063BE93 /* MICmdArgValOptionShort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValOptionShort.cpp; path = "tools/lldb-mi/MICmdArgValOptionShort.cpp"; sourceTree = SOURCE_ROOT; }; + 266941731A6DC2AC0063BE93 /* MICmdArgValOptionShort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValOptionShort.h; path = "tools/lldb-mi/MICmdArgValOptionShort.h"; sourceTree = SOURCE_ROOT; }; + 266941741A6DC2AC0063BE93 /* MICmdArgValString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValString.cpp; path = "tools/lldb-mi/MICmdArgValString.cpp"; sourceTree = SOURCE_ROOT; }; + 266941751A6DC2AC0063BE93 /* MICmdArgValString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValString.h; path = "tools/lldb-mi/MICmdArgValString.h"; sourceTree = SOURCE_ROOT; }; + 266941761A6DC2AC0063BE93 /* MICmdArgValThreadGrp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValThreadGrp.cpp; path = "tools/lldb-mi/MICmdArgValThreadGrp.cpp"; sourceTree = SOURCE_ROOT; }; + 266941771A6DC2AC0063BE93 /* MICmdArgValThreadGrp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValThreadGrp.h; path = "tools/lldb-mi/MICmdArgValThreadGrp.h"; sourceTree = SOURCE_ROOT; }; + 266941781A6DC2AC0063BE93 /* MICmdBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdBase.cpp; path = "tools/lldb-mi/MICmdBase.cpp"; sourceTree = SOURCE_ROOT; }; + 266941791A6DC2AC0063BE93 /* MICmdBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdBase.h; path = "tools/lldb-mi/MICmdBase.h"; sourceTree = SOURCE_ROOT; }; + 2669417A1A6DC2AC0063BE93 /* MICmdCmd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmd.cpp; path = "tools/lldb-mi/MICmdCmd.cpp"; sourceTree = SOURCE_ROOT; }; + 2669417B1A6DC2AC0063BE93 /* MICmdCmd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmd.h; path = "tools/lldb-mi/MICmdCmd.h"; sourceTree = SOURCE_ROOT; }; + 2669417C1A6DC2AC0063BE93 /* MICmdCmdBreak.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdBreak.cpp; path = "tools/lldb-mi/MICmdCmdBreak.cpp"; sourceTree = SOURCE_ROOT; }; + 2669417D1A6DC2AC0063BE93 /* MICmdCmdBreak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdBreak.h; path = "tools/lldb-mi/MICmdCmdBreak.h"; sourceTree = SOURCE_ROOT; }; + 2669417E1A6DC2AC0063BE93 /* MICmdCmdData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdData.cpp; path = "tools/lldb-mi/MICmdCmdData.cpp"; sourceTree = SOURCE_ROOT; }; + 2669417F1A6DC2AC0063BE93 /* MICmdCmdData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdData.h; path = "tools/lldb-mi/MICmdCmdData.h"; sourceTree = SOURCE_ROOT; }; + 266941801A6DC2AC0063BE93 /* MICmdCmdEnviro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdEnviro.cpp; path = "tools/lldb-mi/MICmdCmdEnviro.cpp"; sourceTree = SOURCE_ROOT; }; + 266941811A6DC2AC0063BE93 /* MICmdCmdEnviro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdEnviro.h; path = "tools/lldb-mi/MICmdCmdEnviro.h"; sourceTree = SOURCE_ROOT; }; + 266941821A6DC2AC0063BE93 /* MICmdCmdExec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdExec.cpp; path = "tools/lldb-mi/MICmdCmdExec.cpp"; sourceTree = SOURCE_ROOT; }; + 266941831A6DC2AC0063BE93 /* MICmdCmdExec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdExec.h; path = "tools/lldb-mi/MICmdCmdExec.h"; sourceTree = SOURCE_ROOT; }; + 266941841A6DC2AC0063BE93 /* MICmdCmdFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdFile.cpp; path = "tools/lldb-mi/MICmdCmdFile.cpp"; sourceTree = SOURCE_ROOT; }; + 266941851A6DC2AC0063BE93 /* MICmdCmdFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdFile.h; path = "tools/lldb-mi/MICmdCmdFile.h"; sourceTree = SOURCE_ROOT; }; + 266941861A6DC2AC0063BE93 /* MICmdCmdGdbInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdGdbInfo.cpp; path = "tools/lldb-mi/MICmdCmdGdbInfo.cpp"; sourceTree = SOURCE_ROOT; }; + 266941871A6DC2AC0063BE93 /* MICmdCmdGdbInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdGdbInfo.h; path = "tools/lldb-mi/MICmdCmdGdbInfo.h"; sourceTree = SOURCE_ROOT; }; + 266941881A6DC2AC0063BE93 /* MICmdCmdGdbSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdGdbSet.cpp; path = "tools/lldb-mi/MICmdCmdGdbSet.cpp"; sourceTree = SOURCE_ROOT; }; + 266941891A6DC2AC0063BE93 /* MICmdCmdGdbSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdGdbSet.h; path = "tools/lldb-mi/MICmdCmdGdbSet.h"; sourceTree = SOURCE_ROOT; }; + 2669418A1A6DC2AC0063BE93 /* MICmdCmdGdbThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdGdbThread.cpp; path = "tools/lldb-mi/MICmdCmdGdbThread.cpp"; sourceTree = SOURCE_ROOT; }; + 2669418B1A6DC2AC0063BE93 /* MICmdCmdGdbThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdGdbThread.h; path = "tools/lldb-mi/MICmdCmdGdbThread.h"; sourceTree = SOURCE_ROOT; }; + 2669418C1A6DC2AC0063BE93 /* MICmdCmdMiscellanous.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdMiscellanous.cpp; path = "tools/lldb-mi/MICmdCmdMiscellanous.cpp"; sourceTree = SOURCE_ROOT; }; + 2669418D1A6DC2AC0063BE93 /* MICmdCmdMiscellanous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdMiscellanous.h; path = "tools/lldb-mi/MICmdCmdMiscellanous.h"; sourceTree = SOURCE_ROOT; }; + 2669418E1A6DC2AC0063BE93 /* MICmdCmdStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdStack.cpp; path = "tools/lldb-mi/MICmdCmdStack.cpp"; sourceTree = SOURCE_ROOT; }; + 2669418F1A6DC2AC0063BE93 /* MICmdCmdStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdStack.h; path = "tools/lldb-mi/MICmdCmdStack.h"; sourceTree = SOURCE_ROOT; }; + 266941901A6DC2AC0063BE93 /* MICmdCmdSupportInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdSupportInfo.cpp; path = "tools/lldb-mi/MICmdCmdSupportInfo.cpp"; sourceTree = SOURCE_ROOT; }; + 266941911A6DC2AC0063BE93 /* MICmdCmdSupportInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdSupportInfo.h; path = "tools/lldb-mi/MICmdCmdSupportInfo.h"; sourceTree = SOURCE_ROOT; }; + 266941921A6DC2AC0063BE93 /* MICmdCmdSupportList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdSupportList.cpp; path = "tools/lldb-mi/MICmdCmdSupportList.cpp"; sourceTree = SOURCE_ROOT; }; + 266941931A6DC2AC0063BE93 /* MICmdCmdSupportList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdSupportList.h; path = "tools/lldb-mi/MICmdCmdSupportList.h"; sourceTree = SOURCE_ROOT; }; + 266941941A6DC2AC0063BE93 /* MICmdCmdTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdTarget.cpp; path = "tools/lldb-mi/MICmdCmdTarget.cpp"; sourceTree = SOURCE_ROOT; }; + 266941951A6DC2AC0063BE93 /* MICmdCmdTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdTarget.h; path = "tools/lldb-mi/MICmdCmdTarget.h"; sourceTree = SOURCE_ROOT; }; + 266941961A6DC2AC0063BE93 /* MICmdCmdThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdThread.cpp; path = "tools/lldb-mi/MICmdCmdThread.cpp"; sourceTree = SOURCE_ROOT; }; + 266941971A6DC2AC0063BE93 /* MICmdCmdThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdThread.h; path = "tools/lldb-mi/MICmdCmdThread.h"; sourceTree = SOURCE_ROOT; }; + 266941981A6DC2AC0063BE93 /* MICmdCmdTrace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdTrace.cpp; path = "tools/lldb-mi/MICmdCmdTrace.cpp"; sourceTree = SOURCE_ROOT; }; + 266941991A6DC2AC0063BE93 /* MICmdCmdTrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdTrace.h; path = "tools/lldb-mi/MICmdCmdTrace.h"; sourceTree = SOURCE_ROOT; }; + 2669419A1A6DC2AC0063BE93 /* MICmdCmdVar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdVar.cpp; path = "tools/lldb-mi/MICmdCmdVar.cpp"; sourceTree = SOURCE_ROOT; }; + 2669419B1A6DC2AC0063BE93 /* MICmdCmdVar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdVar.h; path = "tools/lldb-mi/MICmdCmdVar.h"; sourceTree = SOURCE_ROOT; }; + 2669419C1A6DC2AC0063BE93 /* MICmdCommands.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCommands.cpp; path = "tools/lldb-mi/MICmdCommands.cpp"; sourceTree = SOURCE_ROOT; }; + 2669419D1A6DC2AC0063BE93 /* MICmdCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCommands.h; path = "tools/lldb-mi/MICmdCommands.h"; sourceTree = SOURCE_ROOT; }; + 2669419E1A6DC2AC0063BE93 /* MICmdData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdData.cpp; path = "tools/lldb-mi/MICmdData.cpp"; sourceTree = SOURCE_ROOT; }; + 2669419F1A6DC2AC0063BE93 /* MICmdData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdData.h; path = "tools/lldb-mi/MICmdData.h"; sourceTree = SOURCE_ROOT; }; + 266941A01A6DC2AC0063BE93 /* MICmdFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdFactory.cpp; path = "tools/lldb-mi/MICmdFactory.cpp"; sourceTree = SOURCE_ROOT; }; + 266941A11A6DC2AC0063BE93 /* MICmdFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdFactory.h; path = "tools/lldb-mi/MICmdFactory.h"; sourceTree = SOURCE_ROOT; }; + 266941A21A6DC2AC0063BE93 /* MICmdInterpreter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdInterpreter.cpp; path = "tools/lldb-mi/MICmdInterpreter.cpp"; sourceTree = SOURCE_ROOT; }; + 266941A31A6DC2AC0063BE93 /* MICmdInterpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdInterpreter.h; path = "tools/lldb-mi/MICmdInterpreter.h"; sourceTree = SOURCE_ROOT; }; + 266941A41A6DC2AC0063BE93 /* MICmdInvoker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdInvoker.cpp; path = "tools/lldb-mi/MICmdInvoker.cpp"; sourceTree = SOURCE_ROOT; }; + 266941A51A6DC2AC0063BE93 /* MICmdInvoker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdInvoker.h; path = "tools/lldb-mi/MICmdInvoker.h"; sourceTree = SOURCE_ROOT; }; + 266941A61A6DC2AC0063BE93 /* MICmdMgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdMgr.cpp; path = "tools/lldb-mi/MICmdMgr.cpp"; sourceTree = SOURCE_ROOT; }; + 266941A71A6DC2AC0063BE93 /* MICmdMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdMgr.h; path = "tools/lldb-mi/MICmdMgr.h"; sourceTree = SOURCE_ROOT; }; + 266941A81A6DC2AC0063BE93 /* MICmdMgrSetCmdDeleteCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdMgrSetCmdDeleteCallback.cpp; path = "tools/lldb-mi/MICmdMgrSetCmdDeleteCallback.cpp"; sourceTree = SOURCE_ROOT; }; + 266941A91A6DC2AC0063BE93 /* MICmdMgrSetCmdDeleteCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdMgrSetCmdDeleteCallback.h; path = "tools/lldb-mi/MICmdMgrSetCmdDeleteCallback.h"; sourceTree = SOURCE_ROOT; }; + 266941AA1A6DC2AC0063BE93 /* MICmnBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnBase.cpp; path = "tools/lldb-mi/MICmnBase.cpp"; sourceTree = SOURCE_ROOT; }; + 266941AB1A6DC2AC0063BE93 /* MICmnBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnBase.h; path = "tools/lldb-mi/MICmnBase.h"; sourceTree = SOURCE_ROOT; }; + 266941AC1A6DC2AC0063BE93 /* MICmnConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnConfig.h; path = "tools/lldb-mi/MICmnConfig.h"; sourceTree = SOURCE_ROOT; }; + 266941AD1A6DC2AC0063BE93 /* MICmnLLDBBroadcaster.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLLDBBroadcaster.cpp; path = "tools/lldb-mi/MICmnLLDBBroadcaster.cpp"; sourceTree = SOURCE_ROOT; }; + 266941AE1A6DC2AC0063BE93 /* MICmnLLDBBroadcaster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLLDBBroadcaster.h; path = "tools/lldb-mi/MICmnLLDBBroadcaster.h"; sourceTree = SOURCE_ROOT; }; + 266941AF1A6DC2AC0063BE93 /* MICmnLLDBDebugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLLDBDebugger.cpp; path = "tools/lldb-mi/MICmnLLDBDebugger.cpp"; sourceTree = SOURCE_ROOT; }; + 266941B01A6DC2AC0063BE93 /* MICmnLLDBDebugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLLDBDebugger.h; path = "tools/lldb-mi/MICmnLLDBDebugger.h"; sourceTree = SOURCE_ROOT; }; + 266941B11A6DC2AC0063BE93 /* MICmnLLDBDebuggerHandleEvents.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLLDBDebuggerHandleEvents.cpp; path = "tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp"; sourceTree = SOURCE_ROOT; }; + 266941B21A6DC2AC0063BE93 /* MICmnLLDBDebuggerHandleEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLLDBDebuggerHandleEvents.h; path = "tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h"; sourceTree = SOURCE_ROOT; }; + 266941B31A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLLDBDebugSessionInfo.cpp; path = "tools/lldb-mi/MICmnLLDBDebugSessionInfo.cpp"; sourceTree = SOURCE_ROOT; }; + 266941B41A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLLDBDebugSessionInfo.h; path = "tools/lldb-mi/MICmnLLDBDebugSessionInfo.h"; sourceTree = SOURCE_ROOT; }; + 266941B51A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfoVarObj.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLLDBDebugSessionInfoVarObj.cpp; path = "tools/lldb-mi/MICmnLLDBDebugSessionInfoVarObj.cpp"; sourceTree = SOURCE_ROOT; }; + 266941B61A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfoVarObj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLLDBDebugSessionInfoVarObj.h; path = "tools/lldb-mi/MICmnLLDBDebugSessionInfoVarObj.h"; sourceTree = SOURCE_ROOT; }; + 266941B71A6DC2AC0063BE93 /* MICmnLLDBProxySBValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLLDBProxySBValue.cpp; path = "tools/lldb-mi/MICmnLLDBProxySBValue.cpp"; sourceTree = SOURCE_ROOT; }; + 266941B81A6DC2AC0063BE93 /* MICmnLLDBProxySBValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLLDBProxySBValue.h; path = "tools/lldb-mi/MICmnLLDBProxySBValue.h"; sourceTree = SOURCE_ROOT; }; + 266941B91A6DC2AC0063BE93 /* MICmnLLDBUtilSBValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLLDBUtilSBValue.cpp; path = "tools/lldb-mi/MICmnLLDBUtilSBValue.cpp"; sourceTree = SOURCE_ROOT; }; + 266941BA1A6DC2AC0063BE93 /* MICmnLLDBUtilSBValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLLDBUtilSBValue.h; path = "tools/lldb-mi/MICmnLLDBUtilSBValue.h"; sourceTree = SOURCE_ROOT; }; + 266941BB1A6DC2AC0063BE93 /* MICmnLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLog.cpp; path = "tools/lldb-mi/MICmnLog.cpp"; sourceTree = SOURCE_ROOT; }; + 266941BC1A6DC2AC0063BE93 /* MICmnLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLog.h; path = "tools/lldb-mi/MICmnLog.h"; sourceTree = SOURCE_ROOT; }; + 266941BD1A6DC2AC0063BE93 /* MICmnLogMediumFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnLogMediumFile.cpp; path = "tools/lldb-mi/MICmnLogMediumFile.cpp"; sourceTree = SOURCE_ROOT; }; + 266941BE1A6DC2AC0063BE93 /* MICmnLogMediumFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnLogMediumFile.h; path = "tools/lldb-mi/MICmnLogMediumFile.h"; sourceTree = SOURCE_ROOT; }; + 266941BF1A6DC2AC0063BE93 /* MICmnMIOutOfBandRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnMIOutOfBandRecord.cpp; path = "tools/lldb-mi/MICmnMIOutOfBandRecord.cpp"; sourceTree = SOURCE_ROOT; }; + 266941C01A6DC2AC0063BE93 /* MICmnMIOutOfBandRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnMIOutOfBandRecord.h; path = "tools/lldb-mi/MICmnMIOutOfBandRecord.h"; sourceTree = SOURCE_ROOT; }; + 266941C11A6DC2AC0063BE93 /* MICmnMIResultRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnMIResultRecord.cpp; path = "tools/lldb-mi/MICmnMIResultRecord.cpp"; sourceTree = SOURCE_ROOT; }; + 266941C21A6DC2AC0063BE93 /* MICmnMIResultRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnMIResultRecord.h; path = "tools/lldb-mi/MICmnMIResultRecord.h"; sourceTree = SOURCE_ROOT; }; + 266941C31A6DC2AC0063BE93 /* MICmnMIValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnMIValue.cpp; path = "tools/lldb-mi/MICmnMIValue.cpp"; sourceTree = SOURCE_ROOT; }; + 266941C41A6DC2AC0063BE93 /* MICmnMIValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnMIValue.h; path = "tools/lldb-mi/MICmnMIValue.h"; sourceTree = SOURCE_ROOT; }; + 266941C51A6DC2AC0063BE93 /* MICmnMIValueConst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnMIValueConst.cpp; path = "tools/lldb-mi/MICmnMIValueConst.cpp"; sourceTree = SOURCE_ROOT; }; + 266941C61A6DC2AC0063BE93 /* MICmnMIValueConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnMIValueConst.h; path = "tools/lldb-mi/MICmnMIValueConst.h"; sourceTree = SOURCE_ROOT; }; + 266941C71A6DC2AC0063BE93 /* MICmnMIValueList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnMIValueList.cpp; path = "tools/lldb-mi/MICmnMIValueList.cpp"; sourceTree = SOURCE_ROOT; }; + 266941C81A6DC2AC0063BE93 /* MICmnMIValueList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnMIValueList.h; path = "tools/lldb-mi/MICmnMIValueList.h"; sourceTree = SOURCE_ROOT; }; + 266941C91A6DC2AC0063BE93 /* MICmnMIValueResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnMIValueResult.cpp; path = "tools/lldb-mi/MICmnMIValueResult.cpp"; sourceTree = SOURCE_ROOT; }; + 266941CA1A6DC2AC0063BE93 /* MICmnMIValueResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnMIValueResult.h; path = "tools/lldb-mi/MICmnMIValueResult.h"; sourceTree = SOURCE_ROOT; }; + 266941CB1A6DC2AC0063BE93 /* MICmnMIValueTuple.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnMIValueTuple.cpp; path = "tools/lldb-mi/MICmnMIValueTuple.cpp"; sourceTree = SOURCE_ROOT; }; + 266941CC1A6DC2AC0063BE93 /* MICmnMIValueTuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnMIValueTuple.h; path = "tools/lldb-mi/MICmnMIValueTuple.h"; sourceTree = SOURCE_ROOT; }; + 266941CD1A6DC2AC0063BE93 /* MICmnResources.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnResources.cpp; path = "tools/lldb-mi/MICmnResources.cpp"; sourceTree = SOURCE_ROOT; }; + 266941CE1A6DC2AC0063BE93 /* MICmnResources.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnResources.h; path = "tools/lldb-mi/MICmnResources.h"; sourceTree = SOURCE_ROOT; }; + 266941CF1A6DC2AC0063BE93 /* MICmnStreamStderr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnStreamStderr.cpp; path = "tools/lldb-mi/MICmnStreamStderr.cpp"; sourceTree = SOURCE_ROOT; }; + 266941D01A6DC2AC0063BE93 /* MICmnStreamStderr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnStreamStderr.h; path = "tools/lldb-mi/MICmnStreamStderr.h"; sourceTree = SOURCE_ROOT; }; + 266941D11A6DC2AC0063BE93 /* MICmnStreamStdin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnStreamStdin.cpp; path = "tools/lldb-mi/MICmnStreamStdin.cpp"; sourceTree = SOURCE_ROOT; }; + 266941D21A6DC2AC0063BE93 /* MICmnStreamStdin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnStreamStdin.h; path = "tools/lldb-mi/MICmnStreamStdin.h"; sourceTree = SOURCE_ROOT; }; + 266941D71A6DC2AC0063BE93 /* MICmnStreamStdout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnStreamStdout.cpp; path = "tools/lldb-mi/MICmnStreamStdout.cpp"; sourceTree = SOURCE_ROOT; }; + 266941D81A6DC2AC0063BE93 /* MICmnStreamStdout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnStreamStdout.h; path = "tools/lldb-mi/MICmnStreamStdout.h"; sourceTree = SOURCE_ROOT; }; + 266941D91A6DC2AC0063BE93 /* MICmnThreadMgrStd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmnThreadMgrStd.cpp; path = "tools/lldb-mi/MICmnThreadMgrStd.cpp"; sourceTree = SOURCE_ROOT; }; + 266941DA1A6DC2AC0063BE93 /* MICmnThreadMgrStd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmnThreadMgrStd.h; path = "tools/lldb-mi/MICmnThreadMgrStd.h"; sourceTree = SOURCE_ROOT; }; + 266941DB1A6DC2AC0063BE93 /* MIDataTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIDataTypes.h; path = "tools/lldb-mi/MIDataTypes.h"; sourceTree = SOURCE_ROOT; }; + 266941DC1A6DC2AC0063BE93 /* MIDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIDriver.cpp; path = "tools/lldb-mi/MIDriver.cpp"; sourceTree = SOURCE_ROOT; }; + 266941DD1A6DC2AC0063BE93 /* MIDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIDriver.h; path = "tools/lldb-mi/MIDriver.h"; sourceTree = SOURCE_ROOT; }; + 266941DE1A6DC2AC0063BE93 /* MIDriverBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIDriverBase.cpp; path = "tools/lldb-mi/MIDriverBase.cpp"; sourceTree = SOURCE_ROOT; }; + 266941DF1A6DC2AC0063BE93 /* MIDriverBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIDriverBase.h; path = "tools/lldb-mi/MIDriverBase.h"; sourceTree = SOURCE_ROOT; }; + 266941E01A6DC2AC0063BE93 /* MIDriverMain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIDriverMain.cpp; path = "tools/lldb-mi/MIDriverMain.cpp"; sourceTree = SOURCE_ROOT; }; + 266941E11A6DC2AC0063BE93 /* MIDriverMgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIDriverMgr.cpp; path = "tools/lldb-mi/MIDriverMgr.cpp"; sourceTree = SOURCE_ROOT; }; + 266941E21A6DC2AC0063BE93 /* MIDriverMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIDriverMgr.h; path = "tools/lldb-mi/MIDriverMgr.h"; sourceTree = SOURCE_ROOT; }; + 266941E31A6DC2AC0063BE93 /* MIReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = MIReadMe.txt; path = "tools/lldb-mi/MIReadMe.txt"; sourceTree = SOURCE_ROOT; }; + 266941E41A6DC2AC0063BE93 /* MIUtilDateTimeStd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIUtilDateTimeStd.cpp; path = "tools/lldb-mi/MIUtilDateTimeStd.cpp"; sourceTree = SOURCE_ROOT; }; + 266941E51A6DC2AC0063BE93 /* MIUtilDateTimeStd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilDateTimeStd.h; path = "tools/lldb-mi/MIUtilDateTimeStd.h"; sourceTree = SOURCE_ROOT; }; + 266941E61A6DC2AC0063BE93 /* MIUtilDebug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIUtilDebug.cpp; path = "tools/lldb-mi/MIUtilDebug.cpp"; sourceTree = SOURCE_ROOT; }; + 266941E71A6DC2AC0063BE93 /* MIUtilDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilDebug.h; path = "tools/lldb-mi/MIUtilDebug.h"; sourceTree = SOURCE_ROOT; }; + 266941E81A6DC2AC0063BE93 /* MIUtilFileStd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIUtilFileStd.cpp; path = "tools/lldb-mi/MIUtilFileStd.cpp"; sourceTree = SOURCE_ROOT; }; + 266941E91A6DC2AC0063BE93 /* MIUtilFileStd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilFileStd.h; path = "tools/lldb-mi/MIUtilFileStd.h"; sourceTree = SOURCE_ROOT; }; + 266941EA1A6DC2AC0063BE93 /* MIUtilMapIdToVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIUtilMapIdToVariant.cpp; path = "tools/lldb-mi/MIUtilMapIdToVariant.cpp"; sourceTree = SOURCE_ROOT; }; + 266941EB1A6DC2AC0063BE93 /* MIUtilMapIdToVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilMapIdToVariant.h; path = "tools/lldb-mi/MIUtilMapIdToVariant.h"; sourceTree = SOURCE_ROOT; }; + 266941EC1A6DC2AC0063BE93 /* MIUtilSingletonBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilSingletonBase.h; path = "tools/lldb-mi/MIUtilSingletonBase.h"; sourceTree = SOURCE_ROOT; }; + 266941ED1A6DC2AC0063BE93 /* MIUtilSingletonHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilSingletonHelper.h; path = "tools/lldb-mi/MIUtilSingletonHelper.h"; sourceTree = SOURCE_ROOT; }; + 266941EE1A6DC2AC0063BE93 /* MIUtilString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIUtilString.cpp; path = "tools/lldb-mi/MIUtilString.cpp"; sourceTree = SOURCE_ROOT; }; + 266941EF1A6DC2AC0063BE93 /* MIUtilString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilString.h; path = "tools/lldb-mi/MIUtilString.h"; sourceTree = SOURCE_ROOT; }; + 266941F81A6DC2AC0063BE93 /* MIUtilThreadBaseStd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIUtilThreadBaseStd.cpp; path = "tools/lldb-mi/MIUtilThreadBaseStd.cpp"; sourceTree = SOURCE_ROOT; }; + 266941F91A6DC2AC0063BE93 /* MIUtilThreadBaseStd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilThreadBaseStd.h; path = "tools/lldb-mi/MIUtilThreadBaseStd.h"; sourceTree = SOURCE_ROOT; }; + 266941FA1A6DC2AC0063BE93 /* MIUtilVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIUtilVariant.cpp; path = "tools/lldb-mi/MIUtilVariant.cpp"; sourceTree = SOURCE_ROOT; }; + 266941FB1A6DC2AC0063BE93 /* MIUtilVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilVariant.h; path = "tools/lldb-mi/MIUtilVariant.h"; sourceTree = SOURCE_ROOT; }; + 266941FC1A6DC2AC0063BE93 /* Platform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Platform.cpp; path = "tools/lldb-mi/Platform.cpp"; sourceTree = SOURCE_ROOT; }; + 266941FD1A6DC2AC0063BE93 /* Platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Platform.h; path = "tools/lldb-mi/Platform.h"; sourceTree = SOURCE_ROOT; }; + 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 = ""; }; + 266960631199F4230075C61A /* sed-sources */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "sed-sources"; sourceTree = ""; }; + 266DFE9613FD656E00D0C574 /* OperatingSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OperatingSystem.cpp; path = source/Target/OperatingSystem.cpp; sourceTree = ""; }; + 266DFE9813FD658300D0C574 /* OperatingSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OperatingSystem.h; path = include/lldb/Target/OperatingSystem.h; sourceTree = ""; }; + 266E82951B8CE346008FCA06 /* DWARFDIE.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWARFDIE.h; sourceTree = ""; }; + 266E82961B8CE3AC008FCA06 /* DWARFDIE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDIE.cpp; sourceTree = ""; }; + 266E829C1B8E542C008FCA06 /* DWARFAttribute.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFAttribute.cpp; sourceTree = ""; }; + 266F5CBB12FC846200DFCE33 /* Config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Config.h; path = include/lldb/Host/Config.h; sourceTree = ""; }; + 26709E311964A34000B94724 /* LaunchServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LaunchServices.framework; path = /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework; sourceTree = ""; }; + 2670F8111862B44A006B332C /* libncurses.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libncurses.dylib; path = /usr/lib/libncurses.dylib; sourceTree = ""; }; + 2671A0CD134825F6003A87BB /* ConnectionMachPort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConnectionMachPort.h; path = include/lldb/Core/ConnectionMachPort.h; sourceTree = ""; }; + 2671A0CF13482601003A87BB /* ConnectionMachPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ConnectionMachPort.cpp; path = source/Core/ConnectionMachPort.cpp; 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 = ""; }; + 26744EED1338317700EF765A /* GDBRemoteCommunicationClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GDBRemoteCommunicationClient.cpp; sourceTree = ""; }; + 26744EEE1338317700EF765A /* GDBRemoteCommunicationClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GDBRemoteCommunicationClient.h; sourceTree = ""; }; + 26744EEF1338317700EF765A /* GDBRemoteCommunicationServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GDBRemoteCommunicationServer.cpp; sourceTree = ""; }; + 26744EF01338317700EF765A /* GDBRemoteCommunicationServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GDBRemoteCommunicationServer.h; sourceTree = ""; }; + 2675F6FE1332BE690067997B /* PlatformRemoteiOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformRemoteiOS.cpp; sourceTree = ""; }; + 2675F6FF1332BE690067997B /* PlatformRemoteiOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformRemoteiOS.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 = ""; }; + 267A47F21B14115A0021A5BC /* SoftwareBreakpoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SoftwareBreakpoint.h; path = include/lldb/Host/common/SoftwareBreakpoint.h; sourceTree = ""; }; + 267A47F31B14116E0021A5BC /* NativeBreakpoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NativeBreakpoint.h; path = include/lldb/Host/common/NativeBreakpoint.h; sourceTree = ""; }; + 267A47F41B1411750021A5BC /* NativeBreakpointList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NativeBreakpointList.h; path = include/lldb/Host/common/NativeBreakpointList.h; sourceTree = ""; }; + 267A47F51B14117F0021A5BC /* NativeProcessProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NativeProcessProtocol.h; path = include/lldb/Host/common/NativeProcessProtocol.h; sourceTree = ""; }; + 267A47F61B14118F0021A5BC /* NativeRegisterContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContext.h; path = include/lldb/Host/common/NativeRegisterContext.h; sourceTree = ""; }; + 267A47F71B14119A0021A5BC /* NativeRegisterContextRegisterInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContextRegisterInfo.h; path = include/lldb/Host/common/NativeRegisterContextRegisterInfo.h; sourceTree = ""; }; + 267A47F81B1411A40021A5BC /* NativeThreadProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NativeThreadProtocol.h; path = include/lldb/Host/common/NativeThreadProtocol.h; sourceTree = ""; }; + 267A47F91B1411AC0021A5BC /* NativeWatchpointList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NativeWatchpointList.h; path = include/lldb/Host/common/NativeWatchpointList.h; sourceTree = ""; }; + 267A47FA1B1411C40021A5BC /* NativeRegisterContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContext.cpp; path = source/Host/common/NativeRegisterContext.cpp; sourceTree = ""; }; + 267A47FC1B1411CC0021A5BC /* NativeRegisterContextRegisterInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContextRegisterInfo.cpp; path = source/Host/common/NativeRegisterContextRegisterInfo.cpp; sourceTree = ""; }; + 267A47FE1B1411D90021A5BC /* NativeWatchpointList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NativeWatchpointList.cpp; path = source/Host/common/NativeWatchpointList.cpp; sourceTree = ""; }; + 267A48001B1411E40021A5BC /* XML.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XML.cpp; path = source/Host/common/XML.cpp; sourceTree = ""; }; + 267A48031B1416080021A5BC /* XML.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XML.h; path = include/lldb/Host/XML.h; sourceTree = ""; }; + 267C0128136880C7006E963E /* OptionGroupValueObjectDisplay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupValueObjectDisplay.h; path = include/lldb/Interpreter/OptionGroupValueObjectDisplay.h; sourceTree = ""; }; + 267C012A136880DF006E963E /* OptionGroupValueObjectDisplay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupValueObjectDisplay.cpp; path = source/Interpreter/OptionGroupValueObjectDisplay.cpp; sourceTree = ""; }; + 267DFB441B06752A00000FB7 /* MICmdArgValPrintValues.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdArgValPrintValues.cpp; path = "tools/lldb-mi/MICmdArgValPrintValues.cpp"; sourceTree = SOURCE_ROOT; }; + 267DFB451B06752A00000FB7 /* MICmdArgValPrintValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdArgValPrintValues.h; path = "tools/lldb-mi/MICmdArgValPrintValues.h"; sourceTree = SOURCE_ROOT; }; + 2682100C143A59AE004BCF2D /* MappedHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MappedHash.h; path = include/lldb/Core/MappedHash.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 = include/lldb/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 = ""; }; + 268648C116531BF800F04704 /* com.apple.debugserver.posix.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.posix.plist; path = tools/debugserver/source/com.apple.debugserver.posix.plist; sourceTree = ""; }; + 268648C216531BF800F04704 /* com.apple.debugserver.applist.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.applist.internal.plist; path = tools/debugserver/source/com.apple.debugserver.applist.internal.plist; sourceTree = ""; }; + 268648C316531BF800F04704 /* com.apple.debugserver.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.internal.plist; path = tools/debugserver/source/com.apple.debugserver.internal.plist; sourceTree = ""; }; + 2686536B1370ACB200D186A3 /* OptionGroupBoolean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupBoolean.cpp; path = source/Interpreter/OptionGroupBoolean.cpp; sourceTree = ""; }; + 2686536D1370ACC600D186A3 /* OptionGroupBoolean.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupBoolean.h; path = include/lldb/Interpreter/OptionGroupBoolean.h; sourceTree = ""; }; + 2686536E1370AE5A00D186A3 /* OptionGroupUInt64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupUInt64.h; path = include/lldb/Interpreter/OptionGroupUInt64.h; sourceTree = ""; }; + 2686536F1370AE7200D186A3 /* OptionGroupUInt64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupUInt64.cpp; path = source/Interpreter/OptionGroupUInt64.cpp; sourceTree = ""; }; + 26879CE51333F5750012C1F8 /* CommandObjectPlatform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectPlatform.h; path = source/Commands/CommandObjectPlatform.h; sourceTree = ""; }; + 26879CE71333F58B0012C1F8 /* CommandObjectPlatform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectPlatform.cpp; path = source/Commands/CommandObjectPlatform.cpp; 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 = ""; }; + 2689FFCA13353D7A00698AC0 /* liblldb-core.a */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "liblldb-core.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 268A683D1321B53B000E3FB8 /* DynamicLoaderStatic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicLoaderStatic.cpp; sourceTree = ""; }; + 268A683E1321B53B000E3FB8 /* DynamicLoaderStatic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicLoaderStatic.h; sourceTree = ""; }; + 268A813F115B19D000F645B0 /* UniqueCStringMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UniqueCStringMap.h; path = include/lldb/Core/UniqueCStringMap.h; sourceTree = ""; }; + 268DA871130095D000C9483A /* Terminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Terminal.h; path = include/lldb/Host/Terminal.h; sourceTree = ""; }; + 268DA873130095ED00C9483A /* Terminal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Terminal.cpp; sourceTree = ""; }; + 268ED0A2140FF52F00DE830F /* DataEncoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DataEncoder.h; path = include/lldb/Core/DataEncoder.h; sourceTree = ""; }; + 268ED0A4140FF54200DE830F /* DataEncoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataEncoder.cpp; path = source/Core/DataEncoder.cpp; sourceTree = ""; }; + 268F9D52123AA15200B91E9B /* SBSymbolContextList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBSymbolContextList.h; path = include/lldb/API/SBSymbolContextList.h; sourceTree = ""; }; + 268F9D54123AA16600B91E9B /* SBSymbolContextList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBSymbolContextList.cpp; path = source/API/SBSymbolContextList.cpp; sourceTree = ""; }; + 2690B36F1381D5B600ECFBAE /* Memory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Memory.h; path = include/lldb/Target/Memory.h; sourceTree = ""; }; + 2690B3701381D5C300ECFBAE /* Memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Memory.cpp; path = source/Target/Memory.cpp; sourceTree = ""; }; + 2690CD171A6DC0D000E717C8 /* lldb-mi */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-mi"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2692BA13136610C100F9E14D /* UnwindAssemblyInstEmulation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnwindAssemblyInstEmulation.cpp; sourceTree = ""; }; + 2692BA14136610C100F9E14D /* UnwindAssemblyInstEmulation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnwindAssemblyInstEmulation.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 = ""; }; + 2694E99A14FC0BB30076DE67 /* PlatformFreeBSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformFreeBSD.cpp; sourceTree = ""; }; + 2694E99B14FC0BB30076DE67 /* PlatformFreeBSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformFreeBSD.h; sourceTree = ""; }; + 2694E9A114FC0BBD0076DE67 /* PlatformLinux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformLinux.cpp; sourceTree = ""; }; + 2694E9A214FC0BBD0076DE67 /* PlatformLinux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformLinux.h; sourceTree = ""; }; + 26954EBC1401EE8B00294D09 /* DynamicRegisterInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DynamicRegisterInfo.cpp; path = Utility/DynamicRegisterInfo.cpp; sourceTree = ""; }; + 26954EBD1401EE8B00294D09 /* DynamicRegisterInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DynamicRegisterInfo.h; path = Utility/DynamicRegisterInfo.h; sourceTree = ""; }; + 26957D9213D381C900670048 /* RegisterContextDarwin_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextDarwin_arm.cpp; path = Utility/RegisterContextDarwin_arm.cpp; sourceTree = ""; }; + 26957D9313D381C900670048 /* RegisterContextDarwin_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextDarwin_arm.h; path = Utility/RegisterContextDarwin_arm.h; sourceTree = ""; }; + 26957D9413D381C900670048 /* RegisterContextDarwin_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextDarwin_i386.cpp; path = Utility/RegisterContextDarwin_i386.cpp; sourceTree = ""; }; + 26957D9513D381C900670048 /* RegisterContextDarwin_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextDarwin_i386.h; path = Utility/RegisterContextDarwin_i386.h; sourceTree = ""; }; + 26957D9613D381C900670048 /* RegisterContextDarwin_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextDarwin_x86_64.cpp; path = Utility/RegisterContextDarwin_x86_64.cpp; sourceTree = ""; }; + 26957D9713D381C900670048 /* RegisterContextDarwin_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextDarwin_x86_64.h; path = Utility/RegisterContextDarwin_x86_64.h; sourceTree = ""; }; + 2697A39215E404B1003E682C /* OptionValueArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueArch.cpp; path = source/Interpreter/OptionValueArch.cpp; sourceTree = ""; }; + 2697A39415E404BA003E682C /* OptionValueArch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueArch.h; path = include/lldb/Interpreter/OptionValueArch.h; sourceTree = ""; }; + 2697A54B133A6305004E4240 /* PlatformDarwin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformDarwin.cpp; sourceTree = ""; }; + 2697A54C133A6305004E4240 /* PlatformDarwin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformDarwin.h; sourceTree = ""; }; + 2698699815E6CBD0002415FF /* OperatingSystemPython.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OperatingSystemPython.cpp; sourceTree = ""; }; + 2698699915E6CBD0002415FF /* OperatingSystemPython.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OperatingSystemPython.h; sourceTree = ""; }; + 269DDD451B8FD01A00D0DBD8 /* DWARFASTParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFASTParser.h; sourceTree = ""; }; + 269DDD481B8FD1C300D0DBD8 /* DWARFASTParserClang.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFASTParserClang.cpp; sourceTree = ""; }; + 269DDD491B8FD1C300D0DBD8 /* DWARFASTParserClang.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFASTParserClang.h; sourceTree = ""; }; + 269FF07D12494F7D00225026 /* FuncUnwinders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FuncUnwinders.h; path = include/lldb/Symbol/FuncUnwinders.h; sourceTree = ""; }; + 269FF07F12494F8E00225026 /* UnwindPlan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnwindPlan.h; path = include/lldb/Symbol/UnwindPlan.h; sourceTree = ""; }; + 269FF08112494FC200225026 /* UnwindTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnwindTable.h; path = include/lldb/Symbol/UnwindTable.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 = ""; }; + 26A0DA4D140F721D006DA411 /* HashedNameToDIE.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HashedNameToDIE.h; 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; lineEnding = 0; path = LLDBWrapPython.cpp; sourceTree = BUILT_PRODUCTS_DIR; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 26A527BD14E24F5F00F3A14A /* ProcessMachCore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessMachCore.cpp; sourceTree = ""; }; + 26A527BE14E24F5F00F3A14A /* ProcessMachCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessMachCore.h; sourceTree = ""; }; + 26A527BF14E24F5F00F3A14A /* ThreadMachCore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadMachCore.cpp; sourceTree = ""; }; + 26A527C014E24F5F00F3A14A /* ThreadMachCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadMachCore.h; sourceTree = ""; }; + 26A7A034135E6E4200FB369E /* OptionValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValue.cpp; path = source/Interpreter/OptionValue.cpp; sourceTree = ""; }; + 26A7A036135E6E5300FB369E /* OptionValue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionValue.h; path = include/lldb/Interpreter/OptionValue.h; sourceTree = ""; }; + 26AB54111832DC3400EADFF3 /* RegisterCheckpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterCheckpoint.h; path = include/lldb/Target/RegisterCheckpoint.h; sourceTree = ""; }; + 26AB92101819D74600E63F3E /* DWARFDataExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDataExtractor.cpp; sourceTree = ""; }; + 26AB92111819D74600E63F3E /* DWARFDataExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDataExtractor.h; sourceTree = ""; }; + 26ACEC2715E077AE00E94760 /* Property.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Property.h; path = include/lldb/Interpreter/Property.h; sourceTree = ""; }; + 26B167A41123BF5500DC7B4F /* ThreadSafeValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadSafeValue.h; path = include/lldb/Core/ThreadSafeValue.h; sourceTree = ""; }; + 26B1EFAC154638AF00E2DAC7 /* DWARFDeclContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDeclContext.cpp; sourceTree = ""; }; + 26B1EFAD154638AF00E2DAC7 /* DWARFDeclContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDeclContext.h; sourceTree = ""; }; + 26B42C4C1187ABA50079C8C8 /* LLDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLDB.h; path = include/lldb/API/LLDB.h; sourceTree = ""; }; + 26B4E26E112F35F700AB3F64 /* TimeValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TimeValue.h; path = include/lldb/Host/TimeValue.h; sourceTree = ""; }; + 26B7564C14F89356008D9CB3 /* PlatformiOSSimulator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformiOSSimulator.cpp; sourceTree = ""; }; + 26B7564D14F89356008D9CB3 /* PlatformiOSSimulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformiOSSimulator.h; sourceTree = ""; }; + 26B75B421AD6E29A001F7A57 /* MipsLinuxSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MipsLinuxSignals.cpp; path = Utility/MipsLinuxSignals.cpp; sourceTree = ""; }; + 26B75B431AD6E29A001F7A57 /* MipsLinuxSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MipsLinuxSignals.h; path = Utility/MipsLinuxSignals.h; sourceTree = ""; }; + 26B8283C142D01E9002DBC64 /* SBSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBSection.h; path = include/lldb/API/SBSection.h; sourceTree = ""; }; + 26B8283F142D020F002DBC64 /* SBSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBSection.cpp; path = source/API/SBSection.cpp; sourceTree = ""; }; + 26B8B42212EEC52A00A831B2 /* UniqueDWARFASTType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UniqueDWARFASTType.h; sourceTree = ""; }; + 26B8B42312EEC52A00A831B2 /* UniqueDWARFASTType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UniqueDWARFASTType.cpp; sourceTree = ""; }; + 26BC179718C7F2B300D2196D /* JITLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JITLoader.cpp; path = source/Target/JITLoader.cpp; sourceTree = ""; }; + 26BC179818C7F2B300D2196D /* JITLoaderList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JITLoaderList.cpp; path = source/Target/JITLoaderList.cpp; sourceTree = ""; }; + 26BC179B18C7F2CB00D2196D /* JITLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JITLoader.h; path = include/lldb/Target/JITLoader.h; sourceTree = ""; }; + 26BC179C18C7F2CB00D2196D /* JITLoaderList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JITLoaderList.h; path = include/lldb/Target/JITLoaderList.h; sourceTree = ""; }; + 26BC17A218C7F4CB00D2196D /* ProcessElfCore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessElfCore.cpp; sourceTree = ""; }; + 26BC17A318C7F4CB00D2196D /* ProcessElfCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessElfCore.h; sourceTree = ""; }; + 26BC17A418C7F4CB00D2196D /* RegisterContextPOSIXCore_mips64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXCore_mips64.cpp; sourceTree = ""; }; + 26BC17A518C7F4CB00D2196D /* RegisterContextPOSIXCore_mips64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXCore_mips64.h; sourceTree = ""; }; + 26BC17A618C7F4CB00D2196D /* RegisterContextPOSIXCore_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXCore_x86_64.cpp; sourceTree = ""; }; + 26BC17A718C7F4CB00D2196D /* RegisterContextPOSIXCore_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXCore_x86_64.h; sourceTree = ""; }; + 26BC17A818C7F4CB00D2196D /* ThreadElfCore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadElfCore.cpp; sourceTree = ""; }; + 26BC17A918C7F4CB00D2196D /* ThreadElfCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadElfCore.h; sourceTree = ""; }; + 26BC17BA18C7F4FA00D2196D /* ProcessMessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessMessage.cpp; sourceTree = ""; }; + 26BC17BB18C7F4FA00D2196D /* ProcessMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessMessage.h; sourceTree = ""; }; + 26BC17BE18C7F4FA00D2196D /* ProcessPOSIXLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessPOSIXLog.cpp; sourceTree = ""; }; + 26BC17BF18C7F4FA00D2196D /* ProcessPOSIXLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessPOSIXLog.h; 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 /* Watchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Watchpoint.h; path = include/lldb/Breakpoint/Watchpoint.h; sourceTree = ""; }; + 26BC7D1410F1B76300F91463 /* CommandObjectBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectBreakpoint.h; path = source/Commands/CommandObjectBreakpoint.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 = ""; }; + 26BC7D1A10F1B76300F91463 /* CommandObjectHelp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectHelp.h; path = source/Commands/CommandObjectHelp.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 = ""; }; + 26BC7D2410F1B76300F91463 /* CommandObjectScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectScript.h; path = source/Interpreter/CommandObjectScript.h; sourceTree = ""; }; + 26BC7D2710F1B76300F91463 /* CommandObjectSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectSettings.h; path = source/Commands/CommandObjectSettings.h; sourceTree = ""; }; + 26BC7D2910F1B76300F91463 /* CommandObjectSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectSource.h; path = source/Commands/CommandObjectSource.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 = ""; }; + 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/Interpreter/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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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/Interpreter/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 = ""; }; + 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 /* ClangExpressionHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangExpressionHelper.h; path = ExpressionParser/Clang/ClangExpressionHelper.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 = ""; }; + 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 = ""; }; + 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 /* Watchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Watchpoint.cpp; path = source/Breakpoint/Watchpoint.cpp; sourceTree = ""; }; + 26BC7E2D10F1B84700F91463 /* CommandObjectBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectBreakpoint.cpp; path = source/Commands/CommandObjectBreakpoint.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 = ""; }; + 26BC7E3310F1B84700F91463 /* CommandObjectHelp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectHelp.cpp; path = source/Commands/CommandObjectHelp.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 = ""; }; + 26BC7E3D10F1B84700F91463 /* CommandObjectScript.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectScript.cpp; path = source/Interpreter/CommandObjectScript.cpp; sourceTree = ""; }; + 26BC7E4010F1B84700F91463 /* CommandObjectSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectSettings.cpp; path = source/Commands/CommandObjectSettings.cpp; sourceTree = ""; }; + 26BC7E4210F1B84700F91463 /* CommandObjectSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectSource.cpp; path = source/Commands/CommandObjectSource.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 = ""; }; + 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/Interpreter/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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 26BC7E7B10F1B85900F91463 /* FileSpecList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileSpecList.cpp; path = source/Core/FileSpecList.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/Interpreter/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 = ""; }; + 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 /* ClangUserExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangUserExpression.cpp; path = ExpressionParser/Clang/ClangUserExpression.cpp; sourceTree = ""; }; + 26BC7ED810F1B86700F91463 /* DWARFExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DWARFExpression.cpp; path = source/Expression/DWARFExpression.cpp; sourceTree = ""; }; + 26BC7EE810F1B88F00F91463 /* Host.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Host.mm; path = source/Host/macosx/Host.mm; 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 26BCFC4F1368ADF7006DC050 /* OptionGroupFormat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupFormat.h; path = include/lldb/Interpreter/OptionGroupFormat.h; sourceTree = ""; }; + 26BCFC511368AE38006DC050 /* OptionGroupFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupFormat.cpp; path = source/Interpreter/OptionGroupFormat.cpp; sourceTree = ""; }; + 26BCFC531368B3E4006DC050 /* OptionGroupOutputFile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupOutputFile.cpp; path = source/Interpreter/OptionGroupOutputFile.cpp; sourceTree = ""; }; + 26BCFC541368B4B8006DC050 /* OptionGroupOutputFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupOutputFile.h; path = include/lldb/Interpreter/OptionGroupOutputFile.h; sourceTree = ""; }; + 26BD407D135D2AC400237D80 /* FileLineResolver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FileLineResolver.h; path = include/lldb/Core/FileLineResolver.h; sourceTree = ""; }; + 26BD407E135D2ADF00237D80 /* FileLineResolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileLineResolver.cpp; path = source/Core/FileLineResolver.cpp; sourceTree = ""; }; + 26BF51EA1B3C754400016294 /* ABISysV_hexagon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ABISysV_hexagon.cpp; sourceTree = ""; }; + 26BF51EB1B3C754400016294 /* ABISysV_hexagon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ABISysV_hexagon.h; sourceTree = ""; }; + 26BF51EF1B3C754400016294 /* ABISysV_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ABISysV_i386.cpp; sourceTree = ""; }; + 26BF51F01B3C754400016294 /* ABISysV_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ABISysV_i386.h; sourceTree = ""; }; + 26C5577B132575AD008FD8FE /* PlatformMacOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformMacOSX.cpp; sourceTree = ""; }; + 26C5577C132575AD008FD8FE /* PlatformMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformMacOSX.h; sourceTree = ""; }; + 26C6886D137880B900407EDF /* RegisterValue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RegisterValue.h; path = include/lldb/Core/RegisterValue.h; sourceTree = ""; }; + 26C6886E137880C400407EDF /* RegisterValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterValue.cpp; path = source/Core/RegisterValue.cpp; sourceTree = ""; }; + 26C72C93124322890068DC16 /* SBStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBStream.h; path = include/lldb/API/SBStream.h; sourceTree = ""; }; + 26C72C951243229A0068DC16 /* SBStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBStream.cpp; path = source/API/SBStream.cpp; sourceTree = ""; }; + 26C7C4811BFFEA7E009BD01F /* WindowsMiniDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WindowsMiniDump.cpp; sourceTree = ""; }; + 26C7C4821BFFEA7E009BD01F /* WindowsMiniDump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowsMiniDump.h; 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 = ""; }; + 26CA979F172B1FD5005DC71B /* RegisterContextThreadMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextThreadMemory.cpp; path = Utility/RegisterContextThreadMemory.cpp; sourceTree = ""; }; + 26CA97A0172B1FD5005DC71B /* RegisterContextThreadMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextThreadMemory.h; path = Utility/RegisterContextThreadMemory.h; sourceTree = ""; }; + 26CEB5F018762056008F575A /* CommandObjectGUI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectGUI.cpp; path = source/Commands/CommandObjectGUI.cpp; sourceTree = ""; }; + 26CEB5F118762056008F575A /* CommandObjectGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectGUI.h; path = source/Commands/CommandObjectGUI.h; sourceTree = ""; }; + 26CF992414428766001E4138 /* AnsiTerminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnsiTerminal.h; path = include/lldb/Utility/AnsiTerminal.h; sourceTree = ""; }; + 26CFDCA01861638D000E63E5 /* Editline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Editline.h; path = include/lldb/Host/Editline.h; sourceTree = ""; }; + 26CFDCA2186163A4000E63E5 /* Editline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Editline.cpp; 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 = ""; }; + 26D1803C16CEBFD300EDFB5B /* KQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = KQueue.cpp; path = source/Utility/KQueue.cpp; sourceTree = ""; }; + 26D1804016CEDF0700EDFB5B /* TimeSpecTimeout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TimeSpecTimeout.cpp; path = source/Utility/TimeSpecTimeout.cpp; sourceTree = ""; }; + 26D1804416CEE12500EDFB5B /* KQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KQueue.h; path = source/Utility/KQueue.h; sourceTree = ""; }; + 26D1804616CEE12C00EDFB5B /* TimeSpecTimeout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TimeSpecTimeout.h; path = source/Utility/TimeSpecTimeout.h; sourceTree = ""; }; + 26D27C9D11ED3A4E0024D721 /* ELFHeader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ELFHeader.cpp; sourceTree = ""; }; + 26D27C9E11ED3A4E0024D721 /* ELFHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELFHeader.h; sourceTree = ""; }; + 26D52C1D1A980FE300E5D2FB /* MICmdCmdSymbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdSymbol.cpp; path = "tools/lldb-mi/MICmdCmdSymbol.cpp"; sourceTree = SOURCE_ROOT; }; + 26D52C1E1A980FE300E5D2FB /* MICmdCmdSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdSymbol.h; path = "tools/lldb-mi/MICmdCmdSymbol.h"; sourceTree = SOURCE_ROOT; }; + 26D55234159A7DB100708D8D /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = /usr/lib/libxml2.dylib; sourceTree = ""; }; + 26D5E15E135BAEA2006EA0A7 /* OptionGroupArchitecture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupArchitecture.cpp; path = source/Interpreter/OptionGroupArchitecture.cpp; sourceTree = ""; }; + 26D5E160135BAEB0006EA0A7 /* OptionGroupArchitecture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupArchitecture.h; path = include/lldb/Interpreter/OptionGroupArchitecture.h; sourceTree = ""; }; + 26D5E161135BB040006EA0A7 /* OptionGroupPlatform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupPlatform.h; path = include/lldb/Interpreter/OptionGroupPlatform.h; sourceTree = ""; }; + 26D5E162135BB054006EA0A7 /* OptionGroupPlatform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupPlatform.cpp; path = source/Interpreter/OptionGroupPlatform.cpp; sourceTree = ""; }; + 26D6F3F4183E7F9300194858 /* lldb-gdbserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lldb-gdbserver.cpp"; path = "tools/lldb-server/lldb-gdbserver.cpp"; sourceTree = ""; }; + 26D7E45B13D5E2F9007FD12B /* SocketAddress.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SocketAddress.h; path = include/lldb/Host/SocketAddress.h; sourceTree = ""; }; + 26D7E45C13D5E30A007FD12B /* SocketAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SocketAddress.cpp; path = source/Host/common/SocketAddress.cpp; sourceTree = ""; }; + 26D9FDC612F784E60003F2EE /* EmulateInstruction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EmulateInstruction.h; path = include/lldb/Core/EmulateInstruction.h; sourceTree = ""; }; + 26D9FDC812F784FD0003F2EE /* EmulateInstruction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = EmulateInstruction.cpp; path = source/Core/EmulateInstruction.cpp; sourceTree = ""; }; + 26DAED5F15D327A200E15819 /* OptionValuePathMappings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValuePathMappings.h; path = include/lldb/Interpreter/OptionValuePathMappings.h; sourceTree = ""; }; + 26DAED6215D327C200E15819 /* OptionValuePathMappings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValuePathMappings.cpp; path = source/Interpreter/OptionValuePathMappings.cpp; sourceTree = ""; }; + 26DAFD9711529BC7005A394E /* ExecutionContextScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExecutionContextScope.h; path = include/lldb/Target/ExecutionContextScope.h; sourceTree = ""; }; + 26DB3E071379E7AD0080DC73 /* ABIMacOSX_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ABIMacOSX_arm.cpp; sourceTree = ""; }; + 26DB3E081379E7AD0080DC73 /* ABIMacOSX_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ABIMacOSX_arm.h; sourceTree = ""; }; + 26DB3E0B1379E7AD0080DC73 /* ABIMacOSX_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ABIMacOSX_arm64.cpp; sourceTree = ""; }; + 26DB3E0C1379E7AD0080DC73 /* ABIMacOSX_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ABIMacOSX_arm64.h; sourceTree = ""; }; + 26DB3E0F1379E7AD0080DC73 /* ABIMacOSX_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ABIMacOSX_i386.cpp; sourceTree = ""; }; + 26DB3E101379E7AD0080DC73 /* ABIMacOSX_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ABIMacOSX_i386.h; sourceTree = ""; }; + 26DB3E131379E7AD0080DC73 /* ABISysV_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ABISysV_x86_64.cpp; sourceTree = ""; }; + 26DB3E141379E7AD0080DC73 /* ABISysV_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ABISysV_x86_64.h; sourceTree = ""; }; + 26DC6A101337FE6900FF7998 /* lldb-server */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-server"; sourceTree = BUILT_PRODUCTS_DIR; }; + 26DC6A1C1337FECA00FF7998 /* lldb-platform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lldb-platform.cpp"; path = "tools/lldb-server/lldb-platform.cpp"; sourceTree = ""; }; + 26DE1E6A11616C2E00A093E2 /* lldb-forward.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = "lldb-forward.h"; path = "include/lldb/lldb-forward.h"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 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 = ""; }; + 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 = ""; }; + 26DFBC58113B48F300DD817F /* CommandObjectMultiword.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectMultiword.cpp; path = source/Commands/CommandObjectMultiword.cpp; sourceTree = ""; }; + 26DFBC59113B48F300DD817F /* CommandObjectRegexCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectRegexCommand.cpp; path = source/Interpreter/CommandObjectRegexCommand.cpp; sourceTree = ""; }; + 26E152231419CACA007967D0 /* ObjectFilePECOFF.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectFilePECOFF.cpp; sourceTree = ""; }; + 26E152241419CACA007967D0 /* ObjectFilePECOFF.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjectFilePECOFF.h; sourceTree = ""; }; + 26E3EEBD11A9870400FBADB6 /* Unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Unwind.h; path = include/lldb/Target/Unwind.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 = ""; }; + 26E6902E129C6BD500DDECD9 /* ClangExternalASTSourceCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangExternalASTSourceCallbacks.h; path = include/lldb/Symbol/ClangExternalASTSourceCallbacks.h; sourceTree = ""; }; + 26E69030129C6BEF00DDECD9 /* ClangExternalASTSourceCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangExternalASTSourceCallbacks.cpp; path = source/Symbol/ClangExternalASTSourceCallbacks.cpp; sourceTree = ""; }; + 26ECA04213665FED008D1F18 /* ARM_DWARF_Registers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ARM_DWARF_Registers.cpp; path = source/Utility/ARM_DWARF_Registers.cpp; sourceTree = ""; }; + 26ED3D6C13C563810017D45E /* OptionGroupVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupVariable.cpp; path = source/Interpreter/OptionGroupVariable.cpp; sourceTree = ""; }; + 26ED3D6F13C5638A0017D45E /* OptionGroupVariable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupVariable.h; path = include/lldb/Interpreter/OptionGroupVariable.h; sourceTree = ""; }; + 26EFB6181BFE8D3E00544801 /* PlatformNetBSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformNetBSD.cpp; sourceTree = ""; }; + 26EFB6191BFE8D3E00544801 /* PlatformNetBSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformNetBSD.h; sourceTree = ""; }; + 26EFC4CA18CFAF0D00865D87 /* ObjectFileJIT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectFileJIT.cpp; sourceTree = ""; }; + 26EFC4CB18CFAF0D00865D87 /* ObjectFileJIT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectFileJIT.h; sourceTree = ""; }; + 26F006541B4DD86700B872E5 /* DynamicLoaderWindowsDYLD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicLoaderWindowsDYLD.cpp; sourceTree = ""; }; + 26F006551B4DD86700B872E5 /* DynamicLoaderWindowsDYLD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicLoaderWindowsDYLD.h; sourceTree = ""; }; + 26F2F8FD1B156678007857DE /* StructuredData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StructuredData.h; path = include/lldb/Core/StructuredData.h; sourceTree = ""; }; + 26F4A21A13FBA31A0064B613 /* ThreadMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadMemory.cpp; path = Utility/ThreadMemory.cpp; sourceTree = ""; }; + 26F4A21B13FBA31A0064B613 /* ThreadMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadMemory.h; path = Utility/ThreadMemory.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 = ""; }; + 26F5C32410F3DF23009D5894 /* libpython.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpython.dylib; path = /usr/lib/libpython.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 = ""; }; + 26F7305F139D8FC900FD51C7 /* History.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = History.h; path = include/lldb/Core/History.h; sourceTree = ""; }; + 26F73061139D8FDB00FD51C7 /* History.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = History.cpp; path = source/Core/History.cpp; 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_Stabs_Registers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARM_Stabs_Registers.h; path = source/Utility/ARM_Stabs_Registers.h; sourceTree = ""; }; + 26FA4315130103F400E71120 /* FileSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileSpec.h; path = include/lldb/Host/FileSpec.h; sourceTree = ""; }; + 26FA43171301048600E71120 /* FileSpec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileSpec.cpp; sourceTree = ""; }; + 26FFC19314FC072100087D58 /* AuxVector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AuxVector.cpp; sourceTree = ""; }; + 26FFC19414FC072100087D58 /* AuxVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuxVector.h; sourceTree = ""; }; + 26FFC19514FC072100087D58 /* DYLDRendezvous.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DYLDRendezvous.cpp; sourceTree = ""; }; + 26FFC19614FC072100087D58 /* DYLDRendezvous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DYLDRendezvous.h; sourceTree = ""; }; + 26FFC19714FC072100087D58 /* DynamicLoaderPOSIXDYLD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicLoaderPOSIXDYLD.cpp; sourceTree = ""; }; + 26FFC19814FC072100087D58 /* DynamicLoaderPOSIXDYLD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicLoaderPOSIXDYLD.h; sourceTree = ""; }; + 30DED5DC1B4ECB17004CC508 /* MainLoopPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MainLoopPosix.cpp; sourceTree = ""; }; + 33064C991A5C7A330033D415 /* UriParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UriParser.cpp; path = source/Utility/UriParser.cpp; sourceTree = ""; }; + 33064C9B1A5C7A490033D415 /* UriParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UriParser.h; path = source/Utility/UriParser.h; sourceTree = ""; }; + 3392EBB71AFF402200858B9F /* SBLanguageRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBLanguageRuntime.h; path = include/lldb/API/SBLanguageRuntime.h; sourceTree = ""; }; + 33E5E8411A672A240024ED68 /* StringConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringConvert.cpp; sourceTree = ""; }; + 33E5E8451A6736D30024ED68 /* StringConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringConvert.h; path = include/lldb/Host/StringConvert.h; sourceTree = SOURCE_ROOT; }; + 3F5E8AF31A40D4A500A73232 /* PipeBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PipeBase.h; path = include/lldb/Host/PipeBase.h; sourceTree = ""; }; + 3F8160A51AB9F7DD001DA9DF /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = source/Core/Logging.cpp; sourceTree = ""; }; + 3F8160A71AB9F809001DA9DF /* Logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = include/lldb/Core/Logging.h; sourceTree = ""; }; + 3F8169171ABA2419001DA9DF /* ConvertEnum.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ConvertEnum.cpp; path = source/Utility/ConvertEnum.cpp; sourceTree = ""; }; + 3F8169181ABA2419001DA9DF /* NameMatches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NameMatches.cpp; path = source/Utility/NameMatches.cpp; sourceTree = ""; }; + 3F81691B1ABA242B001DA9DF /* ConvertEnum.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ConvertEnum.h; path = include/lldb/Utility/ConvertEnum.h; sourceTree = ""; }; + 3F81691C1ABA242B001DA9DF /* NameMatches.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NameMatches.h; path = include/lldb/Utility/NameMatches.h; sourceTree = ""; }; + 3F81692A1ABB7A16001DA9DF /* SystemInitializerFull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SystemInitializerFull.cpp; path = source/API/SystemInitializerFull.cpp; sourceTree = ""; }; + 3F81692D1ABB7A40001DA9DF /* SystemInitializerFull.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SystemInitializerFull.h; path = include/lldb/API/SystemInitializerFull.h; sourceTree = ""; }; + 3F81692E1ABB7A6D001DA9DF /* SystemInitializer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SystemInitializer.cpp; path = source/Initialization/SystemInitializer.cpp; sourceTree = ""; }; + 3F81692F1ABB7A6D001DA9DF /* SystemInitializerCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SystemInitializerCommon.cpp; path = source/Initialization/SystemInitializerCommon.cpp; sourceTree = ""; }; + 3F8169301ABB7A6D001DA9DF /* SystemLifetimeManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SystemLifetimeManager.cpp; path = source/Initialization/SystemLifetimeManager.cpp; sourceTree = ""; }; + 3F8169341ABB7A80001DA9DF /* SystemInitializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SystemInitializer.h; path = include/lldb/Initialization/SystemInitializer.h; sourceTree = ""; }; + 3F8169351ABB7A80001DA9DF /* SystemInitializerCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SystemInitializerCommon.h; path = include/lldb/Initialization/SystemInitializerCommon.h; sourceTree = ""; }; + 3F8169361ABB7A80001DA9DF /* SystemLifetimeManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SystemLifetimeManager.h; path = include/lldb/Initialization/SystemLifetimeManager.h; sourceTree = ""; }; + 3FA093141BF65D3A0037DD08 /* PythonExceptionStateTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PythonExceptionStateTests.cpp; sourceTree = ""; }; + 3FBA69DD1B6067020008F44A /* ScriptInterpreterNone.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScriptInterpreterNone.cpp; path = ScriptInterpreter/None/ScriptInterpreterNone.cpp; sourceTree = ""; }; + 3FBA69DE1B6067020008F44A /* ScriptInterpreterNone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScriptInterpreterNone.h; path = ScriptInterpreter/None/ScriptInterpreterNone.h; sourceTree = ""; }; + 3FBA69E21B60672A0008F44A /* lldb-python.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-python.h"; path = "ScriptInterpreter/Python/lldb-python.h"; sourceTree = ""; }; + 3FBA69E31B60672A0008F44A /* PythonDataObjects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PythonDataObjects.cpp; path = ScriptInterpreter/Python/PythonDataObjects.cpp; sourceTree = ""; }; + 3FBA69E41B60672A0008F44A /* PythonDataObjects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PythonDataObjects.h; path = ScriptInterpreter/Python/PythonDataObjects.h; sourceTree = ""; }; + 3FBA69E51B60672A0008F44A /* ScriptInterpreterPython.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScriptInterpreterPython.cpp; path = ScriptInterpreter/Python/ScriptInterpreterPython.cpp; sourceTree = ""; }; + 3FBA69E61B60672A0008F44A /* ScriptInterpreterPython.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScriptInterpreterPython.h; path = ScriptInterpreter/Python/ScriptInterpreterPython.h; sourceTree = ""; }; + 3FDFD6C3199C396E009756A7 /* FileAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileAction.h; path = include/lldb/Target/FileAction.h; sourceTree = ""; }; + 3FDFDDBC199C3A06009756A7 /* FileAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileAction.cpp; path = source/Target/FileAction.cpp; sourceTree = ""; }; + 3FDFDDBE199D345E009756A7 /* FileCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileCache.cpp; path = source/Host/common/FileCache.cpp; sourceTree = ""; }; + 3FDFDDC0199D34E2009756A7 /* FileCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FileCache.h; path = include/lldb/Host/FileCache.h; sourceTree = ""; }; + 3FDFDDC1199D34E2009756A7 /* FileSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FileSystem.h; path = include/lldb/Host/FileSystem.h; sourceTree = ""; }; + 3FDFDDC5199D37ED009756A7 /* FileSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileSystem.cpp; sourceTree = ""; }; + 3FDFE52B19A2917A009756A7 /* HostInfoMacOSX.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = HostInfoMacOSX.mm; path = source/Host/macosx/HostInfoMacOSX.mm; sourceTree = ""; }; + 3FDFE52D19A291AF009756A7 /* HostInfoMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HostInfoMacOSX.h; path = include/lldb/Host/macosx/HostInfoMacOSX.h; sourceTree = ""; }; + 3FDFE53019A292F0009756A7 /* HostInfoPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HostInfoPosix.cpp; sourceTree = ""; }; + 3FDFE53219A29304009756A7 /* HostInfoPosix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HostInfoPosix.h; path = ../../../include/lldb/Host/posix/HostInfoPosix.h; sourceTree = ""; }; + 3FDFE53419A29327009756A7 /* HostInfoBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HostInfoBase.cpp; sourceTree = ""; }; + 3FDFE53619A2933E009756A7 /* HostInfoLinux.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HostInfoLinux.cpp; sourceTree = ""; }; + 3FDFE53719A2936B009756A7 /* HostInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostInfo.h; path = include/lldb/Host/HostInfo.h; sourceTree = ""; }; + 3FDFE53819A2936B009756A7 /* HostInfoBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostInfoBase.h; path = include/lldb/Host/HostInfoBase.h; sourceTree = ""; }; + 3FDFE53B19A293B3009756A7 /* HostInfoFreeBSD.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HostInfoFreeBSD.cpp; path = source/Host/freebsd/HostInfoFreeBSD.cpp; sourceTree = ""; }; + 3FDFE53C19A293CA009756A7 /* Config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Config.h; path = include/lldb/Host/freebsd/Config.h; sourceTree = ""; }; + 3FDFE53D19A293CA009756A7 /* HostInfoFreeBSD.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostInfoFreeBSD.h; path = include/lldb/Host/freebsd/HostInfoFreeBSD.h; sourceTree = ""; }; + 3FDFE53F19A29448009756A7 /* Condition.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Condition.cpp; path = source/Host/windows/Condition.cpp; sourceTree = ""; }; + 3FDFE54019A29448009756A7 /* EditLineWin.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = EditLineWin.cpp; path = source/Host/windows/EditLineWin.cpp; sourceTree = ""; }; + 3FDFE54119A29448009756A7 /* FileSystem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FileSystem.cpp; path = source/Host/windows/FileSystem.cpp; sourceTree = ""; }; + 3FDFE54219A29448009756A7 /* Host.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Host.cpp; path = source/Host/windows/Host.cpp; sourceTree = ""; }; + 3FDFE54319A29448009756A7 /* HostInfoWindows.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HostInfoWindows.cpp; path = source/Host/windows/HostInfoWindows.cpp; sourceTree = ""; }; + 3FDFE54419A29448009756A7 /* Mutex.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Mutex.cpp; path = source/Host/windows/Mutex.cpp; sourceTree = ""; }; + 3FDFE54519A29448009756A7 /* ProcessRunLock.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ProcessRunLock.cpp; path = source/Host/windows/ProcessRunLock.cpp; sourceTree = ""; }; + 3FDFE54619A29448009756A7 /* Windows.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Windows.cpp; path = source/Host/windows/Windows.cpp; sourceTree = ""; }; + 3FDFE54719A2946B009756A7 /* AutoHandle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AutoHandle.h; path = include/lldb/Host/windows/AutoHandle.h; sourceTree = ""; }; + 3FDFE54819A2946B009756A7 /* editlinewin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = editlinewin.h; path = include/lldb/Host/windows/editlinewin.h; sourceTree = ""; }; + 3FDFE54919A2946B009756A7 /* HostInfoWindows.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostInfoWindows.h; path = include/lldb/Host/windows/HostInfoWindows.h; sourceTree = ""; }; + 3FDFE54A19A2946B009756A7 /* win32.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = win32.h; path = include/lldb/Host/windows/win32.h; sourceTree = ""; }; + 3FDFE54B19A2946B009756A7 /* windows.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = windows.h; path = include/lldb/Host/windows/windows.h; sourceTree = ""; }; + 3FDFE55E19AF9B14009756A7 /* Host.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Host.cpp; path = source/Host/freebsd/Host.cpp; sourceTree = ""; }; + 3FDFE55F19AF9B14009756A7 /* HostThreadFreeBSD.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HostThreadFreeBSD.cpp; path = source/Host/freebsd/HostThreadFreeBSD.cpp; sourceTree = ""; }; + 3FDFE56019AF9B39009756A7 /* HostThreadFreeBSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HostThreadFreeBSD.h; path = include/lldb/Host/freebsd/HostThreadFreeBSD.h; sourceTree = ""; }; + 3FDFE56219AF9B60009756A7 /* HostThreadLinux.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HostThreadLinux.cpp; sourceTree = ""; }; + 3FDFE56319AF9B77009756A7 /* Config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Config.h; path = include/lldb/Host/linux/Config.h; sourceTree = SOURCE_ROOT; }; + 3FDFE56419AF9B77009756A7 /* HostInfoLinux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostInfoLinux.h; path = include/lldb/Host/linux/HostInfoLinux.h; sourceTree = SOURCE_ROOT; }; + 3FDFE56519AF9B77009756A7 /* HostThreadLinux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostThreadLinux.h; path = include/lldb/Host/linux/HostThreadLinux.h; sourceTree = SOURCE_ROOT; }; + 3FDFE56619AF9BB2009756A7 /* Config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Config.h; path = include/lldb/Host/macosx/Config.h; sourceTree = ""; }; + 3FDFE56719AF9BB2009756A7 /* HostThreadMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HostThreadMacOSX.h; path = include/lldb/Host/macosx/HostThreadMacOSX.h; sourceTree = ""; }; + 3FDFE56A19AF9C44009756A7 /* HostProcessPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HostProcessPosix.cpp; sourceTree = ""; }; + 3FDFE56B19AF9C44009756A7 /* HostThreadPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HostThreadPosix.cpp; sourceTree = ""; }; + 3FDFE56E19AF9C5A009756A7 /* HostProcessPosix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HostProcessPosix.h; path = ../../../include/lldb/Host/posix/HostProcessPosix.h; sourceTree = ""; }; + 3FDFE56F19AF9C5A009756A7 /* HostThreadPosix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HostThreadPosix.h; path = ../../../include/lldb/Host/posix/HostThreadPosix.h; sourceTree = ""; }; + 3FDFE57019AF9CA0009756A7 /* HostProcessWindows.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HostProcessWindows.cpp; path = source/Host/windows/HostProcessWindows.cpp; sourceTree = ""; }; + 3FDFE57119AF9CA0009756A7 /* HostThreadWindows.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HostThreadWindows.cpp; path = source/Host/windows/HostThreadWindows.cpp; sourceTree = ""; }; + 3FDFE57219AF9CD3009756A7 /* HostProcessWindows.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostProcessWindows.h; path = include/lldb/Host/windows/HostProcessWindows.h; sourceTree = ""; }; + 3FDFE57319AF9CD3009756A7 /* HostThreadWindows.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostThreadWindows.h; path = include/lldb/Host/windows/HostThreadWindows.h; sourceTree = ""; }; + 3FDFE57419AFABFD009756A7 /* HostProcess.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostProcess.h; path = include/lldb/Host/HostProcess.h; sourceTree = ""; }; + 3FDFE57519AFABFD009756A7 /* HostThread.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostThread.h; path = include/lldb/Host/HostThread.h; sourceTree = ""; }; + 3FDFED0519B7C898009756A7 /* HostThreadMacOSX.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = HostThreadMacOSX.mm; path = source/Host/macosx/HostThreadMacOSX.mm; sourceTree = ""; }; + 3FDFED0619B7C898009756A7 /* ThisThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThisThread.cpp; path = source/Host/macosx/ThisThread.cpp; sourceTree = ""; }; + 3FDFED0919B7C8C7009756A7 /* ThisThread.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThisThread.h; path = include/lldb/Host/ThisThread.h; sourceTree = ""; }; + 3FDFED0D19B7D269009756A7 /* ThisThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThisThread.cpp; path = source/Host/common/ThisThread.cpp; sourceTree = ""; }; + 3FDFED1E19BA6D55009756A7 /* Debug.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Debug.h; path = include/lldb/Host/Debug.h; sourceTree = ""; }; + 3FDFED1F19BA6D55009756A7 /* HostGetOpt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostGetOpt.h; path = include/lldb/Host/HostGetOpt.h; sourceTree = ""; }; + 3FDFED2019BA6D55009756A7 /* HostNativeThread.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostNativeThread.h; path = include/lldb/Host/HostNativeThread.h; sourceTree = ""; }; + 3FDFED2119BA6D55009756A7 /* HostNativeThreadBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostNativeThreadBase.h; path = include/lldb/Host/HostNativeThreadBase.h; sourceTree = ""; }; + 3FDFED2219BA6D55009756A7 /* ProcessRunLock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ProcessRunLock.h; path = include/lldb/Host/ProcessRunLock.h; sourceTree = ""; }; + 3FDFED2319BA6D55009756A7 /* ThreadLauncher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadLauncher.h; path = include/lldb/Host/ThreadLauncher.h; sourceTree = ""; }; + 3FDFED2419BA6D96009756A7 /* HostNativeThreadBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HostNativeThreadBase.cpp; sourceTree = ""; }; + 3FDFED2519BA6D96009756A7 /* HostThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HostThread.cpp; sourceTree = ""; }; + 3FDFED2619BA6D96009756A7 /* ThreadLauncher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadLauncher.cpp; sourceTree = ""; }; + 3FDFED2C19C257A0009756A7 /* HostProcess.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HostProcess.cpp; sourceTree = ""; }; + 449ACC96197DE9EC008D175E /* FastDemangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FastDemangle.cpp; path = source/Core/FastDemangle.cpp; sourceTree = ""; }; + 4906FD4012F2255300A2A77C /* ASTDumper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ASTDumper.cpp; path = ExpressionParser/Clang/ASTDumper.cpp; sourceTree = ""; }; + 4906FD4412F2257600A2A77C /* ASTDumper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTDumper.h; path = ExpressionParser/Clang/ASTDumper.h; sourceTree = ""; }; + 490A36BD180F0E6F00BA31F8 /* PlatformWindows.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformWindows.cpp; sourceTree = ""; }; + 490A36BE180F0E6F00BA31F8 /* PlatformWindows.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformWindows.h; sourceTree = ""; }; + 4911934B1226383D00578B7F /* ASTStructExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTStructExtractor.h; path = ExpressionParser/Clang/ASTStructExtractor.h; sourceTree = ""; }; + 491193501226386000578B7F /* ASTStructExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ASTStructExtractor.cpp; path = ExpressionParser/Clang/ASTStructExtractor.cpp; sourceTree = ""; }; + 49307AAD11DEA4D90081F992 /* IRForTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IRForTarget.cpp; path = ExpressionParser/Clang/IRForTarget.cpp; sourceTree = ""; }; + 49307AB111DEA4F20081F992 /* IRForTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IRForTarget.h; path = ExpressionParser/Clang/IRForTarget.h; sourceTree = ""; }; + 4939EA8B1BD56B3700084382 /* REPL.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = REPL.h; path = include/lldb/Expression/REPL.h; sourceTree = ""; }; + 4939EA8C1BD56B6D00084382 /* REPL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = REPL.cpp; path = source/Expression/REPL.cpp; sourceTree = ""; }; + 494260D7145790D5003C1C78 /* VerifyDecl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = VerifyDecl.h; path = include/lldb/Symbol/VerifyDecl.h; sourceTree = ""; }; + 494260D914579144003C1C78 /* VerifyDecl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VerifyDecl.cpp; path = source/Symbol/VerifyDecl.cpp; sourceTree = ""; }; + 49445C2512245E3600C11A81 /* ClangExpressionParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangExpressionParser.cpp; path = ExpressionParser/Clang/ClangExpressionParser.cpp; sourceTree = ""; }; + 49445C2912245E5500C11A81 /* ClangExpressionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangExpressionParser.h; path = ExpressionParser/Clang/ClangExpressionParser.h; sourceTree = ""; }; + 49445E341225AB6A00C11A81 /* ClangUserExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangUserExpression.h; path = ExpressionParser/Clang/ClangUserExpression.h; sourceTree = ""; }; + 4959511B1A1BC48100F6F8FC /* ClangModulesDeclVendor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangModulesDeclVendor.h; path = ExpressionParser/Clang/ClangModulesDeclVendor.h; sourceTree = ""; }; + 4959511E1A1BC4BC00F6F8FC /* ClangModulesDeclVendor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangModulesDeclVendor.cpp; path = ExpressionParser/Clang/ClangModulesDeclVendor.cpp; sourceTree = ""; }; + 495B38431489714C002708C5 /* ClangExternalASTSourceCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClangExternalASTSourceCommon.h; path = include/lldb/Symbol/ClangExternalASTSourceCommon.h; 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 = ""; }; + 4966DCC3148978A10028481B /* ClangExternalASTSourceCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangExternalASTSourceCommon.cpp; path = source/Symbol/ClangExternalASTSourceCommon.cpp; sourceTree = ""; }; + 49684D781BAB37E400E6D5D5 /* MIUtilParse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MIUtilParse.cpp; path = "tools/lldb-mi/MIUtilParse.cpp"; sourceTree = SOURCE_ROOT; }; + 49684D791BAB37E400E6D5D5 /* MIUtilParse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MIUtilParse.h; path = "tools/lldb-mi/MIUtilParse.h"; sourceTree = SOURCE_ROOT; }; + 496B01581406DE8900F830D5 /* IRInterpreter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IRInterpreter.cpp; path = source/Expression/IRInterpreter.cpp; sourceTree = ""; }; + 496B015A1406DEB100F830D5 /* IRInterpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IRInterpreter.h; path = include/lldb/Expression/IRInterpreter.h; sourceTree = ""; }; + 49724D971AD6ED390033C538 /* RenderScriptRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RenderScriptRuntime.cpp; path = RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp; sourceTree = ""; }; + 49724D981AD6ED390033C538 /* RenderScriptRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RenderScriptRuntime.h; path = RenderScript/RenderScriptRuntime/RenderScriptRuntime.h; sourceTree = ""; }; + 497C86BD122823D800B54702 /* ClangUtilityFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangUtilityFunction.cpp; path = ExpressionParser/Clang/ClangUtilityFunction.cpp; sourceTree = ""; }; + 497C86C1122823F300B54702 /* ClangUtilityFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangUtilityFunction.h; path = ExpressionParser/Clang/ClangUtilityFunction.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 = ""; }; + 4984BA0E1B978C3E008658D4 /* ClangExpressionVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangExpressionVariable.cpp; path = ExpressionParser/Clang/ClangExpressionVariable.cpp; sourceTree = ""; }; + 4984BA0F1B978C3E008658D4 /* ClangExpressionVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangExpressionVariable.h; path = ExpressionParser/Clang/ClangExpressionVariable.h; sourceTree = ""; }; + 4984BA151B979973008658D4 /* ExpressionVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ExpressionVariable.cpp; path = source/Expression/ExpressionVariable.cpp; sourceTree = ""; }; + 4984BA171B979C08008658D4 /* ExpressionVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExpressionVariable.h; path = include/lldb/Expression/ExpressionVariable.h; 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 = ""; }; + 49A1CAC11430E21D00306AC9 /* ExpressionSourceCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExpressionSourceCode.h; path = include/lldb/Expression/ExpressionSourceCode.h; sourceTree = ""; }; + 49A1CAC31430E8BD00306AC9 /* ExpressionSourceCode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ExpressionSourceCode.cpp; path = source/Expression/ExpressionSourceCode.cpp; sourceTree = ""; }; + 49A8A39F11D568A300AD3B68 /* ASTResultSynthesizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ASTResultSynthesizer.cpp; path = ExpressionParser/Clang/ASTResultSynthesizer.cpp; sourceTree = ""; }; + 49A8A3A311D568BF00AD3B68 /* ASTResultSynthesizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTResultSynthesizer.h; path = ExpressionParser/Clang/ASTResultSynthesizer.h; sourceTree = ""; }; + 49B01A2D15F67B1700666829 /* DeclVendor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DeclVendor.h; path = include/lldb/Symbol/DeclVendor.h; sourceTree = ""; }; + 49BB309511F79450001A4197 /* TaggedASTType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TaggedASTType.h; path = include/lldb/Symbol/TaggedASTType.h; sourceTree = ""; }; + 49C66B1C17011A43004D1922 /* IRMemoryMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = IRMemoryMap.h; path = include/lldb/Expression/IRMemoryMap.h; sourceTree = ""; }; + 49CF9829122C70BD007A0B96 /* IRDynamicChecks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IRDynamicChecks.cpp; path = source/Expression/IRDynamicChecks.cpp; sourceTree = ""; }; + 49CF9833122C718B007A0B96 /* IRDynamicChecks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IRDynamicChecks.h; path = include/lldb/Expression/IRDynamicChecks.h; sourceTree = ""; }; + 49D4FE821210B5FB00CDB854 /* ClangPersistentVariables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangPersistentVariables.h; path = ExpressionParser/Clang/ClangPersistentVariables.h; sourceTree = ""; }; + 49D4FE871210B61C00CDB854 /* ClangPersistentVariables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangPersistentVariables.cpp; path = ExpressionParser/Clang/ClangPersistentVariables.cpp; sourceTree = ""; }; + 49D7072611B5AD03001AD875 /* ClangASTSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangASTSource.h; path = ExpressionParser/Clang/ClangASTSource.h; sourceTree = ""; }; + 49D7072811B5AD11001AD875 /* ClangASTSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangASTSource.cpp; path = ExpressionParser/Clang/ClangASTSource.cpp; sourceTree = ""; }; + 49D8FB3513B558DE00411094 /* ClangASTImporter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangASTImporter.cpp; path = source/Symbol/ClangASTImporter.cpp; sourceTree = ""; }; + 49D8FB3713B5594900411094 /* ClangASTImporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangASTImporter.h; path = include/lldb/Symbol/ClangASTImporter.h; sourceTree = ""; }; + 49DA65021485C92A005FF180 /* AppleObjCDeclVendor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = AppleObjCDeclVendor.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 49DA65041485C942005FF180 /* AppleObjCDeclVendor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppleObjCDeclVendor.h; sourceTree = ""; }; + 49DCF6FD170E6B4A0092F75E /* IRMemoryMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IRMemoryMap.cpp; path = source/Expression/IRMemoryMap.cpp; sourceTree = ""; }; + 49DCF6FF170E6FD90092F75E /* Materializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Materializer.h; path = include/lldb/Expression/Materializer.h; sourceTree = ""; }; + 49DCF700170E70120092F75E /* Materializer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Materializer.cpp; path = source/Expression/Materializer.cpp; sourceTree = ""; }; + 49E45FA911F660DC008F7B28 /* CompilerType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompilerType.h; path = include/lldb/Symbol/CompilerType.h; sourceTree = ""; }; + 49E45FAD11F660FE008F7B28 /* CompilerType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CompilerType.cpp; path = source/Symbol/CompilerType.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 = ExpressionParser/Clang/ClangExpressionDeclMap.cpp; sourceTree = ""; }; + 49F1A74911B338AE003ED505 /* ClangExpressionDeclMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangExpressionDeclMap.h; path = ExpressionParser/Clang/ClangExpressionDeclMap.h; sourceTree = ""; }; + 4C00832C1B9A58A700D5CF24 /* Expression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Expression.h; path = include/lldb/Expression/Expression.h; sourceTree = ""; }; + 4C00832D1B9A58A700D5CF24 /* FunctionCaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FunctionCaller.h; path = include/lldb/Expression/FunctionCaller.h; sourceTree = ""; }; + 4C00832E1B9A58A700D5CF24 /* UserExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UserExpression.h; path = include/lldb/Expression/UserExpression.h; sourceTree = ""; }; + 4C0083321B9A5DE200D5CF24 /* FunctionCaller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FunctionCaller.cpp; path = source/Expression/FunctionCaller.cpp; sourceTree = ""; }; + 4C0083331B9A5DE200D5CF24 /* UserExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UserExpression.cpp; path = source/Expression/UserExpression.cpp; sourceTree = ""; }; + 4C00833D1B9F9B8400D5CF24 /* UtilityFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UtilityFunction.h; path = include/lldb/Expression/UtilityFunction.h; sourceTree = ""; }; + 4C00833F1B9F9BA900D5CF24 /* UtilityFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UtilityFunction.cpp; path = source/Expression/UtilityFunction.cpp; 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 = ""; }; + 4C08CDE711C81EF8001610A8 /* ThreadSpec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadSpec.cpp; path = source/Target/ThreadSpec.cpp; sourceTree = ""; }; + 4C08CDEB11C81F1E001610A8 /* ThreadSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadSpec.h; path = include/lldb/Target/ThreadSpec.h; 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/Commands/CommandCompletions.cpp; sourceTree = ""; }; + 4C2479BE1BA39843009C9A7B /* ExpressionParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ExpressionParser.h; path = include/lldb/Expression/ExpressionParser.h; sourceTree = ""; }; + 4C29E77D1BA2403F00DFF855 /* ExpressionTypeSystemHelper.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; name = ExpressionTypeSystemHelper.h; path = include/lldb/Expression/ExpressionTypeSystemHelper.h; sourceTree = ""; }; + 4C2FAE2E135E3A70001EDE44 /* SharedCluster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharedCluster.h; path = include/lldb/Utility/SharedCluster.h; 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 = ""; }; + 4C56543019D1EFAA002E9C44 /* ThreadPlanPython.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanPython.cpp; path = source/Target/ThreadPlanPython.cpp; sourceTree = ""; }; + 4C56543219D1EFB5002E9C44 /* ThreadPlanPython.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanPython.h; path = include/lldb/Target/ThreadPlanPython.h; sourceTree = ""; }; + 4C56543419D2297A002E9C44 /* SBThreadPlan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBThreadPlan.h; path = include/lldb/API/SBThreadPlan.h; sourceTree = ""; }; + 4C56543619D22B32002E9C44 /* SBThreadPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBThreadPlan.cpp; path = source/API/SBThreadPlan.cpp; sourceTree = ""; }; + 4C56543819D22FD9002E9C44 /* SBThreadPlan.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBThreadPlan.i; sourceTree = ""; }; + 4C5DBBC611E3FEC60035160F /* CommandObjectCommands.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectCommands.cpp; path = source/Commands/CommandObjectCommands.cpp; sourceTree = ""; }; + 4C5DBBC711E3FEC60035160F /* CommandObjectCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectCommands.h; path = source/Commands/CommandObjectCommands.h; sourceTree = ""; }; + 4C626533130F1B0A00C889F6 /* StreamTee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamTee.h; path = include/lldb/Core/StreamTee.h; sourceTree = ""; }; + 4C66499F14EEE7F100B0316F /* StreamCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamCallback.h; path = include/lldb/Core/StreamCallback.h; sourceTree = ""; }; + 4C6649A214EEE81000B0316F /* StreamCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamCallback.cpp; path = source/Core/StreamCallback.cpp; sourceTree = ""; }; + 4C73152119B7D71700F865A4 /* Iterable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Iterable.h; path = include/lldb/Utility/Iterable.h; sourceTree = ""; }; + 4C7CF7E31295E10E00B4FBB5 /* ThreadPlanCallUserExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanCallUserExpression.h; path = include/lldb/Target/ThreadPlanCallUserExpression.h; sourceTree = ""; }; + 4C7CF7E51295E12B00B4FBB5 /* ThreadPlanCallUserExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanCallUserExpression.cpp; path = source/Target/ThreadPlanCallUserExpression.cpp; sourceTree = ""; }; + 4C88BC291BA3722B00AA0964 /* Expression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Expression.cpp; path = source/Expression/Expression.cpp; sourceTree = ""; }; + 4C98D3DA118FB96F00E575D0 /* ClangFunctionCaller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangFunctionCaller.cpp; path = ExpressionParser/Clang/ClangFunctionCaller.cpp; sourceTree = ""; }; + 4C98D3DB118FB96F00E575D0 /* IRExecutionUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IRExecutionUnit.cpp; path = source/Expression/IRExecutionUnit.cpp; sourceTree = ""; }; + 4C98D3E0118FB98F00E575D0 /* ClangFunctionCaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangFunctionCaller.h; path = ExpressionParser/Clang/ClangFunctionCaller.h; sourceTree = ""; }; + 4C98D3E1118FB98F00E575D0 /* IRExecutionUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IRExecutionUnit.h; path = include/lldb/Expression/IRExecutionUnit.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 = ""; }; + 4CAA56121422D96A001FFA01 /* BreakpointResolverFileRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointResolverFileRegex.h; path = include/lldb/Breakpoint/BreakpointResolverFileRegex.h; sourceTree = ""; }; + 4CAA56141422D986001FFA01 /* BreakpointResolverFileRegex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointResolverFileRegex.cpp; path = source/Breakpoint/BreakpointResolverFileRegex.cpp; sourceTree = ""; }; + 4CAB257C18EC9DB800BAD33E /* SafeMachO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SafeMachO.h; path = include/lldb/Utility/SafeMachO.h; sourceTree = ""; }; + 4CABA9DC134A8BA700539BDD /* ValueObjectMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueObjectMemory.h; path = include/lldb/Core/ValueObjectMemory.h; sourceTree = ""; }; + 4CABA9DF134A8BCD00539BDD /* ValueObjectMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectMemory.cpp; path = source/Core/ValueObjectMemory.cpp; 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 = ""; }; + 4CB4430912491DDA00C13DC2 /* LanguageRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LanguageRuntime.h; path = include/lldb/Target/LanguageRuntime.h; sourceTree = ""; }; + 4CB4430A12491DDA00C13DC2 /* LanguageRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LanguageRuntime.cpp; path = source/Target/LanguageRuntime.cpp; sourceTree = ""; }; + 4CB443BB1249920C00C13DC2 /* CPPLanguageRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPPLanguageRuntime.h; path = include/lldb/Target/CPPLanguageRuntime.h; sourceTree = ""; }; + 4CB443BC1249920C00C13DC2 /* CPPLanguageRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPPLanguageRuntime.cpp; path = source/Target/CPPLanguageRuntime.cpp; sourceTree = ""; }; + 4CB443F212499B5000C13DC2 /* ObjCLanguageRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ObjCLanguageRuntime.cpp; path = source/Target/ObjCLanguageRuntime.cpp; sourceTree = ""; }; + 4CB443F612499B6E00C13DC2 /* ObjCLanguageRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ObjCLanguageRuntime.h; path = include/lldb/Target/ObjCLanguageRuntime.h; sourceTree = ""; }; + 4CC2A148128C73ED001531C4 /* ThreadPlanTracer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanTracer.cpp; path = source/Target/ThreadPlanTracer.cpp; sourceTree = ""; }; + 4CC2A14C128C7409001531C4 /* ThreadPlanTracer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanTracer.h; path = include/lldb/Target/ThreadPlanTracer.h; sourceTree = ""; }; + 4CCA643D13B40B82003BDF98 /* ItaniumABILanguageRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ItaniumABILanguageRuntime.cpp; sourceTree = ""; }; + 4CCA643E13B40B82003BDF98 /* ItaniumABILanguageRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ItaniumABILanguageRuntime.h; sourceTree = ""; }; + 4CCA644213B40B82003BDF98 /* AppleObjCRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleObjCRuntime.cpp; sourceTree = ""; }; + 4CCA644313B40B82003BDF98 /* AppleObjCRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleObjCRuntime.h; sourceTree = ""; }; + 4CCA644413B40B82003BDF98 /* AppleObjCRuntimeV1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = AppleObjCRuntimeV1.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 4CCA644513B40B82003BDF98 /* AppleObjCRuntimeV1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AppleObjCRuntimeV1.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4CCA644613B40B82003BDF98 /* AppleObjCRuntimeV2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = AppleObjCRuntimeV2.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 4CCA644713B40B82003BDF98 /* AppleObjCRuntimeV2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AppleObjCRuntimeV2.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4CCA644813B40B82003BDF98 /* AppleObjCTrampolineHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleObjCTrampolineHandler.cpp; sourceTree = ""; }; + 4CCA644913B40B82003BDF98 /* AppleObjCTrampolineHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleObjCTrampolineHandler.h; sourceTree = ""; }; + 4CCA644A13B40B82003BDF98 /* AppleThreadPlanStepThroughObjCTrampoline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleThreadPlanStepThroughObjCTrampoline.cpp; sourceTree = ""; }; + 4CCA644B13B40B82003BDF98 /* AppleThreadPlanStepThroughObjCTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleThreadPlanStepThroughObjCTrampoline.h; sourceTree = ""; }; + 4CD0BD0C134BFAB600CB44D4 /* ValueObjectDynamicValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueObjectDynamicValue.h; path = include/lldb/Core/ValueObjectDynamicValue.h; sourceTree = ""; }; + 4CD0BD0E134BFADF00CB44D4 /* ValueObjectDynamicValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectDynamicValue.cpp; path = source/Core/ValueObjectDynamicValue.cpp; sourceTree = ""; }; + 4CE4F672162C971A00F75CB3 /* SBExpressionOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBExpressionOptions.h; path = include/lldb/API/SBExpressionOptions.h; sourceTree = ""; }; + 4CE4F674162C973F00F75CB3 /* SBExpressionOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBExpressionOptions.cpp; path = source/API/SBExpressionOptions.cpp; sourceTree = ""; }; + 4CE4F676162CE1E100F75CB3 /* SBExpressionOptions.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBExpressionOptions.i; sourceTree = ""; }; + 4CEDAED311754F5E00E875A6 /* ThreadPlanStepUntil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepUntil.h; path = include/lldb/Target/ThreadPlanStepUntil.h; sourceTree = ""; }; + 4CF52AF41428291E0051E832 /* SBFileSpecList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBFileSpecList.h; path = include/lldb/API/SBFileSpecList.h; sourceTree = ""; }; + 4CF52AF7142829390051E832 /* SBFileSpecList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBFileSpecList.cpp; path = source/API/SBFileSpecList.cpp; sourceTree = ""; }; + 69A01E1B1236C5D400C660B5 /* Condition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Condition.cpp; sourceTree = ""; }; + 69A01E1C1236C5D400C660B5 /* Host.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Host.cpp; sourceTree = ""; }; + 69A01E1E1236C5D400C660B5 /* Mutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Mutex.cpp; sourceTree = ""; }; + 69A01E1F1236C5D400C660B5 /* Symbols.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Symbols.cpp; sourceTree = ""; }; + 69A01E201236C5D400C660B5 /* TimeValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeValue.cpp; sourceTree = ""; }; + 6D55B28D1A8A806200A70529 /* GDBRemoteCommunicationServerCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GDBRemoteCommunicationServerCommon.cpp; sourceTree = ""; }; + 6D55B28E1A8A806200A70529 /* GDBRemoteCommunicationServerLLGS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GDBRemoteCommunicationServerLLGS.cpp; sourceTree = ""; }; + 6D55B28F1A8A806200A70529 /* GDBRemoteCommunicationServerPlatform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GDBRemoteCommunicationServerPlatform.cpp; sourceTree = ""; }; + 6D55B2931A8A808400A70529 /* GDBRemoteCommunicationServerCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GDBRemoteCommunicationServerCommon.h; sourceTree = ""; }; + 6D55B2941A8A808400A70529 /* GDBRemoteCommunicationServerLLGS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GDBRemoteCommunicationServerLLGS.h; sourceTree = ""; }; + 6D55B2951A8A808400A70529 /* GDBRemoteCommunicationServerPlatform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GDBRemoteCommunicationServerPlatform.h; sourceTree = ""; }; + 6D55BAE01A8CD03D00A70529 /* HostInfoAndroid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HostInfoAndroid.cpp; path = source/Host/android/HostInfoAndroid.cpp; sourceTree = ""; }; + 6D55BAE11A8CD03D00A70529 /* ProcessLauncherAndroid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ProcessLauncherAndroid.cpp; path = source/Host/android/ProcessLauncherAndroid.cpp; sourceTree = ""; }; + 6D55BAE21A8CD06000A70529 /* Android.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Android.h; path = include/lldb/Host/android/Android.h; sourceTree = ""; }; + 6D55BAE31A8CD06000A70529 /* Config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Config.h; path = include/lldb/Host/android/Config.h; sourceTree = ""; }; + 6D55BAE41A8CD06000A70529 /* HostInfoAndroid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HostInfoAndroid.h; path = include/lldb/Host/android/HostInfoAndroid.h; sourceTree = ""; }; + 6D55BAE51A8CD06000A70529 /* ProcessLauncherAndroid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ProcessLauncherAndroid.h; path = include/lldb/Host/android/ProcessLauncherAndroid.h; sourceTree = ""; }; + 6D55BAE91A8CD08C00A70529 /* PlatformAndroid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformAndroid.cpp; sourceTree = ""; }; + 6D55BAEA1A8CD08C00A70529 /* PlatformAndroid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformAndroid.h; sourceTree = ""; }; + 6D55BAEB1A8CD08C00A70529 /* PlatformAndroidRemoteGDBServer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformAndroidRemoteGDBServer.cpp; sourceTree = ""; }; + 6D55BAEC1A8CD08C00A70529 /* PlatformAndroidRemoteGDBServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformAndroidRemoteGDBServer.h; sourceTree = ""; }; + 6D762BEC1B1605CD006C929D /* LLDBServerUtilities.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LLDBServerUtilities.cpp; path = "tools/lldb-server/LLDBServerUtilities.cpp"; sourceTree = ""; }; + 6D762BED1B1605CD006C929D /* LLDBServerUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LLDBServerUtilities.h; path = "tools/lldb-server/LLDBServerUtilities.h"; sourceTree = ""; }; + 6D86CE9E1B440F6B00A7FBFA /* CommandObjectBugreport.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectBugreport.cpp; path = source/Commands/CommandObjectBugreport.cpp; sourceTree = ""; }; + 6D86CE9F1B440F6B00A7FBFA /* CommandObjectBugreport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CommandObjectBugreport.h; path = source/Commands/CommandObjectBugreport.h; sourceTree = ""; }; + 6D95DBFD1B9DC057000E318A /* DIERef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DIERef.cpp; sourceTree = ""; }; + 6D95DBFE1B9DC057000E318A /* HashedNameToDIE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HashedNameToDIE.cpp; sourceTree = ""; }; + 6D95DBFF1B9DC057000E318A /* SymbolFileDWARFDwo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolFileDWARFDwo.cpp; sourceTree = ""; }; + 6D95DC031B9DC06F000E318A /* DIERef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DIERef.h; sourceTree = ""; }; + 6D95DC041B9DC06F000E318A /* SymbolFileDWARFDwo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolFileDWARFDwo.h; sourceTree = ""; }; + 6D99A3611BBC2F1600979793 /* ArmUnwindInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ArmUnwindInfo.h; path = include/lldb/Symbol/ArmUnwindInfo.h; sourceTree = ""; }; + 6D99A3621BBC2F3200979793 /* ArmUnwindInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ArmUnwindInfo.cpp; path = source/Symbol/ArmUnwindInfo.cpp; sourceTree = ""; }; + 6D9AB3DC1BB2B74E003F2289 /* TypeMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeMap.cpp; path = source/Symbol/TypeMap.cpp; sourceTree = ""; }; + 6D9AB3DE1BB2B76B003F2289 /* TypeMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TypeMap.h; path = include/lldb/Symbol/TypeMap.h; sourceTree = ""; }; + 6DEC6F381BD66D750091ABA6 /* TaskPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TaskPool.cpp; path = source/Utility/TaskPool.cpp; sourceTree = ""; }; + 6DEC6F3A1BD66D950091ABA6 /* TaskPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TaskPool.h; path = include/lldb/Utility/TaskPool.h; sourceTree = ""; }; + 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MemoryHistory.cpp; path = source/Target/MemoryHistory.cpp; sourceTree = ""; }; + 8C2D6A54197A1EBE006989C9 /* MemoryHistory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MemoryHistory.h; path = include/lldb/Target/MemoryHistory.h; sourceTree = ""; }; + 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryHistoryASan.cpp; sourceTree = ""; }; + 8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryHistoryASan.h; sourceTree = ""; }; + 8CCB017A19BA283D0009FD44 /* ThreadCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadCollection.cpp; path = source/Target/ThreadCollection.cpp; sourceTree = ""; }; + 8CCB017C19BA289B0009FD44 /* ThreadCollection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadCollection.h; path = include/lldb/Target/ThreadCollection.h; sourceTree = ""; }; + 8CCB017F19BA4DD00009FD44 /* SBThreadCollection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SBThreadCollection.cpp; path = source/API/SBThreadCollection.cpp; sourceTree = ""; }; + 8CCB018119BA4E210009FD44 /* SBThreadCollection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBThreadCollection.h; path = include/lldb/API/SBThreadCollection.h; sourceTree = ""; }; + 8CCB018419BA54930009FD44 /* SBThreadCollection.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBThreadCollection.i; sourceTree = ""; }; + 8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = InstrumentationRuntime.cpp; path = source/Target/InstrumentationRuntime.cpp; sourceTree = ""; }; + 8CF02AE019DCBF3B00B14BE0 /* InstrumentationRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = InstrumentationRuntime.h; path = include/lldb/Target/InstrumentationRuntime.h; sourceTree = ""; }; + 8CF02AE519DCBF8400B14BE0 /* AddressSanitizerRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddressSanitizerRuntime.cpp; sourceTree = ""; }; + 8CF02AE619DCBF8400B14BE0 /* AddressSanitizerRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddressSanitizerRuntime.h; sourceTree = ""; }; + 8CF02AED19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = InstrumentationRuntimeStopInfo.cpp; path = source/Target/InstrumentationRuntimeStopInfo.cpp; sourceTree = ""; }; + 8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = InstrumentationRuntimeStopInfo.h; path = include/lldb/Target/InstrumentationRuntimeStopInfo.h; sourceTree = ""; }; + 94005E0313F438DF001EF42D /* python-wrapper.swig */ = {isa = PBXFileReference; lastKnownFileType = text; path = "python-wrapper.swig"; sourceTree = ""; }; + 94005E0513F45A1B001EF42D /* embedded_interpreter.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = embedded_interpreter.py; path = source/Interpreter/embedded_interpreter.py; sourceTree = ""; }; + 94031A9F13CF5B3D00DCFF3C /* PriorityPointerPair.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PriorityPointerPair.h; path = include/lldb/Utility/PriorityPointerPair.h; sourceTree = ""; }; + 940495781BEC497E00926025 /* NSError.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NSError.cpp; path = Language/ObjC/NSError.cpp; sourceTree = ""; }; + 940495791BEC497E00926025 /* NSException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NSException.cpp; path = Language/ObjC/NSException.cpp; sourceTree = ""; }; + 94094C68163B6CCC0083A547 /* ValueObjectCast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectCast.h; path = include/lldb/Core/ValueObjectCast.h; sourceTree = ""; }; + 94094C69163B6CD90083A547 /* ValueObjectCast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectCast.cpp; path = source/Core/ValueObjectCast.cpp; sourceTree = ""; }; + 940B02F419DC96CB00AD0F52 /* SBExecutionContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBExecutionContext.h; path = include/lldb/API/SBExecutionContext.h; sourceTree = ""; }; + 940B02F519DC96E700AD0F52 /* SBExecutionContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBExecutionContext.cpp; path = source/API/SBExecutionContext.cpp; sourceTree = ""; }; + 940B02F719DC970900AD0F52 /* SBExecutionContext.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBExecutionContext.i; sourceTree = ""; }; + 940B04D81A8984FF0045D5F7 /* argdumper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = argdumper.cpp; path = tools/argdumper/argdumper.cpp; sourceTree = ""; }; + 94145430175D7FDE00284436 /* lldb-versioning.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "lldb-versioning.h"; path = "include/lldb/lldb-versioning.h"; sourceTree = ""; }; + 9418EBCB1AA9108B0058B02E /* VectorType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = VectorType.h; path = include/lldb/DataFormatters/VectorType.h; sourceTree = ""; }; + 9418EBCC1AA910910058B02E /* VectorType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VectorType.cpp; path = source/DataFormatters/VectorType.cpp; sourceTree = ""; }; + 94235B9A1A8D5FD800EB2EED /* SBVariablesOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBVariablesOptions.h; path = include/lldb/API/SBVariablesOptions.h; sourceTree = ""; }; + 94235B9B1A8D5FF300EB2EED /* SBVariablesOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBVariablesOptions.cpp; path = source/API/SBVariablesOptions.cpp; sourceTree = ""; }; + 94235B9D1A8D601A00EB2EED /* SBVariablesOptions.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBVariablesOptions.i; sourceTree = ""; }; + 942612F51B94FFE900EF842E /* LanguageCategory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LanguageCategory.h; path = include/lldb/DataFormatters/LanguageCategory.h; sourceTree = ""; }; + 942612F61B95000000EF842E /* LanguageCategory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LanguageCategory.cpp; path = source/DataFormatters/LanguageCategory.cpp; sourceTree = ""; }; + 942829541A89614000521B30 /* JSON.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JSON.h; path = include/lldb/Utility/JSON.h; sourceTree = ""; }; + 942829551A89614C00521B30 /* JSON.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSON.cpp; path = source/Utility/JSON.cpp; sourceTree = ""; }; + 942829C01A89835300521B30 /* lldb-argdumper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-argdumper"; sourceTree = BUILT_PRODUCTS_DIR; }; + 94380B8019940B0300BFE4A8 /* StringLexer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StringLexer.h; path = include/lldb/Utility/StringLexer.h; sourceTree = ""; }; + 94380B8119940B0A00BFE4A8 /* StringLexer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringLexer.cpp; path = source/Utility/StringLexer.cpp; sourceTree = ""; }; + 943B90FC1B991586007BA499 /* VectorIterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = VectorIterator.h; path = include/lldb/DataFormatters/VectorIterator.h; sourceTree = ""; }; + 943BDEFC1AA7B2DE00789CE8 /* LLDBAssert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LLDBAssert.h; path = include/lldb/Utility/LLDBAssert.h; sourceTree = ""; }; + 943BDEFD1AA7B2F800789CE8 /* LLDBAssert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LLDBAssert.cpp; path = source/Utility/LLDBAssert.cpp; sourceTree = ""; }; + 944372DA171F6B4300E57C32 /* RegisterContextDummy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextDummy.cpp; path = Utility/RegisterContextDummy.cpp; sourceTree = ""; }; + 944372DB171F6B4300E57C32 /* RegisterContextDummy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextDummy.h; path = Utility/RegisterContextDummy.h; sourceTree = ""; }; + 9443B120140C18A90013457C /* SBData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBData.h; path = include/lldb/API/SBData.h; sourceTree = ""; }; + 9443B121140C18C10013457C /* SBData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBData.cpp; path = source/API/SBData.cpp; sourceTree = ""; }; + 9447DE411BD5962900E67212 /* DumpValueObjectOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DumpValueObjectOptions.h; path = include/lldb/DataFormatters/DumpValueObjectOptions.h; sourceTree = ""; }; + 9447DE421BD5963300E67212 /* DumpValueObjectOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DumpValueObjectOptions.cpp; path = source/DataFormatters/DumpValueObjectOptions.cpp; sourceTree = ""; }; + 9449B8031B30E0690019342B /* ThreadSafeDenseSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadSafeDenseSet.h; path = include/lldb/Core/ThreadSafeDenseSet.h; sourceTree = ""; }; + 944DC3481774C99000D7D884 /* python-swigsafecast.swig */ = {isa = PBXFileReference; lastKnownFileType = text; path = "python-swigsafecast.swig"; sourceTree = ""; }; + 945215DD17F639E600521C0B /* ValueObjectPrinter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectPrinter.h; path = include/lldb/DataFormatters/ValueObjectPrinter.h; sourceTree = ""; }; + 945215DE17F639EE00521C0B /* ValueObjectPrinter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectPrinter.cpp; path = source/DataFormatters/ValueObjectPrinter.cpp; sourceTree = ""; }; + 9452573616262CD000325455 /* SBDeclaration.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBDeclaration.i; sourceTree = ""; }; + 9452573816262CEF00325455 /* SBDeclaration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBDeclaration.h; path = include/lldb/API/SBDeclaration.h; sourceTree = ""; }; + 9452573916262D0200325455 /* SBDeclaration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBDeclaration.cpp; path = source/API/SBDeclaration.cpp; sourceTree = ""; }; + 945261B31B9A11E800BF138D /* CxxStringTypes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CxxStringTypes.cpp; path = Language/CPlusPlus/CxxStringTypes.cpp; sourceTree = ""; }; + 945261B41B9A11E800BF138D /* CxxStringTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CxxStringTypes.h; path = Language/CPlusPlus/CxxStringTypes.h; sourceTree = ""; }; + 945261B51B9A11E800BF138D /* LibCxx.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxx.cpp; path = Language/CPlusPlus/LibCxx.cpp; sourceTree = ""; }; + 945261B61B9A11E800BF138D /* LibCxx.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LibCxx.h; path = Language/CPlusPlus/LibCxx.h; sourceTree = ""; }; + 945261B71B9A11E800BF138D /* LibCxxInitializerList.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxInitializerList.cpp; path = Language/CPlusPlus/LibCxxInitializerList.cpp; sourceTree = ""; }; + 945261B81B9A11E800BF138D /* LibCxxList.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxList.cpp; path = Language/CPlusPlus/LibCxxList.cpp; sourceTree = ""; }; + 945261B91B9A11E800BF138D /* LibCxxMap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxMap.cpp; path = Language/CPlusPlus/LibCxxMap.cpp; sourceTree = ""; }; + 945261BA1B9A11E800BF138D /* LibCxxUnorderedMap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxUnorderedMap.cpp; path = Language/CPlusPlus/LibCxxUnorderedMap.cpp; sourceTree = ""; }; + 945261BB1B9A11E800BF138D /* LibCxxVector.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxVector.cpp; path = Language/CPlusPlus/LibCxxVector.cpp; sourceTree = ""; }; + 945261BC1B9A11E800BF138D /* LibStdcpp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibStdcpp.cpp; path = Language/CPlusPlus/LibStdcpp.cpp; sourceTree = ""; }; + 945261BD1B9A11E800BF138D /* LibStdcpp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LibStdcpp.h; path = Language/CPlusPlus/LibStdcpp.h; sourceTree = ""; }; + 945261C71B9A14D300BF138D /* CXXFunctionPointer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CXXFunctionPointer.cpp; path = source/DataFormatters/CXXFunctionPointer.cpp; sourceTree = ""; }; + 945261C91B9A14E000BF138D /* CXXFunctionPointer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CXXFunctionPointer.h; path = include/lldb/DataFormatters/CXXFunctionPointer.h; sourceTree = ""; }; + 9455630A1BEAD0570073F75F /* PlatformAppleSimulator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformAppleSimulator.cpp; sourceTree = ""; }; + 9455630B1BEAD0570073F75F /* PlatformAppleSimulator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformAppleSimulator.h; sourceTree = ""; }; + 9455630C1BEAD0570073F75F /* PlatformiOSSimulatorCoreSimulatorSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformiOSSimulatorCoreSimulatorSupport.h; sourceTree = ""; }; + 9455630D1BEAD0570073F75F /* PlatformiOSSimulatorCoreSimulatorSupport.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformiOSSimulatorCoreSimulatorSupport.mm; sourceTree = ""; }; + 945759651534941F005A9070 /* PlatformPOSIX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PlatformPOSIX.cpp; path = POSIX/PlatformPOSIX.cpp; sourceTree = ""; }; + 945759661534941F005A9070 /* PlatformPOSIX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlatformPOSIX.h; path = POSIX/PlatformPOSIX.h; sourceTree = ""; }; + 945E8D7D152F6AA80019BCCD /* StreamGDBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamGDBRemote.h; path = include/lldb/Core/StreamGDBRemote.h; sourceTree = ""; }; + 945E8D7F152F6AB40019BCCD /* StreamGDBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamGDBRemote.cpp; path = source/Core/StreamGDBRemote.cpp; sourceTree = ""; }; + 9461568614E355F2003A195C /* SBTypeFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBTypeFilter.h; path = include/lldb/API/SBTypeFilter.h; sourceTree = ""; }; + 9461568714E355F2003A195C /* SBTypeFormat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBTypeFormat.h; path = include/lldb/API/SBTypeFormat.h; sourceTree = ""; }; + 9461568814E355F2003A195C /* SBTypeSummary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBTypeSummary.h; path = include/lldb/API/SBTypeSummary.h; sourceTree = ""; }; + 9461568914E355F2003A195C /* SBTypeSynthetic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBTypeSynthetic.h; path = include/lldb/API/SBTypeSynthetic.h; sourceTree = ""; }; + 9461568A14E35621003A195C /* SBTypeFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBTypeFilter.cpp; path = source/API/SBTypeFilter.cpp; sourceTree = ""; }; + 9461568B14E35621003A195C /* SBTypeFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBTypeFormat.cpp; path = source/API/SBTypeFormat.cpp; sourceTree = ""; }; + 9461568C14E35621003A195C /* SBTypeSummary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBTypeSummary.cpp; path = source/API/SBTypeSummary.cpp; sourceTree = ""; }; + 9461568D14E35621003A195C /* SBTypeSynthetic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBTypeSynthetic.cpp; path = source/API/SBTypeSynthetic.cpp; sourceTree = ""; }; + 9461569214E3567F003A195C /* SBTypeFilter.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBTypeFilter.i; sourceTree = ""; }; + 9461569314E3567F003A195C /* SBTypeFormat.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBTypeFormat.i; sourceTree = ""; }; + 9461569414E3567F003A195C /* SBTypeSummary.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBTypeSummary.i; sourceTree = ""; }; + 9461569514E3567F003A195C /* SBTypeSynthetic.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBTypeSynthetic.i; sourceTree = ""; }; + 946216BF1A97C055006E19CC /* OptionValueLanguage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValueLanguage.h; path = include/lldb/Interpreter/OptionValueLanguage.h; sourceTree = ""; }; + 946216C11A97C080006E19CC /* OptionValueLanguage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionValueLanguage.cpp; path = source/Interpreter/OptionValueLanguage.cpp; sourceTree = ""; }; + 9463D4CC13B1798800C230D4 /* CommandObjectType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = CommandObjectType.cpp; path = source/Commands/CommandObjectType.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 9463D4CE13B179A500C230D4 /* CommandObjectType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CommandObjectType.h; path = source/Commands/CommandObjectType.h; sourceTree = ""; }; + 9475C18514E5E9C5001BFC6D /* SBTypeCategory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBTypeCategory.h; path = include/lldb/API/SBTypeCategory.h; sourceTree = ""; }; + 9475C18714E5E9FA001BFC6D /* SBTypeCategory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBTypeCategory.cpp; path = source/API/SBTypeCategory.cpp; sourceTree = ""; }; + 9475C18A14E5EA1C001BFC6D /* SBTypeCategory.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBTypeCategory.i; sourceTree = ""; }; + 9475C18B14E5F818001BFC6D /* SBTypeNameSpecifier.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBTypeNameSpecifier.i; sourceTree = ""; }; + 9475C18C14E5F826001BFC6D /* SBTypeNameSpecifier.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBTypeNameSpecifier.h; path = include/lldb/API/SBTypeNameSpecifier.h; sourceTree = ""; }; + 9475C18D14E5F834001BFC6D /* SBTypeNameSpecifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBTypeNameSpecifier.cpp; path = source/API/SBTypeNameSpecifier.cpp; sourceTree = ""; }; + 947A1D621616476A0017C8D1 /* CommandObjectPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectPlugin.cpp; path = source/Commands/CommandObjectPlugin.cpp; sourceTree = ""; }; + 947A1D631616476A0017C8D1 /* CommandObjectPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectPlugin.h; path = source/Commands/CommandObjectPlugin.h; sourceTree = ""; }; + 9481FE6B1B5F2D9200DED357 /* Either.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Either.h; path = include/lldb/Utility/Either.h; sourceTree = ""; }; + 949ADF001406F62E004833E1 /* ValueObjectConstResultImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectConstResultImpl.h; path = include/lldb/Core/ValueObjectConstResultImpl.h; sourceTree = ""; }; + 949ADF021406F648004833E1 /* ValueObjectConstResultImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectConstResultImpl.cpp; path = source/Core/ValueObjectConstResultImpl.cpp; sourceTree = ""; }; + 949EED9E1BA74B64008C63CF /* CoreMedia.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CoreMedia.cpp; path = Language/ObjC/CoreMedia.cpp; sourceTree = ""; }; + 949EED9F1BA74B64008C63CF /* CoreMedia.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CoreMedia.h; path = Language/ObjC/CoreMedia.h; sourceTree = ""; }; + 949EEDA11BA76571008C63CF /* Cocoa.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Cocoa.cpp; path = Language/ObjC/Cocoa.cpp; sourceTree = ""; }; + 949EEDA21BA76571008C63CF /* Cocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Cocoa.h; path = Language/ObjC/Cocoa.h; sourceTree = ""; }; + 949EEDA41BA765B5008C63CF /* NSArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NSArray.cpp; path = Language/ObjC/NSArray.cpp; sourceTree = ""; }; + 949EEDA51BA765B5008C63CF /* NSDictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NSDictionary.cpp; path = Language/ObjC/NSDictionary.cpp; sourceTree = ""; }; + 949EEDA61BA765B5008C63CF /* NSIndexPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NSIndexPath.cpp; path = Language/ObjC/NSIndexPath.cpp; sourceTree = ""; }; + 949EEDA71BA765B5008C63CF /* NSSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NSSet.cpp; path = Language/ObjC/NSSet.cpp; sourceTree = ""; }; + 949EEDAC1BA76719008C63CF /* CF.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CF.cpp; path = Language/ObjC/CF.cpp; sourceTree = ""; }; + 949EEDAD1BA76719008C63CF /* CF.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CF.h; path = Language/ObjC/CF.h; sourceTree = ""; }; + 94A5B3951AB9FE8300A5EE7F /* EmulateInstructionMIPS64.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = EmulateInstructionMIPS64.cpp; path = MIPS64/EmulateInstructionMIPS64.cpp; sourceTree = ""; }; + 94A5B3961AB9FE8300A5EE7F /* EmulateInstructionMIPS64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EmulateInstructionMIPS64.h; path = MIPS64/EmulateInstructionMIPS64.h; sourceTree = ""; }; + 94B638511B8F8E53004FE1E4 /* Language.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Language.h; path = include/lldb/Target/Language.h; sourceTree = ""; }; + 94B638521B8F8E6C004FE1E4 /* Language.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Language.cpp; path = source/Target/Language.cpp; sourceTree = ""; }; + 94B6385B1B8FB174004FE1E4 /* CPlusPlusLanguage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CPlusPlusLanguage.cpp; path = Language/CPlusPlus/CPlusPlusLanguage.cpp; sourceTree = ""; }; + 94B6385C1B8FB174004FE1E4 /* CPlusPlusLanguage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CPlusPlusLanguage.h; path = Language/CPlusPlus/CPlusPlusLanguage.h; sourceTree = ""; }; + 94B6385E1B8FB7A2004FE1E4 /* ObjCLanguage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ObjCLanguage.cpp; path = Language/ObjC/ObjCLanguage.cpp; sourceTree = ""; }; + 94B6385F1B8FB7A2004FE1E4 /* ObjCLanguage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ObjCLanguage.h; path = Language/ObjC/ObjCLanguage.h; sourceTree = ""; }; + 94B638611B8FB7E9004FE1E4 /* ObjCPlusPlusLanguage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ObjCPlusPlusLanguage.h; path = Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h; sourceTree = ""; }; + 94B638621B8FB7F1004FE1E4 /* ObjCPlusPlusLanguage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ObjCPlusPlusLanguage.cpp; path = Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp; sourceTree = ""; }; + 94B6E76013D8833C005F417F /* ValueObjectSyntheticFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectSyntheticFilter.h; path = include/lldb/Core/ValueObjectSyntheticFilter.h; sourceTree = ""; }; + 94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectSyntheticFilter.cpp; path = source/Core/ValueObjectSyntheticFilter.cpp; sourceTree = ""; }; + 94B9E50E1BBEFDFE000A48DC /* NSDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NSDictionary.h; path = Language/ObjC/NSDictionary.h; sourceTree = ""; }; + 94B9E50F1BBF0069000A48DC /* NSSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NSSet.h; path = Language/ObjC/NSSet.h; sourceTree = ""; }; + 94B9E5101BBF20B7000A48DC /* NSString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NSString.h; path = Language/ObjC/NSString.h; sourceTree = ""; }; + 94B9E5111BBF20F4000A48DC /* NSString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NSString.cpp; path = Language/ObjC/NSString.cpp; sourceTree = ""; }; + 94BA8B6C176F8C9B005A91B5 /* Range.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Range.cpp; path = source/Utility/Range.cpp; sourceTree = ""; }; + 94BA8B6E176F8CA0005A91B5 /* Range.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Range.h; path = include/lldb/Utility/Range.h; sourceTree = ""; }; + 94BA8B6F176F97CE005A91B5 /* CommandHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandHistory.cpp; path = source/Interpreter/CommandHistory.cpp; sourceTree = ""; }; + 94BA8B71176F97D4005A91B5 /* CommandHistory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CommandHistory.h; path = include/lldb/Interpreter/CommandHistory.h; sourceTree = ""; }; + 94CB255816B069770059775D /* DataVisualization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataVisualization.cpp; path = source/DataFormatters/DataVisualization.cpp; sourceTree = ""; }; + 94CB255916B069770059775D /* FormatClasses.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FormatClasses.cpp; path = source/DataFormatters/FormatClasses.cpp; sourceTree = ""; }; + 94CB255A16B069770059775D /* FormatManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FormatManager.cpp; path = source/DataFormatters/FormatManager.cpp; sourceTree = ""; }; + 94CB256016B069800059775D /* DataVisualization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DataVisualization.h; path = include/lldb/DataFormatters/DataVisualization.h; sourceTree = ""; }; + 94CB256116B069800059775D /* FormatClasses.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FormatClasses.h; path = include/lldb/DataFormatters/FormatClasses.h; sourceTree = ""; }; + 94CB256216B069800059775D /* FormatManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FormatManager.h; path = include/lldb/DataFormatters/FormatManager.h; sourceTree = ""; }; + 94CB256416B096F10059775D /* TypeCategory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeCategory.cpp; path = source/DataFormatters/TypeCategory.cpp; sourceTree = ""; }; + 94CB256516B096F10059775D /* TypeCategoryMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeCategoryMap.cpp; path = source/DataFormatters/TypeCategoryMap.cpp; sourceTree = ""; }; + 94CB256816B096F90059775D /* TypeCategory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TypeCategory.h; path = include/lldb/DataFormatters/TypeCategory.h; sourceTree = ""; }; + 94CB256916B096FA0059775D /* TypeCategoryMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TypeCategoryMap.h; path = include/lldb/DataFormatters/TypeCategoryMap.h; sourceTree = ""; }; + 94CB256A16B0A4030059775D /* TypeFormat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TypeFormat.h; path = include/lldb/DataFormatters/TypeFormat.h; sourceTree = ""; }; + 94CB256B16B0A4030059775D /* TypeSummary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TypeSummary.h; path = include/lldb/DataFormatters/TypeSummary.h; sourceTree = ""; }; + 94CB256C16B0A4040059775D /* TypeSynthetic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TypeSynthetic.h; path = include/lldb/DataFormatters/TypeSynthetic.h; sourceTree = ""; }; + 94CB256D16B0A4260059775D /* TypeFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeFormat.cpp; path = source/DataFormatters/TypeFormat.cpp; sourceTree = ""; }; + 94CB256E16B0A4260059775D /* TypeSummary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeSummary.cpp; path = source/DataFormatters/TypeSummary.cpp; sourceTree = ""; }; + 94CB256F16B0A4270059775D /* TypeSynthetic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeSynthetic.cpp; path = source/DataFormatters/TypeSynthetic.cpp; sourceTree = ""; }; + 94CB257316B1D3870059775D /* FormatCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FormatCache.cpp; path = source/DataFormatters/FormatCache.cpp; sourceTree = ""; }; + 94CB257516B1D3910059775D /* FormatCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FormatCache.h; path = include/lldb/DataFormatters/FormatCache.h; sourceTree = ""; }; + 94CD131819BA33A100DB7BED /* TypeValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TypeValidator.h; path = include/lldb/DataFormatters/TypeValidator.h; sourceTree = ""; }; + 94CD131919BA33B400DB7BED /* TypeValidator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeValidator.cpp; path = source/DataFormatters/TypeValidator.cpp; sourceTree = ""; }; + 94CD7D0719A3FB8600908B7C /* AppleObjCClassDescriptorV2.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppleObjCClassDescriptorV2.h; sourceTree = ""; }; + 94CD7D0819A3FBA300908B7C /* AppleObjCClassDescriptorV2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleObjCClassDescriptorV2.cpp; sourceTree = ""; }; + 94CD7D0A19A3FBC300908B7C /* AppleObjCTypeEncodingParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppleObjCTypeEncodingParser.h; sourceTree = ""; }; + 94CD7D0B19A3FBCE00908B7C /* AppleObjCTypeEncodingParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = AppleObjCTypeEncodingParser.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 94D0858A1B9675A0000D24BD /* FormattersHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FormattersHelpers.h; path = include/lldb/DataFormatters/FormattersHelpers.h; sourceTree = ""; }; + 94D0858B1B9675B8000D24BD /* FormattersHelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FormattersHelpers.cpp; path = source/DataFormatters/FormattersHelpers.cpp; sourceTree = ""; }; + 94E367CC140C4EC4001C7A5A /* modify-python-lldb.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = "modify-python-lldb.py"; sourceTree = ""; }; + 94E367CE140C4EEA001C7A5A /* python-typemaps.swig */ = {isa = PBXFileReference; lastKnownFileType = text; path = "python-typemaps.swig"; sourceTree = ""; }; + 94EBAC8313D9EE26009BA64E /* PythonPointer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PythonPointer.h; path = include/lldb/Utility/PythonPointer.h; sourceTree = ""; }; + 94ED54A119C8A822007BE2EA /* ThreadSafeDenseMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadSafeDenseMap.h; path = include/lldb/Core/ThreadSafeDenseMap.h; sourceTree = ""; }; + 94EE33F218643C6900CD703B /* FormattersContainer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FormattersContainer.h; path = include/lldb/DataFormatters/FormattersContainer.h; sourceTree = ""; }; + 94F48F231A01C679005C0EC6 /* StringPrinter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StringPrinter.h; path = include/lldb/DataFormatters/StringPrinter.h; sourceTree = ""; }; + 94F48F241A01C687005C0EC6 /* StringPrinter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringPrinter.cpp; path = source/DataFormatters/StringPrinter.cpp; sourceTree = ""; }; + 94F6C4D119C264C70049D089 /* ProcessStructReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ProcessStructReader.h; path = include/lldb/Utility/ProcessStructReader.h; sourceTree = ""; }; + 94FA3DDD1405D4E500833217 /* ValueObjectConstResultChild.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectConstResultChild.h; path = include/lldb/Core/ValueObjectConstResultChild.h; sourceTree = ""; }; + 94FA3DDF1405D50300833217 /* ValueObjectConstResultChild.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectConstResultChild.cpp; path = source/Core/ValueObjectConstResultChild.cpp; sourceTree = ""; }; + 94FE476613FC1DA8001F8475 /* finish-swig-Python-LLDB.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "finish-swig-Python-LLDB.sh"; sourceTree = ""; }; + 961FABB81235DE1600F93A47 /* FuncUnwinders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FuncUnwinders.cpp; path = source/Symbol/FuncUnwinders.cpp; sourceTree = ""; }; + 961FABB91235DE1600F93A47 /* UnwindPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindPlan.cpp; path = source/Symbol/UnwindPlan.cpp; sourceTree = ""; }; + 961FABBA1235DE1600F93A47 /* UnwindTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindTable.cpp; path = source/Symbol/UnwindTable.cpp; sourceTree = ""; }; + 964463EB1A330C0500154ED8 /* CompactUnwindInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CompactUnwindInfo.cpp; path = source/Symbol/CompactUnwindInfo.cpp; sourceTree = ""; }; + 964463ED1A330C1B00154ED8 /* CompactUnwindInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompactUnwindInfo.h; path = include/lldb/Symbol/CompactUnwindInfo.h; sourceTree = ""; }; + 966C6B7818E6A56A0093F5EC /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = ""; }; + 9694FA6F1B32AA64005EBB16 /* ABISysV_mips.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ABISysV_mips.cpp; path = "SysV-mips/ABISysV_mips.cpp"; sourceTree = ""; }; + 9694FA701B32AA64005EBB16 /* ABISysV_mips.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ABISysV_mips.h; path = "SysV-mips/ABISysV_mips.h"; 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 = ""; }; + 9A22A15D135E30370024DDC3 /* EmulateInstructionARM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EmulateInstructionARM.cpp; sourceTree = ""; }; + 9A22A15E135E30370024DDC3 /* EmulateInstructionARM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmulateInstructionARM.h; sourceTree = ""; }; + 9A22A15F135E30370024DDC3 /* EmulationStateARM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EmulationStateARM.cpp; sourceTree = ""; }; + 9A22A160135E30370024DDC3 /* EmulationStateARM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmulationStateARM.h; 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 = ""; }; + 9A4633DA11F65D8600955CE1 /* UserSettingsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UserSettingsController.h; path = include/lldb/Core/UserSettingsController.h; sourceTree = ""; }; + 9A4633DC11F65D9A00955CE1 /* UserSettingsController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UserSettingsController.cpp; path = source/Core/UserSettingsController.cpp; sourceTree = ""; }; + 9A48A3A7124AAA5A00922451 /* python-extensions.swig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "python-extensions.swig"; sourceTree = ""; }; + 9A4F350F1368A51A00823F52 /* StreamAsynchronousIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamAsynchronousIO.cpp; path = source/Core/StreamAsynchronousIO.cpp; sourceTree = ""; }; + 9A4F35111368A54100823F52 /* StreamAsynchronousIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamAsynchronousIO.h; path = include/lldb/Core/StreamAsynchronousIO.h; 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 = ""; }; + 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 = ""; }; + 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; lineEnding = 0; name = SBTarget.cpp; path = source/API/SBTarget.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 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 = ""; }; + 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 = ""; }; + A36FF33B17D8E94600244D40 /* OptionParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptionParser.cpp; sourceTree = ""; }; + A36FF33D17D8E98800244D40 /* OptionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionParser.h; path = include/lldb/Host/OptionParser.h; sourceTree = ""; }; + AE44FB261BB07DC60033EB62 /* GoAST.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoAST.h; path = ExpressionParser/Go/GoAST.h; sourceTree = ""; }; + AE44FB271BB07DC60033EB62 /* GoLexer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoLexer.h; path = ExpressionParser/Go/GoLexer.h; sourceTree = ""; }; + AE44FB281BB07DC60033EB62 /* GoParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoParser.h; path = ExpressionParser/Go/GoParser.h; sourceTree = ""; }; + AE44FB291BB07DC60033EB62 /* GoUserExpression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoUserExpression.h; path = ExpressionParser/Go/GoUserExpression.h; sourceTree = ""; }; + AE44FB2A1BB07DD80033EB62 /* GoLexer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoLexer.cpp; path = ExpressionParser/Go/GoLexer.cpp; sourceTree = ""; }; + AE44FB2B1BB07DD80033EB62 /* GoParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoParser.cpp; path = ExpressionParser/Go/GoParser.cpp; sourceTree = ""; }; + AE44FB2C1BB07DD80033EB62 /* GoUserExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoUserExpression.cpp; path = ExpressionParser/Go/GoUserExpression.cpp; sourceTree = ""; }; + AE44FB3C1BB4858A0033EB62 /* GoLanguageRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoLanguageRuntime.h; path = Go/GoLanguageRuntime.h; sourceTree = ""; }; + AE44FB3D1BB485960033EB62 /* GoLanguageRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoLanguageRuntime.cpp; path = Go/GoLanguageRuntime.cpp; sourceTree = ""; }; + AE44FB451BB4BB090033EB62 /* GoLanguage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoLanguage.cpp; path = Language/Go/GoLanguage.cpp; sourceTree = ""; }; + AE44FB461BB4BB090033EB62 /* GoLanguage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GoLanguage.h; path = Language/Go/GoLanguage.h; sourceTree = ""; }; + AE44FB4A1BB4BB540033EB62 /* GoFormatterFunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoFormatterFunctions.cpp; path = Language/Go/GoFormatterFunctions.cpp; sourceTree = ""; }; + AE44FB4B1BB4BB540033EB62 /* GoFormatterFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GoFormatterFunctions.h; path = Language/Go/GoFormatterFunctions.h; sourceTree = ""; }; + AE6897261B94F6DE0018845D /* DWARFASTParserGo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFASTParserGo.cpp; sourceTree = ""; }; + AE6897271B94F6DE0018845D /* DWARFASTParserGo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFASTParserGo.h; sourceTree = ""; }; + AE8F624719EF3E1E00326B21 /* OperatingSystemGo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OperatingSystemGo.cpp; path = Go/OperatingSystemGo.cpp; sourceTree = ""; }; + AE8F624819EF3E1E00326B21 /* OperatingSystemGo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OperatingSystemGo.h; path = Go/OperatingSystemGo.h; sourceTree = ""; }; + AEB0E4581BD6E9F800B24093 /* LLVMUserExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LLVMUserExpression.cpp; path = source/Expression/LLVMUserExpression.cpp; sourceTree = ""; }; + AEB0E45A1BD6EA1400B24093 /* LLVMUserExpression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LLVMUserExpression.h; path = include/lldb/Expression/LLVMUserExpression.h; sourceTree = ""; }; + AEC6FF9F1BE970A2007882C1 /* GoParserTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GoParserTest.cpp; sourceTree = ""; }; + AEEA33F61AC74FE700AB639D /* TypeSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TypeSystem.h; path = include/lldb/Symbol/TypeSystem.h; sourceTree = ""; }; + AEEA34041AC88A7400AB639D /* TypeSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeSystem.cpp; path = source/Symbol/TypeSystem.cpp; sourceTree = ""; }; + AEEA340F1ACA08A000AB639D /* GoASTContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GoASTContext.h; path = include/lldb/Symbol/GoASTContext.h; sourceTree = ""; }; + AEFFBA7C1AC4835D0087B932 /* GoASTContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoASTContext.cpp; path = source/Symbol/GoASTContext.cpp; sourceTree = ""; }; + AF061F85182C97ED00B6A19C /* RegisterContextHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextHistory.cpp; path = Utility/RegisterContextHistory.cpp; sourceTree = ""; }; + AF061F86182C97ED00B6A19C /* RegisterContextHistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextHistory.h; path = Utility/RegisterContextHistory.h; sourceTree = ""; }; + AF061F89182C980000B6A19C /* HistoryThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HistoryThread.h; path = Utility/HistoryThread.h; sourceTree = ""; }; + AF061F8A182C980000B6A19C /* HistoryUnwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HistoryUnwind.h; path = Utility/HistoryUnwind.h; sourceTree = ""; }; + AF0C112718580CD800C4C45B /* QueueItem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QueueItem.cpp; path = source/Target/QueueItem.cpp; sourceTree = ""; }; + AF0E22EE18A09FB20009B7D1 /* AppleGetItemInfoHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetItemInfoHandler.cpp; sourceTree = ""; }; + AF0E22EF18A09FB20009B7D1 /* AppleGetItemInfoHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleGetItemInfoHandler.h; sourceTree = ""; }; + AF0EBBE6185940FB0059E52F /* SBQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBQueue.cpp; path = source/API/SBQueue.cpp; sourceTree = ""; }; + AF0EBBE7185940FB0059E52F /* SBQueueItem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBQueueItem.cpp; path = source/API/SBQueueItem.cpp; sourceTree = ""; }; + AF0EBBEA185941360059E52F /* SBQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBQueue.h; path = include/lldb/API/SBQueue.h; sourceTree = ""; }; + AF0EBBEB185941360059E52F /* SBQueueItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBQueueItem.h; path = include/lldb/API/SBQueueItem.h; sourceTree = ""; }; + AF0EBBEE1859419F0059E52F /* SBQueue.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBQueue.i; sourceTree = ""; }; + AF0EBBEF1859419F0059E52F /* SBQueueItem.i */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBQueueItem.i; sourceTree = ""; }; + AF0F6E4E1739A76D009180FE /* RegisterContextKDP_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextKDP_arm64.cpp; sourceTree = ""; }; + AF0F6E4F1739A76D009180FE /* RegisterContextKDP_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextKDP_arm64.h; sourceTree = ""; }; + AF1729D4182C907200E0AB97 /* HistoryThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HistoryThread.cpp; path = Utility/HistoryThread.cpp; sourceTree = ""; }; + AF1729D5182C907200E0AB97 /* HistoryUnwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HistoryUnwind.cpp; path = Utility/HistoryUnwind.cpp; sourceTree = ""; }; + AF1F7B05189C904B0087DB9C /* AppleGetPendingItemsHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetPendingItemsHandler.cpp; sourceTree = ""; }; + AF1F7B06189C904B0087DB9C /* AppleGetPendingItemsHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleGetPendingItemsHandler.h; sourceTree = ""; }; + AF1FA8891A60A69500272AFC /* RegisterNumber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterNumber.cpp; path = source/Utility/RegisterNumber.cpp; sourceTree = ""; }; + AF20F7641AF18F8500751A6E /* ABISysV_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ABISysV_arm.cpp; path = "SysV-arm/ABISysV_arm.cpp"; sourceTree = ""; }; + AF20F7651AF18F8500751A6E /* ABISysV_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ABISysV_arm.h; path = "SysV-arm/ABISysV_arm.h"; sourceTree = ""; }; + AF20F7681AF18F9000751A6E /* ABISysV_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ABISysV_arm64.cpp; path = "SysV-arm64/ABISysV_arm64.cpp"; sourceTree = ""; }; + AF20F7691AF18F9000751A6E /* ABISysV_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ABISysV_arm64.h; path = "SysV-arm64/ABISysV_arm64.h"; sourceTree = ""; }; + AF20F76C1AF18FC700751A6E /* SBLanguageRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBLanguageRuntime.cpp; path = source/API/SBLanguageRuntime.cpp; sourceTree = ""; }; + AF20F76E1AF1902900751A6E /* RegisterContextLinux_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextLinux_arm64.cpp; path = Utility/RegisterContextLinux_arm64.cpp; sourceTree = ""; }; + AF20F76F1AF1902900751A6E /* RegisterContextLinux_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLinux_arm64.h; path = Utility/RegisterContextLinux_arm64.h; sourceTree = ""; }; + AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FreeBSDSignals.cpp; path = Utility/FreeBSDSignals.cpp; sourceTree = ""; }; + AF23B4DA19009C66003E2A58 /* FreeBSDSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FreeBSDSignals.h; path = Utility/FreeBSDSignals.h; sourceTree = ""; }; + AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformDarwinKernel.cpp; sourceTree = ""; }; + AF254E30170CCC33007AE5C9 /* PlatformDarwinKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformDarwinKernel.h; sourceTree = ""; }; + AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetQueuesHandler.cpp; sourceTree = ""; }; + AF25AB25188F685C0030DEC3 /* AppleGetQueuesHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleGetQueuesHandler.h; sourceTree = ""; }; + AF2670381852D01E00B6CC36 /* Queue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Queue.cpp; path = source/Target/Queue.cpp; sourceTree = ""; }; + AF2670391852D01E00B6CC36 /* QueueList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QueueList.cpp; path = source/Target/QueueList.cpp; sourceTree = ""; }; + AF2BCA6918C7EFDE005B4526 /* JITLoaderGDB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITLoaderGDB.cpp; sourceTree = ""; }; + AF2BCA6A18C7EFDE005B4526 /* JITLoaderGDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITLoaderGDB.h; sourceTree = ""; }; + AF33B4BC1C1FA441001B28D9 /* NetBSDSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NetBSDSignals.cpp; path = Utility/NetBSDSignals.cpp; sourceTree = ""; }; + AF33B4BD1C1FA441001B28D9 /* NetBSDSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetBSDSignals.h; path = Utility/NetBSDSignals.h; sourceTree = ""; }; + AF37E10917C861F20061E18E /* ProcessRunLock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessRunLock.cpp; sourceTree = ""; }; + AF3F54AE1B3BA59C00186E73 /* CrashReason.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CrashReason.cpp; sourceTree = ""; }; + AF3F54AF1B3BA59C00186E73 /* CrashReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrashReason.h; sourceTree = ""; }; + AF3F54B21B3BA5D500186E73 /* POSIXStopInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = POSIXStopInfo.cpp; sourceTree = ""; }; + AF3F54B31B3BA5D500186E73 /* POSIXStopInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSIXStopInfo.h; sourceTree = ""; }; + AF3F54B81B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXProcessMonitor_arm.cpp; sourceTree = ""; }; + AF3F54B91B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXProcessMonitor_arm.h; sourceTree = ""; }; + AF3F54BA1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXProcessMonitor_arm64.cpp; sourceTree = ""; }; + AF3F54BB1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXProcessMonitor_arm64.h; sourceTree = ""; }; + AF3F54BC1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_mips64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXProcessMonitor_mips64.cpp; sourceTree = ""; }; + AF3F54BD1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_mips64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXProcessMonitor_mips64.h; sourceTree = ""; }; + AF3F54BE1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_powerpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXProcessMonitor_powerpc.cpp; sourceTree = ""; }; + AF3F54BF1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_powerpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXProcessMonitor_powerpc.h; sourceTree = ""; }; + AF3F54C01B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_x86.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXProcessMonitor_x86.cpp; sourceTree = ""; }; + AF3F54C11B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_x86.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXProcessMonitor_x86.h; sourceTree = ""; }; + AF45E1FC1BF57C8D000563EB /* PythonTestSuite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PythonTestSuite.cpp; sourceTree = ""; }; + AF45E1FD1BF57C8D000563EB /* PythonTestSuite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PythonTestSuite.h; sourceTree = ""; }; + AF45FDE318A1F3AC0007051C /* AppleGetThreadItemInfoHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleGetThreadItemInfoHandler.cpp; sourceTree = ""; }; + AF45FDE418A1F3AC0007051C /* AppleGetThreadItemInfoHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleGetThreadItemInfoHandler.h; sourceTree = ""; }; + AF68D2541255416E002FF25B /* RegisterContextLLDB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextLLDB.cpp; path = Utility/RegisterContextLLDB.cpp; sourceTree = ""; }; + AF68D2551255416E002FF25B /* RegisterContextLLDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLLDB.h; path = Utility/RegisterContextLLDB.h; sourceTree = ""; }; + AF68D32F1255A110002FF25B /* UnwindLLDB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindLLDB.cpp; path = Utility/UnwindLLDB.cpp; sourceTree = ""; }; + AF68D3301255A110002FF25B /* UnwindLLDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnwindLLDB.h; path = Utility/UnwindLLDB.h; sourceTree = ""; }; + AF77E08D1A033C700096C0EA /* ABISysV_ppc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ABISysV_ppc.cpp; path = "SysV-ppc/ABISysV_ppc.cpp"; sourceTree = ""; }; + AF77E08E1A033C700096C0EA /* ABISysV_ppc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ABISysV_ppc.h; path = "SysV-ppc/ABISysV_ppc.h"; sourceTree = ""; }; + AF77E0911A033C7F0096C0EA /* ABISysV_ppc64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ABISysV_ppc64.cpp; path = "SysV-ppc64/ABISysV_ppc64.cpp"; sourceTree = ""; }; + AF77E0921A033C7F0096C0EA /* ABISysV_ppc64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ABISysV_ppc64.h; path = "SysV-ppc64/ABISysV_ppc64.h"; sourceTree = ""; }; + AF77E0991A033D360096C0EA /* RegisterContext_powerpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContext_powerpc.h; path = Utility/RegisterContext_powerpc.h; sourceTree = ""; }; + AF77E09A1A033D360096C0EA /* RegisterContextFreeBSD_powerpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextFreeBSD_powerpc.cpp; path = Utility/RegisterContextFreeBSD_powerpc.cpp; sourceTree = ""; }; + AF77E09B1A033D360096C0EA /* RegisterContextFreeBSD_powerpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextFreeBSD_powerpc.h; path = Utility/RegisterContextFreeBSD_powerpc.h; sourceTree = ""; }; + AF77E09C1A033D360096C0EA /* RegisterContextMacOSXFrameBackchain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextMacOSXFrameBackchain.cpp; path = Utility/RegisterContextMacOSXFrameBackchain.cpp; sourceTree = ""; }; + AF77E09D1A033D360096C0EA /* RegisterContextPOSIX_powerpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextPOSIX_powerpc.cpp; path = Utility/RegisterContextPOSIX_powerpc.cpp; sourceTree = ""; }; + AF77E09E1A033D360096C0EA /* RegisterContextPOSIX_powerpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextPOSIX_powerpc.h; path = Utility/RegisterContextPOSIX_powerpc.h; sourceTree = ""; }; + AF77E09F1A033D360096C0EA /* RegisterInfos_powerpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterInfos_powerpc.h; path = Utility/RegisterInfos_powerpc.h; sourceTree = ""; }; + AF77E0A71A033D740096C0EA /* RegisterContextPOSIXCore_powerpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXCore_powerpc.cpp; sourceTree = ""; }; + AF77E0A81A033D740096C0EA /* RegisterContextPOSIXCore_powerpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXCore_powerpc.h; sourceTree = ""; }; + AF81DEF91828A23F0042CF19 /* SystemRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SystemRuntime.cpp; path = source/Target/SystemRuntime.cpp; sourceTree = ""; }; + AF8AD62A1BEC28A400150209 /* PlatformAppleTVSimulator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformAppleTVSimulator.cpp; sourceTree = ""; }; + AF8AD62B1BEC28A400150209 /* PlatformAppleTVSimulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformAppleTVSimulator.h; sourceTree = ""; }; + AF8AD62C1BEC28A400150209 /* PlatformAppleWatchSimulator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformAppleWatchSimulator.cpp; sourceTree = ""; }; + AF8AD62D1BEC28A400150209 /* PlatformAppleWatchSimulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformAppleWatchSimulator.h; sourceTree = ""; }; + AF8AD6331BEC28C400150209 /* PlatformRemoteAppleTV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformRemoteAppleTV.cpp; sourceTree = ""; }; + AF8AD6341BEC28C400150209 /* PlatformRemoteAppleTV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformRemoteAppleTV.h; sourceTree = ""; }; + AF8AD6351BEC28C400150209 /* PlatformRemoteAppleWatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformRemoteAppleWatch.cpp; sourceTree = ""; }; + AF8AD6361BEC28C400150209 /* PlatformRemoteAppleWatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformRemoteAppleWatch.h; sourceTree = ""; }; + AF90106315AB7C5700FF120D /* lldb.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = lldb.1; path = docs/lldb.1; sourceTree = ""; }; + AF9107E91685709A00DBCD3C /* ARM64_Stabs_Registers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARM64_Stabs_Registers.h; path = source/Utility/ARM64_Stabs_Registers.h; sourceTree = ""; }; + AF9107EC168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextDarwin_arm64.cpp; path = Utility/RegisterContextDarwin_arm64.cpp; sourceTree = ""; }; + AF9107ED168570D200DBCD3C /* RegisterContextDarwin_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextDarwin_arm64.h; path = Utility/RegisterContextDarwin_arm64.h; sourceTree = ""; }; + AF94005711C03F6500085DB9 /* SymbolVendor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolVendor.cpp; path = source/Symbol/SymbolVendor.cpp; sourceTree = ""; }; + AF94726E1B575E430063D65C /* ValueObjectConstResultCast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectConstResultCast.cpp; path = source/Core/ValueObjectConstResultCast.cpp; sourceTree = ""; }; + AF9472701B575E5F0063D65C /* ValueObjectConstResultCast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectConstResultCast.h; path = include/lldb/Core/ValueObjectConstResultCast.h; sourceTree = ""; }; + AF9B8F31182DB52900DA866F /* SystemRuntimeMacOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemRuntimeMacOSX.cpp; sourceTree = ""; }; + AF9B8F32182DB52900DA866F /* SystemRuntimeMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SystemRuntimeMacOSX.h; sourceTree = ""; }; + AFB3D27E1AC262AB003B4B30 /* MICmdCmdGdbShow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MICmdCmdGdbShow.cpp; path = "tools/lldb-mi/MICmdCmdGdbShow.cpp"; sourceTree = SOURCE_ROOT; }; + AFB3D27F1AC262AB003B4B30 /* MICmdCmdGdbShow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MICmdCmdGdbShow.h; path = "tools/lldb-mi/MICmdCmdGdbShow.h"; sourceTree = SOURCE_ROOT; }; + AFC234061AF85CE000CDE8B6 /* CommandObjectLanguage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectLanguage.cpp; path = source/Commands/CommandObjectLanguage.cpp; sourceTree = ""; }; + AFC234071AF85CE000CDE8B6 /* CommandObjectLanguage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectLanguage.h; path = source/Commands/CommandObjectLanguage.h; sourceTree = ""; }; + AFCB2BBB1BF577F40018B553 /* PythonExceptionState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PythonExceptionState.cpp; path = ScriptInterpreter/Python/PythonExceptionState.cpp; sourceTree = ""; }; + AFCB2BBC1BF577F40018B553 /* PythonExceptionState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PythonExceptionState.h; path = ScriptInterpreter/Python/PythonExceptionState.h; sourceTree = ""; }; + AFDFDFD019E34D3400EAE509 /* ConnectionFileDescriptorPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConnectionFileDescriptorPosix.cpp; sourceTree = ""; }; + AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StructuredData.cpp; path = source/Core/StructuredData.cpp; sourceTree = ""; }; + AFF87C86150FF669000E1742 /* com.apple.debugserver.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.plist; path = tools/debugserver/source/com.apple.debugserver.plist; sourceTree = ""; }; + AFF87C8A150FF677000E1742 /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.applist.plist; path = tools/debugserver/source/com.apple.debugserver.applist.plist; sourceTree = ""; }; + AFF87C8C150FF680000E1742 /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.applist.plist; path = tools/debugserver/source/com.apple.debugserver.applist.plist; sourceTree = ""; }; + AFF87C8E150FF688000E1742 /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.debugserver.applist.plist; path = tools/debugserver/source/com.apple.debugserver.applist.plist; sourceTree = ""; }; + B207C4921429607D00F36E4E /* CommandObjectWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectWatchpoint.cpp; path = source/Commands/CommandObjectWatchpoint.cpp; sourceTree = ""; }; + B207C4941429609C00F36E4E /* CommandObjectWatchpoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CommandObjectWatchpoint.h; path = source/Commands/CommandObjectWatchpoint.h; sourceTree = ""; }; + B23DD24F12EDFAC1000C3894 /* ARMUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARMUtils.h; path = Utility/ARMUtils.h; sourceTree = ""; }; + B2462246141AD37D00F3D409 /* OptionGroupWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionGroupWatchpoint.cpp; path = source/Interpreter/OptionGroupWatchpoint.cpp; sourceTree = ""; }; + B2462248141AD39B00F3D409 /* OptionGroupWatchpoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OptionGroupWatchpoint.h; path = include/lldb/Interpreter/OptionGroupWatchpoint.h; sourceTree = ""; }; + B2462249141AE62200F3D409 /* Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = include/lldb/Utility/Utils.h; sourceTree = ""; }; + B27318411416AC12006039C8 /* WatchpointList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WatchpointList.cpp; path = source/Breakpoint/WatchpointList.cpp; sourceTree = ""; }; + B27318431416AC43006039C8 /* WatchpointList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WatchpointList.h; path = include/lldb/Breakpoint/WatchpointList.h; sourceTree = ""; }; + B28058A0139988B0002D96D0 /* InferiorCallPOSIX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InferiorCallPOSIX.cpp; path = Utility/InferiorCallPOSIX.cpp; sourceTree = ""; }; + B28058A2139988C6002D96D0 /* InferiorCallPOSIX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = InferiorCallPOSIX.h; path = Utility/InferiorCallPOSIX.h; sourceTree = ""; }; + B287E63E12EFAE2C00C9BEFE /* ARMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARMDefines.h; path = Utility/ARMDefines.h; sourceTree = ""; }; + B296983412C2FB2B002D92C3 /* CommandObjectVersion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectVersion.cpp; path = source/Commands/CommandObjectVersion.cpp; sourceTree = ""; }; + B296983512C2FB2B002D92C3 /* CommandObjectVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectVersion.h; path = source/Commands/CommandObjectVersion.h; sourceTree = ""; }; + B299580A14F2FA1400050A04 /* DisassemblerLLVMC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DisassemblerLLVMC.cpp; sourceTree = ""; }; + B299580C14F2FA1F00050A04 /* DisassemblerLLVMC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DisassemblerLLVMC.h; sourceTree = ""; }; + B2A58721143119810092BFBA /* SBWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBWatchpoint.h; path = include/lldb/API/SBWatchpoint.h; sourceTree = ""; }; + B2A58723143119D50092BFBA /* SBWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBWatchpoint.cpp; path = source/API/SBWatchpoint.cpp; sourceTree = ""; }; + B2A5872514313B480092BFBA /* SBWatchpoint.i */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = SBWatchpoint.i; sourceTree = ""; }; + B2B7CCEA15D1BD6600EEFB57 /* CommandObjectWatchpointCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectWatchpointCommand.cpp; path = source/Commands/CommandObjectWatchpointCommand.cpp; sourceTree = ""; }; + B2B7CCEC15D1BD9600EEFB57 /* CommandObjectWatchpointCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CommandObjectWatchpointCommand.h; path = source/Commands/CommandObjectWatchpointCommand.h; sourceTree = ""; }; + B2B7CCED15D1BFB700EEFB57 /* WatchpointOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WatchpointOptions.h; path = include/lldb/Breakpoint/WatchpointOptions.h; sourceTree = ""; }; + B2B7CCEF15D1C20F00EEFB57 /* WatchpointOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WatchpointOptions.cpp; path = source/Breakpoint/WatchpointOptions.cpp; sourceTree = ""; }; + B2D3033612EFA5C500F84EB3 /* InstructionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InstructionUtils.h; path = Utility/InstructionUtils.h; sourceTree = ""; }; + B5EFAE841AE53B1D007059F3 /* RegisterContextFreeBSD_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextFreeBSD_arm.cpp; path = Utility/RegisterContextFreeBSD_arm.cpp; sourceTree = ""; }; + B5EFAE851AE53B1D007059F3 /* RegisterContextFreeBSD_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextFreeBSD_arm.h; path = Utility/RegisterContextFreeBSD_arm.h; sourceTree = ""; }; + E73A15A41B548EC500786197 /* GDBRemoteSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GDBRemoteSignals.cpp; path = Utility/GDBRemoteSignals.cpp; sourceTree = ""; }; + E73A15A51B548EC500786197 /* GDBRemoteSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GDBRemoteSignals.h; path = Utility/GDBRemoteSignals.h; sourceTree = ""; }; + E769331D1A94D18100C73337 /* lldb-server.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lldb-server.cpp"; path = "tools/lldb-server/lldb-server.cpp"; sourceTree = ""; }; + E7723D421AC4A7FB002BA082 /* RegisterContextPOSIXCore_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextPOSIXCore_arm64.cpp; sourceTree = ""; }; + E7723D431AC4A7FB002BA082 /* RegisterContextPOSIXCore_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextPOSIXCore_arm64.h; sourceTree = ""; }; + E7723D461AC4A8C8002BA082 /* RegisterContextFreeBSD_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextFreeBSD_arm64.cpp; path = Utility/RegisterContextFreeBSD_arm64.cpp; sourceTree = ""; }; + E7723D471AC4A8C8002BA082 /* RegisterContextFreeBSD_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextFreeBSD_arm64.h; path = Utility/RegisterContextFreeBSD_arm64.h; sourceTree = ""; }; + E7723D4A1AC4A944002BA082 /* RegisterContextPOSIX_arm64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextPOSIX_arm64.cpp; path = Utility/RegisterContextPOSIX_arm64.cpp; sourceTree = ""; }; + E7723D4B1AC4A944002BA082 /* RegisterContextPOSIX_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextPOSIX_arm64.h; path = Utility/RegisterContextPOSIX_arm64.h; sourceTree = ""; }; + E778E99F1B062D1700247609 /* EmulateInstructionMIPS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EmulateInstructionMIPS.cpp; sourceTree = ""; }; + E778E9A01B062D1700247609 /* EmulateInstructionMIPS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmulateInstructionMIPS.h; sourceTree = ""; }; + EB8375E61B553DE800BA907D /* ThreadPlanCallFunctionUsingABI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanCallFunctionUsingABI.cpp; path = source/Target/ThreadPlanCallFunctionUsingABI.cpp; sourceTree = ""; }; + EB8375E81B553DFE00BA907D /* ThreadPlanCallFunctionUsingABI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadPlanCallFunctionUsingABI.h; path = include/lldb/Target/ThreadPlanCallFunctionUsingABI.h; sourceTree = ""; }; + EDB919B414F6F10D008FF64B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 239504D11BDD451400963CEA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2326CF4F1BDD687800A5CEAC /* libpanel.dylib in Frameworks */, + 2326CF4D1BDD684B00A5CEAC /* libedit.dylib in Frameworks */, + 2326CF4B1BDD681800A5CEAC /* libz.dylib in Frameworks */, + 2326CF491BDD67D800A5CEAC /* libncurses.dylib in Frameworks */, + 2326CF441BDD643700A5CEAC /* liblldb-core.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 26579F66126A25920007C5CB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 26680205115FD0ED008E1FE4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 260157C81885F53100F875CF /* libpanel.dylib in Frameworks */, + 2670F8121862B44A006B332C /* libncurses.dylib in Frameworks */, + 26CEB5EF18761CB2008F575A /* libedit.dylib in Frameworks */, + 26D55235159A7DB100708D8D /* libxml2.dylib in Frameworks */, + 268901161335BBC300698AC0 /* liblldb-core.a in Frameworks */, + 966C6B7A18E6A56A0093F5EC /* libz.dylib in Frameworks */, + 2668022F115FD19D008E1FE4 /* CoreFoundation.framework in Frameworks */, + 26680233115FD1A7008E1FE4 /* libobjc.dylib in Frameworks */, + 4CF3D80C15AF4DC800845BF3 /* Security.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2689FFC713353D7A00698AC0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2690CD141A6DC0D000E717C8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2669424D1A6DC32B0063BE93 /* LLDB.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 26DC6A0E1337FE6900FF7998 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 260157C61885F51C00F875CF /* libpanel.dylib in Frameworks */, + 966C6B7C18E6A56A0093F5EC /* libz.dylib in Frameworks */, + 26780C611867C33D00234593 /* libncurses.dylib in Frameworks */, + 26CFDCA818616473000E63E5 /* libedit.dylib in Frameworks */, + 2606EDDF184E68A10034641B /* liblldb-core.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 26F5C26810F3D9A4009D5894 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 26F5C32D10F3DFDD009D5894 /* libtermcap.dylib in Frameworks */, + 2668035C11601108008E1FE4 /* LLDB.framework in Frameworks */, + 966C6B7918E6A56A0093F5EC /* libz.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 942829BD1A89835300521B30 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2656BBC31AE0739C00441749 /* libedit.dylib in Frameworks */, + 2656BBC61AE073B500441749 /* libz.dylib in Frameworks */, + 2656BBC51AE073AD00441749 /* libpanel.dylib in Frameworks */, + 2656BBC41AE073A800441749 /* libncurses.dylib in Frameworks */, + 942829CC1A89839300521B30 /* liblldb-core.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* lldb */ = { + isa = PBXGroup; + children = ( + 2326CF4E1BDD687800A5CEAC /* libpanel.dylib */, + 2326CF4C1BDD684B00A5CEAC /* libedit.dylib */, + 2326CF4A1BDD681800A5CEAC /* libz.dylib */, + 2326CF471BDD67C100A5CEAC /* libncurses.dylib */, + 2326CF451BDD647400A5CEAC /* Foundation.framework */, + 2326CF3F1BDD613E00A5CEAC /* Python.framework */, + 26F5C32810F3DF7D009D5894 /* Libraries */, + 264E8576159BE51A00E9D7A2 /* Resources */, + 08FB7795FE84155DC02AAC07 /* Source */, + 26F5C22410F3D950009D5894 /* Tools */, + 2690CD181A6DC0D000E717C8 /* lldb-mi */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + 2321F9331BDD326500BA9A93 /* unittests */, + ); + name = lldb; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 266960581199F4230075C61A /* Scripts */, + 26BC7E7410F1B85900F91463 /* lldb.cpp */, + 26BC7C2A10F1B3BC00F91463 /* lldb-private.h */, + 26217932133BCB850083B112 /* lldb-private-enumerations.h */, + 26BC7C2810F1B3BC00F91463 /* lldb-private-interfaces.h */, + 26217930133BC8640083B112 /* lldb-private-types.h */, + 262D3190111B4341004E6F88 /* API */, + 26BC7CEB10F1B70800F91463 /* Breakpoint */, + 26BC7D0D10F1B71D00F91463 /* Commands */, + 26BC7C1010F1B34800F91463 /* Core */, + 94CB255616B0683B0059775D /* DataFormatters */, + 26BC7DBE10F1B78200F91463 /* Expression */, + 26BC7DD010F1B7C100F91463 /* Host */, + 3F8169261ABB73C1001DA9DF /* Initialization */, + 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 */, + 26579F68126A25920007C5CB /* darwin-debug */, + 26DC6A101337FE6900FF7998 /* lldb-server */, + 2690CD171A6DC0D000E717C8 /* lldb-mi */, + 942829C01A89835300521B30 /* lldb-argdumper */, + 239504D41BDD451400963CEA /* lldb-gtest */, + ); + name = Products; + sourceTree = ""; + usesTabs = 0; + }; + 23042D0F1976C9D800621B2C /* Kalimba */ = { + isa = PBXGroup; + children = ( + 23042D111976CA0A00621B2C /* PlatformKalimba.h */, + 23042D101976CA0A00621B2C /* PlatformKalimba.cpp */, + ); + path = Kalimba; + sourceTree = ""; + }; + 2321F9331BDD326500BA9A93 /* unittests */ = { + isa = PBXGroup; + children = ( + 239504C61BDD3FF300963CEA /* CMakeLists.txt */, + 239504C21BDD3FD600963CEA /* gtest_common.h */, + 2321F9371BDD32ED00BA9A93 /* Host */, + 2321F93C1BDD339A00BA9A93 /* Interpreter */, + 2326CF501BDD68CA00A5CEAC /* Editline */, + AEC6FF9D1BE97035007882C1 /* Expression */, + 2321F93F1BDD33D800BA9A93 /* ScriptInterpreter */, + 2321F9421BDD343A00BA9A93 /* Utility */, + ); + path = unittests; + sourceTree = ""; + }; + 2321F9371BDD32ED00BA9A93 /* Host */ = { + isa = PBXGroup; + children = ( + 2321F9381BDD332400BA9A93 /* CMakeLists.txt */, + 2321F9391BDD332400BA9A93 /* SocketAddressTest.cpp */, + 2321F93A1BDD332400BA9A93 /* SocketTest.cpp */, + 2321F93B1BDD332400BA9A93 /* SymbolsTest.cpp */, + ); + path = Host; + sourceTree = ""; + }; + 2321F93C1BDD339A00BA9A93 /* Interpreter */ = { + isa = PBXGroup; + children = ( + 2321F93D1BDD33CE00BA9A93 /* CMakeLists.txt */, + 2321F93E1BDD33CE00BA9A93 /* TestArgs.cpp */, + ); + path = Interpreter; + sourceTree = ""; + }; + 2321F93F1BDD33D800BA9A93 /* ScriptInterpreter */ = { + isa = PBXGroup; + children = ( + 2321F9401BDD340D00BA9A93 /* CMakeLists.txt */, + 2321F94B1BDD35D500BA9A93 /* Python */, + ); + path = ScriptInterpreter; + sourceTree = ""; + }; + 2321F9421BDD343A00BA9A93 /* Utility */ = { + isa = PBXGroup; + children = ( + 2321F9431BDD346100BA9A93 /* CMakeLists.txt */, + 2321F9441BDD346100BA9A93 /* StringExtractorTest.cpp */, + 2321F9451BDD346100BA9A93 /* TaskPoolTest.cpp */, + 2321F9461BDD346100BA9A93 /* UriParserTest.cpp */, + ); + path = Utility; + sourceTree = ""; + }; + 2321F94B1BDD35D500BA9A93 /* Python */ = { + isa = PBXGroup; + children = ( + 2321F94C1BDD360F00BA9A93 /* CMakeLists.txt */, + 2321F94D1BDD360F00BA9A93 /* PythonDataObjectsTests.cpp */, + 3FA093141BF65D3A0037DD08 /* PythonExceptionStateTests.cpp */, + AF45E1FC1BF57C8D000563EB /* PythonTestSuite.cpp */, + AF45E1FD1BF57C8D000563EB /* PythonTestSuite.h */, + ); + path = Python; + sourceTree = ""; + }; + 2326CF501BDD68CA00A5CEAC /* Editline */ = { + isa = PBXGroup; + children = ( + 2326CF511BDD693B00A5CEAC /* EditlineTest.cpp */, + ); + path = Editline; + sourceTree = ""; + }; + 233B008B196106E90090E598 /* Linux */ = { + isa = PBXGroup; + children = ( + 256CBDAB1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm.cpp */, + 256CBDAC1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm.h */, + 256CBDAD1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm64.cpp */, + 256CBDAE1ADD0DB600BC6CDC /* NativeRegisterContextLinux_arm64.h */, + 256CBDAF1ADD0DB600BC6CDC /* NativeRegisterContextLinux_mips64.cpp */, + 256CBDB01ADD0DB600BC6CDC /* NativeRegisterContextLinux_mips64.h */, + 232CB62B19213AC200EF39FC /* NativeProcessLinux.cpp */, + 232CB62C19213AC200EF39FC /* NativeProcessLinux.h */, + 233B00AA19622F3F0090E598 /* NativeRegisterContextLinux_x86_64.h */, + 233B00A919622F3F0090E598 /* NativeRegisterContextLinux_x86_64.cpp */, + 232CB62D19213AC200EF39FC /* NativeThreadLinux.cpp */, + 232CB62E19213AC200EF39FC /* NativeThreadLinux.h */, + 233B00A2196113730090E598 /* ProcFileReader.h */, + 233B00A1196113730090E598 /* ProcFileReader.cpp */, + ); + path = Linux; + sourceTree = ""; + }; + 233B009C19610D130090E598 /* linux */ = { + isa = PBXGroup; + children = ( + 3FDFE56319AF9B77009756A7 /* Config.h */, + 233B009D19610D6B0090E598 /* Host.cpp */, + 237C577A19AF9D9F00213D59 /* HostInfoLinux.h */, + 3FDFE53619A2933E009756A7 /* HostInfoLinux.cpp */, + 3FDFE56419AF9B77009756A7 /* HostInfoLinux.h */, + 3FDFE56219AF9B60009756A7 /* HostThreadLinux.cpp */, + 3FDFE56519AF9B77009756A7 /* HostThreadLinux.h */, + ); + name = linux; + path = source/Host/linux; + sourceTree = ""; + }; + 23AB0526199FF5D3003B8084 /* FreeBSD */ = { + isa = PBXGroup; + children = ( + AF3F54B21B3BA5D500186E73 /* POSIXStopInfo.cpp */, + AF3F54B31B3BA5D500186E73 /* POSIXStopInfo.h */, + AF3F54B81B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_arm.cpp */, + AF3F54B91B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_arm.h */, + AF3F54BA1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_arm64.cpp */, + AF3F54BB1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_arm64.h */, + AF3F54BC1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_mips64.cpp */, + AF3F54BD1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_mips64.h */, + AF3F54BE1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_powerpc.cpp */, + AF3F54BF1B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_powerpc.h */, + AF3F54C01B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_x86.cpp */, + AF3F54C11B3BA5D500186E73 /* RegisterContextPOSIXProcessMonitor_x86.h */, + 23AB052E199FF639003B8084 /* FreeBSDThread.h */, + 23AB052D199FF639003B8084 /* FreeBSDThread.cpp */, + 23AB0530199FF639003B8084 /* ProcessFreeBSD.h */, + 23AB052F199FF639003B8084 /* ProcessFreeBSD.cpp */, + 23AB0532199FF639003B8084 /* ProcessMonitor.h */, + 23AB0531199FF639003B8084 /* ProcessMonitor.cpp */, + ); + path = FreeBSD; + sourceTree = ""; + }; + 260C897110F57C5600BB2B04 /* Plugins */ = { + isa = PBXGroup; + children = ( + 8CF02ADD19DCBEC200B14BE0 /* InstrumentationRuntime */, + 8C2D6A58197A1FB9006989C9 /* MemoryHistory */, + 26DB3E051379E7AD0080DC73 /* ABI */, + 260C897210F57C5600BB2B04 /* Disassembler */, + 260C897810F57C5600BB2B04 /* DynamicLoader */, + 4984BA0B1B975E9F008658D4 /* ExpressionParser */, + 26D9FDCA12F785120003F2EE /* Instruction */, + AF2BCA6518C7EFDE005B4526 /* JITLoader */, + 94B638541B8FABEA004FE1E4 /* Language */, + 4CCA643A13B40B82003BDF98 /* LanguageRuntime */, + 260C897E10F57C5600BB2B04 /* ObjectContainer */, + 260C898210F57C5600BB2B04 /* ObjectFile */, + 266DFE9013FD64D200D0C574 /* OperatingSystem */, + 26C5577E132575B6008FD8FE /* Platform */, + 260C898A10F57C5600BB2B04 /* Process */, + 3FBA69DA1B6066D20008F44A /* ScriptInterpreter */, + AF11CB34182CA85A00D9B618 /* SystemRuntime */, + 260C89B110F57C5600BB2B04 /* SymbolFile */, + 260C89E010F57C5600BB2B04 /* SymbolVendor */, + 26AC3F441365F40E0065C7EF /* UnwindAssembly */, + ); + name = Plugins; + path = source/Plugins; + sourceTree = ""; + }; + 260C897210F57C5600BB2B04 /* Disassembler */ = { + isa = PBXGroup; + children = ( + 260C897310F57C5600BB2B04 /* llvm */, + ); + path = Disassembler; + sourceTree = ""; + }; + 260C897310F57C5600BB2B04 /* llvm */ = { + isa = PBXGroup; + children = ( + B299580A14F2FA1400050A04 /* DisassemblerLLVMC.cpp */, + B299580C14F2FA1F00050A04 /* DisassemblerLLVMC.h */, + ); + path = llvm; + sourceTree = ""; + }; + 260C897810F57C5600BB2B04 /* DynamicLoader */ = { + isa = PBXGroup; + children = ( + 26274FA414030F79006BA130 /* Darwin-Kernel */, + 2666ADBF1B3CB675001FAFD3 /* Hexagon-DYLD */, + 260C897910F57C5600BB2B04 /* MacOSX-DYLD */, + 26FFC19214FC072100087D58 /* POSIX-DYLD */, + 26F006521B4DD86700B872E5 /* Windows-DYLD */, + 268A683C1321B505000E3FB8 /* Static */, + ); + path = DynamicLoader; + sourceTree = ""; + }; + 260C897910F57C5600BB2B04 /* MacOSX-DYLD */ = { + isa = PBXGroup; + children = ( + 260C897A10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLD.cpp */, + 260C897B10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLD.h */, + ); + 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 */, + 26EFC4C718CFAF0D00865D87 /* JIT */, + 260C898710F57C5600BB2B04 /* Mach-O */, + 26E152221419CACA007967D0 /* PECOFF */, + ); + path = ObjectFile; + sourceTree = ""; + }; + 260C898310F57C5600BB2B04 /* ELF */ = { + isa = PBXGroup; + children = ( + 26D27C9E11ED3A4E0024D721 /* ELFHeader.h */, + 26D27C9D11ED3A4E0024D721 /* ELFHeader.cpp */, + 260C898610F57C5600BB2B04 /* ObjectFileELF.h */, + 260C898510F57C5600BB2B04 /* ObjectFileELF.cpp */, + ); + path = ELF; + sourceTree = ""; + }; + 260C898710F57C5600BB2B04 /* Mach-O */ = { + isa = PBXGroup; + children = ( + 260C898810F57C5600BB2B04 /* ObjectFileMachO.cpp */, + 260C898910F57C5600BB2B04 /* ObjectFileMachO.h */, + ); + path = "Mach-O"; + sourceTree = ""; + }; + 260C898A10F57C5600BB2B04 /* Process */ = { + isa = PBXGroup; + children = ( + 26BC179F18C7F4CB00D2196D /* elf-core */, + 23AB0526199FF5D3003B8084 /* FreeBSD */, + 4CEE62F71145F1C70064CF93 /* GDB Remote */, + 233B008B196106E90090E598 /* Linux */, + 2642FBA713D003B400ED6808 /* MacOSX-Kernel */, + 26A527BC14E24F5F00F3A14A /* mach-core */, + 26BC17B318C7F4FA00D2196D /* POSIX */, + 26B4666E11A2080F00CF6220 /* Utility */, + ); + path = Process; + sourceTree = ""; + }; + 260C89B110F57C5600BB2B04 /* SymbolFile */ = { + isa = PBXGroup; + children = ( + 260C89B210F57C5600BB2B04 /* DWARF */, + 260C89DD10F57C5600BB2B04 /* Symtab */, + ); + path = SymbolFile; + sourceTree = ""; + }; + 260C89B210F57C5600BB2B04 /* DWARF */ = { + isa = PBXGroup; + children = ( + 6D95DC031B9DC06F000E318A /* DIERef.h */, + 6D95DC041B9DC06F000E318A /* SymbolFileDWARFDwo.h */, + 6D95DBFD1B9DC057000E318A /* DIERef.cpp */, + 6D95DBFE1B9DC057000E318A /* HashedNameToDIE.cpp */, + 6D95DBFF1B9DC057000E318A /* SymbolFileDWARFDwo.cpp */, + 260C89B310F57C5600BB2B04 /* DWARFAbbreviationDeclaration.cpp */, + 260C89B410F57C5600BB2B04 /* DWARFAbbreviationDeclaration.h */, + 269DDD451B8FD01A00D0DBD8 /* DWARFASTParser.h */, + 269DDD491B8FD1C300D0DBD8 /* DWARFASTParserClang.h */, + 269DDD481B8FD1C300D0DBD8 /* DWARFASTParserClang.cpp */, + AE6897271B94F6DE0018845D /* DWARFASTParserGo.h */, + AE6897261B94F6DE0018845D /* DWARFASTParserGo.cpp */, + 260C89B610F57C5600BB2B04 /* DWARFAttribute.h */, + 266E829C1B8E542C008FCA06 /* DWARFAttribute.cpp */, + 260C89B710F57C5600BB2B04 /* DWARFCompileUnit.cpp */, + 260C89B810F57C5600BB2B04 /* DWARFCompileUnit.h */, + 26AB92101819D74600E63F3E /* DWARFDataExtractor.cpp */, + 26AB92111819D74600E63F3E /* DWARFDataExtractor.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 */, + 23E77CD61C20F29F007192AD /* DWARFDebugMacro.cpp */, + 23E77CD71C20F29F007192AD /* DWARFDebugMacro.h */, + 260C89C910F57C5600BB2B04 /* DWARFDebugPubnames.cpp */, + 260C89CA10F57C5600BB2B04 /* DWARFDebugPubnames.h */, + 260C89CB10F57C5600BB2B04 /* DWARFDebugPubnamesSet.cpp */, + 260C89CC10F57C5600BB2B04 /* DWARFDebugPubnamesSet.h */, + 260C89CD10F57C5600BB2B04 /* DWARFDebugRanges.cpp */, + 260C89CE10F57C5600BB2B04 /* DWARFDebugRanges.h */, + 26B1EFAC154638AF00E2DAC7 /* DWARFDeclContext.cpp */, + 26B1EFAD154638AF00E2DAC7 /* DWARFDeclContext.h */, + 260C89CF10F57C5600BB2B04 /* DWARFDefines.cpp */, + 260C89D010F57C5600BB2B04 /* DWARFDefines.h */, + 266E82951B8CE346008FCA06 /* DWARFDIE.h */, + 266E82961B8CE3AC008FCA06 /* DWARFDIE.cpp */, + 260C89D110F57C5600BB2B04 /* DWARFDIECollection.cpp */, + 260C89D210F57C5600BB2B04 /* DWARFDIECollection.h */, + 260C89D310F57C5600BB2B04 /* DWARFFormValue.cpp */, + 260C89D410F57C5600BB2B04 /* DWARFFormValue.h */, + 26A0DA4D140F721D006DA411 /* HashedNameToDIE.h */, + 26109B3B1155D70100CC3529 /* LogChannelDWARF.cpp */, + 26109B3C1155D70100CC3529 /* LogChannelDWARF.h */, + 2618D9EA12406FE600F2B8FE /* NameToDIE.cpp */, + 2618D957124056C700F2B8FE /* NameToDIE.h */, + 260C89D910F57C5600BB2B04 /* SymbolFileDWARF.cpp */, + 260C89DA10F57C5600BB2B04 /* SymbolFileDWARF.h */, + 260C89DB10F57C5600BB2B04 /* SymbolFileDWARFDebugMap.cpp */, + 260C89DC10F57C5600BB2B04 /* SymbolFileDWARFDebugMap.h */, + 26B8B42212EEC52A00A831B2 /* UniqueDWARFASTType.h */, + 26B8B42312EEC52A00A831B2 /* UniqueDWARFASTType.cpp */, + ); + path = DWARF; + sourceTree = ""; + }; + 260C89DD10F57C5600BB2B04 /* Symtab */ = { + isa = PBXGroup; + children = ( + 260C89DE10F57C5600BB2B04 /* SymbolFileSymtab.cpp */, + 260C89DF10F57C5600BB2B04 /* SymbolFileSymtab.h */, + ); + path = Symtab; + sourceTree = ""; + }; + 260C89E010F57C5600BB2B04 /* SymbolVendor */ = { + isa = PBXGroup; + children = ( + 2635878D17822E56004C30BA /* ELF */, + 260C89E110F57C5600BB2B04 /* MacOSX */, + ); + path = SymbolVendor; + sourceTree = ""; + }; + 260C89E110F57C5600BB2B04 /* MacOSX */ = { + isa = PBXGroup; + children = ( + 260C89E210F57C5600BB2B04 /* SymbolVendorMacOSX.cpp */, + 260C89E310F57C5600BB2B04 /* SymbolVendorMacOSX.h */, + ); + path = MacOSX; + sourceTree = ""; + }; + 2611FEEE142D83060017FEA3 /* interface */ = { + isa = PBXGroup; + children = ( + 254FBBA61A91672800BD6378 /* SBAttachInfo.i */, + 254FBB921A81AA5200BD6378 /* SBLaunchInfo.i */, + 2611FEEF142D83060017FEA3 /* SBAddress.i */, + 2611FEF0142D83060017FEA3 /* SBBlock.i */, + 2611FEF1142D83060017FEA3 /* SBBreakpoint.i */, + 2611FEF2142D83060017FEA3 /* SBBreakpointLocation.i */, + 2611FEF3142D83060017FEA3 /* SBBroadcaster.i */, + 2611FEF4142D83060017FEA3 /* SBCommandInterpreter.i */, + 2611FEF5142D83060017FEA3 /* SBCommandReturnObject.i */, + 2611FEF6142D83060017FEA3 /* SBCommunication.i */, + 2611FEF7142D83060017FEA3 /* SBCompileUnit.i */, + 2611FEF8142D83060017FEA3 /* SBData.i */, + 2611FEF9142D83060017FEA3 /* SBDebugger.i */, + 9452573616262CD000325455 /* SBDeclaration.i */, + 2611FEFA142D83060017FEA3 /* SBError.i */, + 2611FEFB142D83060017FEA3 /* SBEvent.i */, + 940B02F719DC970900AD0F52 /* SBExecutionContext.i */, + 2611FEFC142D83060017FEA3 /* SBFileSpec.i */, + 2611FEFD142D83060017FEA3 /* SBFileSpecList.i */, + 2611FEFE142D83060017FEA3 /* SBFrame.i */, + 4CE4F676162CE1E100F75CB3 /* SBExpressionOptions.i */, + 2611FEFF142D83060017FEA3 /* SBFunction.i */, + 2611FF00142D83060017FEA3 /* SBHostOS.i */, + 2611FF02142D83060017FEA3 /* SBInstruction.i */, + 2611FF03142D83060017FEA3 /* SBInstructionList.i */, + 2611FF04142D83060017FEA3 /* SBLineEntry.i */, + 2611FF05142D83060017FEA3 /* SBListener.i */, + 2611FF06142D83060017FEA3 /* SBModule.i */, + 263C493B178B61CC0070F12D /* SBModuleSpec.i */, + 262F12B8183546C900AEB384 /* SBPlatform.i */, + 2611FF07142D83060017FEA3 /* SBProcess.i */, + AF0EBBEE1859419F0059E52F /* SBQueue.i */, + AF0EBBEF1859419F0059E52F /* SBQueueItem.i */, + 2611FF08142D83060017FEA3 /* SBSection.i */, + 2611FF09142D83060017FEA3 /* SBSourceManager.i */, + 2611FF0A142D83060017FEA3 /* SBStream.i */, + 2611FF0B142D83060017FEA3 /* SBStringList.i */, + 2611FF0C142D83060017FEA3 /* SBSymbol.i */, + 2611FF0D142D83060017FEA3 /* SBSymbolContext.i */, + 2611FF0E142D83060017FEA3 /* SBSymbolContextList.i */, + 2611FF0F142D83060017FEA3 /* SBTarget.i */, + 2611FF10142D83060017FEA3 /* SBThread.i */, + 4C56543819D22FD9002E9C44 /* SBThreadPlan.i */, + 8CCB018419BA54930009FD44 /* SBThreadCollection.i */, + 2611FF11142D83060017FEA3 /* SBType.i */, + 9475C18A14E5EA1C001BFC6D /* SBTypeCategory.i */, + 9461569214E3567F003A195C /* SBTypeFilter.i */, + 9461569314E3567F003A195C /* SBTypeFormat.i */, + 9475C18B14E5F818001BFC6D /* SBTypeNameSpecifier.i */, + 9461569414E3567F003A195C /* SBTypeSummary.i */, + 9461569514E3567F003A195C /* SBTypeSynthetic.i */, + 2611FF12142D83060017FEA3 /* SBValue.i */, + 2611FF13142D83060017FEA3 /* SBValueList.i */, + 94235B9D1A8D601A00EB2EED /* SBVariablesOptions.i */, + B2A5872514313B480092BFBA /* SBWatchpoint.i */, + ); + name = interface; + path = scripts/interface; + sourceTree = SOURCE_ROOT; + }; + 26274FA414030F79006BA130 /* Darwin-Kernel */ = { + isa = PBXGroup; + children = ( + 26274FA514030F79006BA130 /* DynamicLoaderDarwinKernel.cpp */, + 26274FA614030F79006BA130 /* DynamicLoaderDarwinKernel.h */, + ); + path = "Darwin-Kernel"; + sourceTree = ""; + }; + 262D3190111B4341004E6F88 /* API */ = { + isa = PBXGroup; + children = ( + 2611FEEE142D83060017FEA3 /* interface */, + 26BC7C2510F1B3BC00F91463 /* lldb-defines.h */, + 26BC7C2610F1B3BC00F91463 /* lldb-enumerations.h */, + 26DE1E6A11616C2E00A093E2 /* lldb-forward.h */, + 26651A14133BEC76005B64B7 /* lldb-public.h */, + 26BC7C2910F1B3BC00F91463 /* lldb-types.h */, + 94145430175D7FDE00284436 /* lldb-versioning.h */, + 26B42C4C1187ABA50079C8C8 /* LLDB.h */, + 9A9830FC1125FC5800A56CB0 /* SBDefines.h */, + 26DE204211618ACA00A093E2 /* SBAddress.h */, + 26DE204411618ADA00A093E2 /* SBAddress.cpp */, + 254FBBA21A9166F100BD6378 /* SBAttachInfo.h */, + 254FBBA41A91670E00BD6378 /* SBAttachInfo.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 */, + 9A9830F71125FC5800A56CB0 /* SBCommandInterpreter.h */, + 9A9830F61125FC5800A56CB0 /* SBCommandInterpreter.cpp */, + 9A9830F91125FC5800A56CB0 /* SBCommandReturnObject.h */, + 9A9830F81125FC5800A56CB0 /* SBCommandReturnObject.cpp */, + 260223E7115F06D500A601A2 /* SBCommunication.h */, + 260223E8115F06E500A601A2 /* SBCommunication.cpp */, + 26DE205411618FB800A093E2 /* SBCompileUnit.h */, + 26DE205E1161901B00A093E2 /* SBCompileUnit.cpp */, + 9443B120140C18A90013457C /* SBData.h */, + 9443B121140C18C10013457C /* SBData.cpp */, + 9A9830FB1125FC5800A56CB0 /* SBDebugger.h */, + 9A9830FA1125FC5800A56CB0 /* SBDebugger.cpp */, + 9452573816262CEF00325455 /* SBDeclaration.h */, + 9452573916262D0200325455 /* SBDeclaration.cpp */, + 2682F286115EF3BD00CCFF99 /* SBError.h */, + 2682F284115EF3A700CCFF99 /* SBError.cpp */, + 9A9830FE1125FC5800A56CB0 /* SBEvent.h */, + 9A9830FD1125FC5800A56CB0 /* SBEvent.cpp */, + 940B02F419DC96CB00AD0F52 /* SBExecutionContext.h */, + 940B02F519DC96E700AD0F52 /* SBExecutionContext.cpp */, + 4CE4F672162C971A00F75CB3 /* SBExpressionOptions.h */, + 4CE4F674162C973F00F75CB3 /* SBExpressionOptions.cpp */, + 26022531115F27FA00A601A2 /* SBFileSpec.h */, + 26022532115F281400A601A2 /* SBFileSpec.cpp */, + 4CF52AF41428291E0051E832 /* SBFileSpecList.h */, + 4CF52AF7142829390051E832 /* SBFileSpecList.cpp */, + 9A633FE8112DCE3C001A7E43 /* SBFrame.h */, + 9A633FE7112DCE3C001A7E43 /* SBFrame.cpp */, + 26DE205211618FAC00A093E2 /* SBFunction.h */, + 26DE205C1161901400A093E2 /* SBFunction.cpp */, + 9A3576A7116E9AB700E8ED2F /* SBHostOS.h */, + 9A3576A9116E9AC700E8ED2F /* SBHostOS.cpp */, + 9AC7038D117674EB0086C050 /* SBInstruction.h */, + 9AC703AE117675410086C050 /* SBInstruction.cpp */, + 9AC7038F117675270086C050 /* SBInstructionList.h */, + 9AC703B0117675490086C050 /* SBInstructionList.cpp */, + 3392EBB71AFF402200858B9F /* SBLanguageRuntime.h */, + AF20F76C1AF18FC700751A6E /* SBLanguageRuntime.cpp */, + 254FBB961A81B03100BD6378 /* SBLaunchInfo.h */, + 254FBB941A81AA7F00BD6378 /* SBLaunchInfo.cpp */, + 26DE205811618FE700A093E2 /* SBLineEntry.h */, + 26DE20621161904200A093E2 /* SBLineEntry.cpp */, + 9A9831021125FC5800A56CB0 /* SBListener.h */, + 9A9831011125FC5800A56CB0 /* SBListener.cpp */, + 26DE204E11618E9800A093E2 /* SBModule.h */, + 26DE204C11618E7A00A093E2 /* SBModule.cpp */, + 263C4939178B50CF0070F12D /* SBModuleSpec.h */, + 263C4937178B50C40070F12D /* SBModuleSpec.cpp */, + 262F12B61835469C00AEB384 /* SBPlatform.h */, + 262F12B41835468600AEB384 /* SBPlatform.cpp */, + AF0EBBEA185941360059E52F /* SBQueue.h */, + AF0EBBE6185940FB0059E52F /* SBQueue.cpp */, + AF0EBBEB185941360059E52F /* SBQueueItem.h */, + AF0EBBE7185940FB0059E52F /* SBQueueItem.cpp */, + 9A9831041125FC5800A56CB0 /* SBProcess.h */, + 9A9831031125FC5800A56CB0 /* SBProcess.cpp */, + 26B8283C142D01E9002DBC64 /* SBSection.h */, + 26B8283F142D020F002DBC64 /* SBSection.cpp */, + 9A9831061125FC5800A56CB0 /* SBSourceManager.h */, + 9A9831051125FC5800A56CB0 /* SBSourceManager.cpp */, + 26C72C93124322890068DC16 /* SBStream.h */, + 26C72C951243229A0068DC16 /* SBStream.cpp */, + 9A357670116E7B5200E8ED2F /* SBStringList.h */, + 9A357672116E7B6400E8ED2F /* SBStringList.cpp */, + 26DE205A11618FF600A093E2 /* SBSymbol.h */, + 26DE20641161904E00A093E2 /* SBSymbol.cpp */, + 26DE204011618AB900A093E2 /* SBSymbolContext.h */, + 26DE204611618AED00A093E2 /* SBSymbolContext.cpp */, + 268F9D52123AA15200B91E9B /* SBSymbolContextList.h */, + 268F9D54123AA16600B91E9B /* SBSymbolContextList.cpp */, + 9A9831081125FC5800A56CB0 /* SBTarget.h */, + 9A9831071125FC5800A56CB0 /* SBTarget.cpp */, + 9A98310A1125FC5800A56CB0 /* SBThread.h */, + 9A9831091125FC5800A56CB0 /* SBThread.cpp */, + 8CCB018119BA4E210009FD44 /* SBThreadCollection.h */, + 8CCB017F19BA4DD00009FD44 /* SBThreadCollection.cpp */, + 4C56543419D2297A002E9C44 /* SBThreadPlan.h */, + 4C56543619D22B32002E9C44 /* SBThreadPlan.cpp */, + 2617447911685869005ADD65 /* SBType.h */, + 261744771168585B005ADD65 /* SBType.cpp */, + 9475C18514E5E9C5001BFC6D /* SBTypeCategory.h */, + 9475C18714E5E9FA001BFC6D /* SBTypeCategory.cpp */, + 23EFE388193D1ABC00E54E54 /* SBTypeEnumMember.h */, + 23EFE38A193D1AEC00E54E54 /* SBTypeEnumMember.cpp */, + 9461568614E355F2003A195C /* SBTypeFilter.h */, + 9461568A14E35621003A195C /* SBTypeFilter.cpp */, + 9461568714E355F2003A195C /* SBTypeFormat.h */, + 9461568B14E35621003A195C /* SBTypeFormat.cpp */, + 9475C18C14E5F826001BFC6D /* SBTypeNameSpecifier.h */, + 9475C18D14E5F834001BFC6D /* SBTypeNameSpecifier.cpp */, + 9461568814E355F2003A195C /* SBTypeSummary.h */, + 9461568C14E35621003A195C /* SBTypeSummary.cpp */, + 9461568914E355F2003A195C /* SBTypeSynthetic.h */, + 9461568D14E35621003A195C /* SBTypeSynthetic.cpp */, + 23059A111958B37B007B8189 /* SBUnixSignals.h */, + 23059A0F1958B319007B8189 /* SBUnixSignals.cpp */, + 9A19A6A51163BB7E00E0D453 /* SBValue.h */, + 9A19A6AD1163BB9800E0D453 /* SBValue.cpp */, + 9A357582116CFDEE00E8ED2F /* SBValueList.h */, + 9A35758D116CFE0F00E8ED2F /* SBValueList.cpp */, + 94235B9A1A8D5FD800EB2EED /* SBVariablesOptions.h */, + 94235B9B1A8D5FF300EB2EED /* SBVariablesOptions.cpp */, + B2A58721143119810092BFBA /* SBWatchpoint.h */, + B2A58723143119D50092BFBA /* SBWatchpoint.cpp */, + 3F81692D1ABB7A40001DA9DF /* SystemInitializerFull.h */, + 3F81692A1ABB7A16001DA9DF /* SystemInitializerFull.cpp */, + ); + name = API; + sourceTree = ""; + }; + 2635878D17822E56004C30BA /* ELF */ = { + isa = PBXGroup; + children = ( + 2635879017822E56004C30BA /* SymbolVendorELF.cpp */, + 2635879117822E56004C30BA /* SymbolVendorELF.h */, + ); + path = ELF; + sourceTree = ""; + }; + 263641141B34AEE200145B2F /* SysV-mips64 */ = { + isa = PBXGroup; + children = ( + 263641151B34AEE200145B2F /* ABISysV_mips64.cpp */, + 263641161B34AEE200145B2F /* ABISysV_mips64.h */, + ); + path = "SysV-mips64"; + sourceTree = ""; + }; + 2642FBA713D003B400ED6808 /* MacOSX-Kernel */ = { + isa = PBXGroup; + children = ( + AF0F6E4E1739A76D009180FE /* RegisterContextKDP_arm64.cpp */, + AF0F6E4F1739A76D009180FE /* RegisterContextKDP_arm64.h */, + 2642FBA813D003B400ED6808 /* CommunicationKDP.cpp */, + 2642FBA913D003B400ED6808 /* CommunicationKDP.h */, + 2642FBAA13D003B400ED6808 /* ProcessKDP.cpp */, + 2642FBAB13D003B400ED6808 /* ProcessKDP.h */, + 2642FBAC13D003B400ED6808 /* ProcessKDPLog.cpp */, + 2642FBAD13D003B400ED6808 /* ProcessKDPLog.h */, + 265205A213D3E3F700132FE2 /* RegisterContextKDP_arm.cpp */, + 265205A313D3E3F700132FE2 /* RegisterContextKDP_arm.h */, + 265205A413D3E3F700132FE2 /* RegisterContextKDP_i386.cpp */, + 265205A513D3E3F700132FE2 /* RegisterContextKDP_i386.h */, + 265205A613D3E3F700132FE2 /* RegisterContextKDP_x86_64.cpp */, + 265205A713D3E3F700132FE2 /* RegisterContextKDP_x86_64.h */, + 2628A4D313D4977900F5487A /* ThreadKDP.cpp */, + 2628A4D413D4977900F5487A /* ThreadKDP.h */, + ); + path = "MacOSX-Kernel"; + sourceTree = ""; + }; + 264A12F91372522000875C42 /* ARM64 */ = { + isa = PBXGroup; + children = ( + 264A12FA1372522000875C42 /* EmulateInstructionARM64.cpp */, + 264A12FB1372522000875C42 /* EmulateInstructionARM64.h */, + ); + name = ARM64; + path = source/Plugins/Instruction/ARM64; + sourceTree = SOURCE_ROOT; + }; + 264A97BC133918A30017F0BE /* GDB Server */ = { + isa = PBXGroup; + children = ( + 264A97BD133918BC0017F0BE /* PlatformRemoteGDBServer.cpp */, + 264A97BE133918BC0017F0BE /* PlatformRemoteGDBServer.h */, + ); + name = "GDB Server"; + sourceTree = ""; + }; + 264E8576159BE51A00E9D7A2 /* Resources */ = { + isa = PBXGroup; + children = ( + AF90106315AB7C5700FF120D /* lldb.1 */, + 268648C116531BF800F04704 /* com.apple.debugserver.posix.plist */, + 268648C216531BF800F04704 /* com.apple.debugserver.applist.internal.plist */, + 268648C316531BF800F04704 /* com.apple.debugserver.internal.plist */, + AFF87C8E150FF688000E1742 /* com.apple.debugserver.applist.plist */, + AFF87C8C150FF680000E1742 /* com.apple.debugserver.applist.plist */, + AFF87C8A150FF677000E1742 /* com.apple.debugserver.applist.plist */, + AFF87C86150FF669000E1742 /* com.apple.debugserver.plist */, + ); + name = Resources; + sourceTree = ""; + }; + 26579F55126A255E0007C5CB /* darwin-debug */ = { + isa = PBXGroup; + children = ( + 26368A3B126B697600E8659F /* darwin-debug.cpp */, + ); + name = "darwin-debug"; + sourceTree = ""; + }; + 265E9BE0115C2B8500D0DCCB /* debugserver */ = { + isa = PBXGroup; + children = ( + 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */, + ); + name = debugserver; + sourceTree = ""; + }; + 265E9BE2115C2BAA00D0DCCB /* Products */ = { + isa = PBXGroup; + children = ( + 26CE05A0115C31E50022F371 /* debugserver */, + 239504C51BDD3FD700963CEA /* debugserver */, + ); + name = Products; + sourceTree = ""; + }; + 2665CD0915080846002C8FAE /* install-headers */ = { + isa = PBXGroup; + children = ( + 2665CD0D15080846002C8FAE /* Makefile */, + ); + name = "install-headers"; + path = "tools/install-headers"; + sourceTree = ""; + }; + 2666ADBF1B3CB675001FAFD3 /* Hexagon-DYLD */ = { + isa = PBXGroup; + children = ( + 2666ADC11B3CB675001FAFD3 /* DynamicLoaderHexagonDYLD.cpp */, + 2666ADC21B3CB675001FAFD3 /* DynamicLoaderHexagonDYLD.h */, + 2666ADC31B3CB675001FAFD3 /* HexagonDYLDRendezvous.cpp */, + 2666ADC41B3CB675001FAFD3 /* HexagonDYLDRendezvous.h */, + ); + path = "Hexagon-DYLD"; + 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 */, + 94FE476613FC1DA8001F8475 /* finish-swig-Python-LLDB.sh */, + 94E367CC140C4EC4001C7A5A /* modify-python-lldb.py */, + 9A48A3A7124AAA5A00922451 /* python-extensions.swig */, + 944DC3481774C99000D7D884 /* python-swigsafecast.swig */, + 94E367CE140C4EEA001C7A5A /* python-typemaps.swig */, + 94005E0313F438DF001EF42D /* python-wrapper.swig */, + ); + path = Python; + sourceTree = ""; + }; + 266DFE9013FD64D200D0C574 /* OperatingSystem */ = { + isa = PBXGroup; + children = ( + AE8F624519EF3DFC00326B21 /* Go */, + 2698699715E6CBD0002415FF /* Python */, + ); + path = OperatingSystem; + sourceTree = ""; + }; + 2682F168115ED9C800CCFF99 /* Utility */ = { + isa = PBXGroup; + children = ( + 6DEC6F3A1BD66D950091ABA6 /* TaskPool.h */, + 6DEC6F381BD66D750091ABA6 /* TaskPool.cpp */, + 257E47151AA56C2000A62F81 /* ModuleCache.cpp */, + 257E47161AA56C2000A62F81 /* ModuleCache.h */, + 33064C9B1A5C7A490033D415 /* UriParser.h */, + 33064C991A5C7A330033D415 /* UriParser.cpp */, + AF9107E91685709A00DBCD3C /* ARM64_Stabs_Registers.h */, + 26CF992414428766001E4138 /* AnsiTerminal.h */, + 26F996A7119B79C300412154 /* ARM_DWARF_Registers.h */, + 26ECA04213665FED008D1F18 /* ARM_DWARF_Registers.cpp */, + 264A12FF137252C700875C42 /* ARM64_DWARF_Registers.h */, + 264A12FE137252C700875C42 /* ARM64_DWARF_Registers.cpp */, + 26F996A8119B79C300412154 /* ARM_Stabs_Registers.h */, + 264723A511FA076E00DE380C /* CleanUp.h */, + 3F81691B1ABA242B001DA9DF /* ConvertEnum.h */, + 3F8169171ABA2419001DA9DF /* ConvertEnum.cpp */, + 9481FE6B1B5F2D9200DED357 /* Either.h */, + 4C73152119B7D71700F865A4 /* Iterable.h */, + 942829541A89614000521B30 /* JSON.h */, + 942829551A89614C00521B30 /* JSON.cpp */, + 943BDEFC1AA7B2DE00789CE8 /* LLDBAssert.h */, + 943BDEFD1AA7B2F800789CE8 /* LLDBAssert.cpp */, + 26D1804416CEE12500EDFB5B /* KQueue.h */, + 26D1803C16CEBFD300EDFB5B /* KQueue.cpp */, + 3F81691C1ABA242B001DA9DF /* NameMatches.h */, + 3F8169181ABA2419001DA9DF /* NameMatches.cpp */, + 94031A9F13CF5B3D00DCFF3C /* PriorityPointerPair.h */, + 94F6C4D119C264C70049D089 /* ProcessStructReader.h */, + 2682F16B115EDA0D00CCFF99 /* PseudoTerminal.h */, + 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.cpp */, + 4CAB257C18EC9DB800BAD33E /* SafeMachO.h */, + 261B5A5211C3F2AD00AABD0A /* SharingPtr.cpp */, + 4C2FAE2E135E3A70001EDE44 /* SharedCluster.h */, + 261B5A5311C3F2AD00AABD0A /* SharingPtr.h */, + 2660D9F711922A1300958FBD /* StringExtractor.h */, + 2660D9F611922A1300958FBD /* StringExtractor.cpp */, + 2676A094119C93C8008A98EF /* StringExtractorGDBRemote.h */, + 2676A093119C93C8008A98EF /* StringExtractorGDBRemote.cpp */, + 94380B8019940B0300BFE4A8 /* StringLexer.h */, + 94380B8119940B0A00BFE4A8 /* StringLexer.cpp */, + 26D1804616CEE12C00EDFB5B /* TimeSpecTimeout.h */, + 26D1804016CEDF0700EDFB5B /* TimeSpecTimeout.cpp */, + 94EBAC8313D9EE26009BA64E /* PythonPointer.h */, + 94BA8B6E176F8CA0005A91B5 /* Range.h */, + 94BA8B6C176F8C9B005A91B5 /* Range.cpp */, + AF1FA8891A60A69500272AFC /* RegisterNumber.cpp */, + B2462249141AE62200F3D409 /* Utils.h */, + ); + name = Utility; + sourceTree = ""; + }; + 268A683C1321B505000E3FB8 /* Static */ = { + isa = PBXGroup; + children = ( + 268A683E1321B53B000E3FB8 /* DynamicLoaderStatic.h */, + 268A683D1321B53B000E3FB8 /* DynamicLoaderStatic.cpp */, + ); + path = Static; + sourceTree = ""; + }; + 2690CD181A6DC0D000E717C8 /* lldb-mi */ = { + isa = PBXGroup; + children = ( + 2669415B1A6DC2AB0063BE93 /* CMakeLists.txt */, + 2669415E1A6DC2AB0063BE93 /* lldb-Info.plist */, + 2669415F1A6DC2AB0063BE93 /* Makefile */, + 266941601A6DC2AB0063BE93 /* MICmdArgContext.cpp */, + 266941611A6DC2AB0063BE93 /* MICmdArgContext.h */, + 266941621A6DC2AB0063BE93 /* MICmdArgSet.cpp */, + 266941631A6DC2AB0063BE93 /* MICmdArgSet.h */, + 266941641A6DC2AB0063BE93 /* MICmdArgValBase.cpp */, + 266941651A6DC2AB0063BE93 /* MICmdArgValBase.h */, + 266941661A6DC2AB0063BE93 /* MICmdArgValConsume.cpp */, + 266941671A6DC2AB0063BE93 /* MICmdArgValConsume.h */, + 266941681A6DC2AB0063BE93 /* MICmdArgValFile.cpp */, + 266941691A6DC2AB0063BE93 /* MICmdArgValFile.h */, + 2669416A1A6DC2AC0063BE93 /* MICmdArgValListBase.cpp */, + 2669416B1A6DC2AC0063BE93 /* MICmdArgValListBase.h */, + 2669416C1A6DC2AC0063BE93 /* MICmdArgValListOfN.cpp */, + 2669416D1A6DC2AC0063BE93 /* MICmdArgValListOfN.h */, + 2669416E1A6DC2AC0063BE93 /* MICmdArgValNumber.cpp */, + 2669416F1A6DC2AC0063BE93 /* MICmdArgValNumber.h */, + 266941701A6DC2AC0063BE93 /* MICmdArgValOptionLong.cpp */, + 266941711A6DC2AC0063BE93 /* MICmdArgValOptionLong.h */, + 266941721A6DC2AC0063BE93 /* MICmdArgValOptionShort.cpp */, + 266941731A6DC2AC0063BE93 /* MICmdArgValOptionShort.h */, + 267DFB441B06752A00000FB7 /* MICmdArgValPrintValues.cpp */, + 267DFB451B06752A00000FB7 /* MICmdArgValPrintValues.h */, + 266941741A6DC2AC0063BE93 /* MICmdArgValString.cpp */, + 266941751A6DC2AC0063BE93 /* MICmdArgValString.h */, + 266941761A6DC2AC0063BE93 /* MICmdArgValThreadGrp.cpp */, + 266941771A6DC2AC0063BE93 /* MICmdArgValThreadGrp.h */, + 266941781A6DC2AC0063BE93 /* MICmdBase.cpp */, + 266941791A6DC2AC0063BE93 /* MICmdBase.h */, + 2669417A1A6DC2AC0063BE93 /* MICmdCmd.cpp */, + 2669417B1A6DC2AC0063BE93 /* MICmdCmd.h */, + 2669417C1A6DC2AC0063BE93 /* MICmdCmdBreak.cpp */, + 2669417D1A6DC2AC0063BE93 /* MICmdCmdBreak.h */, + 2669417E1A6DC2AC0063BE93 /* MICmdCmdData.cpp */, + 2669417F1A6DC2AC0063BE93 /* MICmdCmdData.h */, + 266941801A6DC2AC0063BE93 /* MICmdCmdEnviro.cpp */, + 266941811A6DC2AC0063BE93 /* MICmdCmdEnviro.h */, + 266941821A6DC2AC0063BE93 /* MICmdCmdExec.cpp */, + 266941831A6DC2AC0063BE93 /* MICmdCmdExec.h */, + 266941841A6DC2AC0063BE93 /* MICmdCmdFile.cpp */, + 266941851A6DC2AC0063BE93 /* MICmdCmdFile.h */, + 266941861A6DC2AC0063BE93 /* MICmdCmdGdbInfo.cpp */, + 266941871A6DC2AC0063BE93 /* MICmdCmdGdbInfo.h */, + 266941881A6DC2AC0063BE93 /* MICmdCmdGdbSet.cpp */, + 266941891A6DC2AC0063BE93 /* MICmdCmdGdbSet.h */, + AFB3D27E1AC262AB003B4B30 /* MICmdCmdGdbShow.cpp */, + AFB3D27F1AC262AB003B4B30 /* MICmdCmdGdbShow.h */, + 2669418A1A6DC2AC0063BE93 /* MICmdCmdGdbThread.cpp */, + 2669418B1A6DC2AC0063BE93 /* MICmdCmdGdbThread.h */, + 2669418C1A6DC2AC0063BE93 /* MICmdCmdMiscellanous.cpp */, + 2669418D1A6DC2AC0063BE93 /* MICmdCmdMiscellanous.h */, + 2669418E1A6DC2AC0063BE93 /* MICmdCmdStack.cpp */, + 2669418F1A6DC2AC0063BE93 /* MICmdCmdStack.h */, + 266941901A6DC2AC0063BE93 /* MICmdCmdSupportInfo.cpp */, + 266941911A6DC2AC0063BE93 /* MICmdCmdSupportInfo.h */, + 266941921A6DC2AC0063BE93 /* MICmdCmdSupportList.cpp */, + 266941931A6DC2AC0063BE93 /* MICmdCmdSupportList.h */, + 26D52C1D1A980FE300E5D2FB /* MICmdCmdSymbol.cpp */, + 26D52C1E1A980FE300E5D2FB /* MICmdCmdSymbol.h */, + 266941941A6DC2AC0063BE93 /* MICmdCmdTarget.cpp */, + 266941951A6DC2AC0063BE93 /* MICmdCmdTarget.h */, + 266941961A6DC2AC0063BE93 /* MICmdCmdThread.cpp */, + 266941971A6DC2AC0063BE93 /* MICmdCmdThread.h */, + 266941981A6DC2AC0063BE93 /* MICmdCmdTrace.cpp */, + 266941991A6DC2AC0063BE93 /* MICmdCmdTrace.h */, + 2669419A1A6DC2AC0063BE93 /* MICmdCmdVar.cpp */, + 2669419B1A6DC2AC0063BE93 /* MICmdCmdVar.h */, + 2669419C1A6DC2AC0063BE93 /* MICmdCommands.cpp */, + 2669419D1A6DC2AC0063BE93 /* MICmdCommands.h */, + 2669419E1A6DC2AC0063BE93 /* MICmdData.cpp */, + 2669419F1A6DC2AC0063BE93 /* MICmdData.h */, + 266941A01A6DC2AC0063BE93 /* MICmdFactory.cpp */, + 266941A11A6DC2AC0063BE93 /* MICmdFactory.h */, + 266941A21A6DC2AC0063BE93 /* MICmdInterpreter.cpp */, + 266941A31A6DC2AC0063BE93 /* MICmdInterpreter.h */, + 266941A41A6DC2AC0063BE93 /* MICmdInvoker.cpp */, + 266941A51A6DC2AC0063BE93 /* MICmdInvoker.h */, + 266941A61A6DC2AC0063BE93 /* MICmdMgr.cpp */, + 266941A71A6DC2AC0063BE93 /* MICmdMgr.h */, + 266941A81A6DC2AC0063BE93 /* MICmdMgrSetCmdDeleteCallback.cpp */, + 266941A91A6DC2AC0063BE93 /* MICmdMgrSetCmdDeleteCallback.h */, + 266941AA1A6DC2AC0063BE93 /* MICmnBase.cpp */, + 266941AB1A6DC2AC0063BE93 /* MICmnBase.h */, + 266941AC1A6DC2AC0063BE93 /* MICmnConfig.h */, + 266941AD1A6DC2AC0063BE93 /* MICmnLLDBBroadcaster.cpp */, + 266941AE1A6DC2AC0063BE93 /* MICmnLLDBBroadcaster.h */, + 266941AF1A6DC2AC0063BE93 /* MICmnLLDBDebugger.cpp */, + 266941B01A6DC2AC0063BE93 /* MICmnLLDBDebugger.h */, + 266941B11A6DC2AC0063BE93 /* MICmnLLDBDebuggerHandleEvents.cpp */, + 266941B21A6DC2AC0063BE93 /* MICmnLLDBDebuggerHandleEvents.h */, + 266941B31A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfo.cpp */, + 266941B41A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfo.h */, + 266941B51A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfoVarObj.cpp */, + 266941B61A6DC2AC0063BE93 /* MICmnLLDBDebugSessionInfoVarObj.h */, + 266941B71A6DC2AC0063BE93 /* MICmnLLDBProxySBValue.cpp */, + 266941B81A6DC2AC0063BE93 /* MICmnLLDBProxySBValue.h */, + 266941B91A6DC2AC0063BE93 /* MICmnLLDBUtilSBValue.cpp */, + 266941BA1A6DC2AC0063BE93 /* MICmnLLDBUtilSBValue.h */, + 266941BB1A6DC2AC0063BE93 /* MICmnLog.cpp */, + 266941BC1A6DC2AC0063BE93 /* MICmnLog.h */, + 266941BD1A6DC2AC0063BE93 /* MICmnLogMediumFile.cpp */, + 266941BE1A6DC2AC0063BE93 /* MICmnLogMediumFile.h */, + 266941BF1A6DC2AC0063BE93 /* MICmnMIOutOfBandRecord.cpp */, + 266941C01A6DC2AC0063BE93 /* MICmnMIOutOfBandRecord.h */, + 266941C11A6DC2AC0063BE93 /* MICmnMIResultRecord.cpp */, + 266941C21A6DC2AC0063BE93 /* MICmnMIResultRecord.h */, + 266941C31A6DC2AC0063BE93 /* MICmnMIValue.cpp */, + 266941C41A6DC2AC0063BE93 /* MICmnMIValue.h */, + 266941C51A6DC2AC0063BE93 /* MICmnMIValueConst.cpp */, + 266941C61A6DC2AC0063BE93 /* MICmnMIValueConst.h */, + 266941C71A6DC2AC0063BE93 /* MICmnMIValueList.cpp */, + 266941C81A6DC2AC0063BE93 /* MICmnMIValueList.h */, + 266941C91A6DC2AC0063BE93 /* MICmnMIValueResult.cpp */, + 266941CA1A6DC2AC0063BE93 /* MICmnMIValueResult.h */, + 266941CB1A6DC2AC0063BE93 /* MICmnMIValueTuple.cpp */, + 266941CC1A6DC2AC0063BE93 /* MICmnMIValueTuple.h */, + 266941CD1A6DC2AC0063BE93 /* MICmnResources.cpp */, + 266941CE1A6DC2AC0063BE93 /* MICmnResources.h */, + 266941CF1A6DC2AC0063BE93 /* MICmnStreamStderr.cpp */, + 266941D01A6DC2AC0063BE93 /* MICmnStreamStderr.h */, + 266941D11A6DC2AC0063BE93 /* MICmnStreamStdin.cpp */, + 266941D21A6DC2AC0063BE93 /* MICmnStreamStdin.h */, + 266941D71A6DC2AC0063BE93 /* MICmnStreamStdout.cpp */, + 266941D81A6DC2AC0063BE93 /* MICmnStreamStdout.h */, + 266941D91A6DC2AC0063BE93 /* MICmnThreadMgrStd.cpp */, + 266941DA1A6DC2AC0063BE93 /* MICmnThreadMgrStd.h */, + 266941DB1A6DC2AC0063BE93 /* MIDataTypes.h */, + 266941DC1A6DC2AC0063BE93 /* MIDriver.cpp */, + 266941DD1A6DC2AC0063BE93 /* MIDriver.h */, + 266941DE1A6DC2AC0063BE93 /* MIDriverBase.cpp */, + 266941DF1A6DC2AC0063BE93 /* MIDriverBase.h */, + 266941E01A6DC2AC0063BE93 /* MIDriverMain.cpp */, + 266941E11A6DC2AC0063BE93 /* MIDriverMgr.cpp */, + 266941E21A6DC2AC0063BE93 /* MIDriverMgr.h */, + 266941E31A6DC2AC0063BE93 /* MIReadMe.txt */, + 266941E41A6DC2AC0063BE93 /* MIUtilDateTimeStd.cpp */, + 266941E51A6DC2AC0063BE93 /* MIUtilDateTimeStd.h */, + 266941E61A6DC2AC0063BE93 /* MIUtilDebug.cpp */, + 266941E71A6DC2AC0063BE93 /* MIUtilDebug.h */, + 266941E81A6DC2AC0063BE93 /* MIUtilFileStd.cpp */, + 266941E91A6DC2AC0063BE93 /* MIUtilFileStd.h */, + 266941EA1A6DC2AC0063BE93 /* MIUtilMapIdToVariant.cpp */, + 266941EB1A6DC2AC0063BE93 /* MIUtilMapIdToVariant.h */, + 49684D781BAB37E400E6D5D5 /* MIUtilParse.cpp */, + 49684D791BAB37E400E6D5D5 /* MIUtilParse.h */, + 266941EC1A6DC2AC0063BE93 /* MIUtilSingletonBase.h */, + 266941ED1A6DC2AC0063BE93 /* MIUtilSingletonHelper.h */, + 266941EE1A6DC2AC0063BE93 /* MIUtilString.cpp */, + 266941EF1A6DC2AC0063BE93 /* MIUtilString.h */, + 266941F81A6DC2AC0063BE93 /* MIUtilThreadBaseStd.cpp */, + 266941F91A6DC2AC0063BE93 /* MIUtilThreadBaseStd.h */, + 266941FA1A6DC2AC0063BE93 /* MIUtilVariant.cpp */, + 266941FB1A6DC2AC0063BE93 /* MIUtilVariant.h */, + 266941FC1A6DC2AC0063BE93 /* Platform.cpp */, + 266941FD1A6DC2AC0063BE93 /* Platform.h */, + ); + path = "lldb-mi"; + sourceTree = ""; + }; + 2692BA12136610C100F9E14D /* InstEmulation */ = { + isa = PBXGroup; + children = ( + 2692BA13136610C100F9E14D /* UnwindAssemblyInstEmulation.cpp */, + 2692BA14136610C100F9E14D /* UnwindAssemblyInstEmulation.h */, + ); + path = InstEmulation; + sourceTree = ""; + }; + 2692BA17136611CD00F9E14D /* x86 */ = { + isa = PBXGroup; + children = ( + 263E949D13661AE400E7D1CE /* UnwindAssembly-x86.cpp */, + 263E949E13661AE400E7D1CE /* UnwindAssembly-x86.h */, + ); + path = x86; + sourceTree = ""; + }; + 2694E99814FC0BB30076DE67 /* FreeBSD */ = { + isa = PBXGroup; + children = ( + 2694E99A14FC0BB30076DE67 /* PlatformFreeBSD.cpp */, + 2694E99B14FC0BB30076DE67 /* PlatformFreeBSD.h */, + ); + path = FreeBSD; + sourceTree = ""; + }; + 2694E99F14FC0BBD0076DE67 /* Linux */ = { + isa = PBXGroup; + children = ( + 2694E9A114FC0BBD0076DE67 /* PlatformLinux.cpp */, + 2694E9A214FC0BBD0076DE67 /* PlatformLinux.h */, + ); + path = Linux; + sourceTree = ""; + }; + 2698699715E6CBD0002415FF /* Python */ = { + isa = PBXGroup; + children = ( + 2698699815E6CBD0002415FF /* OperatingSystemPython.cpp */, + 2698699915E6CBD0002415FF /* OperatingSystemPython.h */, + ); + path = Python; + sourceTree = ""; + }; + 26A3B4AB1181454800381BC2 /* BSD-Archive */ = { + isa = PBXGroup; + children = ( + 26A3B4AC1181454800381BC2 /* ObjectContainerBSDArchive.cpp */, + 26A3B4AD1181454800381BC2 /* ObjectContainerBSDArchive.h */, + ); + path = "BSD-Archive"; + sourceTree = ""; + }; + 26A527BC14E24F5F00F3A14A /* mach-core */ = { + isa = PBXGroup; + children = ( + 26A527BD14E24F5F00F3A14A /* ProcessMachCore.cpp */, + 26A527BE14E24F5F00F3A14A /* ProcessMachCore.h */, + 26A527BF14E24F5F00F3A14A /* ThreadMachCore.cpp */, + 26A527C014E24F5F00F3A14A /* ThreadMachCore.h */, + ); + path = "mach-core"; + sourceTree = ""; + }; + 26AC3F441365F40E0065C7EF /* UnwindAssembly */ = { + isa = PBXGroup; + children = ( + 2692BA12136610C100F9E14D /* InstEmulation */, + 2692BA17136611CD00F9E14D /* x86 */, + ); + path = UnwindAssembly; + sourceTree = ""; + }; + 26B4666E11A2080F00CF6220 /* Utility */ = { + isa = PBXGroup; + children = ( + E73A15A41B548EC500786197 /* GDBRemoteSignals.cpp */, + E73A15A51B548EC500786197 /* GDBRemoteSignals.h */, + B5EFAE841AE53B1D007059F3 /* RegisterContextFreeBSD_arm.cpp */, + B5EFAE851AE53B1D007059F3 /* RegisterContextFreeBSD_arm.h */, + 256CBDBE1ADD11C000BC6CDC /* RegisterContextPOSIX_arm.cpp */, + 256CBDBF1ADD11C000BC6CDC /* RegisterContextPOSIX_arm.h */, + 256CBDB61ADD107200BC6CDC /* RegisterContextLinux_arm.cpp */, + 256CBDB71ADD107200BC6CDC /* RegisterContextLinux_arm.h */, + AF20F76E1AF1902900751A6E /* RegisterContextLinux_arm64.cpp */, + AF20F76F1AF1902900751A6E /* RegisterContextLinux_arm64.h */, + 256CBDB81ADD107200BC6CDC /* RegisterContextLinux_mips64.cpp */, + 256CBDB91ADD107200BC6CDC /* RegisterContextLinux_mips64.h */, + E7723D4A1AC4A944002BA082 /* RegisterContextPOSIX_arm64.cpp */, + E7723D4B1AC4A944002BA082 /* RegisterContextPOSIX_arm64.h */, + E7723D461AC4A8C8002BA082 /* RegisterContextFreeBSD_arm64.cpp */, + E7723D471AC4A8C8002BA082 /* RegisterContextFreeBSD_arm64.h */, + B287E63E12EFAE2C00C9BEFE /* ARMDefines.h */, + B23DD24F12EDFAC1000C3894 /* ARMUtils.h */, + 26954EBC1401EE8B00294D09 /* DynamicRegisterInfo.cpp */, + 26954EBD1401EE8B00294D09 /* DynamicRegisterInfo.h */, + AF23B4D919009C66003E2A58 /* FreeBSDSignals.cpp */, + AF23B4DA19009C66003E2A58 /* FreeBSDSignals.h */, + AF1729D4182C907200E0AB97 /* HistoryThread.cpp */, + AF061F89182C980000B6A19C /* HistoryThread.h */, + AF1729D5182C907200E0AB97 /* HistoryUnwind.cpp */, + AF061F8A182C980000B6A19C /* HistoryUnwind.h */, + B28058A0139988B0002D96D0 /* InferiorCallPOSIX.cpp */, + B28058A2139988C6002D96D0 /* InferiorCallPOSIX.h */, + B2D3033612EFA5C500F84EB3 /* InstructionUtils.h */, + 23059A0519532B96007B8189 /* LinuxSignals.cpp */, + 23059A0619532B96007B8189 /* LinuxSignals.h */, + 23173F8B192BA93F005C708F /* lldb-x86-register-enums.h */, + 26B75B421AD6E29A001F7A57 /* MipsLinuxSignals.cpp */, + 26B75B431AD6E29A001F7A57 /* MipsLinuxSignals.h */, + AF33B4BC1C1FA441001B28D9 /* NetBSDSignals.cpp */, + AF33B4BD1C1FA441001B28D9 /* NetBSDSignals.h */, + 26474C9E18D0CAEC0073DEBA /* RegisterContext_mips64.h */, + AF77E0991A033D360096C0EA /* RegisterContext_powerpc.h */, + 26474C9F18D0CAEC0073DEBA /* RegisterContext_x86.h */, + 26957D9213D381C900670048 /* RegisterContextDarwin_arm.cpp */, + 26957D9313D381C900670048 /* RegisterContextDarwin_arm.h */, + AF9107EC168570D200DBCD3C /* RegisterContextDarwin_arm64.cpp */, + AF9107ED168570D200DBCD3C /* RegisterContextDarwin_arm64.h */, + 26957D9413D381C900670048 /* RegisterContextDarwin_i386.cpp */, + 26957D9513D381C900670048 /* RegisterContextDarwin_i386.h */, + 26957D9613D381C900670048 /* RegisterContextDarwin_x86_64.cpp */, + 26957D9713D381C900670048 /* RegisterContextDarwin_x86_64.h */, + 944372DA171F6B4300E57C32 /* RegisterContextDummy.cpp */, + 944372DB171F6B4300E57C32 /* RegisterContextDummy.h */, + 26474CA218D0CB070073DEBA /* RegisterContextFreeBSD_i386.cpp */, + 26474CA318D0CB070073DEBA /* RegisterContextFreeBSD_i386.h */, + 26474CA418D0CB070073DEBA /* RegisterContextFreeBSD_mips64.cpp */, + 26474CA518D0CB070073DEBA /* RegisterContextFreeBSD_mips64.h */, + AF77E09A1A033D360096C0EA /* RegisterContextFreeBSD_powerpc.cpp */, + AF77E09B1A033D360096C0EA /* RegisterContextFreeBSD_powerpc.h */, + 26474CA618D0CB070073DEBA /* RegisterContextFreeBSD_x86_64.cpp */, + 26474CA718D0CB070073DEBA /* RegisterContextFreeBSD_x86_64.h */, + AF061F85182C97ED00B6A19C /* RegisterContextHistory.cpp */, + AF061F86182C97ED00B6A19C /* RegisterContextHistory.h */, + 26474CAE18D0CB180073DEBA /* RegisterContextLinux_i386.cpp */, + 26474CAF18D0CB180073DEBA /* RegisterContextLinux_i386.h */, + 26474CB018D0CB180073DEBA /* RegisterContextLinux_x86_64.cpp */, + 26474CB118D0CB180073DEBA /* RegisterContextLinux_x86_64.h */, + AF68D2541255416E002FF25B /* RegisterContextLLDB.cpp */, + AF68D2551255416E002FF25B /* RegisterContextLLDB.h */, + 26474CB618D0CB2D0073DEBA /* RegisterContextMach_arm.cpp */, + 26474CB718D0CB2D0073DEBA /* RegisterContextMach_arm.h */, + 26474CB818D0CB2D0073DEBA /* RegisterContextMach_i386.cpp */, + 26474CB918D0CB2D0073DEBA /* RegisterContextMach_i386.h */, + 26474CBA18D0CB2D0073DEBA /* RegisterContextMach_x86_64.cpp */, + 26474CBB18D0CB2D0073DEBA /* RegisterContextMach_x86_64.h */, + AF77E09C1A033D360096C0EA /* RegisterContextMacOSXFrameBackchain.cpp */, + 26E3EEF811A994E800FBADB6 /* RegisterContextMacOSXFrameBackchain.h */, + 262D24E413FB8710002D1960 /* RegisterContextMemory.cpp */, + 262D24E513FB8710002D1960 /* RegisterContextMemory.h */, + 26474CC418D0CB5B0073DEBA /* RegisterContextPOSIX_mips64.cpp */, + 26474CC518D0CB5B0073DEBA /* RegisterContextPOSIX_mips64.h */, + AF77E09D1A033D360096C0EA /* RegisterContextPOSIX_powerpc.cpp */, + AF77E09E1A033D360096C0EA /* RegisterContextPOSIX_powerpc.h */, + 26474CC618D0CB5B0073DEBA /* RegisterContextPOSIX_x86.cpp */, + 26474CC718D0CB5B0073DEBA /* RegisterContextPOSIX_x86.h */, + 26474CC818D0CB5B0073DEBA /* RegisterContextPOSIX.h */, + 26CA979F172B1FD5005DC71B /* RegisterContextThreadMemory.cpp */, + 26CA97A0172B1FD5005DC71B /* RegisterContextThreadMemory.h */, + 23EDE3371926AAD500F6A132 /* RegisterInfoInterface.h */, + 26474CD018D0CB700073DEBA /* RegisterInfos_i386.h */, + 26474CD118D0CB710073DEBA /* RegisterInfos_mips64.h */, + AF77E09F1A033D360096C0EA /* RegisterInfos_powerpc.h */, + 26474CD218D0CB710073DEBA /* RegisterInfos_x86_64.h */, + 2615DBC81208B5FC0021781D /* StopInfoMachException.cpp */, + 2615DBC91208B5FC0021781D /* StopInfoMachException.h */, + 26F4A21A13FBA31A0064B613 /* ThreadMemory.cpp */, + 26F4A21B13FBA31A0064B613 /* ThreadMemory.h */, + AF68D32F1255A110002FF25B /* UnwindLLDB.cpp */, + AF68D3301255A110002FF25B /* UnwindLLDB.h */, + 26E3EEE311A9901300FBADB6 /* UnwindMacOSXFrameBackchain.cpp */, + 26E3EEE411A9901300FBADB6 /* UnwindMacOSXFrameBackchain.h */, + 26E3EEF711A994E800FBADB6 /* RegisterContextMacOSXFrameBackchain.cpp */, + 26474CC218D0CB5B0073DEBA /* RegisterContextMemory.cpp */, + 26474CC318D0CB5B0073DEBA /* RegisterContextMemory.h */, + ); + name = Utility; + sourceTree = ""; + }; + 26BC179F18C7F4CB00D2196D /* elf-core */ = { + isa = PBXGroup; + children = ( + 256CBDB21ADD0EFD00BC6CDC /* RegisterContextPOSIXCore_arm.cpp */, + 256CBDB31ADD0EFD00BC6CDC /* RegisterContextPOSIXCore_arm.h */, + E7723D421AC4A7FB002BA082 /* RegisterContextPOSIXCore_arm64.cpp */, + E7723D431AC4A7FB002BA082 /* RegisterContextPOSIXCore_arm64.h */, + 26BC17A218C7F4CB00D2196D /* ProcessElfCore.cpp */, + 26BC17A318C7F4CB00D2196D /* ProcessElfCore.h */, + 26BC17A418C7F4CB00D2196D /* RegisterContextPOSIXCore_mips64.cpp */, + 26BC17A518C7F4CB00D2196D /* RegisterContextPOSIXCore_mips64.h */, + AF77E0A71A033D740096C0EA /* RegisterContextPOSIXCore_powerpc.cpp */, + AF77E0A81A033D740096C0EA /* RegisterContextPOSIXCore_powerpc.h */, + 26BC17A618C7F4CB00D2196D /* RegisterContextPOSIXCore_x86_64.cpp */, + 26BC17A718C7F4CB00D2196D /* RegisterContextPOSIXCore_x86_64.h */, + 26BC17A818C7F4CB00D2196D /* ThreadElfCore.cpp */, + 26BC17A918C7F4CB00D2196D /* ThreadElfCore.h */, + ); + path = "elf-core"; + sourceTree = ""; + }; + 26BC17B318C7F4FA00D2196D /* POSIX */ = { + isa = PBXGroup; + children = ( + AF3F54AE1B3BA59C00186E73 /* CrashReason.cpp */, + AF3F54AF1B3BA59C00186E73 /* CrashReason.h */, + 26BC17BA18C7F4FA00D2196D /* ProcessMessage.cpp */, + 26BC17BB18C7F4FA00D2196D /* ProcessMessage.h */, + 26BC17BE18C7F4FA00D2196D /* ProcessPOSIXLog.cpp */, + 26BC17BF18C7F4FA00D2196D /* ProcessPOSIXLog.h */, + ); + path = POSIX; + 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 */, + 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 */, + 2671A0CD134825F6003A87BB /* ConnectionMachPort.h */, + 2671A0CF13482601003A87BB /* ConnectionMachPort.cpp */, + 266603CC1345B5C0004DA8B6 /* ConnectionSharedMemory.h */, + 266603C91345B5A8004DA8B6 /* ConnectionSharedMemory.cpp */, + 26BC7D7C10F1B77400F91463 /* ConstString.h */, + 26BC7E9410F1B85900F91463 /* ConstString.cpp */, + 2613F6C91B17B84500D4DB85 /* CxaDemangle.h */, + 2613F6C71B17B82F00D4DB85 /* CxaDemangle.cpp */, + 26BC7D5910F1B77400F91463 /* DataBuffer.h */, + 26BC7D5B10F1B77400F91463 /* DataBufferHeap.h */, + 26BC7E7210F1B85900F91463 /* DataBufferHeap.cpp */, + 26BC7D5C10F1B77400F91463 /* DataBufferMemoryMap.h */, + 26BC7E7310F1B85900F91463 /* DataBufferMemoryMap.cpp */, + 268ED0A2140FF52F00DE830F /* DataEncoder.h */, + 268ED0A4140FF54200DE830F /* DataEncoder.cpp */, + 26BC7D5A10F1B77400F91463 /* DataExtractor.h */, + 26BC7E7110F1B85900F91463 /* DataExtractor.cpp */, + 263664941140A4C10075843B /* Debugger.h */, + 263664921140A4930075843B /* Debugger.cpp */, + 26BC7D5E10F1B77400F91463 /* Disassembler.h */, + 26BC7E7610F1B85900F91463 /* Disassembler.cpp */, + 26BC7D5F10F1B77400F91463 /* dwarf.h */, + 26D9FDC612F784E60003F2EE /* EmulateInstruction.h */, + 26D9FDC812F784FD0003F2EE /* EmulateInstruction.cpp */, + 26BC7D6010F1B77400F91463 /* Error.h */, + 26BC7E7810F1B85900F91463 /* Error.cpp */, + 26BC7D6110F1B77400F91463 /* Event.h */, + 26BC7E7910F1B85900F91463 /* Event.cpp */, + 2613F6CA1B17B85400D4DB85 /* FastDemangle.h */, + 449ACC96197DE9EC008D175E /* FastDemangle.cpp */, + 26BD407D135D2AC400237D80 /* FileLineResolver.h */, + 26BD407E135D2ADF00237D80 /* FileLineResolver.cpp */, + 26BC7D6310F1B77400F91463 /* FileSpecList.h */, + 26BC7E7B10F1B85900F91463 /* FileSpecList.cpp */, + 26BC7D6410F1B77400F91463 /* Flags.h */, + 263FDE5D1A799F2D00E68013 /* FormatEntity.h */, + 263FDE5F1A79A01500E68013 /* FormatEntity.cpp */, + 26F7305F139D8FC900FD51C7 /* History.h */, + 26F73061139D8FDB00FD51C7 /* History.cpp */, + 260A63161861008E00FECF8E /* IOHandler.h */, + 260A63181861009E00FECF8E /* IOHandler.cpp */, + 26BC7D6510F1B77400F91463 /* IOStreamMacros.h */, + 26BC7D6710F1B77400F91463 /* Listener.h */, + 26BC7E7E10F1B85900F91463 /* Listener.cpp */, + 26BC7D6810F1B77400F91463 /* Log.h */, + 26BC7E7F10F1B85900F91463 /* Log.cpp */, + 3F8160A71AB9F809001DA9DF /* Logging.h */, + 3F8160A51AB9F7DD001DA9DF /* Logging.cpp */, + 26BC7D6910F1B77400F91463 /* Mangled.h */, + 26BC7E8010F1B85900F91463 /* Mangled.cpp */, + 2682100C143A59AE004BCF2D /* MappedHash.h */, + 26BC7D6A10F1B77400F91463 /* Module.h */, + 26BC7E8110F1B85900F91463 /* Module.cpp */, + 26BC7D6B10F1B77400F91463 /* ModuleChild.h */, + 26BC7E8210F1B85900F91463 /* ModuleChild.cpp */, + 26BC7D6C10F1B77400F91463 /* ModuleList.h */, + 26BC7E8310F1B85900F91463 /* ModuleList.cpp */, + 260D9B2615EC369500960137 /* ModuleSpec.h */, + 26651A15133BF9CC005B64B7 /* Opcode.h */, + 26651A17133BF9DF005B64B7 /* Opcode.cpp */, + 266DFE9813FD658300D0C574 /* OperatingSystem.h */, + 266DFE9613FD656E00D0C574 /* OperatingSystem.cpp */, + 26BC7D7010F1B77400F91463 /* PluginInterface.h */, + 26BC7D7110F1B77400F91463 /* PluginManager.h */, + 26BC7E8A10F1B85900F91463 /* PluginManager.cpp */, + 2626B6AD143E1BEA00EF935C /* RangeMap.h */, + 26C6886D137880B900407EDF /* RegisterValue.h */, + 26C6886E137880C400407EDF /* RegisterValue.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 */, + 9A4F35111368A54100823F52 /* StreamAsynchronousIO.h */, + 9A4F350F1368A51A00823F52 /* StreamAsynchronousIO.cpp */, + 2623096E13D0EFFB006381D9 /* StreamBuffer.h */, + 4C66499F14EEE7F100B0316F /* StreamCallback.h */, + 4C6649A214EEE81000B0316F /* StreamCallback.cpp */, + 26BC7D7A10F1B77400F91463 /* StreamFile.h */, + 26BC7E9210F1B85900F91463 /* StreamFile.cpp */, + 945E8D7D152F6AA80019BCCD /* StreamGDBRemote.h */, + 945E8D7F152F6AB40019BCCD /* StreamGDBRemote.cpp */, + 26BC7D7B10F1B77400F91463 /* StreamString.h */, + 26BC7E9310F1B85900F91463 /* StreamString.cpp */, + 4C626533130F1B0A00C889F6 /* StreamTee.h */, + 9A35765E116E76A700E8ED2F /* StringList.h */, + 9A35765F116E76B900E8ED2F /* StringList.cpp */, + 26F2F8FD1B156678007857DE /* StructuredData.h */, + AFEC3361194A8ABA00FF05C6 /* StructuredData.cpp */, + 26B167A41123BF5500DC7B4F /* ThreadSafeValue.h */, + 263FEDA5112CC1DA00E4C208 /* ThreadSafeSTLMap.h */, + 26BC7D7E10F1B77400F91463 /* Timer.h */, + 26BC7E9610F1B85900F91463 /* Timer.cpp */, + 94ED54A119C8A822007BE2EA /* ThreadSafeDenseMap.h */, + 9449B8031B30E0690019342B /* ThreadSafeDenseSet.h */, + 268A813F115B19D000F645B0 /* UniqueCStringMap.h */, + 26BC7D8010F1B77400F91463 /* UserID.h */, + 26BC7E9810F1B85900F91463 /* UserID.cpp */, + 9A4633DA11F65D8600955CE1 /* UserSettingsController.h */, + 9A4633DC11F65D9A00955CE1 /* UserSettingsController.cpp */, + 26C81CA411335651004BDC5A /* UUID.h */, + 26C81CA511335651004BDC5A /* UUID.cpp */, + 26BC7D8110F1B77400F91463 /* Value.h */, + 26BC7E9910F1B85900F91463 /* Value.cpp */, + 26BC7D8210F1B77400F91463 /* ValueObject.h */, + 26BC7E9A10F1B85900F91463 /* ValueObject.cpp */, + 94094C68163B6CCC0083A547 /* ValueObjectCast.h */, + 94094C69163B6CD90083A547 /* ValueObjectCast.cpp */, + 26BC7D8310F1B77400F91463 /* ValueObjectChild.h */, + 26BC7E9B10F1B85900F91463 /* ValueObjectChild.cpp */, + 26424E3E125986D30016D82C /* ValueObjectConstResult.h */, + 26424E3C125986CB0016D82C /* ValueObjectConstResult.cpp */, + AF9472701B575E5F0063D65C /* ValueObjectConstResultCast.h */, + AF94726E1B575E430063D65C /* ValueObjectConstResultCast.cpp */, + 94FA3DDD1405D4E500833217 /* ValueObjectConstResultChild.h */, + 94FA3DDF1405D50300833217 /* ValueObjectConstResultChild.cpp */, + 949ADF001406F62E004833E1 /* ValueObjectConstResultImpl.h */, + 949ADF021406F648004833E1 /* ValueObjectConstResultImpl.cpp */, + 4CD0BD0C134BFAB600CB44D4 /* ValueObjectDynamicValue.h */, + 4CD0BD0E134BFADF00CB44D4 /* ValueObjectDynamicValue.cpp */, + 26BC7D8410F1B77400F91463 /* ValueObjectList.h */, + 26BC7E9C10F1B85900F91463 /* ValueObjectList.cpp */, + 4CABA9DC134A8BA700539BDD /* ValueObjectMemory.h */, + 4CABA9DF134A8BCD00539BDD /* ValueObjectMemory.cpp */, + 2643343A1110F63C00CDB6C6 /* ValueObjectRegister.h */, + 264334381110F63100CDB6C6 /* ValueObjectRegister.cpp */, + 94B6E76013D8833C005F417F /* ValueObjectSyntheticFilter.h */, + 94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */, + 26BC7D8510F1B77400F91463 /* ValueObjectVariable.h */, + 26BC7E9D10F1B85900F91463 /* ValueObjectVariable.cpp */, + 26BC7D8610F1B77400F91463 /* VMRange.h */, + 26BC7E9E10F1B85900F91463 /* VMRange.cpp */, + ); + name = Core; + sourceTree = ""; + }; + 26BC7C4B10F1B6C100F91463 /* Symbol */ = { + isa = PBXGroup; + children = ( + 6D9AB3DE1BB2B76B003F2289 /* TypeMap.h */, + 6D9AB3DC1BB2B74E003F2289 /* TypeMap.cpp */, + 6D99A3621BBC2F3200979793 /* ArmUnwindInfo.cpp */, + 6D99A3611BBC2F1600979793 /* ArmUnwindInfo.h */, + 26BC7C5510F1B6E900F91463 /* Block.h */, + 26BC7F1310F1B8EC00F91463 /* Block.cpp */, + 26BC7C5610F1B6E900F91463 /* ClangASTContext.h */, + 26BC7F1410F1B8EC00F91463 /* ClangASTContext.cpp */, + 49D8FB3713B5594900411094 /* ClangASTImporter.h */, + 49D8FB3513B558DE00411094 /* ClangASTImporter.cpp */, + 265192C41BA8E8F8002F08F6 /* CompilerDecl.h */, + 265192C51BA8E905002F08F6 /* CompilerDecl.cpp */, + 2657AFB51B8690EC00958979 /* CompilerDeclContext.h */, + 2657AFB61B86910100958979 /* CompilerDeclContext.cpp */, + 49E45FA911F660DC008F7B28 /* CompilerType.h */, + 49E45FAD11F660FE008F7B28 /* CompilerType.cpp */, + 495B38431489714C002708C5 /* ClangExternalASTSourceCommon.h */, + 4966DCC3148978A10028481B /* ClangExternalASTSourceCommon.cpp */, + 26E6902E129C6BD500DDECD9 /* ClangExternalASTSourceCallbacks.h */, + 26E69030129C6BEF00DDECD9 /* ClangExternalASTSourceCallbacks.cpp */, + 964463ED1A330C1B00154ED8 /* CompactUnwindInfo.h */, + 964463EB1A330C0500154ED8 /* CompactUnwindInfo.cpp */, + 26BC7C5710F1B6E900F91463 /* CompileUnit.h */, + 26BC7F1510F1B8EC00F91463 /* CompileUnit.cpp */, + 23E77CDB1C20F2F2007192AD /* DebugMacros.cpp */, + 26BC7C5810F1B6E900F91463 /* Declaration.h */, + 26BC7F1610F1B8EC00F91463 /* Declaration.cpp */, + 49B01A2D15F67B1700666829 /* DeclVendor.h */, + 26BC7C5910F1B6E900F91463 /* DWARFCallFrameInfo.h */, + 26BC7F1710F1B8EC00F91463 /* DWARFCallFrameInfo.cpp */, + 26BC7C5A10F1B6E900F91463 /* Function.h */, + 26BC7F1810F1B8EC00F91463 /* Function.cpp */, + 269FF07D12494F7D00225026 /* FuncUnwinders.h */, + 961FABB81235DE1600F93A47 /* FuncUnwinders.cpp */, + AEEA340F1ACA08A000AB639D /* GoASTContext.h */, + AEFFBA7C1AC4835D0087B932 /* GoASTContext.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 */, + AF94005711C03F6500085DB9 /* SymbolVendor.cpp */, + 26BC7C6410F1B6E900F91463 /* Symtab.h */, + 26BC7F1F10F1B8EC00F91463 /* Symtab.cpp */, + 49BB309511F79450001A4197 /* TaggedASTType.h */, + 26BC7C6510F1B6E900F91463 /* Type.h */, + 26BC7F2010F1B8EC00F91463 /* Type.cpp */, + 26BC7C6610F1B6E900F91463 /* TypeList.h */, + 26BC7F2110F1B8EC00F91463 /* TypeList.cpp */, + AEEA33F61AC74FE700AB639D /* TypeSystem.h */, + AEEA34041AC88A7400AB639D /* TypeSystem.cpp */, + 269FF07F12494F8E00225026 /* UnwindPlan.h */, + 961FABB91235DE1600F93A47 /* UnwindPlan.cpp */, + 269FF08112494FC200225026 /* UnwindTable.h */, + 961FABBA1235DE1600F93A47 /* UnwindTable.cpp */, + 26BC7C6710F1B6E900F91463 /* Variable.h */, + 26BC7F2210F1B8EC00F91463 /* Variable.cpp */, + 26BC7C6810F1B6E900F91463 /* VariableList.h */, + 26BC7F2310F1B8EC00F91463 /* VariableList.cpp */, + 494260D7145790D5003C1C78 /* VerifyDecl.h */, + 494260D914579144003C1C78 /* VerifyDecl.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 */, + 4CAA56121422D96A001FFA01 /* BreakpointResolverFileRegex.h */, + 4CAA56141422D986001FFA01 /* BreakpointResolverFileRegex.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 /* Watchpoint.h */, + 26BC7E1810F1B83100F91463 /* Watchpoint.cpp */, + B27318431416AC43006039C8 /* WatchpointList.h */, + B27318411416AC12006039C8 /* WatchpointList.cpp */, + B2B7CCED15D1BFB700EEFB57 /* WatchpointOptions.h */, + B2B7CCEF15D1C20F00EEFB57 /* WatchpointOptions.cpp */, + ); + name = Breakpoint; + sourceTree = ""; + }; + 26BC7D0D10F1B71D00F91463 /* Commands */ = { + isa = PBXGroup; + children = ( + 4CA9637A11B6E99A00780E28 /* CommandObjectApropos.h */, + 4CA9637911B6E99A00780E28 /* CommandObjectApropos.cpp */, + 499F381E11A5B3F300F5CE02 /* CommandObjectArgs.h */, + 499F381F11A5B3F300F5CE02 /* CommandObjectArgs.cpp */, + 26BC7D1410F1B76300F91463 /* CommandObjectBreakpoint.h */, + 26BC7E2D10F1B84700F91463 /* CommandObjectBreakpoint.cpp */, + 9A42976111861A9F00FE05CD /* CommandObjectBreakpointCommand.h */, + 9A42976211861AA600FE05CD /* CommandObjectBreakpointCommand.cpp */, + 6D86CE9F1B440F6B00A7FBFA /* CommandObjectBugreport.h */, + 6D86CE9E1B440F6B00A7FBFA /* CommandObjectBugreport.cpp */, + 4C5DBBC711E3FEC60035160F /* CommandObjectCommands.h */, + 4C5DBBC611E3FEC60035160F /* CommandObjectCommands.cpp */, + 26BC7D1710F1B76300F91463 /* CommandObjectDisassemble.h */, + 26BC7E3010F1B84700F91463 /* CommandObjectDisassemble.cpp */, + 26BC7D1810F1B76300F91463 /* CommandObjectExpression.h */, + 26BC7E3110F1B84700F91463 /* CommandObjectExpression.cpp */, + 2672D8471189055500FF4019 /* CommandObjectFrame.h */, + 2672D8461189055500FF4019 /* CommandObjectFrame.cpp */, + 26CEB5F118762056008F575A /* CommandObjectGUI.h */, + 26CEB5F018762056008F575A /* CommandObjectGUI.cpp */, + 26BC7D1A10F1B76300F91463 /* CommandObjectHelp.h */, + 26BC7E3310F1B84700F91463 /* CommandObjectHelp.cpp */, + AFC234061AF85CE000CDE8B6 /* CommandObjectLanguage.cpp */, + AFC234071AF85CE000CDE8B6 /* CommandObjectLanguage.h */, + 264AD83911095BBD00E0B039 /* CommandObjectLog.h */, + 264AD83711095BA600E0B039 /* CommandObjectLog.cpp */, + 26BC7D1D10F1B76300F91463 /* CommandObjectMemory.h */, + 26BC7E3610F1B84700F91463 /* CommandObjectMemory.cpp */, + 26879CE51333F5750012C1F8 /* CommandObjectPlatform.h */, + 26879CE71333F58B0012C1F8 /* CommandObjectPlatform.cpp */, + 947A1D631616476A0017C8D1 /* CommandObjectPlugin.h */, + 947A1D621616476A0017C8D1 /* CommandObjectPlugin.cpp */, + 26BC7D1F10F1B76300F91463 /* CommandObjectProcess.h */, + 26BC7E3810F1B84700F91463 /* CommandObjectProcess.cpp */, + 26BC7D2010F1B76300F91463 /* CommandObjectQuit.h */, + 26BC7E3910F1B84700F91463 /* CommandObjectQuit.cpp */, + 26BC7D2210F1B76300F91463 /* CommandObjectRegister.h */, + 26BC7E3B10F1B84700F91463 /* CommandObjectRegister.cpp */, + 26BC7D2410F1B76300F91463 /* CommandObjectScript.h */, + 26BC7E3D10F1B84700F91463 /* CommandObjectScript.cpp */, + 26BC7D2710F1B76300F91463 /* CommandObjectSettings.h */, + 26BC7E4010F1B84700F91463 /* CommandObjectSettings.cpp */, + 26BC7D2910F1B76300F91463 /* CommandObjectSource.h */, + 26BC7E4210F1B84700F91463 /* CommandObjectSource.cpp */, + 26BC7D2C10F1B76300F91463 /* CommandObjectSyntax.h */, + 26BC7E4510F1B84700F91463 /* CommandObjectSyntax.cpp */, + 269416AE119A024800FF2715 /* CommandObjectTarget.h */, + 269416AD119A024800FF2715 /* CommandObjectTarget.cpp */, + 26BC7D2D10F1B76300F91463 /* CommandObjectThread.h */, + 26BC7E4610F1B84700F91463 /* CommandObjectThread.cpp */, + 9463D4CE13B179A500C230D4 /* CommandObjectType.h */, + 9463D4CC13B1798800C230D4 /* CommandObjectType.cpp */, + B296983512C2FB2B002D92C3 /* CommandObjectVersion.h */, + B296983412C2FB2B002D92C3 /* CommandObjectVersion.cpp */, + B207C4941429609C00F36E4E /* CommandObjectWatchpoint.h */, + B207C4921429607D00F36E4E /* CommandObjectWatchpoint.cpp */, + B2B7CCEC15D1BD9600EEFB57 /* CommandObjectWatchpointCommand.h */, + B2B7CCEA15D1BD6600EEFB57 /* CommandObjectWatchpointCommand.cpp */, + ); + name = Commands; + sourceTree = ""; + }; + 26BC7DBE10F1B78200F91463 /* Expression */ = { + isa = PBXGroup; + children = ( + 4C00832C1B9A58A700D5CF24 /* Expression.h */, + 4C88BC291BA3722B00AA0964 /* Expression.cpp */, + 4C29E77D1BA2403F00DFF855 /* ExpressionTypeSystemHelper.h */, + 4C00832D1B9A58A700D5CF24 /* FunctionCaller.h */, + 4C0083321B9A5DE200D5CF24 /* FunctionCaller.cpp */, + 4C00832E1B9A58A700D5CF24 /* UserExpression.h */, + 4C0083331B9A5DE200D5CF24 /* UserExpression.cpp */, + AEB0E45A1BD6EA1400B24093 /* LLVMUserExpression.h */, + AEB0E4581BD6E9F800B24093 /* LLVMUserExpression.cpp */, + 4C00833D1B9F9B8400D5CF24 /* UtilityFunction.h */, + 4C00833F1B9F9BA900D5CF24 /* UtilityFunction.cpp */, + 49A1CAC11430E21D00306AC9 /* ExpressionSourceCode.h */, + 49A1CAC31430E8BD00306AC9 /* ExpressionSourceCode.cpp */, + 4984BA171B979C08008658D4 /* ExpressionVariable.h */, + 4984BA151B979973008658D4 /* ExpressionVariable.cpp */, + 4C2479BE1BA39843009C9A7B /* ExpressionParser.h */, + 26BC7DC310F1B79500F91463 /* DWARFExpression.h */, + 26BC7ED810F1B86700F91463 /* DWARFExpression.cpp */, + 49CF9833122C718B007A0B96 /* IRDynamicChecks.h */, + 49CF9829122C70BD007A0B96 /* IRDynamicChecks.cpp */, + 49C66B1C17011A43004D1922 /* IRMemoryMap.h */, + 49DCF6FD170E6B4A0092F75E /* IRMemoryMap.cpp */, + 4C98D3E1118FB98F00E575D0 /* IRExecutionUnit.h */, + 4C98D3DB118FB96F00E575D0 /* IRExecutionUnit.cpp */, + 496B015A1406DEB100F830D5 /* IRInterpreter.h */, + 496B01581406DE8900F830D5 /* IRInterpreter.cpp */, + 49DCF6FF170E6FD90092F75E /* Materializer.h */, + 49DCF700170E70120092F75E /* Materializer.cpp */, + 4939EA8B1BD56B3700084382 /* REPL.h */, + 4939EA8C1BD56B6D00084382 /* REPL.cpp */, + ); + name = Expression; + sourceTree = ""; + }; + 26BC7DD010F1B7C100F91463 /* Host */ = { + isa = PBXGroup; + children = ( + 6D55B29B1A8CCFF000A70529 /* android */, + 33E5E8451A6736D30024ED68 /* StringConvert.h */, + 69A01E1A1236C5D400C660B5 /* common */, + 3FDFE53919A29399009756A7 /* freebsd */, + 233B009C19610D130090E598 /* linux */, + 26BC7EE510F1B88100F91463 /* MacOSX */, + 3FDFDDC4199D37BE009756A7 /* posix */, + 3FDFE53E19A2940E009756A7 /* windows */, + 26BC7DD210F1B7D500F91463 /* Condition.h */, + 266F5CBB12FC846200DFCE33 /* Config.h */, + 3FDFED1E19BA6D55009756A7 /* Debug.h */, + 26CFDCA01861638D000E63E5 /* Editline.h */, + 26BC7DD310F1B7D500F91463 /* Endian.h */, + 260C6EA013011578005E16B0 /* File.h */, + 3FDFDDC0199D34E2009756A7 /* FileCache.h */, + 3FDFDDBE199D345E009756A7 /* FileCache.cpp */, + 26FA4315130103F400E71120 /* FileSpec.h */, + 3FDFDDC1199D34E2009756A7 /* FileSystem.h */, + 26BC7DD410F1B7D500F91463 /* Host.h */, + 3FDFED1F19BA6D55009756A7 /* HostGetOpt.h */, + 3FDFE53719A2936B009756A7 /* HostInfo.h */, + 3FDFE53819A2936B009756A7 /* HostInfoBase.h */, + 3FDFED2019BA6D55009756A7 /* HostNativeThread.h */, + 3FDFED2119BA6D55009756A7 /* HostNativeThreadBase.h */, + 3FDFE57419AFABFD009756A7 /* HostProcess.h */, + 3FDFE57519AFABFD009756A7 /* HostThread.h */, + 236124A61986B50E004EFC37 /* IOObject.h */, + 26BC7DD510F1B7D500F91463 /* Mutex.h */, + 267A47F31B14116E0021A5BC /* NativeBreakpoint.h */, + 232CB60B191E00CC00EF39FC /* NativeBreakpoint.cpp */, + 267A47F41B1411750021A5BC /* NativeBreakpointList.h */, + 232CB60D191E00CC00EF39FC /* NativeBreakpointList.cpp */, + 267A47F51B14117F0021A5BC /* NativeProcessProtocol.h */, + 232CB60F191E00CC00EF39FC /* NativeProcessProtocol.cpp */, + 267A47F61B14118F0021A5BC /* NativeRegisterContext.h */, + 267A47FA1B1411C40021A5BC /* NativeRegisterContext.cpp */, + 267A47F71B14119A0021A5BC /* NativeRegisterContextRegisterInfo.h */, + 267A47FC1B1411CC0021A5BC /* NativeRegisterContextRegisterInfo.cpp */, + 267A47F81B1411A40021A5BC /* NativeThreadProtocol.h */, + 232CB611191E00CC00EF39FC /* NativeThreadProtocol.cpp */, + 267A47F91B1411AC0021A5BC /* NativeWatchpointList.h */, + 267A47FE1B1411D90021A5BC /* NativeWatchpointList.cpp */, + A36FF33D17D8E98800244D40 /* OptionParser.h */, + 260A39A519647A3A004B4130 /* Pipe.h */, + 3F5E8AF31A40D4A500A73232 /* PipeBase.h */, + 26BC7DD610F1B7D500F91463 /* Predicate.h */, + 3FDFED2219BA6D55009756A7 /* ProcessRunLock.h */, + 236124A71986B50E004EFC37 /* Socket.h */, + 26D7E45B13D5E2F9007FD12B /* SocketAddress.h */, + 26D7E45C13D5E30A007FD12B /* SocketAddress.cpp */, + 267A47F21B14115A0021A5BC /* SoftwareBreakpoint.h */, + 232CB613191E00CC00EF39FC /* SoftwareBreakpoint.cpp */, + 2689B0A4113EE3CD00A4AEDB /* Symbols.h */, + 268DA871130095D000C9483A /* Terminal.h */, + 3FDFED0D19B7D269009756A7 /* ThisThread.cpp */, + 3FDFED0919B7C8C7009756A7 /* ThisThread.h */, + 3FDFED2319BA6D55009756A7 /* ThreadLauncher.h */, + 26B4E26E112F35F700AB3F64 /* TimeValue.h */, + 267A48031B1416080021A5BC /* XML.h */, + 267A48001B1411E40021A5BC /* XML.cpp */, + ); + name = Host; + sourceTree = ""; + }; + 26BC7DDF10F1B7E200F91463 /* Interpreter */ = { + isa = PBXGroup; + children = ( + 25420ECE1A64911B009ADBCB /* OptionValueChar.h */, + 25420ECC1A6490B8009ADBCB /* OptionValueChar.cpp */, + 26BC7D5310F1B77400F91463 /* Args.h */, + 26BC7E6C10F1B85900F91463 /* Args.cpp */, + 26A4EEB511682AAC007A372A /* LLDBWrapPython.cpp */, + 4C09CB73116BD98B00C7A725 /* CommandCompletions.h */, + 4C09CB74116BD98B00C7A725 /* CommandCompletions.cpp */, + 94BA8B71176F97D4005A91B5 /* CommandHistory.h */, + 94BA8B6F176F97CE005A91B5 /* CommandHistory.cpp */, + 26BC7DE210F1B7F900F91463 /* CommandInterpreter.h */, + 26BC7F0810F1B8DD00F91463 /* CommandInterpreter.cpp */, + 26BC7DE310F1B7F900F91463 /* CommandObject.h */, + 26BC7F0910F1B8DD00F91463 /* CommandObject.cpp */, + 26DFBC51113B48D600DD817F /* CommandObjectMultiword.h */, + 26DFBC58113B48F300DD817F /* CommandObjectMultiword.cpp */, + 26DFBC52113B48D600DD817F /* CommandObjectRegexCommand.h */, + 26DFBC59113B48F300DD817F /* CommandObjectRegexCommand.cpp */, + 23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */, + 26BC7DE410F1B7F900F91463 /* CommandReturnObject.h */, + 26BC7F0A10F1B8DD00F91463 /* CommandReturnObject.cpp */, + 94005E0513F45A1B001EF42D /* embedded_interpreter.py */, + 26A7A036135E6E5300FB369E /* OptionValue.h */, + 26A7A034135E6E4200FB369E /* OptionValue.cpp */, + 260A248D15D06C4F009981B0 /* OptionValues.h */, + 2697A39415E404BA003E682C /* OptionValueArch.h */, + 2697A39215E404B1003E682C /* OptionValueArch.cpp */, + 260CC62115D04377002BF2E0 /* OptionValueArgs.h */, + 260CC63B15D0440D002BF2E0 /* OptionValueArgs.cpp */, + 260CC62215D04377002BF2E0 /* OptionValueArray.h */, + 260CC63C15D0440D002BF2E0 /* OptionValueArray.cpp */, + 260CC62315D04377002BF2E0 /* OptionValueBoolean.h */, + 260CC63D15D0440D002BF2E0 /* OptionValueBoolean.cpp */, + 260CC62515D04377002BF2E0 /* OptionValueDictionary.h */, + 260CC63F15D0440D002BF2E0 /* OptionValueDictionary.cpp */, + 260CC62615D04377002BF2E0 /* OptionValueEnumeration.h */, + 260CC64015D0440D002BF2E0 /* OptionValueEnumeration.cpp */, + 260CC62715D04377002BF2E0 /* OptionValueFileSpec.h */, + 260CC64115D0440D002BF2E0 /* OptionValueFileSpec.cpp */, + 260CC62815D04377002BF2E0 /* OptionValueFileSpecList.h */, + 260CC64215D0440D002BF2E0 /* OptionValueFileSpecLIst.cpp */, + 260CC62915D04377002BF2E0 /* OptionValueFormat.h */, + 260CC64315D0440D002BF2E0 /* OptionValueFormat.cpp */, + 264A58EB1A7DBC8C00A6B1B0 /* OptionValueFormatEntity.h */, + 264A58ED1A7DBCAD00A6B1B0 /* OptionValueFormatEntity.cpp */, + 946216BF1A97C055006E19CC /* OptionValueLanguage.h */, + 946216C11A97C080006E19CC /* OptionValueLanguage.cpp */, + 26DAED5F15D327A200E15819 /* OptionValuePathMappings.h */, + 26DAED6215D327C200E15819 /* OptionValuePathMappings.cpp */, + 260CC62415D04377002BF2E0 /* OptionValueProperties.h */, + 260CC63E15D0440D002BF2E0 /* OptionValueProperties.cpp */, + 26491E3A15E1DB8600CBFFC2 /* OptionValueRegex.h */, + 26491E3D15E1DB9F00CBFFC2 /* OptionValueRegex.cpp */, + 260CC62A15D04377002BF2E0 /* OptionValueSInt64.h */, + 260CC64415D0440D002BF2E0 /* OptionValueSInt64.cpp */, + 260CC62B15D04377002BF2E0 /* OptionValueString.h */, + 260CC64515D0440D002BF2E0 /* OptionValueString.cpp */, + 260CC62C15D04377002BF2E0 /* OptionValueUInt64.h */, + 260CC64615D0440D002BF2E0 /* OptionValueUInt64.cpp */, + 260CC62D15D04377002BF2E0 /* OptionValueUUID.h */, + 260CC64715D0440D002BF2E0 /* OptionValueUUID.cpp */, + 26BC7D6D10F1B77400F91463 /* Options.h */, + 26BC7E8610F1B85900F91463 /* Options.cpp */, + 26D5E160135BAEB0006EA0A7 /* OptionGroupArchitecture.h */, + 26D5E15E135BAEA2006EA0A7 /* OptionGroupArchitecture.cpp */, + 2686536D1370ACC600D186A3 /* OptionGroupBoolean.h */, + 2686536B1370ACB200D186A3 /* OptionGroupBoolean.cpp */, + 260E07C9136FABAC00CF21D3 /* OptionGroupFile.h */, + 260E07C7136FAB9200CF21D3 /* OptionGroupFile.cpp */, + 26BCFC4F1368ADF7006DC050 /* OptionGroupFormat.h */, + 26BCFC511368AE38006DC050 /* OptionGroupFormat.cpp */, + 26BCFC541368B4B8006DC050 /* OptionGroupOutputFile.h */, + 26BCFC531368B3E4006DC050 /* OptionGroupOutputFile.cpp */, + 26D5E161135BB040006EA0A7 /* OptionGroupPlatform.h */, + 26D5E162135BB054006EA0A7 /* OptionGroupPlatform.cpp */, + 262ED0041631FA2800879631 /* OptionGroupString.h */, + 262ED0071631FA3A00879631 /* OptionGroupString.cpp */, + 2686536E1370AE5A00D186A3 /* OptionGroupUInt64.h */, + 2686536F1370AE7200D186A3 /* OptionGroupUInt64.cpp */, + 260E07C3136FA68900CF21D3 /* OptionGroupUUID.h */, + 260E07C5136FA69E00CF21D3 /* OptionGroupUUID.cpp */, + 267C0128136880C7006E963E /* OptionGroupValueObjectDisplay.h */, + 267C012A136880DF006E963E /* OptionGroupValueObjectDisplay.cpp */, + 26ED3D6F13C5638A0017D45E /* OptionGroupVariable.h */, + 26ED3D6C13C563810017D45E /* OptionGroupVariable.cpp */, + B2462248141AD39B00F3D409 /* OptionGroupWatchpoint.h */, + B2462246141AD37D00F3D409 /* OptionGroupWatchpoint.cpp */, + 26ACEC2715E077AE00E94760 /* Property.h */, + 2640E19E15DC78FD00F23B50 /* Property.cpp */, + 26BC7DE510F1B7F900F91463 /* ScriptInterpreter.h */, + 9A82010B10FFB49800182560 /* ScriptInterpreter.cpp */, + ); + name = Interpreter; + sourceTree = ""; + }; + 26BC7DEF10F1B80200F91463 /* Target */ = { + isa = PBXGroup; + children = ( + 8CF02AE019DCBF3B00B14BE0 /* InstrumentationRuntime.h */, + 8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */, + 8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */, + 8CF02AED19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.cpp */, + 3FDFD6C3199C396E009756A7 /* FileAction.h */, + 3FDFDDBC199C3A06009756A7 /* FileAction.cpp */, + 23EDE3311926843600F6A132 /* NativeRegisterContext.h */, + 23EDE3301926839700F6A132 /* NativeRegisterContext.cpp */, + 497E7B331188ED300065CCA1 /* ABI.h */, + 497E7B9D1188F6690065CCA1 /* ABI.cpp */, + 4CB443BB1249920C00C13DC2 /* CPPLanguageRuntime.h */, + 4CB443BC1249920C00C13DC2 /* CPPLanguageRuntime.cpp */, + 26BC7DF110F1B81A00F91463 /* DynamicLoader.h */, + 26BC7E7710F1B85900F91463 /* DynamicLoader.cpp */, + 26BC7DF210F1B81A00F91463 /* ExecutionContext.h */, + 26BC7F3510F1B90C00F91463 /* ExecutionContext.cpp */, + 26DAFD9711529BC7005A394E /* ExecutionContextScope.h */, + 26BC179B18C7F2CB00D2196D /* JITLoader.h */, + 26BC179718C7F2B300D2196D /* JITLoader.cpp */, + 26BC179C18C7F2CB00D2196D /* JITLoaderList.h */, + 26BC179818C7F2B300D2196D /* JITLoaderList.cpp */, + 94B638511B8F8E53004FE1E4 /* Language.h */, + 94B638521B8F8E6C004FE1E4 /* Language.cpp */, + 4CB4430912491DDA00C13DC2 /* LanguageRuntime.h */, + 4CB4430A12491DDA00C13DC2 /* LanguageRuntime.cpp */, + 2690B36F1381D5B600ECFBAE /* Memory.h */, + 2690B3701381D5C300ECFBAE /* Memory.cpp */, + 8C2D6A54197A1EBE006989C9 /* MemoryHistory.h */, + 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */, + 2360092C193FB21500189DB1 /* MemoryRegionInfo.h */, + 4CB443F612499B6E00C13DC2 /* ObjCLanguageRuntime.h */, + 4CB443F212499B5000C13DC2 /* ObjCLanguageRuntime.cpp */, + 495BBACF119A0DE700418BEA /* PathMappingList.h */, + 495BBACB119A0DBE00418BEA /* PathMappingList.cpp */, + 264A43BB1320B3B4005B4096 /* Platform.h */, + 264A43BD1320BCEB005B4096 /* Platform.cpp */, + 233B007A1960A0440090E598 /* ProcessInfo.h */, + 233B007B1960C9E60090E598 /* ProcessInfo.cpp */, + 233B007E1960CB280090E598 /* ProcessLaunchInfo.cpp */, + 233B007919609DB40090E598 /* ProcessLaunchInfo.h */, + 26BC7DF310F1B81A00F91463 /* Process.h */, + 26BC7F3610F1B90C00F91463 /* Process.cpp */, + 260A63111860FDB600FECF8E /* Queue.h */, + AF2670381852D01E00B6CC36 /* Queue.cpp */, + 260A63121860FDBD00FECF8E /* QueueItem.h */, + AF0C112718580CD800C4C45B /* QueueItem.cpp */, + 260A63131860FDC700FECF8E /* QueueList.h */, + AF2670391852D01E00B6CC36 /* QueueList.cpp */, + 26AB54111832DC3400EADFF3 /* RegisterCheckpoint.h */, + 26BC7DF410F1B81A00F91463 /* RegisterContext.h */, + 26BC7F3710F1B90C00F91463 /* RegisterContext.cpp */, + 262173A018395D3800C52091 /* SectionLoadHistory.h */, + 262173A218395D4600C52091 /* SectionLoadHistory.cpp */, + 2618D78F1240115500F2B8FE /* SectionLoadList.h */, + 2618D7911240116900F2B8FE /* SectionLoadList.cpp */, + 26BC7DF510F1B81A00F91463 /* StackFrame.h */, + 26BC7F3810F1B90C00F91463 /* StackFrame.cpp */, + 26BC7DF610F1B81A00F91463 /* StackFrameList.h */, + 26BC7F3910F1B90C00F91463 /* StackFrameList.cpp */, + 26BC7DF710F1B81A00F91463 /* StackID.h */, + 26BC7F3A10F1B90C00F91463 /* StackID.cpp */, + 2615DB841208A9C90021781D /* StopInfo.h */, + 2615DB861208A9E40021781D /* StopInfo.cpp */, + AF81DEF91828A23F0042CF19 /* SystemRuntime.cpp */, + 26BC7DF810F1B81A00F91463 /* Target.h */, + 26BC7F3B10F1B90C00F91463 /* Target.cpp */, + 26BC7DF910F1B81A00F91463 /* TargetList.h */, + 26BC7F3C10F1B90C00F91463 /* TargetList.cpp */, + 26BC7DFA10F1B81A00F91463 /* Thread.h */, + 26BC7F3D10F1B90C00F91463 /* Thread.cpp */, + 8CCB017C19BA289B0009FD44 /* ThreadCollection.h */, + 8CCB017A19BA283D0009FD44 /* ThreadCollection.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 */, + EB8375E81B553DFE00BA907D /* ThreadPlanCallFunctionUsingABI.h */, + EB8375E61B553DE800BA907D /* ThreadPlanCallFunctionUsingABI.cpp */, + 4C7CF7E31295E10E00B4FBB5 /* ThreadPlanCallUserExpression.h */, + 4C7CF7E51295E12B00B4FBB5 /* ThreadPlanCallUserExpression.cpp */, + 4C56543219D1EFB5002E9C44 /* ThreadPlanPython.h */, + 4C56543019D1EFAA002E9C44 /* ThreadPlanPython.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 */, + 4CC2A14C128C7409001531C4 /* ThreadPlanTracer.h */, + 4CC2A148128C73ED001531C4 /* ThreadPlanTracer.cpp */, + 4C08CDEB11C81F1E001610A8 /* ThreadSpec.h */, + 4C08CDE711C81EF8001610A8 /* ThreadSpec.cpp */, + 4C00986F11500B4300F316B0 /* UnixSignals.h */, + 4C00987011500B4300F316B0 /* UnixSignals.cpp */, + 26E3EEBD11A9870400FBADB6 /* Unwind.h */, + 264D8D4E13661BCC003A368F /* UnwindAssembly.h */, + 264D8D4F13661BD7003A368F /* UnwindAssembly.cpp */, + 23F403471926C8D50046DC9B /* NativeRegisterContextRegisterInfo.h */, + 23F403481926CC250046DC9B /* NativeRegisterContextRegisterInfo.cpp */, + ); + name = Target; + sourceTree = ""; + }; + 26BC7EE510F1B88100F91463 /* MacOSX */ = { + isa = PBXGroup; + children = ( + 3FDFE56619AF9BB2009756A7 /* Config.h */, + 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 */, + 26BC7EE810F1B88F00F91463 /* Host.mm */, + 3FDFE52B19A2917A009756A7 /* HostInfoMacOSX.mm */, + 3FDFE52D19A291AF009756A7 /* HostInfoMacOSX.h */, + 3FDFED0519B7C898009756A7 /* HostThreadMacOSX.mm */, + 3FDFE56719AF9BB2009756A7 /* HostThreadMacOSX.h */, + 2689B0B5113EE47E00A4AEDB /* Symbols.cpp */, + 3FDFED0619B7C898009756A7 /* ThisThread.cpp */, + ); + name = MacOSX; + sourceTree = ""; + }; + 26BF51E91B3C754400016294 /* SysV-hexagon */ = { + isa = PBXGroup; + children = ( + 26BF51EA1B3C754400016294 /* ABISysV_hexagon.cpp */, + 26BF51EB1B3C754400016294 /* ABISysV_hexagon.h */, + ); + path = "SysV-hexagon"; + sourceTree = ""; + }; + 26BF51EE1B3C754400016294 /* SysV-i386 */ = { + isa = PBXGroup; + children = ( + 26BF51EF1B3C754400016294 /* ABISysV_i386.cpp */, + 26BF51F01B3C754400016294 /* ABISysV_i386.h */, + ); + path = "SysV-i386"; + sourceTree = ""; + }; + 26C5577E132575B6008FD8FE /* Platform */ = { + isa = PBXGroup; + children = ( + 6D55BAE61A8CD08C00A70529 /* Android */, + 2694E99814FC0BB30076DE67 /* FreeBSD */, + 264A97BC133918A30017F0BE /* GDB Server */, + 23042D0F1976C9D800621B2C /* Kalimba */, + 2694E99F14FC0BBD0076DE67 /* Linux */, + 26C5577F132575C8008FD8FE /* MacOSX */, + 26EFB6151BFE8D3E00544801 /* NetBSD */, + 9457596415349416005A9070 /* POSIX */, + 490A36BA180F0E6F00BA31F8 /* Windows */, + ); + path = Platform; + sourceTree = ""; + }; + 26C5577F132575C8008FD8FE /* MacOSX */ = { + isa = PBXGroup; + children = ( + 9455630A1BEAD0570073F75F /* PlatformAppleSimulator.cpp */, + 9455630B1BEAD0570073F75F /* PlatformAppleSimulator.h */, + AF8AD62A1BEC28A400150209 /* PlatformAppleTVSimulator.cpp */, + AF8AD62B1BEC28A400150209 /* PlatformAppleTVSimulator.h */, + AF8AD62C1BEC28A400150209 /* PlatformAppleWatchSimulator.cpp */, + AF8AD62D1BEC28A400150209 /* PlatformAppleWatchSimulator.h */, + AF254E2F170CCC33007AE5C9 /* PlatformDarwinKernel.cpp */, + AF254E30170CCC33007AE5C9 /* PlatformDarwinKernel.h */, + 2697A54B133A6305004E4240 /* PlatformDarwin.cpp */, + 2697A54C133A6305004E4240 /* PlatformDarwin.h */, + 26B7564C14F89356008D9CB3 /* PlatformiOSSimulator.cpp */, + 26B7564D14F89356008D9CB3 /* PlatformiOSSimulator.h */, + 9455630C1BEAD0570073F75F /* PlatformiOSSimulatorCoreSimulatorSupport.h */, + 9455630D1BEAD0570073F75F /* PlatformiOSSimulatorCoreSimulatorSupport.mm */, + 26C5577B132575AD008FD8FE /* PlatformMacOSX.cpp */, + 26C5577C132575AD008FD8FE /* PlatformMacOSX.h */, + AF8AD6331BEC28C400150209 /* PlatformRemoteAppleTV.cpp */, + AF8AD6341BEC28C400150209 /* PlatformRemoteAppleTV.h */, + AF8AD6351BEC28C400150209 /* PlatformRemoteAppleWatch.cpp */, + AF8AD6361BEC28C400150209 /* PlatformRemoteAppleWatch.h */, + 2675F6FE1332BE690067997B /* PlatformRemoteiOS.cpp */, + 2675F6FF1332BE690067997B /* PlatformRemoteiOS.h */, + ); + path = MacOSX; + sourceTree = ""; + }; + 26D9FDCA12F785120003F2EE /* Instruction */ = { + isa = PBXGroup; + children = ( + E778E99D1B062D1700247609 /* MIPS */, + 26D9FDCB12F785270003F2EE /* ARM */, + 264A12F91372522000875C42 /* ARM64 */, + 94A5B3941AB9FE5F00A5EE7F /* MIPS64 */, + ); + path = Instruction; + sourceTree = ""; + }; + 26D9FDCB12F785270003F2EE /* ARM */ = { + isa = PBXGroup; + children = ( + 9A22A15D135E30370024DDC3 /* EmulateInstructionARM.cpp */, + 9A22A15E135E30370024DDC3 /* EmulateInstructionARM.h */, + 9A22A15F135E30370024DDC3 /* EmulationStateARM.cpp */, + 9A22A160135E30370024DDC3 /* EmulationStateARM.h */, + ); + path = ARM; + sourceTree = ""; + }; + 26DB3E051379E7AD0080DC73 /* ABI */ = { + isa = PBXGroup; + children = ( + 26DB3E061379E7AD0080DC73 /* MacOSX-arm */, + 26DB3E0A1379E7AD0080DC73 /* MacOSX-arm64 */, + 26DB3E0E1379E7AD0080DC73 /* MacOSX-i386 */, + AF20F7621AF18F5E00751A6E /* SysV-arm */, + AF20F7631AF18F6800751A6E /* SysV-arm64 */, + 26BF51E91B3C754400016294 /* SysV-hexagon */, + 26BF51EE1B3C754400016294 /* SysV-i386 */, + 9694FA6E1B32AA35005EBB16 /* SysV-mips */, + 263641141B34AEE200145B2F /* SysV-mips64 */, + AF77E08B1A033C3E0096C0EA /* SysV-ppc */, + AF77E08C1A033C4B0096C0EA /* SysV-ppc64 */, + 26DB3E121379E7AD0080DC73 /* SysV-x86_64 */, + ); + path = ABI; + sourceTree = ""; + }; + 26DB3E061379E7AD0080DC73 /* MacOSX-arm */ = { + isa = PBXGroup; + children = ( + 26DB3E071379E7AD0080DC73 /* ABIMacOSX_arm.cpp */, + 26DB3E081379E7AD0080DC73 /* ABIMacOSX_arm.h */, + ); + path = "MacOSX-arm"; + sourceTree = ""; + }; + 26DB3E0A1379E7AD0080DC73 /* MacOSX-arm64 */ = { + isa = PBXGroup; + children = ( + 26DB3E0B1379E7AD0080DC73 /* ABIMacOSX_arm64.cpp */, + 26DB3E0C1379E7AD0080DC73 /* ABIMacOSX_arm64.h */, + ); + path = "MacOSX-arm64"; + sourceTree = ""; + }; + 26DB3E0E1379E7AD0080DC73 /* MacOSX-i386 */ = { + isa = PBXGroup; + children = ( + 26DB3E0F1379E7AD0080DC73 /* ABIMacOSX_i386.cpp */, + 26DB3E101379E7AD0080DC73 /* ABIMacOSX_i386.h */, + ); + path = "MacOSX-i386"; + sourceTree = ""; + }; + 26DB3E121379E7AD0080DC73 /* SysV-x86_64 */ = { + isa = PBXGroup; + children = ( + 26DB3E131379E7AD0080DC73 /* ABISysV_x86_64.cpp */, + 26DB3E141379E7AD0080DC73 /* ABISysV_x86_64.h */, + ); + path = "SysV-x86_64"; + sourceTree = ""; + }; + 26E152221419CACA007967D0 /* PECOFF */ = { + isa = PBXGroup; + children = ( + 26E152231419CACA007967D0 /* ObjectFilePECOFF.cpp */, + 26E152241419CACA007967D0 /* ObjectFilePECOFF.h */, + 26C7C4811BFFEA7E009BD01F /* WindowsMiniDump.cpp */, + 26C7C4821BFFEA7E009BD01F /* WindowsMiniDump.h */, + ); + path = PECOFF; + sourceTree = ""; + }; + 26EFB6151BFE8D3E00544801 /* NetBSD */ = { + isa = PBXGroup; + children = ( + 26EFB6181BFE8D3E00544801 /* PlatformNetBSD.cpp */, + 26EFB6191BFE8D3E00544801 /* PlatformNetBSD.h */, + ); + path = NetBSD; + sourceTree = ""; + }; + 26EFC4C718CFAF0D00865D87 /* JIT */ = { + isa = PBXGroup; + children = ( + 26EFC4CA18CFAF0D00865D87 /* ObjectFileJIT.cpp */, + 26EFC4CB18CFAF0D00865D87 /* ObjectFileJIT.h */, + ); + path = JIT; + sourceTree = ""; + }; + 26F006521B4DD86700B872E5 /* Windows-DYLD */ = { + isa = PBXGroup; + children = ( + 26F006541B4DD86700B872E5 /* DynamicLoaderWindowsDYLD.cpp */, + 26F006551B4DD86700B872E5 /* DynamicLoaderWindowsDYLD.h */, + ); + path = "Windows-DYLD"; + sourceTree = ""; + }; + 26F5C22410F3D950009D5894 /* Tools */ = { + isa = PBXGroup; + children = ( + E769331B1A94D10E00C73337 /* lldb-server */, + 942829BA1A89830900521B30 /* argdumper */, + 26579F55126A255E0007C5CB /* darwin-debug */, + 265E9BE0115C2B8500D0DCCB /* debugserver */, + 26F5C22510F3D956009D5894 /* Driver */, + 2665CD0915080846002C8FAE /* install-headers */, + ); + name = Tools; + sourceTree = ""; + usesTabs = 0; + }; + 26F5C22510F3D956009D5894 /* Driver */ = { + isa = PBXGroup; + children = ( + 26F5C27210F3D9E4009D5894 /* lldb-Info.plist */, + 26F5C27410F3D9E4009D5894 /* Driver.h */, + 26F5C27310F3D9E4009D5894 /* Driver.cpp */, + ); + name = Driver; + sourceTree = ""; + }; + 26F5C32810F3DF7D009D5894 /* Libraries */ = { + isa = PBXGroup; + children = ( + 26F5C39010F3FA26009D5894 /* CoreFoundation.framework */, + 265ABF6210F42EE900531910 /* DebugSymbols.framework */, + 260C876910F538E700BB2B04 /* Foundation.framework */, + 26709E311964A34000B94724 /* LaunchServices.framework */, + 26F5C32A10F3DFDD009D5894 /* libedit.dylib */, + 2689FFCA13353D7A00698AC0 /* liblldb-core.a */, + 2670F8111862B44A006B332C /* libncurses.dylib */, + 26F5C37410F3F61B009D5894 /* libobjc.dylib */, + 260157C41885F4FF00F875CF /* libpanel.dylib */, + 26F5C32410F3DF23009D5894 /* libpython.dylib */, + 26F5C32B10F3DFDD009D5894 /* libtermcap.dylib */, + 26D55234159A7DB100708D8D /* libxml2.dylib */, + 966C6B7818E6A56A0093F5EC /* libz.dylib */, + EDB919B414F6F10D008FF64B /* Security.framework */, + ); + name = Libraries; + sourceTree = ""; + usesTabs = 0; + }; + 26FFC19214FC072100087D58 /* POSIX-DYLD */ = { + isa = PBXGroup; + children = ( + 26FFC19314FC072100087D58 /* AuxVector.cpp */, + 26FFC19414FC072100087D58 /* AuxVector.h */, + 26FFC19514FC072100087D58 /* DYLDRendezvous.cpp */, + 26FFC19614FC072100087D58 /* DYLDRendezvous.h */, + 26FFC19714FC072100087D58 /* DynamicLoaderPOSIXDYLD.cpp */, + 26FFC19814FC072100087D58 /* DynamicLoaderPOSIXDYLD.h */, + ); + path = "POSIX-DYLD"; + sourceTree = ""; + }; + 3F8169261ABB73C1001DA9DF /* Initialization */ = { + isa = PBXGroup; + children = ( + 3F8169341ABB7A80001DA9DF /* SystemInitializer.h */, + 3F81692E1ABB7A6D001DA9DF /* SystemInitializer.cpp */, + 3F8169351ABB7A80001DA9DF /* SystemInitializerCommon.h */, + 3F81692F1ABB7A6D001DA9DF /* SystemInitializerCommon.cpp */, + 3F8169361ABB7A80001DA9DF /* SystemLifetimeManager.h */, + 3F8169301ABB7A6D001DA9DF /* SystemLifetimeManager.cpp */, + ); + name = Initialization; + sourceTree = ""; + }; + 3FBA69DA1B6066D20008F44A /* ScriptInterpreter */ = { + isa = PBXGroup; + children = ( + 3FBA69DC1B6066E90008F44A /* None */, + 3FBA69DB1B6066E40008F44A /* Python */, + ); + name = ScriptInterpreter; + sourceTree = ""; + }; + 3FBA69DB1B6066E40008F44A /* Python */ = { + isa = PBXGroup; + children = ( + 3FBA69E21B60672A0008F44A /* lldb-python.h */, + 3FBA69E31B60672A0008F44A /* PythonDataObjects.cpp */, + 3FBA69E41B60672A0008F44A /* PythonDataObjects.h */, + AFCB2BBB1BF577F40018B553 /* PythonExceptionState.cpp */, + AFCB2BBC1BF577F40018B553 /* PythonExceptionState.h */, + 3FBA69E51B60672A0008F44A /* ScriptInterpreterPython.cpp */, + 3FBA69E61B60672A0008F44A /* ScriptInterpreterPython.h */, + ); + name = Python; + sourceTree = ""; + }; + 3FBA69DC1B6066E90008F44A /* None */ = { + isa = PBXGroup; + children = ( + 3FBA69DD1B6067020008F44A /* ScriptInterpreterNone.cpp */, + 3FBA69DE1B6067020008F44A /* ScriptInterpreterNone.h */, + ); + name = None; + sourceTree = ""; + }; + 3FDFDDC4199D37BE009756A7 /* posix */ = { + isa = PBXGroup; + children = ( + 2579065E1BD0488D00178368 /* DomainSocket.cpp */, + 255EFF751AFABA950069F277 /* LockFilePosix.cpp */, + 30DED5DC1B4ECB17004CC508 /* MainLoopPosix.cpp */, + AFDFDFD019E34D3400EAE509 /* ConnectionFileDescriptorPosix.cpp */, + 3FDFDDC5199D37ED009756A7 /* FileSystem.cpp */, + 3FDFE53019A292F0009756A7 /* HostInfoPosix.cpp */, + 3FDFE53219A29304009756A7 /* HostInfoPosix.h */, + 3FDFE56A19AF9C44009756A7 /* HostProcessPosix.cpp */, + 3FDFE56E19AF9C5A009756A7 /* HostProcessPosix.h */, + 3FDFE56B19AF9C44009756A7 /* HostThreadPosix.cpp */, + 3FDFE56F19AF9C5A009756A7 /* HostThreadPosix.h */, + 2377C2F719E613C100737875 /* PipePosix.cpp */, + ); + name = posix; + path = source/Host/posix; + sourceTree = ""; + }; + 3FDFE53919A29399009756A7 /* freebsd */ = { + isa = PBXGroup; + children = ( + 3FDFE53C19A293CA009756A7 /* Config.h */, + 3FDFE55E19AF9B14009756A7 /* Host.cpp */, + 3FDFE53B19A293B3009756A7 /* HostInfoFreeBSD.cpp */, + 3FDFE53D19A293CA009756A7 /* HostInfoFreeBSD.h */, + 3FDFE55F19AF9B14009756A7 /* HostThreadFreeBSD.cpp */, + 3FDFE56019AF9B39009756A7 /* HostThreadFreeBSD.h */, + ); + name = freebsd; + sourceTree = ""; + }; + 3FDFE53E19A2940E009756A7 /* windows */ = { + isa = PBXGroup; + children = ( + 255EFF711AFABA4D0069F277 /* LockFileWindows.cpp */, + 255EFF6F1AFABA320069F277 /* LockFileWindows.h */, + 255EFF701AFABA320069F277 /* PipeWindows.h */, + 3FDFE54719A2946B009756A7 /* AutoHandle.h */, + 3FDFE53F19A29448009756A7 /* Condition.cpp */, + 3FDFE54819A2946B009756A7 /* editlinewin.h */, + 3FDFE54019A29448009756A7 /* EditLineWin.cpp */, + 3FDFE54119A29448009756A7 /* FileSystem.cpp */, + 3FDFE54219A29448009756A7 /* Host.cpp */, + 3FDFE54319A29448009756A7 /* HostInfoWindows.cpp */, + 3FDFE54919A2946B009756A7 /* HostInfoWindows.h */, + 3FDFE57019AF9CA0009756A7 /* HostProcessWindows.cpp */, + 3FDFE57219AF9CD3009756A7 /* HostProcessWindows.h */, + 3FDFE57119AF9CA0009756A7 /* HostThreadWindows.cpp */, + 3FDFE57319AF9CD3009756A7 /* HostThreadWindows.h */, + 3FDFE54419A29448009756A7 /* Mutex.cpp */, + 3FDFE54519A29448009756A7 /* ProcessRunLock.cpp */, + 3FDFE54A19A2946B009756A7 /* win32.h */, + 3FDFE54619A29448009756A7 /* Windows.cpp */, + 3FDFE54B19A2946B009756A7 /* windows.h */, + ); + name = windows; + sourceTree = ""; + }; + 490A36BA180F0E6F00BA31F8 /* Windows */ = { + isa = PBXGroup; + children = ( + 490A36BD180F0E6F00BA31F8 /* PlatformWindows.cpp */, + 490A36BE180F0E6F00BA31F8 /* PlatformWindows.h */, + ); + path = Windows; + sourceTree = ""; + }; + 49724D961AD6ECFA0033C538 /* RenderScript */ = { + isa = PBXGroup; + children = ( + 49724D971AD6ED390033C538 /* RenderScriptRuntime.cpp */, + 49724D981AD6ED390033C538 /* RenderScriptRuntime.h */, + ); + name = RenderScript; + sourceTree = ""; + }; + 4984BA0B1B975E9F008658D4 /* ExpressionParser */ = { + isa = PBXGroup; + children = ( + 4984BA0C1B97620B008658D4 /* Clang */, + AE44FB371BB35A2E0033EB62 /* Go */, + ); + name = ExpressionParser; + sourceTree = ""; + }; + 4984BA0C1B97620B008658D4 /* Clang */ = { + isa = PBXGroup; + children = ( + 4C98D3E0118FB98F00E575D0 /* ClangFunctionCaller.h */, + 4C98D3DA118FB96F00E575D0 /* ClangFunctionCaller.cpp */, + 26BC7DC010F1B79500F91463 /* ClangExpressionHelper.h */, + 49445E341225AB6A00C11A81 /* ClangUserExpression.h */, + 26BC7ED510F1B86700F91463 /* ClangUserExpression.cpp */, + 497C86C1122823F300B54702 /* ClangUtilityFunction.h */, + 497C86BD122823D800B54702 /* ClangUtilityFunction.cpp */, + 49D7072611B5AD03001AD875 /* ClangASTSource.h */, + 49D7072811B5AD11001AD875 /* ClangASTSource.cpp */, + 49F1A74911B338AE003ED505 /* ClangExpressionDeclMap.h */, + 49F1A74511B3388F003ED505 /* ClangExpressionDeclMap.cpp */, + 49445C2912245E5500C11A81 /* ClangExpressionParser.h */, + 49445C2512245E3600C11A81 /* ClangExpressionParser.cpp */, + 4959511B1A1BC48100F6F8FC /* ClangModulesDeclVendor.h */, + 4959511E1A1BC4BC00F6F8FC /* ClangModulesDeclVendor.cpp */, + 49D4FE821210B5FB00CDB854 /* ClangPersistentVariables.h */, + 49D4FE871210B61C00CDB854 /* ClangPersistentVariables.cpp */, + 4906FD4412F2257600A2A77C /* ASTDumper.h */, + 4906FD4012F2255300A2A77C /* ASTDumper.cpp */, + 49A8A3A311D568BF00AD3B68 /* ASTResultSynthesizer.h */, + 49A8A39F11D568A300AD3B68 /* ASTResultSynthesizer.cpp */, + 4911934B1226383D00578B7F /* ASTStructExtractor.h */, + 491193501226386000578B7F /* ASTStructExtractor.cpp */, + 49307AB111DEA4F20081F992 /* IRForTarget.h */, + 49307AAD11DEA4D90081F992 /* IRForTarget.cpp */, + 4984BA0F1B978C3E008658D4 /* ClangExpressionVariable.h */, + 4984BA0E1B978C3E008658D4 /* ClangExpressionVariable.cpp */, + ); + name = Clang; + sourceTree = ""; + }; + 4CCA643A13B40B82003BDF98 /* LanguageRuntime */ = { + isa = PBXGroup; + children = ( + AE44FB3B1BB485730033EB62 /* Go */, + 49724D961AD6ECFA0033C538 /* RenderScript */, + 4CCA643B13B40B82003BDF98 /* CPlusPlus */, + 4CCA644013B40B82003BDF98 /* ObjC */, + ); + path = LanguageRuntime; + sourceTree = ""; + }; + 4CCA643B13B40B82003BDF98 /* CPlusPlus */ = { + isa = PBXGroup; + children = ( + 4CCA643C13B40B82003BDF98 /* ItaniumABI */, + ); + path = CPlusPlus; + sourceTree = ""; + }; + 4CCA643C13B40B82003BDF98 /* ItaniumABI */ = { + isa = PBXGroup; + children = ( + 4CCA643D13B40B82003BDF98 /* ItaniumABILanguageRuntime.cpp */, + 4CCA643E13B40B82003BDF98 /* ItaniumABILanguageRuntime.h */, + ); + path = ItaniumABI; + sourceTree = ""; + }; + 4CCA644013B40B82003BDF98 /* ObjC */ = { + isa = PBXGroup; + children = ( + 4CCA644113B40B82003BDF98 /* AppleObjCRuntime */, + ); + path = ObjC; + sourceTree = ""; + }; + 4CCA644113B40B82003BDF98 /* AppleObjCRuntime */ = { + isa = PBXGroup; + children = ( + 94CD7D0719A3FB8600908B7C /* AppleObjCClassDescriptorV2.h */, + 94CD7D0819A3FBA300908B7C /* AppleObjCClassDescriptorV2.cpp */, + 4CCA644213B40B82003BDF98 /* AppleObjCRuntime.cpp */, + 4CCA644313B40B82003BDF98 /* AppleObjCRuntime.h */, + 4CCA644413B40B82003BDF98 /* AppleObjCRuntimeV1.cpp */, + 4CCA644513B40B82003BDF98 /* AppleObjCRuntimeV1.h */, + 4CCA644613B40B82003BDF98 /* AppleObjCRuntimeV2.cpp */, + 4CCA644713B40B82003BDF98 /* AppleObjCRuntimeV2.h */, + 4CCA644813B40B82003BDF98 /* AppleObjCTrampolineHandler.cpp */, + 4CCA644913B40B82003BDF98 /* AppleObjCTrampolineHandler.h */, + 94CD7D0A19A3FBC300908B7C /* AppleObjCTypeEncodingParser.h */, + 94CD7D0B19A3FBCE00908B7C /* AppleObjCTypeEncodingParser.cpp */, + 49DA65041485C942005FF180 /* AppleObjCDeclVendor.h */, + 49DA65021485C92A005FF180 /* AppleObjCDeclVendor.cpp */, + 4CCA644A13B40B82003BDF98 /* AppleThreadPlanStepThroughObjCTrampoline.cpp */, + 4CCA644B13B40B82003BDF98 /* AppleThreadPlanStepThroughObjCTrampoline.h */, + ); + path = AppleObjCRuntime; + sourceTree = ""; + }; + 4CEE62F71145F1C70064CF93 /* GDB Remote */ = { + isa = PBXGroup; + children = ( + 6D55B2931A8A808400A70529 /* GDBRemoteCommunicationServerCommon.h */, + 6D55B2941A8A808400A70529 /* GDBRemoteCommunicationServerLLGS.h */, + 6D55B2951A8A808400A70529 /* GDBRemoteCommunicationServerPlatform.h */, + 6D55B28D1A8A806200A70529 /* GDBRemoteCommunicationServerCommon.cpp */, + 6D55B28E1A8A806200A70529 /* GDBRemoteCommunicationServerLLGS.cpp */, + 6D55B28F1A8A806200A70529 /* GDBRemoteCommunicationServerPlatform.cpp */, + 2618EE5B1315B29C001D6D71 /* GDBRemoteCommunication.cpp */, + 2618EE5C1315B29C001D6D71 /* GDBRemoteCommunication.h */, + 26744EED1338317700EF765A /* GDBRemoteCommunicationClient.cpp */, + 26744EEE1338317700EF765A /* GDBRemoteCommunicationClient.h */, + 26744EEF1338317700EF765A /* GDBRemoteCommunicationServer.cpp */, + 26744EF01338317700EF765A /* GDBRemoteCommunicationServer.h */, + 2618EE5D1315B29C001D6D71 /* GDBRemoteRegisterContext.cpp */, + 2618EE5E1315B29C001D6D71 /* GDBRemoteRegisterContext.h */, + 2618EE5F1315B29C001D6D71 /* ProcessGDBRemote.cpp */, + 2618EE601315B29C001D6D71 /* ProcessGDBRemote.h */, + 2618EE611315B29C001D6D71 /* ProcessGDBRemoteLog.cpp */, + 2618EE621315B29C001D6D71 /* ProcessGDBRemoteLog.h */, + 2618EE631315B29C001D6D71 /* ThreadGDBRemote.cpp */, + 2618EE641315B29C001D6D71 /* ThreadGDBRemote.h */, + ); + name = "GDB Remote"; + path = "gdb-remote"; + sourceTree = ""; + }; + 69A01E1A1236C5D400C660B5 /* common */ = { + isa = PBXGroup; + children = ( + 2579065A1BD0488100178368 /* TCPSocket.cpp */, + 2579065B1BD0488100178368 /* UDPSocket.cpp */, + 255EFF731AFABA720069F277 /* LockFileBase.cpp */, + 250D6AE11A9679270049CC70 /* FileSystem.cpp */, + 33E5E8411A672A240024ED68 /* StringConvert.cpp */, + 25420ED11A649D88009ADBCB /* PipeBase.cpp */, + 26CFDCA2186163A4000E63E5 /* Editline.cpp */, + 260C6EA213011581005E16B0 /* File.cpp */, + 26FA43171301048600E71120 /* FileSpec.cpp */, + 69A01E1B1236C5D400C660B5 /* Condition.cpp */, + 69A01E1C1236C5D400C660B5 /* Host.cpp */, + 3FDFE53419A29327009756A7 /* HostInfoBase.cpp */, + 3FDFED2419BA6D96009756A7 /* HostNativeThreadBase.cpp */, + 3FDFED2C19C257A0009756A7 /* HostProcess.cpp */, + 3FDFED2519BA6D96009756A7 /* HostThread.cpp */, + 236124A21986B4E2004EFC37 /* IOObject.cpp */, + 69A01E1E1236C5D400C660B5 /* Mutex.cpp */, + A36FF33B17D8E94600244D40 /* OptionParser.cpp */, + AF37E10917C861F20061E18E /* ProcessRunLock.cpp */, + 236124A31986B4E2004EFC37 /* Socket.cpp */, + 69A01E1F1236C5D400C660B5 /* Symbols.cpp */, + 268DA873130095ED00C9483A /* Terminal.cpp */, + 3FDFED2619BA6D96009756A7 /* ThreadLauncher.cpp */, + 69A01E201236C5D400C660B5 /* TimeValue.cpp */, + ); + name = common; + path = source/Host/common; + sourceTree = ""; + }; + 6D55B29B1A8CCFF000A70529 /* android */ = { + isa = PBXGroup; + children = ( + 6D55BAE21A8CD06000A70529 /* Android.h */, + 6D55BAE31A8CD06000A70529 /* Config.h */, + 6D55BAE41A8CD06000A70529 /* HostInfoAndroid.h */, + 6D55BAE51A8CD06000A70529 /* ProcessLauncherAndroid.h */, + 6D55BAE01A8CD03D00A70529 /* HostInfoAndroid.cpp */, + 6D55BAE11A8CD03D00A70529 /* ProcessLauncherAndroid.cpp */, + ); + name = android; + sourceTree = ""; + }; + 6D55BAE61A8CD08C00A70529 /* Android */ = { + isa = PBXGroup; + children = ( + 25EF23751AC09AD800908DF0 /* AdbClient.cpp */, + 25EF23761AC09AD800908DF0 /* AdbClient.h */, + 6D55BAE91A8CD08C00A70529 /* PlatformAndroid.cpp */, + 6D55BAEA1A8CD08C00A70529 /* PlatformAndroid.h */, + 6D55BAEB1A8CD08C00A70529 /* PlatformAndroidRemoteGDBServer.cpp */, + 6D55BAEC1A8CD08C00A70529 /* PlatformAndroidRemoteGDBServer.h */, + ); + path = Android; + sourceTree = ""; + }; + 8C2D6A58197A1FB9006989C9 /* MemoryHistory */ = { + isa = PBXGroup; + children = ( + 8C2D6A59197A1FCD006989C9 /* asan */, + ); + path = MemoryHistory; + sourceTree = ""; + }; + 8C2D6A59197A1FCD006989C9 /* asan */ = { + isa = PBXGroup; + children = ( + 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */, + 8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */, + ); + path = asan; + sourceTree = ""; + }; + 8CF02ADD19DCBEC200B14BE0 /* InstrumentationRuntime */ = { + isa = PBXGroup; + children = ( + 8CF02ADE19DCBEE600B14BE0 /* AddressSanitizer */, + ); + path = InstrumentationRuntime; + sourceTree = ""; + }; + 8CF02ADE19DCBEE600B14BE0 /* AddressSanitizer */ = { + isa = PBXGroup; + children = ( + 8CF02AE519DCBF8400B14BE0 /* AddressSanitizerRuntime.cpp */, + 8CF02AE619DCBF8400B14BE0 /* AddressSanitizerRuntime.h */, + ); + path = AddressSanitizer; + sourceTree = ""; + }; + 942829BA1A89830900521B30 /* argdumper */ = { + isa = PBXGroup; + children = ( + 940B04D81A8984FF0045D5F7 /* argdumper.cpp */, + ); + name = argdumper; + sourceTree = ""; + }; + 945261B01B9A11BE00BF138D /* Formatters */ = { + isa = PBXGroup; + children = ( + 945261B41B9A11E800BF138D /* CxxStringTypes.h */, + 945261B31B9A11E800BF138D /* CxxStringTypes.cpp */, + 945261B61B9A11E800BF138D /* LibCxx.h */, + 945261B51B9A11E800BF138D /* LibCxx.cpp */, + 945261B71B9A11E800BF138D /* LibCxxInitializerList.cpp */, + 945261B81B9A11E800BF138D /* LibCxxList.cpp */, + 945261B91B9A11E800BF138D /* LibCxxMap.cpp */, + 945261BA1B9A11E800BF138D /* LibCxxUnorderedMap.cpp */, + 945261BB1B9A11E800BF138D /* LibCxxVector.cpp */, + 945261BD1B9A11E800BF138D /* LibStdcpp.h */, + 945261BC1B9A11E800BF138D /* LibStdcpp.cpp */, + ); + name = Formatters; + sourceTree = ""; + }; + 9457596415349416005A9070 /* POSIX */ = { + isa = PBXGroup; + children = ( + 945759651534941F005A9070 /* PlatformPOSIX.cpp */, + 945759661534941F005A9070 /* PlatformPOSIX.h */, + ); + name = POSIX; + sourceTree = ""; + }; + 949EED9D1BA74AB6008C63CF /* Formatters */ = { + isa = PBXGroup; + children = ( + 949EEDAD1BA76719008C63CF /* CF.h */, + 949EEDAC1BA76719008C63CF /* CF.cpp */, + 949EEDA21BA76571008C63CF /* Cocoa.h */, + 949EEDA11BA76571008C63CF /* Cocoa.cpp */, + 949EED9F1BA74B64008C63CF /* CoreMedia.h */, + 949EED9E1BA74B64008C63CF /* CoreMedia.cpp */, + 949EEDA41BA765B5008C63CF /* NSArray.cpp */, + 94B9E50E1BBEFDFE000A48DC /* NSDictionary.h */, + 949EEDA51BA765B5008C63CF /* NSDictionary.cpp */, + 940495781BEC497E00926025 /* NSError.cpp */, + 940495791BEC497E00926025 /* NSException.cpp */, + 949EEDA61BA765B5008C63CF /* NSIndexPath.cpp */, + 94B9E50F1BBF0069000A48DC /* NSSet.h */, + 949EEDA71BA765B5008C63CF /* NSSet.cpp */, + 94B9E5101BBF20B7000A48DC /* NSString.h */, + 94B9E5111BBF20F4000A48DC /* NSString.cpp */, + ); + name = Formatters; + sourceTree = ""; + }; + 94A5B3941AB9FE5F00A5EE7F /* MIPS64 */ = { + isa = PBXGroup; + children = ( + 94A5B3951AB9FE8300A5EE7F /* EmulateInstructionMIPS64.cpp */, + 94A5B3961AB9FE8300A5EE7F /* EmulateInstructionMIPS64.h */, + ); + name = MIPS64; + sourceTree = ""; + }; + 94B638541B8FABEA004FE1E4 /* Language */ = { + isa = PBXGroup; + children = ( + 94B6385A1B8FB109004FE1E4 /* CPlusPlus */, + AE44FB431BB4BAC20033EB62 /* Go */, + 94B638551B8FAC87004FE1E4 /* ObjC */, + 94B638601B8FB7BE004FE1E4 /* ObjCPlusPlus */, + ); + name = Language; + sourceTree = ""; + }; + 94B638551B8FAC87004FE1E4 /* ObjC */ = { + isa = PBXGroup; + children = ( + 949EED9D1BA74AB6008C63CF /* Formatters */, + 94B6385F1B8FB7A2004FE1E4 /* ObjCLanguage.h */, + 94B6385E1B8FB7A2004FE1E4 /* ObjCLanguage.cpp */, + ); + name = ObjC; + sourceTree = ""; + }; + 94B6385A1B8FB109004FE1E4 /* CPlusPlus */ = { + isa = PBXGroup; + children = ( + 945261B01B9A11BE00BF138D /* Formatters */, + 94B6385C1B8FB174004FE1E4 /* CPlusPlusLanguage.h */, + 94B6385B1B8FB174004FE1E4 /* CPlusPlusLanguage.cpp */, + ); + name = CPlusPlus; + sourceTree = ""; + }; + 94B638601B8FB7BE004FE1E4 /* ObjCPlusPlus */ = { + isa = PBXGroup; + children = ( + 94B638611B8FB7E9004FE1E4 /* ObjCPlusPlusLanguage.h */, + 94B638621B8FB7F1004FE1E4 /* ObjCPlusPlusLanguage.cpp */, + ); + name = ObjCPlusPlus; + sourceTree = ""; + }; + 94CB255616B0683B0059775D /* DataFormatters */ = { + isa = PBXGroup; + children = ( + 945261C91B9A14E000BF138D /* CXXFunctionPointer.h */, + 945261C71B9A14D300BF138D /* CXXFunctionPointer.cpp */, + 94CB256016B069800059775D /* DataVisualization.h */, + 94CB255816B069770059775D /* DataVisualization.cpp */, + 9447DE411BD5962900E67212 /* DumpValueObjectOptions.h */, + 9447DE421BD5963300E67212 /* DumpValueObjectOptions.cpp */, + 94CB257516B1D3910059775D /* FormatCache.h */, + 94CB257316B1D3870059775D /* FormatCache.cpp */, + 94CB256116B069800059775D /* FormatClasses.h */, + 94CB255916B069770059775D /* FormatClasses.cpp */, + 94CB256216B069800059775D /* FormatManager.h */, + 94CB255A16B069770059775D /* FormatManager.cpp */, + 94EE33F218643C6900CD703B /* FormattersContainer.h */, + 94D0858A1B9675A0000D24BD /* FormattersHelpers.h */, + 94D0858B1B9675B8000D24BD /* FormattersHelpers.cpp */, + 942612F51B94FFE900EF842E /* LanguageCategory.h */, + 942612F61B95000000EF842E /* LanguageCategory.cpp */, + 94F48F231A01C679005C0EC6 /* StringPrinter.h */, + 94F48F241A01C687005C0EC6 /* StringPrinter.cpp */, + 94CB256816B096F90059775D /* TypeCategory.h */, + 94CB256416B096F10059775D /* TypeCategory.cpp */, + 94CB256916B096FA0059775D /* TypeCategoryMap.h */, + 94CB256516B096F10059775D /* TypeCategoryMap.cpp */, + 94CB256A16B0A4030059775D /* TypeFormat.h */, + 94CB256D16B0A4260059775D /* TypeFormat.cpp */, + 94CB256B16B0A4030059775D /* TypeSummary.h */, + 94CB256E16B0A4260059775D /* TypeSummary.cpp */, + 94CB256C16B0A4040059775D /* TypeSynthetic.h */, + 94CB256F16B0A4270059775D /* TypeSynthetic.cpp */, + 94CD131819BA33A100DB7BED /* TypeValidator.h */, + 94CD131919BA33B400DB7BED /* TypeValidator.cpp */, + 945215DD17F639E600521C0B /* ValueObjectPrinter.h */, + 945215DE17F639EE00521C0B /* ValueObjectPrinter.cpp */, + 943B90FC1B991586007BA499 /* VectorIterator.h */, + 9418EBCB1AA9108B0058B02E /* VectorType.h */, + 9418EBCC1AA910910058B02E /* VectorType.cpp */, + ); + name = DataFormatters; + sourceTree = ""; + }; + 9694FA6E1B32AA35005EBB16 /* SysV-mips */ = { + isa = PBXGroup; + children = ( + 9694FA6F1B32AA64005EBB16 /* ABISysV_mips.cpp */, + 9694FA701B32AA64005EBB16 /* ABISysV_mips.h */, + ); + name = "SysV-mips"; + sourceTree = ""; + }; + AE44FB371BB35A2E0033EB62 /* Go */ = { + isa = PBXGroup; + children = ( + AE44FB261BB07DC60033EB62 /* GoAST.h */, + AE44FB271BB07DC60033EB62 /* GoLexer.h */, + AE44FB2A1BB07DD80033EB62 /* GoLexer.cpp */, + AE44FB281BB07DC60033EB62 /* GoParser.h */, + AE44FB2B1BB07DD80033EB62 /* GoParser.cpp */, + AE44FB291BB07DC60033EB62 /* GoUserExpression.h */, + AE44FB2C1BB07DD80033EB62 /* GoUserExpression.cpp */, + ); + name = Go; + sourceTree = ""; + }; + AE44FB3B1BB485730033EB62 /* Go */ = { + isa = PBXGroup; + children = ( + AE44FB3C1BB4858A0033EB62 /* GoLanguageRuntime.h */, + AE44FB3D1BB485960033EB62 /* GoLanguageRuntime.cpp */, + ); + name = Go; + sourceTree = ""; + }; + AE44FB431BB4BAC20033EB62 /* Go */ = { + isa = PBXGroup; + children = ( + AE44FB491BB4BB1B0033EB62 /* Formatters */, + AE44FB461BB4BB090033EB62 /* GoLanguage.h */, + AE44FB451BB4BB090033EB62 /* GoLanguage.cpp */, + ); + name = Go; + sourceTree = ""; + }; + AE44FB491BB4BB1B0033EB62 /* Formatters */ = { + isa = PBXGroup; + children = ( + AE44FB4B1BB4BB540033EB62 /* GoFormatterFunctions.h */, + AE44FB4A1BB4BB540033EB62 /* GoFormatterFunctions.cpp */, + ); + name = Formatters; + sourceTree = ""; + }; + AE8F624519EF3DFC00326B21 /* Go */ = { + isa = PBXGroup; + children = ( + AE8F624719EF3E1E00326B21 /* OperatingSystemGo.cpp */, + AE8F624819EF3E1E00326B21 /* OperatingSystemGo.h */, + ); + name = Go; + sourceTree = ""; + }; + AEC6FF9D1BE97035007882C1 /* Expression */ = { + isa = PBXGroup; + children = ( + AEC6FF9F1BE970A2007882C1 /* GoParserTest.cpp */, + ); + path = Expression; + sourceTree = ""; + }; + AF11CB34182CA85A00D9B618 /* SystemRuntime */ = { + isa = PBXGroup; + children = ( + AF11CB35182CA85A00D9B618 /* MacOSX */, + ); + path = SystemRuntime; + sourceTree = ""; + }; + AF11CB35182CA85A00D9B618 /* MacOSX */ = { + isa = PBXGroup; + children = ( + AF0E22EE18A09FB20009B7D1 /* AppleGetItemInfoHandler.cpp */, + AF0E22EF18A09FB20009B7D1 /* AppleGetItemInfoHandler.h */, + AF1F7B05189C904B0087DB9C /* AppleGetPendingItemsHandler.cpp */, + AF1F7B06189C904B0087DB9C /* AppleGetPendingItemsHandler.h */, + AF25AB24188F685C0030DEC3 /* AppleGetQueuesHandler.cpp */, + AF25AB25188F685C0030DEC3 /* AppleGetQueuesHandler.h */, + AF45FDE318A1F3AC0007051C /* AppleGetThreadItemInfoHandler.cpp */, + AF45FDE418A1F3AC0007051C /* AppleGetThreadItemInfoHandler.h */, + AF9B8F31182DB52900DA866F /* SystemRuntimeMacOSX.cpp */, + AF9B8F32182DB52900DA866F /* SystemRuntimeMacOSX.h */, + ); + path = MacOSX; + sourceTree = ""; + }; + AF20F7621AF18F5E00751A6E /* SysV-arm */ = { + isa = PBXGroup; + children = ( + AF20F7641AF18F8500751A6E /* ABISysV_arm.cpp */, + AF20F7651AF18F8500751A6E /* ABISysV_arm.h */, + ); + name = "SysV-arm"; + sourceTree = ""; + }; + AF20F7631AF18F6800751A6E /* SysV-arm64 */ = { + isa = PBXGroup; + children = ( + AF20F7681AF18F9000751A6E /* ABISysV_arm64.cpp */, + AF20F7691AF18F9000751A6E /* ABISysV_arm64.h */, + ); + name = "SysV-arm64"; + sourceTree = ""; + }; + AF2BCA6518C7EFDE005B4526 /* JITLoader */ = { + isa = PBXGroup; + children = ( + AF2BCA6718C7EFDE005B4526 /* GDB */, + ); + path = JITLoader; + sourceTree = ""; + }; + AF2BCA6718C7EFDE005B4526 /* GDB */ = { + isa = PBXGroup; + children = ( + AF2BCA6A18C7EFDE005B4526 /* JITLoaderGDB.h */, + AF2BCA6918C7EFDE005B4526 /* JITLoaderGDB.cpp */, + ); + path = GDB; + sourceTree = ""; + }; + AF77E08B1A033C3E0096C0EA /* SysV-ppc */ = { + isa = PBXGroup; + children = ( + AF77E08D1A033C700096C0EA /* ABISysV_ppc.cpp */, + AF77E08E1A033C700096C0EA /* ABISysV_ppc.h */, + ); + name = "SysV-ppc"; + sourceTree = ""; + }; + AF77E08C1A033C4B0096C0EA /* SysV-ppc64 */ = { + isa = PBXGroup; + children = ( + AF77E0911A033C7F0096C0EA /* ABISysV_ppc64.cpp */, + AF77E0921A033C7F0096C0EA /* ABISysV_ppc64.h */, + ); + name = "SysV-ppc64"; + sourceTree = ""; + }; + E769331B1A94D10E00C73337 /* lldb-server */ = { + isa = PBXGroup; + children = ( + 257906621BD5AFD000178368 /* Acceptor.cpp */, + 257906631BD5AFD000178368 /* Acceptor.h */, + 6D762BEC1B1605CD006C929D /* LLDBServerUtilities.cpp */, + 6D762BED1B1605CD006C929D /* LLDBServerUtilities.h */, + E769331D1A94D18100C73337 /* lldb-server.cpp */, + 26DC6A1C1337FECA00FF7998 /* lldb-platform.cpp */, + 26D6F3F4183E7F9300194858 /* lldb-gdbserver.cpp */, + ); + name = "lldb-server"; + sourceTree = ""; + }; + E778E99D1B062D1700247609 /* MIPS */ = { + isa = PBXGroup; + children = ( + E778E99F1B062D1700247609 /* EmulateInstructionMIPS.cpp */, + E778E9A01B062D1700247609 /* EmulateInstructionMIPS.h */, + ); + path = MIPS; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 26680202115FD0ED008E1FE4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2668020E115FD12C008E1FE4 /* lldb-defines.h in Headers */, + 2668020F115FD12C008E1FE4 /* lldb-enumerations.h in Headers */, + 26DE1E6C11616C2E00A093E2 /* lldb-forward.h in Headers */, + 26680214115FD12C008E1FE4 /* lldb-types.h in Headers */, + 94145431175E63B500284436 /* lldb-versioning.h in Headers */, + 26B42C4D1187ABA50079C8C8 /* LLDB.h in Headers */, + 26DE204311618ACA00A093E2 /* SBAddress.h in Headers */, + 26151DC31B41E4A200FF7F1C /* SharingPtr.h in Headers */, + 26DE205711618FC500A093E2 /* SBBlock.h in Headers */, + 332CCB181AFF41620034D4C4 /* SBLanguageRuntime.h in Headers */, + 26680219115FD13D008E1FE4 /* SBBreakpoint.h in Headers */, + 2668021A115FD13D008E1FE4 /* SBBreakpointLocation.h in Headers */, + 2668021B115FD13D008E1FE4 /* SBBroadcaster.h in Headers */, + 2668021D115FD13D008E1FE4 /* SBCommandInterpreter.h in Headers */, + 2668021E115FD13D008E1FE4 /* SBCommandReturnObject.h in Headers */, + 2668021F115FD13D008E1FE4 /* SBCommunication.h in Headers */, + 26DE205511618FB800A093E2 /* SBCompileUnit.h in Headers */, + 9443B123140C26AB0013457C /* SBData.h in Headers */, + 26680220115FD13D008E1FE4 /* SBDebugger.h in Headers */, + 490A966B1628C3BF00F0002E /* SBDeclaration.h in Headers */, + 254FBBA31A9166F100BD6378 /* SBAttachInfo.h in Headers */, + 26680221115FD13D008E1FE4 /* SBDefines.h in Headers */, + 8CCB018219BA4E270009FD44 /* SBThreadCollection.h in Headers */, + AF0EBBEC185941360059E52F /* SBQueue.h in Headers */, + 26680222115FD13D008E1FE4 /* SBError.h in Headers */, + 26680223115FD13D008E1FE4 /* SBEvent.h in Headers */, + AFDCDBCB19DD0F42005EA55E /* SBExecutionContext.h in Headers */, + 26680224115FD13D008E1FE4 /* SBFileSpec.h in Headers */, + 4CF52AF51428291E0051E832 /* SBFileSpecList.h in Headers */, + 26680225115FD13D008E1FE4 /* SBFrame.h in Headers */, + 26DE205311618FAC00A093E2 /* SBFunction.h in Headers */, + 9A3576A8116E9AB700E8ED2F /* SBHostOS.h in Headers */, + 9AC7038E117674FB0086C050 /* SBInstruction.h in Headers */, + 9AC70390117675270086C050 /* SBInstructionList.h in Headers */, + 26DE205911618FE700A093E2 /* SBLineEntry.h in Headers */, + 254FBB971A81B03100BD6378 /* SBLaunchInfo.h in Headers */, + AF0EBBED185941360059E52F /* SBQueueItem.h in Headers */, + 26680227115FD13D008E1FE4 /* SBListener.h in Headers */, + 26DE204F11618E9800A093E2 /* SBModule.h in Headers */, + 2668022A115FD13D008E1FE4 /* SBProcess.h in Headers */, + 26B8283D142D01E9002DBC64 /* SBSection.h in Headers */, + 2668022B115FD13D008E1FE4 /* SBSourceManager.h in Headers */, + 26C72C94124322890068DC16 /* SBStream.h in Headers */, + 9A357671116E7B5200E8ED2F /* SBStringList.h in Headers */, + 26DE205B11618FF600A093E2 /* SBSymbol.h in Headers */, + 262F12B71835469C00AEB384 /* SBPlatform.h in Headers */, + 26DE204111618AB900A093E2 /* SBSymbolContext.h in Headers */, + 268F9D53123AA15200B91E9B /* SBSymbolContextList.h in Headers */, + 2668022C115FD13D008E1FE4 /* SBTarget.h in Headers */, + 2668022E115FD13D008E1FE4 /* SBThread.h in Headers */, + 4C56543519D2297A002E9C44 /* SBThreadPlan.h in Headers */, + 263C493A178B50CF0070F12D /* SBModuleSpec.h in Headers */, + 2617447A11685869005ADD65 /* SBType.h in Headers */, + 9475C18914E5EA08001BFC6D /* SBTypeCategory.h in Headers */, + 941BCC7F14E48C4000BB969C /* SBTypeFilter.h in Headers */, + 941BCC8014E48C4000BB969C /* SBTypeFormat.h in Headers */, + 9475C18F14E5F858001BFC6D /* SBTypeNameSpecifier.h in Headers */, + 941BCC8114E48C4000BB969C /* SBTypeSummary.h in Headers */, + 23059A121958B3B2007B8189 /* SBUnixSignals.h in Headers */, + 941BCC8214E48C4000BB969C /* SBTypeSynthetic.h in Headers */, + 9A19A6AF1163BBB200E0D453 /* SBValue.h in Headers */, + 9A357583116CFDEE00E8ED2F /* SBValueList.h in Headers */, + B2A58722143119810092BFBA /* SBWatchpoint.h in Headers */, + 26D265BC136B4269002EEE45 /* lldb-public.h in Headers */, + 4CE4F673162C971A00F75CB3 /* SBExpressionOptions.h in Headers */, + 94235B9F1A8D66D600EB2EED /* SBVariablesOptions.h in Headers */, + 23EFE389193D1ABC00E54E54 /* SBTypeEnumMember.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2689FFC813353D7A00698AC0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + AF8AD6381BEC28C400150209 /* PlatformRemoteAppleTV.h in Headers */, + 26EFB61C1BFE8D3E00544801 /* PlatformNetBSD.h in Headers */, + AF33B4BF1C1FA441001B28D9 /* NetBSDSignals.h in Headers */, + 4984BA181B979C08008658D4 /* ExpressionVariable.h in Headers */, + 26C7C4841BFFEA7E009BD01F /* WindowsMiniDump.h in Headers */, + AF8AD62F1BEC28A400150209 /* PlatformAppleTVSimulator.h in Headers */, + AF8AD63A1BEC28C400150209 /* PlatformRemoteAppleWatch.h in Headers */, + 257906651BD5AFD000178368 /* Acceptor.h in Headers */, + 260A63171861008E00FECF8E /* IOHandler.h in Headers */, + AFCB2BBE1BF577F40018B553 /* PythonExceptionState.h in Headers */, + AF8AD6311BEC28A400150209 /* PlatformAppleWatchSimulator.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXLegacyTarget section */ + 2387551E1C24974600CCE8C3 /* lldb-python-test-suite */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "-u $(SRCROOT)/test/dotest.py --apple-sdk $(PLATFORM_NAME) --executable=$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/lldb --results-formatter lldbsuite.test.xunit_formatter.XunitFormatter --results-file $(BUILD_DIR)/test-results.xml --rerun-all-issues --env TERM=vt100"; + buildConfigurationList = 238755241C24974600CCE8C3 /* Build configuration list for PBXLegacyTarget "lldb-python-test-suite" */; + buildPhases = ( + ); + buildToolPath = python; + buildWorkingDirectory = "$(BUILD_DIR)"; + dependencies = ( + ); + name = "lldb-python-test-suite"; + passBuildSettingsInEnvironment = 0; + productName = "LLDB Python Test Suite"; + }; + 2687EAC51508110B00DD8C2E /* install-headers */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "$(ACTION)"; + buildConfigurationList = 2687EAC61508110B00DD8C2E /* Build configuration list for PBXLegacyTarget "install-headers" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = "$(SRCROOT)/tools/install-headers"; + dependencies = ( + ); + name = "install-headers"; + passBuildSettingsInEnvironment = 1; + productName = "install-headers"; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXNativeTarget section */ + 239504D31BDD451400963CEA /* lldb-gtest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 239504DD1BDD451400963CEA /* Build configuration list for PBXNativeTarget "lldb-gtest" */; + buildPhases = ( + 239504D01BDD451400963CEA /* Sources */, + 239504D11BDD451400963CEA /* Frameworks */, + 239504D21BDD451400963CEA /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 23AB8B6B1BDF513B008BF3B0 /* PBXTargetDependency */, + ); + name = "lldb-gtest"; + productName = "lldb-gtest"; + productReference = 239504D41BDD451400963CEA /* lldb-gtest */; + productType = "com.apple.product-type.tool"; + }; + 26579F67126A25920007C5CB /* darwin-debug */ = { + isa = PBXNativeTarget; + buildConfigurationList = 26579F6D126A25BF0007C5CB /* Build configuration list for PBXNativeTarget "darwin-debug" */; + buildPhases = ( + 26579F65126A25920007C5CB /* Sources */, + 26579F66126A25920007C5CB /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "darwin-debug"; + productName = "lldb-launcher"; + productReference = 26579F68126A25920007C5CB /* darwin-debug */; + productType = "com.apple.product-type.tool"; + }; + 26680206115FD0ED008E1FE4 /* LLDB */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2668020B115FD0EE008E1FE4 /* Build configuration list for PBXNativeTarget "LLDB" */; + buildPhases = ( + 26DC6A5813380D4300FF7998 /* Prepare Swig Bindings */, + 26680202115FD0ED008E1FE4 /* Headers */, + 26680203115FD0ED008E1FE4 /* Resources */, + 26680204115FD0ED008E1FE4 /* Sources */, + 26680205115FD0ED008E1FE4 /* Frameworks */, + 261B5A7511C3FA6F00AABD0A /* Fixup Framework Headers */, + 9A19ACE2116563A700E0D453 /* Finish swig wrapper classes (lldb) */, + 4959511A1A1ACE9500F6F8FC /* Install Clang compiler headers */, + 940B04E31A89875C0045D5F7 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 942829CE1A89842900521B30 /* PBXTargetDependency */, + 94E829C9152D33B4006F96A3 /* PBXTargetDependency */, + 2689011513353E9B00698AC0 /* PBXTargetDependency */, + 262CFC7211A450CB00946C6C /* PBXTargetDependency */, + 26368AF6126B95FA00E8659F /* PBXTargetDependency */, + ); + name = LLDB; + productName = LLDB; + productReference = 26680207115FD0ED008E1FE4 /* LLDB.framework */; + productType = "com.apple.product-type.framework"; + }; + 2689FFC913353D7A00698AC0 /* lldb-core */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2689FFD813353D7A00698AC0 /* Build configuration list for PBXNativeTarget "lldb-core" */; + buildPhases = ( + 261EECA21337D399001D193C /* Build llvm and clang */, + 2689FFC613353D7A00698AC0 /* Sources */, + 2689FFC713353D7A00698AC0 /* Frameworks */, + 2689FFC813353D7A00698AC0 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "lldb-core"; + productName = "lldb-core"; + productReference = 2689FFCA13353D7A00698AC0 /* liblldb-core.a */; + productType = "com.apple.product-type.library.dynamic"; + }; + 2690CD161A6DC0D000E717C8 /* lldb-mi */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2690CD1F1A6DC0D000E717C8 /* Build configuration list for PBXNativeTarget "lldb-mi" */; + buildPhases = ( + 2690CD131A6DC0D000E717C8 /* Sources */, + 2690CD141A6DC0D000E717C8 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 26DF74601A6DCDB300B85563 /* PBXTargetDependency */, + ); + name = "lldb-mi"; + productName = "lldb-mi"; + productReference = 2690CD171A6DC0D000E717C8 /* lldb-mi */; + productType = "com.apple.product-type.tool"; + }; + 26DC6A0F1337FE6900FF7998 /* lldb-server */ = { + isa = PBXNativeTarget; + buildConfigurationList = 26DC6A1A1337FE8B00FF7998 /* Build configuration list for PBXNativeTarget "lldb-server" */; + buildPhases = ( + 26DC6A0D1337FE6900FF7998 /* Sources */, + 26DC6A0E1337FE6900FF7998 /* Frameworks */, + 4C3326CA18B2A2B800EB5DD7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 26DC6A161337FE7300FF7998 /* PBXTargetDependency */, + ); + name = "lldb-server"; + productName = "lldb-server"; + productReference = 26DC6A101337FE6900FF7998 /* lldb-server */; + productType = "com.apple.product-type.tool"; + }; + 26F5C26910F3D9A4009D5894 /* lldb-tool */ = { + isa = PBXNativeTarget; + buildConfigurationList = 26F5C26E10F3D9C5009D5894 /* Build configuration list for PBXNativeTarget "lldb-tool" */; + buildPhases = ( + 26F5C26710F3D9A4009D5894 /* Sources */, + 26F5C26810F3D9A4009D5894 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 266803621160110D008E1FE4 /* PBXTargetDependency */, + ); + name = "lldb-tool"; + productName = lldb; + productReference = 26F5C26A10F3D9A4009D5894 /* lldb */; + productType = "com.apple.product-type.tool"; + }; + 942829BF1A89835300521B30 /* lldb-argdumper */ = { + isa = PBXNativeTarget; + buildConfigurationList = 942829C41A89835400521B30 /* Build configuration list for PBXNativeTarget "lldb-argdumper" */; + buildPhases = ( + 942829BC1A89835300521B30 /* Sources */, + 942829BD1A89835300521B30 /* Frameworks */, + 942829BE1A89835300521B30 /* CopyFiles */, + 940B04E21A89871F0045D5F7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 942829CA1A89836A00521B30 /* PBXTargetDependency */, + ); + name = "lldb-argdumper"; + productName = argdumper; + productReference = 942829C01A89835300521B30 /* lldb-argdumper */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0700; + LastUpgradeCheck = 0720; + TargetAttributes = { + 2387551E1C24974600CCE8C3 = { + CreatedOnToolsVersion = 7.2; + }; + 239504D31BDD451400963CEA = { + CreatedOnToolsVersion = 7.1; + }; + 2690CD161A6DC0D000E717C8 = { + CreatedOnToolsVersion = 6.3; + }; + 942829BF1A89835300521B30 = { + CreatedOnToolsVersion = 7.0; + }; + }; + }; + buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "lldb" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 08FB7794FE84155DC02AAC07 /* lldb */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 265E9BE2115C2BAA00D0DCCB /* Products */; + ProjectRef = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 26CEF3B114FD592B007286B2 /* desktop */, + 26CEF3A914FD58BF007286B2 /* desktop_no_xpc */, + 26CEF3BC14FD596A007286B2 /* ios */, + 26F5C26910F3D9A4009D5894 /* lldb-tool */, + 26680206115FD0ED008E1FE4 /* LLDB */, + 239504D31BDD451400963CEA /* lldb-gtest */, + 2387551E1C24974600CCE8C3 /* lldb-python-test-suite */, + 26579F67126A25920007C5CB /* darwin-debug */, + 2689FFC913353D7A00698AC0 /* lldb-core */, + 26DC6A0F1337FE6900FF7998 /* lldb-server */, + 2687EAC51508110B00DD8C2E /* install-headers */, + 2690CD161A6DC0D000E717C8 /* lldb-mi */, + 942829BF1A89835300521B30 /* lldb-argdumper */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 239504C51BDD3FD700963CEA /* debugserver */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = debugserver; + remoteRef = 239504C41BDD3FD700963CEA /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 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 = ( + 94E829CA152D33C1006F96A3 /* lldb-server in Resources */, + 262CFC7711A4510000946C6C /* debugserver in Resources */, + 26368AF7126B960500E8659F /* darwin-debug in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 261B5A7511C3FA6F00AABD0A /* Fixup Framework Headers */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Fixup Framework Headers"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd \"${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}\"\nfor file in *.h\ndo\n\t/usr/bin/sed -i '' 's/\\(#include\\)[ ]*\"lldb\\/\\(API\\/\\)\\{0,1\\}\\(.*\\)\"/\\1 /1' \"$file\"\n\t/usr/bin/sed -i '' 's| + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/LLDB.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/LLDB.xcscheme new file mode 100644 index 000000000000..881829e27feb --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/LLDB.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/Run Testsuite.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/Run Testsuite.xcscheme new file mode 100644 index 000000000000..fb4bc136c9e9 --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/Run Testsuite.xcscheme @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/darwin-debug.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/darwin-debug.xcscheme new file mode 100644 index 000000000000..d8fa52cd63dc --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/darwin-debug.xcscheme @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/desktop.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/desktop.xcscheme new file mode 100644 index 000000000000..e9240bb01118 --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/desktop.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/launcherRootXPCService.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/launcherRootXPCService.xcscheme new file mode 100644 index 000000000000..5e1b68cd93b0 --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/launcherRootXPCService.xcscheme @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/launcherXPCService.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/launcherXPCService.xcscheme new file mode 100644 index 000000000000..39629e1e1459 --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/launcherXPCService.xcscheme @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/lldb-gtest.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/lldb-gtest.xcscheme new file mode 100644 index 000000000000..cd5ff8af9ebf --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/lldb-gtest.xcscheme @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/lldb-python-test-suite.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/lldb-python-test-suite.xcscheme new file mode 100644 index 000000000000..dcf0c0f2c7ae --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/lldb-python-test-suite.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme b/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme new file mode 100644 index 000000000000..01dea120349c --- /dev/null +++ b/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb.xcworkspace/contents.xcworkspacedata b/lldb.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..3266d5b39e25 --- /dev/null +++ b/lldb.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/packages/Python/lldbsuite/__init__.py b/packages/Python/lldbsuite/__init__.py new file mode 100644 index 000000000000..aa6874e78bc2 --- /dev/null +++ b/packages/Python/lldbsuite/__init__.py @@ -0,0 +1,28 @@ +# Module level initialization for the `lldbsuite` module. + +import inspect +import os +import sys + +def find_lldb_root(): + lldb_root = os.path.dirname(inspect.getfile(inspect.currentframe())) + while True: + lldb_root = os.path.dirname(lldb_root) + if lldb_root is None: + return None + + test_path = os.path.join(lldb_root, "use_lldb_suite_root.py") + if os.path.isfile(test_path): + return lldb_root + return None + +# lldbsuite.lldb_root refers to the root of the git/svn source checkout +lldb_root = find_lldb_root() + +# lldbsuite.lldb_test_root refers to the root of the python test tree +lldb_test_root = os.path.join( + lldb_root, + "packages", + "Python", + "lldbsuite", + "test") diff --git a/packages/Python/lldbsuite/support/__init__.py b/packages/Python/lldbsuite/support/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/support/fs.py b/packages/Python/lldbsuite/support/fs.py new file mode 100644 index 000000000000..9a56808369de --- /dev/null +++ b/packages/Python/lldbsuite/support/fs.py @@ -0,0 +1,64 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Prepares language bindings for LLDB build process. Run with --help +to see a description of the supported command line arguments. +""" + +# Python modules: +import os +import platform +import sys + + +def _find_file_in_paths(paths, exe_basename): + """Returns the full exe path for the first path match. + + @params paths the list of directories to search for the exe_basename + executable + @params exe_basename the name of the file for which to search. + e.g. "swig" or "swig.exe". + + @return the full path to the executable if found in one of the + given paths; otherwise, returns None. + """ + for path in paths: + trial_exe_path = os.path.join(path, exe_basename) + if os.path.exists(trial_exe_path): + return os.path.normcase(trial_exe_path) + return None + +def find_executable(executable): + """Finds the specified executable in the PATH or known good locations.""" + + # Figure out what we're looking for. + if platform.system() == "Windows": + executable = executable + ".exe" + extra_dirs = [] + else: + extra_dirs = ["/usr/local/bin"] + + # Figure out what paths to check. + path_env = os.environ.get("PATH", None) + if path_env is not None: + paths_to_check = path_env.split(os.path.pathsep) + else: + paths_to_check = [] + + # Add in the extra dirs + paths_to_check.extend(extra_dirs) + if len(paths_to_check) < 1: + raise os.OSError( + "executable was not specified, PATH has no " + "contents, and there are no extra directories to search") + + result = _find_file_in_paths(paths_to_check, executable) + + if not result or len(result) < 1: + raise os.OSError( + "failed to find exe='%s' in paths='%s'" % (executable, paths_to_check)) + return result + diff --git a/packages/Python/lldbsuite/support/seven.py b/packages/Python/lldbsuite/support/seven.py new file mode 100644 index 000000000000..56ddd8db3f66 --- /dev/null +++ b/packages/Python/lldbsuite/support/seven.py @@ -0,0 +1,20 @@ +import six + +if six.PY2: + import commands + get_command_output = commands.getoutput + get_command_status_output = commands.getstatusoutput + + cmp_ = cmp +else: + def get_command_status_output(command): + try: + import subprocess + return (0, subprocess.check_output(command, shell=True, universal_newlines=True)) + except subprocess.CalledProcessError as e: + return (e.returncode, e.output) + + def get_command_output(command): + return get_command_status_output(command)[1] + + cmp_ = lambda x, y: (x > y) - (x < y) \ No newline at end of file diff --git a/packages/Python/lldbsuite/support/sockutil.py b/packages/Python/lldbsuite/support/sockutil.py new file mode 100644 index 000000000000..b3d81d14884a --- /dev/null +++ b/packages/Python/lldbsuite/support/sockutil.py @@ -0,0 +1,23 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Helper functions for working with sockets. +""" + +# Python modules: +import io +import socket + +# LLDB modules +import use_lldb_suite + +def recvall(sock, size): + bytes = io.BytesIO() + while size > 0: + this_result = sock.recv(size) + bytes.write(this_result) + size -= len(this_result) + return bytes.getvalue() diff --git a/packages/Python/lldbsuite/test/.categories b/packages/Python/lldbsuite/test/.categories new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/Makefile b/packages/Python/lldbsuite/test/Makefile new file mode 100644 index 000000000000..4d08ef39d4db --- /dev/null +++ b/packages/Python/lldbsuite/test/Makefile @@ -0,0 +1,33 @@ +LLDB_LEVEL := .. +include $(LLDB_LEVEL)/Makefile + +.PHONY: programs + +all:: check-local + +#---------------------------------------------------------------------- +# Make all of the test programs +#---------------------------------------------------------------------- +programs: + find . -type d -depth 1 | xargs -J % find % \ + -name Makefile \ + -exec echo \; \ + -exec echo make -f '{}' \; \ + -execdir make \; + +#---------------------------------------------------------------------- +# Clean 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 \; + +#---------------------------------------------------------------------- +# Run the tests +#---------------------------------------------------------------------- +check-local:: + rm -rf lldb-test-traces + python $(PROJ_SRC_DIR)/dotest.py --executable $(ToolDir)/lldb -q -s lldb-test-traces -u CXXFLAGS -u CFLAGS -C $(subst ccache,,$(CC)) diff --git a/packages/Python/lldbsuite/test/README-TestSuite b/packages/Python/lldbsuite/test/README-TestSuite new file mode 100644 index 000000000000..6df4d7bd7be7 --- /dev/null +++ b/packages/Python/lldbsuite/test/README-TestSuite @@ -0,0 +1,159 @@ +This README file describes the files and directories related to the Python test +suite under the current 'test' directory. + +o dotest.py + + Provides the test driver for the test suite. To invoke it, cd to the 'test' + directory and issue the './dotest.py' command or './dotest.py -v' for more + verbose output. '.dotest.py -h' prints out the help messge. + + A specific naming pattern is followed by the .py script under the 'test' + directory in order to be recognized by 'dotest.py' test driver as a module + which implements a test case, namely, Test*.py. + + Some example usages: + + 1. ./dotest.py -v . 2> ~/Developer/Log/lldbtest.log0 + This runs the test suite and directs the run log to a file. + + 2. LLDB_LOG=/tmp/lldb.log GDB_REMOTE_LOG=/tmp/gdb-remote.log ./dotest.py -v . 2> ~/Developer/Log/lldbtest.log + This runs the test suite, with logging turned on for the lldb as well as + the process.gdb-remote channels and directs the run log to a file. + +o lldbtest.py + + Provides an abstract base class of lldb test case named 'TestBase', which in + turn inherits from Python's unittest.TestCase. The concrete subclass can + override lldbtest.TestBase in order to inherit the common behavior for + unittest.TestCase.setUp/tearDown implemented in this file. + + To provide a test case, the concrete subclass provides methods whose names + start with the letters test. For more details about the Python's unittest + framework, go to http://docs.python.org/library/unittest.html. + + ./command_source/TestCommandSource.py provides a simple example of test case + which overrides lldbtest.TestBase to exercise the lldb's 'command source' + command. The subclass should override the attribute 'mydir' in order for the + runtime to locate the individual test cases when running as part of a large + test suite or when running each test case as a separate Python invocation. + + The doc string provides more details about the setup required for running a + test case on its own. To run the whole test suite, 'dotest.py' is all you + need to do. + +o subdirectories of 'test' + + Most of them predate the introduction of the python test suite and contain + example C/C++/ObjC source files which get compiled into executables which are + to be exercised by the debugger. + + For such subdirectory which has an associated Test*.py file, it was added as + part of the Python-based test suite to test lldb functionality. + + Some of the subdirectories, for example, the 'help' subdirectory, do not have + C/C++/ObjC source files; they were created to house the Python test case which + does not involve lldb reading in an executable file at all. + +o make directory + + Contains Makefile.rules, which can be utilized by test cases to write Makefile + based rules to build binaries for the inferiors. + + By default, the built executable name is a.out, which can be overwritten by + specifying your EXE make variable, via the Makefile under the specific test + directory or via supplying a Python dictionary to the build method in your + Python test script. An example of the latter can be found in + test/lang/objc/radar-9691614/TestObjCMethodReturningBOOL.py, where: + + def test_method_ret_BOOL_with_dsym(self): + """Test that objective-c method returning BOOL works correctly.""" + d = {'EXE': self.exe_name} + self.buildDsym(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.objc_method_ret_BOOL(self.exe_name) + + def test_method_ret_BOOL_with_dwarf(self): + """Test that objective-c method returning BOOL works correctly.""" + d = {'EXE': self.exe_name} + self.buildDwarf(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.objc_method_ret_BOOL(self.exe_name) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break inside main(). + self.main_source = "main.m" + self.line = line_number(self.main_source, '// Set breakpoint here.') + + The exe names for the two test methods are equal to the test method names and + are therefore guaranteed different. + +o plugins directory + + Contains platform specific plugin to build binaries with dsym/dwarf debugging + info. Other platform specific functionalities may be added in the future. + +o unittest2 directory + + Many new features were added to unittest in Python 2.7, including test + discovery. unittest2 allows you to use these features with earlier versions of + Python. + + It currently has unittest2 0.5.1 from http://pypi.python.org/pypi/unittest2. + Version 0.5.1 of unittest2 has feature parity with unittest in Python 2.7 + final. If you want to ensure that your tests run identically under unittest2 + and unittest in Python 2.7 you should use unittest2 0.5.1. + + Later versions of unittest2 include changes in unittest made in Python 3.2 and + onwards after the release of Python 2.7. + +o dotest.pl + + In case you wonder, there is also a 'dotest.pl' perl script file. It was + created to visit each Python test case under the specified directory and + invoke Python's builtin unittest.main() on each test case. + + It does not take advantage of the test runner and test suite functionality + provided by Python's unitest framework. Its existence is because we want a + different way of running the whole test suite. As lldb and the Python test + suite become more reliable, we don't expect to be using 'dotest.pl' anymore. + + Note: dotest.pl has been moved to the attic directory. + +o Profiling dotest.py runs + + I used the following command line thingy to do the profiling on a SnowLeopard + machine: + +$ DOTEST_PROFILE=YES DOTEST_SCRIPT_DIR=/Volumes/data/lldb/svn/trunk/test /System/Library/Frameworks/Python.framework/Versions/Current/lib/python2.6/cProfile.py -o my.profile ./dotest.py -v -w 2> ~/Developer/Log/lldbtest.log + + After that, I used the pstats.py module to browse the statistics: + +$ python /System/Library/Frameworks/Python.framework/Versions/Current/lib/python2.6/pstats.py my.profile + +o Writing test cases: + + We strongly prefer writing test cases using the SB API's rather than the runCmd & expect. + Unless you are actually testing some feature of the command line, please don't write + command based tests. For historical reasons there are plenty of examples of tests in the + test suite that use runCmd where they shouldn't, but don't copy them, copy the plenty that + do use the SB API's instead. + + The reason for this is that our policy is that we will maintain compatibility with the + SB API's. But we don't make any similar guarantee about the details of command result format. + If your test is using the command line, it is going to have to check against the command result + text, and you either end up writing your check pattern by checking as little as possible so + you won't be exposed to random changes in the text; in which case you can end up missing some + failure, or you test too much and it means irrelevant changes break your tests. + + However, if you use the Python API's it is possible to check all the results you want + to check in a very explicit way, which makes the tests much more robust. + + Even if you are testing that a command-line command does some specific thing, it is still + better in general to use the SB API's to drive to the point where you want to run the test, + then use SBInterpreter::HandleCommand to run the command. You get the full result text + from the command in the command return object, and all the part where you are driving the + debugger to the point you want to test will be more robust. diff --git a/packages/Python/lldbsuite/test/__init__.py b/packages/Python/lldbsuite/test/__init__.py new file mode 100644 index 000000000000..93971c2f2364 --- /dev/null +++ b/packages/Python/lldbsuite/test/__init__.py @@ -0,0 +1,7 @@ +# Module level initialization for the `lldbsuite.test` module. + +from __future__ import absolute_import + +from . import dotest + +run_suite = dotest.run_suite diff --git a/packages/Python/lldbsuite/test/android/platform/Makefile b/packages/Python/lldbsuite/test/android/platform/Makefile new file mode 100644 index 000000000000..22e42c5a776e --- /dev/null +++ b/packages/Python/lldbsuite/test/android/platform/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/android/platform/TestDefaultCacheLineSize.py b/packages/Python/lldbsuite/test/android/platform/TestDefaultCacheLineSize.py new file mode 100644 index 000000000000..b925afe1b70c --- /dev/null +++ b/packages/Python/lldbsuite/test/android/platform/TestDefaultCacheLineSize.py @@ -0,0 +1,38 @@ +""" +Verify the default cache line size for android targets +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class DefaultCacheLineSizeTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessPlatform(['android']) + def test_cache_line_size(self): + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target and target.IsValid(), "Target is valid") + + breakpoint = target.BreakpointCreateByName("main") + self.assertTrue(breakpoint and breakpoint.IsValid(), "Breakpoint is valid") + + # Run the program. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + # check the setting value + self.expect("settings show target.process.memory-cache-line-size", patterns=[" = 2048"]) + + # Run to completion. + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/android/platform/main.cpp b/packages/Python/lldbsuite/test/android/platform/main.cpp new file mode 100644 index 000000000000..3fb9edc47270 --- /dev/null +++ b/packages/Python/lldbsuite/test/android/platform/main.cpp @@ -0,0 +1,13 @@ +//===-- 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 () +{ + return 0; +} diff --git a/packages/Python/lldbsuite/test/api/check_public_api_headers/Makefile b/packages/Python/lldbsuite/test/api/check_public_api_headers/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/api/check_public_api_headers/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/api/check_public_api_headers/TestPublicAPIHeaders.py b/packages/Python/lldbsuite/test/api/check_public_api_headers/TestPublicAPIHeaders.py new file mode 100644 index 000000000000..0d0507f1d70c --- /dev/null +++ b/packages/Python/lldbsuite/test/api/check_public_api_headers/TestPublicAPIHeaders.py @@ -0,0 +1,88 @@ +"""Test the integrity of the lldb public api directory containing SB*.h headers. + +There should be nothing unwanted there and a simpe main.cpp which includes SB*.h +should compile and link with the LLDB framework.""" + +from __future__ import print_function + + + +import os, re +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SBDirCheckerCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.template = 'main.cpp.template' + self.source = 'main.cpp' + self.exe_name = 'a.out' + + @skipIfNoSBHeaders + def test_sb_api_directory(self): + """Test the SB API directory and make sure there's no unwanted stuff.""" + + # Only proceed if this is an Apple OS, "x86_64", and local platform. + if not (self.platformIsDarwin() and self.getArchitecture() == "x86_64"): + self.skipTest("This test is only for LLDB.framework built 64-bit") + if self.getArchitecture() == "i386": + self.skipTest("LLDB is 64-bit and cannot be linked to 32-bit test program.") + + # Generate main.cpp, build it, and execute. + self.generate_main_cpp() + self.buildDriver(self.source, self.exe_name) + self.sanity_check_executable(self.exe_name) + + def generate_main_cpp(self): + """Generate main.cpp from main.cpp.template.""" + temp = os.path.join(os.getcwd(), self.template) + with open(temp, 'r') as f: + content = f.read() + + public_api_dir = os.path.join(os.environ["LLDB_SRC"], "include", "lldb", "API") + + # Look under the include/lldb/API directory and add #include statements + # for all the SB API headers. + public_headers = os.listdir(public_api_dir) + # For different platforms, the include statement can vary. + if self.platformIsDarwin(): + include_stmt = "'#include <%s>' % os.path.join('LLDB', header)" + if self.getPlatform() == "freebsd" or self.getPlatform() == "linux" or os.environ.get('LLDB_BUILD_TYPE') == 'Makefile': + include_stmt = "'#include <%s>' % os.path.join(public_api_dir, header)" + list = [eval(include_stmt) for header in public_headers if (header.startswith("SB") and + header.endswith(".h"))] + includes = '\n'.join(list) + new_content = content.replace('%include_SB_APIs%', includes) + src = os.path.join(os.getcwd(), self.source) + with open(src, 'w') as f: + f.write(new_content) + + # The main.cpp has been generated, add a teardown hook to remove it. + self.addTearDownHook(lambda: os.remove(src)) + + def sanity_check_executable(self, exe_name): + """Sanity check executable compiled from the auto-generated program.""" + exe = os.path.join(os.getcwd(), exe_name) + self.runCmd("file %s" % exe, CURRENT_EXECUTABLE_SET) + + self.line_to_break = line_number(self.source, '// Set breakpoint here.') + + env_cmd = "settings set target.env-vars %s=%s" %(self.dylibPath, self.getLLDBLibraryEnvVal()) + if self.TraceOn(): + print("Set environment to: ", env_cmd) + self.runCmd(env_cmd) + self.addTearDownHook(lambda: self.dbg.HandleCommand("settings remove target.env-vars %s" % self.dylibPath)) + + lldbutil.run_break_set_by_file_and_line (self, self.source, self.line_to_break, num_expected_locations = -1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.runCmd('frame variable') diff --git a/packages/Python/lldbsuite/test/api/check_public_api_headers/main.cpp.template b/packages/Python/lldbsuite/test/api/check_public_api_headers/main.cpp.template new file mode 100644 index 000000000000..ed754add6100 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/check_public_api_headers/main.cpp.template @@ -0,0 +1,24 @@ +//===-- 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_SB_APIs% + +using namespace lldb; +int +main(int argc, char const *argv[]) +{ + SBDebugger::Initialize(); + SBDebugger dbg = SBDebugger::Create(); + + printf("Hello SBDebugger %llu\n", dbg.GetID()); // Set breakpoint here. + + SBDebugger::Terminate(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories b/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories new file mode 100644 index 000000000000..6e70196ccd70 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories @@ -0,0 +1 @@ +stresstest diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile b/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile new file mode 100644 index 000000000000..08f8850e3e8f --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +MAKE_DSYM := NO + +ENABLE_THREADS := YES +CXX_SOURCES := multi-process-driver.cpp testprog.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py b/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py new file mode 100644 index 000000000000..67dca2273d8a --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py @@ -0,0 +1,44 @@ +"""Test the lldb public C++ api when doing multiple debug sessions simultaneously.""" + +from __future__ import print_function + + + +import os, re +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import lldb +import subprocess + +class TestMultipleSimultaneousDebuggers(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfi386 + @skipIfNoSBHeaders + @expectedFailureFreeBSD("llvm.org/pr20282") + @expectedFailureLinux("llvm.org/pr20282") + @expectedFailureWindows # Test crashes + @expectedFlakeyDarwin() + def test_multiple_debuggers(self): + env = {self.dylibPath : self.getLLDBLibraryEnvVal()} + + self.driver_exe = os.path.join(os.getcwd(), "multi-process-driver") + self.buildDriver('multi-process-driver.cpp', self.driver_exe) + self.addTearDownHook(lambda: os.remove(self.driver_exe)) + self.signBinary(self.driver_exe) + + self.inferior_exe = os.path.join(os.getcwd(), "testprog") + self.buildDriver('testprog.cpp', self.inferior_exe) + self.addTearDownHook(lambda: os.remove(self.inferior_exe)) + +# check_call will raise a CalledProcessError if multi-process-driver doesn't return +# exit code 0 to indicate success. We can let this exception go - the test harness +# will recognize it as a test failure. + + if self.TraceOn(): + print("Running test %s" % self.driver_exe) + check_call([self.driver_exe, self.inferior_exe], env=env) + else: + with open(os.devnull, 'w') as fnull: + check_call([self.driver_exe, self.inferior_exe], env=env, stdout=fnull, stderr=fnull) diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp b/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp new file mode 100644 index 000000000000..1c2689ff6c4f --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp @@ -0,0 +1,283 @@ + +// This program creates NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS of pthreads, +// creates an lldb Debugger on each thread, creates targets, inserts two +// breakpoints, runs to the first breakpoint, backtraces, runs to the second +// breakpoint, backtraces, kills the inferior process, closes down the +// debugger. + +// The main thread keeps track of which pthreads have completed and which +// pthreads have completed successfully, and exits when all pthreads have +// completed successfully, or our time limit has been exceeded. + +// This test file helps to uncover race conditions and locking mistakes +// that are hit when lldb is being used to debug multiple processes +// simultaneously. + +#include +#include + + +#include "lldb/API/LLDB.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" + +#include +#include + +#define NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS 20 + +#define DEBUG 0 + +using namespace lldb; + +bool *completed_threads_array = 0; +bool *successful_threads_array = 0; + +const char *inferior_process_name = "testprog"; + +bool +wait_for_stop_event (SBProcess process, SBListener listener) +{ + bool stopped = false; + while (!stopped) + { + SBEvent event; + bool waitfor_ret = listener.WaitForEvent (2, event); + if (event.GetType() == SBProcess::eBroadcastBitStateChanged) + { + if (process.GetState() == StateType::eStateStopped + || process.GetState() == StateType::eStateCrashed + || process.GetState() == StateType::eStateDetached + || process.GetState() == StateType::eStateExited) + { + stopped = true; + } + } + } + return stopped; +} + +bool +walk_stack_to_main (SBThread thread) +{ + if (thread.IsValid() == 0) + { + return false; + } + + bool found_main = false; + uint32_t curr_frame = 0; + const uint32_t framecount = thread.GetNumFrames(); + while (!found_main && curr_frame < framecount) + { + SBFrame frame = thread.GetFrameAtIndex (curr_frame); + if (strcmp (frame.GetFunctionName(), "main") == 0) + { + found_main = true; + break; + } + curr_frame += 1; + } + return found_main; +} + +void *do_one_debugger (void *in) +{ + uint64_t threadnum = (uint64_t) in; + +#if defined (__APPLE__) + char *threadname; + asprintf (&threadname, "thread #%lld", threadnum); + pthread_setname_np (threadname); + free (threadname); +#endif + +#if DEBUG == 1 + printf ("#%lld: Starting debug session\n", threadnum); +#endif + + SBDebugger debugger = lldb::SBDebugger::Create (false); + if (debugger.IsValid ()) + { + debugger.SetAsync (true); + SBTarget target = debugger.CreateTargetWithFileAndArch(inferior_process_name, "x86_64"); + SBCommandInterpreter command_interp = debugger.GetCommandInterpreter(); + if (target.IsValid()) + { + SBBreakpoint bar_br = target.BreakpointCreateByName ("bar", "testprog"); + if (!bar_br.IsValid()) + { + printf ("#%lld: failed to set breakpoint on bar, exiting.\n", threadnum); + exit (1); + } + SBBreakpoint foo_br = target.BreakpointCreateByName ("foo", "testprog"); + if (!foo_br.IsValid()) + { + printf ("#%lld: Failed to set breakpoint on foo()\n", threadnum); + } + + SBLaunchInfo launch_info (NULL); + SBError error; + SBProcess process = target.Launch (launch_info, error); + if (process.IsValid()) + { + SBListener listener = debugger.GetListener(); + SBBroadcaster broadcaster = process.GetBroadcaster(); + uint32_t rc = broadcaster.AddListener (listener, SBProcess::eBroadcastBitStateChanged); + if (rc == 0) + { + printf ("adding listener failed\n"); + exit (1); + } + + wait_for_stop_event (process, listener); + + if (!walk_stack_to_main (process.GetThreadAtIndex(0))) + { + printf ("#%lld: backtrace while @ foo() failed\n", threadnum); + completed_threads_array[threadnum] = true; + return (void *) 1; + } + + if (strcmp (process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(), "foo") != 0) + { +#if DEBUG == 1 + printf ("#%lld: First breakpoint did not stop at foo(), instead stopped at '%s'\n", threadnum, process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName()); +#endif + completed_threads_array[threadnum] = true; + return (void*) 1; + } + + process.Continue(); + + wait_for_stop_event (process, listener); + + if (process.GetState() == StateType::eStateExited) + { + printf ("#%lld: Process exited\n", threadnum); + completed_threads_array[threadnum] = true; + return (void *) 1; + } + + + if (!walk_stack_to_main (process.GetThreadAtIndex(0))) + { + printf ("#%lld: backtrace while @ bar() failed\n", threadnum); + completed_threads_array[threadnum] = true; + return (void *) 1; + } + + if (strcmp (process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(), "bar") != 0) + { + printf ("#%lld: First breakpoint did not stop at bar()\n", threadnum); + completed_threads_array[threadnum] = true; + return (void*) 1; + } + + process.Kill(); + + wait_for_stop_event (process, listener); + + SBDebugger::Destroy(debugger); + +#if DEBUG == 1 + printf ("#%lld: All good!\n", threadnum); +#endif + successful_threads_array[threadnum] = true; + completed_threads_array[threadnum] = true; + return (void*) 0; + } + else + { + printf("#%lld: process failed to launch\n", threadnum); + successful_threads_array[threadnum] = false; + completed_threads_array[threadnum] = true; + return (void*) 0; + } + } + else + { + printf ("#%lld: did not get valid target\n", threadnum); + successful_threads_array[threadnum] = false; + completed_threads_array[threadnum] = true; + return (void*) 0; + } + } + else + { + printf ("#%lld: did not get debugger\n", threadnum); + successful_threads_array[threadnum] = false; + completed_threads_array[threadnum] = true; + return (void*) 0; + } + completed_threads_array[threadnum] = true; + return (void*) 1; +} + +int main (int argc, char **argv) +{ + SBDebugger::Initialize(); + + completed_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); + memset (completed_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); + successful_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); + memset (successful_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); + + if (argc > 1 && argv[1] != NULL) + { + inferior_process_name = argv[1]; + } + + std::thread threads[NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS]; + for (uint64_t i = 0; i< NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++) + { + threads[i] = std::move(std::thread(do_one_debugger, (void*)i)); + } + + + int max_time_to_wait = 20; // 20 iterations, or 60 seconds + int iter = 0; + while (1) + { + std::this_thread::sleep_for(std::chrono::seconds(3)); + bool all_done = true; + int successful_threads = 0; + int total_completed_threads = 0; + for (uint64_t i = 0; i < NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++) + { + if (successful_threads_array[i] == true) + successful_threads++; + if (completed_threads_array[i] == true) + total_completed_threads++; + if (completed_threads_array[i] == false) + { + all_done = false; + } + } + if (all_done) + { +#if DEBUG == 1 + printf ("All threads completed.\n"); + printf ("%d threads completed successfully out of %d\n", successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); +#endif + SBDebugger::Terminate(); + exit(0); + } + else + { +#if DEBUG == 1 + printf ("%d threads completed so far (%d successfully), out of %d\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); +#endif + } + if (iter++ == max_time_to_wait) + { + printf ("reached maximum timeout but only %d threads have completed so far (%d successfully), out of %d. Exiting.\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); + break; + } + } + + + SBDebugger::Terminate(); + exit (1); +} diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp b/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp new file mode 100644 index 000000000000..c9d1ea14f17b --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp @@ -0,0 +1,12 @@ +int bar () +{ + return 5; +} +int foo () +{ + return bar() + 5; +} +int main () +{ + return foo(); +} diff --git a/packages/Python/lldbsuite/test/api/multithreaded/Makefile b/packages/Python/lldbsuite/test/api/multithreaded/Makefile new file mode 100644 index 000000000000..37323ea78190 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + +clean:: + rm -rf $(wildcard *.o *.d *.dSYM) diff --git a/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py b/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py new file mode 100644 index 000000000000..0b405091ebd1 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py @@ -0,0 +1,91 @@ +"""Test the lldb public C++ api breakpoint callbacks.""" + +from __future__ import print_function + +# __package__ = "lldbsuite.test" + + +import os, re +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import subprocess + +class SBBreakpointCallbackCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfRemote + @skipIfNoSBHeaders + @skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538) + def test_breakpoint_callback(self): + """Test the that SBBreakpoint callback is invoked when a breakpoint is hit. """ + self.build_and_test('driver.cpp test_breakpoint_callback.cpp', + 'test_breakpoint_callback') + + @skipIfRemote + @skipIfNoSBHeaders + @skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538) + @expectedFlakeyFreeBSD + def test_sb_api_listener_event_description(self): + """ Test the description of an SBListener breakpoint event is valid.""" + self.build_and_test('driver.cpp listener_test.cpp test_listener_event_description.cpp', + 'test_listener_event_description') + pass + + @skipIfRemote + @skipIfNoSBHeaders + @skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538) + @expectedFlakeyFreeBSD + @expectedFlakeyLinux # Driver occasionally returns '1' as exit status + @expectedFailureAll("llvm.org/pr23139", oslist=["linux"], compiler="gcc", compiler_version=[">=","4.9"], archs=["x86_64"]) + def test_sb_api_listener_event_process_state(self): + """ Test that a registered SBListener receives events when a process + changes state. + """ + self.build_and_test('driver.cpp listener_test.cpp test_listener_event_process_state.cpp', + 'test_listener_event_process_state') + pass + + + @skipIfRemote + @skipIfNoSBHeaders + @skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538) + @expectedFlakeyFreeBSD + @expectedFlakeyLinux + def test_sb_api_listener_resume(self): + """ Test that a process can be resumed from a non-main thread. """ + self.build_and_test('driver.cpp listener_test.cpp test_listener_resume.cpp', + 'test_listener_resume') + pass + + def build_and_test(self, sources, test_name, args = None): + """ Build LLDB test from sources, and run expecting 0 exit code """ + + # These tests link against host lldb API. + # Compiler's target triple must match liblldb triple + # because remote is disabled, we can assume that the os is the same + # still need to check architecture + if self.getLldbArchitecture() != self.getArchitecture(): + self.skipTest("This test is only run if the target arch is the same as the lldb binary arch") + + self.inferior = 'inferior_program' + self.buildProgram('inferior.cpp', self.inferior) + self.addTearDownHook(lambda: os.remove(self.inferior)) + + self.buildDriver(sources, test_name) + self.addTearDownHook(lambda: os.remove(test_name)) + + test_exe = os.path.join(os.getcwd(), test_name) + self.signBinary(test_exe) + exe = [test_exe, self.inferior] + + env = {self.dylibPath : self.getLLDBLibraryEnvVal()} + if self.TraceOn(): + print("Running test %s" % " ".join(exe)) + check_call(exe, env=env) + else: + with open(os.devnull, 'w') as fnull: + check_call(exe, env=env, stdout=fnull, stderr=fnull) + + def build_program(self, sources, program): + return self.buildDriver(sources, program) diff --git a/packages/Python/lldbsuite/test/api/multithreaded/common.h b/packages/Python/lldbsuite/test/api/multithreaded/common.h new file mode 100644 index 000000000000..dad8bba07a3f --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/common.h @@ -0,0 +1,68 @@ +#ifndef LLDB_TEST_API_COMMON_H +#define LLDB_TEST_API_COMMON_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +/// Simple exception class with a message +struct Exception : public std::exception +{ + std::string s; + Exception(std::string ss) : s(ss) {} + virtual ~Exception() throw () { } + const char* what() const throw() { return s.c_str(); } +}; + +// Synchronized data structure for listener to send events through +template +class multithreaded_queue { + std::condition_variable m_condition; + std::mutex m_mutex; + std::queue m_data; + bool m_notified; + +public: + + void push(T e) { + std::lock_guard lock(m_mutex); + m_data.push(e); + m_notified = true; + m_condition.notify_all(); + } + + T pop(int timeout_seconds, bool &success) { + int count = 0; + while (count < timeout_seconds) { + std::unique_lock lock(m_mutex); + if (!m_data.empty()) { + m_notified = false; + T ret = m_data.front(); + m_data.pop(); + success = true; + return ret; + } else if (!m_notified) + m_condition.wait_for(lock, std::chrono::seconds(1)); + count ++; + } + success = false; + return T(); + } +}; + +/// Allocates a char buffer with the current working directory +inline char* get_working_dir() { +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) + return getwd(0); +#else + return get_current_dir_name(); +#endif +} + +#endif // LLDB_TEST_API_COMMON_H diff --git a/packages/Python/lldbsuite/test/api/multithreaded/driver.cpp b/packages/Python/lldbsuite/test/api/multithreaded/driver.cpp new file mode 100644 index 000000000000..fa0c48e0ecfb --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/driver.cpp @@ -0,0 +1,38 @@ + +/// LLDB C API Test Driver + +#include +#include +#include +#include +#include + +#include "lldb-headers.h" + +#include "common.h" + +using namespace std; +using namespace lldb; + +void test(SBDebugger &dbg, std::vector args); + +int main(int argc, char** argv) { + int code = 0; + + SBDebugger::Initialize(); + SBDebugger dbg = SBDebugger::Create(); + + try { + if (!dbg.IsValid()) + throw Exception("invalid debugger"); + vector args(argv + 1, argv + argc); + + test(dbg, args); + } catch (Exception &e) { + cout << "ERROR: " << e.what() << endl; + code = 1; + } + + SBDebugger::Destroy(dbg); + return code; +} diff --git a/packages/Python/lldbsuite/test/api/multithreaded/inferior.cpp b/packages/Python/lldbsuite/test/api/multithreaded/inferior.cpp new file mode 100644 index 000000000000..9dbb289f98be --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/inferior.cpp @@ -0,0 +1,17 @@ + +#include + +using namespace std; + +int next() { + static int i = 0; + cout << "incrementing " << i << endl; + return ++i; +} + +int main() { + int i = 0; + while (i < 5) + i = next(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/api/multithreaded/listener_test.cpp b/packages/Python/lldbsuite/test/api/multithreaded/listener_test.cpp new file mode 100644 index 000000000000..b20868ff94f4 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/listener_test.cpp @@ -0,0 +1,74 @@ +// LLDB test snippet that registers a listener with a process that hits +// a breakpoint. + +#include +#include +#include +#include +#include + +#include "lldb-headers.h" +#include "common.h" + +using namespace lldb; +using namespace std; + +void listener_func(); +void check_listener(SBDebugger &dbg); + +// Listener thread and related variables +atomic g_done; +SBListener g_listener("test-listener"); +thread g_listener_thread; + +void shutdown_listener() { + g_done.store(true); + if (g_listener_thread.joinable()) + g_listener_thread.join(); +} + +void test(SBDebugger &dbg, std::vector args) { + try { + g_done.store(false); + SBTarget target = dbg.CreateTarget(args.at(0).c_str()); + if (!target.IsValid()) throw Exception("invalid target"); + + SBBreakpoint breakpoint = target.BreakpointCreateByName("next"); + if (!breakpoint.IsValid()) throw Exception("invalid breakpoint"); + + std::unique_ptr working_dir(get_working_dir()); + + SBError error; + SBProcess process = target.Launch(g_listener, + 0, 0, 0, 0, 0, + working_dir.get(), + 0, + false, + error); + if (!error.Success()) + throw Exception("Error launching process."); + + /* FIXME: the approach below deadlocks + SBProcess process = target.LaunchSimple (0, 0, working_dir.get()); + + // get debugger listener (which is attached to process by default) + g_listener = dbg.GetListener(); + */ + + // FIXME: because a listener is attached to the process at launch-time, + // registering the listener below results in two listeners being attached, + // which is not supported by LLDB. + // register listener + // process.GetBroadcaster().AddListener(g_listener, + // SBProcess::eBroadcastBitStateChanged); + + // start listener thread + g_listener_thread = thread(listener_func); + check_listener(dbg); + + } catch (Exception &e) { + shutdown_listener(); + throw e; + } + shutdown_listener(); +} diff --git a/packages/Python/lldbsuite/test/api/multithreaded/lldb-headers.h b/packages/Python/lldbsuite/test/api/multithreaded/lldb-headers.h new file mode 100644 index 000000000000..da0914b3b071 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/lldb-headers.h @@ -0,0 +1,11 @@ + +#ifndef LLDB_HEADERS_H +#define LLDB_HEADERS_H + +#ifdef __APPLE__ +#include +#else +#include "lldb/API/LLDB.h" +#endif + +#endif // LLDB_HEADERS_H diff --git a/packages/Python/lldbsuite/test/api/multithreaded/test_breakpoint_callback.cpp b/packages/Python/lldbsuite/test/api/multithreaded/test_breakpoint_callback.cpp new file mode 100644 index 000000000000..def31f82b0c7 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/test_breakpoint_callback.cpp @@ -0,0 +1,49 @@ + +// LLDB C++ API Test: verify that the function registered with +// SBBreakpoint.SetCallback() is invoked when a breakpoint is hit. + +#include +#include +#include +#include + +#include "lldb-headers.h" + +#include "common.h" + +using namespace std; +using namespace lldb; + +mutex g_mutex; +condition_variable g_condition; +int g_breakpoint_hit_count = 0; + +bool BPCallback (void *baton, + SBProcess &process, + SBThread &thread, + SBBreakpointLocation &location) { + lock_guard lock(g_mutex); + g_breakpoint_hit_count += 1; + g_condition.notify_all(); + return true; +} + +void test(SBDebugger &dbg, vector args) { + dbg.SetAsync(false); + SBTarget target = dbg.CreateTarget(args.at(0).c_str()); + if (!target.IsValid()) throw Exception("invalid target"); + + SBBreakpoint breakpoint = target.BreakpointCreateByName("next"); + if (!breakpoint.IsValid()) throw Exception("invalid breakpoint"); + breakpoint.SetCallback(BPCallback, 0); + + std::unique_ptr working_dir(get_working_dir()); + SBProcess process = target.LaunchSimple (0, 0, working_dir.get()); + + { + unique_lock lock(g_mutex); + g_condition.wait_for(lock, chrono::seconds(5)); + if (g_breakpoint_hit_count != 1) + throw Exception("Breakpoint hit count expected to be 1"); + } +} diff --git a/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_description.cpp b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_description.cpp new file mode 100644 index 000000000000..0d7844dce6d0 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_description.cpp @@ -0,0 +1,97 @@ + +// LLDB C++ API Test: verify the event description that is received by an +// SBListener object registered with a process with a breakpoint. + +#include +#include +#include +#include +#include + +#include "lldb-headers.h" + +#include "common.h" + +using namespace lldb; +using namespace std; + +// listener thread control +extern atomic g_done; +extern SBListener g_listener; + +multithreaded_queue g_event_descriptions; +string g_error_desc; + +void listener_func() { + while (!g_done) { + SBEvent event; + bool got_event = g_listener.WaitForEvent(1, event); + + if (got_event) { + if (!event.IsValid()) + throw Exception("event is not valid in listener thread"); + + SBStream description; + event.GetDescription(description); + string str(description.GetData()); + g_event_descriptions.push(str); + } + } +} + +bool check_state(string &state, string &desc, bool got_description) +{ + g_error_desc.clear(); + + if(!got_description) + { + g_error_desc.append("Did not get expected event description"); + return false; + } + + if (desc.find("state-changed") == desc.npos) + g_error_desc.append("Event description incorrect: missing 'state-changed' "); + + if (desc.find("pid = ") == desc.npos) + g_error_desc.append("Event description incorrect: missing process pid "); + + string state_search_str = "state = " + state; + if (desc.find(state_search_str) == desc.npos) + { + string errString = ("Event description incorrect: expected state " + + state + + " but desc was " + + desc); + g_error_desc.append(errString); + } + + if (g_error_desc.length() > 0) + return false; + + cout << "check_state: " << state << " OK\n"; + return true; +} + +void check_listener(SBDebugger &dbg) +{ + bool got_description; + string state; + + // check for "launching" state, this may or may not be present + string desc = g_event_descriptions.pop(5, got_description); + state = "launching"; + if (check_state(state, desc, got_description)) + { + // found a 'launching' state, pop next one from queue + desc = g_event_descriptions.pop(5, got_description); + } + + state = "running"; + if( !check_state(state, desc, got_description) ) + throw Exception(g_error_desc); + + desc = g_event_descriptions.pop(5, got_description); + state = "stopped"; + if( !check_state(state, desc, got_description) ) + throw Exception(g_error_desc); +} diff --git a/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_process_state.cpp b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_process_state.cpp new file mode 100644 index 000000000000..0a698d1081d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_process_state.cpp @@ -0,0 +1,68 @@ + +// LLDB C++ API Test: verify the event description as obtained by calling +// SBEvent::GetCStringFromEvent that is received by an +// SBListener object registered with a process with a breakpoint. + +#include +#include +#include +#include + +#include "lldb-headers.h" + +#include "common.h" + +using namespace lldb; +using namespace std; + +// listener thread control +extern atomic g_done; + +multithreaded_queue g_thread_descriptions; +multithreaded_queue g_frame_functions; + +extern SBListener g_listener; + +void listener_func() { + while (!g_done) { + SBEvent event; + bool got_event = g_listener.WaitForEvent(1, event); + if (got_event) { + if (!event.IsValid()) + throw Exception("event is not valid in listener thread"); + + // send process description + SBProcess process = SBProcess::GetProcessFromEvent(event); + SBStream description; + + for (int i = 0; i < process.GetNumThreads(); ++i) { + // send each thread description + description.Clear(); + SBThread thread = process.GetThreadAtIndex(i); + thread.GetDescription(description); + g_thread_descriptions.push(description.GetData()); + + // send each frame function name + uint32_t num_frames = thread.GetNumFrames(); + for(int j = 0; j < num_frames; ++j) { + const char* function_name = thread.GetFrameAtIndex(j).GetSymbol().GetName(); + if (function_name) + g_frame_functions.push(function_name); + } + } + } + } +} + +void check_listener(SBDebugger &dbg) { + // check thread description + bool got_description = false; + string desc = g_thread_descriptions.pop(5, got_description); + if (!got_description) + throw Exception("Expected at least one thread description string"); + + // check at least one frame has a function name + desc = g_frame_functions.pop(5, got_description); + if (!got_description) + throw Exception("Expected at least one frame function name string"); +} diff --git a/packages/Python/lldbsuite/test/api/multithreaded/test_listener_resume.cpp b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_resume.cpp new file mode 100644 index 000000000000..8cf786b11603 --- /dev/null +++ b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_resume.cpp @@ -0,0 +1,53 @@ + +// LLDB C++ API Test: verify the event description as obtained by calling +// SBEvent::GetCStringFromEvent that is received by an +// SBListener object registered with a process with a breakpoint. + +#include +#include +#include +#include + +#include "lldb-headers.h" + +#include "common.h" + +using namespace lldb; +using namespace std; + +// listener thread control +extern atomic g_done; + +// used by listener thread to communicate a successful process continue command +// back to the checking thread. + +multithreaded_queue g_process_started; + +extern SBListener g_listener; + +void listener_func() { + while (!g_done) { + SBEvent event; + bool got_event = g_listener.WaitForEvent(1, event); + if (got_event) { + if (!event.IsValid()) + throw Exception("event is not valid in listener thread"); + + SBProcess process = SBProcess::GetProcessFromEvent(event); + if (process.GetState() == eStateStopped) { + SBError error = process.Continue(); + if (!error.Success()) + throw Exception(string("Cannot continue process from listener thread: ") + + error.GetCString()); + g_process_started.push(true); + } + } + } +} + +void check_listener(SBDebugger &dbg) { + bool got_message = false; + while (!got_message) + g_process_started.pop(5, got_message); + g_done = true; +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/TestEmulations.py b/packages/Python/lldbsuite/test/arm_emulation/TestEmulations.py new file mode 100644 index 000000000000..8a78d21e7978 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/TestEmulations.py @@ -0,0 +1,54 @@ +""" +Test some ARM instruction emulation. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class ARMEmulationTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_thumb_emulations (self): + current_dir = os.getcwd(); + test_dir = os.path.join (current_dir, "new-test-files") + files = os.listdir (test_dir) + thumb_files = list() + for f in files: + if '-thumb.dat' in f: + thumb_files.append (f) + + for f in thumb_files: + test_file = os.path.join (test_dir, f) + self.run_a_single_test (test_file) + + @no_debug_info_test + def test_arm_emulations (self): + current_dir = os.getcwd(); + test_dir = os.path.join (current_dir, "new-test-files") + files = os.listdir (test_dir) + arm_files = list() + for f in files: + if '-arm.dat' in f: + arm_files.append (f) + + for f in arm_files: + test_file = os.path.join (test_dir, f) + self.run_a_single_test (test_file) + + def run_a_single_test (self, filename): + insn = lldb.SBInstruction (); + stream = lldb.SBStream (); + success = insn.TestEmulation (stream, filename); + output = stream.GetData(); + if self.TraceOn(): + print('\nRunning test ' + os.path.basename(filename)) + print(output) + + self.assertTrue (success, 'Emulation test succeeded.') diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-1-arm.dat new file mode 100644 index 000000000000..64b2506f9d56 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r0, r0, r15" +triple=arm-apple-ios +opcode=0xe080000f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00003000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-1-thumb.dat new file mode 100644 index 000000000000..daa32d25a41a --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r0, r13, #0" +triple=thumb-apple-ios +opcode=0xa800 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-10-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-10-thumb.dat new file mode 100644 index 000000000000..bb3d760219b4 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-10-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add sp, r13" +triple=thumb-apple-ios +opcode=0x44ed +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x5fbffca0 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-11-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-11-thumb.dat new file mode 100644 index 000000000000..e26f2218cbcd --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-11-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add sp, r15" +triple=thumb-apple-ios +opcode=0x44fd +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fe02e50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-12-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-12-thumb.dat new file mode 100644 index 000000000000..a7f7344e7d97 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-12-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add sp, r8" +triple=thumb-apple-ios +opcode=0x44c5 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-2-arm.dat new file mode 100644 index 000000000000..cf6e0a9779a9 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-2-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r0, r13, #0" +triple=arm-apple-ios +opcode=0xe28d0000 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-2-thumb.dat new file mode 100644 index 000000000000..9a178a0a159d --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-2-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r0, sp, r0" +triple=thumb-apple-ios +opcode=0x4468 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-3-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-3-arm.dat new file mode 100644 index 000000000000..9fc44b79395c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-3-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r0, r1, r0, lsl #2" +triple=arm-apple-ios +opcode=0xe0810100 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000001 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-3-thumb.dat new file mode 100644 index 000000000000..c8f96ec09d3e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-3-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add.w r10, r13, #31" +triple=thumb-apple-ios +opcode=0xf10d0a1f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x2fdffe5f +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-4-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-4-arm.dat new file mode 100644 index 000000000000..12b40ed76fce --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-4-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r0, r2, r7, lsl r1" +triple=arm-apple-ios +opcode=0xe0820117 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x5fbffc82 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-4-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-4-thumb.dat new file mode 100644 index 000000000000..922b8ecdba53 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-4-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r3, r13, #16" +triple=thumb-apple-ios +opcode=0xab04 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x2fdffe58 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-5-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-5-arm.dat new file mode 100644 index 000000000000..dfb6a87f014d --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-5-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r10, r13, #31" +triple=arm-apple-ios +opcode=0xe28da01f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x2fdffe6f +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-5-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-5-thumb.dat new file mode 100644 index 000000000000..16ff517436b0 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-5-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r3, sp, r3" +triple=thumb-apple-ios +opcode=0x446b +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x2fdffe53 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-6-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-6-arm.dat new file mode 100644 index 000000000000..8a87eff5f002 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-6-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r12, r13, #24" +triple=arm-apple-ios +opcode=0xe28dc018 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x2fdffe68 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-6-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-6-thumb.dat new file mode 100644 index 000000000000..e8abd6950f20 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-6-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r5, r13, #32" +triple=thumb-apple-ios +opcode=0xad08 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x2fdffe68 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-7-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-7-arm.dat new file mode 100644 index 000000000000..312514246e3e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-7-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add r6, sp, #8" +triple=arm-apple-ios +opcode=0xe28d6008 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x2fdffe68 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-7-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-7-thumb.dat new file mode 100644 index 000000000000..506071309d7c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-7-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add sp, #16" +triple=thumb-apple-ios +opcode=0xb004 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe68 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-8-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-8-arm.dat new file mode 100644 index 000000000000..44a400f5287a --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-8-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add sp, r8" +triple=arm-apple-ios +opcode=0xe08dd008 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe68 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-8-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-8-thumb.dat new file mode 100644 index 000000000000..8bc6c789a51d --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-8-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add sp, #4" +triple=thumb-apple-ios +opcode=0xb001 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe5c +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-9-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-9-thumb.dat new file mode 100644 index 000000000000..832af4cf0b48 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-add-9-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="add sp, r10" +triple=thumb-apple-ios +opcode=0x44d5 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe5a +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-bic-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-bic-1-arm.dat new file mode 100644 index 000000000000..c1cd4f15ab3e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-bic-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="bic r4, r9" +triple=arm-apple-ios +opcode=0xe1c44009 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-bic-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-bic-1-thumb.dat new file mode 100644 index 000000000000..c62422650182 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-bic-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="bics r4, r6" +triple=thumb-apple-ios +opcode=0x43b4 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000000 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-1-arm.dat new file mode 100644 index 000000000000..e1922d9edb9a --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-1-arm.dat @@ -0,0 +1,119 @@ +InstructionEmulationState={ +assembly_string="ldmia r0!, {r1, r3}" +triple=arm-apple-ios +opcode=0xe8b0000a +before_state={ +memory={ +address=0x2fdffe50 +data_encoding=uint32_t +data=[ + 0x0 + 0x2e7c +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x00000000 +r2=0x2fdffe70 +r3=0x00002e7c +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-1-thumb.dat new file mode 100644 index 000000000000..60a805e09d83 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-1-thumb.dat @@ -0,0 +1,119 @@ +InstructionEmulationState={ +assembly_string="ldmia r0!, {r1, r3}" +triple=thumb-apple-ios +opcode=0xc80a +before_state={ +memory={ +address=0x2fdffe40 +data_encoding=uint32_t +data=[ +0x0 +0x2f84 +] +} +registers={ +r0=0x2fdffe40 +r1=0x2fdffe50 +r2=0x2fdffe60 +r3=0x2fdffe70 +r4=0x2fdffe80 +r5=0x2fdffe90 +r6=0x2fdffea0 +r7=0x2fdffe40 +r8=0x2fdffec0 +r9=0x2fdffed0 +r10=0x2fdffee0 +r11=0x2fdffef0 +r12=0x2fdfff00 +r13=0x2fdffe40 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe48 +r1=0x00000000 +r2=0x2fdffe60 +r3=0x00002f84 +r4=0x2fdffe80 +r5=0x2fdffe90 +r6=0x2fdffea0 +r7=0x2fdffe40 +r8=0x2fdffec0 +r9=0x2fdffed0 +r10=0x2fdffee0 +r11=0x2fdffef0 +r12=0x2fdfff00 +r13=0x2fdffe40 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-2-arm.dat new file mode 100644 index 000000000000..99401bbd2e21 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-2-arm.dat @@ -0,0 +1,123 @@ +InstructionEmulationState={ +assembly_string="ldmia r0!, {r2, r4, r6, r8, r10, r12}" +triple=arm-apple-ios +opcode=0xe8b01554 +before_state={ +memory={ +address=0x2fdffe20 +data_encoding=uint32_t +data=[ +0x0 +0x2e7c +0x1 +0x2fdffe84 +0x0 +0x0 +] +} +registers={ +r0=0x2fdffe20 +r1=0x2fdffe30 +r2=0x2fdffe40 +r3=0x0000001f +r4=0x2fdffe60 +r5=0x2fdffe70 +r6=0x2fdffe80 +r7=0x2fdffe20 +r8=0x2fdffea0 +r9=0x2fdffeb0 +r10=0x2fdffec0 +r11=0x2fdffed0 +r12=0x2fdffee0 +r13=0x2fdffe20 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe38 +r1=0x2fdffe30 +r2=0x00000000 +r3=0x0000001f +r4=0x00002e7c +r5=0x2fdffe70 +r6=0x00000001 +r7=0x2fdffe20 +r8=0x2fdffe84 +r9=0x2fdffeb0 +r10=0x00000000 +r11=0x2fdffed0 +r12=0x00000000 +r13=0x2fdffe20 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-2-thumb.dat new file mode 100644 index 000000000000..39ecf947c6de --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-2-thumb.dat @@ -0,0 +1,123 @@ +InstructionEmulationState={ +assembly_string="ldmia.w r0!,{r2,r4,r6,r8,r10,r12}" +triple=thumb-apple-ios +opcode=0xe8b01554 +before_state={ +memory={ +address=0x2fdffe50 +data_encoding=uint32_t +data=[ +0x0 +0x2f80 +0x1 +0x2fdffeac +0x0 +0x0 +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe68 +r1=0x2fdffe60 +r2=0x00000000 +r3=0x2fdffe80 +r4=0x00002f80 +r5=0x2fdffea0 +r6=0x00000001 +r7=0x2fdffe50 +r8=0x2fdffeac +r9=0x2fdffee0 +r10=0x00000000 +r11=0x2fdfff00 +r12=0x00000000 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-3-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-3-arm.dat new file mode 100644 index 000000000000..427d7b27485c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-3-arm.dat @@ -0,0 +1,119 @@ +InstructionEmulationState={ +assembly_string="ldmia r14!, {r1, r3}" +triple=arm-apple-ios +opcode=0xe8be000a +before_state={ +memory={ +address=0x2e7c +data_encoding=uint32_t +data=[ +0xe59fc00c +0xe08fc00c +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0xe59fc00c +r2=0x2fdffe70 +r3=0xe08fc00c +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e84 +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-3-thumb.dat new file mode 100644 index 000000000000..9738073c038e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldmia-3-thumb.dat @@ -0,0 +1,119 @@ +InstructionEmulationState={ +assembly_string="ldmia.w r14!, {r1, r3}" +triple=thumb-apple-ios +opcode=0xe8be000a +before_state={ +memory={ +address=0x2f80 +data_encoding=uint32_t +data=[ +0xe59fc00c +0xe08fc00c +] +} +registers={ +r0=0x2fdffe78 +r1=0x2fdffe88 +r2=0x2fdffe98 +r3=0x2fdffea8 +r4=0x2fdffeb8 +r5=0x2fdffec8 +r6=0x2fdffed8 +r7=0x2fdffe78 +r8=0x2fdffef8 +r9=0x2fdfff08 +r10=0x2fdfff18 +r11=0x2fdfff28 +r12=0x2fdfff38 +r13=0x2fdffe78 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe78 +r1=0xe59fc00c +r2=0x2fdffe98 +r3=0xe08fc00c +r4=0x2fdffeb8 +r5=0x2fdffec8 +r6=0x2fdffed8 +r7=0x2fdffe78 +r8=0x2fdffef8 +r9=0x2fdfff08 +r10=0x2fdfff18 +r11=0x2fdfff28 +r12=0x2fdfff38 +r13=0x2fdffe78 +r14=0x00002f88 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-1-arm.dat new file mode 100644 index 000000000000..307402fd2f5c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-1-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r0, [pc, #+24]" +triple=arm-apple-ios +opcode=0xe59f0018 +before_state={ +memory={ +address=0x3018 +data_encoding=uint32_t +data=[ +0x3030 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00003030 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-1-thumb.dat new file mode 100644 index 000000000000..471669b0cb65 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-1-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r0, [pc, #12]" +triple=thumb-apple-ios +opcode=0x4803 +before_state={ +memory={ +address=0x300c +data_encoding=uint32_t +data=[ +0x3024 +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00003024 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-10-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-10-thumb.dat new file mode 100644 index 000000000000..bc7693c9a684 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-10-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr.w r10,[ pc, #4]" +triple=thumb-apple-ios +opcode=0xf8dfa004 +before_state={ +memory={ +address=0x3000 +data_encoding=uint32_t +data=[ +0x2fe01000 +] +} +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fe01000 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-11-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-11-thumb.dat new file mode 100644 index 000000000000..b5bf18127cd6 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-11-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr.w r8, [pc , #0]" +triple=thumb-apple-ios +opcode=0xf8df8000 +before_state={ +memory={ +address=0x2ffc +data_encoding=uint32_t +data=[ +0xa0e1defe +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0xa0e1defe +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-12-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-12-thumb.dat new file mode 100644 index 000000000000..5c3a5485f0b3 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-12-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr.w r9, [pc, #-4]" +triple=thumb-apple-ios +opcode=0xf85f9004 +before_state={ +memory={ +address=0x2fec +data_encoding=uint32_t +data=[ +0x9004f85f +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x9004f85f +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-2-arm.dat new file mode 100644 index 000000000000..068297e3edce --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-2-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r0, [pc, #256]" +triple=arm-apple-ios +opcode=0xe59f0100 +before_state={ +memory={ +address=0x3100 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-2-thumb.dat new file mode 100644 index 000000000000..27727831d6d5 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-2-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r0, [pc, #+24]" +triple=thumb-apple-ios +opcode=0x4806 +before_state={ +memory={ +address=0x3018 +data_encoding=uint32_t +data=[ +0x3030 +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00003030 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-3-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-3-arm.dat new file mode 100644 index 000000000000..f0d6d8aaba63 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-3-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r0, [r13, #+24]" +triple=arm-apple-ios +opcode=0xe59d0018 +before_state={ +memory={ +address=0x2fdffe70 +data_encoding=uint32_t +data=[ +0x2fdffe80 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe80 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-3-thumb.dat new file mode 100644 index 000000000000..012e1c53e663 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-3-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r0, [pc, #256]" +triple=thumb-apple-ios +opcode=0x4840 +before_state={ +memory={ +address=0x3100 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-4-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-4-arm.dat new file mode 100644 index 000000000000..e2d2aa6aa887 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-4-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r1, [r5, #16]" +triple=arm-apple-ios +opcode=0xe5951010 +before_state={ +memory={ +address=0x2fdffeb8 +data_encoding=uint32_t +data=[ +0x7365742d +] +} +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x7365742d +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-4-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-4-thumb.dat new file mode 100644 index 000000000000..3eb6f167b488 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-4-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r0, [r13, #+24]" +triple=thumb-apple-ios +opcode=0x9806 +before_state={ +memory={ +address=0x2fdffe60 +data_encoding=uint32_t +data=[ +0x2fdffe70 +] +} +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe70 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-5-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-5-arm.dat new file mode 100644 index 000000000000..45210d9600d9 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-5-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r2, [r0]" +triple=arm-apple-ios +opcode=0xe5902000 +before_state={ +memory={ +address=0x2fdffe60 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x2fdffe60 +r1=0x2fdffe70 +r2=0x2fdffe80 +r3=0x0000001f +r4=0x2fdffea0 +r5=0x2fdffeb0 +r6=0x2fdffec0 +r7=0x2fdffe60 +r8=0x2fdffee0 +r9=0x2fdffef0 +r10=0x2fdfff00 +r11=0x2fdfff10 +r12=0x2fdfff20 +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe60 +r1=0x2fdffe70 +r2=0x00000000 +r3=0x0000001f +r4=0x2fdffea0 +r5=0x2fdffeb0 +r6=0x2fdffec0 +r7=0x2fdffe60 +r8=0x2fdffee0 +r9=0x2fdffef0 +r10=0x2fdfff00 +r11=0x2fdfff10 +r12=0x2fdfff20 +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-5-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-5-thumb.dat new file mode 100644 index 000000000000..45289e7e899c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-5-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r1, [pc, #0]" +triple=thumb-apple-ios +opcode=0x4900 +before_state={ +memory={ +address=0x3000 +data_encoding=uint32_t +data=[ +0x2fe01000 +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fe01000 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-6-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-6-arm.dat new file mode 100644 index 000000000000..0f379fd49809 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-6-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r2, [r6], +r8, lsl #2" +triple=arm-apple-ios +opcode=0xe6962108 +before_state={ +memory={ +address=0x2fdffea8 +data_encoding=uint32_t +data=[ +0x7365742d +] +} +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x0000001f +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x7365742d +r3=0x0000001f +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0xef5ff9c8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-6-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-6-thumb.dat new file mode 100644 index 000000000000..5b3c119e7437 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-6-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r1, [r5, #16]" +triple=thumb-apple-ios +opcode=0x6929 +before_state={ +memory={ +address=0x2fdffeb0 +data_encoding=uint32_t +data=[ +0x65742d62 +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x65742d62 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-7-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-7-arm.dat new file mode 100644 index 000000000000..ff425ef5284f --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-7-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r2, [sp, #24]" +triple=arm-apple-ios +opcode=0xe59d2018 +before_state={ +memory={ +address=0x2fdffe70 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000000 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-7-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-7-thumb.dat new file mode 100644 index 000000000000..db8d58796ca8 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-7-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r2, [r0]" +triple=thumb-apple-ios +opcode=0x6802 +before_state={ +memory={ +address=0x2fdffe58 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x2fdffe88 +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x00000000 +r3=0x2fdffe88 +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-8-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-8-arm.dat new file mode 100644 index 000000000000..c11dfc133e5b --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-8-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r3, [r11, #-8]" +triple=arm-apple-ios +opcode=0xe51b3008 +before_state={ +memory={ +address=0x2fdfff00 +data_encoding=uint32_t +data=[ +0x63387830 +] +} +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x63387830 +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-8-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-8-thumb.dat new file mode 100644 index 000000000000..01d7013ceaaa --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-8-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr r2, [sp, #24]" +triple=thumb-apple-ios +opcode=0x9a06 +before_state={ +memory={ +address=0x2fdffe68 +data_encoding=uint32_t +data=[ +0x2fdffe78 +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe78 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-9-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-9-thumb.dat new file mode 100644 index 000000000000..cc3c4db2f4de --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldr-9-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldr.w r3, [r11, #8]" +triple=thumb-apple-ios +opcode=0xf8db3008 +before_state={ +memory={ +address=0x2fdfff08 +data_encoding=uint32_t +data=[ +0x62343134 +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x62343134 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-1-arm.dat new file mode 100644 index 000000000000..970f8aa7c0a4 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-1-arm.dat @@ -0,0 +1,119 @@ +InstructionEmulationState={ +assembly_string="ldrd r0, r1, [r12, #+4]" +triple=arm-apple-ios +opcode=0xe1cc00d4 +before_state={ +memory={ +address=0x2fdfff14 +data_encoding=uint32_t +data=[ +0x30313038 +0x31623039 +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x30313038 +r1=0x31623039 +r2=0x2fdffe70 +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-1-thumb.dat new file mode 100644 index 000000000000..7ab41ce35dbe --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-1-thumb.dat @@ -0,0 +1,119 @@ +InstructionEmulationState={ +assembly_string="ldrd r0, r1, [r12, #+4]" +triple=thumb-apple-ios +opcode=0xe9dc0101 +before_state={ +memory={ +address=0x2fdfff3c +data_encoding=uint32_t +data=[ +0x0 +0x0 +] +} +registers={ +r0=0x2fdffe78 +r1=0x2fdffe88 +r2=0x2fdffe98 +r3=0x2fdffea8 +r4=0x2fdffeb8 +r5=0x2fdffec8 +r6=0x2fdffed8 +r7=0x2fdffe78 +r8=0x2fdffef8 +r9=0x2fdfff08 +r10=0x2fdfff18 +r11=0x2fdfff28 +r12=0x2fdfff38 +r13=0x2fdffe78 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000000 +r2=0x2fdffe98 +r3=0x2fdffea8 +r4=0x2fdffeb8 +r5=0x2fdffec8 +r6=0x2fdffed8 +r7=0x2fdffe78 +r8=0x2fdffef8 +r9=0x2fdfff08 +r10=0x2fdfff18 +r11=0x2fdfff28 +r12=0x2fdfff38 +r13=0x2fdffe78 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-2-thumb.dat new file mode 100644 index 000000000000..70fe6f5cfff7 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrd-2-thumb.dat @@ -0,0 +1,119 @@ +InstructionEmulationState={ +assembly_string="ldrd r4, r5, [pc, #-0]" +triple=thumb-apple-ios +opcode=0xe9df4500 +before_state={ +memory={ +address=0x2ffc +data_encoding=uint32_t +data=[ +0xa0e1defe +0x2fe01000 +] +} +registers={ +r0=0x2fdffe78 +r1=0x2fdffe88 +r2=0x2fdffe98 +r3=0x2fdffea8 +r4=0x2fdffeb8 +r5=0x2fdffec8 +r6=0x2fdffed8 +r7=0x2fdffe78 +r8=0x2fdffef8 +r9=0x2fdfff08 +r10=0x2fdfff18 +r11=0x2fdfff28 +r12=0x2fdfff38 +r13=0x2fdffe78 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe78 +r1=0x2fdffe88 +r2=0x2fdffe98 +r3=0x2fdffea8 +r4=0xa0e1defe +r5=0x2fe01000 +r6=0x2fdffed8 +r7=0x2fdffe78 +r8=0x2fdffef8 +r9=0x2fdfff08 +r10=0x2fdfff18 +r11=0x2fdfff28 +r12=0x2fdfff38 +r13=0x2fdffe78 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrh-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrh-1-thumb.dat new file mode 100644 index 000000000000..2a47002fb658 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrh-1-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldrh r0, [r2, #16]" +triple=thumb-apple-ios +opcode=0x8a10 +before_state={ +memory={ +address=0x2fdffe78 +data_encoding=uint32_t +data=[ +0x762f +] +} +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x0000762f +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrsh-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrsh-1-arm.dat new file mode 100644 index 000000000000..cecf397f1d49 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrsh-1-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldrsh r0, [r2], #+15" +triple=arm-apple-ios +opcode=0xe0d200ff +before_state={ +memory={ +address=0x2fdffe70 +data_encoding=uint32_t +data=[ +0xfffffeeb +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0xfffffeeb +r1=0x2fdffe60 +r2=0x2fdffe7f +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrsh-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrsh-2-arm.dat new file mode 100644 index 000000000000..660b3d3b5db7 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-ldrsh-2-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="ldrsh r14, [r2], #+15" +triple=arm-apple-ios +opcode=0xe0d2e0ff +before_state={ +memory={ +address=0x2fdffe70 +data_encoding=uint32_t +data=[ +0xfffffeec +] +} +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe7f +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0xfffffeec +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-1-arm.dat new file mode 100644 index 000000000000..232bc967fede --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r12, #256" +triple=arm-apple-ios +opcode=0xe3a0cc01 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x00000100 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-1-thumb.dat new file mode 100644 index 000000000000..569510dbd3ba --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov ip, pc" +triple=thumb-apple-ios +opcode=0x46fc +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x00003000 +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-10-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-10-thumb.dat new file mode 100644 index 000000000000..6bf9b0d7ef7e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-10-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r0, r15" +triple=thumb-apple-ios +opcode=0x4678 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00003000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-11-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-11-thumb.dat new file mode 100644 index 000000000000..82043ef335b1 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-11-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r0, r7" +triple=thumb-apple-ios +opcode=0x4638 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-12-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-12-thumb.dat new file mode 100644 index 000000000000..1fce18af917c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-12-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov.w r12, #256" +triple=thumb-apple-ios +opcode=0xf44f7c80 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x00000100 +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-13-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-13-thumb.dat new file mode 100644 index 000000000000..d1c1a4428c9f --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-13-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r12, r13" +triple=thumb-apple-ios +opcode=0x46ec +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x2fdffe50 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-14-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-14-thumb.dat new file mode 100644 index 000000000000..dfd89c265fbd --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-14-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r14, r2" +triple=thumb-apple-ios +opcode=0x4696 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00000002 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-15-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-15-thumb.dat new file mode 100644 index 000000000000..0ff8e5d7dc5c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-15-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r1, r14" +triple=thumb-apple-ios +opcode=0x4671 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00002f84 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-16-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-16-thumb.dat new file mode 100644 index 000000000000..1baf42dfc56c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-16-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r2, ip" +triple=thumb-apple-ios +opcode=0x4662 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x0000000c +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-17-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-17-thumb.dat new file mode 100644 index 000000000000..70cee03fb54f --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-17-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r2, r13" +triple=thumb-apple-ios +opcode=0x466a +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x2fdffe50 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-18-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-18-thumb.dat new file mode 100644 index 000000000000..1893e188b0d0 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-18-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r2, r9" +triple=thumb-apple-ios +opcode=0x464a +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000009 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-19-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-19-thumb.dat new file mode 100644 index 000000000000..238e16b4aa42 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-19-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r3, r12" +triple=thumb-apple-ios +opcode=0x4663 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000000c +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-2-arm.dat new file mode 100644 index 000000000000..6d6c0cbf80c5 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-2-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r12, r13" +triple=arm-apple-ios +opcode=0xe1a0c00d +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x2fdffe58 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-2-thumb.dat new file mode 100644 index 000000000000..ad6854427fbf --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-2-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov ip, r8" +triple=thumb-apple-ios +opcode=0x46c4 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x00000008 +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-20-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-20-thumb.dat new file mode 100644 index 000000000000..15ecd2ebfa0a --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-20-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r3, r13" +triple=thumb-apple-ios +opcode=0x466b +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x2fdffe50 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-21-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-21-thumb.dat new file mode 100644 index 000000000000..6a38f1671b7c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-21-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r3, sp" +triple=thumb-apple-ios +opcode=0x466b +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x2fdffe58 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-22-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-22-thumb.dat new file mode 100644 index 000000000000..242b56824194 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-22-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r4, r11" +triple=thumb-apple-ios +opcode=0x465c +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x0000000b +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-23-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-23-thumb.dat new file mode 100644 index 000000000000..7f57440a8421 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-23-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r5, r10" +triple=thumb-apple-ios +opcode=0x4655 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x0000000a +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-24-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-24-thumb.dat new file mode 100644 index 000000000000..9a1756e8473b --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-24-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r6, r9" +triple=thumb-apple-ios +opcode=0x464e +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000009 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-25-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-25-thumb.dat new file mode 100644 index 000000000000..c2fd6a739644 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-25-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r7, lr" +triple=thumb-apple-ios +opcode=0x4677 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x00002f84 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-26-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-26-thumb.dat new file mode 100644 index 000000000000..2cc155d8071c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-26-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r7, r8" +triple=thumb-apple-ios +opcode=0x4647 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x00000008 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-27-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-27-thumb.dat new file mode 100644 index 000000000000..0b35377b08ee --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-27-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r9, pc" +triple=thumb-apple-ios +opcode=0x46f9 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00003000 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-28-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-28-thumb.dat new file mode 100644 index 000000000000..f7d7778a2512 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-28-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov sp, ip" +triple=thumb-apple-ios +opcode=0x46e5 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x0000000c +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-29-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-29-thumb.dat new file mode 100644 index 000000000000..5e9098f99d2f --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-29-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov sp, pc" +triple=thumb-apple-ios +opcode=0x46fd +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x00003000 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-3-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-3-arm.dat new file mode 100644 index 000000000000..1fe7155bd3a3 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-3-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r14, r2" +triple=arm-apple-ios +opcode=0xe1a0e002 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00000002 +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-3-thumb.dat new file mode 100644 index 000000000000..8ce129682bf9 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-3-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov ip, sp" +triple=thumb-apple-ios +opcode=0x46ec +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x2fdffe58 +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-30-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-30-thumb.dat new file mode 100644 index 000000000000..a0dddad6b5a1 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-30-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov sp, r7" +triple=thumb-apple-ios +opcode=0x46bd +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-31-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-31-thumb.dat new file mode 100644 index 000000000000..20d8dc7c06f6 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-31-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="movs r3, #1" +triple=thumb-apple-ios +opcode=0x2301 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000001 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x20000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-4-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-4-arm.dat new file mode 100644 index 000000000000..8ef83b268055 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-4-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r2, r9" +triple=arm-apple-ios +opcode=0xe1a02009 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000009 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-4-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-4-thumb.dat new file mode 100644 index 000000000000..f854ba26641e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-4-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov lr, pc" +triple=thumb-apple-ios +opcode=0x46fe +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00003000 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-5-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-5-arm.dat new file mode 100644 index 000000000000..ee85779083f6 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-5-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r3, #2147483648" +triple=arm-apple-ios +opcode=0xe3a03102 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x80000000 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-5-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-5-thumb.dat new file mode 100644 index 000000000000..d6140bdb92d7 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-5-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov lr, r2" +triple=thumb-apple-ios +opcode=0x4696 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00000002 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-6-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-6-arm.dat new file mode 100644 index 000000000000..863a6d3e9bd8 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-6-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov r3, r13" +triple=arm-apple-ios +opcode=0xe1a0300d +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x2fdffe60 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-6-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-6-thumb.dat new file mode 100644 index 000000000000..fac96bf5a5a8 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-6-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov pc, ip" +triple=thumb-apple-ios +opcode=0x46e7 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x0000000c +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-7-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-7-thumb.dat new file mode 100644 index 000000000000..c537d30cb249 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-7-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov pc, lr" +triple=thumb-apple-ios +opcode=0x46f7 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002f84 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-8-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-8-thumb.dat new file mode 100644 index 000000000000..b724c79a0d56 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-8-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov pc, r4" +triple=thumb-apple-ios +opcode=0x46a7 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00000004 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-9-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-9-thumb.dat new file mode 100644 index 000000000000..ccf7ea6c3423 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mov-9-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mov pc, sp" +triple=thumb-apple-ios +opcode=0x46ef +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x2fdffe58 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-moveq-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-moveq-1-arm.dat new file mode 100644 index 000000000000..f46b1bfd6e87 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-moveq-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="moveq r3, #1" +triple=arm-apple-ios +opcode=0x3a03001 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000001 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-movs-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-movs-1-arm.dat new file mode 100644 index 000000000000..38292508d43c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-movs-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="movs r12, r13" +triple=arm-apple-ios +opcode=0xe1b0c00d +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x2fdffe58 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x20000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-1-arm.dat new file mode 100644 index 000000000000..024dd9fa4bcb --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mvn r14, #1" +triple=arm-apple-ios +opcode=0xe3e0e001 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0xfffffffe +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-1-thumb.dat new file mode 100644 index 000000000000..c5c385c8522d --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mvn r0, #1" +triple=thumb-apple-ios +opcode=0xf06f0001 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe98 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe98 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0xfffffffe +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe98 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe98 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-2-arm.dat new file mode 100644 index 000000000000..10df542fcad5 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-2-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mvn r0, #1" +triple=arm-apple-ios +opcode=0xe3e00001 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe68 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe68 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0xfffffffe +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe68 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe68 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-2-thumb.dat new file mode 100644 index 000000000000..3c2f6f4da279 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-2-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mvn r0, #31" +triple=thumb-apple-ios +opcode=0xf06f001f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe90 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe90 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0xffffffe0 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe90 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe90 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-3-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-3-arm.dat new file mode 100644 index 000000000000..742708978b6d --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-3-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mvn r0, #31" +triple=arm-apple-ios +opcode=0xe3e0001f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0xffffffe0 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-3-thumb.dat new file mode 100644 index 000000000000..ff4ca7331a9f --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-3-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mvn r14, #1" +triple=thumb-apple-ios +opcode=0xf06f0e01 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe90 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe90 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe90 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe90 +r14=0xfffffffe +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-4-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-4-arm.dat new file mode 100644 index 000000000000..4efc18cc5f28 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-4-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mvn r3, r8" +triple=arm-apple-ios +opcode=0xe1e03008 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0xfffffff7 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-4-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-4-thumb.dat new file mode 100644 index 000000000000..f8b1569e9063 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-mvn-4-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="mvns r3, r8" +triple=thumb-apple-ios +opcode=0xea7f0308 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe90 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe90 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0xfffffff7 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe90 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe90 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0xa0000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-1-arm.dat new file mode 100644 index 000000000000..07163f80aeb6 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-1-arm.dat @@ -0,0 +1,121 @@ +InstructionEmulationState={ +assembly_string="pop {r3, r4, r8, r10}" +triple=arm-apple-ios +opcode=0xe8bd0518 +before_state={ +memory={ +address=0x2fdffe50 +data_encoding=uint32_t +data=[ +0x0 +0x2e7c +0x1 +0x2fdffeac +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000000 +r4=0x00002e7c +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000001 +r9=0x00000009 +r10=0x2fdffeac +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-1-thumb.dat new file mode 100644 index 000000000000..fad6765be9a8 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-1-thumb.dat @@ -0,0 +1,121 @@ +InstructionEmulationState={ +assembly_string="pop.w {r3, r4, r8, r10}" +triple=thumb-apple-ios +opcode=0xe8bd0518 +before_state={ +memory={ +address=0x2fdffe38 +data_encoding=uint32_t +data=[ +0x0 +0x2f80 +0x1000 +0x1 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000000 +r4=0x00002f80 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00001000 +r9=0x00000009 +r10=0x00000001 +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-2-arm.dat new file mode 100644 index 000000000000..4b6ae43ca285 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-2-arm.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="pop {r9}" +triple=arm-apple-ios +opcode=0xe8bd0200 +before_state={ +memory={ +address=0x2fdffe70 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe70 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe70 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe70 +r8=0x00000008 +r9=0x00000000 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe74 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-2-thumb.dat new file mode 100644 index 000000000000..9f3efe57e9d2 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-2-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="pop {r3}" +triple=thumb-apple-ios +opcode=0xbc08 +before_state={ +memory={ +address=0x2fdffe60 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000000 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe64 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-3-thumb.dat new file mode 100644 index 000000000000..a2adc418c4a5 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-pop-3-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="pop {r6}" +triple=thumb-apple-ios +opcode=0xbc40 +before_state={ +memory={ +address=0x2fdffe60 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000000 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe64 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-1-arm.dat new file mode 100644 index 000000000000..b2f6e0965ab9 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="push {r0, r1, r2, r3, r7, r14}" +triple=arm-apple-ios +opcode=0xe92d408f +before_state={ +registers={ +r0=0x2fdffe30 +r1=0x2fdffe40 +r2=0x2fdffe50 +r3=0x0000001f +r4=0x2fdffe70 +r5=0x2fdffe80 +r6=0x2fdffe90 +r7=0x2fdffe30 +r8=0x2fdffeb0 +r9=0x2fdffec0 +r10=0x2fdffed0 +r11=0x2fdffee0 +r12=0x2fdffef0 +r13=0x2fdffe30 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe30 +r1=0x2fdffe40 +r2=0x2fdffe50 +r3=0x0000001f +r4=0x2fdffe70 +r5=0x2fdffe80 +r6=0x2fdffe90 +r7=0x2fdffe30 +r8=0x2fdffeb0 +r9=0x2fdffec0 +r10=0x2fdffed0 +r11=0x2fdffee0 +r12=0x2fdffef0 +r13=0x2fdffe18 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-1-thumb.dat new file mode 100644 index 000000000000..6f5b29ed1990 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="push {r0, r1, r2, r3, r7, r14}" +triple=thumb-apple-ios +opcode=0xb58f +before_state={ +registers={ +r0=0x2fdffe28 +r1=0x2fdffe38 +r2=0x2fdffe48 +r3=0x2fdffe58 +r4=0x2fdffe68 +r5=0x2fdffe78 +r6=0x2fdffe88 +r7=0x2fdffe28 +r8=0x2fdffea8 +r9=0x2fdffeb8 +r10=0x2fdffec8 +r11=0x2fdffed8 +r12=0x2fdffee8 +r13=0x2fdffe28 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe28 +r1=0x2fdffe38 +r2=0x2fdffe48 +r3=0x2fdffe58 +r4=0x2fdffe68 +r5=0x2fdffe78 +r6=0x2fdffe88 +r7=0x2fdffe28 +r8=0x2fdffea8 +r9=0x2fdffeb8 +r10=0x2fdffec8 +r11=0x2fdffed8 +r12=0x2fdffee8 +r13=0x2fdffe10 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-2-arm.dat new file mode 100644 index 000000000000..6f1f4389e80e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-2-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="push {r6}" +triple=arm-apple-ios +opcode=0xe92d0040 +before_state={ +registers={ +r0=0x2fdffe68 +r1=0x2fdffe78 +r2=0x2fdffe88 +r3=0x0000001f +r4=0x2fdffea8 +r5=0x2fdffeb8 +r6=0x2fdffec8 +r7=0x2fdffe68 +r8=0x2fdffee8 +r9=0x2fdffef8 +r10=0x2fdfff08 +r11=0x2fdfff18 +r12=0x2fdfff28 +r13=0x2fdffe68 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe68 +r1=0x2fdffe78 +r2=0x2fdffe88 +r3=0x0000001f +r4=0x2fdffea8 +r5=0x2fdffeb8 +r6=0x2fdffec8 +r7=0x2fdffe68 +r8=0x2fdffee8 +r9=0x2fdffef8 +r10=0x2fdfff08 +r11=0x2fdfff18 +r12=0x2fdfff28 +r13=0x2fdffe64 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-2-thumb.dat new file mode 100644 index 000000000000..23e4df446bf4 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-2-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="push {r6}" +triple=thumb-apple-ios +opcode=0xb440 +before_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x2fdffe88 +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x2fdffe88 +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe54 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-3-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-3-arm.dat new file mode 100644 index 000000000000..06847a5ff3ba --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-3-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="push {r7, r14}" +triple=arm-apple-ios +opcode=0xe92d4080 +before_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-3-thumb.dat new file mode 100644 index 000000000000..e3330f537cb6 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-push-3-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="push {r7, r14}" +triple=thumb-apple-ios +opcode=0xb580 +before_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-1-arm.dat new file mode 100644 index 000000000000..70a5be3cb69c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str r0, [r13]" +triple=arm-apple-ios +opcode=0xe58d0000 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-1-thumb.dat new file mode 100644 index 000000000000..3559652ce6c1 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str r0, [r13]" +triple=thumb-apple-ios +opcode=0x9000 +before_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-2-arm.dat new file mode 100644 index 000000000000..3d77724b4f96 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-2-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str r1, [r0, #+4]" +triple=arm-apple-ios +opcode=0xe5801004 +before_state={ +registers={ +r0=0x2fdffe60 +r1=0x2fdffe70 +r2=0x2fdffe80 +r3=0x0000001f +r4=0x2fdffea0 +r5=0x2fdffeb0 +r6=0x2fdffec0 +r7=0x2fdffe60 +r8=0x2fdffee0 +r9=0x2fdffef0 +r10=0x2fdfff00 +r11=0x2fdfff10 +r12=0x2fdfff20 +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe60 +r1=0x2fdffe70 +r2=0x2fdffe80 +r3=0x0000001f +r4=0x2fdffea0 +r5=0x2fdffeb0 +r6=0x2fdffec0 +r7=0x2fdffe60 +r8=0x2fdffee0 +r9=0x2fdffef0 +r10=0x2fdfff00 +r11=0x2fdfff10 +r12=0x2fdfff20 +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-2-thumb.dat new file mode 100644 index 000000000000..73e8cd3e548e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-2-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str r0, [sp, #0]" +triple=thumb-apple-ios +opcode=0x9000 +before_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-3-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-3-arm.dat new file mode 100644 index 000000000000..97c90a0050c3 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-3-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str r2, [r0]" +triple=arm-apple-ios +opcode=0xe5802000 +before_state={ +registers={ +r0=0x2fdffe60 +r1=0x2fdffe70 +r2=0x2fdffe80 +r3=0x0000001f +r4=0x2fdffea0 +r5=0x2fdffeb0 +r6=0x2fdffec0 +r7=0x2fdffe60 +r8=0x2fdffee0 +r9=0x2fdffef0 +r10=0x2fdfff00 +r11=0x2fdfff10 +r12=0x2fdfff20 +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe60 +r1=0x2fdffe70 +r2=0x2fdffe80 +r3=0x0000001f +r4=0x2fdffea0 +r5=0x2fdffeb0 +r6=0x2fdffec0 +r7=0x2fdffe60 +r8=0x2fdffee0 +r9=0x2fdffef0 +r10=0x2fdfff00 +r11=0x2fdfff10 +r12=0x2fdfff20 +r13=0x2fdffe60 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-3-thumb.dat new file mode 100644 index 000000000000..8e891c042879 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-3-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str r1, [sp, #32]" +triple=thumb-apple-ios +opcode=0x9108 +before_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x2fdffe80 +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-4-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-4-arm.dat new file mode 100644 index 000000000000..ed3b54eaf38a --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-4-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str r2, [r13, #+4]" +triple=arm-apple-ios +opcode=0xe58d2004 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-4-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-4-thumb.dat new file mode 100644 index 000000000000..ec9e2dc1e549 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-4-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str.w r7, [r13, #-12]!" +triple=thumb-apple-ios +opcode=0xf84d7d0c +before_state={ +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe48 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe48 +r1=0x2fdffe58 +r2=0x2fdffe68 +r3=0x2fdffe78 +r4=0x2fdffe88 +r5=0x2fdffe98 +r6=0x2fdffea8 +r7=0x2fdffe48 +r8=0x2fdffec8 +r9=0x2fdffed8 +r10=0x2fdffee8 +r11=0x2fdffef8 +r12=0x2fdfff08 +r13=0x2fdffe3c +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-5-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-5-arm.dat new file mode 100644 index 000000000000..98b8dfb16b37 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-str-5-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="str r7, [r13, #-12]!" +triple=arm-apple-ios +opcode=0xe52d700c +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe4c +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strb-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strb-1-arm.dat new file mode 100644 index 000000000000..2f7320d007ad --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strb-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="strb r0, [r2], #+15" +triple=arm-apple-ios +opcode=0xe4c2000f +before_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe87 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strb-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strb-2-arm.dat new file mode 100644 index 000000000000..495c8e720a11 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strb-2-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="strb r3, [r0, #+8]" +triple=arm-apple-ios +opcode=0xe5c03008 +before_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strbt-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strbt-1-arm.dat new file mode 100644 index 000000000000..494c49af9527 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strbt-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="strbt r14, [r2], #+15" +triple=arm-apple-ios +opcode=0xe4e2e00f +before_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe70 +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x2fdffe60 +r2=0x2fdffe7f +r3=0x0000001f +r4=0x2fdffe90 +r5=0x2fdffea0 +r6=0x2fdffeb0 +r7=0x2fdffe50 +r8=0x2fdffed0 +r9=0x2fdffee0 +r10=0x2fdffef0 +r11=0x2fdfff00 +r12=0x2fdfff10 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strd-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strd-1-thumb.dat new file mode 100644 index 000000000000..4876f8741b0e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strd-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="strd r10, r11, [r6, #+28]" +triple=thumb-apple-ios +opcode=0xe9c6ab07 +before_state={ +registers={ +r0=0x2fdffe70 +r1=0x2fdffe80 +r2=0x2fdffe90 +r3=0x2fdffea0 +r4=0x2fdffeb0 +r5=0x2fdffec0 +r6=0x2fdffed0 +r7=0x2fdffe70 +r8=0x2fdffef0 +r9=0x2fdfff00 +r10=0x2fdfff10 +r11=0x2fdfff20 +r12=0x2fdfff30 +r13=0x2fdffe70 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe70 +r1=0x2fdffe80 +r2=0x2fdffe90 +r3=0x2fdffea0 +r4=0x2fdffeb0 +r5=0x2fdffec0 +r6=0x2fdffed0 +r7=0x2fdffe70 +r8=0x2fdffef0 +r9=0x2fdfff00 +r10=0x2fdfff10 +r11=0x2fdfff20 +r12=0x2fdfff30 +r13=0x2fdffe70 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strt-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strt-1-arm.dat new file mode 100644 index 000000000000..9fc311565eaa --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-strt-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="strt r0, [r2], #+15" +triple=arm-apple-ios +opcode=0xe4a2000f +before_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe78 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe58 +r1=0x2fdffe68 +r2=0x2fdffe87 +r3=0x0000001f +r4=0x2fdffe98 +r5=0x2fdffea8 +r6=0x2fdffeb8 +r7=0x2fdffe58 +r8=0x2fdffed8 +r9=0x2fdffee8 +r10=0x2fdffef8 +r11=0x2fdfff08 +r12=0x2fdfff18 +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-1-arm.dat new file mode 100644 index 000000000000..98c435d616a5 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r0, sp, r8" +triple=arm-apple-ios +opcode=0xe04d0008 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x2fdffe50 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-1-thumb.dat new file mode 100644 index 000000000000..482480d0cd43 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub.w r10, sp, #16" +triple=thumb-apple-ios +opcode=0xf1ad0a10 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x2fdffe30 +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-10-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-10-arm.dat new file mode 100644 index 000000000000..9f07a947f7db --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-10-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r7, r12, #8" +triple=arm-apple-ios +opcode=0xe24c7008 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x00000004 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-2-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-2-arm.dat new file mode 100644 index 000000000000..9a67268196e2 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-2-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r10, r12, #31" +triple=arm-apple-ios +opcode=0xe24ca01f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0xffffffed +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-2-thumb.dat new file mode 100644 index 000000000000..11d213803e2e --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-2-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub.w r10, sp, #31" +triple=thumb-apple-ios +opcode=0xf1ad0a1f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x2fdffe21 +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-3-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-3-arm.dat new file mode 100644 index 000000000000..4dfe680ce470 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-3-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r12, r13, #12" +triple=arm-apple-ios +opcode=0xe24dc00c +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x2fdffe44 +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-3-thumb.dat new file mode 100644 index 000000000000..a017f5284302 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-3-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub.w r12, sp, #31" +triple=thumb-apple-ios +opcode=0xf1ad0c1f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe40 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x2fdffe21 +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-4-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-4-arm.dat new file mode 100644 index 000000000000..50d13c994755 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-4-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r13, r13, #24" +triple=arm-apple-ios +opcode=0xe24dd018 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-4-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-4-thumb.dat new file mode 100644 index 000000000000..214808d6a429 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-4-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub.w r1, sp, r3, lsl #2" +triple=thumb-apple-ios +opcode=0xebad0183 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe30 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe30 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x2fdffe24 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe30 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe30 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-5-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-5-arm.dat new file mode 100644 index 000000000000..58de27b5d4e2 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-5-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r13, r13, #4" +triple=arm-apple-ios +opcode=0xe24dd004 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe54 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-5-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-5-thumb.dat new file mode 100644 index 000000000000..126882866859 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-5-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub.w r7, sp, #1" +triple=thumb-apple-ios +opcode=0xf1ad0701 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe4f +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-6-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-6-arm.dat new file mode 100644 index 000000000000..be6891307dbe --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-6-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r1, r13, #4" +triple=arm-apple-ios +opcode=0xe24d1004 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x2fdffe54 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-6-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-6-thumb.dat new file mode 100644 index 000000000000..d48c42eb8d8c --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-6-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub.w sp, sp, #4" +triple=thumb-apple-ios +opcode=0xf1ad0d04 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe4c +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-8-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-8-arm.dat new file mode 100644 index 000000000000..21b17dfbcd80 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-8-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r2, r2, r3" +triple=arm-apple-ios +opcode=0xe0422003 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0xffffffe3 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-9-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-9-arm.dat new file mode 100644 index 000000000000..06888ad3a4b7 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-sub-9-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="sub r4, r12, r7" +triple=arm-apple-ios +opcode=0xe04c4007 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0xd02001b4 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe58 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe58 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-1-arm.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-1-arm.dat new file mode 100644 index 000000000000..856a55749b18 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-1-arm.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs r10, r13, #31" +triple=arm-apple-ios +opcode=0xe25da01f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ff8 +cpsr=0x60000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x0000001f +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x2fdffe31 +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002e7c +r15=0x00002ffc +cpsr=0x20000010 +s0=0x00000000 +s1=0x00000001 +s2=0x00000002 +s3=0x00000003 +s4=0x00000004 +s5=0x00000005 +s6=0x00000006 +s7=0x00000007 +s8=0x00000008 +s9=0x00000009 +s10=0x0000000a +s11=0x0000000b +s12=0x0000000c +s13=0x0000000d +s14=0x0000000e +s15=0x0000000f +s16=0x00000010 +s17=0x00000011 +s18=0x00000012 +s19=0x00000013 +s20=0x00000014 +s21=0x00000015 +s22=0x00000016 +s23=0x00000017 +s24=0x00000018 +s25=0x00000019 +s26=0x0000001a +s27=0x0000001b +s28=0x0000001c +s29=0x0000001d +s30=0x0000001e +s31=0x0000001f +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-1-thumb.dat new file mode 100644 index 000000000000..63aa7b8ed13f --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs r0, r6, #0" +triple=thumb-apple-ios +opcode=0x1e30 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000006 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x20000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-10-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-10-thumb.dat new file mode 100644 index 000000000000..75938b4e1d5b --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-10-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs.w r1, sp, #4" +triple=thumb-apple-ios +opcode=0xf1bd0104 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x2fdffe44 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x20000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-2-thumb.dat new file mode 100644 index 000000000000..74bddcea97cb --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-2-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs r0, r7, r5" +triple=thumb-apple-ios +opcode=0x1b78 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x2fdffe43 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x20000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-3-thumb.dat new file mode 100644 index 000000000000..965ebf370176 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-3-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs.w r10, r13, #31" +triple=thumb-apple-ios +opcode=0xf1bd0a1f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x2fdffe19 +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x20000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-4-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-4-thumb.dat new file mode 100644 index 000000000000..068724170d57 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-4-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs r1, r3, #4" +triple=thumb-apple-ios +opcode=0x1f19 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0xffffffff +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x80000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-5-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-5-thumb.dat new file mode 100644 index 000000000000..72ed2ef25c0f --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-5-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs r4, r2, r6" +triple=thumb-apple-ios +opcode=0x1b94 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0xfffffffc +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe48 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe48 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x80000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-6-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-6-thumb.dat new file mode 100644 index 000000000000..67133b2832e5 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-6-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs r7, r5, #7" +triple=thumb-apple-ios +opcode=0x1fef +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe50 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0xfffffffe +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f84 +r15=0x00002ffe +cpsr=0x80000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-8-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-8-thumb.dat new file mode 100644 index 000000000000..965ebf370176 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-8-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs.w r10, r13, #31" +triple=thumb-apple-ios +opcode=0xf1bd0a1f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x2fdffe19 +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x20000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-9-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-9-thumb.dat new file mode 100644 index 000000000000..ea1597743162 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-subs-9-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="subs.w r10, sp, #31" +triple=thumb-apple-ios +opcode=0xf1bd0a1f +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe38 +r8=0x00000008 +r9=0x00000009 +r10=0x2fdffe19 +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe38 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x20000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-1-thumb.dat new file mode 100644 index 000000000000..22dd9d88b4b6 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-1-thumb.dat @@ -0,0 +1,125 @@ +InstructionEmulationState={ +assembly_string="vpop {d11, d12, d13, d14}" +triple=thumb-apple-ios +opcode=0xecbdbb08 +before_state={ +memory={ +address=0x2fdffe60 +data_encoding=uint32_t +data=[ +0x0 +0x2f80 +0x1000 +0x1 +0x2fdffebc +0x0 +0x0 +0x2fdffe8c +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe80 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00002f80 +s24=0x00001000 +s25=0x00000001 +s26=0x2fdffebc +s27=0x00000000 +s28=0x00000000 +s29=0x2fdffe8c +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-2-thumb.dat new file mode 100644 index 000000000000..e69cd9eef2d5 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-2-thumb.dat @@ -0,0 +1,118 @@ +InstructionEmulationState={ +assembly_string="vpop {s0}" +triple=thumb-apple-ios +opcode=0xecbd0a01 +before_state={ +memory={ +address=0x2fdffe98 +data_encoding=uint32_t +data=[ +0x0 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe98 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe98 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe98 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe9c +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-3-thumb.dat new file mode 100644 index 000000000000..375a92eaaab5 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpop-3-thumb.dat @@ -0,0 +1,121 @@ +InstructionEmulationState={ +assembly_string="vpop {s11, s12, s13, s14}" +triple=thumb-apple-ios +opcode=0xecfd5a04 +before_state={ +memory={ +address=0x2fdffe60 +data_encoding=uint32_t +data=[ +0x0 +0x2f80 +0x1000 +0x1 +] +} +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe70 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00002f80 +s13=0x00001000 +s14=0x00000001 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-1-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-1-thumb.dat new file mode 100644 index 000000000000..ac4ef56be665 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-1-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="vpush {d11, d12, d13, d14}" +triple=thumb-apple-ios +opcode=0xed2dbb08 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe40 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-2-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-2-thumb.dat new file mode 100644 index 000000000000..58055d6539ce --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-2-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="vpush {s0}" +triple=thumb-apple-ios +opcode=0xed2d0a01 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe90 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe90 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe90 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe8c +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-3-thumb.dat b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-3-thumb.dat new file mode 100644 index 000000000000..2658b43bc883 --- /dev/null +++ b/packages/Python/lldbsuite/test/arm_emulation/new-test-files/test-vpush-3-thumb.dat @@ -0,0 +1,111 @@ +InstructionEmulationState={ +assembly_string="vpush {s11, s12, s13, s14}" +triple=thumb-apple-ios +opcode=0xed6d5a04 +before_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe60 +r14=0x00002f80 +r15=0x00002ff8 +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +after_state={ +registers={ +r0=0x00000000 +r1=0x00000001 +r2=0x00000002 +r3=0x00000003 +r4=0x00000004 +r5=0x00000005 +r6=0x00000006 +r7=0x2fdffe60 +r8=0x00000008 +r9=0x00000009 +r10=0x0000000a +r11=0x0000000b +r12=0x0000000c +r13=0x2fdffe50 +r14=0x00002f80 +r15=0x00002ffc +cpsr=0x60000030 +s0=0x00000000 +s1=0x00000000 +s2=0x00000000 +s3=0x00000000 +s4=0x00000000 +s5=0x00000000 +s6=0x00000000 +s7=0x00000000 +s8=0x00000000 +s9=0x00000000 +s10=0x00000000 +s11=0x00000000 +s12=0x00000000 +s13=0x00000000 +s14=0x00000000 +s15=0x00000000 +s16=0x00000000 +s17=0x00000000 +s18=0x00000000 +s19=0x00000000 +s20=0x00000000 +s21=0x00000000 +s22=0x00000000 +s23=0x00000000 +s24=0x00000000 +s25=0x00000000 +s26=0x00000000 +s27=0x00000000 +s28=0x00000000 +s29=0x00000000 +s30=0x00000000 +s31=0x00000000 +} +} +} diff --git a/packages/Python/lldbsuite/test/attic/dotest.pl b/packages/Python/lldbsuite/test/attic/dotest.pl new file mode 100644 index 000000000000..f093ed8f8f22 --- /dev/null +++ b/packages/Python/lldbsuite/test/attic/dotest.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl -w + +# +# Use this script to visit each python test case under the specified directory +# and invoke unittest.main() on each test case. +# + +use strict; +use FindBin; +use File::Find; +use File::Basename; +use Cwd; +use Cwd 'abs_path'; + +scalar(@ARGV) == 1 or die "Usage: dotest.pl testdir"; + +my $scriptDir = $FindBin::Bin; +my $baseDir = abs_path("$scriptDir/.."); +my $pluginDir = "$baseDir/test/plugins"; +my $testDir = $ARGV[0]; + +my $dbgPath = "$baseDir/build/Debug/LLDB.framework/Resources/Python"; +my $relPath = "$baseDir/build/Release/LLDB.framework/Resources/Python"; +if (-d $dbgPath) { + $ENV{'PYTHONPATH'} = "$dbgPath:$scriptDir:$pluginDir"; +} elsif (-d $relPath) { + $ENV{'PYTHONPATH'} = "$relPath:$scriptDir:$pluginDir"; +} +#print("ENV{PYTHONPATH}=$ENV{'PYTHONPATH'}\n"); + +# Traverse the directory to find our python test cases. +find(\&handleFind, $testDir); + +sub handleFind { + my $foundFile = $File::Find::name; + my $dir = getcwd; + #print("foundFile: $foundFile\n"); + + # Test*.py is the naming pattern for our test cases. + if ($foundFile =~ /.*\/(Test.*\.py)$/) { + print("Running python $1 (cwd = $dir)...\n"); + system("python $1"); + } +} diff --git a/packages/Python/lldbsuite/test/attic/tester.py b/packages/Python/lldbsuite/test/attic/tester.py new file mode 100644 index 000000000000..5c1a2370ced8 --- /dev/null +++ b/packages/Python/lldbsuite/test/attic/tester.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +from __future__ import print_function + +import math, os.path, re, sys, time, unittest + +def setupSysPath(): + testPath = sys.path[0] + rem = re.match("(^.*/)test$", testPath) + if not rem: + print("This script expects to reside in .../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, end='') + 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 list(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) + "] ", end='') + print("µ " + prettyTime(sampleMean) + ", σ " + prettyTime(sampleStandardDeviation)) + m_executionTimes = None + +setupSysPath() + +import lldb + +class LLDBTestCase(unittest.TestCase): + def setUp(self): + debugger = lldb.SBDebugger.Create() + debugger.SetAsync(True) + self.m_commandInterpreter = debugger.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 + def getCategories(self): + return [] + +class SanityCheckTestCase(LLDBTestCase): + def runTest(self): + ret = self.runCommand("show arch", "show-arch") + #print(ret) + def getCategories(self): + return [] + +suite = unittest.TestLoader().loadTestsFromTestCase(SanityCheckTestCase) +unittest.TextTestRunner(verbosity=2).run(suite) +ExecutionTimes.executionTimes().dumpStats() diff --git a/packages/Python/lldbsuite/test/bench-history b/packages/Python/lldbsuite/test/bench-history new file mode 100644 index 000000000000..471332b25221 --- /dev/null +++ b/packages/Python/lldbsuite/test/bench-history @@ -0,0 +1,206 @@ +config: MacBook Pro (10.7.2) + 2.8 GHz Intel Core 2 Duo + 4 GB 1067 MHz DDR3 + +r142707 (Oct 21, 2011): +[17:45:55] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.104274 (Laps: 30, Total Elapsed Time: 3.128214) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102216 (Laps: 30, Total Elapsed Time: 3.066470) +lldb frame variable benchmark: Avg: 1.649162 (Laps: 20, Total Elapsed Time: 32.983245) +lldb stepping benchmark: Avg: 0.104409 (Laps: 50, Total Elapsed Time: 5.220461) +lldb expr cmd benchmark: Avg: 0.206774 (Laps: 25, Total Elapsed Time: 5.169350) +lldb disassembly benchmark: Avg: 0.089086 (Laps: 10, Total Elapsed Time: 0.890859) + +r142868 (Oct 24, 2011): +[15:53:34] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.105384 (Laps: 30, Total Elapsed Time: 3.161530) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102334 (Laps: 30, Total Elapsed Time: 3.070012) +lldb frame variable benchmark: Avg: 1.661701 (Laps: 20, Total Elapsed Time: 33.234026) +lldb stepping benchmark: Avg: 0.107101 (Laps: 50, Total Elapsed Time: 5.355043) +lldb expr cmd benchmark: Avg: 0.209475 (Laps: 25, Total Elapsed Time: 5.236863) +lldb disassembly benchmark: Avg: 0.005543 (Laps: 10, Total Elapsed Time: 0.055426) +[16:09:20] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103826 (Laps: 30, Total Elapsed Time: 3.114768) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102312 (Laps: 30, Total Elapsed Time: 3.069371) +lldb frame variable benchmark: Avg: 1.602898 (Laps: 20, Total Elapsed Time: 32.057950) +lldb stepping benchmark: Avg: 0.104091 (Laps: 50, Total Elapsed Time: 5.204557) +lldb expr cmd benchmark: Avg: 0.207095 (Laps: 25, Total Elapsed Time: 5.177363) +lldb disassembly benchmark: Avg: 0.001531 (Laps: 10, Total Elapsed Time: 0.015311) + +r143065 (Oct 26, 2011): +# Establish a baseline by using a fixed lldb executable as the inferior program +# for the lldb debugger to operate on. The fixed lldb executable corresponds to +# r142902. +[15:50:34] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103774 (Laps: 30, Total Elapsed Time: 3.113226) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102230 (Laps: 30, Total Elapsed Time: 3.066896) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.448635 (Laps: 30, Total Elapsed Time: 13.459048) +lldb frame variable benchmark: Avg: 1.615647 (Laps: 20, Total Elapsed Time: 32.312934) +lldb stepping benchmark: Avg: 0.138386 (Laps: 50, Total Elapsed Time: 6.919313) +lldb expr cmd benchmark: Avg: 0.218967 (Laps: 25, Total Elapsed Time: 5.474171) +lldb disassembly benchmark: Avg: 0.092677 (Laps: 10, Total Elapsed Time: 0.926766) + +# With patch to lldbbench.py to display min and max of samples. +[17:27:09] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103625 (Laps: 30, Total Elapsed Time: 3.108748, min=0.101225, max=0.136308) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102321 (Laps: 30, Total Elapsed Time: 3.069623, min=0.101270, max=0.102824) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.445943 (Laps: 30, Total Elapsed Time: 13.378290, min=0.438535, max=0.475691) +lldb frame variable benchmark: Avg: 1.612034 (Laps: 20, Total Elapsed Time: 32.240689, min=1.591328, max=1.649720) +lldb stepping benchmark: Avg: 0.155064 (Laps: 50, Total Elapsed Time: 7.753182, min=0.101287, max=2.028978) +lldb expr cmd benchmark: Avg: 0.206160 (Laps: 25, Total Elapsed Time: 5.154005, min=0.203282, max=0.224982) +lldb disassembly benchmark: Avg: 0.032946 (Laps: 10, Total Elapsed Time: 0.329464, min=0.031380, max=0.039198) + +r143118 (Oct 27, 2011): +# No performance regression. +[11:30:10] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.109056 (Laps: 30, Total Elapsed Time: 3.271690, min=0.101212, max=0.296496) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102844 (Laps: 30, Total Elapsed Time: 3.085306, min=0.101028, max=0.112504) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.467196 (Laps: 30, Total Elapsed Time: 14.015870, min=0.430639, max=1.059366) +lldb frame variable benchmark: Avg: 1.615098 (Laps: 20, Total Elapsed Time: 32.301958, min=1.599064, max=1.665124) +lldb stepping benchmark: Avg: 0.158506 (Laps: 50, Total Elapsed Time: 7.925285, min=0.101469, max=1.724667) +lldb expr cmd benchmark: Avg: 0.232584 (Laps: 25, Total Elapsed Time: 5.814609, min=0.202931, max=0.782470) +lldb disassembly benchmark: Avg: 0.090124 (Laps: 10, Total Elapsed Time: 0.901241, min=0.087497, max=0.099313) +[11:37:23] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103782 (Laps: 30, Total Elapsed Time: 3.113456, min=0.101887, max=0.136767) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102348 (Laps: 30, Total Elapsed Time: 3.070431, min=0.101648, max=0.102756) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.445628 (Laps: 30, Total Elapsed Time: 13.368829, min=0.438276, max=0.455466) +lldb frame variable benchmark: Avg: 1.583635 (Laps: 20, Total Elapsed Time: 31.672710, min=0.133858, max=2.073698) +lldb stepping benchmark: Avg: 0.157824 (Laps: 50, Total Elapsed Time: 7.891186, min=0.101352, max=2.172388) +lldb expr cmd benchmark: Avg: 0.207600 (Laps: 25, Total Elapsed Time: 5.189992, min=0.203037, max=0.241189) +lldb disassembly benchmark: Avg: 0.006725 (Laps: 10, Total Elapsed Time: 0.067249, min=0.002470, max=0.042690) + +r143199 (Oct 28, 2011): +# No performance regression. +[11:11:54] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.104717 (Laps: 30, Total Elapsed Time: 3.141521, min=0.101881, max=0.162480) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.116985 (Laps: 30, Total Elapsed Time: 3.509554, min=0.101890, max=0.537634) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.462864 (Laps: 30, Total Elapsed Time: 13.885922, min=0.434867, max=0.877184) +lldb frame variable benchmark: Avg: 1.749605 (Laps: 20, Total Elapsed Time: 34.992090, min=1.604043, max=2.365440) +lldb stepping benchmark: Avg: 0.147439 (Laps: 50, Total Elapsed Time: 7.371955, min=0.101088, max=2.360912) +lldb expr cmd benchmark: Avg: 0.210252 (Laps: 25, Total Elapsed Time: 5.256302, min=0.202591, max=0.258185) +lldb disassembly benchmark: Avg: 0.028243 (Laps: 10, Total Elapsed Time: 0.282434, min=0.027451, max=0.031222) +[11:16:16] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103913 (Laps: 30, Total Elapsed Time: 3.117384, min=0.101510, max=0.136605) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102452 (Laps: 30, Total Elapsed Time: 3.073559, min=0.101508, max=0.105250) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.461532 (Laps: 30, Total Elapsed Time: 13.845950, min=0.436977, max=0.676502) +lldb frame variable benchmark: Avg: 1.640990 (Laps: 20, Total Elapsed Time: 32.819801, min=1.583961, max=2.031209) +lldb stepping benchmark: Avg: 0.104966 (Laps: 50, Total Elapsed Time: 5.248294, min=0.101296, max=0.240781) +lldb expr cmd benchmark: Avg: 0.211872 (Laps: 25, Total Elapsed Time: 5.296811, min=0.203827, max=0.259524) +lldb disassembly benchmark: Avg: 0.104108 (Laps: 10, Total Elapsed Time: 1.041076, min=0.089751, max=0.227173) + +r143260 +# No performance regression. +[16:51:27] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103818 (Laps: 30, Total Elapsed Time: 3.114534, min=0.101654, max=0.135579) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102786 (Laps: 30, Total Elapsed Time: 3.083594, min=0.101567, max=0.115159) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.444478 (Laps: 30, Total Elapsed Time: 13.334348, min=0.429840, max=0.514523) +lldb frame variable benchmark: Avg: 1.627282 (Laps: 20, Total Elapsed Time: 32.545639, min=1.596098, max=1.768229) +lldb stepping benchmark: Avg: 0.134925 (Laps: 50, Total Elapsed Time: 6.746272, min=0.101132, max=1.743882) +lldb expr cmd benchmark: Avg: 0.209595 (Laps: 25, Total Elapsed Time: 5.239863, min=0.203182, max=0.255394) +lldb disassembly benchmark: Avg: 0.091296 (Laps: 10, Total Elapsed Time: 0.912963, min=0.089690, max=0.103690) +[17:31:27] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.104030 (Laps: 30, Total Elapsed Time: 3.120891, min=0.101980, max=0.136737) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102375 (Laps: 30, Total Elapsed Time: 3.071264, min=0.101454, max=0.103283) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.443155 (Laps: 30, Total Elapsed Time: 13.294637, min=0.434646, max=0.459273) +lldb frame variable benchmark: Avg: 1.608266 (Laps: 20, Total Elapsed Time: 32.165314, min=1.596172, max=1.620366) +lldb stepping benchmark: Avg: 0.104140 (Laps: 50, Total Elapsed Time: 5.207003, min=0.101436, max=0.196166) +lldb expr cmd benchmark: Avg: 0.208264 (Laps: 25, Total Elapsed Time: 5.206604, min=0.204082, max=0.258287) +lldb disassembly benchmark: Avg: 0.091677 (Laps: 10, Total Elapsed Time: 0.916773, min=0.090101, max=0.099648) + +r143359 +# No performance regression. +[13:20:01] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.106920 (Laps: 30, Total Elapsed Time: 3.207593, min=0.101197, max=0.197640) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.147324 (Laps: 30, Total Elapsed Time: 4.419733, min=0.101534, max=0.900991) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.463680 (Laps: 30, Total Elapsed Time: 13.910386, min=0.429071, max=0.872424) +lldb frame variable benchmark: Avg: 1.655778 (Laps: 20, Total Elapsed Time: 33.115570, min=1.597218, max=1.779512) +lldb stepping benchmark: Avg: 0.145967 (Laps: 50, Total Elapsed Time: 7.298373, min=0.101121, max=2.288423) +lldb expr cmd benchmark: Avg: 0.207389 (Laps: 25, Total Elapsed Time: 5.184725, min=0.203226, max=0.240773) +lldb disassembly benchmark: Avg: 0.091902 (Laps: 10, Total Elapsed Time: 0.919019, min=0.088413, max=0.115076) +[13:24:24] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.150258 (Laps: 30, Total Elapsed Time: 4.507753, min=0.101110, max=1.296165) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.192823 (Laps: 30, Total Elapsed Time: 5.784686, min=0.101897, max=0.697168) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.451879 (Laps: 30, Total Elapsed Time: 13.556380, min=0.428884, max=0.536964) +lldb frame variable benchmark: Avg: 1.725232 (Laps: 20, Total Elapsed Time: 34.504649, min=1.596926, max=2.303283) +lldb stepping benchmark: Avg: 0.111959 (Laps: 50, Total Elapsed Time: 5.597941, min=0.101375, max=0.586357) +lldb expr cmd benchmark: Avg: 0.207557 (Laps: 25, Total Elapsed Time: 5.188913, min=0.203918, max=0.241022) +lldb disassembly benchmark: Avg: 0.092959 (Laps: 10, Total Elapsed Time: 0.929594, min=0.089094, max=0.109135) + +r143469 +# No performance regression. +[11:45:27] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.112890 (Laps: 30, Total Elapsed Time: 3.386693, min=0.101696, max=0.414717) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.133955 (Laps: 30, Total Elapsed Time: 4.018643, min=0.101611, max=1.050222) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.470838 (Laps: 30, Total Elapsed Time: 14.125148, min=0.425866, max=1.437070) +lldb frame variable benchmark: Avg: 1.608339 (Laps: 20, Total Elapsed Time: 32.166771, min=1.579538, max=1.721902) +lldb stepping benchmark: Avg: 0.136881 (Laps: 50, Total Elapsed Time: 6.844043, min=0.101149, max=1.835094) +lldb expr cmd benchmark: Avg: 0.207529 (Laps: 25, Total Elapsed Time: 5.188236, min=0.203476, max=0.241281) +lldb disassembly benchmark: Avg: 0.036412 (Laps: 10, Total Elapsed Time: 0.364124, min=0.031895, max=0.069457) +[12:14:54] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103313 (Laps: 30, Total Elapsed Time: 3.099402, min=0.101813, max=0.126989) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102558 (Laps: 30, Total Elapsed Time: 3.076735, min=0.101576, max=0.107419) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.438302 (Laps: 30, Total Elapsed Time: 13.149063, min=0.431972, max=0.449742) +lldb frame variable benchmark: Avg: 1.610409 (Laps: 20, Total Elapsed Time: 32.208184, min=1.575063, max=1.724046) +lldb stepping benchmark: Avg: 0.103874 (Laps: 50, Total Elapsed Time: 5.193698, min=0.101364, max=0.186751) +lldb expr cmd benchmark: Avg: 0.207476 (Laps: 25, Total Elapsed Time: 5.186895, min=0.203365, max=0.240869) +lldb disassembly benchmark: Avg: 0.090917 (Laps: 10, Total Elapsed Time: 0.909167, min=0.089431, max=0.099787) + +r144546 +# Observed possible performance regression in lldb startup delay (run to breakpoint)? +# Need more investigation. +[11:27:23] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.112386 (Laps: 30, Total Elapsed Time: 3.371577, min=0.101772, max=0.393109) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.108774 (Laps: 30, Total Elapsed Time: 3.263209, min=0.101605, max=0.292425) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.529530 (Laps: 30, Total Elapsed Time: 15.885899, min=0.468891, max=1.747227) +lldb frame variable benchmark: Avg: 1.631610 (Laps: 20, Total Elapsed Time: 32.632193, min=1.575342, max=1.775941) +lldb stepping benchmark: Avg: 0.146749 (Laps: 50, Total Elapsed Time: 7.337451, min=0.100780, max=2.165057) +lldb expr cmd benchmark: Avg: 0.217782 (Laps: 25, Total Elapsed Time: 5.444553, min=0.203476, max=0.467801) +lldb disassembly benchmark: Avg: 0.091264 (Laps: 10, Total Elapsed Time: 0.912644, min=0.088928, max=0.101681) +[14:58:21] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.105535 (Laps: 30, Total Elapsed Time: 3.166062, min=0.101935, max=0.147107) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102372 (Laps: 30, Total Elapsed Time: 3.071164, min=0.100945, max=0.102721) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.483826 (Laps: 30, Total Elapsed Time: 14.514786, min=0.471770, max=0.532260) +lldb frame variable benchmark: Avg: 1.576992 (Laps: 20, Total Elapsed Time: 31.539838, min=0.102487, max=2.098732) +lldb stepping benchmark: Avg: 0.102836 (Laps: 50, Total Elapsed Time: 5.141815, min=0.101461, max=0.122378) +lldb expr cmd benchmark: Avg: 0.206334 (Laps: 25, Total Elapsed Time: 5.158355, min=0.203804, max=0.241043) +lldb disassembly benchmark: Avg: 0.033536 (Laps: 10, Total Elapsed Time: 0.335359, min=0.031975, max=0.041612) +[15:01:59] johnny:/Volumes/data/lldb/svn/trunk/test $ + +# Redid the r143469 measurements (svn/regress dir) right before the r145371 measurements (svn/trunk dir) on 10.7.2 (build 11C74). +# The lldb startup delay (run to breakpoint) in avg is not as significant. The min still shows some possible regression. +# The frame variable benchmark shows some possible regression. +[10:51:56] johnny:/Volumes/data/lldb/svn/regress/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.109821 (Laps: 30, Total Elapsed Time: 3.294643, min=0.101836, max=0.297468) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.121465 (Laps: 30, Total Elapsed Time: 3.643950, min=0.100776, max=0.665900) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.501870 (Laps: 30, Total Elapsed Time: 15.056112, min=0.469871, max=0.718775) +lldb frame variable benchmark: Avg: 1.663890 (Laps: 20, Total Elapsed Time: 33.277791, min=1.584265, max=1.999998) +lldb stepping benchmark: Avg: 0.105434 (Laps: 50, Total Elapsed Time: 5.271683, min=0.101120, max=0.256289) +lldb expr cmd benchmark: Avg: 0.240440 (Laps: 25, Total Elapsed Time: 6.011000, min=0.203547, max=0.956405) +lldb disassembly benchmark: Avg: 0.096988 (Laps: 10, Total Elapsed Time: 0.969877, min=0.089511, max=0.147797) +[10:55:38] johnny:/Volumes/data/lldb/svn/regress/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.107368 (Laps: 30, Total Elapsed Time: 3.221026, min=0.101374, max=0.163785) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.153588 (Laps: 30, Total Elapsed Time: 4.607645, min=0.101554, max=0.776372) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.510726 (Laps: 30, Total Elapsed Time: 15.321779, min=0.468395, max=0.643357) +lldb frame variable benchmark: Avg: 1.715136 (Laps: 20, Total Elapsed Time: 34.302718, min=1.527872, max=3.125777) +lldb stepping benchmark: Avg: 0.116140 (Laps: 50, Total Elapsed Time: 5.807012, min=0.100857, max=0.796673) +lldb expr cmd benchmark: Avg: 0.206397 (Laps: 25, Total Elapsed Time: 5.159914, min=0.203491, max=0.241283) +lldb disassembly benchmark: Avg: 0.036542 (Laps: 10, Total Elapsed Time: 0.365422, min=0.031872, max=0.060183) +[10:59:43] johnny:/Volumes/data/lldb/svn/regress/test $ pushd +1 +/Volumes/data/lldb/svn/trunk/test /Volumes/data/lldb/llvm /Volumes/data/lldb/svn/regress/test +[10:59:48] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103919 (Laps: 30, Total Elapsed Time: 3.117560, min=0.101766, max=0.137548) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.102705 (Laps: 30, Total Elapsed Time: 3.081165, min=0.101311, max=0.114192) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.504830 (Laps: 30, Total Elapsed Time: 15.144906, min=0.483528, max=0.627122) +lldb frame variable benchmark: Avg: 1.924774 (Laps: 20, Total Elapsed Time: 38.495475, min=1.798745, max=2.248269) +lldb stepping benchmark: Avg: 0.102595 (Laps: 50, Total Elapsed Time: 5.129757, min=0.100690, max=0.125930) +lldb expr cmd benchmark: Avg: 0.205395 (Laps: 25, Total Elapsed Time: 5.134883, min=0.202864, max=0.210484) +lldb disassembly benchmark: Avg: 0.033576 (Laps: 10, Total Elapsed Time: 0.335765, min=0.032021, max=0.039222) +[11:03:46] johnny:/Volumes/data/lldb/svn/trunk/test $ ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' +lldb startup delay (create fresh target) benchmark: Avg: 0.103371 (Laps: 30, Total Elapsed Time: 3.101121, min=0.101556, max=0.127819) +lldb startup delay (set first breakpoint) benchmark: Avg: 0.103174 (Laps: 30, Total Elapsed Time: 3.095230, min=0.101763, max=0.114733) +lldb startup delay (run to breakpoint) benchmark: Avg: 0.513848 (Laps: 30, Total Elapsed Time: 15.415439, min=0.490004, max=0.748310) +lldb frame variable benchmark: Avg: 1.906068 (Laps: 20, Total Elapsed Time: 38.121364, min=1.872328, max=2.044472) +lldb stepping benchmark: Avg: 0.254817 (Laps: 50, Total Elapsed Time: 12.740872, min=0.100743, max=7.628899) +lldb expr cmd benchmark: Avg: 0.207850 (Laps: 25, Total Elapsed Time: 5.196255, min=0.203986, max=0.257482) +lldb disassembly benchmark: Avg: 0.092003 (Laps: 10, Total Elapsed Time: 0.920026, min=0.089568, max=0.100374) +[11:07:53] johnny:/Volumes/data/lldb/svn/trunk/test $ diff --git a/packages/Python/lldbsuite/test/bench.py b/packages/Python/lldbsuite/test/bench.py new file mode 100644 index 000000000000..ce9c2e75d711 --- /dev/null +++ b/packages/Python/lldbsuite/test/bench.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +""" +A simple bench runner which delegates to the ./dotest.py test driver to run the +benchmarks defined in the list named 'benches'. + +You need to hand edit 'benches' to modify/change the command lines passed to the +test driver. + +Use the following to get only the benchmark results in your terminal output: + + ./bench.py -e /Volumes/data/lldb/svn/regression/build/Debug/lldb -x '-F Driver::MainLoop()' 2>&1 | grep -P '^lldb.*benchmark:' + +See also bench-history. +""" + +from __future__ import print_function +from __future__ import absolute_import + +import os, sys +import re +from optparse import OptionParser + +# dotest.py invocation with no '-e exe-path' uses lldb as the inferior program, +# unless there is a mentioning of custom executable program. +benches = [ + # Measure startup delays creating a target, setting a breakpoint, and run to breakpoint stop. + './dotest.py -v +b %E %X -n -p TestStartupDelays.py', + + # Measure 'frame variable' response after stopping at a breakpoint. + './dotest.py -v +b %E %X -n -p TestFrameVariableResponse.py', + + # Measure stepping speed after stopping at a breakpoint. + './dotest.py -v +b %E %X -n -p TestSteppingSpeed.py', + + # Measure expression cmd response with a simple custom executable program. + './dotest.py +b -n -p TestExpressionCmd.py', + + # Attach to a spawned process then run disassembly benchmarks. + './dotest.py -v +b -n %E -p TestDoAttachThenDisassembly.py' +] + +def main(): + """Read the items from 'benches' and run the command line one by one.""" + parser = OptionParser(usage="""\ +%prog [options] +Run the standard benchmarks defined in the list named 'benches'.\ +""") + parser.add_option('-e', '--executable', + type='string', action='store', + dest='exe', + help='The target program launched by lldb.') + parser.add_option('-x', '--breakpoint-spec', + type='string', action='store', + dest='break_spec', + help='The lldb breakpoint spec for the target program.') + + # Parses the options, if any. + opts, args = parser.parse_args() + + print("Starting bench runner....") + + for item in benches: + command = item.replace('%E', + '-e "%s"' % opts.exe if opts.exe else '') + command = command.replace('%X', + '-x "%s"' % opts.break_spec if opts.break_spec else '') + print("Running %s" % (command)) + os.system(command) + + print("Bench runner done.") + +if __name__ == '__main__': + main() diff --git a/packages/Python/lldbsuite/test/benchmarks/continue/Makefile b/packages/Python/lldbsuite/test/benchmarks/continue/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/continue/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/benchmarks/continue/TestBenchmarkContinue.py b/packages/Python/lldbsuite/test/benchmarks/continue/TestBenchmarkContinue.py new file mode 100644 index 000000000000..25ca13a3bfd9 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/continue/TestBenchmarkContinue.py @@ -0,0 +1,66 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbbench import * +import lldbsuite.test.lldbutil as lldbutil + +class TestBenchmarkContinue(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + @benchmarks_test + def test_run_command(self): + """Benchmark different ways to continue a process""" + self.build() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + BenchBase.setUp(self) + + def data_formatter_commands(self): + """Benchmark different ways to continue a process""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "// break here")) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + runCmd_sw = Stopwatch() + lldbutil_sw = Stopwatch() + + for i in range(0,15): + runCmd_sw.start() + self.runCmd("continue") + runCmd_sw.stop() + + for i in range(0,15): + lldbutil_sw.start() + lldbutil.continue_to_breakpoint(self.process(), bkpt) + lldbutil_sw.stop() + + print("runCmd: %s\nlldbutil: %s" % (runCmd_sw,lldbutil_sw)) diff --git a/packages/Python/lldbsuite/test/benchmarks/continue/main.cpp b/packages/Python/lldbsuite/test/benchmarks/continue/main.cpp new file mode 100644 index 000000000000..d715a1150d06 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/continue/main.cpp @@ -0,0 +1,36 @@ +#include + +#define intint_map std::map + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intint_map ii; + + for (int i = 0; i < 15; i++) + { + ii[i] = i + 1; + thefoo_rw(i); // break here + } + + ii.clear(); + + for (int j = 0; j < 15; j++) + { + ii[j] = j + 1; + thefoo_rw(j); // break here + } + + return 0; +} diff --git a/packages/Python/lldbsuite/test/benchmarks/disassembly/TestDisassembly.py b/packages/Python/lldbsuite/test/benchmarks/disassembly/TestDisassembly.py new file mode 100644 index 000000000000..a18cb2a64c76 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/disassembly/TestDisassembly.py @@ -0,0 +1,155 @@ +"""Disassemble lldb's Driver::MainLoop() functions comparing lldb against gdb.""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.lldbbench import * + +def is_exe(fpath): + """Returns true if fpath is an executable.""" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +class DisassembleDriverMainLoop(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + """ + Note that lldbtest_config.lldbExec can be specified with the LLDB_EXEC env variable (see + dotest.py), and gdbExec can be specified with the GDB_EXEC env variable. + This provides a flexibility in specifying different versions of gdb for + comparison purposes. + """ + BenchBase.setUp(self) + # If env var GDB_EXEC is specified, use it; otherwise, use gdb in your + # PATH env var. + if "GDB_EXEC" in os.environ and is_exe(os.environ["GDB_EXEC"]): + self.gdbExec = os.environ["GDB_EXEC"] + else: + self.gdbExec = "gdb" + + self.exe = lldbtest_config.lldbExec + self.function = 'Driver::MainLoop()' + self.lldb_avg = None + self.gdb_avg = None + self.count = 5 + + @benchmarks_test + @no_debug_info_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_run_lldb_then_gdb(self): + """Test disassembly on a large function with lldb vs. gdb.""" + print() + print("lldb path: %s" % lldbtest_config.lldbExec) + print("gdb path: %s" % self.gdbExec) + + print() + self.run_lldb_disassembly(self.exe, self.function, self.count) + print("lldb benchmark:", self.stopwatch) + self.run_gdb_disassembly(self.exe, self.function, self.count) + print("gdb benchmark:", self.stopwatch) + print("lldb_avg/gdb_avg: %f" % (self.lldb_avg/self.gdb_avg)) + + @benchmarks_test + @no_debug_info_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_run_gdb_then_lldb(self): + """Test disassembly on a large function with lldb vs. gdb.""" + print() + print("lldb path: %s" % lldbtest_config.lldbExec) + print("gdb path: %s" % self.gdbExec) + + print() + self.run_gdb_disassembly(self.exe, self.function, self.count) + print("gdb benchmark:", self.stopwatch) + self.run_lldb_disassembly(self.exe, self.function, self.count) + print("lldb benchmark:", self.stopwatch) + print("lldb_avg/gdb_avg: %f" % (self.lldb_avg/self.gdb_avg)) + + def run_lldb_disassembly(self, exe, function, count): + import pexpect + # Set self.child_prompt, which is "(lldb) ". + self.child_prompt = '(lldb) ' + prompt = self.child_prompt + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, exe)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('breakpoint set -F %s' % function) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + + # Reset the stopwatch now. + self.stopwatch.reset() + for i in range(count): + with self.stopwatch: + # Disassemble the function. + child.sendline('disassemble -f') + child.expect_exact(prompt) + child.sendline('next') + child.expect_exact(prompt) + + child.sendline('quit') + try: + self.child.expect(pexpect.EOF) + except: + pass + + self.lldb_avg = self.stopwatch.avg() + if self.TraceOn(): + print("lldb disassembly benchmark:", str(self.stopwatch)) + self.child = None + + def run_gdb_disassembly(self, exe, function, count): + import pexpect + # Set self.child_prompt, which is "(gdb) ". + self.child_prompt = '(gdb) ' + prompt = self.child_prompt + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s --nx %s' % (self.gdbExec, exe)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('break %s' % function) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + + # Reset the stopwatch now. + self.stopwatch.reset() + for i in range(count): + with self.stopwatch: + # Disassemble the function. + child.sendline('disassemble') + child.expect_exact(prompt) + child.sendline('next') + child.expect_exact(prompt) + + child.sendline('quit') + child.expect_exact('The program is running. Exit anyway?') + child.sendline('y') + try: + self.child.expect(pexpect.EOF) + except: + pass + + self.gdb_avg = self.stopwatch.avg() + if self.TraceOn(): + print("gdb disassembly benchmark:", str(self.stopwatch)) + self.child = None diff --git a/packages/Python/lldbsuite/test/benchmarks/disassembly/TestDoAttachThenDisassembly.py b/packages/Python/lldbsuite/test/benchmarks/disassembly/TestDoAttachThenDisassembly.py new file mode 100644 index 000000000000..b7b07090959e --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/disassembly/TestDoAttachThenDisassembly.py @@ -0,0 +1,67 @@ +"""Test lldb's disassemblt speed. This bench deliberately attaches to an lldb +inferior and traverses the stack for thread0 to arrive at frame with function +'MainLoop'. It is important to specify an lldb executable as the inferior.""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test.lldbbench import * +from lldbsuite.test import configuration + +class AttachThenDisassemblyBench(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + BenchBase.setUp(self) + self.exe = lldbtest_config.lldbExec + self.count = 10 + + @benchmarks_test + @no_debug_info_test + def test_attach_then_disassembly(self): + """Attach to a spawned lldb process then run disassembly benchmarks.""" + print() + self.run_lldb_attach_then_disassembly(self.exe, self.count) + print("lldb disassembly benchmark:", self.stopwatch) + + def run_lldb_attach_then_disassembly(self, exe, count): + target = self.dbg.CreateTarget(exe) + + # Spawn a new process and don't display the stdout if not in TraceOn() mode. + import subprocess + popen = subprocess.Popen([exe, self.lldbOption], + stdout = open(os.devnull, 'w') if not self.TraceOn() else None) + if self.TraceOn(): + print("pid of spawned process: %d" % popen.pid) + + # Attach to the launched lldb process. + listener = lldb.SBListener("my.attach.listener") + error = lldb.SBError() + process = target.AttachToProcessWithID(listener, popen.pid, error) + + # Set thread0 as the selected thread, followed by the 'MainLoop' frame + # as the selected frame. Then do disassembly on the function. + thread0 = process.GetThreadAtIndex(0) + process.SetSelectedThread(thread0) + i = 0 + found = False + for f in thread0: + #print("frame#%d %s" % (i, f.GetFunctionName())) + if "MainLoop" in f.GetFunctionName(): + found = True + thread0.SetSelectedFrame(i) + if self.TraceOn(): + print("Found frame#%d for function 'MainLoop'" % i) + break + i += 1 + + # Reset the stopwatch now. + self.stopwatch.reset() + for i in range(count): + with self.stopwatch: + # Disassemble the function. + self.runCmd("disassemble -f") diff --git a/packages/Python/lldbsuite/test/benchmarks/disassembly/TestXcode41Vs42GDBDisassembly.py b/packages/Python/lldbsuite/test/benchmarks/disassembly/TestXcode41Vs42GDBDisassembly.py new file mode 100644 index 000000000000..745e7b6a8c3b --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/disassembly/TestXcode41Vs42GDBDisassembly.py @@ -0,0 +1,94 @@ +"""Disassemble lldb's Driver::MainLoop() functions comparing Xcode 4.1 vs. 4.2's gdb.""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.lldbbench import * + +class XCode41Vs42GDBDisassembly(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + BenchBase.setUp(self) + self.gdb_41_exe = '/Xcode41/usr/bin/gdb' + self.gdb_42_exe = '/Developer/usr/bin/gdb' + self.exe = lldbtest_config.lldbExec + self.function = 'Driver::MainLoop()' + self.gdb_41_avg = None + self.gdb_42_avg = None + self.count = 5 + + @benchmarks_test + @no_debug_info_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_run_41_then_42(self): + """Test disassembly on a large function with 4.1 vs. 4.2's gdb.""" + print() + self.run_gdb_disassembly(self.gdb_41_exe, self.exe, self.function, self.count) + print("4.1 gdb benchmark:", self.stopwatch) + self.gdb_41_avg = self.stopwatch.avg() + self.run_gdb_disassembly(self.gdb_42_exe, self.exe, self.function, self.count) + print("4.2 gdb benchmark:", self.stopwatch) + self.gdb_42_avg = self.stopwatch.avg() + print("gdb_42_avg/gdb_41_avg: %f" % (self.gdb_42_avg/self.gdb_41_avg)) + + @benchmarks_test + @no_debug_info_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_run_42_then_41(self): + """Test disassembly on a large function with 4.1 vs. 4.2's gdb.""" + print() + self.run_gdb_disassembly(self.gdb_42_exe, self.exe, self.function, self.count) + print("4.2 gdb benchmark:", self.stopwatch) + self.gdb_42_avg = self.stopwatch.avg() + self.run_gdb_disassembly(self.gdb_41_exe, self.exe, self.function, self.count) + print("4.1 gdb benchmark:", self.stopwatch) + self.gdb_41_avg = self.stopwatch.avg() + print("gdb_42_avg/gdb_41_avg: %f" % (self.gdb_42_avg/self.gdb_41_avg)) + + def run_gdb_disassembly(self, gdb_exe_path, exe, function, count): + import pexpect + # Set self.child_prompt, which is "(gdb) ". + self.child_prompt = '(gdb) ' + prompt = self.child_prompt + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s --nx %s' % (gdb_exe_path, exe)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('break %s' % function) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + + # Reset the stopwatch now. + self.stopwatch.reset() + for i in range(count): + with self.stopwatch: + # Disassemble the function. + child.sendline('disassemble') + child.expect_exact(prompt) + child.sendline('next') + child.expect_exact(prompt) + + child.sendline('quit') + child.expect_exact('The program is running. Exit anyway?') + child.sendline('y') + try: + self.child.expect(pexpect.EOF) + except: + pass + + if self.TraceOn(): + print("gdb disassembly benchmark:", str(self.stopwatch)) + self.child = None diff --git a/packages/Python/lldbsuite/test/benchmarks/expression/Makefile b/packages/Python/lldbsuite/test/benchmarks/expression/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/expression/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/benchmarks/expression/TestExpressionCmd.py b/packages/Python/lldbsuite/test/benchmarks/expression/TestExpressionCmd.py new file mode 100644 index 000000000000..abf45147d636 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/expression/TestExpressionCmd.py @@ -0,0 +1,72 @@ +"""Test lldb's expression evaluations and collect statistics.""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.lldbbench import * + +class ExpressionEvaluationCase(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + BenchBase.setUp(self) + self.source = 'main.cpp' + self.line_to_break = line_number(self.source, '// Set breakpoint here.') + self.count = 25 + + @benchmarks_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_expr_cmd(self): + """Test lldb's expression commands and collect statistics.""" + self.build() + self.exe_name = 'a.out' + + print() + self.run_lldb_repeated_exprs(self.exe_name, self.count) + print("lldb expr cmd benchmark:", self.stopwatch) + + def run_lldb_repeated_exprs(self, exe_name, count): + import pexpect + exe = os.path.join(os.getcwd(), exe_name) + + # Set self.child_prompt, which is "(lldb) ". + self.child_prompt = '(lldb) ' + prompt = self.child_prompt + + # Reset the stopwatch now. + self.stopwatch.reset() + for i in range(count): + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, exe)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('breakpoint set -f %s -l %d' % (self.source, self.line_to_break)) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + expr_cmd1 = 'expr ptr[j]->point.x' + expr_cmd2 = 'expr ptr[j]->point.y' + + with self.stopwatch: + child.sendline(expr_cmd1) + child.expect_exact(prompt) + child.sendline(expr_cmd2) + child.expect_exact(prompt) + + child.sendline('quit') + try: + self.child.expect(pexpect.EOF) + except: + pass + + self.child = None diff --git a/packages/Python/lldbsuite/test/benchmarks/expression/TestRepeatedExprs.py b/packages/Python/lldbsuite/test/benchmarks/expression/TestRepeatedExprs.py new file mode 100644 index 000000000000..e5cada3563fa --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/expression/TestRepeatedExprs.py @@ -0,0 +1,130 @@ +"""Test evaluating expressions repeatedly comparing lldb against gdb.""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.lldbbench import * + +class RepeatedExprsCase(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + BenchBase.setUp(self) + self.source = 'main.cpp' + self.line_to_break = line_number(self.source, '// Set breakpoint here.') + self.lldb_avg = None + self.gdb_avg = None + self.count = 100 + + @benchmarks_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_compare_lldb_to_gdb(self): + """Test repeated expressions with lldb vs. gdb.""" + self.build() + self.exe_name = 'a.out' + + print() + self.run_lldb_repeated_exprs(self.exe_name, self.count) + print("lldb benchmark:", self.stopwatch) + self.run_gdb_repeated_exprs(self.exe_name, self.count) + print("gdb benchmark:", self.stopwatch) + print("lldb_avg/gdb_avg: %f" % (self.lldb_avg/self.gdb_avg)) + + def run_lldb_repeated_exprs(self, exe_name, count): + import pexpect + exe = os.path.join(os.getcwd(), exe_name) + + # Set self.child_prompt, which is "(lldb) ". + self.child_prompt = '(lldb) ' + prompt = self.child_prompt + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, exe)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('breakpoint set -f %s -l %d' % (self.source, self.line_to_break)) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + expr_cmd1 = 'expr ptr[j]->point.x' + expr_cmd2 = 'expr ptr[j]->point.y' + + # Reset the stopwatch now. + self.stopwatch.reset() + for i in range(count): + with self.stopwatch: + child.sendline(expr_cmd1) + child.expect_exact(prompt) + child.sendline(expr_cmd2) + child.expect_exact(prompt) + child.sendline('process continue') + child.expect_exact(prompt) + + child.sendline('quit') + try: + self.child.expect(pexpect.EOF) + except: + pass + + self.lldb_avg = self.stopwatch.avg() + if self.TraceOn(): + print("lldb expression benchmark:", str(self.stopwatch)) + self.child = None + + def run_gdb_repeated_exprs(self, exe_name, count): + import pexpect + exe = os.path.join(os.getcwd(), exe_name) + + # Set self.child_prompt, which is "(gdb) ". + self.child_prompt = '(gdb) ' + prompt = self.child_prompt + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('gdb --nx %s' % exe) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('break %s:%d' % (self.source, self.line_to_break)) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + expr_cmd1 = 'print ptr[j]->point.x' + expr_cmd2 = 'print ptr[j]->point.y' + + # Reset the stopwatch now. + self.stopwatch.reset() + for i in range(count): + with self.stopwatch: + child.sendline(expr_cmd1) + child.expect_exact(prompt) + child.sendline(expr_cmd2) + child.expect_exact(prompt) + child.sendline('continue') + child.expect_exact(prompt) + + child.sendline('quit') + child.expect_exact('The program is running. Exit anyway?') + child.sendline('y') + try: + self.child.expect(pexpect.EOF) + except: + pass + + self.gdb_avg = self.stopwatch.avg() + if self.TraceOn(): + print("gdb expression benchmark:", str(self.stopwatch)) + self.child = None diff --git a/packages/Python/lldbsuite/test/benchmarks/expression/main.cpp b/packages/Python/lldbsuite/test/benchmarks/expression/main.cpp new file mode 100644 index 000000000000..8f2706e26388 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/expression/main.cpp @@ -0,0 +1,51 @@ +//===-- 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 + +class Point { +public: + int x; + int y; + Point(int a, int b): + x(a), + y(b) + {} +}; + +class Data { +public: + int id; + Point point; + Data(int i): + id(i), + point(0, 0) + {} +}; + +int main(int argc, char const *argv[]) { + Data *data[1000]; + Data **ptr = data; + for (int i = 0; i < 1000; ++i) { + ptr[i] = new Data(i); + ptr[i]->point.x = i; + ptr[i]->point.y = i+1; + } + + printf("Finished populating data.\n"); + for (int j = 0; j < 1000; ++j) { + bool dump = argc > 1; // Set breakpoint here. + // Evaluate a couple of expressions (2*1000 = 2000 exprs): + // expr ptr[j]->point.x + // expr ptr[j]->point.y + if (dump) { + printf("data[%d] = %d (%d, %d)\n", j, ptr[j]->id, ptr[j]->point.x, ptr[j]->point.y); + } + } + return 0; +} diff --git a/packages/Python/lldbsuite/test/benchmarks/frame_variable/TestFrameVariableResponse.py b/packages/Python/lldbsuite/test/benchmarks/frame_variable/TestFrameVariableResponse.py new file mode 100644 index 000000000000..5fdf05763fd1 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/frame_variable/TestFrameVariableResponse.py @@ -0,0 +1,70 @@ +"""Test lldb's response time for 'frame variable' command.""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test import configuration +from lldbsuite.test import lldbtest_config +from lldbsuite.test.lldbbench import * + +class FrameVariableResponseBench(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + BenchBase.setUp(self) + self.exe = lldbtest_config.lldbExec + self.break_spec = '-n main' + self.count = 20 + + @benchmarks_test + @no_debug_info_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_startup_delay(self): + """Test response time for the 'frame variable' command.""" + print() + self.run_frame_variable_bench(self.exe, self.break_spec, self.count) + print("lldb frame variable benchmark:", self.stopwatch) + + def run_frame_variable_bench(self, exe, break_spec, count): + import pexpect + # Set self.child_prompt, which is "(lldb) ". + self.child_prompt = '(lldb) ' + prompt = self.child_prompt + + # Reset the stopwatchs now. + self.stopwatch.reset() + for i in range(count): + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, exe)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + # Set our breakpoint. + child.sendline('breakpoint set %s' % break_spec) + child.expect_exact(prompt) + + # Run the target and expect it to be stopped due to breakpoint. + child.sendline('run') # Aka 'process launch'. + child.expect_exact(prompt) + + with self.stopwatch: + # Measure the 'frame variable' response time. + child.sendline('frame variable') + child.expect_exact(prompt) + + child.sendline('quit') + try: + self.child.expect(pexpect.EOF) + except: + pass + + # The test is about to end and if we come to here, the child process has + # been terminated. Mark it so. + self.child = None diff --git a/packages/Python/lldbsuite/test/benchmarks/libcxxlist/Makefile b/packages/Python/lldbsuite/test/benchmarks/libcxxlist/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/libcxxlist/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/benchmarks/libcxxlist/TestBenchmarkLibcxxList.py b/packages/Python/lldbsuite/test/benchmarks/libcxxlist/TestBenchmarkLibcxxList.py new file mode 100644 index 000000000000..7606bd1d3e8b --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/libcxxlist/TestBenchmarkLibcxxList.py @@ -0,0 +1,59 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbbench import * +import lldbsuite.test.lldbutil as lldbutil + +class TestBenchmarkLibcxxList(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + @benchmarks_test + def test_run_command(self): + """Benchmark the std::list data formatter (libc++)""" + self.build() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + BenchBase.setUp(self) + + def data_formatter_commands(self): + """Benchmark the std::list data formatter (libc++)""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "break here")) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + sw = Stopwatch() + + sw.start() + self.expect('frame variable -A list', substrs=['[300]', '300']) + sw.stop() + + print("time to print: %s" % (sw)) diff --git a/packages/Python/lldbsuite/test/benchmarks/libcxxlist/main.cpp b/packages/Python/lldbsuite/test/benchmarks/libcxxlist/main.cpp new file mode 100644 index 000000000000..9c4113ad0514 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/libcxxlist/main.cpp @@ -0,0 +1,11 @@ +#include + +int main() +{ + std::list list; + for (int i = 0; + i < 1500; + i++) + list.push_back(i); + return list.size(); // break here +} diff --git a/packages/Python/lldbsuite/test/benchmarks/libcxxmap/Makefile b/packages/Python/lldbsuite/test/benchmarks/libcxxmap/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/libcxxmap/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/benchmarks/libcxxmap/TestBenchmarkLibcxxMap.py b/packages/Python/lldbsuite/test/benchmarks/libcxxmap/TestBenchmarkLibcxxMap.py new file mode 100644 index 000000000000..806bc0ee29e2 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/libcxxmap/TestBenchmarkLibcxxMap.py @@ -0,0 +1,59 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbbench import * +import lldbsuite.test.lldbutil as lldbutil + +class TestBenchmarkLibcxxMap(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + @benchmarks_test + def test_run_command(self): + """Benchmark the std::map data formatter (libc++)""" + self.build() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + BenchBase.setUp(self) + + def data_formatter_commands(self): + """Benchmark the std::map data formatter (libc++)""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "break here")) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + sw = Stopwatch() + + sw.start() + self.expect('frame variable -A map', substrs=['[300]', '300']) + sw.stop() + + print("time to print: %s" % (sw)) diff --git a/packages/Python/lldbsuite/test/benchmarks/libcxxmap/main.cpp b/packages/Python/lldbsuite/test/benchmarks/libcxxmap/main.cpp new file mode 100644 index 000000000000..45efb26b6b04 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/libcxxmap/main.cpp @@ -0,0 +1,11 @@ +#include + +int main() +{ + std::map map; + for (int i = 0; + i < 1500; + i++) + map[i] = i; + return map.size(); // break here +} diff --git a/packages/Python/lldbsuite/test/benchmarks/startup/TestStartupDelays.py b/packages/Python/lldbsuite/test/benchmarks/startup/TestStartupDelays.py new file mode 100644 index 000000000000..a215721ed52f --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/startup/TestStartupDelays.py @@ -0,0 +1,80 @@ +"""Test lldb's startup delays creating a target, setting a breakpoint, and run to breakpoint stop.""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test import configuration +from lldbsuite.test import lldbtest_config +from lldbsuite.test.lldbbench import * + +class StartupDelaysBench(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + BenchBase.setUp(self) + # Create self.stopwatch2 for measuring "set first breakpoint". + # The default self.stopwatch is for "create fresh target". + self.stopwatch2 = Stopwatch() + # Create self.stopwatch3 for measuring "run to breakpoint". + self.stopwatch3 = Stopwatch() + self.exe = lldbtest_config.lldbExec + self.break_spec = '-n main' + self.count = 30 + + @benchmarks_test + @no_debug_info_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_startup_delay(self): + """Test start up delays creating a target, setting a breakpoint, and run to breakpoint stop.""" + print() + self.run_startup_delays_bench(self.exe, self.break_spec, self.count) + print("lldb startup delay (create fresh target) benchmark:", self.stopwatch) + print("lldb startup delay (set first breakpoint) benchmark:", self.stopwatch2) + print("lldb startup delay (run to breakpoint) benchmark:", self.stopwatch3) + + def run_startup_delays_bench(self, exe, break_spec, count): + import pexpect + # Set self.child_prompt, which is "(lldb) ". + self.child_prompt = '(lldb) ' + prompt = self.child_prompt + + # Reset the stopwatchs now. + self.stopwatch.reset() + self.stopwatch2.reset() + for i in range(count): + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s' % (lldbtest_config.lldbExec, self.lldbOption)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + with self.stopwatch: + # Create a fresh target. + child.sendline('file %s' % exe) # Aka 'target create'. + child.expect_exact(prompt) + + with self.stopwatch2: + # Read debug info and set the first breakpoint. + child.sendline('breakpoint set %s' % break_spec) + child.expect_exact(prompt) + + with self.stopwatch3: + # Run to the breakpoint just set. + child.sendline('run') + child.expect_exact(prompt) + + child.sendline('quit') + try: + self.child.expect(pexpect.EOF) + except: + pass + + # The test is about to end and if we come to here, the child process has + # been terminated. Mark it so. + self.child = None diff --git a/packages/Python/lldbsuite/test/benchmarks/stepping/TestSteppingSpeed.py b/packages/Python/lldbsuite/test/benchmarks/stepping/TestSteppingSpeed.py new file mode 100644 index 000000000000..14738a978e86 --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/stepping/TestSteppingSpeed.py @@ -0,0 +1,67 @@ +"""Test lldb's stepping speed.""" + +from __future__ import print_function + +import os, sys +import lldb +from lldbsuite.test import configuration +from lldbsuite.test import lldbtest_config +from lldbsuite.test.lldbbench import * + +class SteppingSpeedBench(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + BenchBase.setUp(self) + self.exe = lldbtest_config.lldbExec + self.break_spec = '-n main' + self.count = 50 + + #print("self.exe=%s" % self.exe) + #print("self.break_spec=%s" % self.break_spec) + + @benchmarks_test + @no_debug_info_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_run_lldb_steppings(self): + """Test lldb steppings on a large executable.""" + print() + self.run_lldb_steppings(self.exe, self.break_spec, self.count) + print("lldb stepping benchmark:", self.stopwatch) + + def run_lldb_steppings(self, exe, break_spec, count): + import pexpect + # Set self.child_prompt, which is "(lldb) ". + self.child_prompt = '(lldb) ' + prompt = self.child_prompt + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, exe)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('breakpoint set %s' % break_spec) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + + # Reset the stopwatch now. + self.stopwatch.reset() + for i in range(count): + with self.stopwatch: + # Disassemble the function. + child.sendline('next') # Aka 'thread step-over'. + child.expect_exact(prompt) + + child.sendline('quit') + try: + self.child.expect(pexpect.EOF) + except: + pass + + self.child = None diff --git a/packages/Python/lldbsuite/test/benchmarks/turnaround/TestCompileRunToBreakpointTurnaround.py b/packages/Python/lldbsuite/test/benchmarks/turnaround/TestCompileRunToBreakpointTurnaround.py new file mode 100644 index 000000000000..731eb13bbcfb --- /dev/null +++ b/packages/Python/lldbsuite/test/benchmarks/turnaround/TestCompileRunToBreakpointTurnaround.py @@ -0,0 +1,119 @@ +"""Benchmark the turnaround time starting a debugger and run to the breakpont with lldb vs. gdb.""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.lldbbench import * + +class CompileRunToBreakpointBench(BenchBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + BenchBase.setUp(self) + self.exe = lldbtest_config.lldbExec + self.function = 'Driver::MainLoop()' + self.count = 3 + + self.lldb_avg = None + self.gdb_avg = None + + @benchmarks_test + @no_debug_info_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_run_lldb_then_gdb(self): + """Benchmark turnaround time with lldb vs. gdb.""" + print() + self.run_lldb_turnaround(self.exe, self.function, self.count) + print("lldb turnaround benchmark:", self.stopwatch) + self.run_gdb_turnaround(self.exe, self.function, self.count) + print("gdb turnaround benchmark:", self.stopwatch) + print("lldb_avg/gdb_avg: %f" % (self.lldb_avg/self.gdb_avg)) + + def run_lldb_turnaround(self, exe, function, count): + import pexpect + def run_one_round(): + prompt = self.child_prompt + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, exe)) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('breakpoint set -F %s' % function) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + + # Set self.child_prompt, which is "(lldb) ". + self.child_prompt = '(lldb) ' + # Reset the stopwatch now. + self.stopwatch.reset() + + for i in range(count + 1): + # Ignore the first invoke lldb and run to the breakpoint turnaround time. + if i == 0: + run_one_round() + else: + with self.stopwatch: + run_one_round() + + self.child.sendline('quit') + try: + self.child.expect(pexpect.EOF) + except: + pass + + self.lldb_avg = self.stopwatch.avg() + self.child = None + + def run_gdb_turnaround(self, exe, function, count): + import pexpect + def run_one_round(): + prompt = self.child_prompt + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('gdb --nx %s' % exe) + child = self.child + + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + child.expect_exact(prompt) + child.sendline('break %s' % function) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact(prompt) + + # Set self.child_prompt, which is "(gdb) ". + self.child_prompt = '(gdb) ' + # Reset the stopwatch now. + self.stopwatch.reset() + + for i in range(count+1): + # Ignore the first invoke lldb and run to the breakpoint turnaround time. + if i == 0: + run_one_round() + else: + with self.stopwatch: + run_one_round() + + self.child.sendline('quit') + self.child.expect_exact('The program is running. Exit anyway?') + self.child.sendline('y') + try: + self.child.expect(pexpect.EOF) + except: + pass + + self.gdb_avg = self.stopwatch.avg() + self.child = None diff --git a/packages/Python/lldbsuite/test/configuration.py b/packages/Python/lldbsuite/test/configuration.py new file mode 100644 index 000000000000..69ed9fc32e2b --- /dev/null +++ b/packages/Python/lldbsuite/test/configuration.py @@ -0,0 +1,161 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Provides the configuration class, which holds all information related to +how this invocation of the test suite should be run. +""" + +from __future__ import absolute_import +from __future__ import print_function + +# System modules +import os +import platform +import subprocess + + +# Third-party modules +import unittest2 + +# LLDB Modules +import lldbsuite + +def __setCrashInfoHook_Mac(text): + from . import crashinfo + crashinfo.setCrashReporterDescription(text) + +def setupCrashInfoHook(): + if platform.system() == "Darwin": + from . import lock + test_dir = os.environ['LLDB_TEST'] + if not test_dir or not os.path.exists(test_dir): + return + dylib_lock = os.path.join(test_dir,"crashinfo.lock") + dylib_src = os.path.join(test_dir,"crashinfo.c") + dylib_dst = os.path.join(test_dir,"crashinfo.so") + try: + compile_lock = lock.Lock(dylib_lock) + compile_lock.acquire() + if not os.path.isfile(dylib_dst) or os.path.getmtime(dylib_dst) < os.path.getmtime(dylib_src): + # we need to compile + cmd = "SDKROOT= xcrun clang %s -o %s -framework Python -Xlinker -dylib -iframework /System/Library/Frameworks/ -Xlinker -F /System/Library/Frameworks/" % (dylib_src,dylib_dst) + if subprocess.call(cmd,shell=True) != 0 or not os.path.isfile(dylib_dst): + raise Exception('command failed: "{}"'.format(cmd)) + finally: + compile_lock.release() + del compile_lock + + setCrashInfoHook = __setCrashInfoHook_Mac + + else: + pass + +# The test suite. +suite = unittest2.TestSuite() + +# The list of categories we said we care about +categoriesList = None +# set to true if we are going to use categories for cherry-picking test cases +useCategories = False +# Categories we want to skip +skipCategories = [] +# use this to track per-category failures +failuresPerCategory = {} + +# The path to LLDB.framework is optional. +lldbFrameworkPath = None + +# Test suite repeat count. Can be overwritten with '-# count'. +count = 1 + +# The 'archs' and 'compilers' can be specified via command line. The corresponding +# options can be specified more than once. For example, "-A x86_64 -A i386" +# => archs=['x86_64', 'i386'] and "-C gcc -C clang" => compilers=['gcc', 'clang']. +archs = None # Must be initialized after option parsing +compilers = None # Must be initialized after option parsing + +# The arch might dictate some specific CFLAGS to be passed to the toolchain to build +# the inferior programs. The global variable cflags_extras provides a hook to do +# just that. +cflags_extras = '' + +# The filters (testclass.testmethod) used to admit tests into our test suite. +filters = [] + +# By default, we skip long running test case. Use '-l' option to override. +skip_long_running_test = True + +# Parsable mode silences headers, and any other output this script might generate, and instead +# prints machine-readable output similar to what clang tests produce. +parsable = False + +# The regular expression pattern to match against eligible filenames as our test cases. +regexp = None + +# By default, recorded session info for errored/failed test are dumped into its +# own file under a session directory named after the timestamp of the test suite +# run. Use '-s session-dir-name' to specify a specific dir name. +sdir_name = None + +# Set this flag if there is any session info dumped during the test run. +sdir_has_content = False + +# svn_info stores the output from 'svn info lldb.base.dir'. +svn_info = '' + +# Default verbosity is 0. +verbose = 0 + +# By default, search from the script directory. +# We can't use sys.path[0] to determine the script directory +# because it doesn't work under a debugger +testdirs = [ os.path.dirname(os.path.realpath(__file__)) ] + +# Separator string. +separator = '-' * 70 + +failed = False + +# LLDB Remote platform setting +lldb_platform_name = None +lldb_platform_url = None +lldb_platform_working_dir = None + +# Parallel execution settings +is_inferior_test_runner = False +multiprocess_test_subdir = None +num_threads = None +no_multiprocess_test_runner = False +test_runner_name = None + +# Test results handling globals +results_filename = None +results_port = None +results_formatter_name = None +results_formatter_object = None +results_formatter_options = None +test_result = None + +# Test rerun configuration vars +rerun_all_issues = False +rerun_max_file_threhold = 0 + +# The names of all tests. Used to assert we don't have two tests with the same base name. +all_tests = set() + +# safe default +setCrashInfoHook = lambda x : None + +def shouldSkipBecauseOfCategories(test_categories): + if useCategories: + if len(test_categories) == 0 or len(categoriesList & set(test_categories)) == 0: + return True + + for category in skipCategories: + if category in test_categories: + return True + + return False diff --git a/packages/Python/lldbsuite/test/crashinfo.c b/packages/Python/lldbsuite/test/crashinfo.c new file mode 100644 index 000000000000..19013468bb26 --- /dev/null +++ b/packages/Python/lldbsuite/test/crashinfo.c @@ -0,0 +1,64 @@ +/****************************************************************************** + The LLVM Compiler Infrastructure + + This file is distributed under the University of Illinois Open Source + License. See LICENSE.TXT for details. + ****************************************************************************** + +* This C file vends a simple interface to set the Application Specific Info +* on Mac OS X through Python. To use, compile as a dylib, import crashinfo +* and call crashinfo.setCrashReporterDescription("hello world") +* The testCrashReporterDescription() API is simply there to let you test that this +* is doing what it is intended to do without having to actually cons up a crash +******************************************************************************/ + +#include +#include +#include +#include + +void *__crashreporter_info__ = NULL; + +asm(".desc ___crashreporter_info__, 0x10"); + +static PyObject* setCrashReporterDescription(PyObject* self, PyObject* string) +{ + if (__crashreporter_info__) + { + free(__crashreporter_info__); + __crashreporter_info__ = NULL; + } + + if (string && PyString_Check(string)) + { + Py_ssize_t size = PyString_Size(string); + char* data = PyString_AsString(string); + if (size > 0 && data) + { + ++size; // Include the NULL terminateor in allocation and memcpy() + __crashreporter_info__ = malloc(size); + memcpy(__crashreporter_info__, data, size); + return Py_True; + } + } + return Py_False; +} + +static PyObject* testCrashReporterDescription(PyObject*self, PyObject* arg) +{ + int* ptr = 0; + *ptr = 1; + return Py_None; +} + +static PyMethodDef crashinfo_methods[] = { + {"setCrashReporterDescription", setCrashReporterDescription, METH_O}, + {"testCrashReporterDescription", testCrashReporterDescription, METH_O}, + {NULL, NULL} +}; + +void initcrashinfo() +{ + (void) Py_InitModule("crashinfo", crashinfo_methods); +} + diff --git a/packages/Python/lldbsuite/test/curses_results.py b/packages/Python/lldbsuite/test/curses_results.py new file mode 100644 index 000000000000..9d480b946ea2 --- /dev/null +++ b/packages/Python/lldbsuite/test/curses_results.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python + +""" + The LLVM Compiler Infrastructure + + This file is distributed under the University of Illinois Open Source + License. See LICENSE.TXT for details. + +Configuration options for lldbtest.py set by dotest.py during initialization +""" + +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import curses +import datetime +import math +import sys +import time + +# Third-party modules + +# LLDB modules +from . import lldbcurses +from . import result_formatter +from .result_formatter import EventBuilder + + +class Curses(result_formatter.ResultsFormatter): + """Receives live results from tests that are running and reports them to the terminal in a curses GUI""" + + def __init__(self, out_file, options): + # Initialize the parent + super(Curses, self).__init__(out_file, options) + self.using_terminal = True + self.have_curses = True + self.initialize_event = None + self.jobs = [None] * 64 + self.job_tests = [None] * 64 + self.results = list() + try: + self.main_window = lldbcurses.intialize_curses() + self.main_window.add_key_action('\t', self.main_window.select_next_first_responder, "Switch between views that can respond to keyboard input") + self.main_window.refresh() + self.job_panel = None + self.results_panel = None + self.status_panel = None + self.info_panel = None + self.hide_status_list = list() + self.start_time = time.time() + except: + self.have_curses = False + lldbcurses.terminate_curses() + self.using_terminal = False + print("Unexpected error:", sys.exc_info()[0]) + raise + + self.line_dict = dict() + # self.events_file = open("/tmp/events.txt", "w") + # self.formatters = list() + # if tee_results_formatter: + # self.formatters.append(tee_results_formatter) + + def status_to_short_str(self, status): + if status == EventBuilder.STATUS_SUCCESS: + return '.' + elif status == EventBuilder.STATUS_FAILURE: + return 'F' + elif status == EventBuilder.STATUS_UNEXPECTED_SUCCESS: + return '?' + elif status == EventBuilder.STATUS_EXPECTED_FAILURE: + return 'X' + elif status == EventBuilder.STATUS_SKIP: + return 'S' + elif status == EventBuilder.STATUS_ERROR: + return 'E' + else: + return status + + def show_info_panel(self): + selected_idx = self.results_panel.get_selected_idx() + if selected_idx >= 0 and selected_idx < len(self.results): + if self.info_panel is None: + info_frame = self.results_panel.get_contained_rect(top_inset=10, left_inset=10, right_inset=10, height=30) + self.info_panel = lldbcurses.BoxedPanel(info_frame, "Result Details") + # Add a key action for any key that will hide this panel when any key is pressed + self.info_panel.add_key_action(-1, self.hide_info_panel, 'Hide the info panel') + self.info_panel.top() + else: + self.info_panel.show() + + self.main_window.push_first_responder(self.info_panel) + test_start = self.results[selected_idx][0] + test_result = self.results[selected_idx][1] + self.info_panel.set_line(0, "File: %s" % (test_start['test_filename'])) + self.info_panel.set_line(1, "Test: %s.%s" % (test_start['test_class'], test_start['test_name'])) + self.info_panel.set_line(2, "Time: %s" % (test_result['elapsed_time'])) + self.info_panel.set_line(3, "Status: %s" % (test_result['status'])) + + def hide_info_panel(self): + self.main_window.pop_first_responder(self.info_panel) + self.info_panel.hide() + self.main_window.refresh() + + def toggle_status(self, status): + if status: + # Toggle showing and hiding results whose status matches "status" in "Results" window + if status in self.hide_status_list: + self.hide_status_list.remove(status) + else: + self.hide_status_list.append(status) + self.update_results() + + def update_results(self, update=True): + '''Called after a category of test have been show/hidden to update the results list with + what the user desires to see.''' + self.results_panel.clear(update=False) + for result in self.results: + test_result = result[1] + status = test_result['status'] + if status in self.hide_status_list: + continue + name = test_result['test_class'] + '.' + test_result['test_name'] + self.results_panel.append_line('%s (%6.2f sec) %s' % (self.status_to_short_str(status), test_result['elapsed_time'], name)) + if update: + self.main_window.refresh() + + def handle_event(self, test_event): + with self.lock: + super(Curses, self).handle_event(test_event) + # for formatter in self.formatters: + # formatter.process_event(test_event) + if self.have_curses: + worker_index = -1 + if 'worker_index' in test_event: + worker_index = test_event['worker_index'] + if 'event' in test_event: + check_for_one_key = True + #print(str(test_event), file=self.events_file) + event = test_event['event'] + if self.status_panel: + self.status_panel.update_status('time', str(datetime.timedelta(seconds=math.floor(time.time() - self.start_time)))) + if event == 'test_start': + name = test_event['test_class'] + '.' + test_event['test_name'] + self.job_tests[worker_index] = test_event + if 'pid' in test_event: + line = 'pid: %5d ' % (test_event['pid']) + name + else: + line = name + self.job_panel.set_line(worker_index, line) + self.main_window.refresh() + elif event == 'test_result': + status = test_event['status'] + self.status_panel.increment_status(status) + if 'pid' in test_event: + line = 'pid: %5d ' % (test_event['pid']) + else: + line = '' + self.job_panel.set_line(worker_index, line) + name = test_event['test_class'] + '.' + test_event['test_name'] + elapsed_time = test_event['event_time'] - self.job_tests[worker_index]['event_time'] + if not status in self.hide_status_list: + self.results_panel.append_line('%s (%6.2f sec) %s' % (self.status_to_short_str(status), elapsed_time, name)) + self.main_window.refresh() + # Append the result pairs + test_event['elapsed_time'] = elapsed_time + self.results.append([self.job_tests[worker_index], test_event]) + self.job_tests[worker_index] = '' + elif event == 'job_begin': + self.jobs[worker_index] = test_event + if 'pid' in test_event: + line = 'pid: %5d ' % (test_event['pid']) + else: + line = '' + self.job_panel.set_line(worker_index, line) + elif event == 'job_end': + self.jobs[worker_index] = '' + self.job_panel.set_line(worker_index, '') + elif event == 'initialize': + self.initialize_event = test_event + num_jobs = test_event['worker_count'] + job_frame = self.main_window.get_contained_rect(height=num_jobs+2) + results_frame = self.main_window.get_contained_rect(top_inset=num_jobs+2, bottom_inset=1) + status_frame = self.main_window.get_contained_rect(height=1, top_inset=self.main_window.get_size().h-1) + self.job_panel = lldbcurses.BoxedPanel(frame=job_frame, title="Jobs") + self.results_panel = lldbcurses.BoxedPanel(frame=results_frame, title="Results") + + self.results_panel.add_key_action(curses.KEY_UP, self.results_panel.select_prev , "Select the previous list entry") + self.results_panel.add_key_action(curses.KEY_DOWN, self.results_panel.select_next , "Select the next list entry") + self.results_panel.add_key_action(curses.KEY_HOME, self.results_panel.scroll_begin , "Scroll to the start of the list") + self.results_panel.add_key_action(curses.KEY_END, self.results_panel.scroll_end , "Scroll to the end of the list") + self.results_panel.add_key_action(curses.KEY_ENTER, self.show_info_panel , "Display info for the selected result item") + self.results_panel.add_key_action('.', lambda : self.toggle_status(EventBuilder.STATUS_SUCCESS) , "Toggle showing/hiding tests whose status is 'success'") + self.results_panel.add_key_action('e', lambda : self.toggle_status(EventBuilder.STATUS_ERROR) , "Toggle showing/hiding tests whose status is 'error'") + self.results_panel.add_key_action('f', lambda : self.toggle_status(EventBuilder.STATUS_FAILURE) , "Toggle showing/hiding tests whose status is 'failure'") + self.results_panel.add_key_action('s', lambda : self.toggle_status(EventBuilder.STATUS_SKIP) , "Toggle showing/hiding tests whose status is 'skip'") + self.results_panel.add_key_action('x', lambda : self.toggle_status(EventBuilder.STATUS_EXPECTED_FAILURE) , "Toggle showing/hiding tests whose status is 'expected_failure'") + self.results_panel.add_key_action('?', lambda : self.toggle_status(EventBuilder.STATUS_UNEXPECTED_SUCCESS), "Toggle showing/hiding tests whose status is 'unexpected_success'") + self.status_panel = lldbcurses.StatusPanel(frame=status_frame) + + self.main_window.add_child(self.job_panel) + self.main_window.add_child(self.results_panel) + self.main_window.add_child(self.status_panel) + self.main_window.set_first_responder(self.results_panel) + + self.status_panel.add_status_item(name="time", title="Elapsed", format="%s", width=20, value="0:00:00", update=False) + self.status_panel.add_status_item(name=EventBuilder.STATUS_SUCCESS, title="Success", format="%u", width=20, value=0, update=False) + self.status_panel.add_status_item(name=EventBuilder.STATUS_FAILURE, title="Failure", format="%u", width=20, value=0, update=False) + self.status_panel.add_status_item(name=EventBuilder.STATUS_ERROR, title="Error", format="%u", width=20, value=0, update=False) + self.status_panel.add_status_item(name=EventBuilder.STATUS_SKIP, title="Skipped", format="%u", width=20, value=0, update=True) + self.status_panel.add_status_item(name=EventBuilder.STATUS_EXPECTED_FAILURE, title="Expected Failure", format="%u", width=30, value=0, update=False) + self.status_panel.add_status_item(name=EventBuilder.STATUS_UNEXPECTED_SUCCESS, title="Unexpected Success", format="%u", width=30, value=0, update=False) + self.main_window.refresh() + elif event == 'terminate': + #self.main_window.key_event_loop() + lldbcurses.terminate_curses() + check_for_one_key = False + self.using_terminal = False + # Check for 1 keypress with no delay + + # Check for 1 keypress with no delay + if check_for_one_key: + self.main_window.key_event_loop(0, 1) diff --git a/packages/Python/lldbsuite/test/dosep.py b/packages/Python/lldbsuite/test/dosep.py new file mode 100644 index 000000000000..51275d55c178 --- /dev/null +++ b/packages/Python/lldbsuite/test/dosep.py @@ -0,0 +1,1680 @@ +""" +Run the test suite using a separate process for each test file. + +Each test will run with a time limit of 10 minutes by default. + +Override the default time limit of 10 minutes by setting +the environment variable LLDB_TEST_TIMEOUT. + +E.g., export LLDB_TEST_TIMEOUT=10m + +Override the time limit for individual tests by setting +the environment variable LLDB_[TEST NAME]_TIMEOUT. + +E.g., export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=2m + +Set to "0" to run without time limit. + +E.g., export LLDB_TEST_TIMEOUT=0 +or export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=0 + +To collect core files for timed out tests, +do the following before running dosep.py + +OSX +ulimit -c unlimited +sudo sysctl -w kern.corefile=core.%P + +Linux: +ulimit -c unlimited +echo core.%p | sudo tee /proc/sys/kernel/core_pattern +""" + +from __future__ import print_function +from __future__ import absolute_import + +# system packages and modules +import asyncore +import distutils.version +import fnmatch +import multiprocessing +import multiprocessing.pool +import os +import platform +import re +import signal +import sys +import threading + +from six.moves import queue + +# Our packages and modules +import lldbsuite +import lldbsuite.support.seven as seven + +from . import configuration +from . import dotest_channels +from . import dotest_args +from . import result_formatter + +from .result_formatter import EventBuilder + + +# Todo: Convert this folder layout to be relative-import friendly and +# don't hack up sys.path like this +sys.path.append(os.path.join(os.path.dirname(__file__), "test_runner", "lib")) +import lldb_utils +import process_control + +# Status codes for running command with timeout. +eTimedOut, ePassed, eFailed = 124, 0, 1 + +output_lock = None +test_counter = None +total_tests = None +test_name_len = None +dotest_options = None +RESULTS_FORMATTER = None +RUNNER_PROCESS_ASYNC_MAP = None +RESULTS_LISTENER_CHANNEL = None + +"""Contains an optional function pointer that can return the worker index + for the given thread/process calling it. Returns a 0-based index.""" +GET_WORKER_INDEX = None + + +def setup_global_variables( + lock, counter, total, name_len, options, worker_index_map): + global output_lock, test_counter, total_tests, test_name_len + global dotest_options + output_lock = lock + test_counter = counter + total_tests = total + test_name_len = name_len + dotest_options = options + + if worker_index_map is not None: + # We'll use the output lock for this to avoid sharing another lock. + # This won't be used much. + index_lock = lock + + def get_worker_index_use_pid(): + """Returns a 0-based, process-unique index for the worker.""" + pid = os.getpid() + with index_lock: + if pid not in worker_index_map: + worker_index_map[pid] = len(worker_index_map) + return worker_index_map[pid] + + global GET_WORKER_INDEX + GET_WORKER_INDEX = get_worker_index_use_pid + +def report_test_failure(name, command, output): + global output_lock + with output_lock: + if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()): + print(file=sys.stderr) + print(output, file=sys.stderr) + print("[%s FAILED]" % name, file=sys.stderr) + print("Command invoked: %s" % ' '.join(command), file=sys.stderr) + update_progress(name) + + +def report_test_pass(name, output): + global output_lock + with output_lock: + update_progress(name) + + +def update_progress(test_name=""): + global output_lock, test_counter, total_tests, test_name_len + with output_lock: + counter_len = len(str(total_tests)) + if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()): + sys.stderr.write( + "\r%*d out of %d test suites processed - %-*s" % + (counter_len, test_counter.value, total_tests, + test_name_len.value, test_name)) + if len(test_name) > test_name_len.value: + test_name_len.value = len(test_name) + test_counter.value += 1 + sys.stdout.flush() + sys.stderr.flush() + + +def parse_test_results(output): + passes = 0 + failures = 0 + unexpected_successes = 0 + for result in output: + pass_count = re.search("^RESULT:.*([0-9]+) passes", + result, re.MULTILINE) + fail_count = re.search("^RESULT:.*([0-9]+) failures", + result, re.MULTILINE) + error_count = re.search("^RESULT:.*([0-9]+) errors", + result, re.MULTILINE) + unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes", + result, re.MULTILINE) + if pass_count is not None: + passes = passes + int(pass_count.group(1)) + if fail_count is not None: + failures = failures + int(fail_count.group(1)) + if unexpected_success_count is not None: + unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1)) + if error_count is not None: + failures = failures + int(error_count.group(1)) + return passes, failures, unexpected_successes + + +class DoTestProcessDriver(process_control.ProcessDriver): + """Drives the dotest.py inferior process and handles bookkeeping.""" + def __init__(self, output_file, output_file_lock, pid_events, file_name, + soft_terminate_timeout): + super(DoTestProcessDriver, self).__init__( + soft_terminate_timeout=soft_terminate_timeout) + self.output_file = output_file + self.output_lock = lldb_utils.OptionalWith(output_file_lock) + self.pid_events = pid_events + self.results = None + self.file_name = file_name + + def write(self, content): + with self.output_lock: + self.output_file.write(content) + + def on_process_started(self): + if self.pid_events: + self.pid_events.put_nowait(('created', self.process.pid)) + + def on_process_exited(self, command, output, was_timeout, exit_status): + if self.pid_events: + # No point in culling out those with no exit_status (i.e. + # those we failed to kill). That would just cause + # downstream code to try to kill it later on a Ctrl-C. At + # this point, a best-effort-to-kill already took place. So + # call it destroyed here. + self.pid_events.put_nowait(('destroyed', self.process.pid)) + + # Override the exit status if it was a timeout. + if was_timeout: + exit_status = eTimedOut + + # If we didn't end up with any output, call it empty for + # stdout/stderr. + if output is None: + output = ('', '') + + # Now parse the output. + passes, failures, unexpected_successes = parse_test_results(output) + if exit_status == 0: + # stdout does not have any useful information from 'dotest.py', + # only stderr does. + report_test_pass(self.file_name, output[1]) + else: + report_test_failure(self.file_name, command, output[1]) + + # Save off the results for the caller. + self.results = ( + self.file_name, + exit_status, + passes, + failures, + unexpected_successes) + + def is_exceptional_exit(self): + """Returns whether the process returned a timeout. + + Not valid to call until after on_process_exited() completes. + + @return True if the exit is an exceptional exit (e.g. signal on + POSIX); False otherwise. + """ + if self.results is None: + raise Exception( + "exit status checked before results are available") + return self.process_helper.is_exceptional_exit( + self.results[1]) + + def exceptional_exit_details(self): + if self.results is None: + raise Exception( + "exit status checked before results are available") + return self.process_helper.exceptional_exit_details(self.results[1]) + + def is_timeout(self): + if self.results is None: + raise Exception( + "exit status checked before results are available") + return self.results[1] == eTimedOut + + +def get_soft_terminate_timeout(): + # Defaults to 10 seconds, but can set + # LLDB_TEST_SOFT_TERMINATE_TIMEOUT to a floating point + # number in seconds. This value indicates how long + # the test runner will wait for the dotest inferior to + # handle a timeout via a soft terminate before it will + # assume that failed and do a hard terminate. + + # TODO plumb through command-line option + return float(os.environ.get('LLDB_TEST_SOFT_TERMINATE_TIMEOUT', 10.0)) + + +def want_core_on_soft_terminate(): + # TODO plumb through command-line option + if platform.system() == 'Linux': + return True + else: + return False + + +def send_events_to_collector(events, command): + """Sends the given events to the collector described in the command line. + + @param events the list of events to send to the test event collector. + @param command the inferior command line which contains the details on + how to connect to the test event collector. + """ + if events is None or len(events) == 0: + # Nothing to do. + return + + # Find the port we need to connect to from the --results-port option. + try: + arg_index = command.index("--results-port") + 1 + except ValueError: + # There is no results port, so no way to communicate back to + # the event collector. This is not a problem if we're not + # using event aggregation. + # TODO flag as error once we always use the event system + print( + "INFO: no event collector, skipping post-inferior test " + "event reporting") + return + + if arg_index >= len(command): + raise Exception( + "expected collector port at index {} in {}".format( + arg_index, command)) + event_port = int(command[arg_index]) + + # Create results formatter connected back to collector via socket. + config = result_formatter.FormatterConfig() + config.port = event_port + formatter_spec = result_formatter.create_results_formatter(config) + if formatter_spec is None or formatter_spec.formatter is None: + raise Exception( + "Failed to create socket-based ResultsFormatter " + "back to test event collector") + + # Send the events: the port-based event just pickles the content + # and sends over to the server side of the socket. + for event in events: + formatter_spec.formatter.handle_event(event) + + # Cleanup + if formatter_spec.cleanup_func is not None: + formatter_spec.cleanup_func() + + +def send_inferior_post_run_events( + command, worker_index, process_driver, test_filename): + """Sends any test events that should be generated after the inferior runs. + + These events would include timeouts and exceptional (i.e. signal-returning) + process completion results. + + @param command the list of command parameters passed to subprocess.Popen(). + @param worker_index the worker index (possibly None) used to run + this process + @param process_driver the ProcessDriver-derived instance that was used + to run the inferior process. + @param test_filename the full path to the Python test file that is being + run. + """ + if process_driver is None: + raise Exception("process_driver must not be None") + if process_driver.results is None: + # Invalid condition - the results should have been set one way or + # another, even in a timeout. + raise Exception("process_driver.results were not set") + + # The code below fills in the post events struct. If there are any post + # events to fire up, we'll try to make a connection to the socket and + # provide the results. + post_events = [] + + # Handle signal/exceptional exits. + if process_driver.is_exceptional_exit(): + (code, desc) = process_driver.exceptional_exit_details() + post_events.append( + EventBuilder.event_for_job_exceptional_exit( + process_driver.pid, + worker_index, + code, + desc, + test_filename, + command)) + + # Handle timeouts. + if process_driver.is_timeout(): + post_events.append(EventBuilder.event_for_job_timeout( + process_driver.pid, + worker_index, + test_filename, + command)) + + if len(post_events) > 0: + send_events_to_collector(post_events, command) + + +def call_with_timeout( + command, timeout, name, inferior_pid_events, test_filename): + # Add our worker index (if we have one) to all test events + # from this inferior. + worker_index = None + if GET_WORKER_INDEX is not None: + try: + worker_index = GET_WORKER_INDEX() + command.extend([ + "--event-add-entries", + "worker_index={}:int".format(worker_index)]) + except: # pylint: disable=bare-except + # Ctrl-C does bad things to multiprocessing.Manager.dict() + # lookup. Just swallow it. + pass + + # Create the inferior dotest.py ProcessDriver. + soft_terminate_timeout = get_soft_terminate_timeout() + want_core = want_core_on_soft_terminate() + + process_driver = DoTestProcessDriver( + sys.stdout, + output_lock, + inferior_pid_events, + name, + soft_terminate_timeout) + + # Run it with a timeout. + process_driver.run_command_with_timeout(command, timeout, want_core) + + # Return the results. + if not process_driver.results: + # This is truly exceptional. Even a failing or timed out + # binary should have called the results-generation code. + raise Exception("no test results were generated whatsoever") + + # Handle cases where the test inferior cannot adequately provide + # meaningful results to the test event system. + send_inferior_post_run_events( + command, + worker_index, + process_driver, + test_filename) + + return process_driver.results + + +def process_dir(root, files, dotest_argv, inferior_pid_events): + """Examine a directory for tests, and invoke any found within it.""" + results = [] + for (base_name, full_test_path) in files: + import __main__ as main + script_file = main.__file__ + command = ([sys.executable, script_file] + + dotest_argv + + ["--inferior", "-p", base_name, root]) + + timeout_name = os.path.basename(os.path.splitext(base_name)[0]).upper() + + timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or + getDefaultTimeout(dotest_options.lldb_platform_name)) + + results.append(call_with_timeout( + command, timeout, base_name, inferior_pid_events, full_test_path)) + + # result = (name, status, passes, failures, unexpected_successes) + timed_out = [name for name, status, _, _, _ in results + if status == eTimedOut] + passed = [name for name, status, _, _, _ in results + if status == ePassed] + failed = [name for name, status, _, _, _ in results + if status != ePassed] + unexpected_passes = [ + name for name, _, _, _, unexpected_successes in results + if unexpected_successes > 0] + + pass_count = sum([result[2] for result in results]) + fail_count = sum([result[3] for result in results]) + + return ( + timed_out, passed, failed, unexpected_passes, pass_count, fail_count) + +in_q = None +out_q = None + + +def process_dir_worker_multiprocessing( + a_output_lock, a_test_counter, a_total_tests, a_test_name_len, + a_dotest_options, job_queue, result_queue, inferior_pid_events, + worker_index_map): + """Worker thread main loop when in multiprocessing mode. + Takes one directory specification at a time and works on it.""" + + # Shut off interrupt handling in the child process. + signal.signal(signal.SIGINT, signal.SIG_IGN) + if hasattr(signal, 'SIGHUP'): + signal.signal(signal.SIGHUP, signal.SIG_IGN) + + # Setup the global state for the worker process. + setup_global_variables( + a_output_lock, a_test_counter, a_total_tests, a_test_name_len, + a_dotest_options, worker_index_map) + + # Keep grabbing entries from the queue until done. + while not job_queue.empty(): + try: + job = job_queue.get(block=False) + result = process_dir(job[0], job[1], job[2], + inferior_pid_events) + result_queue.put(result) + except queue.Empty: + # Fine, we're done. + pass + + +def process_dir_worker_multiprocessing_pool(args): + return process_dir(*args) + + +def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events): + """Worker thread main loop when in threading mode. + + This one supports the hand-rolled pooling support. + + Takes one directory specification at a time and works on it.""" + + # Keep grabbing entries from the queue until done. + while not job_queue.empty(): + try: + job = job_queue.get(block=False) + result = process_dir(job[0], job[1], job[2], + inferior_pid_events) + result_queue.put(result) + except queue.Empty: + # Fine, we're done. + pass + + +def process_dir_worker_threading_pool(args): + return process_dir(*args) + + +def process_dir_mapper_inprocess(args): + """Map adapter for running the subprocess-based, non-threaded test runner. + + @param args the process work item tuple + @return the test result tuple + """ + return process_dir(*args) + + +def collect_active_pids_from_pid_events(event_queue): + """ + Returns the set of what should be active inferior pids based on + the event stream. + + @param event_queue a multiprocessing.Queue containing events of the + form: + ('created', pid) + ('destroyed', pid) + + @return set of inferior dotest.py pids activated but never completed. + """ + active_pid_set = set() + while not event_queue.empty(): + pid_event = event_queue.get_nowait() + if pid_event[0] == 'created': + active_pid_set.add(pid_event[1]) + elif pid_event[0] == 'destroyed': + active_pid_set.remove(pid_event[1]) + return active_pid_set + + +def kill_all_worker_processes(workers, inferior_pid_events): + """ + Kills all specified worker processes and their process tree. + + @param workers a list of multiprocess.Process worker objects. + @param inferior_pid_events a multiprocess.Queue that contains + all inferior create and destroy events. Used to construct + the list of child pids still outstanding that need to be killed. + """ + for worker in workers: + worker.terminate() + worker.join() + + # Add all the child test pids created. + active_pid_set = collect_active_pids_from_pid_events( + inferior_pid_events) + for inferior_pid in active_pid_set: + print("killing inferior pid {}".format(inferior_pid)) + os.kill(inferior_pid, signal.SIGKILL) + + +def kill_all_worker_threads(workers, inferior_pid_events): + """ + Kills all specified worker threads and their process tree. + + @param workers a list of multiprocess.Process worker objects. + @param inferior_pid_events a multiprocess.Queue that contains + all inferior create and destroy events. Used to construct + the list of child pids still outstanding that need to be killed. + """ + + # Add all the child test pids created. + active_pid_set = collect_active_pids_from_pid_events( + inferior_pid_events) + for inferior_pid in active_pid_set: + print("killing inferior pid {}".format(inferior_pid)) + os.kill(inferior_pid, signal.SIGKILL) + + # We don't have a way to nuke the threads. However, since we killed + # all the inferiors, and we drained the job queue, this will be + # good enough. Wait cleanly for each worker thread to wrap up. + for worker in workers: + worker.join() + + +def find_test_files_in_dir_tree(dir_root, found_func): + """Calls found_func for all the test files in the given dir hierarchy. + + @param dir_root the path to the directory to start scanning + for test files. All files in this directory and all its children + directory trees will be searched. + + @param found_func a callable object that will be passed + the parent directory (relative to dir_root) and the list of + test files from within that directory. + """ + for root, _, files in os.walk(dir_root, topdown=False): + def is_test_filename(test_dir, base_filename): + """Returns True if the given filename matches the test name format. + + @param test_dir the directory to check. Should be absolute or + relative to current working directory. + + @param base_filename the base name of the filename to check for a + dherence to the python test case filename format. + + @return True if name matches the python test case filename format. + """ + # Not interested in symbolically linked files. + if os.path.islink(os.path.join(test_dir, base_filename)): + return False + # Only interested in test files with the "Test*.py" naming pattern. + return (base_filename.startswith("Test") and + base_filename.endswith(".py")) + + tests = [ + (filename, os.path.join(root, filename)) + for filename in files + if is_test_filename(root, filename)] + if tests: + found_func(root, tests) + + +def initialize_global_vars_common(num_threads, test_work_items): + global total_tests, test_counter, test_name_len + + total_tests = sum([len(item[1]) for item in test_work_items]) + test_counter = multiprocessing.Value('i', 0) + test_name_len = multiprocessing.Value('i', 0) + if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()): + print("Testing: %d test suites, %d thread%s" % ( + total_tests, num_threads, (num_threads > 1) * "s"), file=sys.stderr) + update_progress() + + +def initialize_global_vars_multiprocessing(num_threads, test_work_items): + # Initialize the global state we'll use to communicate with the + # rest of the flat module. + global output_lock + output_lock = multiprocessing.RLock() + + initialize_global_vars_common(num_threads, test_work_items) + + +def initialize_global_vars_threading(num_threads, test_work_items): + """Initializes global variables used in threading mode. + @param num_threads specifies the number of workers used. + @param test_work_items specifies all the work items + that will be processed. + """ + # Initialize the global state we'll use to communicate with the + # rest of the flat module. + global output_lock + output_lock = threading.RLock() + + index_lock = threading.RLock() + index_map = {} + + def get_worker_index_threading(): + """Returns a 0-based, thread-unique index for the worker thread.""" + thread_id = threading.current_thread().ident + with index_lock: + if thread_id not in index_map: + index_map[thread_id] = len(index_map) + return index_map[thread_id] + + + global GET_WORKER_INDEX + GET_WORKER_INDEX = get_worker_index_threading + + initialize_global_vars_common(num_threads, test_work_items) + + +def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler): + """Provides a main loop that is Ctrl-C protected. + + The main loop calls the main_op_func() repeatedly until done_func() + returns true. The ctrl_c_handler() method is called with a single + int parameter that contains the number of times the ctrl_c has been + hit (starting with 1). The ctrl_c_handler() should mutate whatever + it needs to have the done_func() return True as soon as it is desired + to exit the loop. + """ + done = False + ctrl_c_count = 0 + + while not done: + try: + # See if we're done. Start with done check since it is + # the first thing executed after a Ctrl-C handler in the + # following loop. + done = done_func() + if not done: + # Run the main op once. + main_op_func() + + except KeyboardInterrupt: + ctrl_c_count += 1 + ctrl_c_handler(ctrl_c_count) + + +def pump_workers_and_asyncore_map(workers, asyncore_map): + """Prunes out completed workers and maintains the asyncore loop. + + The asyncore loop contains the optional socket listener + and handlers. When all workers are complete, this method + takes care of stopping the listener. It also runs the + asyncore loop for the given async map for 10 iterations. + + @param workers the list of worker Thread/Process instances. + + @param asyncore_map the asyncore threading-aware map that + indicates which channels are in use and still alive. + """ + + # Check on all the workers, removing them from the workers + # list as they complete. + dead_workers = [] + for worker in workers: + # This non-blocking join call is what allows us + # to still receive keyboard interrupts. + worker.join(0.01) + if not worker.is_alive(): + dead_workers.append(worker) + # Clear out the completed workers + for dead_worker in dead_workers: + workers.remove(dead_worker) + + # If there are no more workers and there is a listener, + # close the listener. + global RESULTS_LISTENER_CHANNEL + if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None: + RESULTS_LISTENER_CHANNEL.close() + RESULTS_LISTENER_CHANNEL = None + + # Pump the asyncore map if it isn't empty. + if len(asyncore_map) > 0: + asyncore.loop(0.1, False, asyncore_map, 10) + + +def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events, + stop_all_inferiors_func): + """Performs the appropriate ctrl-c action for non-pool parallel test runners + + @param ctrl_c_count starting with 1, indicates the number of times ctrl-c + has been intercepted. The value is 1 on the first intercept, 2 on the + second, etc. + + @param job_queue a Queue object that contains the work still outstanding + (i.e. hasn't been assigned to a worker yet). + + @param workers list of Thread or Process workers. + + @param inferior_pid_events specifies a Queue of inferior process + construction and destruction events. Used to build the list of inferior + processes that should be killed if we get that far. + + @param stop_all_inferiors_func a callable object that takes the + workers and inferior_pid_events parameters (in that order) if a hard + stop is to be used on the workers. + """ + + # Print out which Ctrl-C we're handling. + key_name = [ + "first", + "second", + "third", + "many"] + + if ctrl_c_count < len(key_name): + name_index = ctrl_c_count - 1 + else: + name_index = len(key_name) - 1 + message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index]) + with output_lock: + print(message) + + if ctrl_c_count == 1: + # Remove all outstanding items from the work queue so we stop + # doing any more new work. + while not job_queue.empty(): + try: + # Just drain it to stop more work from being started. + job_queue.get_nowait() + except queue.Empty: + pass + with output_lock: + print("Stopped more work from being started.") + elif ctrl_c_count == 2: + # Try to stop all inferiors, even the ones currently doing work. + stop_all_inferiors_func(workers, inferior_pid_events) + else: + with output_lock: + print("All teardown activities kicked off, should finish soon.") + + +def workers_and_async_done(workers, async_map): + """Returns True if the workers list and asyncore channels are all done. + + @param workers list of workers (threads/processes). These must adhere + to the threading Thread or multiprocessing.Process interface. + + @param async_map the threading-aware asyncore channel map to check + for live channels. + + @return False if the workers list exists and has any entries in it, or + if the async_map exists and has any entries left in it; otherwise, True. + """ + if workers is not None and len(workers) > 0: + # We're not done if we still have workers left. + return False + if async_map is not None and len(async_map) > 0: + return False + # We're done. + return True + + +def multiprocessing_test_runner(num_threads, test_work_items): + """Provides hand-wrapped pooling test runner adapter with Ctrl-C support. + + This concurrent test runner is based on the multiprocessing + library, and rolls its own worker pooling strategy so it + can handle Ctrl-C properly. + + This test runner is known to have an issue running on + Windows platforms. + + @param num_threads the number of worker processes to use. + + @param test_work_items the iterable of test work item tuples + to run. + """ + + # Initialize our global state. + initialize_global_vars_multiprocessing(num_threads, test_work_items) + + # Create jobs. + job_queue = multiprocessing.Queue(len(test_work_items)) + for test_work_item in test_work_items: + job_queue.put(test_work_item) + + result_queue = multiprocessing.Queue(len(test_work_items)) + + # Create queues for started child pids. Terminating + # the multiprocess processes does not terminate the + # child processes they spawn. We can remove this tracking + # if/when we move to having the multiprocess process directly + # perform the test logic. The Queue size needs to be able to + # hold 2 * (num inferior dotest.py processes started) entries. + inferior_pid_events = multiprocessing.Queue(4096) + + # Worker dictionary allows each worker to figure out its worker index. + manager = multiprocessing.Manager() + worker_index_map = manager.dict() + + # Create workers. We don't use multiprocessing.Pool due to + # challenges with handling ^C keyboard interrupts. + workers = [] + for _ in range(num_threads): + worker = multiprocessing.Process( + target=process_dir_worker_multiprocessing, + args=(output_lock, + test_counter, + total_tests, + test_name_len, + dotest_options, + job_queue, + result_queue, + inferior_pid_events, + worker_index_map)) + worker.start() + workers.append(worker) + + # Main loop: wait for all workers to finish and wait for + # the socket handlers to wrap up. + ctrl_c_loop( + # Main operation of loop + lambda: pump_workers_and_asyncore_map( + workers, RUNNER_PROCESS_ASYNC_MAP), + + # Return True when we're done with the main loop. + lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP), + + # Indicate what we do when we receive one or more Ctrl-Cs. + lambda ctrl_c_count: handle_ctrl_c( + ctrl_c_count, job_queue, workers, inferior_pid_events, + kill_all_worker_processes)) + + # Reap the test results. + test_results = [] + while not result_queue.empty(): + test_results.append(result_queue.get(block=False)) + return test_results + + +def map_async_run_loop(future, channel_map, listener_channel): + """Blocks until the Pool.map_async completes and the channel completes. + + @param future an AsyncResult instance from a Pool.map_async() call. + + @param channel_map the asyncore dispatch channel map that should be pumped. + Optional: may be None. + + @param listener_channel the channel representing a listener that should be + closed once the map_async results are available. + + @return the results from the async_result instance. + """ + map_results = None + + done = False + while not done: + # Check if we need to reap the map results. + if map_results is None: + if future.ready(): + # Get the results. + map_results = future.get() + + # Close the runner process listener channel if we have + # one: no more connections will be incoming. + if listener_channel is not None: + listener_channel.close() + + # Pump the asyncore loop if we have a listener socket. + if channel_map is not None: + asyncore.loop(0.01, False, channel_map, 10) + + # Figure out if we're done running. + done = map_results is not None + if channel_map is not None: + # We have a runner process async map. Check if it + # is complete. + if len(channel_map) > 0: + # We still have an asyncore channel running. Not done yet. + done = False + + return map_results + + +def multiprocessing_test_runner_pool(num_threads, test_work_items): + # Initialize our global state. + initialize_global_vars_multiprocessing(num_threads, test_work_items) + + manager = multiprocessing.Manager() + worker_index_map = manager.dict() + + pool = multiprocessing.Pool( + num_threads, + initializer=setup_global_variables, + initargs=(output_lock, test_counter, total_tests, test_name_len, + dotest_options, worker_index_map)) + + # Start the map operation (async mode). + map_future = pool.map_async( + process_dir_worker_multiprocessing_pool, test_work_items) + return map_async_run_loop( + map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL) + + +def threading_test_runner(num_threads, test_work_items): + """Provides hand-wrapped pooling threading-based test runner adapter + with Ctrl-C support. + + This concurrent test runner is based on the threading + library, and rolls its own worker pooling strategy so it + can handle Ctrl-C properly. + + @param num_threads the number of worker processes to use. + + @param test_work_items the iterable of test work item tuples + to run. + """ + + # Initialize our global state. + initialize_global_vars_threading(num_threads, test_work_items) + + # Create jobs. + job_queue = queue.Queue() + for test_work_item in test_work_items: + job_queue.put(test_work_item) + + result_queue = queue.Queue() + + # Create queues for started child pids. Terminating + # the threading threads does not terminate the + # child processes they spawn. + inferior_pid_events = queue.Queue() + + # Create workers. We don't use multiprocessing.pool.ThreadedPool + # due to challenges with handling ^C keyboard interrupts. + workers = [] + for _ in range(num_threads): + worker = threading.Thread( + target=process_dir_worker_threading, + args=(job_queue, + result_queue, + inferior_pid_events)) + worker.start() + workers.append(worker) + + # Main loop: wait for all workers to finish and wait for + # the socket handlers to wrap up. + ctrl_c_loop( + # Main operation of loop + lambda: pump_workers_and_asyncore_map( + workers, RUNNER_PROCESS_ASYNC_MAP), + + # Return True when we're done with the main loop. + lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP), + + # Indicate what we do when we receive one or more Ctrl-Cs. + lambda ctrl_c_count: handle_ctrl_c( + ctrl_c_count, job_queue, workers, inferior_pid_events, + kill_all_worker_threads)) + + # Reap the test results. + test_results = [] + while not result_queue.empty(): + test_results.append(result_queue.get(block=False)) + return test_results + + +def threading_test_runner_pool(num_threads, test_work_items): + # Initialize our global state. + initialize_global_vars_threading(num_threads, test_work_items) + + pool = multiprocessing.pool.ThreadPool(num_threads) + map_future = pool.map_async( + process_dir_worker_threading_pool, test_work_items) + + return map_async_run_loop( + map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL) + + +def asyncore_run_loop(channel_map): + try: + asyncore.loop(None, False, channel_map) + except: + # Swallow it, we're seeing: + # error: (9, 'Bad file descriptor') + # when the listener channel is closed. Shouldn't be the case. + pass + + +def inprocess_exec_test_runner(test_work_items): + # Initialize our global state. + initialize_global_vars_multiprocessing(1, test_work_items) + + # We're always worker index 0 + global GET_WORKER_INDEX + GET_WORKER_INDEX = lambda: 0 + + # Run the listener and related channel maps in a separate thread. + # global RUNNER_PROCESS_ASYNC_MAP + global RESULTS_LISTENER_CHANNEL + if RESULTS_LISTENER_CHANNEL is not None: + socket_thread = threading.Thread( + target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP)) + socket_thread.start() + + # Do the work. + test_results = list(map(process_dir_mapper_inprocess, test_work_items)) + + # If we have a listener channel, shut it down here. + if RESULTS_LISTENER_CHANNEL is not None: + # Close down the channel. + RESULTS_LISTENER_CHANNEL.close() + RESULTS_LISTENER_CHANNEL = None + + # Wait for the listener and handlers to complete. + socket_thread.join() + + return test_results + +def walk_and_invoke(test_files, dotest_argv, num_workers, test_runner_func): + """Invokes the test runner on each test file specified by test_files. + + @param test_files a list of (test_subdir, list_of_test_files_in_dir) + @param num_workers the number of worker queues working on these test files + @param test_runner_func the test runner configured to run the tests + + @return a tuple of results from the running of the specified tests, + of the form (timed_out, passed, failed, unexpected_successes, pass_count, + fail_count) + """ + # The async_map is important to keep all thread-related asyncore + # channels distinct when we call asyncore.loop() later on. + global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP + RUNNER_PROCESS_ASYNC_MAP = {} + + # If we're outputting side-channel test results, create the socket + # listener channel and tell the inferior to send results to the + # port on which we'll be listening. + if RESULTS_FORMATTER is not None: + forwarding_func = RESULTS_FORMATTER.handle_event + RESULTS_LISTENER_CHANNEL = ( + dotest_channels.UnpicklingForwardingListenerChannel( + RUNNER_PROCESS_ASYNC_MAP, "localhost", 0, + 2 * num_workers, forwarding_func)) + # Set the results port command line arg. Might have been + # inserted previous, so first try to replace. + listener_port = str(RESULTS_LISTENER_CHANNEL.address[1]) + try: + port_value_index = dotest_argv.index("--results-port") + 1 + dotest_argv[port_value_index] = listener_port + except ValueError: + # --results-port doesn't exist (yet), add it + dotest_argv.append("--results-port") + dotest_argv.append(listener_port) + + # Build the test work items out of the (dir, file_list) entries passed in. + test_work_items = [] + for entry in test_files: + test_work_items.append((entry[0], entry[1], dotest_argv, None)) + + # Convert test work items into test results using whatever + # was provided as the test run function. + test_results = test_runner_func(test_work_items) + + # Summarize the results and return to caller. + timed_out = sum([result[0] for result in test_results], []) + passed = sum([result[1] for result in test_results], []) + failed = sum([result[2] for result in test_results], []) + unexpected_successes = sum([result[3] for result in test_results], []) + pass_count = sum([result[4] for result in test_results]) + fail_count = sum([result[5] for result in test_results]) + + return (timed_out, passed, failed, unexpected_successes, pass_count, + fail_count) + + +def getExpectedTimeouts(platform_name): + # returns a set of test filenames that might timeout + # are we running against a remote target? + host = sys.platform + if platform_name is None: + target = sys.platform + else: + m = re.search(r'remote-(\w+)', platform_name) + target = m.group(1) + + expected_timeout = set() + + if target.startswith("android"): + expected_timeout |= { + "TestExitDuringStep.py", + "TestHelloWorld.py", + } + if host.startswith("win32"): + expected_timeout |= { + "TestEvents.py", + "TestThreadStates.py", + } + elif target.startswith("freebsd"): + expected_timeout |= { + "TestBreakpointConditions.py", + "TestChangeProcessGroup.py", + "TestValueObjectRecursion.py", + "TestWatchpointConditionAPI.py", + } + elif target.startswith("darwin"): + expected_timeout |= { + # times out on MBP Retina, Mid 2012 + "TestThreadSpecificBreakpoint.py", + "TestExitDuringStep.py", + "TestIntegerTypesExpr.py", + } + return expected_timeout + + +def getDefaultTimeout(platform_name): + if os.getenv("LLDB_TEST_TIMEOUT"): + return os.getenv("LLDB_TEST_TIMEOUT") + + if platform_name is None: + platform_name = sys.platform + + if platform_name.startswith("remote-"): + return "10m" + elif platform_name == 'darwin': + # We are consistently needing more time on a few tests. + return "6m" + else: + return "4m" + + +def touch(fname, times=None): + if os.path.exists(fname): + os.utime(fname, times) + + +def find(pattern, path): + result = [] + for root, dirs, files in os.walk(path): + for name in files: + if fnmatch.fnmatch(name, pattern): + result.append(os.path.join(root, name)) + return result + + +def get_test_runner_strategies(num_threads): + """Returns the test runner strategies by name in a dictionary. + + @param num_threads specifies the number of threads/processes + that will be used for concurrent test runners. + + @return dictionary with key as test runner strategy name and + value set to a callable object that takes the test work item + and returns a test result tuple. + """ + return { + # multiprocessing supports ctrl-c and does not use + # multiprocessing.Pool. + "multiprocessing": + (lambda work_items: multiprocessing_test_runner( + num_threads, work_items)), + + # multiprocessing-pool uses multiprocessing.Pool but + # does not support Ctrl-C. + "multiprocessing-pool": + (lambda work_items: multiprocessing_test_runner_pool( + num_threads, work_items)), + + # threading uses a hand-rolled worker pool much + # like multiprocessing, but instead uses in-process + # worker threads. This one supports Ctrl-C. + "threading": + (lambda work_items: threading_test_runner(num_threads, work_items)), + + # threading-pool uses threading for the workers (in-process) + # and uses the multiprocessing.pool thread-enabled pool. + # This does not properly support Ctrl-C. + "threading-pool": + (lambda work_items: threading_test_runner_pool( + num_threads, work_items)), + + # serial uses the subprocess-based, single process + # test runner. This provides process isolation but + # no concurrent test execution. + "serial": + inprocess_exec_test_runner + } + + +def _remove_option( + args, long_option_name, short_option_name, takes_arg): + """Removes option and related option arguments from args array. + + This method removes all short/long options that match the given + arguments. + + @param args the array of command line arguments (in/out) + + @param long_option_name the full command line representation of the + long-form option that will be removed (including '--'). + + @param short_option_name the short version of the command line option + that will be removed (including '-'). + + @param takes_arg True if the option takes an argument. + + """ + if long_option_name is not None: + regex_string = "^" + long_option_name + "=" + long_regex = re.compile(regex_string) + if short_option_name is not None: + # Short options we only match the -X and assume + # any arg is one command line argument jammed together. + # i.e. -O--abc=1 is a single argument in the args list. + # We don't handle -O --abc=1, as argparse doesn't handle + # it, either. + regex_string = "^" + short_option_name + short_regex = re.compile(regex_string) + + def remove_long_internal(): + """Removes one matching long option from args. + @returns True if one was found and removed; False otherwise. + """ + try: + index = args.index(long_option_name) + # Handle the exact match case. + if takes_arg: + removal_count = 2 + else: + removal_count = 1 + del args[index:index+removal_count] + return True + except ValueError: + # Thanks to argparse not handling options with known arguments + # like other options parsing libraries (see + # https://bugs.python.org/issue9334), we need to support the + # --results-formatter-options={second-level-arguments} (note + # the equal sign to fool the first-level arguments parser into + # not treating the second-level arguments as first-level + # options). We're certainly at risk of getting this wrong + # since now we're forced into the business of trying to figure + # out what is an argument (although I think this + # implementation will suffice). + for index in range(len(args)): + match = long_regex.search(args[index]) + if match: + del args[index] + return True + return False + + def remove_short_internal(): + """Removes one matching short option from args. + @returns True if one was found and removed; False otherwise. + """ + for index in range(len(args)): + match = short_regex.search(args[index]) + if match: + del args[index] + return True + return False + + removal_count = 0 + while long_option_name is not None and remove_long_internal(): + removal_count += 1 + while short_option_name is not None and remove_short_internal(): + removal_count += 1 + if removal_count == 0: + raise Exception( + "failed to find at least one of '{}', '{}' in options".format( + long_option_name, short_option_name)) + + +def adjust_inferior_options(dotest_argv): + """Adjusts the commandline args array for inferiors. + + This method adjusts the inferior dotest commandline options based + on the parallel test runner's options. Some of the inferior options + will need to change to properly handle aggregation functionality. + """ + global dotest_options + + # If we don't have a session directory, create one. + if not dotest_options.s: + # no session log directory, we need to add this to prevent + # every dotest invocation from creating its own directory + import datetime + # The windows platforms don't like ':' in the pathname. + timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S") + dotest_argv.append('-s') + dotest_argv.append(timestamp_started) + dotest_options.s = timestamp_started + + # Adjust inferior results formatter options - if the parallel + # test runner is collecting into the user-specified test results, + # we'll have inferiors spawn with the --results-port option and + # strip the original test runner options. + if dotest_options.results_file is not None: + _remove_option(dotest_argv, "--results-file", None, True) + if dotest_options.results_port is not None: + _remove_option(dotest_argv, "--results-port", None, True) + if dotest_options.results_formatter is not None: + _remove_option(dotest_argv, "--results-formatter", None, True) + if dotest_options.results_formatter_options is not None: + _remove_option(dotest_argv, "--results-formatter-option", "-O", + True) + + # Remove the --curses shortcut if specified. + if dotest_options.curses: + _remove_option(dotest_argv, "--curses", None, False) + + # Remove test runner name if present. + if dotest_options.test_runner_name is not None: + _remove_option(dotest_argv, "--test-runner-name", None, True) + + +def is_darwin_version_lower_than(target_version): + """Checks that os is Darwin and version is lower than target_version. + + @param target_version the StrictVersion indicating the version + we're checking against. + + @return True if the OS is Darwin (OS X) and the version number of + the OS is less than target_version; False in all other cases. + """ + if platform.system() != 'Darwin': + # Can't be Darwin lower than a certain version. + return False + + system_version = distutils.version.StrictVersion(platform.mac_ver()[0]) + return seven.cmp_(system_version, target_version) < 0 + + +def default_test_runner_name(num_threads): + """Returns the default test runner name for the configuration. + + @param num_threads the number of threads/workers this test runner is + supposed to use. + + @return the test runner name that should be used by default when + no test runner was explicitly called out on the command line. + """ + if num_threads == 1: + # Use the serial runner. + test_runner_name = "serial" + elif os.name == "nt": + # On Windows, Python uses CRT with a low limit on the number of open + # files. If you have a lot of cores, the threading-pool runner will + # often fail because it exceeds that limit. It's not clear what the + # right balance is, so until we can investigate it more deeply, + # just use the one that works + test_runner_name = "multiprocessing-pool" + elif is_darwin_version_lower_than( + distutils.version.StrictVersion("10.10.0")): + # OS X versions before 10.10 appear to have an issue using + # the threading test runner. Fall back to multiprocessing. + # Supports Ctrl-C. + test_runner_name = "multiprocessing" + else: + # For everyone else, use the ctrl-c-enabled threading support. + # Should use fewer system resources than the multprocessing + # variant. + test_runner_name = "threading" + return test_runner_name + + +def rerun_tests(test_subdir, tests_for_rerun, dotest_argv): + # Build the list of test files to rerun. Some future time we'll + # enable re-run by test method so we can constrain the rerun set + # to just the method(s) that were in issued within a file. + + # Sort rerun files into subdirectories. + print("\nRerunning the following files:") + rerun_files_by_subdir = {} + for test_filename in tests_for_rerun.keys(): + # Print the file we'll be rerunning + test_relative_path = os.path.relpath( + test_filename, lldbsuite.lldb_test_root) + print(" {}".format(test_relative_path)) + + # Store test filenames by subdir. + test_dir = os.path.dirname(test_filename) + test_basename = os.path.basename(test_filename) + if test_dir in rerun_files_by_subdir: + rerun_files_by_subdir[test_dir].append( + (test_basename, test_filename)) + else: + rerun_files_by_subdir[test_dir] = [(test_basename, test_filename)] + + # Break rerun work up by subdirectory. We do this since + # we have an invariant that states only one test file can + # be run at a time in any given subdirectory (related to + # rules around built inferior test program lifecycle). + rerun_work = [] + for files_by_subdir in rerun_files_by_subdir.values(): + rerun_work.append((test_subdir, files_by_subdir)) + + # Run the work with the serial runner. + # Do not update legacy counts, I am getting rid of + # them so no point adding complicated merge logic here. + rerun_thread_count = 1 + # Force the parallel test runner to choose a multi-worker strategy. + rerun_runner_name = default_test_runner_name(rerun_thread_count + 1) + print("rerun will use the '{}' test runner strategy".format( + rerun_runner_name)) + + runner_strategies_by_name = get_test_runner_strategies(rerun_thread_count) + rerun_runner_func = runner_strategies_by_name[ + rerun_runner_name] + if rerun_runner_func is None: + raise Exception( + "failed to find rerun test runner " + "function named '{}'".format(rerun_runner_name)) + + walk_and_invoke( + rerun_work, + dotest_argv, + rerun_thread_count, + rerun_runner_func) + print("\nTest rerun complete\n") + + +def main(num_threads, test_subdir, test_runner_name, results_formatter): + """Run dotest.py in inferior mode in parallel. + + @param num_threads the parsed value of the num-threads command line + argument. + + @param test_subdir optionally specifies a subdir to limit testing + within. May be None if the entire test tree is to be used. This subdir + is assumed to be relative to the lldb/test root of the test hierarchy. + + @param test_runner_name if specified, contains the test runner + name which selects the strategy used to run the isolated and + optionally concurrent test runner. Specify None to allow the + system to choose the most appropriate test runner given desired + thread count and OS type. + + @param results_formatter if specified, provides the TestResultsFormatter + instance that will format and output test result data from the + side-channel test results. When specified, inferior dotest calls + will send test results side-channel data over a socket to the parallel + test runner, which will forward them on to results_formatter. + """ + + # Do not shut down on sighup. + if hasattr(signal, 'SIGHUP'): + signal.signal(signal.SIGHUP, signal.SIG_IGN) + + dotest_argv = sys.argv[1:] + + global RESULTS_FORMATTER + RESULTS_FORMATTER = results_formatter + + # We can't use sys.path[0] to determine the script directory + # because it doesn't work under a debugger + parser = dotest_args.create_parser() + global dotest_options + dotest_options = dotest_args.parse_args(parser, dotest_argv) + + adjust_inferior_options(dotest_argv) + + session_dir = os.path.join(os.getcwd(), dotest_options.s) + + # The root directory was specified on the command line + test_directory = os.path.dirname(os.path.realpath(__file__)) + if test_subdir and len(test_subdir) > 0: + test_subdir = os.path.join(test_directory, test_subdir) + else: + test_subdir = test_directory + + # clean core files in test tree from previous runs (Linux) + cores = find('core.*', test_subdir) + for core in cores: + os.unlink(core) + + system_info = " ".join(platform.uname()) + + # Figure out which test files should be enabled for expected + # timeout + expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name) + if results_formatter is not None: + results_formatter.set_expected_timeouts_by_basename(expected_timeout) + + # Figure out which testrunner strategy we'll use. + runner_strategies_by_name = get_test_runner_strategies(num_threads) + + # If the user didn't specify a test runner strategy, determine + # the default now based on number of threads and OS type. + if not test_runner_name: + test_runner_name = default_test_runner_name(num_threads) + + if test_runner_name not in runner_strategies_by_name: + raise Exception( + "specified testrunner name '{}' unknown. Valid choices: {}".format( + test_runner_name, + list(runner_strategies_by_name.keys()))) + test_runner_func = runner_strategies_by_name[test_runner_name] + + # Collect the files on which we'll run the first test run phase. + test_files = [] + find_test_files_in_dir_tree( + test_subdir, lambda tdir, tfiles: test_files.append( + (test_subdir, tfiles))) + + # Do the first test run phase. + summary_results = walk_and_invoke( + test_files, + dotest_argv, + num_threads, + test_runner_func) + + (timed_out, passed, failed, unexpected_successes, pass_count, + fail_count) = summary_results + + # Check if we have any tests to rerun as phase 2. + if results_formatter is not None: + tests_for_rerun = results_formatter.tests_for_rerun + results_formatter.tests_for_rerun = {} + + if tests_for_rerun is not None and len(tests_for_rerun) > 0: + rerun_file_count = len(tests_for_rerun) + print("\n{} test files marked for rerun\n".format( + rerun_file_count)) + + # Check if the number of files exceeds the max cutoff. If so, + # we skip the rerun step. + if rerun_file_count > configuration.rerun_max_file_threshold: + print("Skipping rerun: max rerun file threshold ({}) " + "exceeded".format( + configuration.rerun_max_file_threshold)) + else: + rerun_tests(test_subdir, tests_for_rerun, dotest_argv) + + # The results formatter - if present - is done now. Tell it to + # terminate. + if results_formatter is not None: + results_formatter.send_terminate_as_needed() + + timed_out = set(timed_out) + num_test_files = len(passed) + len(failed) + num_test_cases = pass_count + fail_count + + # move core files into session dir + cores = find('core.*', test_subdir) + for core in cores: + dst = core.replace(test_directory, "")[1:] + dst = dst.replace(os.path.sep, "-") + os.rename(core, os.path.join(session_dir, dst)) + + # remove expected timeouts from failures + for xtime in expected_timeout: + if xtime in timed_out: + timed_out.remove(xtime) + failed.remove(xtime) + result = "ExpectedTimeout" + elif xtime in passed: + result = "UnexpectedCompletion" + else: + result = None # failed + + if result: + test_name = os.path.splitext(xtime)[0] + touch(os.path.join(session_dir, "{}-{}".format(result, test_name))) + + # Only run the old summary logic if we don't have a results formatter + # that already prints the summary. + print_legacy_summary = results_formatter is None + if not print_legacy_summary: + # Print summary results. Summarized results at the end always + # get printed to stdout, even if --results-file specifies a different + # file for, say, xUnit output. + results_formatter.print_results(sys.stdout) + + # Figure out exit code by count of test result types. + issue_count = 0 + for issue_status in EventBuilder.TESTRUN_ERROR_STATUS_VALUES: + issue_count += results_formatter.counts_by_test_result_status( + issue_status) + + # Return with appropriate result code + if issue_count > 0: + sys.exit(1) + else: + sys.exit(0) + else: + # Print the legacy test results summary. + print() + sys.stdout.write("Ran %d test suites" % num_test_files) + if num_test_files > 0: + sys.stdout.write(" (%d failed) (%f%%)" % ( + len(failed), 100.0 * len(failed) / num_test_files)) + print() + sys.stdout.write("Ran %d test cases" % num_test_cases) + if num_test_cases > 0: + sys.stdout.write(" (%d failed) (%f%%)" % ( + fail_count, 100.0 * fail_count / num_test_cases)) + print() + exit_code = 0 + + if len(failed) > 0: + failed.sort() + print("Failing Tests (%d)" % len(failed)) + for f in failed: + print("%s: LLDB (suite) :: %s (%s)" % ( + "TIMEOUT" if f in timed_out else "FAIL", f, system_info + )) + exit_code = 1 + + if len(unexpected_successes) > 0: + unexpected_successes.sort() + print("\nUnexpected Successes (%d)" % len(unexpected_successes)) + for u in unexpected_successes: + print("UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info)) + + sys.exit(exit_code) + +if __name__ == '__main__': + sys.stderr.write( + "error: dosep.py no longer supports being called directly. " + "Please call dotest.py directly. The dosep.py-specific arguments " + "have been added under the Parallel processing arguments.\n") + sys.exit(128) diff --git a/packages/Python/lldbsuite/test/dotest.py b/packages/Python/lldbsuite/test/dotest.py new file mode 100644 index 000000000000..723f26a417e4 --- /dev/null +++ b/packages/Python/lldbsuite/test/dotest.py @@ -0,0 +1,1128 @@ +""" +A simple testing framework for lldb using python's unit testing framework. + +Tests for lldb are written as python scripts which take advantage of the script +bridging provided by LLDB.framework to interact with lldb core. + +A specific naming pattern is followed by the .py script to be recognized as +a module which implements a test scenario, namely, Test*.py. + +To specify the directories where "Test*.py" python test scripts are located, +you need to pass in a list of directory names. By default, the current +working directory is searched if nothing is specified on the command line. + +Type: + +./dotest.py -h + +for available options. +""" + +from __future__ import absolute_import +from __future__ import print_function + +# System modules +import atexit +import importlib +import os +import errno +import platform +import signal +import socket +import subprocess +import sys +import inspect + +# Third-party modules +import six +import unittest2 + +# LLDB Modules +import lldbsuite +from . import configuration +from . import dotest_args +from . import lldbtest_config +from . import test_categories +from . import result_formatter +from . import test_result +from .result_formatter import EventBuilder +from ..support import seven + +def is_exe(fpath): + """Returns true if fpath is an executable.""" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + """Returns the full path to a program; None otherwise.""" + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +class _WritelnDecorator(object): + """Used to decorate file-like objects with a handy 'writeln' method""" + def __init__(self,stream): + self.stream = stream + + def __getattr__(self, attr): + if attr in ('stream', '__getstate__'): + raise AttributeError(attr) + return getattr(self.stream,attr) + + def writeln(self, arg=None): + if arg: + self.write(arg) + self.write('\n') # text-mode streams translate to \r\n if needed + +# +# Global variables: +# +def usage(parser): + parser.print_help() + if configuration.verbose > 0: + print(""" +Examples: + +This is an example of using the -f option to pinpoint to a specific test class +and test method to be run: + +$ ./dotest.py -f ClassTypesTestCase.test_with_dsym_and_run_command +---------------------------------------------------------------------- +Collected 1 test + +test_with_dsym_and_run_command (TestClassTypes.ClassTypesTestCase) +Test 'frame variable this' when stopped on a class constructor. ... ok + +---------------------------------------------------------------------- +Ran 1 test in 1.396s + +OK + +And this is an example of using the -p option to run a single file (the filename +matches the pattern 'ObjC' and it happens to be 'TestObjCMethods.py'): + +$ ./dotest.py -v -p ObjC +---------------------------------------------------------------------- +Collected 4 tests + +test_break_with_dsym (TestObjCMethods.FoundationTestCase) +Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok +test_break_with_dwarf (TestObjCMethods.FoundationTestCase) +Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'. ... ok +test_data_type_and_expr_with_dsym (TestObjCMethods.FoundationTestCase) +Lookup objective-c data types and evaluate expressions. ... ok +test_data_type_and_expr_with_dwarf (TestObjCMethods.FoundationTestCase) +Lookup objective-c data types and evaluate expressions. ... ok + +---------------------------------------------------------------------- +Ran 4 tests in 16.661s + +OK + +Running of this script also sets up the LLDB_TEST environment variable so that +individual test cases can locate their supporting files correctly. The script +tries to set up Python's search paths for modules by looking at the build tree +relative to this script. See also the '-i' option in the following example. + +Finally, this is an example of using the lldb.py module distributed/installed by +Xcode4 to run against the tests under the 'forward' directory, and with the '-w' +option to add some delay between two tests. It uses ARCH=x86_64 to specify that +as the architecture and CC=clang to specify the compiler used for the test run: + +$ PYTHONPATH=/Xcode4/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python ARCH=x86_64 CC=clang ./dotest.py -v -w -i forward + +Session logs for test failures/errors will go into directory '2010-11-11-13_56_16' +---------------------------------------------------------------------- +Collected 2 tests + +test_with_dsym_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase) +Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok +test_with_dwarf_and_run_command (TestForwardDeclaration.ForwardDeclarationTestCase) +Display *bar_ptr when stopped on a function with forward declaration of struct bar. ... ok + +---------------------------------------------------------------------- +Ran 2 tests in 5.659s + +OK + +The 'Session ...' verbiage is recently introduced (see also the '-s' option) to +notify the directory containing the session logs for test failures or errors. +In case there is any test failure/error, a similar message is appended at the +end of the stderr output for your convenience. + +ENABLING LOGS FROM TESTS + +Option 1: + +Writing logs into different files per test case:: + +This option is particularly useful when multiple dotest instances are created +by dosep.py + +$ ./dotest.py --channel "lldb all" + +$ ./dotest.py --channel "lldb all" --channel "gdb-remote packets" + +These log files are written to: + +/-host.log (logs from lldb host process) +/-server.log (logs from debugserver/lldb-server) +/-.log (console logs) + +By default, logs from successful runs are deleted. Use the --log-success flag +to create reference logs for debugging. + +$ ./dotest.py --log-success + +Option 2: (DEPRECATED) + +The following options can only enable logs from the host lldb process. +Only categories from the "lldb" or "gdb-remote" channels can be enabled +They also do not automatically enable logs in locally running debug servers. +Also, logs from all test case are written into each log file + +o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem + with a default option of 'event process' if LLDB_LOG_OPTION is not defined. + +o GDB_REMOTE_LOG: if defined, specifies the log file pathname for the + 'process.gdb-remote' subsystem with a default option of 'packets' if + GDB_REMOTE_LOG_OPTION is not defined. + +""") + sys.exit(0) + +def parseOptionsAndInitTestdirs(): + """Initialize the list of directories containing our unittest scripts. + + '-h/--help as the first option prints out usage info and exit the program. + """ + + do_help = False + + platform_system = platform.system() + platform_machine = platform.machine() + + parser = dotest_args.create_parser() + args = dotest_args.parse_args(parser, sys.argv[1:]) + + if args.unset_env_varnames: + for env_var in args.unset_env_varnames: + if env_var in os.environ: + # From Python Doc: When unsetenv() is supported, deletion of items in os.environ + # is automatically translated into a corresponding call to unsetenv(). + del os.environ[env_var] + #os.unsetenv(env_var) + + if args.set_env_vars: + for env_var in args.set_env_vars: + parts = env_var.split('=', 1) + if len(parts) == 1: + os.environ[parts[0]] = "" + else: + os.environ[parts[0]] = parts[1] + + # only print the args if being verbose (and parsable is off) + if args.v and not args.q: + print(sys.argv) + + if args.h: + do_help = True + + if args.compilers: + configuration.compilers = args.compilers + else: + # Use a compiler appropriate appropriate for the Apple SDK if one was specified + if platform_system == 'Darwin' and args.apple_sdk: + configuration.compilers = [seven.get_command_output('xcrun -sdk "%s" -find clang 2> /dev/null' % (args.apple_sdk))] + else: + # 'clang' on ubuntu 14.04 is 3.4 so we try clang-3.5 first + candidateCompilers = ['clang-3.5', 'clang', 'gcc'] + for candidate in candidateCompilers: + if which(candidate): + configuration.compilers = [candidate] + break + + if args.channels: + lldbtest_config.channels = args.channels + + if args.log_success: + lldbtest_config.log_success = args.log_success + + # Set SDKROOT if we are using an Apple SDK + if platform_system == 'Darwin' and args.apple_sdk: + os.environ['SDKROOT'] = seven.get_command_output('xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % (args.apple_sdk)) + + if args.archs: + configuration.archs = args.archs + for arch in configuration.archs: + if arch.startswith('arm') and platform_system == 'Darwin' and not args.apple_sdk: + os.environ['SDKROOT'] = seven.get_command_output('xcrun --sdk iphoneos.internal --show-sdk-path 2> /dev/null') + if not os.path.exists(os.environ['SDKROOT']): + os.environ['SDKROOT'] = seven.get_command_output('xcrun --sdk iphoneos --show-sdk-path 2> /dev/null') + else: + configuration.archs = [platform_machine] + + if args.categoriesList: + configuration.categoriesList = set(test_categories.validate(args.categoriesList, False)) + configuration.useCategories = True + else: + configuration.categoriesList = [] + + if args.skipCategories: + configuration.skipCategories = test_categories.validate(args.skipCategories, False) + + if args.E: + cflags_extras = args.E + os.environ['CFLAGS_EXTRAS'] = cflags_extras + + if args.d: + sys.stdout.write("Suspending the process %d to wait for debugger to attach...\n" % os.getpid()) + sys.stdout.flush() + os.kill(os.getpid(), signal.SIGSTOP) + + if args.f: + if any([x.startswith('-') for x in args.f]): + usage(parser) + configuration.filters.extend(args.f) + # Shut off multiprocessing mode when additional filters are specified. + # The rational is that the user is probably going after a very specific + # test and doesn't need a bunch of parallel test runners all looking for + # it in a frenzy. Also, '-v' now spits out all test run output even + # on success, so the standard recipe for redoing a failing test (with -v + # and a -f to filter to the specific test) now causes all test scanning + # (in parallel) to print results for do-nothing runs in a very distracting + # manner. If we really need filtered parallel runs in the future, consider + # adding a --no-output-on-success that prevents -v from setting + # output-on-success. + configuration.no_multiprocess_test_runner = True + + if args.l: + configuration.skip_long_running_test = False + + if args.framework: + configuration.lldbFrameworkPath = args.framework + + if args.executable: + lldbtest_config.lldbExec = args.executable + + if args.p: + if args.p.startswith('-'): + usage(parser) + configuration.regexp = args.p + + if args.q: + configuration.parsable = True + + if args.s: + if args.s.startswith('-'): + usage(parser) + configuration.sdir_name = args.s + + if args.t: + os.environ['LLDB_COMMAND_TRACE'] = 'YES' + + if args.v: + configuration.verbose = 2 + + # argparse makes sure we have a number + if args.sharp: + configuration.count = args.sharp + + if sys.platform.startswith('win32'): + os.environ['LLDB_DISABLE_CRASH_DIALOG'] = str(args.disable_crash_dialog) + os.environ['LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE'] = str(True) + + if do_help == True: + usage(parser) + + if args.no_multiprocess: + configuration.no_multiprocess_test_runner = True + + if args.inferior: + configuration.is_inferior_test_runner = True + + if args.num_threads: + configuration.num_threads = args.num_threads + + if args.test_subdir: + configuration.multiprocess_test_subdir = args.test_subdir + + if args.test_runner_name: + configuration.test_runner_name = args.test_runner_name + + # Capture test results-related args. + if args.curses and not args.inferior: + # Act as if the following args were set. + args.results_formatter = "lldbsuite.test.curses_results.Curses" + args.results_file = "stdout" + + if args.results_file: + configuration.results_filename = args.results_file + + if args.results_port: + configuration.results_port = args.results_port + + if args.results_file and args.results_port: + sys.stderr.write( + "only one of --results-file and --results-port should " + "be specified\n") + usage(args) + + if args.results_formatter: + configuration.results_formatter_name = args.results_formatter + if args.results_formatter_options: + configuration.results_formatter_options = args.results_formatter_options + + # Default to using the BasicResultsFormatter if no formatter is specified + # and we're not a test inferior. + if not args.inferior and configuration.results_formatter_name is None: + configuration.results_formatter_name = ( + "lldbsuite.test.result_formatter.ResultsFormatter") + + # rerun-related arguments + configuration.rerun_all_issues = args.rerun_all_issues + configuration.rerun_max_file_threshold = args.rerun_max_file_threshold + + if args.lldb_platform_name: + configuration.lldb_platform_name = args.lldb_platform_name + if args.lldb_platform_url: + configuration.lldb_platform_url = args.lldb_platform_url + if args.lldb_platform_working_dir: + configuration.lldb_platform_working_dir = args.lldb_platform_working_dir + + if args.event_add_entries and len(args.event_add_entries) > 0: + entries = {} + # Parse out key=val pairs, separated by comma + for keyval in args.event_add_entries.split(","): + key_val_entry = keyval.split("=") + if len(key_val_entry) == 2: + (key, val) = key_val_entry + val_parts = val.split(':') + if len(val_parts) > 1: + (val, val_type) = val_parts + if val_type == 'int': + val = int(val) + entries[key] = val + # Tell the event builder to create all events with these + # key/val pairs in them. + if len(entries) > 0: + result_formatter.EventBuilder.add_entries_to_all_events(entries) + + # Gather all the dirs passed on the command line. + if len(args.args) > 0: + configuration.testdirs = list(map(lambda x: os.path.realpath(os.path.abspath(x)), args.args)) + # Shut off multiprocessing mode when test directories are specified. + configuration.no_multiprocess_test_runner = True + + #print("testdirs:", testdirs) + +def getXcodeOutputPaths(lldbRootDirectory): + result = [] + + # These are for xcode build directories. + xcode3_build_dir = ['build'] + xcode4_build_dir = ['build', 'lldb', 'Build', 'Products'] + + configurations = [['Debug'], ['DebugClang'], ['Release'], ['BuildAndIntegration']] + xcode_build_dirs = [xcode3_build_dir, xcode4_build_dir] + for configuration in configurations: + for xcode_build_dir in xcode_build_dirs: + outputPath = os.path.join(lldbRootDirectory, *(xcode_build_dir+configuration) ) + result.append(outputPath) + + return result + + +def createSocketToLocalPort(port): + def socket_closer(s): + """Close down an opened socket properly.""" + s.shutdown(socket.SHUT_RDWR) + s.close() + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("localhost", port)) + return (sock, lambda: socket_closer(sock)) + + +def setupTestResults(): + """Sets up test results-related objects based on arg settings.""" + # Setup the results formatter configuration. + formatter_config = result_formatter.FormatterConfig() + formatter_config.filename = configuration.results_filename + formatter_config.formatter_name = configuration.results_formatter_name + formatter_config.formatter_options = ( + configuration.results_formatter_options) + formatter_config.port = configuration.results_port + + # Create the results formatter. + formatter_spec = result_formatter.create_results_formatter( + formatter_config) + if formatter_spec is not None and formatter_spec.formatter is not None: + configuration.results_formatter_object = formatter_spec.formatter + + # Send an intialize message to the formatter. + initialize_event = EventBuilder.bare_event("initialize") + if isMultiprocessTestRunner(): + if (configuration.test_runner_name is not None and + configuration.test_runner_name == "serial"): + # Only one worker queue here. + worker_count = 1 + else: + # Workers will be the number of threads specified. + worker_count = configuration.num_threads + else: + worker_count = 1 + initialize_event["worker_count"] = worker_count + + formatter_spec.formatter.handle_event(initialize_event) + + # Make sure we clean up the formatter on shutdown. + if formatter_spec.cleanup_func is not None: + atexit.register(formatter_spec.cleanup_func) + + +def getOutputPaths(lldbRootDirectory): + """ + Returns typical build output paths for the lldb executable + + lldbDirectory - path to the root of the lldb svn/git repo + """ + result = [] + + if sys.platform == 'darwin': + result.extend(getXcodeOutputPaths(lldbRootDirectory)) + + # cmake builds? look for build or build/host folder next to llvm directory + # lldb is located in llvm/tools/lldb so we need to go up three levels + llvmParentDir = os.path.abspath(os.path.join(lldbRootDirectory, os.pardir, os.pardir, os.pardir)) + result.append(os.path.join(llvmParentDir, 'build', 'bin')) + result.append(os.path.join(llvmParentDir, 'build', 'host', 'bin')) + + # some cmake developers keep their build directory beside their lldb directory + lldbParentDir = os.path.abspath(os.path.join(lldbRootDirectory, os.pardir)) + result.append(os.path.join(lldbParentDir, 'build', 'bin')) + result.append(os.path.join(lldbParentDir, 'build', 'host', 'bin')) + + return result + +def setupSysPath(): + """ + Add LLDB.framework/Resources/Python to the search paths for modules. + As a side effect, we also discover the 'lldb' executable and export it here. + """ + + # Get the directory containing the current script. + if "DOTEST_PROFILE" in os.environ and "DOTEST_SCRIPT_DIR" in os.environ: + scriptPath = os.environ["DOTEST_SCRIPT_DIR"] + else: + scriptPath = os.path.dirname(os.path.realpath(__file__)) + if not scriptPath.endswith('test'): + print("This script expects to reside in lldb's test directory.") + sys.exit(-1) + + os.environ["LLDB_TEST"] = scriptPath + + # Set up the LLDB_SRC environment variable, so that the tests can locate + # the LLDB source code. + os.environ["LLDB_SRC"] = lldbsuite.lldb_root + + pluginPath = os.path.join(scriptPath, 'plugins') + toolsLLDBMIPath = os.path.join(scriptPath, 'tools', 'lldb-mi') + toolsLLDBServerPath = os.path.join(scriptPath, 'tools', 'lldb-server') + + # Insert script dir, plugin dir, lldb-mi dir and lldb-server dir to the sys.path. + sys.path.insert(0, pluginPath) + sys.path.insert(0, toolsLLDBMIPath) # Adding test/tools/lldb-mi to the path makes it easy + # to "import lldbmi_testcase" from the MI tests + sys.path.insert(0, toolsLLDBServerPath) # Adding test/tools/lldb-server to the path makes it easy + # to "import lldbgdbserverutils" from the lldb-server tests + + # This is the root of the lldb git/svn checkout + # When this changes over to a package instead of a standalone script, this + # will be `lldbsuite.lldb_root` + lldbRootDirectory = lldbsuite.lldb_root + + # Some of the tests can invoke the 'lldb' command directly. + # We'll try to locate the appropriate executable right here. + + # The lldb executable can be set from the command line + # if it's not set, we try to find it now + # first, we try the environment + if not lldbtest_config.lldbExec: + # First, you can define an environment variable LLDB_EXEC specifying the + # full pathname of the lldb executable. + if "LLDB_EXEC" in os.environ: + lldbtest_config.lldbExec = os.environ["LLDB_EXEC"] + + if not lldbtest_config.lldbExec: + outputPaths = getOutputPaths(lldbRootDirectory) + for outputPath in outputPaths: + candidatePath = os.path.join(outputPath, 'lldb') + if is_exe(candidatePath): + lldbtest_config.lldbExec = candidatePath + break + + if not lldbtest_config.lldbExec: + # Last, check the path + lldbtest_config.lldbExec = which('lldb') + + if lldbtest_config.lldbExec and not is_exe(lldbtest_config.lldbExec): + print("'{}' is not a path to a valid executable".format(lldbtest_config.lldbExec)) + lldbtest_config.lldbExec = None + + if not lldbtest_config.lldbExec: + print("The 'lldb' executable cannot be located. Some of the tests may not be run as a result.") + sys.exit(-1) + + lldbLibDir = os.path.dirname(lldbtest_config.lldbExec) # confusingly, this is the "bin" directory + os.environ["LLDB_LIB_DIR"] = lldbLibDir + lldbImpLibDir = os.path.join(lldbLibDir, '..', 'lib') if sys.platform.startswith('win32') else lldbLibDir + os.environ["LLDB_IMPLIB_DIR"] = lldbImpLibDir + print("LLDB library dir:", os.environ["LLDB_LIB_DIR"]) + print("LLDB import library dir:", os.environ["LLDB_IMPLIB_DIR"]) + os.system('%s -v' % lldbtest_config.lldbExec) + + # Assume lldb-mi is in same place as lldb + # If not found, disable the lldb-mi tests + lldbMiExec = None + if lldbtest_config.lldbExec and is_exe(lldbtest_config.lldbExec + "-mi"): + lldbMiExec = lldbtest_config.lldbExec + "-mi" + if not lldbMiExec: + if not configuration.shouldSkipBecauseOfCategories(["lldb-mi"]): + print("The 'lldb-mi' executable cannot be located. The lldb-mi tests can not be run as a result.") + configuration.skipCategories.append("lldb-mi") + else: + os.environ["LLDBMI_EXEC"] = lldbMiExec + + lldbPythonDir = None # The directory that contains 'lldb/__init__.py' + if configuration.lldbFrameworkPath: + candidatePath = os.path.join(configuration.lldbFrameworkPath, 'Resources', 'Python') + if os.path.isfile(os.path.join(candidatePath, 'lldb/__init__.py')): + lldbPythonDir = candidatePath + if not lldbPythonDir: + print('Resources/Python/lldb/__init__.py was not found in ' + configuration.lldbFrameworkPath) + sys.exit(-1) + else: + # If our lldb supports the -P option, use it to find the python path: + init_in_python_dir = os.path.join('lldb', '__init__.py') + + lldb_dash_p_result = subprocess.check_output([lldbtest_config.lldbExec, "-P"], stderr=subprocess.STDOUT, universal_newlines=True) + + if lldb_dash_p_result and not lldb_dash_p_result.startswith(("<", "lldb: invalid option:")) \ + and not lldb_dash_p_result.startswith("Traceback"): + lines = lldb_dash_p_result.splitlines() + + # Workaround for readline vs libedit issue on FreeBSD. If stdout + # is not a terminal Python executes + # rl_variable_bind ("enable-meta-key", "off"); + # This produces a warning with FreeBSD's libedit because the + # enable-meta-key variable is unknown. Not an issue on Apple + # because cpython commit f0ab6f9f0603 added a #ifndef __APPLE__ + # around the call. See http://bugs.python.org/issue19884 for more + # information. For now we just discard the warning output. + if len(lines) >= 1 and lines[0].startswith("bind: Invalid command"): + lines.pop(0) + + # Taking the last line because lldb outputs + # 'Cannot read termcap database;\nusing dumb terminal settings.\n' + # before the path + if len(lines) >= 1 and os.path.isfile(os.path.join(lines[-1], init_in_python_dir)): + lldbPythonDir = lines[-1] + if "freebsd" in sys.platform or "linux" in sys.platform: + os.environ['LLDB_LIB_DIR'] = os.path.join(lldbPythonDir, '..', '..') + + if not lldbPythonDir: + if platform.system() == "Darwin": + python_resource_dir = ['LLDB.framework', 'Resources', 'Python'] + outputPaths = getXcodeOutputPaths() + for outputPath in outputPaths: + candidatePath = os.path.join(outputPath, python_resource_dir) + if os.path.isfile(os.path.join(candidatePath, init_in_python_dir)): + lldbPythonDir = candidatePath + break + + if not lldbPythonDir: + print('This script requires lldb.py to be in either ' + dbgPath + ',', end=' ') + print(relPath + ', or ' + baiPath + '. Some tests might fail.') + else: + print("Unable to load lldb extension module. Possible reasons for this include:") + print(" 1) LLDB was built with LLDB_DISABLE_PYTHON=1") + print(" 2) PYTHONPATH and PYTHONHOME are not set correctly. PYTHONHOME should refer to") + print(" the version of Python that LLDB built and linked against, and PYTHONPATH") + print(" should contain the Lib directory for the same python distro, as well as the") + print(" location of LLDB\'s site-packages folder.") + print(" 3) A different version of Python than that which was built against is exported in") + print(" the system\'s PATH environment variable, causing conflicts.") + print(" 4) The executable '%s' could not be found. Please check " % lldbExecutable) + print(" that it exists and is executable.") + + if lldbPythonDir: + lldbPythonDir = os.path.normpath(lldbPythonDir) + # Some of the code that uses this path assumes it hasn't resolved the Versions... link. + # If the path we've constructed looks like that, then we'll strip out the Versions/A part. + (before, frameWithVersion, after) = lldbPythonDir.rpartition("LLDB.framework/Versions/A") + if frameWithVersion != "" : + lldbPythonDir = before + "LLDB.framework" + after + + lldbPythonDir = os.path.abspath(lldbPythonDir) + + # If tests need to find LLDB_FRAMEWORK, now they can do it + os.environ["LLDB_FRAMEWORK"] = os.path.dirname(os.path.dirname(lldbPythonDir)) + + # This is to locate the lldb.py module. Insert it right after sys.path[0]. + sys.path[1:1] = [lldbPythonDir] + +def visit(prefix, dir, names): + """Visitor function for os.path.walk(path, visit, arg).""" + + dir_components = set(dir.split(os.sep)) + excluded_components = set(['.svn', '.git']) + if dir_components.intersection(excluded_components): + #print("Detected an excluded dir component: %s" % dir) + return + + for name in names: + if '.py' == os.path.splitext(name)[1] and name.startswith(prefix): + + if name in configuration.all_tests: + raise Exception("Found multiple tests with the name %s" % name) + configuration.all_tests.add(name) + + # Try to match the regexp pattern, if specified. + if configuration.regexp: + import re + if re.search(configuration.regexp, name): + #print("Filename: '%s' matches pattern: '%s'" % (name, regexp)) + pass + else: + #print("Filename: '%s' does not match pattern: '%s'" % (name, regexp)) + continue + + # We found a match for our test. Add it to the suite. + + # Update the sys.path first. + if not sys.path.count(dir): + sys.path.insert(0, dir) + base = os.path.splitext(name)[0] + + # Thoroughly check the filterspec against the base module and admit + # the (base, filterspec) combination only when it makes sense. + filterspec = None + for filterspec in configuration.filters: + # Optimistically set the flag to True. + filtered = True + module = __import__(base) + parts = filterspec.split('.') + obj = module + for part in parts: + try: + parent, obj = obj, getattr(obj, part) + except AttributeError: + # The filterspec has failed. + filtered = False + break + + # If filtered, we have a good filterspec. Add it. + if filtered: + #print("adding filter spec %s to module %s" % (filterspec, module)) + configuration.suite.addTests( + unittest2.defaultTestLoader.loadTestsFromName(filterspec, module)) + continue + + # Forgo this module if the (base, filterspec) combo is invalid + if configuration.filters and not filtered: + continue + + # Add either the filtered test case(s) (which is done before) or the entire test class. + if not filterspec or not filtered: + # A simple case of just the module name. Also the failover case + # from the filterspec branch when the (base, filterspec) combo + # doesn't make sense. + configuration.suite.addTests(unittest2.defaultTestLoader.loadTestsFromName(base)) + + +def disabledynamics(): + import lldb + ci = lldb.DBG.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + ci.HandleCommand("setting set target.prefer-dynamic-value no-dynamic-values", res, False) + if not res.Succeeded(): + raise Exception('disabling dynamic type support failed') + +def lldbLoggings(): + import lldb + """Check and do lldb loggings if necessary.""" + + # Turn on logging for debugging purposes if ${LLDB_LOG} environment variable is + # defined. Use ${LLDB_LOG} to specify the log file. + ci = lldb.DBG.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + if ("LLDB_LOG" in os.environ): + open(os.environ["LLDB_LOG"], 'w').close() + if ("LLDB_LOG_OPTION" in os.environ): + lldb_log_option = os.environ["LLDB_LOG_OPTION"] + else: + lldb_log_option = "event process expr state api" + ci.HandleCommand( + "log enable -n -f " + os.environ["LLDB_LOG"] + " lldb " + lldb_log_option, + res) + if not res.Succeeded(): + raise Exception('log enable failed (check LLDB_LOG env variable)') + + if ("LLDB_LINUX_LOG" in os.environ): + open(os.environ["LLDB_LINUX_LOG"], 'w').close() + if ("LLDB_LINUX_LOG_OPTION" in os.environ): + lldb_log_option = os.environ["LLDB_LINUX_LOG_OPTION"] + else: + lldb_log_option = "event process expr state api" + ci.HandleCommand( + "log enable -n -f " + os.environ["LLDB_LINUX_LOG"] + " linux " + lldb_log_option, + res) + if not res.Succeeded(): + raise Exception('log enable failed (check LLDB_LINUX_LOG env variable)') + + # Ditto for gdb-remote logging if ${GDB_REMOTE_LOG} environment variable is defined. + # Use ${GDB_REMOTE_LOG} to specify the log file. + if ("GDB_REMOTE_LOG" in os.environ): + if ("GDB_REMOTE_LOG_OPTION" in os.environ): + gdb_remote_log_option = os.environ["GDB_REMOTE_LOG_OPTION"] + else: + gdb_remote_log_option = "packets process" + ci.HandleCommand( + "log enable -n -f " + os.environ["GDB_REMOTE_LOG"] + " gdb-remote " + + gdb_remote_log_option, + res) + if not res.Succeeded(): + raise Exception('log enable failed (check GDB_REMOTE_LOG env variable)') + +def getMyCommandLine(): + return ' '.join(sys.argv) + +# ======================================== # +# # +# Execution of the test driver starts here # +# # +# ======================================== # + +def checkDsymForUUIDIsNotOn(): + cmd = ["defaults", "read", "com.apple.DebugSymbols"] + pipe = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.STDOUT) + cmd_output = pipe.stdout.read() + if cmd_output and "DBGFileMappedPaths = " in cmd_output: + print("%s =>" % ' '.join(cmd)) + print(cmd_output) + print("Disable automatic lookup and caching of dSYMs before running the test suite!") + print("Exiting...") + sys.exit(0) + +def exitTestSuite(exitCode = None): + import lldb + lldb.SBDebugger.Terminate() + if exitCode: + sys.exit(exitCode) + + +def isMultiprocessTestRunner(): + # We're not multiprocess when we're either explicitly + # the inferior (as specified by the multiprocess test + # runner) OR we've been told to skip using the multiprocess + # test runner + return not (configuration.is_inferior_test_runner or configuration.no_multiprocess_test_runner) + +def getVersionForSDK(sdk): + sdk = str.lower(sdk) + full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk) + basename = os.path.basename(full_path) + basename = os.path.splitext(basename)[0] + basename = str.lower(basename) + ver = basename.replace(sdk, '') + return ver + +def getPathForSDK(sdk): + sdk = str.lower(sdk) + full_path = seven.get_command_output('xcrun -sdk %s --show-sdk-path' % sdk) + if os.path.exists(full_path): return full_path + return None + +def setDefaultTripleForPlatform(): + if configuration.lldb_platform_name == 'ios-simulator': + triple_str = 'x86_64-apple-ios%s' % (getVersionForSDK('iphonesimulator')) + os.environ['TRIPLE'] = triple_str + return {'TRIPLE':triple_str} + return {} + +def run_suite(): + # On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults + # does not exist before proceeding to running the test suite. + if sys.platform.startswith("darwin"): + checkDsymForUUIDIsNotOn() + + # + # Start the actions by first parsing the options while setting up the test + # directories, followed by setting up the search paths for lldb utilities; + # then, we walk the directory trees and collect the tests into our test suite. + # + parseOptionsAndInitTestdirs() + + # Setup test results (test results formatter and output handling). + setupTestResults() + + # If we are running as the multiprocess test runner, kick off the + # multiprocess test runner here. + if isMultiprocessTestRunner(): + from . import dosep + dosep.main(configuration.num_threads, configuration.multiprocess_test_subdir, + configuration.test_runner_name, configuration.results_formatter_object) + raise Exception("should never get here") + elif configuration.is_inferior_test_runner: + # Shut off Ctrl-C processing in inferiors. The parallel + # test runner handles this more holistically. + signal.signal(signal.SIGINT, signal.SIG_IGN) + + setupSysPath() + configuration.setupCrashInfoHook() + + # + # If '-l' is specified, do not skip the long running tests. + if not configuration.skip_long_running_test: + os.environ["LLDB_SKIP_LONG_RUNNING_TEST"] = "NO" + + # For the time being, let's bracket the test runner within the + # lldb.SBDebugger.Initialize()/Terminate() pair. + import lldb + + # Create a singleton SBDebugger in the lldb namespace. + lldb.DBG = lldb.SBDebugger.Create() + + if configuration.lldb_platform_name: + print("Setting up remote platform '%s'" % (configuration.lldb_platform_name)) + lldb.remote_platform = lldb.SBPlatform(configuration.lldb_platform_name) + if not lldb.remote_platform.IsValid(): + print("error: unable to create the LLDB platform named '%s'." % (configuration.lldb_platform_name)) + exitTestSuite(1) + if configuration.lldb_platform_url: + # We must connect to a remote platform if a LLDB platform URL was specified + print("Connecting to remote platform '%s' at '%s'..." % (configuration.lldb_platform_name, configuration.lldb_platform_url)) + platform_connect_options = lldb.SBPlatformConnectOptions(configuration.lldb_platform_url) + err = lldb.remote_platform.ConnectRemote(platform_connect_options) + if err.Success(): + print("Connected.") + else: + print("error: failed to connect to remote platform using URL '%s': %s" % (configuration.lldb_platform_url, err)) + exitTestSuite(1) + else: + configuration.lldb_platform_url = None + + platform_changes = setDefaultTripleForPlatform() + first = True + for key in platform_changes: + if first: + print("Environment variables setup for platform support:") + first = False + print("%s = %s" % (key,platform_changes[key])) + + if configuration.lldb_platform_working_dir: + print("Setting remote platform working directory to '%s'..." % (configuration.lldb_platform_working_dir)) + lldb.remote_platform.SetWorkingDirectory(configuration.lldb_platform_working_dir) + lldb.DBG.SetSelectedPlatform(lldb.remote_platform) + else: + lldb.remote_platform = None + configuration.lldb_platform_working_dir = None + configuration.lldb_platform_url = None + + target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] + + # Don't do debugserver tests on everything except OS X. + configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform + + # Don't do lldb-server (llgs) tests on anything except Linux. + configuration.dont_do_llgs_test = not ("linux" in target_platform) + + # + # Walk through the testdirs while collecting tests. + # + for testdir in configuration.testdirs: + for (dirpath, dirnames, filenames) in os.walk(testdir): + visit('Test', dirpath, filenames) + + # + # Now that we have loaded all the test cases, run the whole test suite. + # + + # Turn on lldb loggings if necessary. + lldbLoggings() + + # Disable default dynamic types for testing purposes + disabledynamics() + + # Install the control-c handler. + unittest2.signals.installHandler() + + # If sdir_name is not specified through the '-s sdir_name' option, get a + # timestamp string and export it as LLDB_SESSION_DIR environment var. This will + # be used when/if we want to dump the session info of individual test cases + # later on. + # + # See also TestBase.dumpSessionInfo() in lldbtest.py. + import datetime + # The windows platforms don't like ':' in the pathname. + timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S") + if not configuration.sdir_name: + configuration.sdir_name = timestamp_started + os.environ["LLDB_SESSION_DIRNAME"] = os.path.join(os.getcwd(), configuration.sdir_name) + + sys.stderr.write("\nSession logs for test failures/errors/unexpected successes" + " will go into directory '%s'\n" % configuration.sdir_name) + sys.stderr.write("Command invoked: %s\n" % getMyCommandLine()) + + if not os.path.isdir(configuration.sdir_name): + try: + os.mkdir(configuration.sdir_name) + except OSError as exception: + if exception.errno != errno.EEXIST: + raise + where_to_save_session = os.getcwd() + fname = os.path.join(configuration.sdir_name, "TestStarted-%d" % os.getpid()) + with open(fname, "w") as f: + print("Test started at: %s\n" % timestamp_started, file=f) + print(configuration.svn_info, file=f) + print("Command invoked: %s\n" % getMyCommandLine(), file=f) + + # + # Invoke the default TextTestRunner to run the test suite, possibly iterating + # over different configurations. + # + + iterArchs = False + iterCompilers = False + + if isinstance(configuration.archs, list) and len(configuration.archs) >= 1: + iterArchs = True + + # + # Add some intervention here to sanity check that the compilers requested are sane. + # If found not to be an executable program, the invalid one is dropped from the list. + for i in range(len(configuration.compilers)): + c = configuration.compilers[i] + if which(c): + continue + else: + if sys.platform.startswith("darwin"): + pipe = subprocess.Popen(['xcrun', '-find', c], stdout = subprocess.PIPE, stderr = subprocess.STDOUT) + cmd_output = pipe.stdout.read() + if cmd_output: + if "not found" in cmd_output: + print("dropping %s from the compilers used" % c) + configuration.compilers.remove(i) + else: + configuration.compilers[i] = cmd_output.split('\n')[0] + print("'xcrun -find %s' returning %s" % (c, configuration.compilers[i])) + + if not configuration.parsable: + print("compilers=%s" % str(configuration.compilers)) + + if not configuration.compilers or len(configuration.compilers) == 0: + print("No eligible compiler found, exiting.") + exitTestSuite(1) + + if isinstance(configuration.compilers, list) and len(configuration.compilers) >= 1: + iterCompilers = True + + # If we iterate on archs or compilers, there is a chance we want to split stderr/stdout. + if iterArchs or iterCompilers: + old_stderr = sys.stderr + old_stdout = sys.stdout + new_stderr = None + new_stdout = None + + # Iterating over all possible architecture and compiler combinations. + for ia in range(len(configuration.archs) if iterArchs else 1): + archConfig = "" + if iterArchs: + os.environ["ARCH"] = configuration.archs[ia] + archConfig = "arch=%s" % configuration.archs[ia] + for ic in range(len(configuration.compilers) if iterCompilers else 1): + if iterCompilers: + os.environ["CC"] = configuration.compilers[ic] + configString = "%s compiler=%s" % (archConfig, configuration.compilers[ic]) + else: + configString = archConfig + + if iterArchs or iterCompilers: + # Translate ' ' to '-' for pathname component. + if six.PY2: + import string + tbl = string.maketrans(' ', '-') + else: + tbl = str.maketrans(' ', '-') + configPostfix = configString.translate(tbl) + + # Output the configuration. + if not configuration.parsable: + sys.stderr.write("\nConfiguration: " + configString + "\n") + + #print("sys.stderr name is", sys.stderr.name) + #print("sys.stdout name is", sys.stdout.name) + + # First, write out the number of collected test cases. + if not configuration.parsable: + sys.stderr.write(configuration.separator + "\n") + sys.stderr.write("Collected %d test%s\n\n" + % (configuration.suite.countTestCases(), + configuration.suite.countTestCases() != 1 and "s" or "")) + + if configuration.parsable: + v = 0 + else: + v = configuration.verbose + + # Invoke the test runner. + if configuration.count == 1: + result = unittest2.TextTestRunner(stream=sys.stderr, + verbosity=v, + resultclass=test_result.LLDBTestResult).run(configuration.suite) + else: + # We are invoking the same test suite more than once. In this case, + # mark __ignore_singleton__ flag as True so the signleton pattern is + # not enforced. + test_result.LLDBTestResult.__ignore_singleton__ = True + for i in range(count): + + result = unittest2.TextTestRunner(stream=sys.stderr, + verbosity=v, + resultclass=test_result.LLDBTestResult).run(configuration.suite) + + configuration.failed = configuration.failed or not result.wasSuccessful() + + if configuration.sdir_has_content and not configuration.parsable: + sys.stderr.write("Session logs for test failures/errors/unexpected successes" + " can be found in directory '%s'\n" % configuration.sdir_name) + + if configuration.useCategories and len(configuration.failuresPerCategory) > 0: + sys.stderr.write("Failures per category:\n") + for category in configuration.failuresPerCategory: + sys.stderr.write("%s - %d\n" % (category, configuration.failuresPerCategory[category])) + + os.chdir(where_to_save_session) + fname = os.path.join(configuration.sdir_name, "TestFinished-%d" % os.getpid()) + with open(fname, "w") as f: + print("Test finished at: %s\n" % datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S"), file=f) + + # Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined. + # This should not be necessary now. + if ("LLDB_TESTSUITE_FORCE_FINISH" in os.environ): + print("Terminating Test suite...") + subprocess.Popen(["/bin/sh", "-c", "kill %s; exit 0" % (os.getpid())]) + + # Exiting. + exitTestSuite(configuration.failed) + +if __name__ == "__main__": + print(__file__ + " is for use as a module only. It should not be run as a standalone script.") + sys.exit(-1) diff --git a/packages/Python/lldbsuite/test/dotest_args.py b/packages/Python/lldbsuite/test/dotest_args.py new file mode 100644 index 000000000000..105156df7e84 --- /dev/null +++ b/packages/Python/lldbsuite/test/dotest_args.py @@ -0,0 +1,188 @@ +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import argparse +import sys +import multiprocessing +import os +import textwrap + +# Third-party modules + +# LLDB modules + +class ArgParseNamespace(object): + pass + +def parse_args(parser, argv): + """ Returns an argument object. LLDB_TEST_ARGUMENTS environment variable can + be used to pass additional arguments. + """ + args = ArgParseNamespace() + + if ('LLDB_TEST_ARGUMENTS' in os.environ): + print("Arguments passed through environment: '%s'" % os.environ['LLDB_TEST_ARGUMENTS']) + args = parser.parse_args([sys.argv[0]].__add__(os.environ['LLDB_TEST_ARGUMENTS'].split()),namespace=args) + + return parser.parse_args(args=argv, namespace=args) + + +def default_thread_count(): + # Check if specified in the environment + num_threads_str = os.environ.get("LLDB_TEST_THREADS") + if num_threads_str: + return int(num_threads_str) + else: + return multiprocessing.cpu_count() + + +def create_parser(): + parser = argparse.ArgumentParser(description='description', prefix_chars='+-', add_help=False) + group = None + + # Helper function for boolean options (group will point to the current group when executing X) + X = lambda optstr, helpstr, **kwargs: group.add_argument(optstr, help=helpstr, action='store_true', **kwargs) + + group = parser.add_argument_group('Help') + group.add_argument('-h', '--help', dest='h', action='store_true', help="Print this help message and exit. Add '-v' for more detailed help.") + + # C and Python toolchain options + group = parser.add_argument_group('Toolchain options') + group.add_argument('-A', '--arch', metavar='arch', action='append', dest='archs', help=textwrap.dedent('''Specify the architecture(s) to test. This option can be specified more than once''')) + group.add_argument('-C', '--compiler', metavar='compiler', dest='compilers', action='append', help=textwrap.dedent('''Specify the compiler(s) used to build the inferior executables. The compiler path can be an executable basename or a full path to a compiler executable. This option can be specified multiple times.''')) + if sys.platform == 'darwin': + group.add_argument('--apple-sdk', metavar='apple_sdk', dest='apple_sdk', help=textwrap.dedent('''Specify the name of the Apple SDK (macosx, macosx.internal, iphoneos, iphoneos.internal, or path to SDK) and use the appropriate tools from that SDK's toolchain.''')) + # FIXME? This won't work for different extra flags according to each arch. + group.add_argument('-E', metavar='extra-flags', help=textwrap.dedent('''Specify the extra flags to be passed to the toolchain when building the inferior programs to be debugged + suggestions: do not lump the "-A arch1 -A arch2" together such that the -E option applies to only one of the architectures''')) + + # Test filtering options + group = parser.add_argument_group('Test filtering options') + group.add_argument('-f', metavar='filterspec', action='append', help='Specify a filter, which consists of the test class name, a dot, followed by the test method, to only admit such test into the test suite') # FIXME: Example? + X('-l', "Don't skip long running tests") + group.add_argument('-p', metavar='pattern', help='Specify a regexp filename pattern for inclusion in the test suite') + group.add_argument('-G', '--category', metavar='category', action='append', dest='categoriesList', help=textwrap.dedent('''Specify categories of test cases of interest. Can be specified more than once.''')) + group.add_argument('--skip-category', metavar='category', action='append', dest='skipCategories', help=textwrap.dedent('''Specify categories of test cases to skip. Takes precedence over -G. Can be specified more than once.''')) + + # Configuration options + group = parser.add_argument_group('Configuration options') + group.add_argument('--framework', metavar='framework-path', help='The path to LLDB.framework') + group.add_argument('--executable', metavar='executable-path', help='The path to the lldb executable') + group.add_argument('-s', metavar='name', help='Specify the name of the dir created to store the session files of tests with errored or failed status. If not specified, the test driver uses the timestamp as the session dir name') + group.add_argument('-y', type=int, metavar='count', help="Specify the iteration count used to collect our benchmarks. An example is the number of times to do 'thread step-over' to measure stepping speed.") + group.add_argument('-#', type=int, metavar='sharp', dest='sharp', help='Repeat the test suite for a specified number of times') + group.add_argument('--channel', metavar='channel', dest='channels', action='append', help=textwrap.dedent("Specify the log channels (and optional categories) e.g. 'lldb all' or 'gdb-remote packets' if no categories are specified, 'default' is used")) + group.add_argument('--log-success', dest='log_success', action='store_true', help="Leave logs/traces even for successful test runs (useful for creating reference log files during debugging.)") + + # Configuration options + group = parser.add_argument_group('Remote platform options') + group.add_argument('--platform-name', dest='lldb_platform_name', metavar='platform-name', help='The name of a remote platform to use') + group.add_argument('--platform-url', dest='lldb_platform_url', metavar='platform-url', help='A LLDB platform URL to use when connecting to a remote platform to run the test suite') + group.add_argument('--platform-working-dir', dest='lldb_platform_working_dir', metavar='platform-working-dir', help='The directory to use on the remote platform.') + + # Test-suite behaviour + group = parser.add_argument_group('Runtime behaviour options') + X('-d', 'Suspend the process after launch to wait indefinitely for a debugger to attach') + X('-q', "Don't print extra output from this script.") + X('-t', 'Turn on tracing of lldb command and other detailed test executions') + group.add_argument('-u', dest='unset_env_varnames', metavar='variable', action='append', help='Specify an environment variable to unset before running the test cases. e.g., -u DYLD_INSERT_LIBRARIES -u MallocScribble') + group.add_argument('--env', dest='set_env_vars', metavar='variable', action='append', help='Specify an environment variable to set to the given value before running the test cases e.g.: --env CXXFLAGS=-O3 --env DYLD_INSERT_LIBRARIES') + X('-v', 'Do verbose mode of unittest framework (print out each test case invocation)') + group.add_argument('--enable-crash-dialog', dest='disable_crash_dialog', action='store_false', help='(Windows only) When LLDB crashes, display the Windows crash dialog.') + group.set_defaults(disable_crash_dialog=True) + + group = parser.add_argument_group('Parallel execution options') + group.add_argument( + '--inferior', + action='store_true', + help=('specify this invocation is a multiprocess inferior, ' + 'used internally')) + group.add_argument( + '--no-multiprocess', + action='store_true', + help='skip running the multiprocess test runner') + group.add_argument( + '--threads', + type=int, + dest='num_threads', + default=default_thread_count(), + help=('The number of threads/processes to use when running tests ' + 'separately, defaults to the number of CPU cores available')) + group.add_argument( + '--test-subdir', + action='store', + help='Specify a test subdirectory to use relative to the test root dir' + ) + group.add_argument( + '--test-runner-name', + action='store', + help=('Specify a test runner strategy. Valid values: multiprocessing,' + ' multiprocessing-pool, serial, threading, threading-pool') + ) + + # Test results support. + group = parser.add_argument_group('Test results options') + group.add_argument( + '--curses', + action='store_true', + help='Shortcut for specifying test results using the curses formatter') + group.add_argument( + '--results-file', + action='store', + help=('Specifies the file where test results will be written ' + 'according to the results-formatter class used')) + group.add_argument( + '--results-port', + action='store', + type=int, + help=('Specifies the localhost port to which the results ' + 'formatted output should be sent')) + group.add_argument( + '--results-formatter', + action='store', + help=('Specifies the full package/module/class name used to translate ' + 'test events into some kind of meaningful report, written to ' + 'the designated output results file-like object')) + group.add_argument( + '--results-formatter-option', + '-O', + action='append', + dest='results_formatter_options', + help=('Specify an option to pass to the formatter. ' + 'Use --results-formatter-option="--option1=val1" ' + 'syntax. Note the "=" is critical, don\'t include whitespace.')) + group.add_argument( + '--event-add-entries', + action='store', + help=('Specify comma-separated KEY=VAL entries to add key and value ' + 'pairs to all test events generated by this test run. VAL may ' + 'be specified as VAL:TYPE, where TYPE may be int to convert ' + 'the value to an int')) + + # Re-run related arguments + group = parser.add_argument_group('Test Re-run Options') + group.add_argument( + '--rerun-all-issues', + action='store_true', + help=('Re-run all issues that occurred during the test run ' + 'irrespective of the test method\'s marking as flakey. ' + 'Default behavior is to apply re-runs only to flakey ' + 'tests that generate issues.')) + group.add_argument( + '--rerun-max-file-threshold', + action='store', + type=int, + default=50, + help=('Maximum number of files requiring a rerun beyond ' + 'which the rerun will not occur. This is meant to ' + 'stop a catastrophically failing test suite from forcing ' + 'all tests to be rerun in the single-worker phase.')) + + # Remove the reference to our helper function + del X + + group = parser.add_argument_group('Test directories') + group.add_argument('args', metavar='test-dir', nargs='*', help='Specify a list of directory names to search for test modules named after Test*.py (test discovery). If empty, search from the current working directory instead.') + + return parser diff --git a/packages/Python/lldbsuite/test/dotest_channels.py b/packages/Python/lldbsuite/test/dotest_channels.py new file mode 100644 index 000000000000..d2faf10c8929 --- /dev/null +++ b/packages/Python/lldbsuite/test/dotest_channels.py @@ -0,0 +1,200 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Sync lldb and related source from a local machine to a remote machine. + +This facilitates working on the lldb sourcecode on multiple machines +and multiple OS types, verifying changes across all. + + +This module provides asyncore channels used within the LLDB test +framework. +""" + +from __future__ import print_function +from __future__ import absolute_import + + +# System modules +import asyncore +import socket + +# Third-party modules +from six.moves import cPickle + +# LLDB modules + + +class UnpicklingForwardingReaderChannel(asyncore.dispatcher): + """Provides an unpickling, forwarding asyncore dispatch channel reader. + + Inferior dotest.py processes with side-channel-based test results will + send test result event data in a pickled format, one event at a time. + This class supports reconstructing the pickled data and forwarding it + on to its final destination. + + The channel data is written in the form: + {num_payload_bytes}#{payload_bytes} + + The bulk of this class is devoted to reading and parsing out + the payload bytes. + """ + def __init__(self, file_object, async_map, forwarding_func): + asyncore.dispatcher.__init__(self, sock=file_object, map=async_map) + + self.header_contents = b"" + self.packet_bytes_remaining = 0 + self.reading_header = True + self.ibuffer = b'' + self.forwarding_func = forwarding_func + if forwarding_func is None: + # This whole class is useless if we do nothing with the + # unpickled results. + raise Exception("forwarding function must be set") + + def deserialize_payload(self): + """Unpickles the collected input buffer bytes and forwards.""" + if len(self.ibuffer) > 0: + self.forwarding_func(cPickle.loads(self.ibuffer)) + self.ibuffer = b'' + + def consume_header_bytes(self, data): + """Consumes header bytes from the front of data. + @param data the incoming data stream bytes + @return any data leftover after consuming header bytes. + """ + # We're done if there is no content. + if not data or (len(data) == 0): + return None + + full_header_len = 4 + + assert len(self.header_contents) < full_header_len + + bytes_avail = len(data) + bytes_needed = full_header_len - len(self.header_contents) + header_bytes_avail = min(bytes_needed, bytes_avail) + self.header_contents += data[:header_bytes_avail] + if len(self.header_contents) == full_header_len: + import struct + # End of header. + self.packet_bytes_remaining = struct.unpack( + "!I", self.header_contents)[0] + self.header_contents = b"" + self.reading_header = False + return data[header_bytes_avail:] + + # If we made it here, we've exhausted the data and + # we're still parsing header content. + return None + + def consume_payload_bytes(self, data): + """Consumes payload bytes from the front of data. + @param data the incoming data stream bytes + @return any data leftover after consuming remaining payload bytes. + """ + if not data or (len(data) == 0): + # We're done and there's nothing to do. + return None + + data_len = len(data) + if data_len <= self.packet_bytes_remaining: + # We're consuming all the data provided. + self.ibuffer += data + self.packet_bytes_remaining -= data_len + + # If we're no longer waiting for payload bytes, + # we flip back to parsing header bytes and we + # unpickle the payload contents. + if self.packet_bytes_remaining < 1: + self.reading_header = True + self.deserialize_payload() + + # We're done, no more data left. + return None + else: + # We're only consuming a portion of the data since + # the data contains more than the payload amount. + self.ibuffer += data[:self.packet_bytes_remaining] + data = data[self.packet_bytes_remaining:] + + # We now move on to reading the header. + self.reading_header = True + self.packet_bytes_remaining = 0 + + # And we can deserialize the payload. + self.deserialize_payload() + + # Return the remaining data. + return data + + def handle_read(self): + # Read some data from the socket. + try: + data = self.recv(8192) + # print('driver socket READ: %d bytes' % len(data)) + except socket.error as socket_error: + print( + "\nINFO: received socket error when reading data " + "from test inferior:\n{}".format(socket_error)) + raise + except Exception as general_exception: + print( + "\nERROR: received non-socket error when reading data " + "from the test inferior:\n{}".format(general_exception)) + raise + + # Consume the message content. + while data and (len(data) > 0): + # If we're reading the header, gather header bytes. + if self.reading_header: + data = self.consume_header_bytes(data) + else: + data = self.consume_payload_bytes(data) + + def handle_close(self): + # print("socket reader: closing port") + self.close() + + +class UnpicklingForwardingListenerChannel(asyncore.dispatcher): + """Provides a socket listener asyncore channel for unpickling/forwarding. + + This channel will listen on a socket port (use 0 for host-selected). Any + client that connects will have an UnpicklingForwardingReaderChannel handle + communication over the connection. + + The dotest parallel test runners, when collecting test results, open the + test results side channel over a socket. This channel handles connections + from inferiors back to the test runner. Each worker fires up a listener + for each inferior invocation. This simplifies the asyncore.loop() usage, + one of the reasons for implementing with asyncore. This listener shuts + down once a single connection is made to it. + """ + def __init__(self, async_map, host, port, backlog_count, forwarding_func): + asyncore.dispatcher.__init__(self, map=async_map) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind((host, port)) + self.address = self.socket.getsockname() + self.listen(backlog_count) + self.handler = None + self.async_map = async_map + self.forwarding_func = forwarding_func + if forwarding_func is None: + # This whole class is useless if we do nothing with the + # unpickled results. + raise Exception("forwarding function must be set") + + def handle_accept(self): + (sock, addr) = self.socket.accept() + if sock and addr: + # print('Incoming connection from %s' % repr(addr)) + self.handler = UnpicklingForwardingReaderChannel( + sock, self.async_map, self.forwarding_func) + + def handle_close(self): + self.close() diff --git a/packages/Python/lldbsuite/test/driver/batch_mode/Makefile b/packages/Python/lldbsuite/test/driver/batch_mode/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/driver/batch_mode/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/driver/batch_mode/TestBatchMode.py b/packages/Python/lldbsuite/test/driver/batch_mode/TestBatchMode.py new file mode 100644 index 000000000000..b5ff88c3bba1 --- /dev/null +++ b/packages/Python/lldbsuite/test/driver/batch_mode/TestBatchMode.py @@ -0,0 +1,89 @@ +""" +Test that the lldb driver's batch mode works correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class DriverBatchModeTest (TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfRemote # test not remote-ready llvm.org/pr24813 + @expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot") + @expectedFlakeyLinux("llvm.org/pr25172") + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_driver_batch_mode(self): + """Test that the lldb driver's batch mode works correctly.""" + self.build() + self.setTearDownCleanup() + self.batch_mode() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + + def expect_string (self, string): + import pexpect + """This expects for "string", with timeout & EOF being test fails.""" + try: + self.child.expect_exact(string) + except pexpect.EOF: + self.fail ("Got EOF waiting for '%s'"%(string)) + except pexpect.TIMEOUT: + self.fail ("Timed out waiting for '%s'"%(string)) + + def batch_mode (self): + import pexpect + exe = os.path.join(os.getcwd(), "a.out") + prompt = "(lldb) " + + # First time through, pass CRASH so the process will crash and stop in batch mode. + run_commands = ' -b -o "break set -n main" -o "run" -o "continue" -k "frame var touch_me_not"' + self.child = pexpect.spawn('%s %s %s %s -- CRASH' % (lldbtest_config.lldbExec, self.lldbOption, run_commands, exe)) + child = self.child + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + # We should see the "run": + self.expect_string ("run") + # We should have hit the breakpoint & continued: + self.expect_string ("continue") + # The App should have crashed: + self.expect_string("About to crash") + # The -k option should have printed the frame variable once: + self.expect_string ('(char *) touch_me_not') + # Then we should have a live prompt: + self.expect_string (prompt) + self.child.sendline("frame variable touch_me_not") + self.expect_string ('(char *) touch_me_not') + + self.deletePexpectChild() + + # Now do it again, and see make sure if we don't crash, we quit: + run_commands = ' -b -o "break set -n main" -o "run" -o "continue" ' + self.child = pexpect.spawn('%s %s %s %s -- NOCRASH' % (lldbtest_config.lldbExec, self.lldbOption, run_commands, exe)) + child = self.child + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + # We should see the "run": + self.expect_string ("run") + # We should have hit the breakpoint & continued: + self.expect_string ("continue") + # The App should have not have crashed: + self.expect_string("Got there on time and it did not crash.") + # Then we should have a live prompt: + self.expect_string ("exited") + index = self.child.expect([pexpect.EOF, pexpect.TIMEOUT]) + self.assertTrue(index == 0, "lldb didn't close on successful batch completion.") + diff --git a/packages/Python/lldbsuite/test/driver/batch_mode/main.c b/packages/Python/lldbsuite/test/driver/batch_mode/main.c new file mode 100644 index 000000000000..418160eaa36d --- /dev/null +++ b/packages/Python/lldbsuite/test/driver/batch_mode/main.c @@ -0,0 +1,15 @@ +#include +#include + +int +main (int argc, char **argv) +{ + if (argc >= 2 && strcmp (argv[1], "CRASH") == 0) + { + char *touch_me_not = (char *) 0; + printf ("About to crash.\n"); + touch_me_not[0] = 'a'; + } + printf ("Got there on time and it did not crash.\n"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/example/TestSequenceFunctions.py b/packages/Python/lldbsuite/test/example/TestSequenceFunctions.py new file mode 100644 index 000000000000..a08529300646 --- /dev/null +++ b/packages/Python/lldbsuite/test/example/TestSequenceFunctions.py @@ -0,0 +1,36 @@ +"""An example unittest copied from python tutorial.""" + +import random +import unittest +import traceback + +class SequenceFunctionsTestCase(unittest.TestCase): + + def setUp(self): + #traceback.print_stack() + self.seq = list(range(10)) + + def tearDown(self): + #traceback.print_stack() + pass + + def test_shuffle(self): + # make sure the shuffled sequence does not lose any elements + random.shuffle(self.seq) + self.seq.sort() + self.assertEqual(self.seq, list(range(10))) + + def test_choice(self): + element = random.choice(self.seq) + self.assertTrue(element in self.seq) + + def test_sample(self): + self.assertRaises(ValueError, random.sample, self.seq, 20) + for element in random.sample(self.seq, 5): + self.assertTrue(element in self.seq) + + def getCategories(self): + return [] + +if __name__ == '__main__': + unittest.main() diff --git a/packages/Python/lldbsuite/test/expression_command/.categories b/packages/Python/lldbsuite/test/expression_command/.categories new file mode 100644 index 000000000000..897e40a99dda --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/.categories @@ -0,0 +1 @@ +expression diff --git a/packages/Python/lldbsuite/test/expression_command/call-function/Makefile b/packages/Python/lldbsuite/test/expression_command/call-function/Makefile new file mode 100644 index 000000000000..d4b82b3db276 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-function/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules + +clean:: + rm -rf $(wildcard *.o *.d *.dSYM) diff --git a/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStdStringFunction.py b/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStdStringFunction.py new file mode 100644 index 000000000000..3756b4a8cea4 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStdStringFunction.py @@ -0,0 +1,43 @@ +""" +Test calling std::String member functions. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprCommandCallFunctionTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.c. + self.line = line_number('main.cpp', + '// Please test these expressions while stopped at this line:') + + @expectedFailureIcc # llvm.org/pr14437, fails with ICC 13.1 + @expectedFailureFreeBSD('llvm.org/pr17807') # Fails on FreeBSD buildbot + @expectedFailureWindows("llvm.org/pr21765") + def test_with(self): + """Test calling std::String member function.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + # Some versions of GCC encode two locations for the 'return' statement in main.cpp + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("print str", + substrs = ['Hello world']) + + # Calling this function now succeeds, but we follow the typedef return type through to + # const char *, and thus don't invoke the Summary formatter. + self.expect("print str.c_str()", + substrs = ['Hello world']) diff --git a/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStopAndContinue.py b/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStopAndContinue.py new file mode 100644 index 000000000000..f6d63885248a --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStopAndContinue.py @@ -0,0 +1,46 @@ +""" +Test calling a function, stopping in the call, continue and gather the result on stop. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprCommandCallStopContinueTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.c. + self.line = line_number('main.cpp', + '// Please test these expressions while stopped at this line:') + self.func_line = line_number ('main.cpp', + '{ 5, "five" }') + + @expectedFlakeyDarwin("llvm.org/pr20274") + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test(self): + """Test gathering result from interrupted function call.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + # Some versions of GCC encode two locations for the 'return' statement in main.cpp + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.func_line, num_expected_locations=-1, loc_exact=True) + + self.expect("expr -i false -- returnsFive()", error=True, + substrs = ['Execution was interrupted, reason: breakpoint']) + + self.runCmd("continue", "Continue completed") + self.expect ("thread list", + substrs = ['stop reason = User Expression thread plan', + r'Completed expression: (Five) $0 = (number = 5, name = "five")']) diff --git a/packages/Python/lldbsuite/test/expression_command/call-function/TestCallUserDefinedFunction.py b/packages/Python/lldbsuite/test/expression_command/call-function/TestCallUserDefinedFunction.py new file mode 100644 index 000000000000..9138af0b0b47 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-function/TestCallUserDefinedFunction.py @@ -0,0 +1,52 @@ +""" +Test calling user defined functions using expression evaluation. + +Note: + LLDBs current first choice of evaluating functions is using the IR interpreter, + which is only supported on Hexagon. Otherwise JIT is used for the evaluation. + +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprCommandCallUserDefinedFunction(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.c. + self.line = line_number('main.cpp', + '// Please test these expressions while stopped at this line:') + @expectedFlakeyDsym("llvm.org/pr20274") + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test(self): + """Test return values of user defined function calls.""" + self.build() + + # Set breakpoint in main and run exe + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Test recursive function call. + self.expect("expr fib(5)", substrs = ['$0 = 5']) + + # Test function with more than one paramter + self.expect("expr add(4,8)", substrs = ['$1 = 12']) + + # Test nesting function calls in function paramters + self.expect("expr add(add(5,2),add(3,4))", substrs = ['$2 = 14']) + self.expect("expr add(add(5,2),fib(5))", substrs = ['$3 = 12']) + + # Test function with pointer paramter + self.expect("exp stringCompare((const char*) \"Hello world\")", substrs = ['$4 = true']) + self.expect("exp stringCompare((const char*) \"Hellworld\")", substrs = ['$5 = false']) diff --git a/packages/Python/lldbsuite/test/expression_command/call-function/main.cpp b/packages/Python/lldbsuite/test/expression_command/call-function/main.cpp new file mode 100644 index 000000000000..9b494c712bcc --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-function/main.cpp @@ -0,0 +1,53 @@ +#include +#include +#include + +struct Five +{ + int number; + const char *name; +}; + +Five +returnsFive() +{ + Five my_five = { 5, "five" }; + return my_five; +} + +unsigned int +fib(unsigned int n) +{ + if (n < 2) + return n; + else + return fib(n - 1) + fib(n - 2); +} + +int +add(int a, int b) +{ + return a + b; +} + +bool +stringCompare(const char *str) +{ + if (strcmp( str, "Hello world" ) == 0) + return true; + else + return false; +} + +int main (int argc, char const *argv[]) +{ + std::string str = "Hello world"; + std::cout << str << std::endl; + std::cout << str.c_str() << std::endl; + Five main_five = returnsFive(); +#if 0 + print str + print str.c_str() +#endif + return 0; // Please test these expressions while stopped at this line: +} diff --git a/packages/Python/lldbsuite/test/expression_command/call-restarts/Makefile b/packages/Python/lldbsuite/test/expression_command/call-restarts/Makefile new file mode 100644 index 000000000000..1c93ae1de439 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-restarts/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := lotta-signals.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py b/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py new file mode 100644 index 000000000000..abf48742567d --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py @@ -0,0 +1,139 @@ +""" +Test calling a function that hits a signal set to auto-restart, make sure the call completes. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprCommandThatRestartsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + self.main_source = "lotta-signals.c" + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + @skipIfFreeBSD # llvm.org/pr19246: intermittent failure + @skipIfDarwin # llvm.org/pr19246: intermittent failure + @skipIfWindows # Test relies on signals, unsupported on Windows + def test(self): + """Test calling function that hits a signal and restarts.""" + self.build() + self.call_function() + + def check_after_call (self, num_sigchld): + after_call = self.sigchld_no.GetValueAsSigned(-1) + self.assertTrue (after_call - self.start_sigchld_no == num_sigchld, "Really got %d SIGCHLD signals through the call."%(num_sigchld)) + self.start_sigchld_no = after_call + + # Check that we are back where we were before: + frame = self.thread.GetFrameAtIndex(0) + self.assertTrue (self.orig_frame_pc == frame.GetPC(), "Restored the zeroth frame correctly") + + def call_function(self): + exe_name = "a.out" + exe = os.path.join(os.getcwd(), exe_name) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + empty = lldb.SBFileSpec() + breakpoint = target.BreakpointCreateBySourceRegex('Stop here in main.',self.main_source_spec) + self.assertTrue(breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be at our breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + + self.assertTrue(len(threads) == 1) + self.thread = threads[0] + + # Make sure the SIGCHLD behavior is pass/no-stop/no-notify: + return_obj = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand("process handle SIGCHLD -s 0 -p 1 -n 0", return_obj) + self.assertTrue (return_obj.Succeeded() == True, "Set SIGCHLD to pass, no-stop") + + # The sigchld_no variable should be 0 at this point. + self.sigchld_no = target.FindFirstGlobalVariable("sigchld_no") + self.assertTrue (self.sigchld_no.IsValid(), "Got a value for sigchld_no") + + self.start_sigchld_no = self.sigchld_no.GetValueAsSigned (-1) + self.assertTrue (self.start_sigchld_no != -1, "Got an actual value for sigchld_no") + + options = lldb.SBExpressionOptions() + # processing 30 signals takes a while, increase the expression timeout a bit + options.SetTimeoutInMicroSeconds(3000000) # 3s + options.SetUnwindOnError(True) + + frame = self.thread.GetFrameAtIndex(0) + # Store away the PC to check that the functions unwind to the right place after calls + self.orig_frame_pc = frame.GetPC() + + num_sigchld = 30 + value = frame.EvaluateExpression ("call_me (%d)"%(num_sigchld), options) + self.assertTrue (value.IsValid()) + self.assertTrue (value.GetError().Success() == True) + self.assertTrue (value.GetValueAsSigned(-1) == num_sigchld) + + self.check_after_call(num_sigchld) + + # Okay, now try with a breakpoint in the called code in the case where + # we are ignoring breakpoint hits. + handler_bkpt = target.BreakpointCreateBySourceRegex("Got sigchld %d.", self.main_source_spec) + self.assertTrue (handler_bkpt.GetNumLocations() > 0) + options.SetIgnoreBreakpoints(True) + options.SetUnwindOnError(True) + + value = frame.EvaluateExpression("call_me (%d)"%(num_sigchld), options) + + self.assertTrue (value.IsValid() and value.GetError().Success() == True) + self.assertTrue (value.GetValueAsSigned(-1) == num_sigchld) + self.check_after_call(num_sigchld) + + # Now set the signal to print but not stop and make sure that calling still works: + self.dbg.GetCommandInterpreter().HandleCommand("process handle SIGCHLD -s 0 -p 1 -n 1", return_obj) + self.assertTrue (return_obj.Succeeded() == True, "Set SIGCHLD to pass, no-stop, notify") + + value = frame.EvaluateExpression("call_me (%d)"%(num_sigchld), options) + + self.assertTrue (value.IsValid() and value.GetError().Success() == True) + self.assertTrue (value.GetValueAsSigned(-1) == num_sigchld) + self.check_after_call(num_sigchld) + + # Now set this unwind on error to false, and make sure that we still complete the call: + options.SetUnwindOnError(False) + value = frame.EvaluateExpression("call_me (%d)"%(num_sigchld), options) + + self.assertTrue (value.IsValid() and value.GetError().Success() == True) + self.assertTrue (value.GetValueAsSigned(-1) == num_sigchld) + self.check_after_call(num_sigchld) + + # Okay, now set UnwindOnError to true, and then make the signal behavior to stop + # and see that now we do stop at the signal point: + + self.dbg.GetCommandInterpreter().HandleCommand("process handle SIGCHLD -s 1 -p 1 -n 1", return_obj) + self.assertTrue (return_obj.Succeeded() == True, "Set SIGCHLD to pass, stop, notify") + + value = frame.EvaluateExpression("call_me (%d)"%(num_sigchld), options) + self.assertTrue (value.IsValid() and value.GetError().Success() == False) + + # Set signal handling back to no-stop, and continue and we should end up back in out starting frame: + self.dbg.GetCommandInterpreter().HandleCommand("process handle SIGCHLD -s 0 -p 1 -n 1", return_obj) + self.assertTrue (return_obj.Succeeded() == True, "Set SIGCHLD to pass, no-stop, notify") + + error = process.Continue() + self.assertTrue (error.Success(), "Continuing after stopping for signal succeeds.") + + frame = self.thread.GetFrameAtIndex(0) + self.assertTrue (frame.GetPC() == self.orig_frame_pc, "Continuing returned to the place we started.") diff --git a/packages/Python/lldbsuite/test/expression_command/call-restarts/lotta-signals.c b/packages/Python/lldbsuite/test/expression_command/call-restarts/lotta-signals.c new file mode 100644 index 000000000000..f5c15b41e2de --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-restarts/lotta-signals.c @@ -0,0 +1,61 @@ +#include +#include +#include + +static int sigchld_no; +static int nosig_no; +static int weird_value; + +void +sigchld_handler (int signo) +{ + sigchld_no++; + printf ("Got sigchld %d.\n", sigchld_no); +} + +int +call_me (int some_value) +{ + int ret_val = 0; + int i; + for (i = 0; i < some_value; i++) + { + int result = 0; + if (i%2 == 0) + result = kill (getpid(), SIGCHLD); + else + sigchld_no++; + + usleep(1000); + if (result == 0) + ret_val++; + } + usleep (10000); + return ret_val; +} + +int +call_me_nosig (int some_value) +{ + int ret_val = 0; + int i; + for (i = 0; i < some_value; i++) + weird_value += i % 4; + + nosig_no += some_value; + return some_value; +} + +int +main () +{ + int ret_val; + signal (SIGCHLD, sigchld_handler); + + ret_val = call_me (2); // Stop here in main. + + ret_val = call_me_nosig (10); + + return 0; + +} diff --git a/packages/Python/lldbsuite/test/expression_command/call-throws/Makefile b/packages/Python/lldbsuite/test/expression_command/call-throws/Makefile new file mode 100644 index 000000000000..ac07b31c48cc --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-throws/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +OBJC_SOURCES := call-throws.m + +include $(LEVEL)/Makefile.rules +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/expression_command/call-throws/TestCallThatThrows.py b/packages/Python/lldbsuite/test/expression_command/call-throws/TestCallThatThrows.py new file mode 100644 index 000000000000..0e766ac2953a --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-throws/TestCallThatThrows.py @@ -0,0 +1,112 @@ +""" +Test calling a function that throws an ObjC exception, make sure that it doesn't propagate the exception. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprCommandWithThrowTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + self.main_source = "call-throws.m" + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + @skipUnlessDarwin + def test(self): + """Test calling a function that throws and ObjC exception.""" + self.build() + self.call_function() + + def check_after_call (self): + # Check that we are back where we were before: + frame = self.thread.GetFrameAtIndex(0) + self.assertTrue (self.orig_frame_pc == frame.GetPC(), "Restored the zeroth frame correctly") + + + def call_function(self): + """Test calling function that throws.""" + exe_name = "a.out" + exe = os.path.join(os.getcwd(), exe_name) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateBySourceRegex('I am about to throw.',self.main_source_spec) + self.assertTrue(breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be at our breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + + self.assertTrue(len(threads) == 1) + self.thread = threads[0] + + options = lldb.SBExpressionOptions() + options.SetUnwindOnError(True) + + frame = self.thread.GetFrameAtIndex(0) + # Store away the PC to check that the functions unwind to the right place after calls + self.orig_frame_pc = frame.GetPC() + + value = frame.EvaluateExpression ("[my_class callMeIThrow]", options) + self.assertTrue (value.IsValid()) + self.assertTrue (value.GetError().Success() == False) + + self.check_after_call() + + # Okay, now try with a breakpoint in the called code in the case where + # we are ignoring breakpoint hits. + handler_bkpt = target.BreakpointCreateBySourceRegex("I felt like it", self.main_source_spec) + self.assertTrue (handler_bkpt.GetNumLocations() > 0) + options.SetIgnoreBreakpoints(True) + options.SetUnwindOnError(True) + + value = frame.EvaluateExpression ("[my_class callMeIThrow]", options) + + self.assertTrue (value.IsValid() and value.GetError().Success() == False) + self.check_after_call() + + # Now set the ObjC language breakpoint and make sure that doesn't interfere with the call: + exception_bkpt = target.BreakpointCreateForException (lldb.eLanguageTypeObjC, False, True) + self.assertTrue(exception_bkpt.GetNumLocations() > 0) + + options.SetIgnoreBreakpoints(True) + options.SetUnwindOnError(True) + + value = frame.EvaluateExpression ("[my_class callMeIThrow]", options) + + self.assertTrue (value.IsValid() and value.GetError().Success() == False) + self.check_after_call() + + + # Now turn off exception trapping, and call a function that catches the exceptions, + # and make sure the function actually completes, and we get the right value: + options.SetTrapExceptions(False) + value = frame.EvaluateExpression ("[my_class iCatchMyself]", options) + self.assertTrue (value.IsValid()) + self.assertTrue (value.GetError().Success() == True) + self.assertTrue (value.GetValueAsUnsigned() == 57) + self.check_after_call() + options.SetTrapExceptions(True) + + # Now set this unwind on error to false, and make sure that we stop where the exception was thrown + options.SetUnwindOnError(False) + value = frame.EvaluateExpression ("[my_class callMeIThrow]", options) + + + self.assertTrue (value.IsValid() and value.GetError().Success() == False) + self.check_after_call() diff --git a/packages/Python/lldbsuite/test/expression_command/call-throws/call-throws.m b/packages/Python/lldbsuite/test/expression_command/call-throws/call-throws.m new file mode 100644 index 000000000000..a184718be7dd --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/call-throws/call-throws.m @@ -0,0 +1,47 @@ +#import + +@interface MyClass : NSObject +{ +} +- (int) callMeIThrow; +- (int) iCatchMyself; +@end + +@implementation MyClass +- (int) callMeIThrow +{ + NSException *e = [NSException + exceptionWithName:@"JustForTheHeckOfItException" + reason:@"I felt like it" + userInfo:nil]; + @throw e; + return 56; +} + +- (int) iCatchMyself +{ + int return_value = 55; + @try + { + return_value = [self callMeIThrow]; + } + @catch (NSException *e) + { + return_value = 57; + } + return return_value; +} +@end + +int +main () +{ + int return_value; + MyClass *my_class = [[MyClass alloc] init]; + + NSLog (@"I am about to throw."); + + return_value = [my_class iCatchMyself]; + + return return_value; +} diff --git a/packages/Python/lldbsuite/test/expression_command/char/Makefile b/packages/Python/lldbsuite/test/expression_command/char/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/char/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py b/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py new file mode 100644 index 000000000000..90e847fd5fa2 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py @@ -0,0 +1,69 @@ +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprCharTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + self.main_source = "main.cpp" + self.main_source_spec = lldb.SBFileSpec (self.main_source) + self.exe = os.path.join(os.getcwd(), "a.out") + + def do_test(self, dictionary=None): + """These basic expression commands should work as expected.""" + self.build(dictionary = dictionary) + + target = self.dbg.CreateTarget(self.exe) + self.assertTrue(target) + + breakpoint = target.BreakpointCreateBySourceRegex('// Break here', self.main_source_spec) + self.assertTrue(breakpoint) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process) + + threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint) + self.assertEqual(len(threads), 1) + + frame = threads[0].GetFrameAtIndex(0) + + value = frame.EvaluateExpression("foo(c)") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(0), 1) + + value = frame.EvaluateExpression("foo(sc)") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(0), 2) + + value = frame.EvaluateExpression("foo(uc)") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(0), 3) + + @expectedFailureWindows("llvm.org/pr21765") + def test_default_char(self): + self.do_test() + + @expectedFailureArch("arm", "llvm.org/pr23069") + @expectedFailureArch("aarch64", "llvm.org/pr23069") + @expectedFailureWindows("llvm.org/pr21765") + def test_signed_char(self): + self.do_test(dictionary={'CFLAGS_EXTRAS': '-fsigned-char'}) + + @expectedFailurei386("llvm.org/pr23069") + @expectedFailurex86_64("llvm.org/pr23069") + @expectedFailureWindows("llvm.org/pr21765") + def test_unsigned_char(self): + self.do_test(dictionary={'CFLAGS_EXTRAS': '-funsigned-char'}) diff --git a/packages/Python/lldbsuite/test/expression_command/char/main.cpp b/packages/Python/lldbsuite/test/expression_command/char/main.cpp new file mode 100644 index 000000000000..c8b0beb1b355 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/char/main.cpp @@ -0,0 +1,10 @@ +int foo(char c) { return 1; } +int foo(signed char c) { return 2; } +int foo(unsigned char c) { return 3; } + +int main() { + char c = 0; + signed char sc = 0; + unsigned char uc = 0; + return 0; // Break here +} diff --git a/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/Makefile b/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/TestExpressionInSyscall.py b/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/TestExpressionInSyscall.py new file mode 100644 index 000000000000..0430fa55f995 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/TestExpressionInSyscall.py @@ -0,0 +1,82 @@ +"""Test that we are able to evaluate expressions when the inferior is blocked in a syscall""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + + +class ExprSyscallTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr21765") # Also getpid() is not a function on Windows anyway + def test_setpgid(self): + self.build() + self.expr_syscall() + + def expr_syscall(self): + exe = os.path.join(os.getcwd(), 'a.out') + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + listener = lldb.SBListener("my listener") + + # launch the inferior and don't wait for it to stop + self.dbg.SetAsync(True) + error = lldb.SBError() + process = target.Launch (listener, + None, # argv + None, # envp + None, # stdin_path + None, # stdout_path + None, # stderr_path + None, # working directory + 0, # launch flags + False, # Stop at entry + error) # error + + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + + event = lldb.SBEvent() + + # Give the child enough time to reach the syscall, + # while clearing out all the pending events. + # The last WaitForEvent call will time out after 2 seconds. + while listener.WaitForEvent(2, event): + pass + + # now the process should be running (blocked in the syscall) + self.assertEqual(process.GetState(), lldb.eStateRunning, "Process is running") + + # send the process a signal + process.SendAsyncInterrupt() + while listener.WaitForEvent(2, event): + pass + + # as a result the process should stop + # in all likelihood we have stopped in the middle of the sleep() syscall + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + thread = process.GetSelectedThread() + + # try evaluating a couple of expressions in this state + self.expect("expr release_flag = 1", substrs = [" = 1"]) + self.expect("print (int)getpid()", substrs = [str(process.GetProcessID())]) + + # and run the process to completion + process.Continue() + + # process all events + while listener.WaitForEvent(10, event): + new_state = lldb.SBProcess.GetStateFromEvent(event) + if new_state == lldb.eStateExited: + break + + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) diff --git a/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/main.cpp b/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/main.cpp new file mode 100644 index 000000000000..743b69434d5f --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/expr-in-syscall/main.cpp @@ -0,0 +1,12 @@ +#include +#include + +volatile int release_flag = 0; + +int main(int argc, char const *argv[]) +{ + while (! release_flag) // Wait for debugger to attach + std::this_thread::sleep_for(std::chrono::seconds(3)); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/expression_command/formatters/Makefile b/packages/Python/lldbsuite/test/expression_command/formatters/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/formatters/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/formatters/TestFormatters.py b/packages/Python/lldbsuite/test/expression_command/formatters/TestFormatters.py new file mode 100644 index 000000000000..3f47206480c3 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/formatters/TestFormatters.py @@ -0,0 +1,167 @@ +""" +Test using LLDB data formatters with frozen objects coming from the expression parser. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprFormattersTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.line = line_number('main.cpp', + '// Stop here') + + @skipIfFreeBSD # llvm.org/pr24691 skipping to avoid crashing the test runner + @expectedFailureFreeBSD('llvm.org/pr19011') # Newer Clang omits C1 complete object constructor + @expectedFailureFreeBSD('llvm.org/pr24691') # we hit an assertion in clang + @expectedFailureWindows("llvm.org/pr21765") + @skipIfTargetAndroid() # skipping to avoid crashing the test runner + @expectedFailureAndroid('llvm.org/pr24691') # we hit an assertion in clang + def test(self): + """Test expr + formatters for good interoperability.""" + self.build() + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type summary clear', check=False) + self.runCmd('type synthetic clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + """Test expr + formatters for good interoperability.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + self.runCmd("command script import formatters.py") + self.runCmd("command script import foosynth.py") + + if self.TraceOn(): + self.runCmd("frame variable foo1 --show-types") + self.runCmd("frame variable foo1.b --show-types") + self.runCmd("frame variable foo1.b.b_ref --show-types") + + self.expect("expression --show-types -- *(new foo(47))", + substrs = ['(int) a = 47', '(bar) b = {', '(int) i = 94', '(baz) b = {', '(int) k = 99']) + + self.runCmd("type summary add -F formatters.foo_SummaryProvider foo") + + self.expect("expression new int(12)", + substrs = ['(int *) $', ' = 0x']) + + self.runCmd("type summary add -s \"${var%pointer} -> ${*var%decimal}\" \"int *\"") + + self.expect("expression new int(12)", + substrs = ['(int *) $', '= 0x', ' -> 12']) + + self.expect("expression foo1.a_ptr", + substrs = ['(int *) $', '= 0x', ' -> 13']) + + self.expect("expression foo1", + substrs = ['(foo) $', ' a = 12', 'a_ptr = ', ' -> 13','i = 24','i_ptr = ', ' -> 25']) + + self.expect("expression --ptr-depth=1 -- new foo(47)", + substrs = ['(foo *) $', 'a = 47','a_ptr = ', ' -> 48','i = 94','i_ptr = ', ' -> 95']) + + self.expect("expression foo2", + substrs = ['(foo) $', 'a = 121','a_ptr = ', ' -> 122','i = 242','i_ptr = ', ' -> 243']) + + object_name = self.res.GetOutput() + object_name = object_name[7:] + object_name = object_name[0:object_name.find(' =')] + + self.expect("frame variable foo2", + substrs = ['(foo)', 'foo2', 'a = 121','a_ptr = ', ' -> 122','i = 242','i_ptr = ', ' -> 243']) + + self.expect("expression $" + object_name, + substrs = ['(foo) $', 'a = 121','a_ptr = ', ' -> 122','i = 242','i_ptr = ', ' -> 243', 'h = 245','k = 247']) + + self.runCmd("type summary delete foo") + self.runCmd("type synthetic add --python-class foosynth.FooSyntheticProvider foo") + + self.expect("expression --show-types -- $" + object_name, + substrs = ['(foo) $', ' = {', '(int) *i_ptr = 243']) + + self.runCmd("n") + self.runCmd("n") + + self.runCmd("type synthetic delete foo") + self.runCmd("type summary add -F formatters.foo_SummaryProvider foo") + + self.expect("expression foo2", + substrs = ['(foo) $', 'a = 7777','a_ptr = ', ' -> 122','i = 242','i_ptr = ', ' -> 8888']) + + self.expect("expression $" + object_name + '.a', + substrs = ['7777']) + + self.expect("expression *$" + object_name + '.b.i_ptr', + substrs = ['8888']) + + self.expect("expression $" + object_name, + substrs = ['(foo) $', 'a = 121', 'a_ptr = ', ' -> 122', 'i = 242', 'i_ptr = ', ' -> 8888', 'h = 245','k = 247']) + + self.runCmd("type summary delete foo") + self.runCmd("type synthetic add --python-class foosynth.FooSyntheticProvider foo") + + self.expect("expression --show-types -- $" + object_name, + substrs = ['(foo) $', ' = {', '(int) *i_ptr = 8888']) + + self.runCmd("n") + + self.runCmd("type synthetic delete foo") + self.runCmd("type summary add -F formatters.foo_SummaryProvider foo") + + self.expect("expression $" + object_name, + substrs = ['(foo) $', 'a = 121','a_ptr = ', ' -> 122','i = 242', 'i_ptr = ', ' -> 8888','k = 247']) + + process = self.dbg.GetSelectedTarget().GetProcess() + thread = process.GetThreadAtIndex(0) + frame = thread.GetSelectedFrame() + + frozen = frame.EvaluateExpression("$" + object_name + ".a_ptr") + + a_data = frozen.GetPointeeData() + + error = lldb.SBError() + self.assertTrue(a_data.GetUnsignedInt32(error, 0) == 122, '*a_ptr = 122') + + self.runCmd("n");self.runCmd("n");self.runCmd("n"); + + self.expect("frame variable numbers", + substrs = ['1','2','3','4','5']) + + self.expect("expression numbers", + substrs = ['1','2','3','4','5']) + + frozen = frame.EvaluateExpression("&numbers") + + a_data = frozen.GetPointeeData(0, 1) + + self.assertTrue(a_data.GetUnsignedInt32(error, 0) == 1, 'numbers[0] == 1') + self.assertTrue(a_data.GetUnsignedInt32(error, 4) == 2, 'numbers[1] == 2') + self.assertTrue(a_data.GetUnsignedInt32(error, 8) == 3, 'numbers[2] == 3') + self.assertTrue(a_data.GetUnsignedInt32(error, 12) == 4, 'numbers[3] == 4') + self.assertTrue(a_data.GetUnsignedInt32(error, 16) == 5, 'numbers[4] == 5') + + frozen = frame.EvaluateExpression("numbers") + + a_data = frozen.GetData() + + self.assertTrue(a_data.GetUnsignedInt32(error, 0) == 1, 'numbers[0] == 1') + self.assertTrue(a_data.GetUnsignedInt32(error, 4) == 2, 'numbers[1] == 2') + self.assertTrue(a_data.GetUnsignedInt32(error, 8) == 3, 'numbers[2] == 3') + self.assertTrue(a_data.GetUnsignedInt32(error, 12) == 4, 'numbers[3] == 4') + self.assertTrue(a_data.GetUnsignedInt32(error, 16) == 5, 'numbers[4] == 5') diff --git a/packages/Python/lldbsuite/test/expression_command/formatters/foosynth.py b/packages/Python/lldbsuite/test/expression_command/formatters/foosynth.py new file mode 100644 index 000000000000..91c4d4a84c62 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/formatters/foosynth.py @@ -0,0 +1,29 @@ +import lldb + +class FooSyntheticProvider: + def __init__(self,valobj,dict): + self.valobj = valobj; + self.update(); + + def update(self): + self.adjust_for_architecture() + + def num_children(self): + return 1; + + def get_child_at_index(self,index): + if index != 0: + return None; + return self.i_ptr.Dereference(); + + def get_child_index(self,name): + if name == "*i_ptr": + return 0; + return None; + + def adjust_for_architecture(self): + self.lp64 = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8) + self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle) + self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() + self.bar = self.valobj.GetChildMemberWithName('b'); + self.i_ptr = self.bar.GetChildMemberWithName('i_ptr'); \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/expression_command/formatters/formatters.py b/packages/Python/lldbsuite/test/expression_command/formatters/formatters.py new file mode 100644 index 000000000000..ce922a8f911d --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/formatters/formatters.py @@ -0,0 +1,17 @@ +def foo_SummaryProvider (valobj,dict): + a = valobj.GetChildMemberWithName('a'); + a_ptr = valobj.GetChildMemberWithName('a_ptr'); + bar = valobj.GetChildMemberWithName('b'); + i = bar.GetChildMemberWithName('i'); + i_ptr = bar.GetChildMemberWithName('i_ptr'); + b_ref = bar.GetChildMemberWithName('b_ref'); + b_ref_ptr = b_ref.AddressOf() + b_ref = b_ref_ptr.Dereference() + h = b_ref.GetChildMemberWithName('h'); + k = b_ref.GetChildMemberWithName('k'); + return 'a = ' + str(a.GetValueAsUnsigned(0)) + ', a_ptr = ' + \ + str(a_ptr.GetValueAsUnsigned(0)) + ' -> ' + str(a_ptr.Dereference().GetValueAsUnsigned(0)) + \ + ', i = ' + str(i.GetValueAsUnsigned(0)) + \ + ', i_ptr = ' + str(i_ptr.GetValueAsUnsigned(0)) + ' -> ' + str(i_ptr.Dereference().GetValueAsUnsigned(0)) + \ + ', b_ref = ' + str(b_ref.GetValueAsUnsigned(0)) + \ + ', h = ' + str(h.GetValueAsUnsigned(0)) + ' , k = ' + str(k.GetValueAsUnsigned(0)) \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/expression_command/formatters/main.cpp b/packages/Python/lldbsuite/test/expression_command/formatters/main.cpp new file mode 100644 index 000000000000..4c3b180f3709 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/formatters/main.cpp @@ -0,0 +1,48 @@ +#include +#include + +struct baz + { + int h; + int k; + baz(int a, int b) : h(a), k(b) {} + }; + +struct bar + { + int i; + int* i_ptr; + baz b; + baz& b_ref; + bar(int x) : i(x),i_ptr(new int(x+1)),b(i+3,i+5),b_ref(b) {} + }; + +struct foo + { + int a; + int* a_ptr; + bar b; + + foo(int x) : a(x), + a_ptr(new int(x+1)), + b(2*x) {} + + }; + +int main(int argc, char** argv) +{ + foo foo1(12); + foo foo2(121); + + foo2.a = 7777; // Stop here + *(foo2.b.i_ptr) = 8888; + foo2.b.b.h = 9999; + + *(foo1.a_ptr) = 9999; + foo1.b.i = 9999; + + int numbers[5] = {1,2,3,4,5}; + + return 0; + +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/expression_command/issue_11588/Makefile b/packages/Python/lldbsuite/test/expression_command/issue_11588/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/issue_11588/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/issue_11588/Test11588.py b/packages/Python/lldbsuite/test/expression_command/issue_11588/Test11588.py new file mode 100644 index 000000000000..fdc981ea178d --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/issue_11588/Test11588.py @@ -0,0 +1,77 @@ +""" +Test the solution to issue 11581. +valobj.AddressOf() returns None when an address is +expected in a SyntheticChildrenProvider +""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class Issue11581TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr24778") + def test_11581_commands(self): + # This is the function to remove the custom commands in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type synthetic clear', check=False) + + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + """valobj.AddressOf() should return correct values.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateBySourceRegex('Set breakpoint here.',lldb.SBFileSpec ("main.cpp", False)) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue (process, "Created a process.") + self.assertTrue (process.GetState() == lldb.eStateStopped, "Stopped it too.") + + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue (len(thread_list) == 1) + thread = thread_list[0] + + self.runCmd("command script import --allow-reload s11588.py") + self.runCmd("type synthetic add --python-class s11588.Issue11581SyntheticProvider StgClosure") + + self.expect("expr --show-types -- *((StgClosure*)(r14-1))", + substrs = ["(StgClosure) $", + "(StgClosure *) &$","0x", + "addr = ", + "load_address = "]) + + # register r14 is an x86_64 extension let's skip this part of the test + # if we are on a different architecture + if self.getArchitecture() == 'x86_64': + target = lldb.debugger.GetSelectedTarget() + process = target.GetProcess() + frame = process.GetSelectedThread().GetSelectedFrame() + pointer = frame.FindVariable("r14") + addr = pointer.GetValueAsUnsigned(0) + self.assertTrue(addr != 0, "could not read pointer to StgClosure") + addr = addr - 1 + self.runCmd("register write r14 %d" % addr) + self.expect("register read r14", + substrs = ["0x",hex(addr)[2:].rstrip("L")]) # Remove trailing 'L' if it exists + self.expect("expr --show-types -- *(StgClosure*)$r14", + substrs = ["(StgClosure) $", + "(StgClosure *) &$","0x", + "addr = ", + "load_address = ", + hex(addr)[2:].rstrip("L"), + str(addr)]) diff --git a/packages/Python/lldbsuite/test/expression_command/issue_11588/main.cpp b/packages/Python/lldbsuite/test/expression_command/issue_11588/main.cpp new file mode 100644 index 000000000000..4f9ea3abf181 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/issue_11588/main.cpp @@ -0,0 +1,54 @@ +// +// 11588.cpp +// + +#include + +class StgInfoTable {}; + +class StgHeader +{ +private: + StgInfoTable* info; +public: + StgHeader() + { + info = new StgInfoTable(); + } + ~StgHeader() + { + delete info; + } +}; + +class StgClosure +{ +private: + StgHeader header; + StgClosure* payload[1]; +public: + StgClosure(bool make_payload = true) + { + if (make_payload) + payload[0] = new StgClosure(false); + else + payload[0] = NULL; + } + ~StgClosure() + { + if (payload[0]) + delete payload[0]; + } +}; + +typedef unsigned long long int ptr_type; + +int main() +{ + StgClosure* r14_ = new StgClosure(); + r14_ = (StgClosure*)(((ptr_type)r14_ | 0x01)); // set the LSB to 1 for tagging + ptr_type r14 = (ptr_type)r14_; + int x = 0; + x = 3; + return (x-1); // Set breakpoint here. +} diff --git a/packages/Python/lldbsuite/test/expression_command/issue_11588/s11588.py b/packages/Python/lldbsuite/test/expression_command/issue_11588/s11588.py new file mode 100644 index 000000000000..01bb09a1b0d6 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/issue_11588/s11588.py @@ -0,0 +1,26 @@ +class Issue11581SyntheticProvider(object): + def __init__(self, valobj, dict): + self.valobj = valobj + self.addrOf = valobj.AddressOf() + self.addr = valobj.GetAddress() + self.load_address = valobj.GetLoadAddress() + + def num_children(self): + return 3; + + def get_child_at_index(self, index): + if index == 0: + return self.addrOf + if index == 1: + return self.valobj.CreateValueFromExpression("addr", str(self.addr)) + if index == 2: + return self.valobj.CreateValueFromExpression("load_address", str(self.load_address)) + + def get_child_index(self, name): + if name == "addrOf": + return 0 + if name == "addr": + return 1 + if name == "load_address": + return 2 + diff --git a/packages/Python/lldbsuite/test/expression_command/macros/Makefile b/packages/Python/lldbsuite/test/expression_command/macros/Makefile new file mode 100644 index 000000000000..1ecd744be14b --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/macros/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +DEBUG_INFO_FLAG = -g3 + + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/macros/TestMacros.py b/packages/Python/lldbsuite/test/expression_command/macros/TestMacros.py new file mode 100644 index 000000000000..c3d6306c5a63 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/macros/TestMacros.py @@ -0,0 +1,106 @@ +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestMacros(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureClang("clang does not emit .debug_macro[.dwo] sections.") + @expectedFailureDwo("GCC produces multiple .debug_macro.dwo sections and the spec is unclear as to what it means") + @expectedFailureAll(hostoslist=["windows"], compiler="gcc", triple='.*-android') + def test_expr_with_macros(self): + self.build() + + # Get main source file + src_file = "main.cpp" + hdr_file = "macro1.h" + src_file_spec = lldb.SBFileSpec(src_file) + self.assertTrue(src_file_spec.IsValid(), "Main source file") + + # Get the path of the executable + cwd = os.getcwd() + exe_file = "a.out" + exe_path = os.path.join(cwd, exe_file) + + # Load the executable + target = self.dbg.CreateTarget(exe_path) + self.assertTrue(target.IsValid(), VALID_TARGET) + + # Set breakpoints + bp1 = target.BreakpointCreateBySourceRegex("Break here", src_file_spec) + self.assertTrue(bp1.IsValid() and bp1.GetNumLocations() >= 1, VALID_BREAKPOINT) + + # Launch the process + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + + # Get the thread of the process + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + + # Get frame for current thread + frame = thread.GetSelectedFrame() + + result = frame.EvaluateExpression("MACRO_1") + self.assertTrue(result.IsValid() and result.GetValue() == "100", "MACRO_1 = 100") + + result = frame.EvaluateExpression("MACRO_2") + self.assertTrue(result.IsValid() and result.GetValue() == "200", "MACRO_2 = 200") + + result = frame.EvaluateExpression("ONE") + self.assertTrue(result.IsValid() and result.GetValue() == "1", "ONE = 1") + + result = frame.EvaluateExpression("TWO") + self.assertTrue(result.IsValid() and result.GetValue() == "2", "TWO = 2") + + result = frame.EvaluateExpression("THREE") + self.assertTrue(result.IsValid() and result.GetValue() == "3", "THREE = 3") + + result = frame.EvaluateExpression("FOUR") + self.assertTrue(result.IsValid() and result.GetValue() == "4", "FOUR = 4") + + result = frame.EvaluateExpression("HUNDRED") + self.assertTrue(result.IsValid() and result.GetValue() == "100", "HUNDRED = 100") + + result = frame.EvaluateExpression("THOUSAND") + self.assertTrue(result.IsValid() and result.GetValue() == "1000", "THOUSAND = 1000") + + result = frame.EvaluateExpression("MILLION") + self.assertTrue(result.IsValid() and result.GetValue() == "1000000", "MILLION = 1000000") + + result = frame.EvaluateExpression("MAX(ONE, TWO)") + self.assertTrue(result.IsValid() and result.GetValue() == "2", "MAX(ONE, TWO) = 2") + + result = frame.EvaluateExpression("MAX(THREE, TWO)") + self.assertTrue(result.IsValid() and result.GetValue() == "3", "MAX(THREE, TWO) = 3") + + # Get the thread of the process + thread.StepOver() + + # Get frame for current thread + frame = thread.GetSelectedFrame() + + result = frame.EvaluateExpression("MACRO_2") + self.assertTrue(result.GetError().Fail(), "Printing MACRO_2 fails in the mail file") + + result = frame.EvaluateExpression("FOUR") + self.assertTrue(result.GetError().Fail(), "Printing FOUR fails in the main file") + + thread.StepInto() + + # Get frame for current thread + frame = thread.GetSelectedFrame() + + result = frame.EvaluateExpression("ONE") + self.assertTrue(result.IsValid() and result.GetValue() == "1", "ONE = 1") + + result = frame.EvaluateExpression("MAX(ONE, TWO)") + self.assertTrue(result.IsValid() and result.GetValue() == "2", "MAX(ONE, TWO) = 2") + + # This time, MACRO_1 and MACRO_2 are not visible. + result = frame.EvaluateExpression("MACRO_1") + self.assertTrue(result.GetError().Fail(), "Printing MACRO_1 fails in the header file") + + result = frame.EvaluateExpression("MACRO_2") + self.assertTrue(result.GetError().Fail(), "Printing MACRO_2 fails in the header file") diff --git a/packages/Python/lldbsuite/test/expression_command/macros/macro1.h b/packages/Python/lldbsuite/test/expression_command/macros/macro1.h new file mode 100644 index 000000000000..e026bc018ace --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/macros/macro1.h @@ -0,0 +1,17 @@ + +#include "macro2.h" + +#define ONE 1 +#define TWO 2 +#define THREE 3 +#define FOUR 4 + +class Simple +{ +public: + int + Method() + { + return ONE + TWO; + }; +}; diff --git a/packages/Python/lldbsuite/test/expression_command/macros/macro2.h b/packages/Python/lldbsuite/test/expression_command/macros/macro2.h new file mode 100644 index 000000000000..cec6efbba990 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/macros/macro2.h @@ -0,0 +1,8 @@ +#define HUNDRED 100 +#define THOUSAND 1000 +#define MILLION 1000000 + +#define MAX(a, b)\ +((a) > (b) ?\ + (a):\ + (b)) diff --git a/packages/Python/lldbsuite/test/expression_command/macros/main.cpp b/packages/Python/lldbsuite/test/expression_command/macros/main.cpp new file mode 100644 index 000000000000..f2c2c101fa15 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/macros/main.cpp @@ -0,0 +1,15 @@ +#include "macro1.h" + +#define MACRO_1 100 +#define MACRO_2 200 + +int +main () +{ + int a = ONE + TWO; // Break here + + #undef MACRO_2 + #undef FOUR + + return Simple().Method(); +} diff --git a/packages/Python/lldbsuite/test/expression_command/options/Makefile b/packages/Python/lldbsuite/test/expression_command/options/Makefile new file mode 100644 index 000000000000..81ae6f6f16ec --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/options/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp foo.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/options/TestExprOptions.py b/packages/Python/lldbsuite/test/expression_command/options/TestExprOptions.py new file mode 100644 index 000000000000..00c34820eef3 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/options/TestExprOptions.py @@ -0,0 +1,76 @@ +""" +Test expression command options. + +Test cases: + +o test_expr_options: + Test expression command options. +""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprOptionsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + self.main_source = "main.cpp" + self.main_source_spec = lldb.SBFileSpec (self.main_source) + self.line = line_number('main.cpp', '// breakpoint_in_main') + self.exe = os.path.join(os.getcwd(), "a.out") + + def test_expr_options(self): + """These expression command options should work as expected.""" + self.build() + + # Set debugger into synchronous mode + self.dbg.SetAsync(False) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(self.exe) + self.assertTrue(target, VALID_TARGET) + + # Set breakpoints inside main. + breakpoint = target.BreakpointCreateBySourceRegex('// breakpoint_in_main', self.main_source_spec) + self.assertTrue(breakpoint) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint) + self.assertEqual(len(threads), 1) + + frame = threads[0].GetFrameAtIndex(0) + options = lldb.SBExpressionOptions() + + # test --language on C++ expression using the SB API's + + # Make sure we can evaluate a C++11 expression. + val = frame.EvaluateExpression('foo != nullptr') + self.assertTrue(val.IsValid()) + self.assertTrue(val.GetError().Success()) + self.DebugSBValue(val) + + # Make sure it still works if language is set to C++11: + options.SetLanguage(lldb.eLanguageTypeC_plus_plus_11) + val = frame.EvaluateExpression('foo != nullptr', options) + self.assertTrue(val.IsValid()) + self.assertTrue(val.GetError().Success()) + self.DebugSBValue(val) + + # Make sure it fails if language is set to C: + options.SetLanguage(lldb.eLanguageTypeC) + val = frame.EvaluateExpression('foo != nullptr', options) + self.assertTrue(val.IsValid()) + self.assertFalse(val.GetError().Success()) diff --git a/packages/Python/lldbsuite/test/expression_command/options/foo.cpp b/packages/Python/lldbsuite/test/expression_command/options/foo.cpp new file mode 100644 index 000000000000..8a5a6a2b5416 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/options/foo.cpp @@ -0,0 +1,11 @@ +namespace ns { + int func(void) + { + return 0; + } +} + +extern "C" int foo(void) +{ + return ns::func(); +} diff --git a/packages/Python/lldbsuite/test/expression_command/options/main.cpp b/packages/Python/lldbsuite/test/expression_command/options/main.cpp new file mode 100644 index 000000000000..ecd9a90f6626 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/options/main.cpp @@ -0,0 +1,15 @@ +extern "C" int foo(void); +static int static_value = 0; + +int +bar() +{ + static_value++; + return static_value; +} + +int main (int argc, char const *argv[]) +{ + bar(); // breakpoint_in_main + return foo(); +} diff --git a/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/Makefile b/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/Makefile new file mode 100644 index 000000000000..8066198300fb --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules +LDFLAGS += -framework Foundation -framework CloudKit + diff --git a/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/TestPersistObjCPointeeType.py b/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/TestPersistObjCPointeeType.py new file mode 100644 index 000000000000..7b0707cdf22c --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/TestPersistObjCPointeeType.py @@ -0,0 +1,50 @@ +""" +Test that we can p *objcObject +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class PersistObjCPointeeType(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.line = line_number('main.m','// break here') + + @skipUnlessDarwin + @expectedFailureAll( + bugnumber='http://llvm.org/pr23504', + oslist=['macosx'], compiler='clang', compiler_version=['<', '7.0.0']) + def test_with(self): + """Test that we can p *objcObject""" + self.build() + + def cleanup(): + pass + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("p *self", substrs=['_sc_name = nil', + '_sc_name2 = nil', + '_sc_name3 = nil', + '_sc_name4 = nil', + '_sc_name5 = nil', + '_sc_name6 = nil', + '_sc_name7 = nil', + '_sc_name8 = nil']) diff --git a/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/main.m b/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/main.m new file mode 100644 index 000000000000..a2b6b703d6c8 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persist_objc_pointeetype/main.m @@ -0,0 +1,80 @@ +/* +clang -g ExtendSuperclass.m -o ExtendSuperclass -framework Foundation -framework ProtectedCloudStorage -F/System/Library/PrivateFrameworks/ -framework CloudKit && ./ExtendSuperclass +*/ +#include +#import +#import + +#define SuperClass CKDatabase + +@interface SubClass : SuperClass +@end + +// class extension +@interface SuperClass () +@property (nonatomic, strong) NSString *_sc_name; +@property (nonatomic, strong) NSString *_sc_name2; +@property (nonatomic, strong) NSString *_sc_name3; +@property (nonatomic, strong) NSString *_sc_name4; +@property (nonatomic, strong) NSString *_sc_name5; +@property (nonatomic, strong) NSString *_sc_name6; +@property (nonatomic, strong) NSString *_sc_name7; +@property (nonatomic, strong) NSString *_sc_name8; +@end + +@implementation SuperClass (MySuperClass) +- (id)initThatDoesNotAssert +{ + return [super init]; +} +@end + +@implementation SubClass +- (id)initThatDoesNotAssert +{ + assert(_sc_name == nil); + assert(_sc_name2 == nil); + assert(_sc_name3 == nil); + assert(_sc_name4 == nil); + assert(_sc_name5 == nil); + assert(_sc_name6 == nil); + assert(_sc_name7 == nil); + assert(_sc_name8 == nil); // break here + + if ((self = [super _initWithContainer:(CKContainer*)@"foo" scope:0xff])) { + assert(_sc_name == nil); + assert(_sc_name2 == nil); + assert(_sc_name3 == nil); + assert(_sc_name4 == nil); + assert(_sc_name5 == nil); + assert(_sc_name6 == nil); + assert(_sc_name7 == nil); + assert(_sc_name8 == nil); + + _sc_name = @"empty"; + } + return self; +} +@synthesize _sc_name; +@synthesize _sc_name2; +@synthesize _sc_name3; +@synthesize _sc_name4; +@synthesize _sc_name5; +@synthesize _sc_name6; +@synthesize _sc_name7; +@synthesize _sc_name8; +- (void)foo:(NSString*)bar { self._sc_name = bar; } +- (NSString*)description { return [NSString stringWithFormat:@"%p: %@", self, self._sc_name]; } +@end + +int main() +{ + SubClass *sc = [[SubClass alloc] initThatDoesNotAssert]; + NSLog(@"%@", sc); + [sc foo:@"bar"]; + NSLog(@"%@", sc); + sc._sc_name = @"bar2"; + NSLog(@"%@", sc); + NSLog(@"%@", sc._sc_name); + return 0; +} diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/Makefile b/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/Makefile new file mode 100644 index 000000000000..db5f575866d9 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules + + diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/TestPersistentPtrUpdate.py b/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/TestPersistentPtrUpdate.py new file mode 100644 index 000000000000..9d7359fb2d45 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/TestPersistentPtrUpdate.py @@ -0,0 +1,41 @@ +""" +Test that we can have persistent pointer variables +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class PersistentPtrUpdateTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def test(self): + """Test that we can have persistent pointer variables""" + self.build() + + def cleanup(): + pass + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.runCmd('break set -p here') + + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("expr void* $foo = 0") + + self.runCmd("continue") + + self.expect("expr $foo", substrs=['$foo','0x0']) diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/main.c b/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/main.c new file mode 100644 index 000000000000..73346969eccc --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_ptr_update/main.c @@ -0,0 +1,11 @@ +void* foo(void *p) +{ + return p; // break here +} + +int main() { + while (1) { + foo(0); + } + return 0; +} diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_types/Makefile b/packages/Python/lldbsuite/test/expression_command/persistent_types/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_types/TestNestedPersistentTypes.py b/packages/Python/lldbsuite/test/expression_command/persistent_types/TestNestedPersistentTypes.py new file mode 100644 index 000000000000..3ea5b7040655 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_types/TestNestedPersistentTypes.py @@ -0,0 +1,41 @@ +""" +Test that nested persistent types work. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class NestedPersistentTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr21765") + def test_persistent_types(self): + """Test that nested persistent types work.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.runCmd("breakpoint set --name main") + + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("expression struct $foo { int a; int b; };") + + self.runCmd("expression struct $bar { struct $foo start; struct $foo end; };") + + self.runCmd("expression struct $bar $my_bar = {{ 2, 3 }, { 4, 5 }};") + + self.expect("expression $my_bar", + substrs = ['a = 2', 'b = 3', 'a = 4', 'b = 5']) + + self.expect("expression $my_bar.start.b", + substrs = ['(int)', '3']) + + self.expect("expression $my_bar.end.b", + substrs = ['(int)', '5']) diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_types/TestPersistentTypes.py b/packages/Python/lldbsuite/test/expression_command/persistent_types/TestPersistentTypes.py new file mode 100644 index 000000000000..47c6675511fd --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_types/TestPersistentTypes.py @@ -0,0 +1,57 @@ +""" +Test that lldb persistent types works correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class PersistenttypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr21765") + def test_persistent_types(self): + """Test that lldb persistent types works correctly.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.runCmd("breakpoint set --name main") + + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("expression struct $foo { int a; int b; };") + + self.expect("expression struct $foo $my_foo; $my_foo.a = 2; $my_foo.b = 3;", + startstr = "(int) $0 = 3") + + self.expect("expression $my_foo", + substrs = ['a = 2', 'b = 3']) + + self.runCmd("expression typedef int $bar") + + self.expect("expression $bar i = 5; i", + startstr = "($bar) $1 = 5") + + self.runCmd("expression struct $foobar { char a; char b; char c; char d; };") + self.runCmd("next") + + self.expect("memory read foo -t $foobar", + substrs = ['($foobar) 0x', ' = ', "a = 'H'","b = 'e'","c = 'l'","d = 'l'"]) # persistent types are OK to use for memory read + + self.expect("memory read foo -t foobar", + substrs = ['($foobar) 0x', ' = ', "a = 'H'","b = 'e'","c = 'l'","d = 'l'"],matching=False,error=True) # the type name is $foobar, make sure we settle for nothing less + + self.expect("expression struct { int a; int b; } x = { 2, 3 }; x", + substrs = ['a = 2', 'b = 3']) + + self.expect("expression struct { int x; int y; int z; } object; object.y = 1; object.z = 3; object.x = 2; object", + substrs = ['x = 2', 'y = 1', 'z = 3']) + + self.expect("expression struct A { int x; int y; }; struct { struct A a; int z; } object; object.a.y = 1; object.z = 3; object.a.x = 2; object", + substrs = ['x = 2', 'y = 1', 'z = 3']) diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_types/main.c b/packages/Python/lldbsuite/test/expression_command/persistent_types/main.c new file mode 100644 index 000000000000..9e26e619bfdb --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_types/main.c @@ -0,0 +1,14 @@ +//===-- 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[]) +{ + const char* foo = "Hello world"; + return 0; +} diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_variables/Makefile b/packages/Python/lldbsuite/test/expression_command/persistent_variables/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_variables/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_variables/TestPersistentVariables.py b/packages/Python/lldbsuite/test/expression_command/persistent_variables/TestPersistentVariables.py new file mode 100644 index 000000000000..e148fcd89599 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_variables/TestPersistentVariables.py @@ -0,0 +1,54 @@ +""" +Test that lldb persistent variables works correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class PersistentVariablesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_persistent_variables(self): + """Test that lldb persistent variables works correctly.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.runCmd("breakpoint set --source-pattern-regexp break") + + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("expression int $i = i") + + self.expect("expression $i == i", + startstr = "(bool) $0 = true") + + self.expect("expression $i + 1", + startstr = "(int) $1 = 6") + + self.expect("expression $i + 3", + startstr = "(int) $2 = 8") + + self.expect("expression $2 + $1", + startstr = "(int) $3 = 14") + + self.expect("expression $3", + startstr = "(int) $3 = 14") + + self.expect("expression $2", + startstr = "(int) $2 = 8") + + self.expect("expression (int)-2", + startstr = "(int) $4 = -2") + + self.expect("expression $4 > (int)31", + startstr = "(bool) $5 = false") + + self.expect("expression (long)$4", + startstr = "(long) $6 = -2") diff --git a/packages/Python/lldbsuite/test/expression_command/persistent_variables/main.c b/packages/Python/lldbsuite/test/expression_command/persistent_variables/main.c new file mode 100644 index 000000000000..fd41471df8ed --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/persistent_variables/main.c @@ -0,0 +1,14 @@ +//===-- 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[]) +{ + int i = 5; + return 0; // Set breakpoint here +} diff --git a/packages/Python/lldbsuite/test/expression_command/po_verbosity/Makefile b/packages/Python/lldbsuite/test/expression_command/po_verbosity/Makefile new file mode 100644 index 000000000000..4464e2ee9f5c --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/po_verbosity/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules +LDFLAGS += -framework Cocoa diff --git a/packages/Python/lldbsuite/test/expression_command/po_verbosity/TestPoVerbosity.py b/packages/Python/lldbsuite/test/expression_command/po_verbosity/TestPoVerbosity.py new file mode 100644 index 000000000000..e415b92a43a6 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/po_verbosity/TestPoVerbosity.py @@ -0,0 +1,61 @@ +""" +Test that the po command acts correctly. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class PoVerbosityTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.line = line_number('main.m', + '// Stop here') + + @skipUnlessDarwin + def test(self): + """Test that the po command acts correctly.""" + self.build() + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type summary clear', check=False) + self.runCmd('type synthetic clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + """Test expr + formatters for good interoperability.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("expr -O -v -- foo", + substrs = ['(id) $',' = 0x', '1 = 2','2 = 3;']) + self.expect("expr -O -vfull -- foo", + substrs = ['(id) $',' = 0x', '1 = 2','2 = 3;']) + self.expect("expr -O -- foo",matching=False, + substrs = ['(id) $']) + + self.expect("expr -O -- 22",matching=False, + substrs = ['(int) $']) + self.expect("expr -O -- 22", + substrs = ['22']) + + self.expect("expr -O -vfull -- 22", + substrs = ['(int) $', ' = 22']) + + self.expect("expr -O -v -- 22", + substrs = ['(int) $', ' = 22']) diff --git a/packages/Python/lldbsuite/test/expression_command/po_verbosity/main.m b/packages/Python/lldbsuite/test/expression_command/po_verbosity/main.m new file mode 100644 index 000000000000..3dbb024b2809 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/po_verbosity/main.m @@ -0,0 +1,9 @@ +#import + +int main() +{ + [NSString initialize]; + id foo = @{@1 : @2, @2 : @3}; + int x = 34; + return 0; // Stop here +} diff --git a/packages/Python/lldbsuite/test/expression_command/radar_8638051/Makefile b/packages/Python/lldbsuite/test/expression_command/radar_8638051/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_8638051/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/radar_8638051/Test8638051.py b/packages/Python/lldbsuite/test/expression_command/radar_8638051/Test8638051.py new file mode 100644 index 000000000000..302b14b7360e --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_8638051/Test8638051.py @@ -0,0 +1,39 @@ +""" +Test the robustness of lldb expression parser. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class Radar8638051TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_expr_commands(self): + """The following expression commands should not crash.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.runCmd("breakpoint set -n c") + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("expression val", + startstr = "(int) $0 = 1") + # (int) $0 = 1 + + self.expect("expression *(&val)", + startstr = "(int) $1 = 1") + # (int) $1 = 1 + + # rdar://problem/8638051 + # lldb expression command: Could this crash be avoided + self.expect("expression &val", + startstr = "(int *) $2 = ") + # (int *) $2 = 0x.... diff --git a/packages/Python/lldbsuite/test/expression_command/radar_8638051/main.c b/packages/Python/lldbsuite/test/expression_command/radar_8638051/main.c new file mode 100644 index 000000000000..1329fd69a2e1 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_8638051/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 + +// This simple program is to demonstrate the capability of the lldb command +// "breakpoint command add" to add a set of commands to a breakpoint to be +// executed when the breakpoint is hit. +// +// In particular, we want to break within c(), but only if the immediate caller +// is a(). + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); // Find the line number where c's parent frame is a here. + + return val; +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/expression_command/radar_9531204/Makefile b/packages/Python/lldbsuite/test/expression_command/radar_9531204/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_9531204/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/radar_9531204/TestPrintfAfterUp.py b/packages/Python/lldbsuite/test/expression_command/radar_9531204/TestPrintfAfterUp.py new file mode 100644 index 000000000000..fb16d2a93326 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_9531204/TestPrintfAfterUp.py @@ -0,0 +1,42 @@ +""" +The evaluating printf(...) after break stop and then up a stack frame. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class Radar9531204TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # rdar://problem/9531204 + @expectedFailureWindows("llvm.org/pr21765") + def test_expr_commands(self): + """The evaluating printf(...) after break stop and then up a stack frame.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_symbol (self, 'foo', sym_exact=True, num_expected_locations=1) + + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("frame variable") + + # This works fine. + self.runCmd('expression (int)printf("value is: %d.\\n", value);') + + # rdar://problem/9531204 + # "Error dematerializing struct" error when evaluating expressions "up" on the stack + self.runCmd('up') # frame select -r 1 + + self.runCmd("frame variable") + + # This does not currently. + self.runCmd('expression (int)printf("argc is: %d.\\n", argc)') diff --git a/packages/Python/lldbsuite/test/expression_command/radar_9531204/main.c b/packages/Python/lldbsuite/test/expression_command/radar_9531204/main.c new file mode 100644 index 000000000000..2bf05f2b3065 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_9531204/main.c @@ -0,0 +1,25 @@ +//===-- 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 + +// breakpoint set -n foo +// +// +int foo (int value) +{ + printf ("I got the value: %d.\n", value); + return 0; +} + +int main (int argc, char **argv) +{ + foo (argc); + printf ("Hello there: %d.\n", argc); + return 0; +} diff --git a/packages/Python/lldbsuite/test/expression_command/radar_9673664/Makefile b/packages/Python/lldbsuite/test/expression_command/radar_9673664/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_9673664/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/radar_9673664/TestExprHelpExamples.py b/packages/Python/lldbsuite/test/expression_command/radar_9673664/TestExprHelpExamples.py new file mode 100644 index 000000000000..ce3e51d05149 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_9673664/TestExprHelpExamples.py @@ -0,0 +1,41 @@ +""" +Test example snippets from the lldb 'help expression' output. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class Radar9673644TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.main_source = "main.c" + self.line = line_number(self.main_source, '// Set breakpoint here.') + + @expectedFailureWindows("llvm.org/pr21765") + def test_expr_commands(self): + """The following expression commands should just work.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, self.main_source, self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # rdar://problem/9673664 lldb expression evaluation problem + + self.expect('expr char c[] = "foo"; c[0]', + substrs = ["'f'"]) + # runCmd: expr char c[] = "foo"; c[0] + # output: (char) $0 = 'f' diff --git a/packages/Python/lldbsuite/test/expression_command/radar_9673664/main.c b/packages/Python/lldbsuite/test/expression_command/radar_9673664/main.c new file mode 100644 index 000000000000..934abdfaef3e --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/radar_9673664/main.c @@ -0,0 +1,16 @@ +//===-- 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[]) +{ + printf("Hello, world.\n"); // Set breakpoint here. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/expression_command/test/Makefile b/packages/Python/lldbsuite/test/expression_command/test/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/test/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/test/TestExprs.py b/packages/Python/lldbsuite/test/expression_command/test/TestExprs.py new file mode 100644 index 000000000000..ec3e7f93af26 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/test/TestExprs.py @@ -0,0 +1,248 @@ +""" +Test many basic expression commands and SBFrame.EvaluateExpression() API. + +Test cases: + +o test_many_expr_commands: + Test many basic expression commands. +o test_evaluate_expression_python: + Use Python APIs (SBFrame.EvaluateExpression()) to evaluate expressions. +o test_expr_commands_can_handle_quotes: + Throw some expression commands with quotes at lldb. +""" + +from __future__ import print_function + + + +import unittest2 + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class BasicExprCommandsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.c. + self.line = line_number('main.cpp', + '// Please test many expressions while stopped at this line:') + + # Disable confirmation prompt to avoid infinite wait + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + + def build_and_run(self): + """These basic expression commands should work as expected.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=False) + + self.runCmd("run", RUN_SUCCEEDED) + + @unittest2.expectedFailure("llvm.org/pr17135 APFloat::toString does not identify the correct (i.e. least) precision.") + def test_floating_point_expr_commands(self): + self.build_and_run() + + self.expect("expression 2.234f", + patterns = ["\(float\) \$.* = 2\.234"]) + # (float) $2 = 2.234 + + @expectedFailureWindows("llvm.org/pr21765") + def test_many_expr_commands(self): + self.build_and_run() + + self.expect("expression 2", + patterns = ["\(int\) \$.* = 2"]) + # (int) $0 = 1 + + self.expect("expression 2ull", + patterns = ["\(unsigned long long\) \$.* = 2"]) + # (unsigned long long) $1 = 2 + + self.expect("expression 0.5f", + patterns = ["\(float\) \$.* = 0\.5"]) + # (float) $2 = 0.5 + + self.expect("expression 2.234", + patterns = ["\(double\) \$.* = 2\.234"]) + # (double) $3 = 2.234 + + self.expect("expression 2+3", + patterns = ["\(int\) \$.* = 5"]) + # (int) $4 = 5 + + self.expect("expression argc", + patterns = ["\(int\) \$.* = 1"]) + # (int) $5 = 1 + + self.expect("expression argc + 22", + patterns = ["\(int\) \$.* = 23"]) + # (int) $6 = 23 + + self.expect("expression argv", + patterns = ["\(const char \*\*\) \$.* = 0x"]) + # (const char *) $7 = ... + + self.expect("expression argv[0]", + substrs = ["(const char *)", + "a.out"]) + # (const char *) $8 = 0x... "/Volumes/data/lldb/svn/trunk/test/expression_command/test/a.out" + + @add_test_categories(['pyapi']) + @expectedFailureWindows # Test crashes + def test_evaluate_expression_python(self): + """Test SBFrame.EvaluateExpression() API for evaluating an expression.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint. + filespec = lldb.SBFileSpec("main.cpp", False) + breakpoint = target.BreakpointCreateByLocation(filespec, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Verify the breakpoint just created. + self.expect(str(breakpoint), BREAKPOINT_CREATED, exe=False, + substrs = ['main.cpp', + str(self.line)]) + + # Launch the process, and do not stop at the entry point. + # Pass 'X Y Z' as the args, which makes argc == 4. + process = target.LaunchSimple (['X', 'Y', 'Z'], None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.LaunchProcess() failed") + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + # The stop reason of the thread should be breakpoint. + thread = process.GetThreadAtIndex(0) + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + from lldbsuite.test.lldbutil import stop_reason_to_str + self.fail(STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS % + stop_reason_to_str(thread.GetStopReason())) + + # The filename of frame #0 should be 'main.cpp' and function is main. + self.expect(lldbutil.get_filenames(thread)[0], + "Break correctly at main.cpp", exe=False, + startstr = "main.cpp") + self.expect(lldbutil.get_function_names(thread)[0], + "Break correctly at main()", exe=False, + startstr = "main") + + # We should be stopped on the breakpoint with a hit count of 1. + self.assertTrue(breakpoint.GetHitCount() == 1, BREAKPOINT_HIT_ONCE) + + # + # Use Python API to evaluate expressions while stopped in a stack frame. + # + frame = thread.GetFrameAtIndex(0) + + val = frame.EvaluateExpression("2.234") + self.expect(val.GetValue(), "2.345 evaluated correctly", exe=False, + startstr = "2.234") + self.expect(val.GetTypeName(), "2.345 evaluated correctly", exe=False, + startstr = "double") + self.DebugSBValue(val) + + val = frame.EvaluateExpression("argc") + self.expect(val.GetValue(), "Argc evaluated correctly", exe=False, + startstr = "4") + self.DebugSBValue(val) + + val = frame.EvaluateExpression("*argv[1]") + self.expect(val.GetValue(), "Argv[1] evaluated correctly", exe=False, + startstr = "'X'") + self.DebugSBValue(val) + + val = frame.EvaluateExpression("*argv[2]") + self.expect(val.GetValue(), "Argv[2] evaluated correctly", exe=False, + startstr = "'Y'") + self.DebugSBValue(val) + + val = frame.EvaluateExpression("*argv[3]") + self.expect(val.GetValue(), "Argv[3] evaluated correctly", exe=False, + startstr = "'Z'") + self.DebugSBValue(val) + + callee_break = target.BreakpointCreateByName ("a_function_to_call", None) + self.assertTrue(callee_break.GetNumLocations() > 0) + + # Make sure ignoring breakpoints works from the command line: + self.expect("expression -i true -- a_function_to_call()", + substrs = ['(int) $', ' 1']) + self.assertTrue (callee_break.GetHitCount() == 1) + + # Now try ignoring breakpoints using the SB API's: + options = lldb.SBExpressionOptions() + options.SetIgnoreBreakpoints(True) + value = frame.EvaluateExpression('a_function_to_call()', options) + self.assertTrue (value.IsValid()) + self.assertTrue (value.GetValueAsSigned(0) == 2) + self.assertTrue (callee_break.GetHitCount() == 2) + + # rdar://problem/8686536 + # CommandInterpreter::HandleCommand is stripping \'s from input for WantsRawCommand commands + @expectedFailureWindows("llvm.org/pr21765") + def test_expr_commands_can_handle_quotes(self): + """Throw some expression commands with quotes at lldb.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.line, num_expected_locations=1,loc_exact=False) + + self.runCmd("run", RUN_SUCCEEDED) + + # runCmd: expression 'a' + # output: (char) $0 = 'a' + self.expect("expression 'a'", + substrs = ['(char) $', + "'a'"]) + + # runCmd: expression (int) printf ("\n\n\tHello there!\n") + # output: (int) $1 = 16 + self.expect(r'''expression (int) printf ("\n\n\tHello there!\n")''', + substrs = ['(int) $', + '16']) + + # runCmd: expression (int) printf("\t\x68\n") + # output: (int) $2 = 3 + self.expect(r'''expression (int) printf("\t\x68\n")''', + substrs = ['(int) $', + '3']) + + # runCmd: expression (int) printf("\"\n") + # output: (int) $3 = 2 + self.expect(r'''expression (int) printf("\"\n")''', + substrs = ['(int) $', + '2']) + + # runCmd: expression (int) printf("'\n") + # output: (int) $4 = 2 + self.expect(r'''expression (int) printf("'\n")''', + substrs = ['(int) $', + '2']) + + # runCmd: command alias print_hi expression (int) printf ("\n\tHi!\n") + # output: + self.runCmd(r'''command alias print_hi expression (int) printf ("\n\tHi!\n")''') + # This fails currently. + self.expect('print_hi', + substrs = ['(int) $', + '6']) diff --git a/packages/Python/lldbsuite/test/expression_command/test/TestExprs2.py b/packages/Python/lldbsuite/test/expression_command/test/TestExprs2.py new file mode 100644 index 000000000000..578a037e9f0f --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/test/TestExprs2.py @@ -0,0 +1,60 @@ +""" +Test some more expression commands. +""" + +from __future__ import print_function + + + +import os +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprCommands2TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.c. + self.line = line_number('main.cpp', + '// Please test many expressions while stopped at this line:') + + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test_more_expr_commands(self): + """Test some more expression commands.""" + self.build() + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.line, num_expected_locations=1,loc_exact=False) + + self.runCmd("run", RUN_SUCCEEDED) + + # Does static casting work? + self.expect("expression (int*)argv", + startstr = "(int *) $0 = 0x") + # (int *) $0 = 0x00007fff5fbff258 + + # Do anonymous symbols work? + self.expect("expression ((char**)environ)[0]", + startstr = "(char *) $1 = 0x") + # (char *) $1 = 0x00007fff5fbff298 "Apple_PubSub_Socket_Render=/tmp/launch-7AEsUD/Render" + + # Do return values containing the contents of expression locals work? + self.expect("expression int i = 5; i", + startstr = "(int) $2 = 5") + # (int) $2 = 5 + self.expect("expression $2 + 1", + startstr = "(int) $3 = 6") + # (int) $3 = 6 + + # Do return values containing the results of static expressions work? + self.expect("expression 20 + 3", + startstr = "(int) $4 = 23") + # (int) $4 = 5 + self.expect("expression $4 + 1", + startstr = "(int) $5 = 24") + # (int) $5 = 6 diff --git a/packages/Python/lldbsuite/test/expression_command/test/main.cpp b/packages/Python/lldbsuite/test/expression_command/test/main.cpp new file mode 100644 index 000000000000..22208a87cb44 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/test/main.cpp @@ -0,0 +1,44 @@ +#include + +static int static_value = 0; + +int +a_function_to_call() +{ + static_value++; + return static_value; +} + +int main (int argc, char const *argv[]) +{ + printf ("Hello world!\n"); + puts ("hello"); + // Please test many expressions while stopped at this line: +#if 0 + expression 'a' // make sure character constant makes it down (this is broken: ) + expression 2 // Test int + expression 2ull // Test unsigned long long + expression 2.234f // Test float constants + expression 2.234 // Test double constants + expression 2+3 + expression argc + expression argc + 22 + expression argv + expression argv[0] + expression argv[1] + expression argv[-1] + expression puts("bonjour") // Test constant strings... + expression printf("\t\x68\n") // Test constant strings that contain the \xXX (TAB, 'h', '\n' should be printed) (this is broken: ) + expression printf("\"\n") // Test constant strings that contains an escaped double quote char (this is broken: ) + expression printf("\'\n") // Test constant strings that contains an escaped single quote char (this is broken: ) + expression printf ("one: %i\n", 1) + expression printf ("1.234 as float: %f\n", 1.234f) + expression printf ("1.234 as double: %g\n", 1.234) + expression printf ("one: %i, two: %llu\n", 1, 2ull) + expression printf ("two: %llu, one: %i\n", 2ull, 1) + expression random() % 255l +#endif + + a_function_to_call(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/expression_command/timeout/Makefile b/packages/Python/lldbsuite/test/expression_command/timeout/Makefile new file mode 100644 index 000000000000..c9cff412c1b2 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/timeout/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := wait-a-while.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/expression_command/timeout/TestCallWithTimeout.py b/packages/Python/lldbsuite/test/expression_command/timeout/TestCallWithTimeout.py new file mode 100644 index 000000000000..a602afc47edb --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/timeout/TestCallWithTimeout.py @@ -0,0 +1,92 @@ +""" +Test calling a function that waits a while, and make sure the timeout option to expr works. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprCommandWithTimeoutsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + self.main_source = "wait-a-while.cpp" + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + + @expectedFlakeyFreeBSD("llvm.org/pr19605") + @expectedFlakeyLinux("llvm.org/pr20275") + @expectedFailureWindows("llvm.org/pr21765") + def test(self): + """Test calling std::String member function.""" + self.build() + + exe_name = "a.out" + exe = os.path.join(os.getcwd(), exe_name) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateBySourceRegex('stop here in main.',self.main_source_spec) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + self.runCmd("breakpoint list") + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.step_out_of_malloc. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + + self.assertTrue(len(threads) == 1) + thread = threads[0] + + # First set the timeout too short, and make sure we fail. + options = lldb.SBExpressionOptions() + options.SetTimeoutInMicroSeconds(10) + options.SetUnwindOnError(True) + + frame = thread.GetFrameAtIndex(0) + + value = frame.EvaluateExpression ("wait_a_while (200000)", options) + self.assertTrue (value.IsValid()) + self.assertFalse (value.GetError().Success()) + + # Now do the same thing with the command line command, and make sure it works too. + interp = self.dbg.GetCommandInterpreter() + + result = lldb.SBCommandReturnObject() + return_value = interp.HandleCommand ("expr -t 100 -u true -- wait_a_while(200000)", result) + self.assertTrue (return_value == lldb.eReturnStatusFailed) + + # Okay, now do it again with long enough time outs: + + options.SetTimeoutInMicroSeconds(1000000) + value = frame.EvaluateExpression ("wait_a_while (1000)", options) + self.assertTrue(value.IsValid()) + self.assertTrue (value.GetError().Success() == True) + + # Now do the same thingwith the command line command, and make sure it works too. + interp = self.dbg.GetCommandInterpreter() + + result = lldb.SBCommandReturnObject() + return_value = interp.HandleCommand ("expr -t 1000000 -u true -- wait_a_while(1000)", result) + self.assertTrue(return_value == lldb.eReturnStatusSuccessFinishResult) + + + # Finally set the one thread timeout and make sure that doesn't change things much: + + options.SetTimeoutInMicroSeconds(1000000) + options.SetOneThreadTimeoutInMicroSeconds(500000) + value = frame.EvaluateExpression ("wait_a_while (1000)", options) + self.assertTrue(value.IsValid()) + self.assertTrue (value.GetError().Success() == True) diff --git a/packages/Python/lldbsuite/test/expression_command/timeout/wait-a-while.cpp b/packages/Python/lldbsuite/test/expression_command/timeout/wait-a-while.cpp new file mode 100644 index 000000000000..ac37c5d243b2 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/timeout/wait-a-while.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include +#include + + +int +wait_a_while (int microseconds) +{ + int num_times = 0; + auto end_time = std::chrono::system_clock::now() + std::chrono::microseconds(microseconds); + + while (1) + { + num_times++; + auto wait_time = end_time - std::chrono::system_clock::now(); + + std::this_thread::sleep_for(wait_time); + if (std::chrono::system_clock::now() > end_time) + break; + } + return num_times; +} + +int +main (int argc, char **argv) +{ + printf ("stop here in main.\n"); + int num_times = wait_a_while (argc * 1000); + printf ("Done, took %d times.\n", num_times); + + return 0; + +} diff --git a/packages/Python/lldbsuite/test/expression_command/two-files/Makefile b/packages/Python/lldbsuite/test/expression_command/two-files/Makefile new file mode 100644 index 000000000000..5974461e256f --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/two-files/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +OBJC_SOURCES := main.m foo.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/expression_command/two-files/TestObjCTypeQueryFromOtherCompileUnit.py b/packages/Python/lldbsuite/test/expression_command/two-files/TestObjCTypeQueryFromOtherCompileUnit.py new file mode 100644 index 000000000000..5b0233509112 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/two-files/TestObjCTypeQueryFromOtherCompileUnit.py @@ -0,0 +1,39 @@ +""" +Regression test for : + +The expression parser's type search only looks in the current compilation unit for types. +""" + +from __future__ import print_function + + + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ObjCTypeQueryTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.m. + self.line = line_number('main.m', + "// Set breakpoint here, then do 'expr (NSArray*)array_token'.") + + @skipUnlessDarwin + def test(self): + """The expression parser's type search should be wider than the current compilation unit.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Now do a NSArry type query from the 'main.m' compile uint. + self.expect("expression (NSArray*)array_token", + substrs = ['(NSArray *) $0 = 0x']) + # (NSArray *) $0 = 0x00007fff70118398 diff --git a/packages/Python/lldbsuite/test/expression_command/two-files/foo.m b/packages/Python/lldbsuite/test/expression_command/two-files/foo.m new file mode 100644 index 000000000000..1609ebd838f6 --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/two-files/foo.m @@ -0,0 +1,28 @@ +#import + +NSMutableArray * +GetArray () +{ + static NSMutableArray *the_array = NULL; + if (the_array == NULL) + the_array = [[NSMutableArray alloc] init]; + return the_array; +} + +int +AddElement (char *value) +{ + NSString *element = [NSString stringWithUTF8String: value]; + int cur_elem = [GetArray() count]; + [GetArray() addObject: element]; + return cur_elem; +} + +const char * +GetElement (int idx) +{ + if (idx >= [GetArray() count]) + return NULL; + else + return [[GetArray() objectAtIndex: idx] UTF8String]; +} diff --git a/packages/Python/lldbsuite/test/expression_command/two-files/main.m b/packages/Python/lldbsuite/test/expression_command/two-files/main.m new file mode 100644 index 000000000000..3f5738314e6b --- /dev/null +++ b/packages/Python/lldbsuite/test/expression_command/two-files/main.m @@ -0,0 +1,22 @@ +#import +#include + +extern int AddElement (char *value); +extern char *GetElement (int idx); +extern void *GetArray(); + +int +main () +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + int idx = AddElement ("some string"); + void *array_token = GetArray(); + + char *string = GetElement (0); // Set breakpoint here, then do 'expr (NSArray*)array_token'. + if (string) + printf ("This: %s.\n", string); + + [pool release]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/abbreviation/.categories b/packages/Python/lldbsuite/test/functionalities/abbreviation/.categories new file mode 100644 index 000000000000..3a3f4df6416b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/abbreviation/.categories @@ -0,0 +1 @@ +cmdline diff --git a/packages/Python/lldbsuite/test/functionalities/abbreviation/TestAbbreviations.py b/packages/Python/lldbsuite/test/functionalities/abbreviation/TestAbbreviations.py new file mode 100644 index 000000000000..1bce5bed491f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/abbreviation/TestAbbreviations.py @@ -0,0 +1,101 @@ +""" +Test some lldb command abbreviations and aliases for proper resolution. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class AbbreviationsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFlakeyFreeBSD("llvm.org/pr22611 thread race condition breaks prompt setting") + @no_debug_info_test + def test_command_abbreviations_and_aliases (self): + command_interpreter = self.dbg.GetCommandInterpreter() + self.assertTrue(command_interpreter, VALID_COMMAND_INTERPRETER) + result = lldb.SBCommandReturnObject() + + # Check that abbreviations are expanded to the full command. + command_interpreter.ResolveCommand("ap script", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("apropos script", result.GetOutput()) + + command_interpreter.ResolveCommand("h", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("help", result.GetOutput()) + + # Check resolution of abbreviations for multi-word commands. + command_interpreter.ResolveCommand("lo li", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("log list", result.GetOutput()) + + command_interpreter.ResolveCommand("br s", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("breakpoint set", result.GetOutput()) + + # Try an ambiguous abbreviation. + # "pl" could be "platform" or "plugin". + command_interpreter.ResolveCommand("pl", result) + self.assertFalse(result.Succeeded()) + self.assertTrue(result.GetError().startswith("Ambiguous command")) + + # Make sure an unabbreviated command is not mangled. + command_interpreter.ResolveCommand("breakpoint set --name main --line 123", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("breakpoint set --name main --line 123", result.GetOutput()) + + # Create some aliases. + self.runCmd("com a alias com al") + self.runCmd("alias gurp help") + + # Check that an alias is replaced with the actual command + command_interpreter.ResolveCommand("gurp target create", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("help target create", result.GetOutput()) + + # Delete the alias and make sure it no longer has an effect. + self.runCmd("com u gurp") + command_interpreter.ResolveCommand("gurp", result) + self.assertFalse(result.Succeeded()) + + # Check aliases with text replacement. + self.runCmd("alias pltty process launch -s -o %1 -e %1") + command_interpreter.ResolveCommand("pltty /dev/tty0", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("process launch -s -o /dev/tty0 -e /dev/tty0", result.GetOutput()) + + self.runCmd("alias xyzzy breakpoint set -n %1 -l %2") + command_interpreter.ResolveCommand("xyzzy main 123", result) + self.assertTrue(result.Succeeded()) + self.assertEqual("breakpoint set -n main -l 123", result.GetOutput().strip()) + + # And again, without enough parameters. + command_interpreter.ResolveCommand("xyzzy main", result) + self.assertFalse(result.Succeeded()) + + # Check a command that wants the raw input. + command_interpreter.ResolveCommand(r'''sc print("\n\n\tHello!\n")''', result) + self.assertTrue(result.Succeeded()) + self.assertEqual(r'''script print("\n\n\tHello!\n")''', result.GetOutput()) + + # Prompt changing stuff should be tested, but this doesn't seem like the + # right test to do it in. It has nothing to do with aliases or abbreviations. + #self.runCmd("com sou ./change_prompt.lldb") + #self.expect("settings show prompt", + # startstr = 'prompt (string) = "[with-three-trailing-spaces] "') + #self.runCmd("settings clear prompt") + #self.expect("settings show prompt", + # startstr = 'prompt (string) = "(lldb) "') + #self.runCmd("se se prompt 'Sycamore> '") + #self.expect("se sh prompt", + # startstr = 'prompt (string) = "Sycamore> "') + #self.runCmd("se cl prompt") + #self.expect("set sh prompt", + # startstr = 'prompt (string) = "(lldb) "') diff --git a/packages/Python/lldbsuite/test/functionalities/abbreviation/TestCommonShortSpellings.py b/packages/Python/lldbsuite/test/functionalities/abbreviation/TestCommonShortSpellings.py new file mode 100644 index 000000000000..9edbf212278c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/abbreviation/TestCommonShortSpellings.py @@ -0,0 +1,39 @@ +""" +Test some lldb command abbreviations to make sure the common short spellings of +many commands remain available even after we add/delete commands in the future. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CommonShortSpellingsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_abbrevs2 (self): + command_interpreter = self.dbg.GetCommandInterpreter() + self.assertTrue(command_interpreter, VALID_COMMAND_INTERPRETER) + result = lldb.SBCommandReturnObject() + + abbrevs = [ + ('br s', 'breakpoint set'), + ('disp', '_regexp-display'), # a.k.a., 'display' + ('di', 'disassemble'), + ('dis', 'disassemble'), + ('ta st a', 'target stop-hook add'), + ('fr v', 'frame variable'), + ('f 1', 'frame select 1'), + ('ta st li', 'target stop-hook list'), + ] + + for (short_val, long_val) in abbrevs: + command_interpreter.ResolveCommand(short_val, result) + self.assertTrue(result.Succeeded()) + self.assertEqual(long_val, result.GetOutput()) diff --git a/packages/Python/lldbsuite/test/functionalities/alias/.categories b/packages/Python/lldbsuite/test/functionalities/alias/.categories new file mode 100644 index 000000000000..3a3f4df6416b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/alias/.categories @@ -0,0 +1 @@ +cmdline diff --git a/packages/Python/lldbsuite/test/functionalities/archives/Makefile b/packages/Python/lldbsuite/test/functionalities/archives/Makefile new file mode 100644 index 000000000000..64da83becbda --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/archives/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +MAKE_DSYM := NO +ARCHIVE_NAME := libfoo.a +ARCHIVE_C_SOURCES := a.c b.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/archives/README b/packages/Python/lldbsuite/test/functionalities/archives/README new file mode 100644 index 000000000000..d327f4585c67 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/archives/README @@ -0,0 +1,62 @@ +a.out file refers to libfoo.a for a.o and b.o, which is what we want to accomplish for +this test case. + +[16:17:44] johnny:/Volumes/data/lldb/svn/latest/test/functionalities/archives $ dsymutil -s a.out +---------------------------------------------------------------------- +Symbol table for: 'a.out' (x86_64) +---------------------------------------------------------------------- +Index n_strx n_type n_sect n_desc n_value +======== -------- ------------------ ------ ------ ---------------- +[ 0] 00000002 64 (N_SO ) 00 0000 0000000000000000 '/Volumes/data/lldb/svn/latest/test/functionalities/archives/' +[ 1] 0000003f 64 (N_SO ) 00 0000 0000000000000000 'main.c' +[ 2] 00000046 66 (N_OSO ) 03 0001 000000004f0f780c '/Volumes/data/lldb/svn/latest/test/functionalities/archives/main.o' +[ 3] 00000001 2e (N_BNSYM ) 01 0000 0000000100000d70 +[ 4] 00000089 24 (N_FUN ) 01 0000 0000000100000d70 '_main' +[ 5] 00000001 24 (N_FUN ) 00 0000 000000000000005d +[ 6] 00000001 4e (N_ENSYM ) 01 0000 000000000000005d +[ 7] 00000001 64 (N_SO ) 01 0000 0000000000000000 +[ 8] 00000002 64 (N_SO ) 00 0000 0000000000000000 '/Volumes/data/lldb/svn/latest/test/functionalities/archives/' +[ 9] 0000008f 64 (N_SO ) 00 0000 0000000000000000 'a.c' +[ 10] 00000093 66 (N_OSO ) 03 0001 000000004f0f780c '/Volumes/data/lldb/svn/latest/test/functionalities/archives/libfoo.a(a.o)' +[ 11] 00000001 2e (N_BNSYM ) 01 0000 0000000100000dd0 +[ 12] 000000dd 24 (N_FUN ) 01 0000 0000000100000dd0 '_a' +[ 13] 00000001 24 (N_FUN ) 00 0000 0000000000000020 +[ 14] 00000001 4e (N_ENSYM ) 01 0000 0000000000000020 +[ 15] 00000001 2e (N_BNSYM ) 01 0000 0000000100000df0 +[ 16] 000000e0 24 (N_FUN ) 01 0000 0000000100000df0 '_aa' +[ 17] 00000001 24 (N_FUN ) 00 0000 0000000000000018 +[ 18] 00000001 4e (N_ENSYM ) 01 0000 0000000000000018 +[ 19] 000000e4 20 (N_GSYM ) 00 0000 0000000000000000 '___a_global' +[ 20] 00000001 64 (N_SO ) 01 0000 0000000000000000 +[ 21] 00000002 64 (N_SO ) 00 0000 0000000000000000 '/Volumes/data/lldb/svn/latest/test/functionalities/archives/' +[ 22] 000000f0 64 (N_SO ) 00 0000 0000000000000000 'b.c' +[ 23] 000000f4 66 (N_OSO ) 03 0001 000000004f0f780c '/Volumes/data/lldb/svn/latest/test/functionalities/archives/libfoo.a(b.o)' +[ 24] 00000001 2e (N_BNSYM ) 01 0000 0000000100000e10 +[ 25] 0000013e 24 (N_FUN ) 01 0000 0000000100000e10 '_b' +[ 26] 00000001 24 (N_FUN ) 00 0000 0000000000000020 +[ 27] 00000001 4e (N_ENSYM ) 01 0000 0000000000000020 +[ 28] 00000001 2e (N_BNSYM ) 01 0000 0000000100000e30 +[ 29] 00000141 24 (N_FUN ) 01 0000 0000000100000e30 '_bb' +[ 30] 00000001 24 (N_FUN ) 00 0000 0000000000000018 +[ 31] 00000001 4e (N_ENSYM ) 01 0000 0000000000000018 +[ 32] 00000145 26 (N_STSYM ) 0a 0000 000000010000104c '___b_global' +[ 33] 00000001 64 (N_SO ) 01 0000 0000000000000000 +[ 34] 00000151 0e ( SECT ) 07 0000 0000000100001000 '_pvars' +[ 35] 00000158 0e ( SECT ) 0a 0000 000000010000104c '___b_global' +[ 36] 00000164 0f ( SECT EXT) 0b 0000 0000000100001050 '_NXArgc' +[ 37] 0000016c 0f ( SECT EXT) 0b 0000 0000000100001058 '_NXArgv' +[ 38] 00000174 0f ( SECT EXT) 0a 0000 0000000100001048 '___a_global' +[ 39] 00000180 0f ( SECT EXT) 0b 0000 0000000100001068 '___progname' +[ 40] 0000018c 03 ( ABS EXT) 01 0010 0000000100000000 '__mh_execute_header' +[ 41] 000001a0 0f ( SECT EXT) 01 0000 0000000100000dd0 '_a' +[ 42] 000001a3 0f ( SECT EXT) 01 0000 0000000100000df0 '_aa' +[ 43] 000001a7 0f ( SECT EXT) 01 0000 0000000100000e10 '_b' +[ 44] 000001aa 0f ( SECT EXT) 01 0000 0000000100000e30 '_bb' +[ 45] 000001ae 0f ( SECT EXT) 0b 0000 0000000100001060 '_environ' +[ 46] 000001b7 0f ( SECT EXT) 01 0000 0000000100000d70 '_main' +[ 47] 000001bd 0f ( SECT EXT) 01 0000 0000000100000d30 'start' +[ 48] 000001c3 01 ( UNDF EXT) 00 0100 0000000000000000 '_exit' +[ 49] 000001c9 01 ( UNDF EXT) 00 0100 0000000000000000 '_printf' +[ 50] 000001d1 01 ( UNDF EXT) 00 0100 0000000000000000 'dyld_stub_binder' + + diff --git a/packages/Python/lldbsuite/test/functionalities/archives/TestBSDArchives.py b/packages/Python/lldbsuite/test/functionalities/archives/TestBSDArchives.py new file mode 100644 index 000000000000..02e1b9551853 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/archives/TestBSDArchives.py @@ -0,0 +1,57 @@ +"""Test breaking inside functions defined within a BSD archive file libfoo.a.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BSDArchivesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number in a(int) to break at. + self.line = line_number('a.c', '// Set file and line breakpoint inside a().') + + @expectedFailureWindows("llvm.org/pr24527") # Makefile.rules doesn't know how to build static libs on Windows. + def test(self): + """Break inside a() and b() defined within libfoo.a.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside a() by file and line first. + lldbutil.run_break_set_by_file_and_line (self, "a.c", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Break at a(int) first. + self.expect("frame variable", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int) arg = 1']) + self.expect("frame variable __a_global", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int) __a_global = 1']) + + # Set breakpoint for b() next. + lldbutil.run_break_set_by_symbol (self, "b", num_expected_locations=1, sym_exact=True) + + # Continue the program, we should break at b(int) next. + self.runCmd("continue") + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + self.expect("frame variable", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int) arg = 2']) + self.expect("frame variable __b_global", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int) __b_global = 2']) diff --git a/packages/Python/lldbsuite/test/functionalities/archives/a.c b/packages/Python/lldbsuite/test/functionalities/archives/a.c new file mode 100644 index 000000000000..2b6ebbe47a76 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/archives/a.c @@ -0,0 +1,19 @@ +//===-- 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 __a_global = 1; + +int a(int arg) { + int result = arg + __a_global; + return result; // Set file and line breakpoint inside a(). +} + +int aa(int arg1) { + int result1 = arg1 - __a_global; + return result1; +} diff --git a/packages/Python/lldbsuite/test/functionalities/archives/b.c b/packages/Python/lldbsuite/test/functionalities/archives/b.c new file mode 100644 index 000000000000..51d77dd4bcdc --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/archives/b.c @@ -0,0 +1,19 @@ +//===-- b.c -----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +static int __b_global = 2; + +int b(int arg) { + int result = arg + __b_global; + return result; +} + +int bb(int arg1) { + int result2 = arg1 - __b_global; + return result2; +} diff --git a/packages/Python/lldbsuite/test/functionalities/archives/main.c b/packages/Python/lldbsuite/test/functionalities/archives/main.c new file mode 100644 index 000000000000..c5b1cc2f0d1c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/archives/main.c @@ -0,0 +1,17 @@ +//===-- 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 + +extern int a(int); +extern int b(int); +int main (int argc, char const *argv[]) +{ + printf ("a(1) returns %d\n", a(1)); + printf ("b(2) returns %d\n", b(2)); +} diff --git a/packages/Python/lldbsuite/test/functionalities/asan/Makefile b/packages/Python/lldbsuite/test/functionalities/asan/Makefile new file mode 100644 index 000000000000..26654a023ed1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/asan/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +C_SOURCES := main.c +CFLAGS_EXTRAS := -fsanitize=address -g + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/asan/TestMemoryHistory.py b/packages/Python/lldbsuite/test/functionalities/asan/TestMemoryHistory.py new file mode 100644 index 000000000000..e92b967d8adf --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/asan/TestMemoryHistory.py @@ -0,0 +1,103 @@ +""" +Test that ASan memory history provider returns correct stack traces +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class AsanTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureLinux # non-core functionality, need to reenable and fix later (DES 2014.11.07) + @skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default + @skipIfRemote + @skipUnlessCompilerRt + @expectedFailureDarwin + def test (self): + self.build () + self.asan_tests () + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + self.line_malloc = line_number('main.c', '// malloc line') + self.line_malloc2 = line_number('main.c', '// malloc2 line') + self.line_free = line_number('main.c', '// free line') + self.line_breakpoint = line_number('main.c', '// break line') + + def asan_tests (self): + exe = os.path.join (os.getcwd(), "a.out") + self.expect("file " + exe, patterns = [ "Current executable set to .*a.out" ]) + + self.runCmd("breakpoint set -f main.c -l %d" % self.line_breakpoint) + + # "memory history" command should not work without a process + self.expect("memory history 0", + error = True, + substrs = ["invalid process"]) + + self.runCmd("run") + + # ASan will relaunch the process to insert its library. + self.expect("thread list", "Process should be stopped due to exec.", + substrs = ['stopped', 'stop reason = ']) + + self.runCmd("continue") + + # the stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # test that the ASan dylib is present + self.expect("image lookup -n __asan_describe_address", "__asan_describe_address should be present", + substrs = ['1 match found']) + + # test the 'memory history' command + self.expect("memory history 'pointer'", + substrs = [ + 'Memory allocated at', 'a.out`f1', 'main.c:%d' % self.line_malloc, + 'Memory deallocated at', 'a.out`f2', 'main.c:%d' % self.line_free]) + + # do the same using SB API + process = self.dbg.GetSelectedTarget().process + val = process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer") + addr = val.GetValueAsUnsigned() + threads = process.GetHistoryThreads(addr); + self.assertEqual(threads.GetSize(), 2) + + history_thread = threads.GetThreadAtIndex(0) + self.assertTrue(history_thread.num_frames >= 2) + self.assertEqual(history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), "main.c") + self.assertEqual(history_thread.frames[1].GetLineEntry().GetLine(), self.line_free) + + history_thread = threads.GetThreadAtIndex(1) + self.assertTrue(history_thread.num_frames >= 2) + self.assertEqual(history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), "main.c") + self.assertEqual(history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc) + + # let's free the container (SBThreadCollection) and see if the SBThreads still live + threads = None + self.assertTrue(history_thread.num_frames >= 2) + self.assertEqual(history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), "main.c") + self.assertEqual(history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc) + + # now let's break when an ASan report occurs and try the API then + self.runCmd("breakpoint set -n __asan_report_error") + + self.runCmd("continue") + + # the stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # make sure the 'memory history' command still works even when we're generating a report now + self.expect("memory history 'another_pointer'", + substrs = [ + 'Memory allocated at', 'a.out`f1', 'main.c:%d' % self.line_malloc2]) diff --git a/packages/Python/lldbsuite/test/functionalities/asan/TestReportData.py b/packages/Python/lldbsuite/test/functionalities/asan/TestReportData.py new file mode 100644 index 000000000000..4ef587895196 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/asan/TestReportData.py @@ -0,0 +1,79 @@ +""" +Test the AddressSanitizer runtime support for report breakpoint and data extraction. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import json + +class AsanTestReportDataCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureLinux # non-core functionality, need to reenable and fix later (DES 2014.11.07) + @skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default + @skipIfRemote + @skipUnlessCompilerRt + @expectedFailureDarwin + def test(self): + self.build () + self.asan_tests () + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + self.line_malloc = line_number('main.c', '// malloc line') + self.line_malloc2 = line_number('main.c', '// malloc2 line') + self.line_free = line_number('main.c', '// free line') + self.line_breakpoint = line_number('main.c', '// break line') + self.line_crash = line_number('main.c', '// BOOM line') + + def asan_tests (self): + exe = os.path.join (os.getcwd(), "a.out") + self.expect("file " + exe, patterns = [ "Current executable set to .*a.out" ]) + self.runCmd("run") + + # ASan will relaunch the process to insert its library. + self.expect("thread list", "Process should be stopped due to exec.", + substrs = ['stopped', 'stop reason = ']) + + # no extended info when we have no ASan report + thread = self.dbg.GetSelectedTarget().process.GetSelectedThread() + s = lldb.SBStream() + self.assertFalse(thread.GetStopReasonExtendedInfoAsJSON(s)) + + self.runCmd("continue") + + self.expect("thread list", "Process should be stopped due to ASan report", + substrs = ['stopped', 'stop reason = Use of deallocated memory detected']) + + self.assertEqual(self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason(), lldb.eStopReasonInstrumentation) + + self.expect("bt", "The backtrace should show the crashing line", + substrs = ['main.c:%d' % self.line_crash]) + + self.expect("thread info -s", "The extended stop info should contain the ASan provided fields", + substrs = ["access_size", "access_type", "address", "pc", "description", "heap-use-after-free"]) + + output_lines = self.res.GetOutput().split('\n') + json_line = output_lines[2] + data = json.loads(json_line) + self.assertEqual(data["description"], "heap-use-after-free") + self.assertEqual(data["instrumentation_class"], "AddressSanitizer") + self.assertEqual(data["stop_type"], "fatal_error") + + # now let's try the SB API + process = self.dbg.GetSelectedTarget().process + thread = process.GetSelectedThread() + + s = lldb.SBStream() + self.assertTrue(thread.GetStopReasonExtendedInfoAsJSON(s)) + s = s.GetData() + data2 = json.loads(s) + self.assertEqual(data, data2) diff --git a/packages/Python/lldbsuite/test/functionalities/asan/main.c b/packages/Python/lldbsuite/test/functionalities/asan/main.c new file mode 100644 index 000000000000..fab760e49f00 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/asan/main.c @@ -0,0 +1,34 @@ +//===-- 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 + +char *pointer; +char *another_pointer; + +void f1() { + pointer = malloc(10); // malloc line + another_pointer = malloc(20); // malloc2 line +} + +void f2() { + free(pointer); // free line +} + +int main (int argc, char const *argv[]) +{ + f1(); + f2(); + + printf("Hello world!\n"); // break line + + pointer[0] = 'A'; // BOOM line + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/attach_resume/Makefile b/packages/Python/lldbsuite/test/functionalities/attach_resume/Makefile new file mode 100644 index 000000000000..13d40a13b3e3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/attach_resume/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +EXE := AttachResume + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/attach_resume/TestAttachResume.py b/packages/Python/lldbsuite/test/functionalities/attach_resume/TestAttachResume.py new file mode 100644 index 000000000000..693c0a70fd62 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/attach_resume/TestAttachResume.py @@ -0,0 +1,73 @@ +""" +Test process attach/resume. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +exe_name = "AttachResume" # Must match Makefile + +class AttachResumeTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfRemote + @expectedFailureFreeBSD('llvm.org/pr19310') + @expectedFailureWindows("llvm.org/pr24778") + def test_attach_continue_interrupt_detach(self): + """Test attach/continue/interrupt/detach""" + self.build() + self.process_attach_continue_interrupt_detach() + + def process_attach_continue_interrupt_detach(self): + """Test attach/continue/interrupt/detach""" + + exe = os.path.join(os.getcwd(), exe_name) + + popen = self.spawnSubprocess(exe) + self.addTearDownHook(self.cleanupSubprocesses) + + self.runCmd("process attach -p " + str(popen.pid)) + + self.setAsync(True) + listener = self.dbg.GetListener() + + self.runCmd("c") + lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning]) + + self.runCmd("process interrupt") + lldbutil.expect_state_changes(self, listener, [lldb.eStateStopped]) + + # be sure to continue/interrupt/continue (r204504) + self.runCmd("c") + lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning]) + + self.runCmd("process interrupt") + lldbutil.expect_state_changes(self, listener, [lldb.eStateStopped]) + + # Second interrupt should have no effect. + self.expect("process interrupt", patterns=["Process is not running"], error=True) + + # check that this breakpoint is auto-cleared on detach (r204752) + self.runCmd("br set -f main.cpp -l %u" % (line_number('main.cpp', '// Set breakpoint here'))) + + self.runCmd("c") + lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning, lldb.eStateStopped]) + self.expect('br list', 'Breakpoint not hit', + substrs = ['hit count = 1']) + + # Make sure the breakpoint is not hit again. + self.expect("expr debugger_flag = false", substrs=[" = false"]); + + self.runCmd("c") + lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning]) + + # make sure to detach while in running state (r204759) + self.runCmd("detach") + lldbutil.expect_state_changes(self, listener, [lldb.eStateDetached]) diff --git a/packages/Python/lldbsuite/test/functionalities/attach_resume/main.cpp b/packages/Python/lldbsuite/test/functionalities/attach_resume/main.cpp new file mode 100644 index 000000000000..7cf360258546 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/attach_resume/main.cpp @@ -0,0 +1,50 @@ +#include +#include + +#include +#include + +#if defined(__linux__) +#include +#endif + +volatile bool debugger_flag = true; // The debugger will flip this to false + +void *start(void *data) +{ + int i; + size_t idx = (size_t)data; + for (i=0; i<30; i++) + { + if ( idx == 0 && debugger_flag) + std::this_thread::sleep_for(std::chrono::microseconds(1)); // Set breakpoint here + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + return 0; +} + +int main(int argc, char const *argv[]) +{ +#if defined(__linux__) + // Immediately enable any ptracer so that we can allow the stub attach + // operation to succeed. Some Linux kernels are locked down so that + // only an ancestor process can be a ptracer of a process. This disables that + // restriction. Without it, attach-related stub tests will fail. +#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY) + // For now we execute on best effort basis. If this fails for + // some reason, so be it. + const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); + static_cast (prctl_result); +#endif +#endif + + static const size_t nthreads = 16; + std::thread threads[nthreads]; + size_t i; + + for (i=0; i= (2, 7, 8) and sys.version_info < (2, 7, 10) + + +class AvoidsFdLeakTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailure(python_leaky_fd_version, "bugs.freebsd.org/197376") + @expectedFailureFreeBSD("llvm.org/pr25624 still failing with Python 2.7.10") + @skipIfWindows # The check for descriptor leakage needs to be implemented differently here. + @skipIfTargetAndroid() # Android have some other file descriptors open by the shell + def test_fd_leak_basic (self): + self.do_test([]) + + @expectedFailure(python_leaky_fd_version, "bugs.freebsd.org/197376") + @expectedFailureFreeBSD("llvm.org/pr25624 still failing with Python 2.7.10") + @skipIfWindows # The check for descriptor leakage needs to be implemented differently here. + @skipIfTargetAndroid() # Android have some other file descriptors open by the shell + def test_fd_leak_log (self): + self.do_test(["log enable -f '/dev/null' lldb commands"]) + + def do_test (self, commands): + self.build() + exe = os.path.join (os.getcwd(), "a.out") + + for c in commands: + self.runCmd(c) + + target = self.dbg.CreateTarget(exe) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + self.assertTrue(process.GetState() == lldb.eStateExited, "Process should have exited.") + self.assertTrue(process.GetExitStatus() == 0, + "Process returned non-zero status. Were incorrect file descriptors passed?") + + @expectedFailure(python_leaky_fd_version, "bugs.freebsd.org/197376") + @expectedFailureFreeBSD("llvm.org/pr25624 still failing with Python 2.7.10") + @expectedFlakeyLinux + @skipIfWindows # The check for descriptor leakage needs to be implemented differently here. + @skipIfTargetAndroid() # Android have some other file descriptors open by the shell + def test_fd_leak_multitarget (self): + self.build() + exe = os.path.join (os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + breakpoint = target.BreakpointCreateBySourceRegex ('Set breakpoint here', lldb.SBFileSpec ("main.c", False)) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + process1 = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process1, PROCESS_IS_VALID) + self.assertTrue(process1.GetState() == lldb.eStateStopped, "Process should have been stopped.") + + target2 = self.dbg.CreateTarget(exe) + process2 = target2.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process2, PROCESS_IS_VALID) + + self.assertTrue(process2.GetState() == lldb.eStateExited, "Process should have exited.") + self.assertTrue(process2.GetExitStatus() == 0, + "Process returned non-zero status. Were incorrect file descriptors passed?") diff --git a/packages/Python/lldbsuite/test/functionalities/avoids-fd-leak/main.c b/packages/Python/lldbsuite/test/functionalities/avoids-fd-leak/main.c new file mode 100644 index 000000000000..5bdf227928ed --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/avoids-fd-leak/main.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +int +main (int argc, char const **argv) +{ + struct stat buf; + int i, rv = 0; // Set breakpoint here. + + // Make sure stdin/stdout/stderr exist. + for (i = 0; i <= 2; ++i) { + if (fstat(i, &buf) != 0) + return 1; + } + + // Make sure no other file descriptors are open. + for (i = 3; i <= 256; ++i) { + if (fstat(i, &buf) == 0 || errno != EBADF) { + fprintf(stderr, "File descriptor %d is open.\n", i); + rv = 2; + } + } + + return rv; +} diff --git a/packages/Python/lldbsuite/test/functionalities/backticks/.categories b/packages/Python/lldbsuite/test/functionalities/backticks/.categories new file mode 100644 index 000000000000..3a3f4df6416b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/backticks/.categories @@ -0,0 +1 @@ +cmdline diff --git a/packages/Python/lldbsuite/test/functionalities/backticks/TestBackticksWithoutATarget.py b/packages/Python/lldbsuite/test/functionalities/backticks/TestBackticksWithoutATarget.py new file mode 100644 index 000000000000..d31412bc783c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/backticks/TestBackticksWithoutATarget.py @@ -0,0 +1,21 @@ +""" +Test that backticks without a target should work (not infinite looping). +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class BackticksWithNoTargetTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_backticks_no_target(self): + """A simple test of backticks without a target.""" + self.expect("print `1+2-3`", + substrs = [' = 0']) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/Makefile new file mode 100644 index 000000000000..6067ee45e984 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +CFLAGS_EXTRAS += -std=c99 + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestAddressBreakpoints.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestAddressBreakpoints.py new file mode 100644 index 000000000000..9442a076e2a3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestAddressBreakpoints.py @@ -0,0 +1,91 @@ +""" +Test address breakpoints set with shared library of SBAddress work correctly. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class AddressBreakpointTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_address_breakpoints (self): + """Test address breakpoints set with shared library of SBAddress work correctly.""" + self.build() + self.address_breakpoints() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def address_breakpoints(self): + """Test address breakpoints set with shared library of SBAddress work correctly.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateBySourceRegex("Set a breakpoint here", lldb.SBFileSpec("main.c")) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Get the breakpoint location from breakpoint after we verified that, + # indeed, it has one location. + location = breakpoint.GetLocationAtIndex(0) + self.assertTrue(location and + location.IsEnabled(), + VALID_BREAKPOINT_LOCATION) + + # Next get the address from the location, and create an address breakpoint using + # that address: + + address = location.GetAddress() + target.BreakpointDelete(breakpoint.GetID()) + + breakpoint = target.BreakpointCreateBySBAddress(address) + + # Disable ASLR. This will allow us to actually test (on platforms that support this flag) + # that the breakpoint was able to track the module. + + launch_info = lldb.SBLaunchInfo(None) + flags = launch_info.GetLaunchFlags() + flags &= ~lldb.eLaunchFlagDisableASLR + launch_info.SetLaunchFlags(flags) + + error = lldb.SBError() + + process = target.Launch (launch_info, error) + self.assertTrue(process, PROCESS_IS_VALID) + + # Did we hit our breakpoint? + from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint + threads = get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue(len(threads) == 1, "There should be a thread stopped at our breakpoint") + + # The hit count for the breakpoint should be 1. + self.assertTrue(breakpoint.GetHitCount() == 1) + + process.Kill() + + # Now re-launch and see that we hit the breakpoint again: + launch_info.Clear() + launch_info.SetLaunchFlags(flags) + + process = target.Launch(launch_info, error) + self.assertTrue (process, PROCESS_IS_VALID) + + thread = get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue(len(threads) == 1, "There should be a thread stopped at our breakpoint") + + # The hit count for the breakpoint should now be 2. + self.assertTrue(breakpoint.GetHitCount() == 2) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/main.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/main.c new file mode 100644 index 000000000000..6b779296e188 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/main.c @@ -0,0 +1,8 @@ +#include + +int +main() +{ + printf ("Set a breakpoint here.\n"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/Makefile new file mode 100644 index 000000000000..a6376f9b165d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c a.c b.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py new file mode 100644 index 000000000000..8cf7539432ba --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py @@ -0,0 +1,206 @@ +""" +Test lldb breakpoint command add/list/delete. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @classmethod + def classCleanup(cls): + """Cleanup the test byproduct of breakpoint_command_sequence(self).""" + cls.RemoveTempFile("output.txt") + cls.RemoveTempFile("output2.txt") + + @expectedFailureWindows("llvm.org/pr24528") + def test(self): + """Test a sequence of breakpoint command add, list, and delete.""" + self.build() + self.breakpoint_command_sequence() + self.breakpoint_command_script_parameters () + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set break point at this line.') + # disable "There is a running process, kill it and restart?" prompt + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + def breakpoint_command_sequence(self): + """Test a sequence of breakpoint command add, list, and delete.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add three breakpoints on the same line. The first time we don't specify the file, + # since the default file is the one containing main: + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1, loc_exact=True) + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + # Breakpoint 4 - set at the same location as breakpoint 1 to test setting breakpoint commands on two breakpoints at a time + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1, loc_exact=True) + + # Now add callbacks for the breakpoints just created. + self.runCmd("breakpoint command add -s command -o 'frame variable --show-types --scope' 1 4") + self.runCmd("breakpoint command add -s python -o 'here = open(\"output.txt\", \"w\"); here.write(\"lldb\\n\"); here.close()' 2") + self.runCmd("breakpoint command add --python-function bktptcmd.function 3") + + # Check that the breakpoint commands are correctly set. + + # The breakpoint list now only contains breakpoint 1. + self.expect("breakpoint list", "Breakpoints 1 & 2 created", + substrs = ["2: file = 'main.c', line = %d, exact_match = 0, locations = 1" % self.line], + patterns = ["1: file = '.*main.c', line = %d, exact_match = 0, locations = 1" % self.line] ) + + self.expect("breakpoint list -f", "Breakpoints 1 & 2 created", + substrs = ["2: file = 'main.c', line = %d, exact_match = 0, locations = 1" % self.line], + patterns = ["1: file = '.*main.c', line = %d, exact_match = 0, locations = 1" % self.line, + "1.1: .+at main.c:%d, .+unresolved, hit count = 0" % self.line, + "2.1: .+at main.c:%d, .+unresolved, hit count = 0" % self.line]) + + self.expect("breakpoint command list 1", "Breakpoint 1 command ok", + substrs = ["Breakpoint commands:", + "frame variable --show-types --scope"]) + self.expect("breakpoint command list 2", "Breakpoint 2 command ok", + substrs = ["Breakpoint commands:", + "here = open", + "here.write", + "here.close()"]) + self.expect("breakpoint command list 3", "Breakpoint 3 command ok", + substrs = ["Breakpoint commands:", + "bktptcmd.function(frame, bp_loc, internal_dict)"]) + + self.expect("breakpoint command list 4", "Breakpoint 4 command ok", + substrs = ["Breakpoint commands:", + "frame variable --show-types --scope"]) + + self.runCmd("breakpoint delete 4") + + self.runCmd("command script import --allow-reload ./bktptcmd.py") + + # Next lets try some other breakpoint kinds. First break with a regular expression + # and then specify only one file. The first time we should get two locations, + # the second time only one: + + lldbutil.run_break_set_by_regexp (self, r"._MyFunction", num_expected_locations=2) + + lldbutil.run_break_set_by_regexp (self, r"._MyFunction", extra_options="-f a.c", num_expected_locations=1) + + lldbutil.run_break_set_by_regexp (self, r"._MyFunction", extra_options="-f a.c -f b.c", num_expected_locations=2) + + # Now try a source regex breakpoint: + lldbutil.run_break_set_by_source_regexp (self, r"is about to return [12]0", extra_options="-f a.c -f b.c", num_expected_locations=2) + + lldbutil.run_break_set_by_source_regexp (self, r"is about to return [12]0", extra_options="-f a.c", num_expected_locations=1) + + # Run the program. Remove 'output.txt' if it exists. + self.RemoveTempFile("output.txt") + self.RemoveTempFile("output2.txt") + self.runCmd("run", RUN_SUCCEEDED) + + # Check that the file 'output.txt' exists and contains the string "lldb". + + # The 'output.txt' file should now exist. + self.assertTrue(os.path.isfile("output.txt"), + "'output.txt' exists due to breakpoint command for breakpoint 2.") + self.assertTrue(os.path.isfile("output2.txt"), + "'output2.txt' exists due to breakpoint command for breakpoint 3.") + + # Read the output file produced by running the program. + with open('output.txt', 'r') as f: + output = f.read() + + self.expect(output, "File 'output.txt' and the content matches", exe=False, + startstr = "lldb") + + with open('output2.txt', 'r') as f: + output = f.read() + + self.expect(output, "File 'output2.txt' and the content matches", exe=False, + startstr = "lldb") + + + # Finish the program. + self.runCmd("process continue") + + # Remove the breakpoint command associated with breakpoint 1. + self.runCmd("breakpoint command delete 1") + + # Remove breakpoint 2. + self.runCmd("breakpoint delete 2") + + self.expect("breakpoint command list 1", + startstr = "Breakpoint 1 does not have an associated command.") + self.expect("breakpoint command list 2", error=True, + startstr = "error: '2' is not a currently valid breakpoint id.") + + # The breakpoint list now only contains breakpoint 1. + self.expect("breakpoint list -f", "Breakpoint 1 exists", + patterns = ["1: file = '.*main.c', line = %d, exact_match = 0, locations = 1, resolved = 1" % + self.line, + "hit count = 1"]) + + # Not breakpoint 2. + self.expect("breakpoint list -f", "No more breakpoint 2", matching=False, + substrs = ["2: file = 'main.c', line = %d, exact_match = 0, locations = 1, resolved = 1" % + self.line]) + + # Run the program again, with breakpoint 1 remaining. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to breakpoint 1. + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 2. + self.expect("breakpoint list -f", BREAKPOINT_HIT_TWICE, + substrs = ['resolved, hit count = 2']) + + def breakpoint_command_script_parameters (self): + """Test that the frame and breakpoint location are being properly passed to the script breakpoint command function.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + + # Now add callbacks for the breakpoints just created. + self.runCmd("breakpoint command add -s python -o 'here = open(\"output-2.txt\", \"w\"); here.write(str(frame) + \"\\n\"); here.write(str(bp_loc) + \"\\n\"); here.close()' 1") + + # Remove 'output-2.txt' if it already exists. + + if (os.path.exists('output-2.txt')): + os.remove ('output-2.txt') + + # Run program, hit breakpoint, and hopefully write out new version of 'output-2.txt' + self.runCmd ("run", RUN_SUCCEEDED) + + # Check that the file 'output.txt' exists and contains the string "lldb". + + # The 'output-2.txt' file should now exist. + self.assertTrue(os.path.isfile("output-2.txt"), + "'output-2.txt' exists due to breakpoint command for breakpoint 1.") + + # Read the output file produced by running the program. + with open('output-2.txt', 'r') as f: + output = f.read() + + self.expect (output, "File 'output-2.txt' and the content matches", exe=False, + startstr = "frame #0:", + patterns = ["1.* where = .*main .* resolved, hit count = 1" ]) + + # Now remove 'output-2.txt' + os.remove ('output-2.txt') diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py new file mode 100644 index 000000000000..7a9cc744d528 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py @@ -0,0 +1,95 @@ +""" +Test that you can set breakpoint commands successfully with the Python API's: +""" + +from __future__ import print_function + + + +import os +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +import sys +from lldbsuite.test.lldbtest import * + +class PythonBreakpointCommandSettingTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + my_var = 10 + + @add_test_categories(['pyapi']) + def test_step_out_python(self): + """Test stepping out using avoid-no-debug with dsyms.""" + self.build() + self.do_set_python_command_from_python () + + def setUp (self): + TestBase.setUp(self) + self.main_source = "main.c" + self.main_source_spec = lldb.SBFileSpec(self.main_source) + + + def do_set_python_command_from_python (self): + exe = os.path.join(os.getcwd(), "a.out") + error = lldb.SBError() + + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + body_bkpt = self.target.BreakpointCreateBySourceRegex("Set break point at this line.", self.main_source_spec) + self.assertTrue(body_bkpt, VALID_BREAKPOINT) + + func_bkpt = self.target.BreakpointCreateBySourceRegex("Set break point at this line.", self.main_source_spec) + self.assertTrue(func_bkpt, VALID_BREAKPOINT) + + # Also test that setting a source regex breakpoint with an empty file spec list sets it on all files: + no_files_bkpt = self.target.BreakpointCreateBySourceRegex("Set a breakpoint here", lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(no_files_bkpt, VALID_BREAKPOINT) + num_locations = no_files_bkpt.GetNumLocations() + self.assertTrue(num_locations >= 2, "Got at least two breakpoint locations") + got_one_in_A = False + got_one_in_B = False + for idx in range(0, num_locations): + comp_unit = no_files_bkpt.GetLocationAtIndex(idx).GetAddress().GetSymbolContext(lldb.eSymbolContextCompUnit).GetCompileUnit().GetFileSpec() + print("Got comp unit: ", comp_unit.GetFilename()) + if comp_unit.GetFilename() == "a.c": + got_one_in_A = True + elif comp_unit.GetFilename() == "b.c": + got_one_in_B = True + + self.assertTrue(got_one_in_A, "Failed to match the pattern in A") + self.assertTrue(got_one_in_B, "Failed to match the pattern in B") + self.target.BreakpointDelete(no_files_bkpt.GetID()) + + PythonBreakpointCommandSettingTestCase.my_var = 10 + error = lldb.SBError() + error = body_bkpt.SetScriptCallbackBody("\ +import TestBreakpointCommandsFromPython\n\ +TestBreakpointCommandsFromPython.PythonBreakpointCommandSettingTestCase.my_var = 20\n\ +print('Hit breakpoint')") + self.assertTrue (error.Success(), "Failed to set the script callback body: %s."%(error.GetCString())) + + self.dbg.HandleCommand("command script import --allow-reload ./bktptcmd.py") + func_bkpt.SetScriptCallbackFunction("bktptcmd.function") + + # We will use the function that touches a text file, so remove it first: + self.RemoveTempFile("output2.txt") + + # Now launch the process, and do not stop at entry point. + self.process = self.target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(self.process, PROCESS_IS_VALID) + + # Now finish, and make sure the return value is correct. + threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, body_bkpt) + self.assertTrue(len(threads) == 1, "Stopped at inner breakpoint.") + self.thread = threads[0] + + self.assertTrue(PythonBreakpointCommandSettingTestCase.my_var == 20) + + # Check for the function version as well, which produced this file: + # Remember to clean up after ourselves... + self.assertTrue(os.path.isfile("output2.txt"), + "'output2.txt' exists due to breakpoint command for breakpoint function.") + self.RemoveTempFile("output2.txt") diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py new file mode 100644 index 000000000000..1ea71cbde45c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py @@ -0,0 +1,51 @@ +""" +Test _regexp-break command which uses regular expression matching to dispatch to other built in breakpoint commands. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class RegexpBreakCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test(self): + """Test _regexp-break command.""" + self.build() + self.regexp_break_command() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.source = 'main.c' + self.line = line_number(self.source, '// Set break point at this line.') + + def regexp_break_command(self): + """Test the super consie "b" command, which is analias for _regexp-break.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + break_results = lldbutil.run_break_set_command (self, "b %d" % self.line) + lldbutil.check_breakpoint_result (self, break_results, file_name='main.c', line_number=self.line, num_locations=1) + + break_results = lldbutil.run_break_set_command (self, "b %s:%d" % (self.source, self.line)) + lldbutil.check_breakpoint_result (self, break_results, file_name='main.c', line_number=self.line, num_locations=1) + + # Check breakpoint with full file path. + full_path = os.path.join(os.getcwd(), self.source) + break_results = lldbutil.run_break_set_command (self, "b %s:%d" % (full_path, self.line)) + lldbutil.check_breakpoint_result (self, break_results, file_name='main.c', line_number=self.line, num_locations=1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/a.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/a.c new file mode 100644 index 000000000000..870e4a6ab166 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/a.c @@ -0,0 +1,9 @@ +#include + +int +a_MyFunction () +{ + // Set a breakpoint here. + printf ("a is about to return 10.\n"); + return 10; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/b.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/b.c new file mode 100644 index 000000000000..02b78e7bd855 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/b.c @@ -0,0 +1,9 @@ +#include + +int +b_MyFunction () +{ + // Set a breakpoint here. + printf ("b is about to return 20.\n"); + return 20; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py new file mode 100644 index 000000000000..4bbb0327eacd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py @@ -0,0 +1,6 @@ +from __future__ import print_function + +def function(frame, bp_loc, dict): + there = open("output2.txt", "w"); + print("lldb", file=there) + there.close() diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/main.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/main.c new file mode 100644 index 000000000000..62ec97f43284 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/main.c @@ -0,0 +1,13 @@ +//===-- 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[]) +{ + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/Makefile new file mode 100644 index 000000000000..6067ee45e984 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +CFLAGS_EXTRAS += -std=c99 + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py new file mode 100644 index 000000000000..8c22c8fe869c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py @@ -0,0 +1,181 @@ +""" +Test breakpoint conditions with 'breakpoint modify -c id'. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class BreakpointConditionsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows # Requires EE to support COFF on Windows (http://llvm.org/pr22232) + def test_breakpoint_condition_and_run_command(self): + """Exercise breakpoint condition with 'breakpoint modify -c id'.""" + self.build() + self.breakpoint_conditions() + + @skipIfWindows # Requires EE to support COFF on Windows (http://llvm.org/pr22232) + def test_breakpoint_condition_inline_and_run_command(self): + """Exercise breakpoint condition inline with 'breakpoint set'.""" + self.build() + self.breakpoint_conditions(inline=True) + + @skipIfWindows # Requires EE to support COFF on Windows (http://llvm.org/pr22232) + @add_test_categories(['pyapi']) + def test_breakpoint_condition_and_python_api(self): + """Use Python APIs to set breakpoint conditions.""" + self.build() + self.breakpoint_conditions_python() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.line1 = line_number('main.c', '// Find the line number of function "c" here.') + self.line2 = line_number('main.c', "// Find the line number of c's parent call here.") + + def breakpoint_conditions(self, inline=False): + """Exercise breakpoint condition with 'breakpoint modify -c id'.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + if inline: + # Create a breakpoint by function name 'c' and set the condition. + lldbutil.run_break_set_by_symbol (self, "c", extra_options="-c 'val == 3'", num_expected_locations=1, sym_exact=True) + else: + # Create a breakpoint by function name 'c'. + lldbutil.run_break_set_by_symbol (self, "c", num_expected_locations=1, sym_exact=True) + + # And set a condition on the breakpoint to stop on when 'val == 3'. + self.runCmd("breakpoint modify -c 'val == 3' 1") + + # Now run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The process should be stopped at this point. + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* stopped']) + + # 'frame variable --show-types val' should return 3 due to breakpoint condition. + self.expect("frame variable --show-types val", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(int) val = 3') + + # Also check the hit count, which should be 3, by design. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = ["resolved = 1", + "Condition: val == 3", + "hit count = 1"]) + + # The frame #0 should correspond to main.c:36, the executable statement + # in function name 'c'. And the parent frame should point to main.c:24. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT_CONDITION, + #substrs = ["stop reason = breakpoint"], + patterns = ["frame #0.*main.c:%d" % self.line1, + "frame #1.*main.c:%d" % self.line2]) + + # Test that "breakpoint modify -c ''" clears the condition for the last + # created breakpoint, so that when the breakpoint hits, val == 1. + self.runCmd("process kill") + self.runCmd("breakpoint modify -c ''") + self.expect("breakpoint list -f", BREAKPOINT_STATE_CORRECT, matching=False, + substrs = ["Condition:"]) + + # Now run the program again. + self.runCmd("run", RUN_SUCCEEDED) + + # The process should be stopped at this point. + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* stopped']) + + # 'frame variable --show-types val' should return 1 since it is the first breakpoint hit. + self.expect("frame variable --show-types val", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(int) val = 1') + + self.runCmd("process kill") + self.runCmd("breakpoint disable") + + self.runCmd("breakpoint set -p Loop") + arch = self.getArchitecture() + if arch in ['x86_64', 'i386']: + self.runCmd("breakpoint modify -c ($eax&&i)") + elif arch in ['aarch64']: + self.runCmd("breakpoint modify -c ($x1&&i)") + elif arch in ['arm']: + self.runCmd("breakpoint modify -c ($r0&&i)") + elif re.match("mips",arch): + self.runCmd("breakpoint modify -c ($r2&&i)") + self.runCmd("run") + + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* stopped']) + + self.runCmd("continue") + + self.expect("process status", PROCESS_EXITED, + patterns = ['Process .* exited']) + + def breakpoint_conditions_python(self): + """Use Python APIs to set breakpoint conditions.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # We didn't associate a thread index with the breakpoint, so it should be invalid. + self.assertTrue(breakpoint.GetThreadIndex() == lldb.UINT32_MAX, + "The thread index should be invalid") + # The thread name should be invalid, too. + self.assertTrue(breakpoint.GetThreadName() is None, + "The thread name should be invalid") + + # Let's set the thread index for this breakpoint and verify that it is, + # indeed, being set correctly. + breakpoint.SetThreadIndex(1) # There's only one thread for the process. + self.assertTrue(breakpoint.GetThreadIndex() == 1, + "The thread index has been set correctly") + + # Get the breakpoint location from breakpoint after we verified that, + # indeed, it has one location. + location = breakpoint.GetLocationAtIndex(0) + self.assertTrue(location and + location.IsEnabled(), + VALID_BREAKPOINT_LOCATION) + + # Set the condition on the breakpoint location. + location.SetCondition('val == 3') + self.expect(location.GetCondition(), exe=False, + startstr = 'val == 3') + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.line1 and the break condition should hold. + from lldbsuite.test.lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + var = frame0.FindValue('val', lldb.eValueTypeVariableArgument) + self.assertTrue(frame0.GetLineEntry().GetLine() == self.line1 and + var.GetValue() == '3') + + # The hit count for the breakpoint should be 1. + self.assertTrue(breakpoint.GetHitCount() == 1) + + process.Continue() diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/main.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/main.c new file mode 100644 index 000000000000..1aa8235e1b0c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_conditions/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 + +// This simple program is to demonstrate the capability of the lldb command +// "breakpoint modify -c 'val == 3' breakpt-id" to break within c(int val) only +// when the value of the arg is 3. + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); // Find the line number of c's parent call here. + + return val; +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; // Find the line number of function "c" here. +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) + printf("a(3) returns %d\n", A3); + + for (int i = 0; i < 2; ++i) + printf("Loop\n"); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/Makefile new file mode 100644 index 000000000000..f89b52a972e9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +ifneq (,$(findstring icc,$(CC))) + CXXFLAGS += -debug inline-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/TestBreakpointIDs.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/TestBreakpointIDs.py new file mode 100644 index 000000000000..b7edf2a6e4d5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/TestBreakpointIDs.py @@ -0,0 +1,51 @@ +""" +Test lldb breakpoint ids. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointIDTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test (self): + self.build() + + exe = os.path.join (os.getcwd(), "a.out") + self.expect("file " + exe, + patterns = [ "Current executable set to .*a.out" ]) + + + bpno = lldbutil.run_break_set_by_symbol (self, 'product', num_expected_locations=-1, sym_exact=False) + self.assertTrue (bpno == 1, "First breakpoint number is 1.") + + bpno = lldbutil.run_break_set_by_symbol (self, 'sum', num_expected_locations=-1, sym_exact=False) + self.assertTrue (bpno == 2, "Second breakpoint number is 2.") + + bpno = lldbutil.run_break_set_by_symbol (self, 'junk', num_expected_locations=0, sym_exact=False) + self.assertTrue (bpno == 3, "Third breakpoint number is 3.") + + self.expect ("breakpoint disable 1.1 - 2.2 ", + COMMAND_FAILED_AS_EXPECTED, error = True, + startstr = "error: Invalid range: Ranges that specify particular breakpoint locations must be within the same major breakpoint; you specified two different major breakpoints, 1 and 2.") + + self.expect ("breakpoint disable 2 - 2.2", + COMMAND_FAILED_AS_EXPECTED, error = True, + startstr = "error: Invalid breakpoint id range: Either both ends of range must specify a breakpoint location, or neither can specify a breakpoint location.") + + self.expect ("breakpoint disable 2.1 - 2", + COMMAND_FAILED_AS_EXPECTED, error = True, + startstr = "error: Invalid breakpoint id range: Either both ends of range must specify a breakpoint location, or neither can specify a breakpoint location.") + + self.expect ("breakpoint disable 2.1 - 2.2", + startstr = "2 breakpoints disabled.") + + self.expect ("breakpoint enable 2.*", + patterns = [ ".* breakpoints enabled."] ) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/main.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/main.cpp new file mode 100644 index 000000000000..3deef22c93c8 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ids/main.cpp @@ -0,0 +1,65 @@ +//===-- 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 +#include +#include + + +#define INLINE inline __attribute__((always_inline)) + +INLINE int +product (int x, int y) +{ + int result = x * y; + return result; +} + +INLINE int +sum (int a, int b) +{ + int result = a + b; + return result; +} + +int +strange_max (int m, int n) +{ + if (m > n) + return m; + else if (n > m) + return n; + else + return 0; +} + +int +foo (int i, int j) +{ + if (strange_max (i, j) == i) + return product (i, j); + else if (strange_max (i, j) == j) + return sum (i, j); + else + return product (sum (i, i), sum (j, j)); +} + +int +main(int argc, char const *argv[]) +{ + + int array[3]; + + array[0] = foo (1238, 78392); + array[1] = foo (379265, 23674); + array[2] = foo (872934, 234); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/TestBreakpointIgnoreCount.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/TestBreakpointIgnoreCount.py new file mode 100644 index 000000000000..8a83cb627f7a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/TestBreakpointIgnoreCount.py @@ -0,0 +1,136 @@ +""" +Test breakpoint ignore count features. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class BreakpointIgnoreCountTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_with_run_command(self): + """Exercise breakpoint ignore count with 'breakpoint set -i '.""" + self.build() + self.breakpoint_ignore_count() + + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Use Python APIs to set breakpoint ignore count.""" + self.build() + self.breakpoint_ignore_count_python() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.line1 = line_number('main.c', '// Find the line number of function "c" here.') + self.line2 = line_number('main.c', '// b(2) -> c(2) Find the call site of b(2).') + self.line3 = line_number('main.c', '// a(3) -> c(3) Find the call site of c(3).') + self.line4 = line_number('main.c', '// a(3) -> c(3) Find the call site of a(3).') + self.line5 = line_number('main.c', '// Find the call site of c in main.') + + def breakpoint_ignore_count(self): + """Exercise breakpoint ignore count with 'breakpoint set -i '.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Create a breakpoint in main.c at line1. + lldbutil.run_break_set_by_file_and_line (self, 'main.c', self.line1, extra_options='-i 1', num_expected_locations=1, loc_exact=True) + + # Now run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The process should be stopped at this point. + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* stopped']) + + # Also check the hit count, which should be 2, due to ignore count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_THRICE, + substrs = ["resolved = 1", + "hit count = 2"]) + + # The frame #0 should correspond to main.c:37, the executable statement + # in function name 'c'. And frame #2 should point to main.c:45. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT, + #substrs = ["stop reason = breakpoint"], + patterns = ["frame #0.*main.c:%d" % self.line1, + "frame #2.*main.c:%d" % self.line2]) + + # continue -i 1 is the same as setting the ignore count to 1 again, try that: + # Now run the program. + self.runCmd("process continue -i 1", RUN_SUCCEEDED) + + # The process should be stopped at this point. + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* stopped']) + + # Also check the hit count, which should be 2, due to ignore count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_THRICE, + substrs = ["resolved = 1", + "hit count = 4"]) + + # The frame #0 should correspond to main.c:37, the executable statement + # in function name 'c'. And frame #2 should point to main.c:45. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT, + #substrs = ["stop reason = breakpoint"], + patterns = ["frame #0.*main.c:%d" % self.line1, + "frame #1.*main.c:%d" % self.line5]) + + + + def breakpoint_ignore_count_python(self): + """Use Python APIs to set breakpoint ignore count.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Get the breakpoint location from breakpoint after we verified that, + # indeed, it has one location. + location = breakpoint.GetLocationAtIndex(0) + self.assertTrue(location and + location.IsEnabled(), + VALID_BREAKPOINT_LOCATION) + + # Set the ignore count on the breakpoint location. + location.SetIgnoreCount(2) + self.assertTrue(location.GetIgnoreCount() == 2, + "SetIgnoreCount() works correctly") + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame#0 should be on main.c:37, frame#1 should be on main.c:25, and + # frame#2 should be on main.c:48. + #lldbutil.print_stacktraces(process) + from lldbsuite.test.lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + frame0 = thread.GetFrameAtIndex(0) + frame1 = thread.GetFrameAtIndex(1) + frame2 = thread.GetFrameAtIndex(2) + self.assertTrue(frame0.GetLineEntry().GetLine() == self.line1 and + frame1.GetLineEntry().GetLine() == self.line3 and + frame2.GetLineEntry().GetLine() == self.line4, + STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT) + + # The hit count for the breakpoint should be 3. + self.assertTrue(breakpoint.GetHitCount() == 3) + + process.Continue() diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/main.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/main.c new file mode 100644 index 000000000000..b74b37b48b08 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_ignore_count/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 + +// This simple program is to demonstrate the capability of the lldb command +// "breakpoint modify -i breakpt-id" to set the number of times a +// breakpoint is skipped before stopping. Ignore count can also be set upon +// breakpoint creation by 'breakpoint set ... -i '. + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); // a(3) -> c(3) Find the call site of c(3). + + return val; +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; // Find the line number of function "c" here. +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) Find the call site of b(2). + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) Find the call site of a(3). + printf("a(3) returns %d\n", A3); + + int C1 = c(5); // Find the call site of c in main. + printf ("c(5) returns %d\n", C1); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/Makefile new file mode 100644 index 000000000000..4f6b058fa324 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := a.c +CXX_SOURCES := main.cpp b.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/TestBreakpointLanguage.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/TestBreakpointLanguage.py new file mode 100644 index 000000000000..94fc7bf79f63 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/TestBreakpointLanguage.py @@ -0,0 +1,85 @@ +""" +Test that the language option for breakpoints works correctly +parser. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import shutil +import subprocess + +class TestBreakpointLanguage(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + + def check_location_file (self, bp, loc, test_name): + bp_loc = bp.GetLocationAtIndex(loc) + addr = bp_loc.GetAddress() + comp_unit = addr.GetCompileUnit() + comp_name = comp_unit.GetFileSpec().GetFilename() + return comp_name == test_name + + def test_regex_breakpoint_language(self): + """Test that the name regex breakpoint commands obey the language filter.""" + + self.build() + # Create a target by the debugger. + exe = os.path.join(os.getcwd(), "a.out") + error = lldb.SBError() + # Don't read in dependencies so we don't come across false matches that + # add unwanted breakpoint hits. + self.target = self.dbg.CreateTarget(exe, None, None, False, error) + self.assertTrue(self.target, VALID_TARGET) + + cpp_bp = self.target.BreakpointCreateByRegex("func_from", lldb.eLanguageTypeC_plus_plus, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(cpp_bp.GetNumLocations() == 1, "Only one C++ symbol matches") + self.assertTrue(self.check_location_file(cpp_bp, 0, "b.cpp")) + + c_bp = self.target.BreakpointCreateByRegex("func_from", lldb.eLanguageTypeC, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(c_bp.GetNumLocations() == 1, "Only one C symbol matches") + self.assertTrue(self.check_location_file(c_bp, 0, "a.c")) + + objc_bp = self.target.BreakpointCreateByRegex("func_from", lldb.eLanguageTypeObjC, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(objc_bp.GetNumLocations() == 0, "No ObjC symbol matches") + + def test_by_name_breakpoint_language(self): + """Test that the name regex breakpoint commands obey the language filter.""" + + self.build() + # Create a target by the debugger. + exe = os.path.join(os.getcwd(), "a.out") + error = lldb.SBError() + # Don't read in dependencies so we don't come across false matches that + # add unwanted breakpoint hits. + self.target = self.dbg.CreateTarget(exe, None, None, False, error) + self.assertTrue(self.target, VALID_TARGET) + + cpp_bp = self.target.BreakpointCreateByName("func_from_cpp", lldb.eFunctionNameTypeAuto, lldb.eLanguageTypeC_plus_plus, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(cpp_bp.GetNumLocations() == 1, "Only one C++ symbol matches") + self.assertTrue(self.check_location_file(cpp_bp, 0, "b.cpp")) + + no_cpp_bp = self.target.BreakpointCreateByName("func_from_c", lldb.eFunctionNameTypeAuto, lldb.eLanguageTypeC_plus_plus, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(no_cpp_bp.GetNumLocations() == 0, "And the C one doesn't match") + + c_bp = self.target.BreakpointCreateByName("func_from_c", lldb.eFunctionNameTypeAuto, lldb.eLanguageTypeC, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(c_bp.GetNumLocations() == 1, "Only one C symbol matches") + self.assertTrue(self.check_location_file(c_bp, 0, "a.c")) + + no_c_bp = self.target.BreakpointCreateByName("func_from_cpp", lldb.eFunctionNameTypeAuto, lldb.eLanguageTypeC, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(no_c_bp.GetNumLocations() == 0, "And the C++ one doesn't match") + + objc_bp = self.target.BreakpointCreateByName("func_from_cpp", lldb.eFunctionNameTypeAuto, lldb.eLanguageTypeObjC, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(objc_bp.GetNumLocations() == 0, "No ObjC symbol matches") + + diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/a.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/a.c new file mode 100644 index 000000000000..b90e2bdcca5d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/a.c @@ -0,0 +1,5 @@ +int +func_from_c () +{ + return 5; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/b.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/b.cpp new file mode 100644 index 000000000000..89373445b9aa --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/b.cpp @@ -0,0 +1,5 @@ +int +func_from_cpp() +{ + return 10; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/main.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/main.cpp new file mode 100644 index 000000000000..b7d00a602029 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_language/main.cpp @@ -0,0 +1,11 @@ +#include +extern "C" int func_from_c(); +extern int func_from_cpp(); + +int +main() +{ + func_from_c(); + func_from_cpp(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/Makefile new file mode 100644 index 000000000000..7934cd5db427 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +ifneq (,$(findstring icc,$(CC))) + CFLAGS += -debug inline-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/TestBreakpointLocations.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/TestBreakpointLocations.py new file mode 100644 index 000000000000..5c4dc219a106 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/TestBreakpointLocations.py @@ -0,0 +1,88 @@ +""" +Test breakpoint commands for a breakpoint ID with multiple locations. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointLocationsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr24528") + @expectedFailureAll(oslist=["linux"], compiler="clang", compiler_version=["=", "3.8"], archs=["i386"], debug_info="dwo") + def test(self): + """Test breakpoint enable/disable for a breakpoint ID with multiple locations.""" + self.build() + self.breakpoint_locations_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set break point at this line.') + + def breakpoint_locations_test(self): + """Test breakpoint enable/disable for a breakpoint ID with multiple locations.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 3 locations. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=3) + + # The breakpoint list should show 3 locations. + self.expect("breakpoint list -f", "Breakpoint locations shown correctly", + substrs = ["1: file = 'main.c', line = %d, exact_match = 0, locations = 3" % self.line], + patterns = ["where = a.out`func_inlined .+unresolved, hit count = 0", + "where = a.out`main .+\[inlined\].+unresolved, hit count = 0"]) + + # The 'breakpoint disable 3.*' command should fail gracefully. + self.expect("breakpoint disable 3.*", + "Disabling an invalid breakpoint should fail gracefully", + error=True, + startstr = "error: '3' is not a valid breakpoint ID.") + + # The 'breakpoint disable 1.*' command should disable all 3 locations. + self.expect("breakpoint disable 1.*", "All 3 breakpoint locatons disabled correctly", + startstr = "3 breakpoints disabled.") + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should not stopped on any breakpoint at all. + self.expect("process status", "No stopping on any disabled breakpoint", + patterns = ["^Process [0-9]+ exited with status = 0"]) + + # The 'breakpoint enable 1.*' command should enable all 3 breakpoints. + self.expect("breakpoint enable 1.*", "All 3 breakpoint locatons enabled correctly", + startstr = "3 breakpoints enabled.") + + # The 'breakpoint disable 1.1' command should disable 1 location. + self.expect("breakpoint disable 1.1", "1 breakpoint locatons disabled correctly", + startstr = "1 breakpoints disabled.") + + # Run the program againt. We should stop on the two breakpoint locations. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped once. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Continue the program, there should be another stop. + self.runCmd("process continue") + + # Stopped again. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # At this point, 1.1 has a hit count of 0 and the other a hit count of 1". + self.expect("breakpoint list -f", "The breakpoints should report correct hit counts", + patterns = ["1\.1: .+ unresolved, hit count = 0 +Options: disabled", + "1\.2: .+ resolved, hit count = 1", + "1\.3: .+ resolved, hit count = 1"]) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/main.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/main.c new file mode 100644 index 000000000000..7ec3ded67b74 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/main.c @@ -0,0 +1,43 @@ +#include + +#define INLINE inline __attribute__((always_inline)) + +int +func_not_inlined (void) +{ + printf ("Called func_not_inlined.\n"); + return 0; +} + +INLINE int +func_inlined (void) +{ + static int func_inline_call_count = 0; + printf ("Called func_inlined.\n"); + ++func_inline_call_count; + printf ("Returning func_inlined call count: %d.\n", func_inline_call_count); + return func_inline_call_count; // Set break point at this line. +} + +extern int func_inlined (void); + +int +main (int argc, char **argv) +{ + printf ("Starting...\n"); + + int (*func_ptr) (void); + func_ptr = func_inlined; + + int a = func_inlined(); + printf("First call to func_inlined() returns: %d.\n", a); + + func_not_inlined (); + + func_ptr (); + + printf("Last call to func_inlined() returns: %d.\n", func_inlined ()); + return 0; +} + + diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/Makefile new file mode 100644 index 000000000000..457c4972f2d5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp foo.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/TestBreakpointOptions.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/TestBreakpointOptions.py new file mode 100644 index 000000000000..29afec202339 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/TestBreakpointOptions.py @@ -0,0 +1,93 @@ +""" +Test breakpoint command for different options. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointOptionsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test(self): + """Test breakpoint command for different options.""" + self.build() + self.breakpoint_options_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + def breakpoint_options_test(self): + """Test breakpoint command for different options.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 1 locations. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, extra_options = "-K 1", num_expected_locations = 1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, extra_options = "-K 0", num_expected_locations = 1) + + # This should create a breakpoint 0 locations. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, extra_options = "-m 0", num_expected_locations = 0) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped once. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 2."]) + + # Check the list of breakpoint. + self.expect("breakpoint list -f", "Breakpoint locations shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.line, + "2: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.line, + "3: file = 'main.cpp', line = %d, exact_match = 1, locations = 0" % self.line]) + + # Continue the program, there should be another stop. + self.runCmd("process continue") + + # Stopped again. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Continue the program, we should exit. + self.runCmd("process continue") + + # We should exit. + self.expect("process status", "Process exited successfully", + patterns = ["^Process [0-9]+ exited with status = 0"]) + + def breakpoint_options_language_test(self): + """Test breakpoint command for language option.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 1 locations. + lldbutil.run_break_set_by_symbol (self, 'ns::func', sym_exact=False, extra_options = "-L c++", num_expected_locations=1) + + # This should create a breakpoint with 0 locations. + lldbutil.run_break_set_by_symbol (self, 'ns::func', sym_exact=False, extra_options = "-L c", num_expected_locations=0) + self.runCmd("settings set target.language c") + lldbutil.run_break_set_by_symbol (self, 'ns::func', sym_exact=False, num_expected_locations=0) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped once. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Continue the program, we should exit. + self.runCmd("process continue") + + # We should exit. + self.expect("process status", "Process exited successfully", + patterns = ["^Process [0-9]+ exited with status = 0"]) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/foo.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/foo.cpp new file mode 100644 index 000000000000..e5d0e09803e6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/foo.cpp @@ -0,0 +1,12 @@ + +namespace ns { + int func(void) + { + return 0; + } +} + +extern "C" int foo(void) +{ + return ns::func(); +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/main.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/main.cpp new file mode 100644 index 000000000000..363b90003d76 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_options/main.cpp @@ -0,0 +1,8 @@ +// Set break point at this line. + +extern "C" int foo(void); +int +main (int argc, char **argv) +{ + return foo(); +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/Makefile new file mode 100644 index 000000000000..0ac34a186b25 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +EXE := CompDirSymLink + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/TestCompDirSymLink.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/TestCompDirSymLink.py new file mode 100644 index 000000000000..e1de38bcad8c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/TestCompDirSymLink.py @@ -0,0 +1,63 @@ +""" +Test breakpoint command with AT_comp_dir set to symbolic link. +""" +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import shutil + + +_EXE_NAME = 'CompDirSymLink' # Must match Makefile +_SRC_FILE = 'main.cpp' +_COMP_DIR_SYM_LINK_PROP = 'plugin.symbol-file.dwarf.comp-dir-symlink-paths' + +class CompDirSymLinkTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number(_SRC_FILE, '// Set break point at this line.') + self.src_path = os.path.join(os.getcwd(), _SRC_FILE) + + @skipIfHostWindows + def test_symlink_paths_set(self): + pwd_symlink = self.create_src_symlink() + self.doBuild(pwd_symlink) + self.runCmd("settings set %s %s" % (_COMP_DIR_SYM_LINK_PROP, pwd_symlink)) + lldbutil.run_break_set_by_file_and_line(self, self.src_path, self.line) + + @skipUnlessHostLinux + def test_symlink_paths_set_procselfcwd(self): + pwd_symlink = '/proc/self/cwd' + self.doBuild(pwd_symlink) + self.runCmd("settings set %s %s" % (_COMP_DIR_SYM_LINK_PROP, pwd_symlink)) + lldbutil.run_break_set_by_file_and_line(self, self.src_path, self.line) + + @skipIfHostWindows + def test_symlink_paths_unset(self): + pwd_symlink = self.create_src_symlink() + self.doBuild(pwd_symlink) + self.runCmd('settings clear ' + _COMP_DIR_SYM_LINK_PROP) + self.assertRaises(AssertionError, lldbutil.run_break_set_by_file_and_line, self, self.src_path, self.line) + + def create_src_symlink(self): + pwd_symlink = os.path.join(os.getcwd(), 'pwd_symlink') + if os.path.exists(pwd_symlink): + os.unlink(pwd_symlink) + os.symlink(os.getcwd(), pwd_symlink) + self.addTearDownHook(lambda: os.remove(pwd_symlink)) + return pwd_symlink + + def doBuild(self, pwd_symlink): + self.build(None, None, {'PWD': pwd_symlink}, True) + + exe = os.path.join(os.getcwd(), _EXE_NAME) + self.runCmd('file ' + exe, CURRENT_EXECUTABLE_SET) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/main.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/main.cpp new file mode 100644 index 000000000000..fef06a011e92 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/comp_dir_symlink/main.cpp @@ -0,0 +1,13 @@ +//===-- 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[]) +{ + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/Makefile new file mode 100644 index 000000000000..f89b52a972e9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +ifneq (,$(findstring icc,$(CC))) + CXXFLAGS += -debug inline-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/TestConsecutiveBreakpoints.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/TestConsecutiveBreakpoints.py new file mode 100644 index 000000000000..af6df3764829 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/TestConsecutiveBreakpoints.py @@ -0,0 +1,59 @@ +""" +Test continue from a breakpoint when there is a breakpoint on the next instruction also. +""" + +from __future__ import print_function + + + +import unittest2 +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ConsecutiveBreakpoitsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureAll("llvm.org/pr23478", oslist = not_in(["macosx"])) + def test (self): + self.build () + self.consecutive_breakpoints_tests() + + def consecutive_breakpoints_tests(self): + exe = os.path.join (os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateBySourceRegex("Set breakpoint here", lldb.SBFileSpec("main.cpp")) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # We should be stopped at the first breakpoint + thread = process.GetThreadAtIndex(0) + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonBreakpoint) + + # Set breakpoint to the next instruction + frame = thread.GetFrameAtIndex(0) + + address = frame.GetPCAddress() + instructions = target.ReadInstructions(address, 2) + self.assertTrue(len(instructions) == 2) + address = instructions[1].GetAddress() + + target.BreakpointCreateByAddress(address.GetLoadAddress(target)) + process.Continue() + + # We should be stopped at the second breakpoint + thread = process.GetThreadAtIndex(0) + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonBreakpoint) + + # Run the process until termination + process.Continue() diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/main.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/main.cpp new file mode 100644 index 000000000000..c1943f03dbf1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/consecutive_breakpoins/main.cpp @@ -0,0 +1,19 @@ +//===-- 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[]) +{ + int a = 0; + int b = 1; + a = b + 1; // Set breakpoint here + b = a + 1; + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/Makefile new file mode 100644 index 000000000000..f89b52a972e9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +ifneq (,$(findstring icc,$(CC))) + CXXFLAGS += -debug inline-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/TestCPPBreakpointLocations.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/TestCPPBreakpointLocations.py new file mode 100644 index 000000000000..dea206b9e9d2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/TestCPPBreakpointLocations.py @@ -0,0 +1,62 @@ +""" +Test lldb breakpoint ids. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestCPPBreakpointLocations(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr24764") + def test (self): + self.build () + self.breakpoint_id_tests () + + def verify_breakpoint_locations(self, target, bp_dict): + + name = bp_dict['name'] + names = bp_dict['loc_names'] + bp = target.BreakpointCreateByName (name) + self.assertTrue (bp.GetNumLocations() == len(names), "Make sure we find the right number of breakpoint locations") + + bp_loc_names = list() + for bp_loc in bp: + bp_loc_names.append(bp_loc.GetAddress().GetFunction().GetName()) + + for name in names: + found = name in bp_loc_names + if not found: + print("Didn't find '%s' in: %s" % (name, bp_loc_names)) + self.assertTrue (found, "Make sure we find all required locations") + + def breakpoint_id_tests (self): + + # Create a target by the debugger. + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + bp_dicts = [ + { 'name' : 'func1', 'loc_names' : [ 'a::c::func1()', 'b::c::func1()'] }, + { 'name' : 'func2', 'loc_names' : [ 'a::c::func2()', 'c::d::func2()'] }, + { 'name' : 'func3', 'loc_names' : [ 'a::c::func3()', 'b::c::func3()', 'c::d::func3()'] }, + { 'name' : 'c::func1', 'loc_names' : [ 'a::c::func1()', 'b::c::func1()'] }, + { 'name' : 'c::func2', 'loc_names' : [ 'a::c::func2()'] }, + { 'name' : 'c::func3', 'loc_names' : [ 'a::c::func3()', 'b::c::func3()'] }, + { 'name' : 'a::c::func1', 'loc_names' : [ 'a::c::func1()'] }, + { 'name' : 'b::c::func1', 'loc_names' : [ 'b::c::func1()'] }, + { 'name' : 'c::d::func2', 'loc_names' : [ 'c::d::func2()'] }, + { 'name' : 'a::c::func1()', 'loc_names' : [ 'a::c::func1()'] }, + { 'name' : 'b::c::func1()', 'loc_names' : [ 'b::c::func1()'] }, + { 'name' : 'c::d::func2()', 'loc_names' : [ 'c::d::func2()'] }, + ] + + for bp_dict in bp_dicts: + self.verify_breakpoint_locations(target, bp_dict) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/main.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/main.cpp new file mode 100644 index 000000000000..ef582aa36ebb --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp/main.cpp @@ -0,0 +1,77 @@ +//===-- 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 + +namespace a { + class c { + public: + c () {} + ~c() {} + void func1() + { + puts (__PRETTY_FUNCTION__); + } + void func2() + { + puts (__PRETTY_FUNCTION__); + } + void func3() + { + puts (__PRETTY_FUNCTION__); + } + }; +} + +namespace b { + class c { + public: + c () {} + ~c() {} + void func1() + { + puts (__PRETTY_FUNCTION__); + } + void func3() + { + puts (__PRETTY_FUNCTION__); + } + }; +} + +namespace c { + class d { + public: + d () {} + ~d() {} + void func2() + { + puts (__PRETTY_FUNCTION__); + } + void func3() + { + puts (__PRETTY_FUNCTION__); + } + }; +} + +int main (int argc, char const *argv[]) +{ + a::c ac; + b::c bc; + c::d cd; + ac.func1(); + ac.func2(); + ac.func3(); + bc.func1(); + bc.func3(); + cd.func2(); + cd.func3(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/TestCPPExceptionBreakpoint.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/TestCPPExceptionBreakpoint.py new file mode 100644 index 000000000000..f7a19098b492 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/TestCPPExceptionBreakpoint.py @@ -0,0 +1,48 @@ +""" +Test that you can set breakpoint and hit the C++ language exception breakpoint +""" + +from __future__ import print_function + + + +import os +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +import sys +from lldbsuite.test.lldbtest import * + +class TestCPPExceptionBreakpoint (TestBase): + + mydir = TestBase.compute_mydir(__file__) + my_var = 10 + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24538") # clang-cl does not support throw or catch + def test_cpp_exception_breakpoint(self): + """Test setting and hitting the C++ exception breakpoint.""" + self.build() + self.do_cpp_exception_bkpt () + + def setUp (self): + TestBase.setUp(self) + self.main_source = "main.c" + self.main_source_spec = lldb.SBFileSpec(self.main_source) + + + def do_cpp_exception_bkpt (self): + exe = os.path.join(os.getcwd(), "a.out") + error = lldb.SBError() + + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + exception_bkpt = self.target.BreakpointCreateForException(lldb.eLanguageTypeC_plus_plus, False, True) + self.assertTrue (exception_bkpt.IsValid(), "Created exception breakpoint.") + + process = self.target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, exception_bkpt) + self.assertTrue (len(thread_list) == 1, "One thread stopped at the exception breakpoint.") diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/main.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/main.cpp new file mode 100644 index 000000000000..76cb22735a71 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/cpp_exception/main.cpp @@ -0,0 +1,13 @@ +#include + +void +throws_int () +{ + throw 5; +} + +int +main () +{ + throws_int(); +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/Makefile new file mode 100644 index 000000000000..7934cd5db427 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +ifneq (,$(findstring icc,$(CC))) + CFLAGS += -debug inline-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/TestBreakpointsWithNoTargets.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/TestBreakpointsWithNoTargets.py new file mode 100644 index 000000000000..f2693e6ed593 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/TestBreakpointsWithNoTargets.py @@ -0,0 +1,67 @@ +""" +Test breakpoint commands set before we have a target +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointInDummyTarget (TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test(self): + """Test breakpoint set before we have a target. """ + self.build() + self.dummy_breakpoint_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', 'Set a breakpoint on this line.') + self.line2 = line_number ('main.c', 'Set another on this line.') + + def dummy_breakpoint_test(self): + """Test breakpoint set before we have a target. """ + + # This should create a breakpoint with 3 locations. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=0) + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line2, num_expected_locations=0) + + # This is the function to remove breakpoints from the dummy target + # to get a clean slate for the next test case. + def cleanup(): + self.runCmd('breakpoint delete -D -f', check=False) + self.runCmd('breakpoint list', check=False) + + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # The breakpoint list should show 3 locations. + self.expect("breakpoint list -f", "Breakpoint locations shown correctly", + substrs = ["1: file = 'main.c', line = %d, exact_match = 0, locations = 1" % self.line, + "2: file = 'main.c', line = %d, exact_match = 0, locations = 1" % self.line2]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped once. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Continue the program, there should be another stop. + self.runCmd("process continue") + + # Stopped again. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 2."]) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/main.c b/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/main.c new file mode 100644 index 000000000000..e1f03237cd11 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/dummy_target_breakpoints/main.c @@ -0,0 +1,11 @@ +#include + +int +main (int argc, char **argv) +{ + printf ("Set a breakpoint on this line.\n"); + + return 0; // Set another on this line. +} + + diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/Makefile new file mode 100644 index 000000000000..5b73ae626f34 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := int.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/TestInlinedBreakpoints.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/TestInlinedBreakpoints.py new file mode 100644 index 000000000000..d04178bda78d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/TestInlinedBreakpoints.py @@ -0,0 +1,57 @@ +""" +Test that inlined breakpoints (breakpoint set on a file/line included from +another source file) works correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class InlinedBreakpointsTestCase(TestBase): + """Bug fixed: rdar://problem/8464339""" + + mydir = TestBase.compute_mydir(__file__) + + def test_with_run_command(self): + """Test 'b basic_types.cpp:176' does break (where int.cpp includes basic_type.cpp).""" + self.build() + self.inlined_breakpoints() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside basic_type.cpp. + self.line = line_number('basic_type.cpp', '// Set break point at this line.') + + def inlined_breakpoints(self): + """Test 'b basic_types.cpp:176' does break (where int.cpp includes basic_type.cpp).""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # With the inline-breakpoint-strategy, our file+line breakpoint should not resolve to a location. + self.runCmd('settings set target.inline-breakpoint-strategy headers') + + # Set a breakpoint and fail because it is in an inlined source implemenation file + lldbutil.run_break_set_by_file_and_line (self, "basic_type.cpp", self.line, num_expected_locations=0) + + # Now enable breakpoints in implementation files and see the breakpoint set succeed + self.runCmd('settings set target.inline-breakpoint-strategy always') + # And add hooks to restore the settings during tearDown(). + self.addTearDownHook( + lambda: self.runCmd("settings set target.inline-breakpoint-strategy always")) + + lldbutil.run_break_set_by_file_and_line (self, "basic_type.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + # And it should break at basic_type.cpp:176. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint', + 'basic_type.cpp:%d' % self.line]) diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/basic_type.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/basic_type.cpp new file mode 100644 index 000000000000..5881afe1f395 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/basic_type.cpp @@ -0,0 +1,178 @@ +// This file must have the following defined before it is included: +// T defined to the type to test (int, float, etc) +// T_CSTR a C string representation of the type T ("int", "float") +// T_VALUE_1 defined to a valid initializer value for TEST_TYPE (7 for int, 2.0 for float) +// T_VALUE_2, T_VALUE_3, T_VALUE_4 defined to a valid initializer value for TEST_TYPE that is different from TEST_VALUE_1 +// T_PRINTF_FORMAT defined if T can be printed with printf +// +// An example for integers is below +#if 0 + +#define T int +#define T_CSTR "int" +#define T_VALUE_1 11001110 +#define T_VALUE_2 22002220 +#define T_VALUE_3 33003330 +#define T_VALUE_4 44044440 +#define T_PRINTF_FORMAT "%i" + +#include "basic_type.cpp" + +#endif + +class a_class +{ +public: + a_class (const T& a, const T& b) : + m_a (a), + m_b (b) + { + } + + ~a_class () + { + } + + const T& + get_a() + { + return m_a; + } + + void + set_a (const T& a) + { + m_a = a; + } + + const T& + get_b() + { + return m_b; + } + + void + set_b (const T& b) + { + m_b = b; + } + +protected: + T m_a; + T m_b; +}; + +typedef struct a_struct_tag { + T a; + T b; +} a_struct_t; + + +typedef union a_union_zero_tag { + T a; + double a_double; +} a_union_zero_t; + +typedef struct a_union_nonzero_tag { + double a_double; + a_union_zero_t u; +} a_union_nonzero_t; + + +#include +#include + +void Puts(char const *msg) +{ + puts(msg); +} + +int +main (int argc, char const *argv[]) +{ + T a = T_VALUE_1; + T* a_ptr = &a; + T& a_ref = a; + T a_array_bounded[2] = { T_VALUE_1, T_VALUE_2 }; + T a_array_unbounded[] = { T_VALUE_1, T_VALUE_2 }; + + a_class a_class_instance (T_VALUE_1, T_VALUE_2); + a_class *a_class_ptr = &a_class_instance; + a_class &a_class_ref = a_class_instance; + + a_struct_t a_struct = { T_VALUE_1, T_VALUE_2 }; + a_struct_t *a_struct_ptr = &a_struct; + a_struct_t &a_struct_ref = a_struct; + + // Create a union with type T at offset zero + a_union_zero_t a_union_zero; + a_union_zero.a = T_VALUE_1; + a_union_zero_t *a_union_zero_ptr = &a_union_zero; + a_union_zero_t &a_union_zero_ref = a_union_zero; + + // Create a union with type T at a non-zero offset + a_union_nonzero_t a_union_nonzero; + a_union_nonzero.u.a = T_VALUE_1; + a_union_nonzero_t *a_union_nonzero_ptr = &a_union_nonzero; + a_union_nonzero_t &a_union_nonzero_ref = a_union_nonzero; + + a_struct_t a_struct_array_bounded[2] = {{ T_VALUE_1, T_VALUE_2 }, { T_VALUE_3, T_VALUE_4 }}; + a_struct_t a_struct_array_unbounded[] = {{ T_VALUE_1, T_VALUE_2 }, { T_VALUE_3, T_VALUE_4 }}; + a_union_zero_t a_union_zero_array_bounded[2]; + a_union_zero_array_bounded[0].a = T_VALUE_1; + a_union_zero_array_bounded[1].a = T_VALUE_2; + a_union_zero_t a_union_zero_array_unbounded[] = {{ T_VALUE_1 }, { T_VALUE_2 }}; + +#ifdef T_PRINTF_FORMAT + printf ("%s: a = '" T_PRINTF_FORMAT "'\n", T_CSTR, a); + printf ("%s*: %p => *a_ptr = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_ptr, *a_ptr); + printf ("%s&: @%p => a_ref = '" T_PRINTF_FORMAT "'\n", T_CSTR, &a_ref, a_ref); + + printf ("%s[2]: a_array_bounded[0] = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_array_bounded[0]); + printf ("%s[2]: a_array_bounded[1] = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_array_bounded[1]); + + printf ("%s[]: a_array_unbounded[0] = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_array_unbounded[0]); + printf ("%s[]: a_array_unbounded[1] = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_array_unbounded[1]); + + printf ("(a_class) a_class_instance.m_a = '" T_PRINTF_FORMAT "'\n", a_class_instance.get_a()); + printf ("(a_class) a_class_instance.m_b = '" T_PRINTF_FORMAT "'\n", a_class_instance.get_b()); + printf ("(a_class*) a_class_ptr = %p, a_class_ptr->m_a = '" T_PRINTF_FORMAT "'\n", a_class_ptr, a_class_ptr->get_a()); + printf ("(a_class*) a_class_ptr = %p, a_class_ptr->m_b = '" T_PRINTF_FORMAT "'\n", a_class_ptr, a_class_ptr->get_b()); + printf ("(a_class&) a_class_ref = %p, a_class_ref.m_a = '" T_PRINTF_FORMAT "'\n", &a_class_ref, a_class_ref.get_a()); + printf ("(a_class&) a_class_ref = %p, a_class_ref.m_b = '" T_PRINTF_FORMAT "'\n", &a_class_ref, a_class_ref.get_b()); + + printf ("(a_struct_t) a_struct.a = '" T_PRINTF_FORMAT "'\n", a_struct.a); + printf ("(a_struct_t) a_struct.b = '" T_PRINTF_FORMAT "'\n", a_struct.b); + printf ("(a_struct_t*) a_struct_ptr = %p, a_struct_ptr->a = '" T_PRINTF_FORMAT "'\n", a_struct_ptr, a_struct_ptr->a); + printf ("(a_struct_t*) a_struct_ptr = %p, a_struct_ptr->b = '" T_PRINTF_FORMAT "'\n", a_struct_ptr, a_struct_ptr->b); + printf ("(a_struct_t&) a_struct_ref = %p, a_struct_ref.a = '" T_PRINTF_FORMAT "'\n", &a_struct_ref, a_struct_ref.a); + printf ("(a_struct_t&) a_struct_ref = %p, a_struct_ref.b = '" T_PRINTF_FORMAT "'\n", &a_struct_ref, a_struct_ref.b); + + printf ("(a_union_zero_t) a_union_zero.a = '" T_PRINTF_FORMAT "'\n", a_union_zero.a); + printf ("(a_union_zero_t*) a_union_zero_ptr = %p, a_union_zero_ptr->a = '" T_PRINTF_FORMAT "'\n", a_union_zero_ptr, a_union_zero_ptr->a); + printf ("(a_union_zero_t&) a_union_zero_ref = %p, a_union_zero_ref.a = '" T_PRINTF_FORMAT "'\n", &a_union_zero_ref, a_union_zero_ref.a); + + printf ("(a_union_nonzero_t) a_union_nonzero.u.a = '" T_PRINTF_FORMAT "'\n", a_union_nonzero.u.a); + printf ("(a_union_nonzero_t*) a_union_nonzero_ptr = %p, a_union_nonzero_ptr->u.a = '" T_PRINTF_FORMAT "'\n", a_union_nonzero_ptr, a_union_nonzero_ptr->u.a); + printf ("(a_union_nonzero_t&) a_union_nonzero_ref = %p, a_union_nonzero_ref.u.a = '" T_PRINTF_FORMAT "'\n", &a_union_nonzero_ref, a_union_nonzero_ref.u.a); + + printf ("(a_struct_t[2]) a_struct_array_bounded[0].a = '" T_PRINTF_FORMAT "'\n", a_struct_array_bounded[0].a); + printf ("(a_struct_t[2]) a_struct_array_bounded[0].b = '" T_PRINTF_FORMAT "'\n", a_struct_array_bounded[0].b); + printf ("(a_struct_t[2]) a_struct_array_bounded[1].a = '" T_PRINTF_FORMAT "'\n", a_struct_array_bounded[1].a); + printf ("(a_struct_t[2]) a_struct_array_bounded[1].b = '" T_PRINTF_FORMAT "'\n", a_struct_array_bounded[1].b); + + printf ("(a_struct_t[]) a_struct_array_unbounded[0].a = '" T_PRINTF_FORMAT "'\n", a_struct_array_unbounded[0].a); + printf ("(a_struct_t[]) a_struct_array_unbounded[0].b = '" T_PRINTF_FORMAT "'\n", a_struct_array_unbounded[0].b); + printf ("(a_struct_t[]) a_struct_array_unbounded[1].a = '" T_PRINTF_FORMAT "'\n", a_struct_array_unbounded[1].a); + printf ("(a_struct_t[]) a_struct_array_unbounded[1].b = '" T_PRINTF_FORMAT "'\n", a_struct_array_unbounded[1].b); + + printf ("(a_union_zero_t[2]) a_union_zero_array_bounded[0].a = '" T_PRINTF_FORMAT "'\n", a_union_zero_array_bounded[0].a); + printf ("(a_union_zero_t[2]) a_union_zero_array_bounded[1].a = '" T_PRINTF_FORMAT "'\n", a_union_zero_array_bounded[1].a); + + printf ("(a_union_zero_t[]) a_union_zero_array_unbounded[0].a = '" T_PRINTF_FORMAT "'\n", a_union_zero_array_unbounded[0].a); + printf ("(a_union_zero_t[]) a_union_zero_array_unbounded[1].a = '" T_PRINTF_FORMAT "'\n", a_union_zero_array_unbounded[1].a); + +#endif + Puts("About to exit, break here to check values..."); // Set break point at this line. + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/int.cpp b/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/int.cpp new file mode 100644 index 000000000000..922398b1c6e3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/inlined_breakpoints/int.cpp @@ -0,0 +1,9 @@ +#define T int +#define T_CSTR "int" +#define T_VALUE_1 11001110 +#define T_VALUE_2 22002220 +#define T_VALUE_3 33003330 +#define T_VALUE_4 44004440 +#define T_PRINTF_FORMAT "%i" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/Makefile b/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/Makefile new file mode 100644 index 000000000000..ad3cb3fadcde --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/TestObjCBreakpoints.py b/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/TestObjCBreakpoints.py new file mode 100644 index 000000000000..648a0f5ea07c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/TestObjCBreakpoints.py @@ -0,0 +1,94 @@ +""" +Test that objective-c constant strings are generated correctly by the expression +parser. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import shutil +import subprocess + +@skipUnlessDarwin +class TestObjCBreakpoints(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_break(self): + """Test setting Objective C specific breakpoints (DWARF in .o files).""" + self.build() + self.setTearDownCleanup() + self.check_objc_breakpoints(False) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.main_source = "main.m" + self.line = line_number(self.main_source, '// Set breakpoint here') + + def check_category_breakpoints(self): + name_bp = self.target.BreakpointCreateByName ("myCategoryFunction") + selector_bp = self.target.BreakpointCreateByName ("myCategoryFunction", lldb.eFunctionNameTypeSelector, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(name_bp.GetNumLocations() == selector_bp.GetNumLocations(), 'Make sure setting a breakpoint by name "myCategoryFunction" sets a breakpoint even though it is in a category') + for bp_loc in selector_bp: + function_name = bp_loc.GetAddress().GetSymbol().GetName() + self.assertTrue(" myCategoryFunction]" in function_name, 'Make sure all function names have " myCategoryFunction]" in their names') + + category_bp = self.target.BreakpointCreateByName ("-[MyClass(MyCategory) myCategoryFunction]") + stripped_bp = self.target.BreakpointCreateByName ("-[MyClass myCategoryFunction]") + stripped2_bp = self.target.BreakpointCreateByName ("[MyClass myCategoryFunction]") + self.assertTrue(category_bp.GetNumLocations() == 1, "Make sure we can set a breakpoint using a full objective C function name with the category included (-[MyClass(MyCategory) myCategoryFunction])") + self.assertTrue(stripped_bp.GetNumLocations() == 1, "Make sure we can set a breakpoint using a full objective C function name without the category included (-[MyClass myCategoryFunction])") + self.assertTrue(stripped2_bp.GetNumLocations() == 1, "Make sure we can set a breakpoint using a full objective C function name without the category included ([MyClass myCategoryFunction])") + + def check_objc_breakpoints(self, have_dsym): + """Test constant string generation amd comparison by the expression parser.""" + + # Set debugger into synchronous mode + self.dbg.SetAsync(False) + + # Create a target by the debugger. + exe = os.path.join(os.getcwd(), "a.out") + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + #---------------------------------------------------------------------- + # Set breakpoints on all selectors whose name is "count". This should + # catch breakpoints that are both C functions _and_ anything whose + # selector is "count" because just looking at "count" we can't tell + # definitively if the name is a selector or a C function + #---------------------------------------------------------------------- + name_bp = self.target.BreakpointCreateByName ("count") + selector_bp = self.target.BreakpointCreateByName ("count", lldb.eFunctionNameTypeSelector, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(name_bp.GetNumLocations() >= selector_bp.GetNumLocations(), 'Make sure we get at least the same amount of breakpoints if not more when setting by name "count"') + self.assertTrue(selector_bp.GetNumLocations() > 50, 'Make sure we find a lot of "count" selectors') # There are 93 on the latest MacOSX + for bp_loc in selector_bp: + function_name = bp_loc.GetAddress().GetSymbol().GetName() + self.assertTrue(" count]" in function_name, 'Make sure all function names have " count]" in their names') + + #---------------------------------------------------------------------- + # Set breakpoints on all selectors whose name is "isEqual:". This should + # catch breakpoints that are only ObjC selectors because no C function + # can end with a : + #---------------------------------------------------------------------- + name_bp = self.target.BreakpointCreateByName ("isEqual:") + selector_bp = self.target.BreakpointCreateByName ("isEqual:", lldb.eFunctionNameTypeSelector, lldb.SBFileSpecList(), lldb.SBFileSpecList()) + self.assertTrue(name_bp.GetNumLocations() == selector_bp.GetNumLocations(), 'Make sure setting a breakpoint by name "isEqual:" only sets selector breakpoints') + for bp_loc in selector_bp: + function_name = bp_loc.GetAddress().GetSymbol().GetName() + self.assertTrue(" isEqual:]" in function_name, 'Make sure all function names have " isEqual:]" in their names') + + self.check_category_breakpoints() + + if have_dsym: + shutil.rmtree(exe + ".dSYM") + self.assertTrue(subprocess.call(['/usr/bin/strip', '-Sx', exe]) == 0, 'stripping dylib succeeded') + + # Check breakpoints again, this time using the symbol table only + self.check_category_breakpoints() diff --git a/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/main.m b/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/main.m new file mode 100644 index 000000000000..53567491219b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/breakpoint/objc/main.m @@ -0,0 +1,98 @@ +#import +#include + +@interface MyClass : NSObject +@end + +@implementation MyClass : NSObject +@end + +@implementation MyClass (MyCategory) + + +- (void) myCategoryFunction { + NSLog (@"myCategoryFunction"); +} + +@end + + + +int +Test_Selector () +{ + SEL sel = @selector(length); + printf("sel = %p\n", sel); + // Expressions to test here for selector: + // expression (char *)sel_getName(sel) + // The expression above should return "sel" as it should be just + // a uniqued C string pointer. We were seeing the result pointer being + // truncated with recent LLDBs. + return 0; // Break here for selector: tests +} + +int +Test_NSString (const char *program) +{ + NSString *str = [NSString stringWithFormat:@"Hello from '%s'", program]; + NSLog(@"NSString instance: %@", str); + printf("str = '%s'\n", [str cStringUsingEncoding: [NSString defaultCStringEncoding]]); + printf("[str length] = %zu\n", (size_t)[str length]); + printf("[str description] = %s\n", [[str description] UTF8String]); + id str_id = str; + // Expressions to test here for NSString: + // expression (char *)sel_getName(sel) + // expression [str length] + // expression [str_id length] + // expression [str description] + // expression [str_id description] + // expression str.length + // expression str.description + // expression str = @"new" + // expression str = [NSString stringWithFormat: @"%cew", 'N'] + return 0; // Break here for NSString tests +} + +NSString *my_global_str = NULL; + +int +Test_NSArray () +{ + NSMutableArray *nil_mutable_array = nil; + NSArray *array1 = [NSArray arrayWithObjects: @"array1 object1", @"array1 object2", @"array1 object3", nil]; + NSArray *array2 = [NSArray arrayWithObjects: array1, @"array2 object2", @"array2 object3", nil]; + // Expressions to test here for NSArray: + // expression [nil_mutable_array count] + // expression [array1 count] + // expression array1.count + // expression [array2 count] + // expression array2.count + id obj; + // After each object at index call, use expression and validate object + obj = [array1 objectAtIndex: 0]; // Break here for NSArray tests + obj = [array1 objectAtIndex: 1]; + obj = [array1 objectAtIndex: 2]; + + obj = [array2 objectAtIndex: 0]; + obj = [array2 objectAtIndex: 1]; + obj = [array2 objectAtIndex: 2]; + NSUInteger count = [nil_mutable_array count]; + return 0; +} + + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + Test_Selector(); // Set breakpoint here + Test_NSArray (); + Test_NSString (argv[0]); + MyClass *my_class = [[MyClass alloc] init]; + [my_class myCategoryFunction]; + printf("sizeof(id) = %zu\n", sizeof(id)); + printf("sizeof(Class) = %zu\n", sizeof(Class)); + printf("sizeof(SEL) = %zu\n", sizeof(SEL)); + + [pool release]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/command_history/.categories b/packages/Python/lldbsuite/test/functionalities/command_history/.categories new file mode 100644 index 000000000000..3a3f4df6416b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_history/.categories @@ -0,0 +1 @@ +cmdline diff --git a/packages/Python/lldbsuite/test/functionalities/command_history/TestCommandHistory.py b/packages/Python/lldbsuite/test/functionalities/command_history/TestCommandHistory.py new file mode 100644 index 000000000000..312c91517f98 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_history/TestCommandHistory.py @@ -0,0 +1,68 @@ +""" +Test the command history mechanism +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class CommandHistoryTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_history(self): + self.runCmd('command history --clear', inHistory=False) + self.runCmd('breakpoint list', check=False, inHistory=True) #0 + self.runCmd('register read', check=False, inHistory=True) #1 + self.runCmd('apropos hello', check=False, inHistory=True) #2 + self.runCmd('memory write', check=False, inHistory=True) #3 + self.runCmd('log list', check=False, inHistory=True) #4 + self.runCmd('disassemble', check=False, inHistory=True) #5 + self.runCmd('expression 1', check=False, inHistory=True) #6 + self.runCmd('type summary list -w default', check=False, inHistory=True) #7 + self.runCmd('version', check=False, inHistory=True) #8 + self.runCmd('frame select 1', check=False, inHistory=True) #9 + + self.expect ("command history -s 3 -c 3", inHistory=True, + substrs = ['3: memory write','4: log list','5: disassemble']) + + self.expect ("command history -s 3 -e 3", inHistory=True, + substrs = ['3: memory write']) + + self.expect ("command history -s 6 -e 7", inHistory=True, + substrs = ['6: expression 1','7: type summary list -w default']) + + self.expect ("command history -c 2", inHistory=True, + substrs = ['0: breakpoint list','1: register read']) + + self.expect ("command history -e 3 -c 1", inHistory=True, + substrs = ['3: memory write']) + + self.expect ("command history -e 2", inHistory=True, + substrs = ['0: breakpoint list','1: register read','2: apropos hello']) + + self.expect ("command history -s 12", inHistory=True, + substrs = ['12: command history -s 6 -e 7','13: command history -c 2','14: command history -e 3 -c 1','15: command history -e 2','16: command history -s 12']) + + self.expect ("command history -s end -c 3", inHistory=True, + substrs = ['15: command history -e 2','16: command history -s 12','17: command history -s end -c 3']) + + self.expect ("command history -s end -e 15", inHistory=True, + substrs = ['15: command history -e 2','16: command history -s 12','17: command history -s end -c 3','command history -s end -e 15']) + + self.expect ("command history -s 5 -c 1", inHistory=True, + substrs = ['5: disassemble']) + + self.expect ("command history -c 1 -s 5", inHistory=True, + substrs = ['5: disassemble']) + + self.expect ("command history -c 1 -e 3", inHistory=True, + substrs = ['3: memory write']) + + self.expect ("command history -c 1 -e 3 -s 5",error=True, inHistory=True, + substrs = ['error: --count, --start-index and --end-index cannot be all specified in the same invocation']) diff --git a/packages/Python/lldbsuite/test/functionalities/command_regex/.categories b/packages/Python/lldbsuite/test/functionalities/command_regex/.categories new file mode 100644 index 000000000000..3a3f4df6416b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_regex/.categories @@ -0,0 +1 @@ +cmdline diff --git a/packages/Python/lldbsuite/test/functionalities/command_regex/TestCommandRegex.py b/packages/Python/lldbsuite/test/functionalities/command_regex/TestCommandRegex.py new file mode 100644 index 000000000000..2c48efa863b4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_regex/TestCommandRegex.py @@ -0,0 +1,56 @@ +""" +Test lldb 'commands regex' command which allows the user to create a regular expression command. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class CommandRegexTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureHostWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @no_debug_info_test + def test_command_regex(self): + """Test a simple scenario of 'command regex' invocation and subsequent use.""" + import pexpect + prompt = "(lldb) " + regex_prompt = "Enter one of more sed substitution commands in the form: 's///'.\r\nTerminate the substitution list with an empty line.\r\n" + regex_prompt1 = "\r\n" + + child = pexpect.spawn('%s %s' % (lldbtest_config.lldbExec, self.lldbOption)) + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + # So that the spawned lldb session gets shutdown durng teardown. + self.child = child + + # Substitute 'Help!' for 'help' using the 'commands regex' mechanism. + child.expect_exact(prompt) + child.sendline("command regex 'Help__'") + child.expect_exact(regex_prompt) + child.sendline('s/^$/help/') + child.expect_exact(regex_prompt1) + child.sendline('') + child.expect_exact(prompt) + # Help! + child.sendline('Help__') + # If we see the familiar 'help' output, the test is done. + child.expect('Debugger commands:') + # Try and incorrectly remove "Help__" using "command unalias" and verify we fail + child.sendline('command unalias Help__') + child.expect_exact("error: 'Help__' is not an alias, it is a debugger command which can be removed using the 'command delete' command") + child.expect_exact(prompt) + + # Delete the regex command using "command delete" + child.sendline('command delete Help__') + child.expect_exact(prompt) + # Verify the command was removed + child.sendline('Help__') + child.expect_exact("error: 'Help__' is not a valid command") + child.expect_exact(prompt) diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/.categories b/packages/Python/lldbsuite/test/functionalities/command_script/.categories new file mode 100644 index 000000000000..3a3f4df6416b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/.categories @@ -0,0 +1 @@ +cmdline diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/Makefile b/packages/Python/lldbsuite/test/functionalities/command_script/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/TestCommandScript.py b/packages/Python/lldbsuite/test/functionalities/command_script/TestCommandScript.py new file mode 100644 index 000000000000..542ee7a1e5f3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/TestCommandScript.py @@ -0,0 +1,142 @@ +""" +Test lldb Python commands. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class CmdPythonTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test (self): + self.build () + self.pycmd_tests () + + def pycmd_tests (self): + self.runCmd("command source py_import") + + # Verify command that specifies eCommandRequiresTarget returns failure + # without a target. + self.expect('targetname', + substrs = ['a.out'], matching=False, error=True) + + exe = os.path.join (os.getcwd(), "a.out") + self.expect("file " + exe, + patterns = [ "Current executable set to .*a.out" ]) + + self.expect('targetname', + substrs = ['a.out'], matching=True, error=False) + + # This is the function to remove the custom commands in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('command script delete welcome', check=False) + self.runCmd('command script delete targetname', check=False) + self.runCmd('command script delete longwait', check=False) + self.runCmd('command script delete mysto', check=False) + self.runCmd('command script delete tell_sync', check=False) + self.runCmd('command script delete tell_async', check=False) + self.runCmd('command script delete tell_curr', check=False) + self.runCmd('command script delete bug11569', check=False) + self.runCmd('command script delete takes_exe_ctx', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Interact with debugger in synchronous mode + self.setAsync(False) + + # We don't want to display the stdout if not in TraceOn() mode. + if not self.TraceOn(): + self.HideStdout() + + self.expect('welcome Enrico', + substrs = ['Hello Enrico, welcome to LLDB']); + + self.expect("help welcome", + substrs = ['Just a docstring for welcome_impl', + 'A command that says hello to LLDB users']) + + self.expect("help", + substrs = ['For more information run', + 'welcome']) + + self.expect("help -a", + substrs = ['For more information run', + 'welcome']) + + self.expect("help -u", matching=False, + substrs = ['For more information run']) + + self.runCmd("command script delete welcome"); + + self.expect('welcome Enrico', matching=False, error=True, + substrs = ['Hello Enrico, welcome to LLDB']); + + self.expect('targetname fail', error=True, + substrs = ['a test for error in command']) + + self.expect('command script list', + substrs = ['targetname', + 'For more information run']) + + self.expect("help targetname", + substrs = ['This', 'command', 'takes', '\'raw\'', 'input', + 'quote', 'stuff']) + + self.expect("longwait", + substrs = ['Done; if you saw the delays I am doing OK']) + + self.runCmd("b main") + self.runCmd("run") + self.runCmd("mysto 3") + self.expect("frame variable array", + substrs = ['[0] = 79630','[1] = 388785018','[2] = 0']) + self.runCmd("mysto 3") + self.expect("frame variable array", + substrs = ['[0] = 79630','[4] = 388785018','[5] = 0']) + +# we cannot use the stepover command to check for async execution mode since LLDB +# seems to get confused when events start to queue up + self.expect("tell_sync", + substrs = ['running sync']) + self.expect("tell_async", + substrs = ['running async']) + self.expect("tell_curr", + substrs = ['I am running sync']) + +# check that the execution context is passed in to commands that ask for it + self.expect("takes_exe_ctx", substrs = ["a.out"]) + + # Test that a python command can redefine itself + self.expect('command script add -f foobar welcome -h "just some help"') + + self.runCmd("command script clear") + + # Test that re-defining an existing command works + self.runCmd('command script add my_command --class welcome.WelcomeCommand') + self.expect('my_command Blah', substrs = ['Hello Blah, welcome to LLDB']) + + self.runCmd('command script add my_command --class welcome.TargetnameCommand') + self.expect('my_command', substrs = ['a.out']) + + self.runCmd("command script clear") + + self.expect('command script list', matching=False, + substrs = ['targetname', + 'longwait']) + + self.expect('command script add -f foobar frame', error=True, + substrs = ['cannot add command']) + + # http://llvm.org/bugs/show_bug.cgi?id=11569 + # LLDBSwigPythonCallCommand crashes when a command script returns an object + self.runCmd('command script add -f bug11569 bug11569') + # This should not crash. + self.runCmd('bug11569', check=False) diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/bug11569.py b/packages/Python/lldbsuite/test/functionalities/command_script/bug11569.py new file mode 100644 index 000000000000..93897d88098f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/bug11569.py @@ -0,0 +1,7 @@ +def bug11569(debugger, args, result, dict): + """ + http://llvm.org/bugs/show_bug.cgi?id=11569 + LLDBSwigPythonCallCommand crashes when a command script returns an object. + """ + return ["return", "a", "non-string", "should", "not", "crash", "LLDB"]; + diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/Makefile b/packages/Python/lldbsuite/test/functionalities/command_script/import/Makefile new file mode 100644 index 000000000000..9374aef487fe --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +EXE := hello_world + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/TestImport.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/TestImport.py new file mode 100644 index 000000000000..691045ae82ed --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/TestImport.py @@ -0,0 +1,73 @@ +"""Test custom import command to import files by path.""" + +from __future__ import print_function + + + +import os, sys, time +import lldb +from lldbsuite.test.lldbtest import * + +class ImportTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_import_command(self): + """Import some Python scripts by path and test them""" + self.run_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def run_test(self): + """Import some Python scripts by path and test them.""" + + # This is the function to remove the custom commands in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('command script delete foo2cmd', check=False) + self.runCmd('command script delete foocmd', check=False) + self.runCmd('command script delete foobarcmd', check=False) + self.runCmd('command script delete barcmd', check=False) + self.runCmd('command script delete barothercmd', check=False) + self.runCmd('command script delete TPcommandA', check=False) + self.runCmd('command script delete TPcommandB', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("command script import ./foo/foo.py --allow-reload") + self.runCmd("command script import ./foo/foo2.py --allow-reload") + self.runCmd("command script import ./foo/bar/foobar.py --allow-reload") + self.runCmd("command script import ./bar/bar.py --allow-reload") + + self.expect("command script import ./nosuchfile.py", + error=True, startstr='error: module importing failed') + self.expect("command script import ./nosuchfolder/", + error=True, startstr='error: module importing failed') + self.expect("command script import ./foo/foo.py", error=False) + + self.runCmd("command script import --allow-reload ./thepackage") + self.expect("TPcommandA",substrs=["hello world A"]) + self.expect("TPcommandB",substrs=["hello world B"]) + + self.runCmd("script import dummymodule") + self.expect("command script import ./dummymodule.py", error=False) + self.expect("command script import --allow-reload ./dummymodule.py", error=False) + + self.runCmd("command script add -f foo.foo_function foocmd") + self.runCmd("command script add -f foobar.foo_function foobarcmd") + self.runCmd("command script add -f bar.bar_function barcmd") + self.expect("foocmd hello", + substrs = ['foo says', 'hello']) + self.expect("foo2cmd hello", + substrs = ['foo2 says', 'hello']) + self.expect("barcmd hello", + substrs = ['barutil says', 'bar told me', 'hello']) + self.expect("barothercmd hello", + substrs = ['barutil says', 'bar told me', 'hello']) + self.expect("foobarcmd hello", + substrs = ['foobar says', 'hello']) diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/bar/bar.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/bar/bar.py new file mode 100644 index 000000000000..bbc41f3b217d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/bar/bar.py @@ -0,0 +1,12 @@ +from __future__ import print_function + +def bar_function(debugger, args, result, dict): + global UtilityModule + print(UtilityModule.barutil_function("bar told me " + args), file=result) + return None + +def __lldb_init_module(debugger, session_dict): + global UtilityModule + UtilityModule = __import__("barutil") + debugger.HandleCommand("command script add -f bar.bar_function barothercmd") + return None \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/bar/barutil.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/bar/barutil.py new file mode 100644 index 000000000000..0d3d2eb1b2d7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/bar/barutil.py @@ -0,0 +1,2 @@ +def barutil_function(x): + return "barutil says: " + x diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/dummymodule.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/dummymodule.py new file mode 100644 index 000000000000..dcc724ec9c23 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/dummymodule.py @@ -0,0 +1,2 @@ +def no_useful_code(foo): + return foo diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/bar/foobar.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/bar/foobar.py new file mode 100644 index 000000000000..659ded22c901 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/bar/foobar.py @@ -0,0 +1,5 @@ +from __future__ import print_function + +def foo_function(debugger, args, result, dict): + print("foobar says " + args, file=result) + return None diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/foo.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/foo.py new file mode 100644 index 000000000000..51cc0c3bab19 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/foo.py @@ -0,0 +1,5 @@ +from __future__ import print_function + +def foo_function(debugger, args, result, dict): + print("foo says " + args, file=result) + return None diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/foo2.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/foo2.py new file mode 100644 index 000000000000..6863454ca6ef --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/foo/foo2.py @@ -0,0 +1,9 @@ +from __future__ import print_function + +def foo2_function(debugger, args, result, dict): + print("foo2 says " + args, file=result) + return None + +def __lldb_init_module(debugger, session_dict): + debugger.HandleCommand("command script add -f foo2.foo2_function foo2cmd") + return None \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/main.c b/packages/Python/lldbsuite/test/functionalities/command_script/import/main.c new file mode 100644 index 000000000000..dffc8c77b04c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/main.c @@ -0,0 +1,15 @@ +#include + +int main(int argc, char const *argv[]) { + printf("Hello world.\n"); // Set break point at this line. + if (argc == 1) + return 0; + + // Waiting to be attached by the debugger, otherwise. + char line[100]; + while (fgets(line, sizeof(line), stdin)) { // Waiting to be attached... + printf("input line=>%s\n", line); + } + + printf("Exiting now\n"); +} diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/Makefile b/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/Makefile new file mode 100644 index 000000000000..7913aaa4b746 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/Makefile @@ -0,0 +1,3 @@ +LEVEL = ../../../../make + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/TestRdar12586188.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/TestRdar12586188.py new file mode 100644 index 000000000000..9800a08c06de --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/TestRdar12586188.py @@ -0,0 +1,31 @@ +"""Check that we handle an ImportError in a special way when command script importing files.""" + +from __future__ import print_function + + + +import os, sys, time +import lldb +from lldbsuite.test.lldbtest import * + +class Rdar12586188TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_rdar12586188_command(self): + """Check that we handle an ImportError in a special way when command script importing files.""" + self.run_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def run_test(self): + """Check that we handle an ImportError in a special way when command script importing files.""" + + self.expect("command script import ./fail12586188.py --allow-reload", + error=True, substrs = ['raise ImportError("I do not want to be imported")']) + self.expect("command script import ./fail212586188.py --allow-reload", + error=True, substrs = ['raise ValueError("I do not want to be imported")']) diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/fail12586188.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/fail12586188.py new file mode 100644 index 000000000000..add85a73f853 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/fail12586188.py @@ -0,0 +1,4 @@ +def f(x): + return x + 1 + +raise ImportError("I do not want to be imported") diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/fail212586188.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/fail212586188.py new file mode 100644 index 000000000000..1549a036590f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/rdar-12586188/fail212586188.py @@ -0,0 +1,4 @@ +def f(x): + return x + 1 + +raise ValueError("I do not want to be imported") diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/TPunitA.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/TPunitA.py new file mode 100644 index 000000000000..fb65305d205c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/TPunitA.py @@ -0,0 +1,6 @@ + +import six + +def command(debugger, command, result, internal_dict): + result.PutCString(six.u("hello world A")) + return None diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/TPunitB.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/TPunitB.py new file mode 100644 index 000000000000..60b31b89f6da --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/TPunitB.py @@ -0,0 +1,6 @@ + +import six + +def command(debugger, command, result, internal_dict): + result.PutCString(six.u("hello world B")) + return None diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/__init__.py b/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/__init__.py new file mode 100644 index 000000000000..faa4e3b0cbff --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/import/thepackage/__init__.py @@ -0,0 +1,6 @@ +import TPunitA +import TPunitB + +def __lldb_init_module(debugger,*args): + debugger.HandleCommand("command script add -f thepackage.TPunitA.command TPcommandA") + debugger.HandleCommand("command script add -f thepackage.TPunitB.command TPcommandB") diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/main.cpp b/packages/Python/lldbsuite/test/functionalities/command_script/main.cpp new file mode 100644 index 000000000000..0b24cb73a619 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/main.cpp @@ -0,0 +1,70 @@ +//===-- 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 +#include +#include +#include + +int +product (int x, int y) +{ + int result = x * y; + return result; +} + +int +sum (int a, int b) +{ + int result = a + b; + return result; +} + +int +strange_max (int m, int n) +{ + if (m > n) + return m; + else if (n > m) + return n; + else + return 0; +} + +int +foo (int i, int j) +{ + if (strange_max (i, j) == i) + return product (i, j); + else if (strange_max (i, j) == j) + return sum (i, j); + else + return product (sum (i, i), sum (j, j)); +} + +int +main(int argc, char const *argv[]) +{ + + int array[9]; + memset(array,0,9*sizeof(int)); + + array[0] = foo (1238, 78392); + array[1] = foo (379265, 23674); + array[2] = foo (872934, 234); + array[3] = foo (1238, 78392); + array[4] = foo (379265, 23674); + array[5] = foo (872934, 234); + array[6] = foo (1238, 78392); + array[7] = foo (379265, 23674); + array[8] = foo (872934, 234); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/mysto.py b/packages/Python/lldbsuite/test/functionalities/command_script/mysto.py new file mode 100644 index 000000000000..656cd1502930 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/mysto.py @@ -0,0 +1,23 @@ +from __future__ import print_function + +import lldb +import sys +import os +import time + +def StepOver(debugger, args, result, dict): + """ + Step over a given number of times instead of only just once + """ + arg_split = args.split(" ") + print(type(arg_split)) + count = int(arg_split[0]) + for i in range(0,count): + debugger.GetSelectedTarget().GetProcess().GetSelectedThread().StepOver(lldb.eOnlyThisThread) + print("step<%d>"%i) + +def __lldb_init_module(debugger, session_dict): + # by default, --synchronicity is set to synchronous + debugger.HandleCommand("command script add -f mysto.StepOver mysto") + return None + diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/py_import b/packages/Python/lldbsuite/test/functionalities/command_script/py_import new file mode 100644 index 000000000000..169daacc1a83 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/py_import @@ -0,0 +1,12 @@ +script import sys, os +script sys.path.append(os.path.join(os.getcwd(), os.pardir)) +script import welcome +script import bug11569 +command script add welcome --class welcome.WelcomeCommand +command script add targetname --class welcome.TargetnameCommand +command script add longwait --function welcome.print_wait_impl +command script import mysto.py --allow-reload +command script add tell_sync --function welcome.check_for_synchro --synchronicity sync +command script add tell_async --function welcome.check_for_synchro --synchronicity async +command script add tell_curr --function welcome.check_for_synchro --synchronicity curr +command script add takes_exe_ctx --function welcome.takes_exe_ctx diff --git a/packages/Python/lldbsuite/test/functionalities/command_script/welcome.py b/packages/Python/lldbsuite/test/functionalities/command_script/welcome.py new file mode 100644 index 000000000000..5dbf09fbbec2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_script/welcome.py @@ -0,0 +1,46 @@ +from __future__ import print_function +import lldb, sys + +class WelcomeCommand(object): + def __init__(self, debugger, session_dict): + pass + + def get_short_help(self): + return "Just a docstring for welcome_impl\nA command that says hello to LLDB users" + + def __call__(self, debugger, args, exe_ctx, result): + print('Hello ' + args + ', welcome to LLDB', file=result); + return None; + +class TargetnameCommand(object): + def __init__(self, debugger, session_dict): + pass + + def __call__(self, debugger, args, exe_ctx, result): + target = debugger.GetSelectedTarget() + file = target.GetExecutable() + print('Current target ' + file.GetFilename(), file=result) + if args == 'fail': + result.SetError('a test for error in command') + + def get_flags(self): + return lldb.eCommandRequiresTarget + +def print_wait_impl(debugger, args, result, dict): + result.SetImmediateOutputFile(sys.stdout) + print('Trying to do long task..', file=result) + import time + time.sleep(1) + print('Still doing long task..', file=result) + time.sleep(1) + print('Done; if you saw the delays I am doing OK', file=result) + +def check_for_synchro(debugger, args, result, dict): + if debugger.GetAsync() == True: + print('I am running async', file=result) + if debugger.GetAsync() == False: + print('I am running sync', file=result) + +def takes_exe_ctx(debugger, args, exe_ctx, result, dict): + print(str(exe_ctx.GetTarget()), file=result) + diff --git a/packages/Python/lldbsuite/test/functionalities/command_source/.categories b/packages/Python/lldbsuite/test/functionalities/command_source/.categories new file mode 100644 index 000000000000..3a3f4df6416b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_source/.categories @@ -0,0 +1 @@ +cmdline diff --git a/packages/Python/lldbsuite/test/functionalities/command_source/.lldb b/packages/Python/lldbsuite/test/functionalities/command_source/.lldb new file mode 100644 index 000000000000..c544523832e7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_source/.lldb @@ -0,0 +1 @@ +script import my diff --git a/packages/Python/lldbsuite/test/functionalities/command_source/TestCommandSource.py b/packages/Python/lldbsuite/test/functionalities/command_source/TestCommandSource.py new file mode 100644 index 000000000000..013803e698af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_source/TestCommandSource.py @@ -0,0 +1,36 @@ +""" +Test that lldb command "command source" works correctly. + +See also http://llvm.org/viewvc/llvm-project?view=rev&revision=109673. +""" + +from __future__ import print_function + + + +import os, sys +import lldb +from lldbsuite.test.lldbtest import * + +class CommandSourceTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_command_source(self): + """Test that lldb command "command source" works correctly.""" + + # Sourcing .lldb in the current working directory, which in turn imports + # the "my" package that defines the date() function. + self.runCmd("command source .lldb") + + # Python should evaluate "my.date()" successfully. + command_interpreter = self.dbg.GetCommandInterpreter() + self.assertTrue(command_interpreter, VALID_COMMAND_INTERPRETER) + result = lldb.SBCommandReturnObject() + command_interpreter.HandleCommand("script my.date()", result) + + import datetime + self.expect(result.GetOutput(), "script my.date() runs successfully", + exe=False, + substrs = [str(datetime.date.today())]) diff --git a/packages/Python/lldbsuite/test/functionalities/command_source/my.py b/packages/Python/lldbsuite/test/functionalities/command_source/my.py new file mode 100644 index 000000000000..cb2fd012e4bd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/command_source/my.py @@ -0,0 +1,6 @@ +from __future__ import print_function + +def date(): + import datetime + today = datetime.date.today() + print(today) diff --git a/packages/Python/lldbsuite/test/functionalities/completion/.categories b/packages/Python/lldbsuite/test/functionalities/completion/.categories new file mode 100644 index 000000000000..3a3f4df6416b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/completion/.categories @@ -0,0 +1 @@ +cmdline diff --git a/packages/Python/lldbsuite/test/functionalities/completion/Makefile b/packages/Python/lldbsuite/test/functionalities/completion/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/completion/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py b/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py new file mode 100644 index 000000000000..d7aab03c0830 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py @@ -0,0 +1,324 @@ +""" +Test the lldb command line completion mechanism. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class CommandLineCompletionTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @classmethod + def classCleanup(cls): + """Cleanup the test byproducts.""" + try: + os.remove("child_send.txt") + os.remove("child_read.txt") + except: + pass + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_at(self): + """Test that 'at' completes to 'attach '.""" + self.complete_from_to('at', 'attach ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_de(self): + """Test that 'de' completes to 'detach '.""" + self.complete_from_to('de', 'detach ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_process_attach_dash_dash_con(self): + """Test that 'process attach --con' completes to 'process attach --continue '.""" + self.complete_from_to('process attach --con', 'process attach --continue ') + + # + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_infinite_loop_while_completing(self): + """Test that 'process print hello\' completes to itself and does not infinite loop.""" + self.complete_from_to('process print hello\\', 'process print hello\\', + turn_off_re_match=True) + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_watchpoint_co(self): + """Test that 'watchpoint co' completes to 'watchpoint command '.""" + self.complete_from_to('watchpoint co', 'watchpoint command ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_watchpoint_command_space(self): + """Test that 'watchpoint command ' completes to ['Available completions:', 'add', 'delete', 'list'].""" + self.complete_from_to('watchpoint command ', ['Available completions:', 'add', 'delete', 'list']) + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_watchpoint_command_a(self): + """Test that 'watchpoint command a' completes to 'watchpoint command add '.""" + self.complete_from_to('watchpoint command a', 'watchpoint command add ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_watchpoint_set_variable_dash_w(self): + """Test that 'watchpoint set variable -w' completes to 'watchpoint set variable -w '.""" + self.complete_from_to('watchpoint set variable -w', 'watchpoint set variable -w ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_watchpoint_set_variable_dash_w_space(self): + """Test that 'watchpoint set variable -w ' completes to ['Available completions:', 'read', 'write', 'read_write'].""" + self.complete_from_to('watchpoint set variable -w ', ['Available completions:', 'read', 'write', 'read_write']) + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_watchpoint_set_ex(self): + """Test that 'watchpoint set ex' completes to 'watchpoint set expression '.""" + self.complete_from_to('watchpoint set ex', 'watchpoint set expression ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_watchpoint_set_var(self): + """Test that 'watchpoint set var' completes to 'watchpoint set variable '.""" + self.complete_from_to('watchpoint set var', 'watchpoint set variable ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_watchpoint_set_variable_dash_w_read_underbar(self): + """Test that 'watchpoint set variable -w read_' completes to 'watchpoint set variable -w read_write'.""" + self.complete_from_to('watchpoint set variable -w read_', 'watchpoint set variable -w read_write') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_help_fi(self): + """Test that 'help fi' completes to ['Available completions:', 'file', 'finish'].""" + self.complete_from_to('help fi', ['Available completions:', 'file', 'finish']) + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_help_watchpoint_s(self): + """Test that 'help watchpoint s' completes to 'help watchpoint set '.""" + self.complete_from_to('help watchpoint s', 'help watchpoint set ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_append_target_er(self): + """Test that 'settings append target.er' completes to 'settings append target.error-path'.""" + self.complete_from_to('settings append target.er', 'settings append target.error-path') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_insert_after_target_en(self): + """Test that 'settings insert-after target.env' completes to 'settings insert-after target.env-vars'.""" + self.complete_from_to('settings insert-after target.env', 'settings insert-after target.env-vars') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_insert_before_target_en(self): + """Test that 'settings insert-before target.env' completes to 'settings insert-before target.env-vars'.""" + self.complete_from_to('settings insert-before target.env', 'settings insert-before target.env-vars') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_replace_target_ru(self): + """Test that 'settings replace target.ru' completes to 'settings replace target.run-args'.""" + self.complete_from_to('settings replace target.ru', 'settings replace target.run-args') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_s(self): + """Test that 'settings s' completes to ['Available completions:', 'set', 'show'].""" + self.complete_from_to('settings s', ['Available completions:', 'set', 'show']) + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_set_th(self): + """Test that 'settings set th' completes to 'settings set thread-format'.""" + self.complete_from_to('settings set th', 'settings set thread-format') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_s_dash(self): + """Test that 'settings set -' completes to 'settings set -g'.""" + self.complete_from_to('settings set -', 'settings set -g') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_clear_th(self): + """Test that 'settings clear th' completes to 'settings clear thread-format'.""" + self.complete_from_to('settings clear th', 'settings clear thread-format') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_set_ta(self): + """Test that 'settings set ta' completes to 'settings set target.'.""" + self.complete_from_to('settings set target.ma', 'settings set target.max-') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_set_target_exec(self): + """Test that 'settings set target.exec' completes to 'settings set target.exec-search-paths '.""" + self.complete_from_to('settings set target.exec', 'settings set target.exec-search-paths') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_set_target_pr(self): + """Test that 'settings set target.pr' completes to ['Available completions:', + 'target.prefer-dynamic-value', 'target.process.'].""" + self.complete_from_to('settings set target.pr', + ['Available completions:', + 'target.prefer-dynamic-value', + 'target.process.']) + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_set_target_process(self): + """Test that 'settings set target.process' completes to 'settings set target.process.'.""" + self.complete_from_to('settings set target.process', 'settings set target.process.') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_set_target_process_dot(self): + """Test that 'settings set target.process.t' completes to 'settings set target.process.thread.'.""" + self.complete_from_to('settings set target.process.t', 'settings set target.process.thread.') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_settings_set_target_process_thread_dot(self): + """Test that 'settings set target.process.thread.' completes to ['Available completions:', + 'target.process.thread.step-avoid-regexp', 'target.process.thread.trace-thread'].""" + self.complete_from_to('settings set target.process.thread.', + ['Available completions:', + 'target.process.thread.step-avoid-regexp', + 'target.process.thread.trace-thread']) + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_target_space(self): + """Test that 'target ' completes to ['Available completions:', 'create', 'delete', 'list', + 'modules', 'select', 'stop-hook', 'variable'].""" + self.complete_from_to('target ', + ['Available completions:', 'create', 'delete', 'list', + 'modules', 'select', 'stop-hook', 'variable']) + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_target_create_dash_co(self): + """Test that 'target create --co' completes to 'target variable --core '.""" + self.complete_from_to('target create --co', 'target create --core ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @skipIfFreeBSD # timing out on the FreeBSD buildbot + @no_debug_info_test + def test_target_va(self): + """Test that 'target va' completes to 'target variable '.""" + self.complete_from_to('target va', 'target variable ') + + @expectedFailureHostWindows("llvm.org/pr24679") + @expectedFailureDarwin("llvm.org/pr25485") + def test_symbol_name(self): + self.build() + self.complete_from_to('''file a.out + breakpoint set -n Fo''', + 'breakpoint set -n Foo::Bar(int,\\ int)', + turn_off_re_match=True) + + def complete_from_to(self, str_input, patterns, turn_off_re_match=False): + """Test that the completion mechanism completes str_input to patterns, + where patterns could be a pattern-string or a list of pattern-strings""" + import pexpect + # Patterns should not be None in order to proceed. + self.assertFalse(patterns is None) + # And should be either a string or list of strings. Check for list type + # below, if not, make a list out of the singleton string. If patterns + # is not a string or not a list of strings, there'll be runtime errors + # later on. + if not isinstance(patterns, list): + patterns = [patterns] + + # The default lldb prompt. + prompt = "(lldb) " + + # So that the child gets torn down after the test. + self.child = pexpect.spawn(lldbtest_config.lldbExec, + [self.lldbOption] + ['--no-use-colors']) + child = self.child + # Turn on logging for input/output to/from the child. + with open('child_send.txt', 'w') as f_send: + with open('child_read.txt', 'w') as f_read: + child.logfile_send = f_send + child.logfile_read = f_read + + child.expect_exact(prompt) + child.setecho(True) + # Sends str_input and a Tab to invoke the completion machinery. + child.send("%s\t" % str_input) + child.sendline('') + child.expect_exact(prompt) + child.sendline('') + child.expect_exact(prompt) + + # Now that the necessary logging is done, restore logfile to None to + # stop further logging. + child.logfile_send = None + child.logfile_read = None + + with open('child_send.txt', 'r') as fs: + if self.TraceOn(): + print("\n\nContents of child_send.txt:") + print(fs.read()) + with open('child_read.txt', 'r') as fr: + from_child = fr.read() + if self.TraceOn(): + print("\n\nContents of child_read.txt:") + print(from_child) + + # The matching could be verbatim or using generic re pattern. + for p in patterns: + # Test that str_input completes to our patterns or substrings. + # If each pattern/substring matches from_child, the completion mechanism works! + if turn_off_re_match: + self.expect(from_child, msg=COMPLETION_MSG(str_input, p), exe=False, + substrs = [p]) + else: + self.expect(from_child, msg=COMPLETION_MSG(str_input, p), exe=False, + patterns = [p]) diff --git a/packages/Python/lldbsuite/test/functionalities/completion/main.cpp b/packages/Python/lldbsuite/test/functionalities/completion/main.cpp new file mode 100644 index 000000000000..b408720d2cdc --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/completion/main.cpp @@ -0,0 +1,14 @@ +class Foo +{ +public: + int Bar(int x, int y) + { + return x + y; + } +}; + +int main() +{ + Foo f; + f.Bar(1, 2); +} diff --git a/packages/Python/lldbsuite/test/functionalities/conditional_break/.lldb b/packages/Python/lldbsuite/test/functionalities/conditional_break/.lldb new file mode 100644 index 000000000000..4be90efee23b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/conditional_break/.lldb @@ -0,0 +1,3 @@ +breakpoint set -n c +command script import -r conditional_break.py +breakpoint command add 1 -F "conditional_break.stop_if_called_from_a" diff --git a/packages/Python/lldbsuite/test/functionalities/conditional_break/Makefile b/packages/Python/lldbsuite/test/functionalities/conditional_break/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/conditional_break/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/conditional_break/TestConditionalBreak.py b/packages/Python/lldbsuite/test/functionalities/conditional_break/TestConditionalBreak.py new file mode 100644 index 000000000000..3ae7a20b4c7d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/conditional_break/TestConditionalBreak.py @@ -0,0 +1,133 @@ +""" +Test conditionally break on a function and inspect its variables. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +# rdar://problem/8532131 +# lldb not able to digest the clang-generated debug info correctly with respect to function name +# +# This class currently fails for clang as well as llvm-gcc. + +class ConditionalBreakTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + def test_with_python(self): + """Exercise some thread and frame APIs to break if c() is called by a().""" + self.build() + self.do_conditional_break() + + def test_with_command(self): + """Simulate a user using lldb commands to break on c() if called from a().""" + self.build() + self.simulate_conditional_break_by_user() + + def do_conditional_break(self): + """Exercise some thread and frame APIs to break if c() is called by a().""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByName("c", exe) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.assertTrue(process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + # Find the line number where a's parent frame function is c. + line = line_number('main.c', + "// Find the line number where c's parent frame is a here.") + + # Suppose we are only interested in the call scenario where c()'s + # immediate caller is a() and we want to find out the value passed from + # a(). + # + # The 10 in range(10) is just an arbitrary number, which means we would + # like to try for at most 10 times. + for j in range(10): + if self.TraceOn(): + print("j is: ", j) + thread = process.GetThreadAtIndex(0) + + if thread.GetNumFrames() >= 2: + frame0 = thread.GetFrameAtIndex(0) + name0 = frame0.GetFunction().GetName() + frame1 = thread.GetFrameAtIndex(1) + name1 = frame1.GetFunction().GetName() + #lldbutil.print_stacktrace(thread) + self.assertTrue(name0 == "c", "Break on function c()") + if (name1 == "a"): + # By design, we know that a() calls c() only from main.c:27. + # In reality, similar logic can be used to find out the call + # site. + self.assertTrue(frame1.GetLineEntry().GetLine() == line, + "Immediate caller a() at main.c:%d" % line) + + # And the local variable 'val' should have a value of (int) 3. + val = frame1.FindVariable("val") + self.assertTrue(val.GetTypeName() == "int", "'val' has int type") + self.assertTrue(val.GetValue() == "3", "'val' has a value of 3") + break + + process.Continue() + + def simulate_conditional_break_by_user(self): + """Simulate a user using lldb commands to break on c() if called from a().""" + + # Sourcing .lldb in the current working directory, which sets the main + # executable, sets the breakpoint on c(), and adds the callback for the + # breakpoint such that lldb only stops when the caller of c() is a(). + # the "my" package that defines the date() function. + if self.TraceOn(): + print("About to source .lldb") + + if not self.TraceOn(): + self.HideStdout() + + # Separate out the "file a.out" command from .lldb file, for the sake of + # remote testsuite. + self.runCmd("file a.out") + self.runCmd("command source .lldb") + + self.runCmd ("break list") + + if self.TraceOn(): + print("About to run.") + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd ("break list") + + if self.TraceOn(): + print("Done running") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # The frame info for frame #0 points to a.out`c and its immediate caller + # (frame #1) points to a.out`a. + + self.expect("frame info", "We should stop at c()", + substrs = ["a.out`c"]) + + # Select our parent frame as the current frame. + self.runCmd("frame select 1") + self.expect("frame info", "The immediate caller should be a()", + substrs = ["a.out`a"]) diff --git a/packages/Python/lldbsuite/test/functionalities/conditional_break/conditional_break.py b/packages/Python/lldbsuite/test/functionalities/conditional_break/conditional_break.py new file mode 100644 index 000000000000..b30a34e56b14 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/conditional_break/conditional_break.py @@ -0,0 +1,30 @@ +import sys +import lldb + +def stop_if_called_from_a(frame, bp_loc, dict): + + thread = frame.GetThread() + process = thread.GetProcess() + target = process.GetTarget() + dbg = target.GetDebugger() + + # Perform synchronous interaction with the debugger. + old_async = dbg.GetAsync() + dbg.SetAsync(True) + + # We check the call frames in order to stop only when the immediate caller + # of the leaf function c() is a(). If it's not the right caller, we ask the + # command interpreter to continue execution. + + should_stop = True + if thread.GetNumFrames() >= 2: + + if (thread.frames[0].function.name == 'c' and thread.frames[1].function.name == 'a'): + should_stop = True + else: + should_stop = False + + dbg.SetAsync(old_async) + return should_stop + + diff --git a/packages/Python/lldbsuite/test/functionalities/conditional_break/main.c b/packages/Python/lldbsuite/test/functionalities/conditional_break/main.c new file mode 100644 index 000000000000..1329fd69a2e1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/conditional_break/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 + +// This simple program is to demonstrate the capability of the lldb command +// "breakpoint command add" to add a set of commands to a breakpoint to be +// executed when the breakpoint is hit. +// +// In particular, we want to break within c(), but only if the immediate caller +// is a(). + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); // Find the line number where c's parent frame is a here. + + return val; +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/.categories b/packages/Python/lldbsuite/test/functionalities/data-formatter/.categories new file mode 100644 index 000000000000..fe1da0247c62 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/.categories @@ -0,0 +1 @@ +dataformatters diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/Makefile new file mode 100644 index 000000000000..261658b10ae8 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +OBJCXX_SOURCES := main.mm + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/TestFormattersBoolRefPtr.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/TestFormattersBoolRefPtr.py new file mode 100644 index 000000000000..94cdfdfeb343 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/TestFormattersBoolRefPtr.py @@ -0,0 +1,72 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class DataFormatterBoolRefPtr(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_boolrefptr_with_run_command(self): + """Test the formatters we use for BOOL& and BOOL* in Objective-C.""" + self.build() + self.boolrefptr_data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.mm', '// Set break point at this line.') + + def boolrefptr_data_formatter_commands(self): + """Test the formatters we use for BOOL& and BOOL* in Objective-C.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.mm", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Now check that we use the right summary for BOOL& + self.expect('frame variable yes_ref', + substrs = ['YES']) + self.expect('frame variable no_ref', + substrs = ['NO']) + + + # Now check that we use the right summary for BOOL* + self.expect('frame variable yes_ptr', + substrs = ['YES']) + self.expect('frame variable no_ptr', + substrs = ['NO']) + + + # Now check that we use the right summary for BOOL + self.expect('frame variable yes', + substrs = ['YES']) + self.expect('frame variable no', + substrs = ['NO']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/main.mm b/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/main.mm new file mode 100644 index 000000000000..a2461fd9da91 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/boolreference/main.mm @@ -0,0 +1,29 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + BOOL yes = YES; + BOOL no = NO; + + BOOL &yes_ref = yes; + BOOL &no_ref = no; + + BOOL* yes_ptr = &yes; + BOOL* no_ptr = &no; + + [pool drain];// Set break point at this line. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/Makefile new file mode 100644 index 000000000000..9b06ad7d705a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Accelerate \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/TestCompactVectors.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/TestCompactVectors.py new file mode 100644 index 000000000000..7cd2a49b4717 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/TestCompactVectors.py @@ -0,0 +1,58 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CompactVectorsFormattingTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @skipUnlessDarwin + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('frame variable', + substrs = ['(vFloat) valueFL = (1.25, 0, 0.25, 0)', + '(int16_t [8]) valueI16 = (1, 0, 4, 0, 0, 1, 0, 4)', + '(int32_t [4]) valueI32 = (1, 0, 4, 0)', + '(vDouble) valueDL = (1.25, 2.25)', + '(vUInt8) valueU8 = (0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)', + '(vUInt16) valueU16 = (1, 0, 4, 0, 0, 1, 0, 4)', + '(vUInt32) valueU32 = (1, 2, 3, 4)', + "(vSInt8) valueS8 = (1, 0, 4, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0)", + '(vSInt16) valueS16 = (1, 0, 4, 0, 0, 1, 0, 4)', + '(vSInt32) valueS32 = (4, 3, 2, 1)', + '(vBool32) valueBool32 = (0, 1, 0, 1)']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/main.cpp new file mode 100644 index 000000000000..bbbd823ec313 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/compactvectors/main.cpp @@ -0,0 +1,26 @@ + //===-- 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 + +int main() +{ + vFloat valueFL = {1.25,0,0.25,0}; + vDouble valueDL = {1.25,2.25}; + int16_t valueI16[8] = {1,0,4,0,0,1,0,4}; + int32_t valueI32[4] = {1,0,4,0}; + vUInt8 valueU8 = {1,0,4,0,0,1,0,4}; + vUInt16 valueU16 = {1,0,4,0,0,1,0,4}; + vUInt32 valueU32 = {1,2,3,4}; + vSInt8 valueS8 = {1,0,4,0,0,1,0,4}; + vSInt16 valueS16 = {1,0,4,0,0,1,0,4}; + vSInt32 valueS32 = {4,3,2,1}; + vBool32 valueBool32 = {false,true,false,true}; + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/TestDataFormatterAdv.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/TestDataFormatterAdv.py new file mode 100644 index 000000000000..8166d1540ca3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/TestDataFormatterAdv.py @@ -0,0 +1,285 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class AdvDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type summary add --summary-string \"pippo\" \"i_am_cool\"") + + self.runCmd("type summary add --summary-string \"pluto\" -x \"i_am_cool[a-z]*\"") + + self.expect("frame variable cool_boy", + substrs = ['pippo']) + + self.expect("frame variable cooler_boy", + substrs = ['pluto']) + + self.runCmd("type summary delete i_am_cool") + + self.expect("frame variable cool_boy", + substrs = ['pluto']) + + self.runCmd("type summary clear") + + self.runCmd("type summary add --summary-string \"${var[]}\" -x \"int \\[[0-9]\\]") + + self.expect("frame variable int_array", + substrs = ['1,2,3,4,5']) + + # this will fail if we don't do [] as regex correctly + self.runCmd('type summary add --summary-string "${var[].integer}" "i_am_cool[]') + + self.expect("frame variable cool_array", + substrs = ['1,1,1,1,6']) + + self.runCmd("type summary clear") + + self.runCmd("type summary add --summary-string \"${var[1-0]%x}\" \"int\"") + + self.expect("frame variable iAmInt", + substrs = ['01']) + + self.runCmd("type summary add --summary-string \"${var[0-1]%x}\" \"int\"") + + self.expect("frame variable iAmInt", + substrs = ['01']) + + self.runCmd("type summary clear") + + self.runCmd("type summary add --summary-string \"${var[0-1]%x}\" int") + self.runCmd("type summary add --summary-string \"${var[0-31]%x}\" float") + + self.expect("frame variable *pointer", + substrs = ['0x', + '2']) + + # check fix for LLDB crashes when using a "type summary" that uses bitfields with no format + self.runCmd("type summary add --summary-string \"${var[0-1]}\" int") + self.expect("frame variable iAmInt", + substrs = ['9 1']) + + self.expect("frame variable cool_array[3].floating", + substrs = ['0x']) + + self.runCmd("type summary add --summary-string \"low bits are ${*var[0-1]} tgt is ${*var}\" \"int *\"") + + self.expect("frame variable pointer", + substrs = ['low bits are', + 'tgt is 6']) + + self.expect("frame variable int_array --summary-string \"${*var[0-1]}\"", + substrs = ['3']) + + self.runCmd("type summary clear") + + self.runCmd('type summary add --summary-string \"${var[0-1]}\" -x \"int \[[0-9]\]\"') + + self.expect("frame variable int_array", + substrs = ['1,2']) + + self.runCmd('type summary add --summary-string \"${var[0-1]}\" "int []"') + + self.expect("frame variable int_array", + substrs = ['1,2']) + + self.runCmd("type summary clear") + + self.runCmd("type summary add -c -x \"i_am_cool \[[0-9]\]\"") + self.runCmd("type summary add -c i_am_cool") + + self.expect("frame variable cool_array", + substrs = ['[0]', + '[1]', + '[2]', + '[3]', + '[4]', + 'integer', + 'character', + 'floating']) + + self.runCmd("type summary add --summary-string \"int = ${*var.int_pointer}, float = ${*var.float_pointer}\" IWrapPointers") + + self.expect("frame variable wrapper", + substrs = ['int = 4', + 'float = 1.1']) + + self.runCmd("type summary add --summary-string \"low bits = ${*var.int_pointer[2]}\" IWrapPointers -p") + + self.expect("frame variable wrapper", + substrs = ['low bits = 1']) + + self.expect("frame variable *wrap_pointer", + substrs = ['low bits = 1']) + + self.runCmd("type summary clear") + + self.expect("frame variable int_array --summary-string \"${var[0][0-2]%hex}\"", + substrs = ['0x', + '7']) + + self.runCmd("type summary clear") + + self.runCmd("type summary add --summary-string \"${*var[].x[0-3]%hex} is a bitfield on a set of integers\" -x \"SimpleWithPointers \[[0-9]\]\"") + + self.expect("frame variable couple --summary-string \"${*var.sp.x[0-2]} are low bits of integer ${*var.sp.x}. If I pretend it is an array I get ${var.sp.x[0-5]}\"", + substrs = ['1 are low bits of integer 9.', + 'If I pretend it is an array I get [9,']) + + # if the summary has an error, we still display the value + self.expect("frame variable couple --summary-string \"${*var.sp.foo[0-2]\"", + substrs = ['(Couple) couple = {','x = 0x','y = 0x','z = 0x','s = 0x']) + + + self.runCmd("type summary add --summary-string \"${*var.sp.x[0-2]} are low bits of integer ${*var.sp.x}. If I pretend it is an array I get ${var.sp.x[0-5]}\" Couple") + + self.expect("frame variable sparray", + substrs = ['[0x0000000f,0x0000000c,0x00000009]']) + + # check that we can format a variable in a summary even if a format is defined for its datatype + self.runCmd("type format add -f hex int") + self.runCmd("type summary add --summary-string \"x=${var.x%d}\" Simple") + + self.expect("frame variable a_simple_object", + substrs = ['x=3']) + + self.expect("frame variable a_simple_object", matching=False, + substrs = ['0x0']) + + # now check that the default is applied if we do not hand out a format + self.runCmd("type summary add --summary-string \"x=${var.x}\" Simple") + + self.expect("frame variable a_simple_object", matching=False, + substrs = ['x=3']) + + self.expect("frame variable a_simple_object", matching=True, + substrs = ['x=0x00000003']) + + # check that we can correctly cap the number of children shown + self.runCmd("settings set target.max-children-count 5") + + self.expect('frame variable a_long_guy', matching=True, + substrs = ['a_1', + 'b_1', + 'c_1', + 'd_1', + 'e_1', + '...']) + + # check that no further stuff is printed (not ALL values are checked!) + self.expect('frame variable a_long_guy', matching=False, + substrs = ['f_1', + 'g_1', + 'h_1', + 'i_1', + 'j_1', + 'q_1', + 'a_2', + 'f_2', + 't_2', + 'w_2']) + + self.runCmd("settings set target.max-children-count 1") + self.expect('frame variable a_long_guy', matching=True, + substrs = ['a_1', + '...']) + self.expect('frame variable a_long_guy', matching=False, + substrs = ['b_1', + 'c_1', + 'd_1', + 'e_1']) + self.expect('frame variable a_long_guy', matching=False, + substrs = ['f_1', + 'g_1', + 'h_1', + 'i_1', + 'j_1', + 'q_1', + 'a_2', + 'f_2', + 't_2', + 'w_2']) + + self.runCmd("settings set target.max-children-count 30") + self.expect('frame variable a_long_guy', matching=True, + substrs = ['a_1', + 'b_1', + 'c_1', + 'd_1', + 'e_1', + 'z_1', + 'a_2', + 'b_2', + 'c_2', + 'd_2', + '...']) + self.expect('frame variable a_long_guy', matching=False, + substrs = ['e_2', + 'n_2', + 'r_2', + 'i_2', + 'k_2', + 'o_2']) + + # override the cap + self.expect('frame variable a_long_guy --show-all-children', matching=True, + substrs = ['a_1', + 'b_1', + 'c_1', + 'd_1', + 'e_1', + 'z_1', + 'a_2', + 'b_2', + 'c_2', + 'd_2']) + self.expect('frame variable a_long_guy --show-all-children', matching=True, + substrs = ['e_2', + 'n_2', + 'r_2', + 'i_2', + 'k_2', + 'o_2']) + self.expect('frame variable a_long_guy --show-all-children', matching=False, + substrs = ['...']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/main.cpp new file mode 100644 index 000000000000..2462e28db127 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-advanced/main.cpp @@ -0,0 +1,174 @@ +//===-- 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 +#include + +struct i_am_cool +{ + int integer; + float floating; + char character; + i_am_cool(int I, float F, char C) : + integer(I), floating(F), character(C) {} + i_am_cool() : integer(1), floating(2), character('3') {} + +}; + +struct i_am_cooler +{ + i_am_cool first_cool; + i_am_cool second_cool; + float floating; + + i_am_cooler(int I1, int I2, float F1, float F2, char C1, char C2) : + first_cool(I1,F1,C1), + second_cool(I2,F2,C2), + floating((F1 + F2)/2) {} +}; + +struct IWrapPointers +{ + int* int_pointer; + float* float_pointer; + IWrapPointers() : int_pointer(new int(4)), float_pointer(new float(1.111)) {} +}; + +struct Simple +{ + int x; + float y; + char z; + Simple(int X, float Y, char Z) : + x(X), + y(Y), + z(Z) + {} +}; + +struct SimpleWithPointers +{ + int *x; + float *y; + char *z; + SimpleWithPointers(int X, float Y, char Z) : + x(new int (X)), + y(new float (Y)), + z(new char[2]) + { + z[0] = Z; + z[1] = '\0'; + } +}; + +struct Couple +{ + SimpleWithPointers sp; + Simple* s; + Couple(int X, float Y, char Z) : sp(X,Y,Z), + s(new Simple(X,Y,Z)) {} +}; + +struct VeryLong +{ + int a_1; + int b_1; + int c_1; + int d_1; + int e_1; + int f_1; + int g_1; + int h_1; + int i_1; + int j_1; + int k_1; + int l_1; + int m_1; + int n_1; + int o_1; + int p_1; + int q_1; + int r_1; + int s_1; + int t_1; + int u_1; + int v_1; + int w_1; + int x_1; + int y_1; + int z_1; + + int a_2; + int b_2; + int c_2; + int d_2; + int e_2; + int f_2; + int g_2; + int h_2; + int i_2; + int j_2; + int k_2; + int l_2; + int m_2; + int n_2; + int o_2; + int p_2; + int q_2; + int r_2; + int s_2; + int t_2; + int u_2; + int v_2; + int w_2; + int x_2; + int y_2; + int z_2; +}; + +int main (int argc, const char * argv[]) +{ + + int iAmInt = 9; + + i_am_cool cool_boy(1,0.5,3); + i_am_cooler cooler_boy(1,2,0.1,0.2,'A','B'); + + i_am_cool *cool_pointer = new i_am_cool(3,-3.141592,'E'); + + i_am_cool cool_array[5]; + + cool_array[3].floating = 5.25; + cool_array[4].integer = 6; + cool_array[2].character = 'Q'; + + int int_array[] = {1,2,3,4,5}; + + IWrapPointers wrapper; + + *int_array = -1; + + int* pointer = &cool_array[4].integer; + + IWrapPointers *wrap_pointer = &wrapper; + + Couple couple(9,9.99,'X'); + + SimpleWithPointers sparray[] = + {SimpleWithPointers(-1,-2,'3'), + SimpleWithPointers(-4,-5,'6'), + SimpleWithPointers(-7,-8,'9')}; + + Simple a_simple_object(3,0.14,'E'); + + VeryLong a_long_guy; + + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py new file mode 100644 index 000000000000..db426cac6e59 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py @@ -0,0 +1,329 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CategoriesDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case (most of these categories do not + # exist anymore, but we just make sure we delete all of them) + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type category delete Category1', check=False) + self.runCmd('type category delete Category2', check=False) + self.runCmd('type category delete NewCategory', check=False) + self.runCmd("type category delete CircleCategory", check=False) + self.runCmd("type category delete RectangleStarCategory", check=False) + self.runCmd("type category delete BaseCategory", check=False) + + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Add a summary to a new category and check that it works + self.runCmd("type summary add Rectangle --summary-string \"ARectangle\" -w NewCategory") + + self.expect("frame variable r1 r2 r3", matching=False, + substrs = ['r1 = ARectangle', + 'r2 = ARectangle', + 'r3 = ARectangle']) + + self.runCmd("type category enable NewCategory") + + self.expect("frame variable r1 r2 r3", matching=True, + substrs = ['r1 = ARectangle', + 'r2 = ARectangle', + 'r3 = ARectangle']) + + # Disable the category and check that the old stuff is there + self.runCmd("type category disable NewCategory") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = {', + 'r2 = {', + 'r3 = {']) + + # Re-enable the category and check that it works + self.runCmd("type category enable NewCategory") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = ARectangle', + 'r2 = ARectangle', + 'r3 = ARectangle']) + + # Delete the category and the old stuff should be there + self.runCmd("type category delete NewCategory") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = {', + 'r2 = {', + 'r3 = {']) + + # Add summaries to two different categories and check that we can switch + self.runCmd("type summary add --summary-string \"Width = ${var.w}, Height = ${var.h}\" Rectangle -w Category1") + self.runCmd("type summary add --python-script \"return 'Area = ' + str( int(valobj.GetChildMemberWithName('w').GetValue()) * int(valobj.GetChildMemberWithName('h').GetValue()) );\" Rectangle -w Category2") + + # check that enable A B is the same as enable B enable A + self.runCmd("type category enable Category1 Category2") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + self.runCmd("type category disable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Area = ', + 'r2 = Area = ', + 'r3 = Area = ']) + + # switch again + + self.runCmd("type category enable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + # Re-enable the category and show that the preference is persisted + self.runCmd("type category disable Category2") + self.runCmd("type category enable Category2") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Area = ', + 'r2 = Area = ', + 'r3 = Area = ']) + + # Now delete the favorite summary + self.runCmd("type summary delete Rectangle -w Category2") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + # Delete the summary from the default category (that does not have it) + self.runCmd("type summary delete Rectangle", check=False) + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + # Now add another summary to another category and switch back and forth + self.runCmd("type category delete Category1 Category2") + + self.runCmd("type summary add Rectangle -w Category1 --summary-string \"Category1\"") + self.runCmd("type summary add Rectangle -w Category2 --summary-string \"Category2\"") + + self.runCmd("type category enable Category2") + self.runCmd("type category enable Category1") + + self.runCmd("type summary list -w Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Category1', + 'r2 = Category1', + 'r3 = Category1']) + + self.runCmd("type category disable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Category2', + 'r2 = Category2', + 'r3 = Category2']) + + # Check that re-enabling an enabled category works + self.runCmd("type category enable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Category1', + 'r2 = Category1', + 'r3 = Category1']) + + self.runCmd("type category delete Category1") + self.runCmd("type category delete Category2") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = {', + 'r2 = {', + 'r3 = {']) + + # Check that multiple summaries can go into one category + self.runCmd("type summary add -w Category1 --summary-string \"Width = ${var.w}, Height = ${var.h}\" Rectangle") + self.runCmd("type summary add -w Category1 --summary-string \"Radius = ${var.r}\" Circle") + + self.runCmd("type category enable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = Radius = ', + 'c2 = Radius = ', + 'c3 = Radius = ']) + + self.runCmd("type summary delete Circle -w Category1") + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = {', + 'c2 = {', + 'c3 = {']) + + # Add a regex based summary to a category + self.runCmd("type summary add -w Category1 --summary-string \"Radius = ${var.r}\" -x Circle") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = Radius = ', + 'c2 = Radius = ', + 'c3 = Radius = ']) + + # Delete it + self.runCmd("type summary delete Circle -w Category1") + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = {', + 'c2 = {', + 'c3 = {']) + + # Change a summary inside a category and check that the change is reflected + self.runCmd("type summary add Circle -w Category1 --summary-string \"summary1\"") + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = summary1', + 'c2 = summary1', + 'c3 = summary1']) + + self.runCmd("type summary add Circle -w Category1 --summary-string \"summary2\"") + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = summary2', + 'c2 = summary2', + 'c3 = summary2']) + + # Check that our order of priority works. Start by clearing categories + self.runCmd("type category delete Category1") + + self.runCmd("type summary add Shape -w BaseCategory --summary-string \"AShape\"") + self.runCmd("type category enable BaseCategory") + + self.expect("print (Shape*)&c1", + substrs = ['AShape']) + self.expect("print (Shape*)&r1", + substrs = ['AShape']) + self.expect("print (Shape*)c_ptr", + substrs = ['AShape']) + self.expect("print (Shape*)r_ptr", + substrs = ['AShape']) + + self.runCmd("type summary add Circle -w CircleCategory --summary-string \"ACircle\"") + self.runCmd("type summary add Rectangle -w RectangleCategory --summary-string \"ARectangle\"") + self.runCmd("type category enable CircleCategory") + + self.expect("frame variable c1", + substrs = ['ACircle']) + self.expect("frame variable c_ptr", + substrs = ['ACircle']) + + self.runCmd("type summary add \"Rectangle *\" -w RectangleStarCategory --summary-string \"ARectangleStar\"") + self.runCmd("type category enable RectangleStarCategory") + + self.expect("frame variable c1 r1 c_ptr r_ptr", + substrs = ['ACircle', + 'ARectangleStar']) + + self.runCmd("type category enable RectangleCategory") + + self.expect("frame variable c1 r1 c_ptr r_ptr", + substrs = ['ACircle', + 'ACircle', + 'ARectangle']) + + # Check that abruptly deleting an enabled category does not crash us + self.runCmd("type category delete RectangleCategory") + + self.expect("frame variable c1 r1 c_ptr r_ptr", + substrs = ['ACircle', + '(Rectangle) r1 = ', 'w = 5', 'h = 6', + 'ACircle', + 'ARectangleStar']) + + # check that list commands work + self.expect("type category list", + substrs = ['RectangleStarCategory (enabled)']) + + self.expect("type summary list", + substrs = ['ARectangleStar']) + + # Disable a category and check that it fallsback + self.runCmd("type category disable CircleCategory") + + # check that list commands work + self.expect("type category list", + substrs = ['CircleCategory (disabled']) + + self.expect("frame variable c1 r_ptr", + substrs = ['AShape', + 'ARectangleStar']) + + # check that filters work into categories + self.runCmd("type filter add Rectangle --child w --category RectangleCategory") + self.runCmd("type category enable RectangleCategory") + self.runCmd("type summary add Rectangle --category RectangleCategory --summary-string \" \" -e") + self.expect('frame variable r2', + substrs = ['w = 9']) + self.runCmd("type summary add Rectangle --summary-string \" \" -e") + self.expect('frame variable r2', matching=False, + substrs = ['h = 16']) + + # Now delete all categories + self.runCmd("type category delete CircleCategory RectangleStarCategory BaseCategory RectangleCategory") + + # check that a deleted category with filter does not blow us up + self.expect('frame variable r2', + substrs = ['w = 9', + 'h = 16']) + + # and also validate that one can print formatters for a language + self.expect('type summary list -l c++', substrs=['vector', 'map', 'list', 'string']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/main.cpp new file mode 100644 index 000000000000..b51dd45a7f60 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-categories/main.cpp @@ -0,0 +1,46 @@ +//===-- 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 +#include + +struct Shape +{ + bool dummy; + Shape() : dummy(true) {} +}; + +struct Rectangle : public Shape { + int w; + int h; + Rectangle(int W = 3, int H = 5) : w(W), h(H) {} +}; + +struct Circle : public Shape { + int r; + Circle(int R = 6) : r(R) {} +}; + +int main (int argc, const char * argv[]) +{ + Rectangle r1(5,6); + Rectangle r2(9,16); + Rectangle r3(4,4); + + Circle c1(5); + Circle c2(6); + Circle c3(7); + + Circle *c_ptr = new Circle(8); + Rectangle *r_ptr = new Rectangle(9,7); + + return 0; // Set break point at this line. +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py new file mode 100644 index 000000000000..2ca737156a1a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py @@ -0,0 +1,265 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CppDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @expectedFailureWindows("llvm.org/pr24462") # Data formatters have problems on Windows + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("frame variable", + substrs = ['(Speed) SPILookHex = 5.55' # Speed by default is 5.55. + ]); + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type format add -C yes -f x Speed BitField") + self.runCmd("type format add -C no -f c RealNumber") + self.runCmd("type format add -C no -f x Type2") + self.runCmd("type format add -C yes -f c Type1") + + # The type format list should show our custom formats. + self.expect("type format list", + substrs = ['RealNumber', + 'Speed', + 'BitField', + 'Type1', + 'Type2']) + + self.expect("frame variable", + patterns = ['\(Speed\) SPILookHex = 0x[0-9a-f]+' # Speed should look hex-ish now. + ]); + + # gcc4.2 on Mac OS X skips typedef chains in the DWARF output + if self.getCompiler() in ['clang', 'llvm-gcc']: + self.expect("frame variable", + patterns = ['\(SignalMask\) SMILookHex = 0x[0-9a-f]+' # SignalMask should look hex-ish now. + ]); + self.expect("frame variable", matching=False, + patterns = ['\(Type4\) T4ILookChar = 0x[0-9a-f]+' # Type4 should NOT look hex-ish now. + ]); + + # Now let's delete the 'Speed' custom format. + self.runCmd("type format delete Speed") + + # The type format list should not show 'Speed' at this point. + self.expect("type format list", matching=False, + substrs = ['Speed']) + + # Delete type format for 'Speed', we should expect an error message. + self.expect("type format delete Speed", error=True, + substrs = ['no custom formatter for Speed']) + + self.runCmd("type summary add --summary-string \"arr = ${var%s}\" -x \"char \\[[0-9]+\\]\" -v") + + self.expect("frame variable strarr", + substrs = ['arr = "Hello world!"']) + + self.runCmd("type summary clear") + + self.runCmd("type summary add --summary-string \"ptr = ${var%s}\" \"char *\" -v") + + self.expect("frame variable strptr", + substrs = ['ptr = "Hello world!"']) + + self.runCmd("type summary add --summary-string \"arr = ${var%s}\" -x \"char \\[[0-9]+\\]\" -v") + + self.expect("frame variable strarr", + substrs = ['arr = "Hello world!']) + + # check that rdar://problem/10011145 (Standard summary format for char[] doesn't work as the result of "expr".) is solved + self.expect("p strarr", + substrs = ['arr = "Hello world!']) + + self.expect("frame variable strptr", + substrs = ['ptr = "Hello world!"']) + + self.expect("p strptr", + substrs = ['ptr = "Hello world!"']) + + self.expect("p (char*)\"1234567890123456789012345678901234567890123456789012345678901234ABC\"", + substrs = ['(char *) $', ' = ptr = ', ' "1234567890123456789012345678901234567890123456789012345678901234ABC"']) + + self.runCmd("type summary add -c Point") + + self.expect("frame variable iAmSomewhere", + substrs = ['x = 4', + 'y = 6']) + + self.expect("type summary list", + substrs = ['Point', + 'one-line']) + + self.runCmd("type summary add --summary-string \"y=${var.y%x}\" Point") + + self.expect("frame variable iAmSomewhere", + substrs = ['y=0x']) + + self.runCmd("type summary add --summary-string \"y=${var.y},x=${var.x}\" Point") + + self.expect("frame variable iAmSomewhere", + substrs = ['y=6', + 'x=4']) + + self.runCmd("type summary add --summary-string \"hello\" Point -e") + + self.expect("type summary list", + substrs = ['Point', + 'show children']) + + self.expect("frame variable iAmSomewhere", + substrs = ['hello', + 'x = 4', + '}']) + + self.runCmd("type summary add --summary-string \"Sign: ${var[31]%B} Exponent: ${var[23-30]%x} Mantissa: ${var[0-22]%u}\" ShowMyGuts") + + self.expect("frame variable cool_pointer->floating", + substrs = ['Sign: true', + 'Exponent: 0x', + '80']) + + self.runCmd("type summary add --summary-string \"a test\" i_am_cool") + + self.expect("frame variable cool_pointer", + substrs = ['a test']) + + self.runCmd("type summary add --summary-string \"a test\" i_am_cool --skip-pointers") + + self.expect("frame variable cool_pointer", + substrs = ['a test'], + matching = False) + + self.runCmd("type summary add --summary-string \"${var[1-3]}\" \"int [5]\"") + + self.expect("frame variable int_array", + substrs = ['2', + '3', + '4']) + + self.runCmd("type summary clear") + + self.runCmd("type summary add --summary-string \"${var[0-2].integer}\" \"i_am_cool *\"") + self.runCmd("type summary add --summary-string \"${var[2-4].integer}\" \"i_am_cool [5]\"") + + self.expect("frame variable cool_array", + substrs = ['1,1,6']) + + self.expect("frame variable cool_pointer", + substrs = ['3,0,0']) + + # test special symbols for formatting variables into summaries + self.runCmd("type summary add --summary-string \"cool object @ ${var%L}\" i_am_cool") + self.runCmd("type summary delete \"i_am_cool [5]\"") + + # this test might fail if the compiler tries to store + # these values into registers.. hopefully this is not + # going to be the case + self.expect("frame variable cool_array", + substrs = ['[0] = cool object @ 0x', + '[1] = cool object @ 0x', + '[2] = cool object @ 0x', + '[3] = cool object @ 0x', + '[4] = cool object @ 0x']) + + # test getting similar output by exploiting ${var} = 'type @ location' for aggregates + self.runCmd("type summary add --summary-string \"${var}\" i_am_cool") + + # this test might fail if the compiler tries to store + # these values into registers.. hopefully this is not + # going to be the case + self.expect("frame variable cool_array", + substrs = ['[0] = i_am_cool @ 0x', + '[1] = i_am_cool @ 0x', + '[2] = i_am_cool @ 0x', + '[3] = i_am_cool @ 0x', + '[4] = i_am_cool @ 0x']) + + + # test getting same output by exploiting %T and %L together for aggregates + self.runCmd("type summary add --summary-string \"${var%T} @ ${var%L}\" i_am_cool") + + # this test might fail if the compiler tries to store + # these values into registers.. hopefully this is not + # going to be the case + self.expect("frame variable cool_array", + substrs = ['[0] = i_am_cool @ 0x', + '[1] = i_am_cool @ 0x', + '[2] = i_am_cool @ 0x', + '[3] = i_am_cool @ 0x', + '[4] = i_am_cool @ 0x']) + + self.runCmd("type summary add --summary-string \"goofy\" i_am_cool") + self.runCmd("type summary add --summary-string \"${var.second_cool%S}\" i_am_cooler") + + self.expect("frame variable the_coolest_guy", + substrs = ['(i_am_cooler) the_coolest_guy = goofy']) + + # check that unwanted type specifiers are removed + self.runCmd("type summary delete i_am_cool") + self.runCmd("type summary add --summary-string \"goofy\" \"class i_am_cool\"") + self.expect("frame variable the_coolest_guy", + substrs = ['(i_am_cooler) the_coolest_guy = goofy']) + + self.runCmd("type summary delete i_am_cool") + self.runCmd("type summary add --summary-string \"goofy\" \"enum i_am_cool\"") + self.expect("frame variable the_coolest_guy", + substrs = ['(i_am_cooler) the_coolest_guy = goofy']) + + self.runCmd("type summary delete i_am_cool") + self.runCmd("type summary add --summary-string \"goofy\" \"struct i_am_cool\"") + self.expect("frame variable the_coolest_guy", + substrs = ['(i_am_cooler) the_coolest_guy = goofy']) + + # many spaces, but we still do the right thing + self.runCmd("type summary delete i_am_cool") + self.runCmd("type summary add --summary-string \"goofy\" \"union i_am_cool\"") + self.expect("frame variable the_coolest_guy", + substrs = ['(i_am_cooler) the_coolest_guy = goofy']) + + # but that not *every* specifier is removed + self.runCmd("type summary delete i_am_cool") + self.runCmd("type summary add --summary-string \"goofy\" \"wrong i_am_cool\"") + self.expect("frame variable the_coolest_guy", matching=False, + substrs = ['(i_am_cooler) the_coolest_guy = goofy']) + + # check that formats are not sticking since that is the behavior we want + self.expect("frame variable iAmInt --format hex", substrs = ['(int) iAmInt = 0x00000001']) + self.expect("frame variable iAmInt", matching=False, substrs = ['(int) iAmInt = 0x00000001']) + self.expect("frame variable iAmInt", substrs = ['(int) iAmInt = 1']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/main.cpp new file mode 100644 index 000000000000..5bcfbfd4d46e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-cpp/main.cpp @@ -0,0 +1,121 @@ +//===-- 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 +#include + +typedef float RealNumber; // should show as char +typedef RealNumber Temperature; // should show as float +typedef RealNumber Speed; // should show as hex + +typedef int Counter; // should show as int +typedef int BitField; // should show as hex + +typedef BitField SignalMask; // should show as hex +typedef BitField Modifiers; // should show as hex + +typedef Counter Accumulator; // should show as int + +typedef int Type1; // should show as char +typedef Type1 Type2; // should show as hex +typedef Type2 Type3; // should show as char +typedef Type3 Type4; // should show as char + +typedef int ChildType; // should show as int +typedef int AnotherChildType; // should show as int + +struct Point { + int x; + int y; + Point(int X = 3, int Y = 2) : x(X), y(Y) {} +}; + +typedef float ShowMyGuts; + +struct i_am_cool +{ + int integer; + ShowMyGuts floating; + char character; + i_am_cool(int I, ShowMyGuts F, char C) : + integer(I), floating(F), character(C) {} + i_am_cool() : integer(1), floating(2), character('3') {} + +}; + +struct i_am_cooler +{ + i_am_cool first_cool; + i_am_cool second_cool; + ShowMyGuts floating; + + i_am_cooler(int I1, int I2, float F1, float F2, char C1, char C2) : + first_cool(I1,F1,C1), + second_cool(I2,F2,C2), + floating((F1 + F2)/2) {} +}; + +struct IUseCharStar +{ + const char* pointer; + IUseCharStar() : pointer("Hello world") {} +}; + +int main (int argc, const char * argv[]) +{ + + int iAmInt = 1; + const float& IAmFloat = float(2.45); + + RealNumber RNILookChar = 3.14; + Temperature TMILookFloat = 4.97; + Speed SPILookHex = 5.55; + + Counter CTILookInt = 6; + BitField BFILookHex = 7; + SignalMask SMILookHex = 8; + Modifiers MFILookHex = 9; + + Accumulator* ACILookInt = new Accumulator(10); + + const Type1& T1ILookChar = 11; + Type2 T2ILookHex = 12; + Type3 T3ILookChar = 13; + Type4 T4ILookChar = 14; + + AnotherChildType AHILookInt = 15; + + Speed* SPPtrILookHex = new Speed(16); + + Point iAmSomewhere(4,6); + + i_am_cool *cool_pointer = (i_am_cool*)malloc(sizeof(i_am_cool)*3); + cool_pointer[0] = i_am_cool(3,-3.141592,'E'); + cool_pointer[1] = i_am_cool(0,-3.141592,'E'); + cool_pointer[2] = i_am_cool(0,-3.141592,'E'); + + i_am_cool cool_array[5]; + + cool_array[3].floating = 5.25; + cool_array[4].integer = 6; + cool_array[2].character = 'Q'; + + int int_array[] = {1,2,3,4,5}; + + IUseCharStar iEncapsulateCharStar; + + char strarr[32] = "Hello world!"; + char* strptr = "Hello world!"; + + i_am_cooler the_coolest_guy(1,2,3.14,6.28,'E','G'); + + return 0; // Set break point at this line. +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/TestDataFormatterDisabling.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/TestDataFormatterDisabling.py new file mode 100644 index 000000000000..0f254a67d5a6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/TestDataFormatterDisabling.py @@ -0,0 +1,80 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class DataFormatterDisablingTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @expectedFailureWindows("llvm.org/pr24462") # Data formatters have problems on Windows + def test_with_run_command(self): + """Check that we can properly disable all data formatter categories.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type category enable *', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + #self.runCmd('type category enable system VectorTypes libcxx gnu-libstdc++ CoreGraphics CoreServices AppKit CoreFoundation objc default', check=False) + + self.expect('type category list', substrs = ['system','enabled',]) + + self.expect("frame variable numbers", + substrs = ['[0] = 1', '[3] = 1234']) + + self.expect('frame variable string1', substrs = ['hello world']) + + # now disable them all and check that nothing is formatted + self.runCmd('type category disable *') + + self.expect("frame variable numbers", matching=False, + substrs = ['[0] = 1', '[3] = 1234']) + + self.expect('frame variable string1', matching=False, substrs = ['hello world']) + + self.expect('type category list', substrs = ['system','disabled',]) + + # now enable and check that we are back to normal + self.runCmd("type category enable *") + + self.expect('type category list', substrs = ['system','enabled']) + + self.expect("frame variable numbers", + substrs = ['[0] = 1', '[3] = 1234']) + + self.expect('frame variable string1', substrs = ['hello world']) + + self.expect('type category list', substrs = ['system','enabled']) + + # last check - our cleanup will re-enable everything + self.runCmd('type category disable *') + self.expect('type category list', substrs = ['system','disabled']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/main.cpp new file mode 100644 index 000000000000..9374642fb0d2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-disabling/main.cpp @@ -0,0 +1,18 @@ +#include + +int main() +{ + + const char* string1 = "hello world"; + + std::vector numbers; + numbers.push_back(1); + numbers.push_back(12); + numbers.push_back(123); + numbers.push_back(1234); + numbers.push_back(12345); + numbers.push_back(123456); + numbers.push_back(1234567); // Set break point at this line. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/TestDataFormatterEnumFormat.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/TestDataFormatterEnumFormat.py new file mode 100644 index 000000000000..1659ade74a06 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/TestDataFormatterEnumFormat.py @@ -0,0 +1,65 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class EnumFormatTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("frame variable", + substrs = ['(Foo) f = Case45', + '(int) x = 1', + '(int) y = 45', + '(int) z = 43' + ]); + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type format add --type Foo int") + + # The type format list should show our custom formats. + self.expect("type format list -w default", + substrs = ['int: as type Foo']) + + self.expect("frame variable", + substrs = ['(Foo) f = Case45', + '(int) x = Case1', + '(int) y = Case45', + '(int) z = 43' + ]); diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/main.cpp new file mode 100644 index 000000000000..6a8074ff9fbe --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-enum-format/main.cpp @@ -0,0 +1,13 @@ +enum Foo { + Case1 = 1, + Case2 = 2, + Case45 = 45 +}; + +int main() { + Foo f = Case45; + int x = 1; + int y = 45; + int z = 43; + return 1; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/TestDataFormatterGlobals.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/TestDataFormatterGlobals.py new file mode 100644 index 000000000000..df3bef091c89 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/TestDataFormatterGlobals.py @@ -0,0 +1,67 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class GlobalsDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type summary add --summary-string \"JustATest\" Point") + + # Simply check we can get at global variables + self.expect("target variable g_point", + substrs = ['JustATest']) + + self.expect("target variable g_point_pointer", + substrs = ['(Point *) g_point_pointer =']) + + # Print some information about the variables + # (we ignore the actual values) + self.runCmd("type summary add --summary-string \"(x=${var.x},y=${var.y})\" Point") + + self.expect("target variable g_point", + substrs = ['x=', + 'y=']) + + self.expect("target variable g_point_pointer", + substrs = ['(Point *) g_point_pointer =']) + + # Test Python code on resulting SBValue + self.runCmd("type summary add --python-script \"return 'x=' + str(valobj.GetChildMemberWithName('x').GetValue());\" Point") + + self.expect("target variable g_point", + substrs = ['x=']) + + self.expect("target variable g_point_pointer", + substrs = ['(Point *) g_point_pointer =']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/main.cpp new file mode 100644 index 000000000000..521f7a6931e9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-globals/main.cpp @@ -0,0 +1,27 @@ +//===-- 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 +#include + +struct Point { + int x; + int y; + Point(int X = 3, int Y = 2) : x(X), y(Y) {} +}; + +Point g_point(3,4); +Point* g_point_pointer = new Point(7,5); + +int main (int argc, const char * argv[]) +{ + return 0; // Set break point at this line. +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/TestDataFormatterNamedSummaries.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/TestDataFormatterNamedSummaries.py new file mode 100644 index 000000000000..94df520d29b3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/TestDataFormatterNamedSummaries.py @@ -0,0 +1,122 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class NamedSummariesDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type summary add --summary-string \"AllUseIt: x=${var.x} {y=${var.y}} {z=${var.z}}\" --name AllUseIt") + self.runCmd("type summary add --summary-string \"First: x=${var.x} y=${var.y} dummy=${var.dummy}\" First") + self.runCmd("type summary add --summary-string \"Second: x=${var.x} y=${var.y%hex}\" Second") + self.runCmd("type summary add --summary-string \"Third: x=${var.x} z=${var.z}\" Third") + + self.expect("frame variable first", + substrs = ['First: x=12']) + + self.expect("frame variable first --summary AllUseIt", + substrs = ['AllUseIt: x=12']) + + # We *DO NOT* remember the summary choice anymore + self.expect("frame variable first", matching=False, + substrs = ['AllUseIt: x=12']) + self.expect("frame variable first", + substrs = ['First: x=12']) + + self.runCmd("thread step-over") # 2 + + self.expect("frame variable first", + substrs = ['First: x=12']) + + self.expect("frame variable first --summary AllUseIt", + substrs = ['AllUseIt: x=12', + 'y=34']) + + self.expect("frame variable second --summary AllUseIt", + substrs = ['AllUseIt: x=65', + 'y=43.25']) + + self.expect("frame variable third --summary AllUseIt", + substrs = ['AllUseIt: x=96', + 'z=', + 'E']) + + self.runCmd("thread step-over") # 3 + + self.expect("frame variable second", + substrs = ['Second: x=65', + 'y=0x']) + + # decided that invalid summaries will raise an error + # instead of just defaulting to the base summary + self.expect("frame variable second --summary NoSuchSummary",error=True, + substrs = ['must specify a valid named summary']) + + self.runCmd("thread step-over") + + self.runCmd("type summary add --summary-string \"FirstAndFriends: x=${var.x} {y=${var.y}} {z=${var.z}}\" First --name FirstAndFriends") + + self.expect("frame variable first", + substrs = ['FirstAndFriends: x=12', + 'y=34']) + + self.runCmd("type summary delete First") + + self.expect("frame variable first --summary FirstAndFriends", + substrs = ['FirstAndFriends: x=12', + 'y=34']) + + self.expect("frame variable first", matching=True, + substrs = ['x = 12', + 'y = 34']) + + self.runCmd("type summary delete FirstAndFriends") + self.expect("type summary delete NoSuchSummary", error=True) + self.runCmd("type summary delete AllUseIt") + + self.expect("frame variable first", matching=False, + substrs = ['FirstAndFriends']) + + self.runCmd("thread step-over") # 4 + + self.expect("frame variable first",matching=False, + substrs = ['FirstAndFriends: x=12', + 'y=34']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/main.cpp new file mode 100644 index 000000000000..fdec5fecd3a2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-named-summaries/main.cpp @@ -0,0 +1,59 @@ +//===-- 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 +#include + +struct First +{ + int x; + int y; + float dummy; + First(int X, int Y) : + x(X), + y(Y), + dummy(3.14) + {} +}; + +struct Second +{ + int x; + float y; + Second(int X, float Y) : + x(X), + y(Y) + {} +}; + +struct Third +{ + int x; + char z; + Third(int X, char Z) : + x(X), + z(Z) + {} +}; + +int main (int argc, const char * argv[]) +{ + First first(12,34); + Second second(65,43.25); + Third *third = new Third(96,'E'); + + first.dummy = 1; // Set break point at this line. + first.dummy = 2; + first.dummy = 3; + first.dummy = 4; + first.dummy = 5; + +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/.categories b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/.categories new file mode 100644 index 000000000000..6326dbcec91b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/.categories @@ -0,0 +1 @@ +dataformatters,objc diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/Makefile new file mode 100644 index 000000000000..9f7fb1ca6231 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py new file mode 100644 index 000000000000..e12ddca2eea7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py @@ -0,0 +1,464 @@ +# encoding: utf-8 +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class ObjCDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_plain_objc_with_run_command(self): + """Test basic ObjC formatting behavior.""" + self.build() + self.plain_data_formatter_commands() + + def appkit_tester_impl(self,commands): + self.build() + self.appkit_common_data_formatters_command() + commands() + + @skipUnlessDarwin + def test_nsnumber_with_run_command(self): + """Test formatters for NSNumber.""" + self.appkit_tester_impl(self.nsnumber_data_formatter_commands) + + @skipUnlessDarwin + def test_nscontainers_with_run_command(self): + """Test formatters for NS container classes.""" + self.appkit_tester_impl(self.nscontainers_data_formatter_commands) + + @skipUnlessDarwin + def test_nsdata_with_run_command(self): + """Test formatters for NSData.""" + self.appkit_tester_impl(self.nsdata_data_formatter_commands) + + @skipUnlessDarwin + def test_nsurl_with_run_command(self): + """Test formatters for NSURL.""" + self.appkit_tester_impl(self.nsurl_data_formatter_commands) + + + @skipUnlessDarwin + def test_nserror_with_run_command(self): + """Test formatters for NSError.""" + self.appkit_tester_impl(self.nserror_data_formatter_commands) + + + @skipUnlessDarwin + def test_nsbundle_with_run_command(self): + """Test formatters for NSBundle.""" + self.appkit_tester_impl(self.nsbundle_data_formatter_commands) + + + @skipUnlessDarwin + def test_nsexception_with_run_command(self): + """Test formatters for NSException.""" + self.appkit_tester_impl(self.nsexception_data_formatter_commands) + + @skipUnlessDarwin + def test_nsmisc_with_run_command(self): + """Test formatters for misc NS classes.""" + self.appkit_tester_impl(self.nsmisc_data_formatter_commands) + + + @skipUnlessDarwin + def test_nsdate_with_run_command(self): + """Test formatters for NSDate.""" + self.appkit_tester_impl(self.nsdate_data_formatter_commands) + + @skipUnlessDarwin + def test_coreframeworks_and_run_command(self): + """Test formatters for Core OSX frameworks.""" + self.build() + self.cf_data_formatter_commands() + + @skipUnlessDarwin + def test_kvo_with_run_command(self): + """Test the behavior of formatters when KVO is in use.""" + self.build() + self.kvo_data_formatter_commands() + + @skipUnlessDarwin + def test_expr_with_run_command(self): + """Test common cases of expression parser <--> formatters interaction.""" + self.build() + self.expr_objc_data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.m', '// Set break point at this line.') + + def plain_data_formatter_commands(self): + """Test basic ObjC formatting behavior.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type summary add --summary-string \"${var%@}\" MyClass") + + self.expect("frame variable object2", + substrs = ['MyOtherClass']); + + self.expect("frame variable *object2", + substrs = ['MyOtherClass']); + + # Now let's delete the 'MyClass' custom summary. + self.runCmd("type summary delete MyClass") + + # The type format list should not show 'MyClass' at this point. + self.expect("type summary list", matching=False, + substrs = ['MyClass']) + + self.runCmd("type summary add --summary-string \"a test\" MyClass") + + self.expect("frame variable *object2", + substrs = ['*object2 =', + 'MyClass = a test', + 'backup = ']); + + self.expect("frame variable object2", matching=False, + substrs = ['a test']); + + self.expect("frame variable object", + substrs = ['a test']); + + self.expect("frame variable *object", + substrs = ['a test']); + + self.expect('frame variable myclass', + substrs = ['(Class) myclass = NSValue']) + self.expect('frame variable myclass2', + substrs = ['(Class) myclass2 = ','NS','String']) + self.expect('frame variable myclass3', + substrs = ['(Class) myclass3 = Molecule']) + self.expect('frame variable myclass4', + substrs = ['(Class) myclass4 = NSMutableArray']) + self.expect('frame variable myclass5', + substrs = ['(Class) myclass5 = nil']) + + def appkit_common_data_formatters_command(self): + """Test formatters for AppKit classes.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + def nsnumber_data_formatter_commands(self): + # Now enable AppKit and check we are displaying Cocoa classes correctly + self.expect('frame variable num1 num2 num3 num4 num5 num6 num7 num8_Y num8_N num9', + substrs = ['(NSNumber *) num1 = ',' (int)5', + '(NSNumber *) num2 = ',' (float)3.1', + '(NSNumber *) num3 = ',' (double)3.14', + '(NSNumber *) num4 = ',' (long)-2', + '(NSNumber *) num5 = ',' (char)65', + '(NSNumber *) num6 = ',' (long)255', + '(NSNumber *) num7 = ','2000000', + '(NSNumber *) num8_Y = ',' @"1"', + '(NSNumber *) num8_N = ',' @"0"', + '(NSNumber *) num9 = ',' (short)-31616']) + + self.expect('frame variable decimal_one', + substrs = ['(NSDecimalNumber *) decimal_one = 0x','1']) + + self.expect('frame variable num_at1 num_at2 num_at3 num_at4', + substrs = ['(NSNumber *) num_at1 = ',' (int)12', + '(NSNumber *) num_at2 = ',' (int)-12', + '(NSNumber *) num_at3 = ',' (double)12.5', + '(NSNumber *) num_at4 = ',' (double)-12.5']) + + def nscontainers_data_formatter_commands(self): + self.expect('frame variable newArray newDictionary newMutableDictionary cfdict_ref mutable_dict_ref cfarray_ref mutable_array_ref', + substrs = ['(NSArray *) newArray = ','@"50 elements"', + '(NSDictionary *) newDictionary = ',' 12 key/value pairs', + '(NSDictionary *) newMutableDictionary = ',' 21 key/value pairs', + '(CFDictionaryRef) cfdict_ref = ','3 key/value pairs', + '(CFMutableDictionaryRef) mutable_dict_ref = ','12 key/value pairs', + '(CFArrayRef) cfarray_ref = ','@"3 elements"', + '(CFMutableArrayRef) mutable_array_ref = ','@"11 elements"']) + + self.expect('frame variable nscounted_set', + substrs = ['(NSCountedSet *) nscounted_set = ','5 elements']) + + self.expect('frame variable iset1 iset2 imset', + substrs = ['4 indexes','512 indexes','10 indexes']) + + self.expect('frame variable mutable_bag_ref cfbag_ref binheap_ref', + substrs = ['(CFMutableBagRef) mutable_bag_ref = ','@"17 values"', + '(CFBagRef) cfbag_ref = ','@"15 values"', + '(CFBinaryHeapRef) binheap_ref = ','@"21 items"']) + + self.expect('expression -d run -- [NSArray new]', substrs=['@"0 elements"']) + + def nsdata_data_formatter_commands(self): + self.expect('frame variable immutableData mutableData data_ref mutable_data_ref mutable_string_ref', + substrs = ['(NSData *) immutableData = ',' 4 bytes', + '(NSData *) mutableData = ',' 14 bytes', + '(CFDataRef) data_ref = ','@"5 bytes"', + '(CFMutableDataRef) mutable_data_ref = ','@"5 bytes"', + '(CFMutableStringRef) mutable_string_ref = ',' @"Wish ya knew"']) + + def nsurl_data_formatter_commands(self): + self.expect('frame variable cfurl_ref cfchildurl_ref cfgchildurl_ref', + substrs = ['(CFURLRef) cfurl_ref = ','@"http://www.foo.bar', + 'cfchildurl_ref = ','@"page.html -- http://www.foo.bar', + '(CFURLRef) cfgchildurl_ref = ','@"?whatever -- http://www.foo.bar/page.html"']) + + self.expect('frame variable nsurl nsurl2 nsurl3', + substrs = ['(NSURL *) nsurl = ','@"http://www.foo.bar', + '(NSURL *) nsurl2 =','@"page.html -- http://www.foo.bar', + '(NSURL *) nsurl3 = ','@"?whatever -- http://www.foo.bar/page.html"']) + + def nserror_data_formatter_commands(self): + self.expect('frame variable nserror', + substrs = ['domain: @"Foobar" - code: 12']) + + self.expect('frame variable nserror->_userInfo', + substrs = ['2 key/value pairs']) + + self.expect('frame variable nserror->_userInfo --ptr-depth 1 -d run-target', + substrs = ['@"a"','@"b"',"1","2"]) + + def nsbundle_data_formatter_commands(self): + self.expect('frame variable bundle_string bundle_url main_bundle', + substrs = ['(NSBundle *) bundle_string = ',' @"/System/Library/Frameworks/Accelerate.framework"', + '(NSBundle *) bundle_url = ',' @"/System/Library/Frameworks/Cocoa.framework"', + '(NSBundle *) main_bundle = ','data-formatter-objc']) + + def nsexception_data_formatter_commands(self): + self.expect('frame variable except0 except1 except2 except3', + substrs = ['(NSException *) except0 = ','name:@"TheGuyWhoHasNoName" reason:@"cuz it\'s funny"', + '(NSException *) except1 = ','name:@"TheGuyWhoHasNoName~1" reason:@"cuz it\'s funny"', + '(NSException *) except2 = ','name:@"TheGuyWhoHasNoName`2" reason:@"cuz it\'s funny"', + '(NSException *) except3 = ','name:@"TheGuyWhoHasNoName/3" reason:@"cuz it\'s funny"']) + + def nsmisc_data_formatter_commands(self): + self.expect('frame variable localhost', + substrs = [' localhost ((','"127.0.0.1"']) + + if self.getArchitecture() in ['i386', 'x86_64']: + self.expect('frame variable my_task', + substrs = [' formatters interaction.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # check that the formatters are able to deal safely and correctly + # with ValueObjects that the expression parser returns + self.expect('expression ((id)@"Hello for long enough to avoid short string types")', matching=False, + substrs = ['Hello for long enough to avoid short string types']) + + self.expect('expression -d run -- ((id)@"Hello for long enough to avoid short string types")', + substrs = ['Hello for long enough to avoid short string types']) + + self.expect('expr -d run -- label1', + substrs = ['Process Name']) + + self.expect('expr -d run -- @"Hello for long enough to avoid short string types"', + substrs = ['Hello for long enough to avoid short string types']) + + self.expect('expr -d run --object-description -- @"Hello for long enough to avoid short string types"', + substrs = ['Hello for long enough to avoid short string types']) + self.expect('expr -d run --object-description -- @"Hello"', matching=False, + substrs = ['@"Hello" Hello']) + + + def cf_data_formatter_commands(self): + """Test formatters for Core OSX frameworks.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd('log timers disable', check=False) + + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # check formatters for common Objective-C types + expect_strings = ['(CFGregorianUnits) cf_greg_units = 1 years, 3 months, 5 days, 12 hours, 5 minutes 7 seconds', + '(CFRange) cf_range = location=4 length=4', + '(NSPoint) ns_point = (x = 4, y = 4)', + '(NSRange) ns_range = location=4, length=4', + '(NSRect) ns_rect = (origin = (x = 1, y = 1), size = (width = 5, height = 5))', + '(NSRectArray) ns_rect_arr = ((x = 1, y = 1), (width = 5, height = 5)), ...', + '(NSSize) ns_size = (width = 5, height = 7)', + '(CGSize) cg_size = (width = 1, height = 6)', + '(CGPoint) cg_point = (x = 2, y = 7)', + '(CGRect) cg_rect = (origin = (x = 1, y = 2), size = (width = 7, height = 7))', + '(Rect) rect = (t=4, l=8, b=4, r=7)', + '(Rect *) rect_ptr = (t=4, l=8, b=4, r=7)', + '(Point) point = (v=7, h=12)', + '(Point *) point_ptr = (v=7, h=12)', + 'name:@"TheGuyWhoHasNoName" reason:@"cuz it\'s funny"', + '1985', + 'foo_selector_impl']; + + if self.getArchitecture() in ['i386', 'x86_64']: + expect_strings.append('(HIPoint) hi_point = (x=7, y=12)') + expect_strings.append('(HIRect) hi_rect = origin=(x = 3, y = 5) size=(width = 4, height = 6)') + expect_strings.append('(RGBColor) rgb_color = red=3 green=56 blue=35') + expect_strings.append('(RGBColor *) rgb_color_ptr = red=3 green=56 blue=35') + + self.expect("frame variable", + substrs = expect_strings) + + + def kvo_data_formatter_commands(self): + """Test the behavior of formatters when KVO is in use.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # as long as KVO is implemented by subclassing, this test should succeed + # we should be able to dynamically figure out that the KVO implementor class + # is a subclass of Molecule, and use the appropriate summary for it + self.runCmd("type summary add -s JustAMoleculeHere Molecule") + self.expect('frame variable molecule', substrs = ['JustAMoleculeHere']) + self.runCmd("next") + self.expect("thread list", + substrs = ['stopped', + 'step over']) + self.expect('frame variable molecule', substrs = ['JustAMoleculeHere']) + + self.runCmd("next") + # check that NSMutableDictionary's formatter is not confused when dealing with a KVO'd dictionary + self.expect('frame variable newMutableDictionary', substrs = ['(NSDictionary *) newMutableDictionary = ',' 21 key/value pairs']) + + lldbutil.run_break_set_by_regexp (self, 'setAtoms') + + self.runCmd("continue") + self.expect("frame variable _cmd",substrs = ['setAtoms:']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/main.m b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/main.m new file mode 100644 index 000000000000..6eb7e021f70b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/main.m @@ -0,0 +1,694 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +#if defined(__APPLE__) +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) +#define IOS +#endif +#endif + +#if defined(IOS) +#import +#else +#import +#endif + +@interface MyClass : NSObject +{ + int i; + char c; + float f; +} + +- (id)initWithInt: (int)x andFloat:(float)y andChar:(char)z; +- (int)doIncrementByInt: (int)x; + +@end + +@interface MyOtherClass : MyClass +{ + int i2; + MyClass *backup; +} +- (id)initWithInt: (int)x andFloat:(float)y andChar:(char)z andOtherInt:(int)q; + +@end + +@implementation MyClass + +- (id)initWithInt: (int)x andFloat:(float)y andChar:(char)z +{ + self = [super init]; + if (self) { + self->i = x; + self->f = y; + self->c = z; + } + return self; +} + +- (int)doIncrementByInt: (int)x +{ + self->i += x; + return self->i; +} + +@end + +@implementation MyOtherClass + +- (id)initWithInt: (int)x andFloat:(float)y andChar:(char)z andOtherInt:(int)q +{ + self = [super initWithInt:x andFloat:y andChar:z]; + if (self) { + self->i2 = q; + self->backup = [[MyClass alloc] initWithInt:x andFloat:y andChar:z]; + } + return self; +} + +@end + +@interface Atom : NSObject { + float mass; +} +-(void)setMass:(float)newMass; +-(float)mass; +@end + +@interface Molecule : NSObject { + NSArray *atoms; +} +-(void)setAtoms:(NSArray *)newAtoms; +-(NSArray *)atoms; +@end + +@implementation Atom + +-(void)setMass:(float)newMass +{ + mass = newMass; +} +-(float)mass +{ + return mass; +} + +@end + +@implementation Molecule + +-(void)setAtoms:(NSArray *)newAtoms +{ + atoms = newAtoms; +} +-(NSArray *)atoms +{ + return atoms; +} +@end + +@interface My_KVO_Observer : NSObject +-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change + context:(void *)context; +- (id) init; +- (void) dealloc; +@end + +@implementation My_KVO_Observer +-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change + context:(void *)context { + // we do not really care about KVO'ing - do nothing + return; +} +- (id) init +{ + self = [super init]; + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} +@end + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + MyClass *object = [[MyClass alloc] initWithInt:1 andFloat:3.14 andChar: 'E']; + + [object doIncrementByInt:3]; + + MyOtherClass *object2 = [[MyOtherClass alloc] initWithInt:2 andFloat:6.28 andChar: 'G' andOtherInt:-1]; + + [object2 doIncrementByInt:3]; + + NSNumber* num1 = [NSNumber numberWithInt:5]; + NSNumber* num2 = [NSNumber numberWithFloat:3.14]; + NSNumber* num3 = [NSNumber numberWithDouble:3.14]; + NSNumber* num4 = [NSNumber numberWithUnsignedLongLong:0xFFFFFFFFFFFFFFFE]; + NSNumber* num5 = [NSNumber numberWithChar:'A']; + NSNumber* num6 = [NSNumber numberWithUnsignedLongLong:0xFF]; + NSNumber* num7 = [NSNumber numberWithLong:0x1E8480]; + NSNumber* num8_Y = [NSNumber numberWithBool:YES]; + NSNumber* num8_N = [NSNumber numberWithBool:NO]; + NSNumber* num9 = [NSNumber numberWithShort:0x1E8480]; + NSNumber* num_at1 = @12; + NSNumber* num_at2 = @-12; + NSNumber* num_at3 = @12.5; + NSNumber* num_at4 = @-12.5; + + NSDecimalNumber* decimal_one = [NSDecimalNumber one]; + + NSString *str0 = [num6 stringValue]; + + NSString *str1 = [NSString stringWithCString:"A rather short ASCII NSString object is here" encoding:NSASCIIStringEncoding]; + + NSString *str2 = [NSString stringWithUTF8String:"A rather short UTF8 NSString object is here"]; + + NSString *str3 = @"A string made with the at sign is here"; + + NSString *str4 = [NSString stringWithFormat:@"This is string number %ld right here", (long)4]; + + NSRect ns_rect_4str = {{1,1},{5,5}}; + + NSString* str5 = NSStringFromRect(ns_rect_4str); + + NSString* str6 = [@"/usr/doc/README.1ST" pathExtension]; + + const unichar myCharacters[] = {0x03C3,'x','x'}; + NSString *str7 = [NSString stringWithCharacters: myCharacters + length: sizeof myCharacters / sizeof *myCharacters]; + + NSString* str8 = [@"/usr/doc/file.hasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTime" pathExtension]; + + const unichar myOtherCharacters[] = {'a',' ', 'v','e','r','y',' ', + 'm','u','c','h',' ','b','o','r','i','n','g',' ','t','a','s','k', + ' ','t','o',' ','w','r','i','t','e', ' ', 'a', ' ', 's', 't', 'r', 'i', 'n', 'g', ' ', + 't','h','i','s',' ','w','a','y','!','!',0x03C3, 0}; + NSString *str9 = [NSString stringWithCharacters: myOtherCharacters + length: sizeof myOtherCharacters / sizeof *myOtherCharacters]; + + const unichar myNextCharacters[] = {0x03C3, 0x0000}; + + NSString *str10 = [NSString stringWithFormat:@"This is a Unicode string %S number %ld right here", myNextCharacters, (long)4]; + + NSString *str11 = NSStringFromClass([str10 class]); + + NSString *label1 = @"Process Name: "; + NSString *label2 = @"Process Id: "; + NSString *processName = [[NSProcessInfo processInfo] processName]; + NSString *processID = [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]]; + NSString *str12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID]; + + NSString *strA1 = [NSString stringWithCString:"A rather short ASCII NSString object is here" encoding:NSASCIIStringEncoding]; + + NSString *strA2 = [NSString stringWithUTF8String:"A rather short UTF8 NSString object is here"]; + + NSString *strA3 = @"A string made with the at sign is here"; + + NSString *strA4 = [NSString stringWithFormat:@"This is string number %ld right here", (long)4]; + + NSString* strA5 = NSStringFromRect(ns_rect_4str); + + NSString* strA6 = [@"/usr/doc/README.1ST" pathExtension]; + + NSString *strA7 = [NSString stringWithCharacters: myCharacters + length: sizeof myCharacters / sizeof *myCharacters]; + + NSString* strA8 = [@"/usr/doc/file.hasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTime" pathExtension]; + + NSString *strA9 = [NSString stringWithCharacters: myOtherCharacters + length: sizeof myOtherCharacters / sizeof *myOtherCharacters]; + + NSString *strA10 = [NSString stringWithFormat:@"This is a Unicode string %S number %ld right here", myNextCharacters, (long)4]; + + NSString *strA11 = NSStringFromClass([str10 class]); + + NSString *strA12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID]; + + NSString *strB1 = [NSString stringWithCString:"A rather short ASCII NSString object is here" encoding:NSASCIIStringEncoding]; + + NSString *strB2 = [NSString stringWithUTF8String:"A rather short UTF8 NSString object is here"]; + + NSString *strB3 = @"A string made with the at sign is here"; + + NSString *strB4 = [NSString stringWithFormat:@"This is string number %ld right here", (long)4]; + + NSString* strB5 = NSStringFromRect(ns_rect_4str); + + NSString* strB6 = [@"/usr/doc/README.1ST" pathExtension]; + + NSString *strB7 = [NSString stringWithCharacters: myCharacters + length: sizeof myCharacters / sizeof *myCharacters]; + + NSString* strB8 = [@"/usr/doc/file.hasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTime" pathExtension]; + + NSString *strB9 = [NSString stringWithCharacters: myOtherCharacters + length: sizeof myOtherCharacters / sizeof *myOtherCharacters]; + + NSString *strB10 = [NSString stringWithFormat:@"This is a Unicode string %S number %ld right here", myNextCharacters, (long)4]; + + NSString *strB11 = NSStringFromClass([str10 class]); + + NSString *strB12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID]; + + NSString *strC11 = NSStringFromClass([str10 class]); + + NSString *strC12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID]; + + NSString *strC1 = [NSString stringWithCString:"A rather short ASCII NSString object is here" encoding:NSASCIIStringEncoding]; + + NSString *strC2 = [NSString stringWithUTF8String:"A rather short UTF8 NSString object is here"]; + + NSString *strC3 = @"A string made with the at sign is here"; + + NSString *strC4 = [NSString stringWithFormat:@"This is string number %ld right here", (long)4]; + + NSString* strC5 = NSStringFromRect(ns_rect_4str); + + NSString* strC6 = [@"/usr/doc/README.1ST" pathExtension]; + + NSString *strC7 = [NSString stringWithCharacters: myCharacters + length: sizeof myCharacters / sizeof *myCharacters]; + + NSString* strC8 = [@"/usr/doc/file.hasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTime" pathExtension]; + + NSString *strC9 = [NSString stringWithCharacters: myOtherCharacters + length: sizeof myOtherCharacters / sizeof *myOtherCharacters]; + + NSString *strC10 = [NSString stringWithFormat:@"This is a Unicode string %S number %ld right here", myNextCharacters, (long)4]; + + NSString *strD11 = NSStringFromClass([str10 class]); + + NSString *strD12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID]; + + NSString *eAcute = [NSString stringWithFormat: @"%C", 0x00E9]; + NSString *randomHaziChar = [NSString stringWithFormat: @"%C", 0x9DC5]; + NSString *japanese = @"色は匂へど散りぬるを"; + NSString *italian = @"L'Italia è una Repubblica democratica, fondata sul lavoro. La sovranità appartiene al popolo, che la esercita nelle forme e nei limiti della Costituzione."; + NSString* french = @"Que veut cette horde d'esclaves, De traîtres, de rois conjurés?"; + NSString* german = @"Über-Ich und aus den Ansprüchen der sozialen Umwelt"; + + void* data_set[3] = {str1,str2,str3}; + + NSString *hebrew = [NSString stringWithString:@"לילה טוב"]; + + NSArray* newArray = [[NSMutableArray alloc] init]; + [newArray addObject:str1]; + [newArray addObject:str2]; + [newArray addObject:str3]; + [newArray addObject:str4]; + [newArray addObject:str5]; + [newArray addObject:str6]; + [newArray addObject:str7]; + [newArray addObject:str8]; + [newArray addObject:str9]; + [newArray addObject:str10]; + [newArray addObject:str11]; + [newArray addObject:str12]; + [newArray addObject:strA1]; + [newArray addObject:strA2]; + [newArray addObject:strA3]; + [newArray addObject:strA4]; + [newArray addObject:strA5]; + [newArray addObject:strA6]; + [newArray addObject:strA7]; + [newArray addObject:strA8]; + [newArray addObject:strA9]; + [newArray addObject:strA10]; + [newArray addObject:strA11]; + [newArray addObject:strA12]; + [newArray addObject:strB1]; + [newArray addObject:strB2]; + [newArray addObject:strB3]; + [newArray addObject:strB4]; + [newArray addObject:strB5]; + [newArray addObject:strB6]; + [newArray addObject:strB7]; + [newArray addObject:strB8]; + [newArray addObject:strB9]; + [newArray addObject:strB10]; + [newArray addObject:strB11]; + [newArray addObject:strB12]; + [newArray addObject:strC1]; + [newArray addObject:strC2]; + [newArray addObject:strC3]; + [newArray addObject:strC4]; + [newArray addObject:strC5]; + [newArray addObject:strC6]; + [newArray addObject:strC7]; + [newArray addObject:strC8]; + [newArray addObject:strC9]; + [newArray addObject:strC10]; + [newArray addObject:strC11]; + [newArray addObject:strC12]; + [newArray addObject:strD11]; + [newArray addObject:strD12]; + + NSDictionary* newDictionary = [[NSDictionary alloc] initWithObjects:newArray forKeys:newArray]; + NSDictionary *newMutableDictionary = [[NSMutableDictionary alloc] init]; + [newMutableDictionary setObject:@"foo" forKey:@"bar0"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar1"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar2"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar3"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar4"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar5"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar6"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar7"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar8"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar9"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar10"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar11"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar12"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar13"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar14"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar15"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar16"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar17"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar18"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar19"]; + [newMutableDictionary setObject:@"foo" forKey:@"bar20"]; + + NSAttributedString* attrString = [[NSAttributedString alloc] initWithString:@"hello world from foo" attributes:newDictionary]; + [attrString isEqual:nil]; + NSAttributedString* mutableAttrString = [[NSMutableAttributedString alloc] initWithString:@"hello world from foo" attributes:newDictionary]; + [mutableAttrString isEqual:nil]; + + NSString* mutableString = [[NSMutableString alloc] initWithString:@"foo"]; + [mutableString insertString:@"foo said this string needs to be very long so much longer than whatever other string has been seen ever before by anyone of the mankind that of course this is still not long enough given what foo our friend foo our lovely dearly friend foo desired of us so i am adding more stuff here for the sake of it and for the joy of our friend who is named guess what just foo. hence, dear friend foo, stay safe, your string is now long enough to accommodate your testing need and I will make sure that if not we extend it with even more fuzzy random meaningless words pasted one after the other from a long tiresome friday evening spent working in my office. my office mate went home but I am still randomly typing just for the fun of seeing what happens of the length of a Mutable String in Cocoa if it goes beyond one byte.. so be it, dear " atIndex:0]; + + NSString* mutableGetConst = [NSString stringWithCString:[mutableString cString]]; + + [mutableGetConst length]; + + NSData *immutableData = [[NSData alloc] initWithBytes:"HELLO" length:4]; + NSData *mutableData = [[NSMutableData alloc] initWithBytes:"NODATA" length:6]; + + [mutableData appendBytes:"MOREDATA" length:8]; + + [immutableData length]; + [mutableData length]; + + NSSet* nsset = [[NSSet alloc] initWithObjects:str1,str2,str3,nil]; + NSSet *nsmutableset = [[NSMutableSet alloc] initWithObjects:str1,str2,str3,nil]; + [nsmutableset addObject:str4]; + + CFDataRef data_ref = CFDataCreate(kCFAllocatorDefault, [immutableData bytes], 5); + + CFMutableDataRef mutable_data_ref = CFDataCreateMutable(kCFAllocatorDefault, 8); + CFDataAppendBytes(mutable_data_ref, [mutableData bytes], 5); + + CFMutableStringRef mutable_string_ref = CFStringCreateMutable(NULL,100); + CFStringAppend(mutable_string_ref, CFSTR("Wish ya knew")); + + CFStringRef cfstring_ref = CFSTR("HELLO WORLD"); + + + CFSetRef set_ref = CFSetCreate(NULL, data_set, 3, NULL); + + CFMutableSetRef mutable_set_ref = CFSetCreateMutable(NULL, 5, NULL); + + CFSetAddValue(mutable_set_ref, str1); + CFSetAddValue(mutable_set_ref, str2); + CFSetAddValue(mutable_set_ref, str3); + CFSetAddValue(mutable_set_ref, str4); + CFSetAddValue(mutable_set_ref, str5); + CFSetAddValue(mutable_set_ref, str6); + CFSetAddValue(mutable_set_ref, str7); + CFSetAddValue(mutable_set_ref, str8); + CFSetAddValue(mutable_set_ref, str9); + CFSetAddValue(mutable_set_ref, str10); + CFSetAddValue(mutable_set_ref, str11); + CFSetAddValue(mutable_set_ref, str12); + + + CFDictionaryRef cfdict_ref = CFDictionaryCreate(NULL, data_set, data_set, 3, NULL, NULL); + CFMutableDictionaryRef mutable_dict_ref = CFDictionaryCreateMutable(NULL, 16, NULL, NULL); + + CFDictionarySetValue(mutable_dict_ref, str1, str1); + CFDictionarySetValue(mutable_dict_ref, str2, str2); + CFDictionarySetValue(mutable_dict_ref, str3, str3); + CFDictionarySetValue(mutable_dict_ref, str4, str1); + CFDictionarySetValue(mutable_dict_ref, str5, str2); + CFDictionarySetValue(mutable_dict_ref, str6, str3); + CFDictionarySetValue(mutable_dict_ref, str7, str1); + CFDictionarySetValue(mutable_dict_ref, str8, str2); + CFDictionarySetValue(mutable_dict_ref, str9, str3); + CFDictionarySetValue(mutable_dict_ref, str10, str1); + CFDictionarySetValue(mutable_dict_ref, str11, str2); + CFDictionarySetValue(mutable_dict_ref, str12, str3); + + CFArrayRef cfarray_ref = CFArrayCreate(NULL, data_set, 3, NULL); + CFMutableArrayRef mutable_array_ref = CFArrayCreateMutable(NULL, 16, NULL); + + CFArraySetValueAtIndex(mutable_array_ref, 0, str1); + CFArraySetValueAtIndex(mutable_array_ref, 1, str2); + CFArraySetValueAtIndex(mutable_array_ref, 2, str3); + CFArraySetValueAtIndex(mutable_array_ref, 3, str4); + CFArraySetValueAtIndex(mutable_array_ref, 0, str5); // replacing value at 0!! + CFArraySetValueAtIndex(mutable_array_ref, 4, str6); + CFArraySetValueAtIndex(mutable_array_ref, 5, str7); + CFArraySetValueAtIndex(mutable_array_ref, 6, str8); + CFArraySetValueAtIndex(mutable_array_ref, 7, str9); + CFArraySetValueAtIndex(mutable_array_ref, 8, str10); + CFArraySetValueAtIndex(mutable_array_ref, 9, str11); + CFArraySetValueAtIndex(mutable_array_ref, 10, str12); + + CFMutableBagRef mutable_bag_ref = CFBagCreateMutable(NULL, 15, NULL); + + CFBagSetValue(mutable_bag_ref, strB10); + CFBagSetValue(mutable_bag_ref, str1); + CFBagSetValue(mutable_bag_ref, str2); + CFBagSetValue(mutable_bag_ref, str3); + CFBagSetValue(mutable_bag_ref, str4); + CFBagSetValue(mutable_bag_ref, str5); + CFBagSetValue(mutable_bag_ref, str6); + CFBagSetValue(mutable_bag_ref, str7); + CFBagSetValue(mutable_bag_ref, str8); + CFBagSetValue(mutable_bag_ref, str9); + CFBagSetValue(mutable_bag_ref, str10); + CFBagSetValue(mutable_bag_ref, str11); + CFBagSetValue(mutable_bag_ref, str12); + CFBagSetValue(mutable_bag_ref, strA1); + CFBagSetValue(mutable_bag_ref, strA2); + CFBagSetValue(mutable_bag_ref, strA3); + + CFBagRef cfbag_ref = CFBagCreateCopy(NULL, mutable_bag_ref); + + CFBagSetValue(mutable_bag_ref, strB8); + CFBagSetValue(mutable_bag_ref, strC4); + + + CFBinaryHeapRef binheap_ref = CFBinaryHeapCreate(NULL, 15, &kCFStringBinaryHeapCallBacks, NULL); + CFBinaryHeapAddValue(binheap_ref, str1); + CFBinaryHeapAddValue(binheap_ref, str2); + CFBinaryHeapAddValue(binheap_ref, str3); + CFBinaryHeapAddValue(binheap_ref, str4); + CFBinaryHeapAddValue(binheap_ref, str5); + CFBinaryHeapAddValue(binheap_ref, str6); + CFBinaryHeapAddValue(binheap_ref, str7); + CFBinaryHeapAddValue(binheap_ref, str8); + CFBinaryHeapAddValue(binheap_ref, str9); + CFBinaryHeapAddValue(binheap_ref, str10); + CFBinaryHeapAddValue(binheap_ref, str11); + CFBinaryHeapAddValue(binheap_ref, str12); + CFBinaryHeapAddValue(binheap_ref, strA1); + CFBinaryHeapAddValue(binheap_ref, strB1); + CFBinaryHeapAddValue(binheap_ref, strC1); + CFBinaryHeapAddValue(binheap_ref, strA11); + CFBinaryHeapAddValue(binheap_ref, strB11); + CFBinaryHeapAddValue(binheap_ref, strC11); + CFBinaryHeapAddValue(binheap_ref, strB12); + CFBinaryHeapAddValue(binheap_ref, strC12); + CFBinaryHeapAddValue(binheap_ref, strA12); + + CFURLRef cfurl_ref = CFURLCreateWithString(NULL, CFSTR("http://www.foo.bar/"), NULL); + CFURLRef cfchildurl_ref = CFURLCreateWithString(NULL, CFSTR("page.html"), cfurl_ref); + CFURLRef cfgchildurl_ref = CFURLCreateWithString(NULL, CFSTR("?whatever"), cfchildurl_ref); + + NSDictionary *error_userInfo = @{@"a": @1, @"b" : @2}; + NSError *nserror = [[NSError alloc] initWithDomain:@"Foobar" code:12 userInfo:error_userInfo]; + + NSBundle* bundle_string = [[NSBundle alloc] initWithPath:@"/System/Library/Frameworks/Accelerate.framework"]; + NSBundle* bundle_url = [[NSBundle alloc] initWithURL:[[NSURL alloc] initWithString:@"file://localhost/System/Library/Frameworks/Cocoa.framework"]]; + + NSBundle* main_bundle = [NSBundle mainBundle]; + + NSArray* bundles = [NSBundle allBundles]; + + NSURL *nsurl0; + + for (NSBundle* bundle in bundles) + { + nsurl0 = [bundle bundleURL]; + } + + NSException* except0 = [[NSException alloc] initWithName:@"TheGuyWhoHasNoName" reason:@"cuz it's funny" userInfo:nil]; + NSException* except1 = [[NSException alloc] initWithName:@"TheGuyWhoHasNoName~1" reason:@"cuz it's funny" userInfo:nil]; + NSException* except2 = [[NSException alloc] initWithName:@"TheGuyWhoHasNoName`2" reason:@"cuz it's funny" userInfo:nil]; + NSException* except3 = [[NSException alloc] initWithName:@"TheGuyWhoHasNoName/3" reason:@"cuz it's funny" userInfo:nil]; + + NSMachPort *port = [NSMachPort port]; + + NSURL *nsurl = [[NSURL alloc] initWithString:@"http://www.foo.bar"]; + NSURL *nsurl2 = [NSURL URLWithString:@"page.html" relativeToURL:nsurl]; + NSURL *nsurl3 = [NSURL URLWithString:@"?whatever" relativeToURL:nsurl2]; + + NSDate *date1 = [NSDate dateWithNaturalLanguageString:@"6pm April 10, 1985"]; + NSDate *date2 = [NSDate dateWithNaturalLanguageString:@"12am January 1, 2011"]; + NSDate *date3 = [NSDate date]; + NSDate *date4 = [NSDate dateWithTimeIntervalSince1970:24*60*60]; + + CFAbsoluteTime date1_abs = CFDateGetAbsoluteTime(date1); + CFAbsoluteTime date2_abs = CFDateGetAbsoluteTime(date2); + CFAbsoluteTime date3_abs = CFDateGetAbsoluteTime(date3); + CFAbsoluteTime date4_abs = CFDateGetAbsoluteTime(date4); + + NSCountedSet *nscounted_set = [[NSCountedSet alloc] initWithCapacity:5]; + + [nscounted_set addObject:str0]; + [nscounted_set addObject:str1]; + [nscounted_set addObject:str0]; + [nscounted_set addObject:str0]; + [nscounted_set addObject:@"foo1"]; + [nscounted_set addObject:@"foo2"]; + [nscounted_set addObject:@"foo3"]; + + NSIndexSet *iset1 = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(1, 4)]; + NSIndexSet *iset2 = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(1, 512)]; + + NSMutableIndexSet *imset = [[NSMutableIndexSet alloc] init]; + [imset addIndex:1936]; + [imset addIndex:7]; + [imset addIndex:9]; + [imset addIndex:11]; + [imset addIndex:24]; + [imset addIndex:41]; + [imset addIndex:58]; + [imset addIndex:61]; + [imset addIndex:62]; + [imset addIndex:63]; + + CFTimeZoneRef cupertino = CFTimeZoneCreateWithName ( + NULL, + CFSTR("PST"), + YES); + CFTimeZoneRef home = CFTimeZoneCreateWithName ( + NULL, + CFSTR("Europe/Rome"), + YES); + CFTimeZoneRef europe = CFTimeZoneCreateWithName ( + NULL, + CFSTR("CET"), + YES); + + NSTimeZone *cupertino_ns = [NSTimeZone timeZoneWithAbbreviation:@"PST"]; + NSTimeZone *home_ns = [NSTimeZone timeZoneWithName:@"Europe/Rome"]; + NSTimeZone *europe_ns = [NSTimeZone timeZoneWithAbbreviation:@"CET"]; + + NSHost *localhost = [NSHost hostWithAddress:@"127.0.0.1"]; + +#ifndef IOS + NSTask *my_task = [[NSTask alloc] init]; +#endif + + + CFGregorianUnits cf_greg_units = {1,3,5,12,5,7}; + CFGregorianDate cf_greg_date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(date1), NULL); + CFRange cf_range = {4,4}; + NSPoint ns_point = {4,4}; + NSRange ns_range = {4,4}; + + NSValue *range_value = [NSValue valueWithRange:ns_range]; + + NSRect ns_rect = {{1,1},{5,5}}; + NSRect* ns_rect_ptr = &ns_rect; + NSRectArray ns_rect_arr = &ns_rect; + NSSize ns_size = {5,7}; + NSSize* ns_size_ptr = &ns_size; + + CGSize cg_size = {1,6}; + CGPoint cg_point = {2,7}; + CGRect cg_rect = {{1,2}, {7,7}}; + +#ifndef IOS + RGBColor rgb_color = {3,56,35}; + RGBColor* rgb_color_ptr = &rgb_color; +#endif + + Rect rect = {4,8,4,7}; + Rect* rect_ptr = ▭ + + Point point = {7,12}; + Point* point_ptr = &point; + +#ifndef IOS + HIPoint hi_point = {7,12}; + HIRect hi_rect = {{3,5},{4,6}}; +#endif + + SEL foo_selector = @selector(foo_selector_impl); + + CFMutableBitVectorRef mut_bv = CFBitVectorCreateMutable(NULL, 64); + CFBitVectorSetCount(mut_bv, 50); + CFBitVectorSetBitAtIndex(mut_bv, 0, 1); + CFBitVectorSetBitAtIndex(mut_bv, 1, 1); + CFBitVectorSetBitAtIndex(mut_bv, 2, 1); + CFBitVectorSetBitAtIndex(mut_bv, 5, 1); + CFBitVectorSetBitAtIndex(mut_bv, 6, 1); + CFBitVectorSetBitAtIndex(mut_bv, 8, 1); + CFBitVectorSetBitAtIndex(mut_bv, 10, 1); + CFBitVectorSetBitAtIndex(mut_bv, 11, 1); + CFBitVectorSetBitAtIndex(mut_bv, 16, 1); + CFBitVectorSetBitAtIndex(mut_bv, 17, 1); + CFBitVectorSetBitAtIndex(mut_bv, 19, 1); + CFBitVectorSetBitAtIndex(mut_bv, 20, 1); + CFBitVectorSetBitAtIndex(mut_bv, 22, 1); + CFBitVectorSetBitAtIndex(mut_bv, 24, 1); + CFBitVectorSetBitAtIndex(mut_bv, 28, 1); + CFBitVectorSetBitAtIndex(mut_bv, 29, 1); + CFBitVectorSetBitAtIndex(mut_bv, 30, 1); + CFBitVectorSetBitAtIndex(mut_bv, 30, 1); + CFBitVectorSetBitAtIndex(mut_bv, 31, 1); + CFBitVectorSetBitAtIndex(mut_bv, 34, 1); + CFBitVectorSetBitAtIndex(mut_bv, 35, 1); + CFBitVectorSetBitAtIndex(mut_bv, 37, 1); + CFBitVectorSetBitAtIndex(mut_bv, 39, 1); + CFBitVectorSetBitAtIndex(mut_bv, 40, 1); + CFBitVectorSetBitAtIndex(mut_bv, 41, 1); + CFBitVectorSetBitAtIndex(mut_bv, 43, 1); + CFBitVectorSetBitAtIndex(mut_bv, 47, 1); + + Molecule *molecule = [Molecule new]; + + Class myclass = NSClassFromString(@"NSValue"); + Class myclass2 = [str0 class]; + Class myclass3 = [molecule class]; + Class myclass4 = NSClassFromString(@"NSMutableArray"); + Class myclass5 = [nil class]; + + NSArray *components = @[@"usr", @"blah", @"stuff"]; + NSString *path = [NSString pathWithComponents: components]; + + [molecule addObserver:[My_KVO_Observer new] forKeyPath:@"atoms" options:0 context:NULL]; // Set break point at this line. + [newMutableDictionary addObserver:[My_KVO_Observer new] forKeyPath:@"weirdKeyToKVO" options:NSKeyValueObservingOptionNew context:NULL]; + + [molecule setAtoms:nil]; + [molecule setAtoms:[NSMutableArray new]]; + + [pool drain]; + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/Makefile new file mode 100644 index 000000000000..0d94c2247f14 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/TestDataFormatterNSString.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/TestDataFormatterNSString.py new file mode 100644 index 000000000000..e11c45e9038e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/TestDataFormatterNSString.py @@ -0,0 +1,107 @@ +# encoding: utf-8 +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class NSStringDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def appkit_tester_impl(self,commands): + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + commands() + + @skipUnlessDarwin + def test_nsstring_with_run_command(self): + """Test formatters for NSString.""" + self.appkit_tester_impl(self.nsstring_data_formatter_commands) + + @skipUnlessDarwin + def test_rdar11106605_with_run_command(self): + """Check that Unicode characters come out of CFString summary correctly.""" + self.appkit_tester_impl(self.rdar11106605_commands) + + @skipUnlessDarwin + def test_nsstring_withNULS_with_run_command(self): + """Test formatters for NSString.""" + self.appkit_tester_impl(self.nsstring_withNULs_commands) + + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.m', '// break here') + + def rdar11106605_commands(self): + """Check that Unicode characters come out of CFString summary correctly.""" + self.expect('frame variable italian', substrs = ['L\'Italia è una Repubblica democratica, fondata sul lavoro. La sovranità appartiene al popolo, che la esercita nelle forme e nei limiti della Costituzione.']) + self.expect('frame variable french', substrs = ['Que veut cette horde d\'esclaves, De traîtres, de rois conjurés?']) + self.expect('frame variable german', substrs = ['Über-Ich und aus den Ansprüchen der sozialen Umwelt']) + self.expect('frame variable japanese', substrs = ['色は匂へど散りぬるを']) + self.expect('frame variable hebrew', substrs = ['לילה טוב']) + + def nsstring_data_formatter_commands(self): + self.expect('frame variable str0 str1 str2 str3 str4 str5 str6 str8 str9 str10 str11 label1 label2 processName str12', + substrs = ['(NSString *) str1 = ',' @"A rather short ASCII NSString object is here"', + # '(NSString *) str0 = ',' @"255"', + '(NSString *) str1 = ',' @"A rather short ASCII NSString object is here"', + '(NSString *) str2 = ',' @"A rather short UTF8 NSString object is here"', + '(NSString *) str3 = ',' @"A string made with the at sign is here"', + '(NSString *) str4 = ',' @"This is string number 4 right here"', + '(NSString *) str5 = ',' @"{{1, 1}, {5, 5}}"', + '(NSString *) str6 = ',' @"1ST"', + '(NSString *) str8 = ',' @"hasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTime', + '(NSString *) str9 = ',' @"a very much boring task to write a string this way!!', + '(NSString *) str10 = ',' @"This is a Unicode string σ number 4 right here"', + '(NSString *) str11 = ',' @"__NSCFString"', + '(NSString *) label1 = ',' @"Process Name: "', + '(NSString *) label2 = ',' @"Process Id: "', + '(NSString *) str12 = ',' @"Process Name: a.out Process Id:']) + self.expect('frame variable attrString mutableAttrString mutableGetConst', + substrs = ['(NSAttributedString *) attrString = ',' @"hello world from foo"', + '(NSAttributedString *) mutableAttrString = ',' @"hello world from foo"', + '(NSString *) mutableGetConst = ',' @"foo said this string needs to be very long so much longer than whatever other string has been seen ever before by anyone of the mankind that of course this is still not long enough given what foo our friend foo our lovely dearly friend foo desired of us so i am adding more stuff here for the sake of it and for the joy of our friend who is named guess what just foo. hence, dear friend foo, stay safe, your string is now long enough to accommodate your testing need and I will make sure that if not we extend it with even more fuzzy random meaningless words pasted one after the other from a long tiresome friday evening spent working in my office. my office mate went home but I am still randomly typing just for the fun of seeing what happens of the length of a Mutable String in Cocoa if it goes beyond one byte.. so be it, dear foo"']) + + self.expect('expr -d run-target -- path',substrs = ['usr/blah/stuff']) + self.expect('frame variable path',substrs = ['usr/blah/stuff']) + + def nsstring_withNULs_commands(self): + """Check that the NSString formatter supports embedded NULs in the text""" + self.expect('po strwithNULs', substrs=['a very much boring task to write']) + self.expect('expr [strwithNULs length]', substrs=['54']) + self.expect('frame variable strwithNULs', substrs=['@"a very much boring task to write\\0a string this way!!']) + self.expect('po strwithNULs2', substrs=['a very much boring task to write']) + self.expect('expr [strwithNULs2 length]', substrs=['52']) + self.expect('frame variable strwithNULs2', substrs=['@"a very much boring task to write\\0a string this way!!']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/main.m b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/main.m new file mode 100644 index 000000000000..7b8c3785a18c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-objc/nsstring/main.m @@ -0,0 +1,99 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +#if defined(__APPLE__) +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) +#define IOS +#endif +#endif + +#if defined(IOS) +#import +#else +#import +#endif + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + NSString *str0 = [[NSNumber numberWithUnsignedLongLong:0xFF] stringValue]; + NSString *str1 = [NSString stringWithCString:"A rather short ASCII NSString object is here" encoding:NSASCIIStringEncoding]; + NSString *str2 = [NSString stringWithUTF8String:"A rather short UTF8 NSString object is here"]; + NSString *str3 = @"A string made with the at sign is here"; + NSString *str4 = [NSString stringWithFormat:@"This is string number %ld right here", (long)4]; + NSRect ns_rect_4str = {{1,1},{5,5}}; + NSString* str5 = NSStringFromRect(ns_rect_4str); + NSString* str6 = [@"/usr/doc/README.1ST" pathExtension]; + const unichar myCharacters[] = {0x03C3,'x','x'}; + NSString *str7 = [NSString stringWithCharacters: myCharacters + length: sizeof myCharacters / sizeof *myCharacters]; + NSString* str8 = [@"/usr/doc/file.hasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTime" pathExtension]; + const unichar myOtherCharacters[] = {'a',' ', 'v','e','r','y',' ', + 'm','u','c','h',' ','b','o','r','i','n','g',' ','t','a','s','k', + ' ','t','o',' ','w','r','i','t','e', ' ', 'a', ' ', 's', 't', 'r', 'i', 'n', 'g', ' ', + 't','h','i','s',' ','w','a','y','!','!',0x03C3, 0}; + NSString *str9 = [NSString stringWithCharacters: myOtherCharacters + length: sizeof myOtherCharacters / sizeof *myOtherCharacters]; + const unichar myNextCharacters[] = {0x03C3, 0x0000}; + NSString *str10 = [NSString stringWithFormat:@"This is a Unicode string %S number %ld right here", myNextCharacters, (long)4]; + NSString *str11 = NSStringFromClass([str10 class]); + NSString *label1 = @"Process Name: "; + NSString *label2 = @"Process Id: "; + NSString *processName = [[NSProcessInfo processInfo] processName]; + NSString *processID = [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]]; + NSString *str12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID]; + NSString *eAcute = [NSString stringWithFormat: @"%C", 0x00E9]; + NSString *randomHaziChar = [NSString stringWithFormat: @"%C", 0x9DC5]; + NSString *japanese = @"色は匂へど散りぬるを"; + NSString *italian = @"L'Italia è una Repubblica democratica, fondata sul lavoro. La sovranità appartiene al popolo, che la esercita nelle forme e nei limiti della Costituzione."; + NSString* french = @"Que veut cette horde d'esclaves, De traîtres, de rois conjurés?"; + NSString* german = @"Über-Ich und aus den Ansprüchen der sozialen Umwelt"; + void* data_set[3] = {str1,str2,str3}; + NSString *hebrew = [NSString stringWithString:@"לילה טוב"]; + + NSAttributedString* attrString = [[NSAttributedString alloc] initWithString:@"hello world from foo" attributes:[NSDictionary new]]; + [attrString isEqual:nil]; + NSAttributedString* mutableAttrString = [[NSMutableAttributedString alloc] initWithString:@"hello world from foo" attributes:[NSDictionary new]]; + [mutableAttrString isEqual:nil]; + + NSString* mutableString = [[NSMutableString alloc] initWithString:@"foo"]; + [mutableString insertString:@"foo said this string needs to be very long so much longer than whatever other string has been seen ever before by anyone of the mankind that of course this is still not long enough given what foo our friend foo our lovely dearly friend foo desired of us so i am adding more stuff here for the sake of it and for the joy of our friend who is named guess what just foo. hence, dear friend foo, stay safe, your string is now long enough to accommodate your testing need and I will make sure that if not we extend it with even more fuzzy random meaningless words pasted one after the other from a long tiresome friday evening spent working in my office. my office mate went home but I am still randomly typing just for the fun of seeing what happens of the length of a Mutable String in Cocoa if it goes beyond one byte.. so be it, dear " atIndex:0]; + + NSString* mutableGetConst = [NSString stringWithCString:[mutableString cString]]; + + [mutableGetConst length]; + CFMutableStringRef mutable_string_ref = CFStringCreateMutable(NULL,100); + CFStringAppend(mutable_string_ref, CFSTR("Wish ya knew")); + CFStringRef cfstring_ref = CFSTR("HELLO WORLD"); + + NSArray *components = @[@"usr", @"blah", @"stuff"]; + NSString *path = [NSString pathWithComponents: components]; + + const unichar someOfTheseAreNUL[] = {'a',' ', 'v','e','r','y',' ', + 'm','u','c','h',' ','b','o','r','i','n','g',' ','t','a','s','k', + ' ','t','o',' ','w','r','i','t','e', 0, 'a', ' ', 's', 't', 'r', 'i', 'n', 'g', ' ', + 't','h','i','s',' ','w','a','y','!','!', 0x03C3, 0}; + NSString *strwithNULs = [NSString stringWithCharacters: someOfTheseAreNUL + length: sizeof someOfTheseAreNUL / sizeof *someOfTheseAreNUL]; + + const unichar someOfTheseAreNUL2[] = {'a',' ', 'v','e','r','y',' ', + 'm','u','c','h',' ','b','o','r','i','n','g',' ','t','a','s','k', + ' ','t','o',' ','w','r','i','t','e', 0, 'a', ' ', 's', 't', 'r', 'i', 'n', 'g', ' ', + 't','h','i','s',' ','w','a','y','!','!'}; + NSString *strwithNULs2 = [NSString stringWithCharacters: someOfTheseAreNUL2 + length: sizeof someOfTheseAreNUL2 / sizeof *someOfTheseAreNUL2]; + + [pool drain]; // break here + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/Makefile new file mode 100644 index 000000000000..9f7fb1ca6231 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/TestFormattersOneIsSingular.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/TestFormattersOneIsSingular.py new file mode 100644 index 000000000000..1281f17ab58a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/TestFormattersOneIsSingular.py @@ -0,0 +1,90 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class DataFormatterOneIsSingularTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_one_is_singular_with_run_command(self): + """Test that 1 item is not as reported as 1 items.""" + self.build() + self.oneness_data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.m', '// Set break point at this line.') + + def oneness_data_formatter_commands(self): + """Test that 1 item is not as reported as 1 items.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Now check that we are displaying Cocoa classes correctly + self.expect('frame variable key', + substrs = ['@"1 element"']) + self.expect('frame variable key', matching=False, + substrs = ['1 elements']) + self.expect('frame variable value', + substrs = ['@"1 element"']) + self.expect('frame variable value', matching=False, + substrs = ['1 elements']) + self.expect('frame variable dict', + substrs = ['1 key/value pair']) + self.expect('frame variable dict', matching=False, + substrs = ['1 key/value pairs']) + self.expect('frame variable mutable_bag_ref', + substrs = ['@"1 value"']) + self.expect('frame variable mutable_bag_ref', matching=False, + substrs = ['1 values']) + self.expect('frame variable nscounted_set', + substrs = ['1 element']) + self.expect('frame variable nscounted_set', matching=False, + substrs = ['1 elements']) + self.expect('frame variable imset', + substrs = ['1 index']) + self.expect('frame variable imset', matching=False, + substrs = ['1 indexes']) + self.expect('frame variable binheap_ref', + substrs = ['@"1 item"']) + self.expect('frame variable binheap_ref', matching=False, + substrs = ['1 items']) + self.expect('frame variable nsset', + substrs = ['1 element']) + self.expect('frame variable nsset', matching=False, + substrs = ['1 elements']) + self.expect('frame variable immutableData', + substrs = ['1 byte']) + self.expect('frame variable immutableData', matching=False, + substrs = ['1 bytes']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/main.m b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/main.m new file mode 100644 index 000000000000..7204d3c7b202 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-proper-plurals/main.m @@ -0,0 +1,42 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + + NSArray* key = [NSArray arrayWithObjects:@"foo",nil]; + NSArray* value = [NSArray arrayWithObjects:@"key",nil]; + NSDictionary *dict = [NSDictionary dictionaryWithObjects:value forKeys:key]; + + CFMutableBagRef mutable_bag_ref = CFBagCreateMutable(NULL, 15, NULL); + CFBagSetValue(mutable_bag_ref, CFSTR("Hello world")); + + NSCountedSet *nscounted_set = [[NSCountedSet alloc] initWithCapacity:5]; + [nscounted_set addObject:@"foo"]; + + NSMutableIndexSet *imset = [[NSMutableIndexSet alloc] init]; + [imset addIndex:4]; + + CFBinaryHeapRef binheap_ref = CFBinaryHeapCreate(NULL, 15, &kCFStringBinaryHeapCallBacks, NULL); + CFBinaryHeapAddValue(binheap_ref, CFSTR("Hello world")); + + NSSet* nsset = [[NSSet alloc] initWithObjects:@"foo",nil]; + + NSData *immutableData = [[NSData alloc] initWithBytes:"HELLO" length:1]; + + + [pool drain];// Set break point at this line. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/TestPtrToArrayFormatting.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/TestPtrToArrayFormatting.py new file mode 100644 index 000000000000..86cd3c427288 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/TestPtrToArrayFormatting.py @@ -0,0 +1,57 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class PtrToArrayDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_with_run_command(self): + """Test that LLDB handles the clang typeclass Paren correctly.""" + self.build() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def data_formatter_commands(self): + """Test that LLDB handles the clang typeclass Paren correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format delete hex', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('p *(int (*)[3])foo', + substrs = ['(int [3]) $','[0] = 1','[1] = 2','[2] = 3']) + + self.expect('p *(int (*)[3])foo', matching=False, + substrs = ['01 00 00 00 02 00 00 00 03 00 00 00']) + self.expect('p *(int (*)[3])foo', matching=False, + substrs = ['0x000000030000000200000001']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/main.cpp new file mode 100644 index 000000000000..15fa5614d7fd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-ptr-to-array/main.cpp @@ -0,0 +1,17 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +bool bar(int const *foo) { + return foo != 0; // Set break point at this line. +} + +int main() { + int foo[] = {1,2,3}; + return bar(foo); +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py new file mode 100644 index 000000000000..d202ff5d64e1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py @@ -0,0 +1,252 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class PythonSynthDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfFreeBSD # llvm.org/pr20545 bogus output confuses buildbot parser + def test_with_run_command(self): + """Test data formatter commands.""" + self.build() + self.data_formatter_commands() + + def test_rdar10960550_with_run_command(self): + """Test data formatter commands.""" + self.build() + self.rdar10960550_formatter_commands() + + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + self.line2 = line_number('main.cpp', '// Set cast break point at this line.') + self.line3 = line_number('main.cpp', '// Set second cast break point at this line.') + + def data_formatter_commands(self): + """Test using Python synthetic children provider.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # print the f00_1 variable without a synth + self.expect("frame variable f00_1", + substrs = ['a = 0', + 'b = 1', + 'r = 33']); + + # now set up the synth + self.runCmd("script from fooSynthProvider import *") + self.runCmd("type synth add -l fooSynthProvider foo") + + # check that we get the two real vars and the fake_a variables + self.expect("frame variable f00_1", + substrs = ['r = 33', + 'fake_a = 16777216', + 'a = 0']); + + # check that we do not get the extra vars + self.expect("frame variable f00_1", matching=False, + substrs = ['b = 1']); + + # check access to members by name + self.expect('frame variable f00_1.fake_a', + substrs = ['16777216']) + + # check access to members by index + self.expect('frame variable f00_1[1]', + substrs = ['16777216']) + + # put synthetic children in summary in several combinations + self.runCmd("type summary add --summary-string \"fake_a=${svar.fake_a}\" foo") + self.expect('frame variable f00_1', + substrs = ['fake_a=16777216']) + self.runCmd("type summary add --summary-string \"fake_a=${svar[1]}\" foo") + self.expect('frame variable f00_1', + substrs = ['fake_a=16777216']) + + # clear the summary + self.runCmd("type summary delete foo") + + # check that the caching does not span beyond the stopoint + self.runCmd("n") + + self.expect("frame variable f00_1", + substrs = ['r = 33', + 'fake_a = 16777216', + 'a = 1']); + + # check that altering the object also alters fake_a + self.runCmd("expr f00_1.a = 280") + self.expect("frame variable f00_1", + substrs = ['r = 33', + 'fake_a = 16777217', + 'a = 280']); + + # check that expanding a pointer does the right thing + self.expect("frame variable --ptr-depth 1 f00_ptr", + substrs = ['r = 45', + 'fake_a = 218103808', + 'a = 12']) + + # now add a filter.. it should fail + self.expect("type filter add foo --child b --child j", error=True, + substrs = ['cannot add']) + + # we get the synth again.. + self.expect('frame variable f00_1', matching=False, + substrs = ['b = 1', + 'j = 17']) + self.expect("frame variable --ptr-depth 1 f00_ptr", + substrs = ['r = 45', + 'fake_a = 218103808', + 'a = 12']) + + # now delete the synth and add the filter + self.runCmd("type synth delete foo") + self.runCmd("type filter add foo --child b --child j") + + self.expect('frame variable f00_1', + substrs = ['b = 1', + 'j = 17']) + self.expect("frame variable --ptr-depth 1 f00_ptr", matching=False, + substrs = ['r = 45', + 'fake_a = 218103808', + 'a = 12']) + + # now add the synth and it should fail + self.expect("type synth add -l fooSynthProvider foo", error=True, + substrs = ['cannot add']) + + # check the listing + self.expect('type synth list', matching=False, + substrs = ['foo', + 'Python class fooSynthProvider']) + self.expect('type filter list', + substrs = ['foo', + '.b', + '.j']) + + # delete the filter, add the synth + self.runCmd("type filter delete foo") + self.runCmd("type synth add -l fooSynthProvider foo") + + self.expect('frame variable f00_1', matching=False, + substrs = ['b = 1', + 'j = 17']) + self.expect("frame variable --ptr-depth 1 f00_ptr", + substrs = ['r = 45', + 'fake_a = 218103808', + 'a = 12']) + + # check the listing + self.expect('type synth list', + substrs = ['foo', + 'Python class fooSynthProvider']) + self.expect('type filter list', matching=False, + substrs = ['foo', + '.b', + '.j']) + + # delete the synth and check that we get good output + self.runCmd("type synth delete foo") + + self.expect("frame variable f00_1", + substrs = ['a = 280', + 'b = 1', + 'j = 17']); + + self.expect("frame variable f00_1", matching=False, + substrs = ['fake_a = ']) + + def rdar10960550_formatter_commands(self): + """Test that synthetic children persist stoppoints.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + # The second breakpoint is on a multi-line expression, so the comment can't be on the right line... + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line2, num_expected_locations=1, loc_exact=False) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line3, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("command script import ./ftsp.py --allow-reload") + self.runCmd("type synth add -l ftsp.ftsp wrapint") + + # we need to check that the VO is properly updated so that the same synthetic children are reused + # but their values change correctly across stop-points - in order to do this, self.runCmd("next") + # does not work because it forces a wipe of the stack frame - this is why we are using this more contrived + # mechanism to achieve our goal of preserving test_cast as a VO + test_cast = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('test_cast') + + str_cast = str(test_cast) + + if self.TraceOn(): + print(str_cast) + + self.assertTrue(str_cast.find('A') != -1, 'could not find A in output') + self.assertTrue(str_cast.find('B') != -1, 'could not find B in output') + self.assertTrue(str_cast.find('C') != -1, 'could not find C in output') + self.assertTrue(str_cast.find('D') != -1, 'could not find D in output') + self.assertTrue(str_cast.find("4 = '\\0'") != -1, 'could not find item 4 == 0') + + self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().StepOver() + + str_cast = str(test_cast) + + if self.TraceOn(): + print(str_cast) + + # we detect that all the values of the child objects have changed - but the counter-generated item + # is still fixed at 0 because it is cached - this would fail if update(self): in ftsp returned False + # or if synthetic children were not being preserved + self.assertTrue(str_cast.find('Q') != -1, 'could not find Q in output') + self.assertTrue(str_cast.find('X') != -1, 'could not find X in output') + self.assertTrue(str_cast.find('T') != -1, 'could not find T in output') + self.assertTrue(str_cast.find('F') != -1, 'could not find F in output') + self.assertTrue(str_cast.find("4 = '\\0'") != -1, 'could not find item 4 == 0') diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py new file mode 100644 index 000000000000..0dc2c233e2a6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py @@ -0,0 +1,23 @@ +import lldb +class fooSynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj; + self.int_type = valobj.GetType().GetBasicType(lldb.eBasicTypeInt) + def num_children(self): + return 3; + def get_child_at_index(self, index): + if index == 0: + child = self.valobj.GetChildMemberWithName('a'); + if index == 1: + child = self.valobj.CreateChildAtOffset ('fake_a', 1, self.int_type); + if index == 2: + child = self.valobj.GetChildMemberWithName('r'); + return child; + def get_child_index(self, name): + if name == 'a': + return 0; + if name == 'fake_a': + return 1; + return 2; + def update(self): + return True \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/ftsp.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/ftsp.py new file mode 100644 index 000000000000..d162b00db329 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/ftsp.py @@ -0,0 +1,32 @@ +import lldb + +counter = 0 + +class ftsp: + def __init__(self, valobj, dict): + self.valobj = valobj; + def num_children(self): + if self.char.IsValid(): + return 5; + return 0; + def get_child_index(self,name): + return 0; + def get_child_at_index(self,index): + if index == 0: + return self.x.Cast(self.char) + if index == 4: + return self.valobj.CreateValueFromExpression(str(index),'(char)('+str(self.count)+')') + return self.x.CreateChildAtOffset(str(index), + index, + self.char); + def update(self): + self.x = self.valobj.GetChildMemberWithName('x'); + self.char = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar) + global counter + self.count = counter + counter = counter + 1 + return True # important: if we return False here, or fail to return, the test will fail + +def __lldb_init_module(debugger, dict): + global counter + counter = 0 \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp new file mode 100644 index 000000000000..48b29dcfd6e4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp @@ -0,0 +1,66 @@ +struct foo +{ + int a; + int b; + int c; + int d; + int e; + int f; + int g; + int h; + int i; + int j; + int k; + int l; + int m; + int n; + int o; + int p; + int q; + int r; + + foo(int X) : + a(X), + b(X+1), + c(X+3), + d(X+5), + e(X+7), + f(X+9), + g(X+11), + h(X+13), + i(X+15), + j(X+17), + k(X+19), + l(X+21), + m(X+23), + n(X+25), + o(X+27), + p(X+29), + q(X+31), + r(X+33) {} +}; + +struct wrapint +{ + int x; + wrapint(int X) : x(X) {} +}; + +int main() +{ + foo f00_1(0); + foo *f00_ptr = new foo(12); + + f00_1.a++; // Set break point at this line. + + wrapint test_cast('A' + + 256*'B' + + 256*256*'C'+ + 256*256*256*'D'); + // Set cast break point at this line. + test_cast.x = 'Q' + + 256*'X' + + 256*256*'T'+ + 256*256*256*'F'; + return 0; // Set second cast break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/TestDataFormatterScript.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/TestDataFormatterScript.py new file mode 100644 index 000000000000..324b372cb11e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/TestDataFormatterScript.py @@ -0,0 +1,170 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ScriptDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_with_run_command(self): + """Test data formatter commands.""" + self.build() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def data_formatter_commands(self): + """Test that that file and class static variables display correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Set the script here to ease the formatting + script = 'a = valobj.GetChildMemberWithName(\'integer\'); a_val = a.GetValue(); str = \'Hello from Python, \' + a_val + \' time\'; return str + (\'!\' if a_val == \'1\' else \'s!\');' + + self.runCmd("type summary add i_am_cool --python-script \"%s\"" % script) + + self.expect("frame variable one", + substrs = ['Hello from Python', + '1 time!']) + + self.expect("frame variable two", + substrs = ['Hello from Python', + '4 times!']) + + self.runCmd("n"); # skip ahead to make values change + + self.expect("frame variable three", + substrs = ['Hello from Python, 10 times!', + 'Hello from Python, 4 times!']) + + self.runCmd("n"); # skip ahead to make values change + + self.expect("frame variable two", + substrs = ['Hello from Python', + '1 time!']) + + script = 'a = valobj.GetChildMemberWithName(\'integer\'); a_val = a.GetValue(); str = \'int says \' + a_val; return str;' + + # Check that changes in the script are immediately reflected + self.runCmd("type summary add i_am_cool --python-script \"%s\"" % script) + + self.expect("frame variable two", + substrs = ['int says 1']) + + self.expect("frame variable twoptr", + substrs = ['int says 1']) + + # Change the summary + self.runCmd("type summary add --summary-string \"int says ${var.integer}, and float says ${var.floating}\" i_am_cool") + + self.expect("frame variable two", + substrs = ['int says 1', + 'and float says 2.71']) + # Try it for pointers + self.expect("frame variable twoptr", + substrs = ['int says 1', + 'and float says 2.71']) + + # Force a failure for pointers + self.runCmd("type summary add i_am_cool -p --python-script \"%s\"" % script) + + self.expect("frame variable twoptr", matching=False, + substrs = ['and float says 2.71']) + + script = 'return \'Python summary\''; + + self.runCmd("type summary add --name test_summary --python-script \"%s\"" % script) + + # attach the Python named summary to someone + self.expect("frame variable one --summary test_summary", + substrs = ['Python summary']) + + # should not bind to the type + self.expect("frame variable two", matching=False, + substrs = ['Python summary']) + + # and should not stick to the variable + self.expect("frame variable one",matching=False, + substrs = ['Python summary']) + + self.runCmd("type summary add i_am_cool --summary-string \"Text summary\"") + + # should be temporary only + self.expect("frame variable one",matching=False, + substrs = ['Python summary']) + + # use the type summary + self.expect("frame variable two", + substrs = ['Text summary']) + + self.runCmd("n"); # skip ahead to make values change + + # both should use the type summary now + self.expect("frame variable one", + substrs = ['Text summary']) + + self.expect("frame variable two", + substrs = ['Text summary']) + + # disable type summary for pointers, and make a Python regex summary + self.runCmd("type summary add i_am_cool -p --summary-string \"Text summary\"") + self.runCmd("type summary add -x cool --python-script \"%s\"" % script) + + # variables should stick to the type summary + self.expect("frame variable one", + substrs = ['Text summary']) + + self.expect("frame variable two", + substrs = ['Text summary']) + + # array and pointer should match the Python one + self.expect("frame variable twoptr", + substrs = ['Python summary']) + + self.expect("frame variable array", + substrs = ['Python summary']) + + # return pointers to the type summary + self.runCmd("type summary add i_am_cool --summary-string \"Text summary\"") + + self.expect("frame variable one", + substrs = ['Text summary']) + + self.expect("frame variable two", + substrs = ['Text summary']) + + self.expect("frame variable twoptr", + substrs = ['Text summary']) + + self.expect("frame variable array", + substrs = ['Python summary']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/main.cpp new file mode 100644 index 000000000000..aaccb6329acf --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-script/main.cpp @@ -0,0 +1,53 @@ +//===-- 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 +#include + +struct i_am_cool +{ + int integer; + float floating; + char character; + i_am_cool(int I, float F, char C) : + integer(I), floating(F), character(C) {} + i_am_cool() : integer(1), floating(2), character('3') {} + +}; + +struct i_am_cooler +{ + i_am_cool first_cool; + i_am_cool second_cool; + float floating; + + i_am_cooler(int I1, int I2, float F1, float F2, char C1, char C2) : + first_cool(I1,F1,C1), + second_cool(I2,F2,C2), + floating((F1 + F2)/2) {} +}; + +int main (int argc, const char * argv[]) +{ + i_am_cool one(1,3.14,'E'); + i_am_cool two(4,2.71,'G'); + + i_am_cool* twoptr = &two; + + i_am_cool array[5]; + + i_am_cooler three(10,4,1985,1/1/2011,'B','E'); // Set break point at this line. + + two.integer = 1; + + int dummy = 1; + + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/Makefile new file mode 100644 index 000000000000..b438bbb970f6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/Makefile @@ -0,0 +1,19 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules + +CXXFLAGS += -O0 + +ifeq (,$(findstring gcc,$(CC))) +CXXFLAGS += -stdlib=libstdc++ +LDFLAGS += -stdlib=libstdc++ +endif diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/TestDataFormatterSkipSummary.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/TestDataFormatterSkipSummary.py new file mode 100644 index 000000000000..38e700812d05 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/TestDataFormatterSkipSummary.py @@ -0,0 +1,175 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SkipSummaryDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureFreeBSD("llvm.org/pr20548") # fails to build on lab.llvm.org buildbot + @expectedFailureWindows("llvm.org/pr24462") # Data formatters have problems on Windows + def test_with_run_command(self): + """Test data formatter commands.""" + self.build() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def data_formatter_commands(self): + """Test that that file and class static variables display correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + #import lldbsuite.test.lldbutil as lldbutil + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Setup the summaries for this scenario + #self.runCmd("type summary add --summary-string \"${var._M_dataplus._M_p}\" std::string") + self.runCmd("type summary add --summary-string \"Level 1\" \"DeepData_1\"") + self.runCmd("type summary add --summary-string \"Level 2\" \"DeepData_2\" -e") + self.runCmd("type summary add --summary-string \"Level 3\" \"DeepData_3\"") + self.runCmd("type summary add --summary-string \"Level 4\" \"DeepData_4\"") + self.runCmd("type summary add --summary-string \"Level 5\" \"DeepData_5\"") + + # Default case, just print out summaries + self.expect('frame variable', + substrs = ['(DeepData_1) data1 = Level 1', + '(DeepData_2) data2 = Level 2 {', + 'm_child1 = Level 3', + 'm_child2 = Level 3', + 'm_child3 = Level 3', + 'm_child4 = Level 3', + '}']) + + # Skip the default (should be 1) levels of summaries + self.expect('frame variable --no-summary-depth', + substrs = ['(DeepData_1) data1 = {', + 'm_child1 = 0x', + '}', + '(DeepData_2) data2 = {', + 'm_child1 = Level 3', + 'm_child2 = Level 3', + 'm_child3 = Level 3', + 'm_child4 = Level 3', + '}']) + + # Now skip 2 levels of summaries + self.expect('frame variable --no-summary-depth=2', + substrs = ['(DeepData_1) data1 = {', + 'm_child1 = 0x', + '}', + '(DeepData_2) data2 = {', + 'm_child1 = {', + 'm_child1 = 0x', + 'Level 4', + 'm_child2 = {', + 'm_child3 = {', + '}']) + + # Check that no "Level 3" comes out + self.expect('frame variable data1.m_child1 --no-summary-depth=2', matching=False, + substrs = ['Level 3']) + + # Now expand a pointer with 2 level of skipped summaries + self.expect('frame variable data1.m_child1 --no-summary-depth=2', + substrs = ['(DeepData_2 *) data1.m_child1 = 0x']) + + # Deref and expand said pointer + self.expect('frame variable *data1.m_child1 --no-summary-depth=2', + substrs = ['(DeepData_2) *data1.m_child1 = {', + 'm_child2 = {', + 'm_child1 = 0x', + 'Level 4', + '}']) + + # Expand an expression, skipping 2 layers of summaries + self.expect('frame variable data1.m_child1->m_child2 --no-summary-depth=2', + substrs = ['(DeepData_3) data1.m_child1->m_child2 = {', + 'm_child2 = {', + 'm_child1 = Level 5', + 'm_child2 = Level 5', + 'm_child3 = Level 5', + '}']) + + # Expand same expression, skipping only 1 layer of summaries + self.expect('frame variable data1.m_child1->m_child2 --no-summary-depth=1', + substrs = ['(DeepData_3) data1.m_child1->m_child2 = {', + 'm_child1 = 0x', + 'Level 4', + 'm_child2 = Level 4', + '}']) + + # Bad debugging info on SnowLeopard gcc (Apple Inc. build 5666). + # Skip the following tests if the condition is met. + if self.getCompiler().endswith('gcc') and not self.getCompiler().endswith('llvm-gcc'): + import re + gcc_version_output = system([[lldbutil.which(self.getCompiler()), "-v"]])[1] + #print("my output:", gcc_version_output) + for line in gcc_version_output.split(os.linesep): + m = re.search('\(Apple Inc\. build ([0-9]+)\)', line) + #print("line:", line) + if m: + gcc_build = int(m.group(1)) + #print("gcc build:", gcc_build) + if gcc_build >= 5666: + # rdar://problem/9804600" + self.skipTest("rdar://problem/9804600 wrong namespace for std::string in debug info") + + # Expand same expression, skipping 3 layers of summaries + self.expect('frame variable data1.m_child1->m_child2 --show-types --no-summary-depth=3', + substrs = ['(DeepData_3) data1.m_child1->m_child2 = {', + 'm_some_text = "Just a test"', + 'm_child2 = {', + 'm_some_text = "Just a test"']) + + # Expand within a standard string (might depend on the implementation of the C++ stdlib you use) + self.expect('frame variable data1.m_child1->m_child2.m_child1.m_child2 --no-summary-depth=2', + substrs = ['(DeepData_5) data1.m_child1->m_child2.m_child1.m_child2 = {', + 'm_some_text = {', + '_M_dataplus = (_M_p = "Just a test")']) + + # Repeat the above, but only skip 1 level of summaries + self.expect('frame variable data1.m_child1->m_child2.m_child1.m_child2 --no-summary-depth=1', + substrs = ['(DeepData_5) data1.m_child1->m_child2.m_child1.m_child2 = {', + 'm_some_text = "Just a test"', + '}']) + + # Change summary and expand, first without --no-summary-depth then with --no-summary-depth + self.runCmd("type summary add --summary-string \"${var.m_some_text}\" DeepData_5") + + self.expect('fr var data2.m_child4.m_child2.m_child2', + substrs = ['(DeepData_5) data2.m_child4.m_child2.m_child2 = "Just a test"']) + + self.expect('fr var data2.m_child4.m_child2.m_child2 --no-summary-depth', + substrs = ['(DeepData_5) data2.m_child4.m_child2.m_child2 = {', + 'm_some_text = "Just a test"', + '}']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/main.cpp new file mode 100644 index 000000000000..82ffb2c20d47 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-skip-summary/main.cpp @@ -0,0 +1,57 @@ +#include + +struct DeepData_5 +{ + std::string m_some_text; + DeepData_5() : + m_some_text("Just a test") {} +}; + +struct DeepData_4 +{ + DeepData_5 m_child1; + DeepData_5 m_child2; + DeepData_5 m_child3; +}; + +struct DeepData_3 +{ + DeepData_4& m_child1; + DeepData_4 m_child2; + + DeepData_3() : m_child1(* (new DeepData_4())), m_child2(DeepData_4()) {} +}; + +struct DeepData_2 +{ + DeepData_3 m_child1; + DeepData_3 m_child2; + DeepData_3 m_child3; + DeepData_3 m_child4; +}; + +struct DeepData_1 +{ + DeepData_2 *m_child1; + + DeepData_1() : + m_child1(new DeepData_2()) + {} +}; + +/* + type summary add -f "${var._M_dataplus._M_p}" std::string + type summary add -f "Level 1" "DeepData_1" + type summary add -f "Level 2" "DeepData_2" -e + type summary add -f "Level 3" "DeepData_3" + type summary add -f "Level 4" "DeepData_4" + type summary add -f "Level 5" "DeepData_5" + */ + +int main() +{ + DeepData_1 data1; + DeepData_2 data2; + + return 0; // Set break point at this line. +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/TestDataFormatterSmartArray.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/TestDataFormatterSmartArray.py new file mode 100644 index 000000000000..ca8858dbad9b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/TestDataFormatterSmartArray.py @@ -0,0 +1,348 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SmartArrayDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr24462") # Data formatters have problems on Windows + def test_with_run_command(self): + """Test data formatter commands.""" + self.build() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def data_formatter_commands(self): + """Test that that file and class static variables display correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + +# check that we are not looping here + self.runCmd("type summary add --summary-string \"${var%V}\" SomeData") + + self.expect("frame variable data", + substrs = ['SomeData @ 0x']) +# ${var%s} + self.runCmd("type summary add --summary-string \"ptr = ${var%s}\" \"char *\"") + + self.expect("frame variable strptr", + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("frame variable other.strptr", + substrs = ['ptr = \"', + 'Nested Hello world!']) + + self.runCmd("type summary add --summary-string \"arr = ${var%s}\" -x \"char \\[[0-9]+\\]\"") + + self.expect("frame variable strarr", + substrs = ['arr = \"', + 'Hello world!']) + + self.expect("frame variable other.strarr", + substrs = ['arr = \"', + 'Nested Hello world!']) + + self.expect("p strarr", + substrs = ['arr = \"', + 'Hello world!']) + + self.expect("p other.strarr", + substrs = ['arr = \"', + 'Nested Hello world!']) + +# ${var%c} + self.runCmd("type summary add --summary-string \"ptr = ${var%c}\" \"char *\"") + + self.expect("frame variable strptr", + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("frame variable other.strptr", + substrs = ['ptr = \"', + 'Nested Hello world!']) + + self.expect("p strptr", + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("p other.strptr", + substrs = ['ptr = \"', + 'Nested Hello world!']) + + self.runCmd("type summary add --summary-string \"arr = ${var%c}\" -x \"char \\[[0-9]+\\]\"") + + self.expect("frame variable strarr", + substrs = ['arr = \"', + 'Hello world!']) + + self.expect("frame variable other.strarr", + substrs = ['arr = \"', + 'Nested Hello world!']) + + self.expect("p strarr", + substrs = ['arr = \"', + 'Hello world!']) + + self.expect("p other.strarr", + substrs = ['arr = \"', + 'Nested Hello world!']) + +# ${var%char[]} + self.runCmd("type summary add --summary-string \"arr = ${var%char[]}\" -x \"char \\[[0-9]+\\]\"") + + self.expect("frame variable strarr", + substrs = ['arr = \"', + 'Hello world!']) + + self.expect("frame variable other.strarr", + substrs = ['arr = ', + 'Nested Hello world!']) + + self.expect("p strarr", + substrs = ['arr = \"', + 'Hello world!']) + + self.expect("p other.strarr", + substrs = ['arr = ', + 'Nested Hello world!']) + + self.runCmd("type summary add --summary-string \"ptr = ${var%char[]}\" \"char *\"") + + self.expect("frame variable strptr", + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("frame variable other.strptr", + substrs = ['ptr = \"', + 'Nested Hello world!']) + + self.expect("p strptr", + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("p other.strptr", + substrs = ['ptr = \"', + 'Nested Hello world!']) + +# ${var%a} + self.runCmd("type summary add --summary-string \"arr = ${var%a}\" -x \"char \\[[0-9]+\\]\"") + + self.expect("frame variable strarr", + substrs = ['arr = \"', + 'Hello world!']) + + self.expect("frame variable other.strarr", + substrs = ['arr = ', + 'Nested Hello world!']) + + self.expect("p strarr", + substrs = ['arr = \"', + 'Hello world!']) + + self.expect("p other.strarr", + substrs = ['arr = ', + 'Nested Hello world!']) + + self.runCmd("type summary add --summary-string \"ptr = ${var%a}\" \"char *\"") + + self.expect("frame variable strptr", + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("frame variable other.strptr", + substrs = ['ptr = \"', + 'Nested Hello world!']) + + self.expect("p strptr", + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("p other.strptr", + substrs = ['ptr = \"', + 'Nested Hello world!']) + + self.runCmd("type summary add --summary-string \"ptr = ${var[]%char[]}\" \"char *\"") + +# I do not know the size of the data, but you are asking for a full array slice.. +# use the ${var%char[]} to obtain a string as result + self.expect("frame variable strptr", matching=False, + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("frame variable other.strptr", matching=False, + substrs = ['ptr = \"', + 'Nested Hello world!']) + + self.expect("p strptr", matching=False, + substrs = ['ptr = \"', + 'Hello world!']) + + self.expect("p other.strptr", matching=False, + substrs = ['ptr = \"', + 'Nested Hello world!']) + +# You asked an array-style printout... + self.runCmd("type summary add --summary-string \"ptr = ${var[0-1]%char[]}\" \"char *\"") + + self.expect("frame variable strptr", + substrs = ['ptr = ', + '[{H},{e}]']) + + self.expect("frame variable other.strptr", + substrs = ['ptr = ', + '[{N},{e}]']) + + self.expect("p strptr", + substrs = ['ptr = ', + '[{H},{e}]']) + + self.expect("p other.strptr", + substrs = ['ptr = ', + '[{N},{e}]']) + +# using [] is required here + self.runCmd("type summary add --summary-string \"arr = ${var%x}\" \"int [5]\"") + + self.expect("frame variable intarr",matching=False, + substrs = ['0x00000001,0x00000001,0x00000002,0x00000003,0x00000005']) + + self.expect("frame variable other.intarr", matching=False, + substrs = ['0x00000009,0x00000008,0x00000007,0x00000006,0x00000005']) + + self.runCmd("type summary add --summary-string \"arr = ${var[]%x}\" \"int [5]\"") + + self.expect("frame variable intarr", + substrs = ['intarr = arr =', + '0x00000001,0x00000001,0x00000002,0x00000003,0x00000005']) + + self.expect("frame variable other.intarr", + substrs = ['intarr = arr =', + '0x00000009,0x00000008,0x00000007,0x00000006,0x00000005']) + +# printing each array item as an array + self.runCmd("type summary add --summary-string \"arr = ${var[]%uint32_t[]}\" \"int [5]\"") + + self.expect("frame variable intarr", + substrs = ['intarr = arr =', + '{0x00000001},{0x00000001},{0x00000002},{0x00000003},{0x00000005}']) + + self.expect("frame variable other.intarr", + substrs = ['intarr = arr = ', + '{0x00000009},{0x00000008},{0x00000007},{0x00000006},{0x00000005}']) + +# printing full array as an array + self.runCmd("type summary add --summary-string \"arr = ${var%uint32_t[]}\" \"int [5]\"") + + self.expect("frame variable intarr", + substrs = ['intarr = arr =', + '0x00000001,0x00000001,0x00000002,0x00000003,0x00000005']) + + self.expect("frame variable other.intarr", + substrs = ['intarr = arr =', + '0x00000009,0x00000008,0x00000007,0x00000006,0x00000005']) + +# printing each array item as an array + self.runCmd("type summary add --summary-string \"arr = ${var[]%float32[]}\" \"float [7]\"") + + self.expect("frame variable flarr", + substrs = ['flarr = arr =', + '{78.5},{77.25},{78},{76.125},{76.75},{76.875},{77}']) + + self.expect("frame variable other.flarr", + substrs = ['flarr = arr = ', + '{25.5},{25.25},{25.125},{26.75},{27.375},{27.5},{26.125}']) + +# printing full array as an array + self.runCmd("type summary add --summary-string \"arr = ${var%float32[]}\" \"float [7]\"") + + self.expect("frame variable flarr", + substrs = ['flarr = arr =', + '78.5,77.25,78,76.125,76.75,76.875,77']) + + self.expect("frame variable other.flarr", + substrs = ['flarr = arr =', + '25.5,25.25,25.125,26.75,27.375,27.5,26.125']) + +# using array smart summary strings for pointers should make no sense + self.runCmd("type summary add --summary-string \"arr = ${var%float32[]}\" \"float *\"") + self.runCmd("type summary add --summary-string \"arr = ${var%int32_t[]}\" \"int *\"") + + self.expect("frame variable flptr", matching=False, + substrs = ['78.5,77.25,78,76.125,76.75,76.875,77']) + + self.expect("frame variable intptr", matching=False, + substrs = ['1,1,2,3,5']) + +# use y and Y + self.runCmd("type summary add --summary-string \"arr = ${var%y}\" \"float [7]\"") + self.runCmd("type summary add --summary-string \"arr = ${var%y}\" \"int [5]\"") + + self.expect("frame variable flarr", + substrs = ['flarr = arr =', + '00 00 9d 42,00 80 9a 42,00 00 9c 42,00 40 98 42,00 80 99 42,00 c0 99 42,00 00 9a 42']) + + self.expect("frame variable other.flarr", + substrs = ['flarr = arr =', + '00 00 cc 41,00 00 ca 41,00 00 c9 41,00 00 d6 41,00 00 db 41,00 00 dc 41,00 00 d1 41']) + + self.expect("frame variable intarr", + substrs = ['intarr = arr =', + '01 00 00 00,01 00 00 00,02 00 00 00,03 00 00 00,05 00 00 00']) + + self.expect("frame variable other.intarr", + substrs = ['intarr = arr = ', + '09 00 00 00,08 00 00 00,07 00 00 00,06 00 00 00,05 00 00 00']) + + self.runCmd("type summary add --summary-string \"arr = ${var%Y}\" \"float [7]\"") + self.runCmd("type summary add --summary-string \"arr = ${var%Y}\" \"int [5]\"") + + self.expect("frame variable flarr", + substrs = ['flarr = arr =', + '00 00 9d 42 ...B,00 80 9a 42 ...B,00 00 9c 42 ...B,00 40 98 42 .@.B,00 80 99 42 ...B,00 c0 99 42 ...B,00 00 9a 42 ...B']) + + self.expect("frame variable other.flarr", + substrs = ['flarr = arr =', + '00 00 cc 41 ...A,00 00 ca 41 ...A,00 00 c9 41 ...A,00 00 d6 41 ...A,00 00 db 41 ...A,00 00 dc 41 ...A,00 00 d1 41 ...A']) + + self.expect("frame variable intarr", + substrs = ['intarr = arr =', + '....,01 00 00 00', + '....,05 00 00 00']) + + self.expect("frame variable other.intarr", + substrs = ['intarr = arr = ', + '09 00 00 00', + '....,07 00 00 00']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/main.cpp new file mode 100644 index 000000000000..9279e414be31 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-smart-array/main.cpp @@ -0,0 +1,65 @@ +//===-- 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 +#include +#include + +struct SomeData +{ + int x; +}; + +struct SomeOtherData +{ + char strarr[32]; + char *strptr; + int intarr[5]; + float flarr[7]; + + SomeOtherData() + { + strcpy(strarr,"Nested Hello world!"); + strptr = new char[128]; + strcpy(strptr,"Nested Hello world!"); + intarr[0] = 9; + intarr[1] = 8; + intarr[2] = 7; + intarr[3] = 6; + intarr[4] = 5; + + flarr[0] = 25.5; + flarr[1] = 25.25; + flarr[2] = 25.125; + flarr[3] = 26.75; + flarr[4] = 27.375; + flarr[5] = 27.5; + flarr[6] = 26.125; + } +}; + +int main (int argc, const char * argv[]) +{ + char strarr[32] = "Hello world!"; + char *strptr = NULL; + strptr = "Hello world!"; + int intarr[5] = {1,1,2,3,5}; + float flarr[7] = {78.5,77.25,78.0,76.125,76.75,76.875,77.0}; + + SomeData data; + + SomeOtherData other; + + float* flptr = flarr; + int* intptr = intarr; + + return 0; // Set break point at this line. + +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/Makefile new file mode 100644 index 000000000000..d37bef7dc5cc --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../../../make +CXX_SOURCES := main.cpp +CXXFLAGS += -std=c++11 +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/TestInitializerList.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/TestInitializerList.py new file mode 100644 index 000000000000..e01f1b6679fc --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/TestInitializerList.py @@ -0,0 +1,40 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class InitializerListTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows # libc++ not ported to Windows yet + @skipIfGcc + @expectedFailureLinux # fails on clang 3.5 and tot + def test(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.")) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("frame variable ili", substrs = ['[1] = 2','[4] = 5']) + self.expect("frame variable ils", substrs = ['[4] = "surprise it is a long string!! yay!!"']) + + self.expect('image list', substrs = self.getLibcPlusPlusLibs()) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/main.cpp new file mode 100644 index 000000000000..9109a20cb510 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/initializerlist/main.cpp @@ -0,0 +1,21 @@ +//===-- 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 +#include + +int main () +{ + std::initializer_list ili{1,2,3,4,5}; + std::initializer_list ils{"1","2","3","4","surprise it is a long string!! yay!!"}; + + return 0; // Set break point at this line. +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py new file mode 100644 index 000000000000..a87a2ed2d434 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py @@ -0,0 +1,66 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxIteratorDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows yet + def test_with_run_command(self): + """Test that libc++ iterators format properly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list', substrs = self.getLibcPlusPlusLibs()) + + self.expect('frame variable ivI', substrs = ['item = 3']) + self.expect('expr ivI', substrs = ['item = 3']) + + self.expect('frame variable iimI', substrs = ['first = 0','second = 12']) + self.expect('expr iimI', substrs = ['first = 0','second = 12']) + + self.expect('frame variable simI', substrs = ['first = "world"','second = 42']) + self.expect('expr simI', substrs = ['first = "world"','second = 42']) + + self.expect('frame variable svI', substrs = ['item = "hello"']) + self.expect('expr svI', substrs = ['item = "hello"']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp new file mode 100644 index 000000000000..97b37851f53d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp @@ -0,0 +1,42 @@ +#include +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include +#include + +typedef std::map intint_map; +typedef std::map strint_map; + +typedef std::vector int_vector; +typedef std::vector string_vector; + +typedef intint_map::iterator iimter; +typedef strint_map::iterator simter; + +typedef int_vector::iterator ivter; +typedef string_vector::iterator svter; + +int main() +{ + intint_map iim; + iim[0] = 12; + + strint_map sim; + sim["world"] = 42; + + int_vector iv; + iv.push_back(3); + + string_vector sv; + sv.push_back("hello"); + + iimter iimI = iim.begin(); + simter simI = sim.begin(); + + ivter ivI = iv.begin(); + svter svI = sv.begin(); + + return 0; // Set break point at this line. +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/TestDataFormatterLibcxxList.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/TestDataFormatterLibcxxList.py new file mode 100644 index 000000000000..18ee31a86106 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/TestDataFormatterLibcxxList.py @@ -0,0 +1,186 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time, re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxListDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + self.line2 = line_number('main.cpp', '// Set second break point at this line.') + self.line3 = line_number('main.cpp', '// Set third break point at this line.') + self.line4 = line_number('main.cpp', '// Set fourth break point at this line.') + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows yet + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line2, num_expected_locations=-1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line3, num_expected_locations=-1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line4, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("frame variable numbers_list --show-types") + self.runCmd("type summary add std::int_list std::string_list int_list string_list --summary-string \"list has ${svar%#} items\" -e") + self.runCmd("type format add -f hex int") + + self.expect("frame variable numbers_list --raw", matching=False, + substrs = ['list has 0 items', + '{}']) + + self.expect("frame variable numbers_list", + substrs = ['list has 0 items', + '{}']) + + self.expect("p numbers_list", + substrs = ['list has 0 items', + '{}']) + + self.runCmd("n") + + self.expect("frame variable numbers_list", + substrs = ['list has 1 items', + '[0] = ', + '0x12345678']) + + self.runCmd("n");self.runCmd("n");self.runCmd("n"); + + self.expect("frame variable numbers_list", + substrs = ['list has 4 items', + '[0] = ', + '0x12345678', + '[1] =', + '0x11223344', + '[2] =', + '0xbeeffeed', + '[3] =', + '0x00abba00']) + + self.runCmd("n");self.runCmd("n"); + + self.expect("frame variable numbers_list", + substrs = ['list has 6 items', + '[0] = ', + '0x12345678', + '0x11223344', + '0xbeeffeed', + '0x00abba00', + '[4] =', + '0x0abcdef0', + '[5] =', + '0x0cab0cab']) + + self.expect("p numbers_list", + substrs = ['list has 6 items', + '[0] = ', + '0x12345678', + '0x11223344', + '0xbeeffeed', + '0x00abba00', + '[4] =', + '0x0abcdef0', + '[5] =', + '0x0cab0cab']) + + # check access-by-index + self.expect("frame variable numbers_list[0]", + substrs = ['0x12345678']); + self.expect("frame variable numbers_list[1]", + substrs = ['0x11223344']); + + self.runCmd("n") + + self.expect("frame variable numbers_list", + substrs = ['list has 0 items', + '{}']) + + self.runCmd("n");self.runCmd("n");self.runCmd("n");self.runCmd("n"); + + self.expect("frame variable numbers_list", + substrs = ['list has 4 items', + '[0] = ', '1', + '[1] = ', '2', + '[2] = ', '3', + '[3] = ', '4']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("numbers_list").MightHaveChildren(), "numbers_list.MightHaveChildren() says False for non empty!") + + self.runCmd("type format delete int") + + self.runCmd("c") + + self.expect("frame variable text_list", + substrs = ['list has 3 items', + '[0]', 'goofy', + '[1]', 'is', + '[2]', 'smart']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("text_list").MightHaveChildren(), "text_list.MightHaveChildren() says False for non empty!") + + self.expect("p text_list", + substrs = ['list has 3 items', + '\"goofy\"', + '\"is\"', + '\"smart\"']) + + self.runCmd("n") + + # check access-by-index + self.expect("frame variable text_list[0]", + substrs = ['goofy']); + self.expect("frame variable text_list[3]", + substrs = ['!!!']); + + self.runCmd("continue") + + # check that the list provider correctly updates if elements move + countingList = self.frame().FindVariable("countingList") + countingList.SetPreferDynamicValue(True) + countingList.SetPreferSyntheticValue(True) + + self.assertTrue(countingList.GetChildAtIndex(0).GetValueAsUnsigned(0) == 3141, "list[0] == 3141") + self.assertTrue(countingList.GetChildAtIndex(1).GetValueAsUnsigned(0) == 3141, "list[1] == 3141") + + self.runCmd("continue") + + self.assertTrue(countingList.GetChildAtIndex(0).GetValueAsUnsigned(0) == 3141, "uniqued list[0] == 3141") + self.assertTrue(countingList.GetChildAtIndex(1).GetValueAsUnsigned(0) == 3142, "uniqued list[1] == 3142") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/Makefile new file mode 100644 index 000000000000..a5dabdb6349d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/TestDataFormatterLibcxxListLoop.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/TestDataFormatterLibcxxListLoop.py new file mode 100644 index 000000000000..167cb2b887af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/TestDataFormatterLibcxxListLoop.py @@ -0,0 +1,54 @@ +""" +Test that the debugger handles loops in std::list (which can appear as a result of e.g. memory +corruption). +""" + +from __future__ import print_function + + + +import os, time, re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxListDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows yet + @add_test_categories(["pyapi"]) + def test_with_run_command(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target and target.IsValid(), "Target is valid") + + file_spec = lldb.SBFileSpec ("main.cpp", False) + breakpoint1 = target.BreakpointCreateBySourceRegex('// Set break point at this line.', file_spec) + self.assertTrue(breakpoint1 and breakpoint1.IsValid()) + breakpoint2 = target.BreakpointCreateBySourceRegex('// Set second break point at this line.', file_spec) + self.assertTrue(breakpoint2 and breakpoint2.IsValid()) + + # Run the program, it should stop at breakpoint 1. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + lldbutil.skip_if_library_missing(self, target, lldbutil.PrintableRegex("libc\+\+")) + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + self.assertEqual(len(lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint1)), 1) + + # verify our list is displayed correctly + self.expect("frame variable *numbers_list", substrs=['[0] = 1', '[1] = 2', '[2] = 3', '[3] = 4', '[5] = 6']) + + # Continue to breakpoint 2. + process.Continue() + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + self.assertEqual(len(lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint2)), 1) + + # The list is now inconsistent. However, we should be able to get the first three + # elements at least (and most importantly, not crash). + self.expect("frame variable *numbers_list", substrs=['[0] = 1', '[1] = 2', '[2] = 3']) + + # Run to completion. + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/main.cpp new file mode 100644 index 000000000000..6a1266528d5e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/main.cpp @@ -0,0 +1,30 @@ +// Evil hack: To simulate memory corruption, we want to fiddle with some internals of std::list. +// Make those accessible to us. +#define private public +#define protected public + +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include + +#include + +typedef std::list int_list; + +int main() +{ +#ifdef LLDB_USING_LIBCPP + int_list *numbers_list = new int_list{1,2,3,4,5,6,7,8,9,10}; + + auto *third_elem = numbers_list->__end_.__next_->__next_->__next_; // Set break point at this line. + assert(third_elem->__value_ == 3); + auto *fifth_elem = third_elem->__next_->__next_; + assert(fifth_elem->__value_ == 5); + fifth_elem->__next_ = third_elem; +#endif + + // Any attempt to free the list will probably crash the program. Let's just leak it. + return 0; // Set second break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/main.cpp new file mode 100644 index 000000000000..4f2bd74495a9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/main.cpp @@ -0,0 +1,43 @@ +#include +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include + + +typedef std::list int_list; +typedef std::list string_list; + +int main() +{ + int_list numbers_list; + + (numbers_list.push_back(0x12345678)); // Set break point at this line. + (numbers_list.push_back(0x11223344)); + (numbers_list.push_back(0xBEEFFEED)); + (numbers_list.push_back(0x00ABBA00)); + (numbers_list.push_back(0x0ABCDEF0)); + (numbers_list.push_back(0x0CAB0CAB)); + + numbers_list.clear(); + + (numbers_list.push_back(1)); + (numbers_list.push_back(2)); + (numbers_list.push_back(3)); + (numbers_list.push_back(4)); + + string_list text_list; + (text_list.push_back(std::string("goofy"))); + (text_list.push_back(std::string("is"))); + (text_list.push_back(std::string("smart"))); + + (text_list.push_back(std::string("!!!"))); // Set second break point at this line. + + std::list countingList = {3141, 3142, 3142,3142,3142, 3142, 3142, 3141}; + countingList.sort(); + countingList.unique(); // Set third break point at this line. + countingList.size(); // Set fourth break point at this line. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py new file mode 100644 index 000000000000..70a98f9c2cae --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py @@ -0,0 +1,298 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxMapDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows yet + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.")) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list', substrs = self.getLibcPlusPlusLibs()) + + self.expect('frame variable ii', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ii', + substrs = ['size=2', + '[0] = ', + 'first = 0', + 'second = 0', + '[1] = ', + 'first = 1', + 'second = 1']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ii', + substrs = ['size=4', + '[2] = ', + 'first = 2', + 'second = 0', + '[3] = ', + 'first = 3', + 'second = 1']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable ii", + substrs = ['size=8', + '[5] = ', + 'first = 5', + 'second = 0', + '[7] = ', + 'first = 7', + 'second = 1']) + + self.expect("p ii", + substrs = ['size=8', + '[5] = ', + 'first = 5', + 'second = 0', + '[7] = ', + 'first = 7', + 'second = 1']) + + # check access-by-index + self.expect("frame variable ii[0]", + substrs = ['first = 0', + 'second = 0']); + self.expect("frame variable ii[3]", + substrs = ['first =', + 'second =']); + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("ii").MightHaveChildren(), "ii.MightHaveChildren() says False for non empty!") + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression ii[8]", matching=False, error=True, + # substrs = ['1234567']) + + self.runCmd("continue"); + + self.expect('frame variable ii', + substrs = ['size=0', + '{}']) + + self.expect('frame variable si', + substrs = ['size=0', + '{}']) + + self.runCmd("continue"); + + self.expect('frame variable si', + substrs = ['size=1', + '[0] = ', + 'first = \"zero\"', + 'second = 0']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable si", + substrs = ['size=4', + '[0] = ', + 'first = \"zero\"', + 'second = 0', + '[1] = ', + 'first = \"one\"', + 'second = 1', + '[2] = ', + 'first = \"two\"', + 'second = 2', + '[3] = ', + 'first = \"three\"', + 'second = 3']) + + self.expect("p si", + substrs = ['size=4', + '[0] = ', + 'first = \"zero\"', + 'second = 0', + '[1] = ', + 'first = \"one\"', + 'second = 1', + '[2] = ', + 'first = \"two\"', + 'second = 2', + '[3] = ', + 'first = \"three\"', + 'second = 3']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("si").MightHaveChildren(), "si.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable si[0]", + substrs = ['first = ', 'one', + 'second = 1']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression si[0]", matching=False, error=True, + # substrs = ['first = ', 'zero']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable si', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable is', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable is", + substrs = ['size=4', + '[0] = ', + 'second = \"goofy\"', + 'first = 85', + '[1] = ', + 'second = \"is\"', + 'first = 1', + '[2] = ', + 'second = \"smart\"', + 'first = 2', + '[3] = ', + 'second = \"!!!\"', + 'first = 3']) + + self.expect("p is", + substrs = ['size=4', + '[0] = ', + 'second = \"goofy\"', + 'first = 85', + '[1] = ', + 'second = \"is\"', + 'first = 1', + '[2] = ', + 'second = \"smart\"', + 'first = 2', + '[3] = ', + 'second = \"!!!\"', + 'first = 3']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("is").MightHaveChildren(), "is.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable is[0]", + substrs = ['first = ', + 'second =']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression is[0]", matching=False, error=True, + # substrs = ['first = ', 'goofy']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable is', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ss', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable ss", + substrs = ['size=3', + '[0] = ', + 'second = \"hello\"', + 'first = \"ciao\"', + '[1] = ', + 'second = \"house\"', + 'first = \"casa\"', + '[2] = ', + 'second = \"cat\"', + 'first = \"gatto\"']) + + self.expect("p ss", + substrs = ['size=3', + '[0] = ', + 'second = \"hello\"', + 'first = \"ciao\"', + '[1] = ', + 'second = \"house\"', + 'first = \"casa\"', + '[2] = ', + 'second = \"cat\"', + 'first = \"gatto\"']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("ss").MightHaveChildren(), "ss.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable ss[2]", + substrs = ['gatto', 'cat']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression ss[3]", matching=False, error=True, + # substrs = ['gatto']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ss', + substrs = ['size=0', + '{}']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/main.cpp new file mode 100644 index 000000000000..6247ca8b2412 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/main.cpp @@ -0,0 +1,77 @@ +#include +#include + +#define intint_map std::map +#define strint_map std::map +#define intstr_map std::map +#define strstr_map std::map + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intint_map ii; + + ii[0] = 0; // Set break point at this line. + ii[1] = 1; + thefoo_rw(1); // Set break point at this line. + ii[2] = 0; + ii[3] = 1; + thefoo_rw(1); // Set break point at this line. + ii[4] = 0; + ii[5] = 1; + ii[6] = 0; + ii[7] = 1; + thefoo_rw(1); // Set break point at this line. + ii[85] = 1234567; + + ii.clear(); + + strint_map si; + thefoo_rw(1); // Set break point at this line. + + si["zero"] = 0; + thefoo_rw(1); // Set break point at this line. + si["one"] = 1; + si["two"] = 2; + si["three"] = 3; + thefoo_rw(1); // Set break point at this line. + si["four"] = 4; + + si.clear(); + thefoo_rw(1); // Set break point at this line. + + intstr_map is; + thefoo_rw(1); // Set break point at this line. + is[85] = "goofy"; + is[1] = "is"; + is[2] = "smart"; + is[3] = "!!!"; + thefoo_rw(1); // Set break point at this line. + + is.clear(); + thefoo_rw(1); // Set break point at this line. + + strstr_map ss; + thefoo_rw(1); // Set break point at this line. + + ss["ciao"] = "hello"; + ss["casa"] = "house"; + ss["gatto"] = "cat"; + thefoo_rw(1); // Set break point at this line. + ss["a Mac.."] = "..is always a Mac!"; + + ss.clear(); + thefoo_rw(1); // Set break point at this line. + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/TestDataFormatterLibccMultiMap.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/TestDataFormatterLibccMultiMap.py new file mode 100644 index 000000000000..d0ca73d3251d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/TestDataFormatterLibccMultiMap.py @@ -0,0 +1,298 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxMultiMapDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows # libc++ not ported to Windows yet + @skipIfGcc + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.")) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list', substrs = self.getLibcPlusPlusLibs()) + + self.expect('frame variable ii', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ii', + substrs = ['size=2', + '[0] = ', + 'first = 0', + 'second = 0', + '[1] = ', + 'first = 1', + 'second = 1']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ii', + substrs = ['size=4', + '[2] = ', + 'first = 2', + 'second = 0', + '[3] = ', + 'first = 3', + 'second = 1']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable ii", + substrs = ['size=8', + '[5] = ', + 'first = 5', + 'second = 0', + '[7] = ', + 'first = 7', + 'second = 1']) + + self.expect("p ii", + substrs = ['size=8', + '[5] = ', + 'first = 5', + 'second = 0', + '[7] = ', + 'first = 7', + 'second = 1']) + + # check access-by-index + self.expect("frame variable ii[0]", + substrs = ['first = 0', + 'second = 0']); + self.expect("frame variable ii[3]", + substrs = ['first =', + 'second =']); + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("ii").MightHaveChildren(), "ii.MightHaveChildren() says False for non empty!") + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression ii[8]", matching=False, error=True, + # substrs = ['1234567']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ii', + substrs = ['size=0', + '{}']) + + self.expect('frame variable si', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable si', + substrs = ['size=1', + '[0] = ', + 'first = \"zero\"', + 'second = 0']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable si", + substrs = ['size=4', + '[0] = ', + 'first = \"zero\"', + 'second = 0', + '[1] = ', + 'first = \"one\"', + 'second = 1', + '[2] = ', + 'first = \"two\"', + 'second = 2', + '[3] = ', + 'first = \"three\"', + 'second = 3']) + + self.expect("p si", + substrs = ['size=4', + '[0] = ', + 'first = \"zero\"', + 'second = 0', + '[1] = ', + 'first = \"one\"', + 'second = 1', + '[2] = ', + 'first = \"two\"', + 'second = 2', + '[3] = ', + 'first = \"three\"', + 'second = 3']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("si").MightHaveChildren(), "si.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable si[0]", + substrs = ['first = ', 'one', + 'second = 1']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression si[0]", matching=False, error=True, + # substrs = ['first = ', 'zero']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable si', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable is', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable is", + substrs = ['size=4', + '[0] = ', + 'second = \"goofy\"', + 'first = 85', + '[1] = ', + 'second = \"is\"', + 'first = 1', + '[2] = ', + 'second = \"smart\"', + 'first = 2', + '[3] = ', + 'second = \"!!!\"', + 'first = 3']) + + self.expect("p is", + substrs = ['size=4', + '[0] = ', + 'second = \"goofy\"', + 'first = 85', + '[1] = ', + 'second = \"is\"', + 'first = 1', + '[2] = ', + 'second = \"smart\"', + 'first = 2', + '[3] = ', + 'second = \"!!!\"', + 'first = 3']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("is").MightHaveChildren(), "is.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable is[0]", + substrs = ['first = ', + 'second =']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression is[0]", matching=False, error=True, + # substrs = ['first = ', 'goofy']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable is', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ss', + substrs = ['size=0', + '{}']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable ss", + substrs = ['size=3', + '[0] = ', + 'second = \"hello\"', + 'first = \"ciao\"', + '[1] = ', + 'second = \"house\"', + 'first = \"casa\"', + '[2] = ', + 'second = \"cat\"', + 'first = \"gatto\"']) + + self.expect("p ss", + substrs = ['size=3', + '[0] = ', + 'second = \"hello\"', + 'first = \"ciao\"', + '[1] = ', + 'second = \"house\"', + 'first = \"casa\"', + '[2] = ', + 'second = \"cat\"', + 'first = \"gatto\"']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("ss").MightHaveChildren(), "ss.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable ss[2]", + substrs = ['gatto', 'cat']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression ss[3]", matching=False, error=True, + # substrs = ['gatto']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect('frame variable ss', + substrs = ['size=0', + '{}']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/main.cpp new file mode 100644 index 000000000000..e8385994125d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/main.cpp @@ -0,0 +1,77 @@ +#include +#include + +#define intint_map std::multimap +#define strint_map std::multimap +#define intstr_map std::multimap +#define strstr_map std::multimap + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intint_map ii; + + ii.emplace(0,0); // Set break point at this line. + ii.emplace(1,1); + thefoo_rw(1); // Set break point at this line. + ii.emplace(2,0); + ii.emplace(3,1); + thefoo_rw(1); // Set break point at this line. + ii.emplace(4,0); + ii.emplace(5,1); + ii.emplace(6,0); + ii.emplace(7,1); + thefoo_rw(1); // Set break point at this line. + ii.emplace(85,1234567); + + ii.clear(); + + strint_map si; + thefoo_rw(1); // Set break point at this line. + + si.emplace("zero",0); + thefoo_rw(1); // Set break point at this line. + si.emplace("one",1); + si.emplace("two",2); + si.emplace("three",3); + thefoo_rw(1); // Set break point at this line. + si.emplace("four",4); + + si.clear(); + thefoo_rw(1); // Set break point at this line. + + intstr_map is; + thefoo_rw(1); // Set break point at this line. + is.emplace(85,"goofy"); + is.emplace(1,"is"); + is.emplace(2,"smart"); + is.emplace(3,"!!!"); + thefoo_rw(1); // Set break point at this line. + + is.clear(); + thefoo_rw(1); // Set break point at this line. + + strstr_map ss; + thefoo_rw(1); // Set break point at this line. + + ss.emplace("ciao","hello"); + ss.emplace("casa","house"); + ss.emplace("gatto","cat"); + thefoo_rw(1); // Set break point at this line. + ss.emplace("a Mac..","..is always a Mac!"); + + ss.clear(); + thefoo_rw(1); // Set break point at this line. + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py new file mode 100644 index 000000000000..384f6130d0c0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py @@ -0,0 +1,69 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxMultiSetDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows yet + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.")) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list', substrs = self.getLibcPlusPlusLibs()) + + self.expect("frame variable ii",substrs = ["size=0","{}"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ii",substrs = ["size=6","[0] = 0","[1] = 1", "[2] = 2", "[3] = 3", "[4] = 4", "[5] = 5"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ii",substrs = ["size=7","[2] = 2", "[3] = 3", "[6] = 6"]) + self.expect("p ii",substrs = ["size=7","[2] = 2", "[3] = 3", "[6] = 6"]) + self.expect("frame variable ii[2]",substrs = [" = 2"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ii",substrs = ["size=0","{}"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ii",substrs = ["size=0","{}"]) + self.expect("frame variable ss",substrs = ["size=0","{}"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ss",substrs = ["size=2",'[0] = "a"','[1] = "a very long string is right here"']) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ss",substrs = ["size=4",'[2] = "b"','[3] = "c"','[0] = "a"','[1] = "a very long string is right here"']) + self.expect("p ss",substrs = ["size=4",'[2] = "b"','[3] = "c"','[0] = "a"','[1] = "a very long string is right here"']) + self.expect("frame variable ss[2]",substrs = [' = "b"']) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ss",substrs = ["size=3",'[0] = "a"','[1] = "a very long string is right here"','[2] = "c"']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/main.cpp new file mode 100644 index 000000000000..1e1dd3b16039 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/main.cpp @@ -0,0 +1,57 @@ +#include +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include + +typedef std::multiset intset; +typedef std::multiset stringset; + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intset ii; + thefoo_rw(1); // Set break point at this line. + + ii.insert(0); + ii.insert(1); + ii.insert(2); + ii.insert(3); + ii.insert(4); + ii.insert(5); + thefoo_rw(1); // Set break point at this line. + + ii.insert(6); + thefoo_rw(1); // Set break point at this line. + + ii.clear(); + thefoo_rw(1); // Set break point at this line. + + stringset ss; + thefoo_rw(1); // Set break point at this line. + + ss.insert("a"); + ss.insert("a very long string is right here"); + thefoo_rw(1); // Set break point at this line. + + ss.insert("b"); + ss.insert("c"); + thefoo_rw(1); // Set break point at this line. + + ss.erase("b"); + thefoo_rw(1); // Set break point at this line. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py new file mode 100644 index 000000000000..fcbfb0a8f0ed --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py @@ -0,0 +1,69 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxSetDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows yet + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.")) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list', substrs = self.getLibcPlusPlusLibs()) + + self.expect("frame variable ii",substrs = ["size=0","{}"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ii",substrs = ["size=6","[0] = 0","[1] = 1", "[2] = 2", "[3] = 3", "[4] = 4", "[5] = 5"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ii",substrs = ["size=7","[2] = 2", "[3] = 3", "[6] = 6"]) + self.expect("frame variable ii[2]",substrs = [" = 2"]) + self.expect("p ii",substrs = ["size=7","[2] = 2", "[3] = 3", "[6] = 6"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ii",substrs = ["size=0","{}"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ii",substrs = ["size=0","{}"]) + self.expect("frame variable ss",substrs = ["size=0","{}"]) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ss",substrs = ["size=2",'[0] = "a"','[1] = "a very long string is right here"']) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ss",substrs = ["size=4",'[2] = "b"','[3] = "c"','[0] = "a"','[1] = "a very long string is right here"']) + self.expect("p ss",substrs = ["size=4",'[2] = "b"','[3] = "c"','[0] = "a"','[1] = "a very long string is right here"']) + self.expect("frame variable ss[2]",substrs = [' = "b"']) + lldbutil.continue_to_breakpoint(self.process(), bkpt) + self.expect("frame variable ss",substrs = ["size=3",'[0] = "a"','[1] = "a very long string is right here"','[2] = "c"']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/main.cpp new file mode 100644 index 000000000000..cc3033ef26e4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/main.cpp @@ -0,0 +1,57 @@ +#include +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include + +typedef std::set intset; +typedef std::set stringset; + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intset ii; + thefoo_rw(1); // Set break point at this line. + + ii.insert(0); + ii.insert(1); + ii.insert(2); + ii.insert(3); + ii.insert(4); + ii.insert(5); + thefoo_rw(1); // Set break point at this line. + + ii.insert(6); + thefoo_rw(1); // Set break point at this line. + + ii.clear(); + thefoo_rw(1); // Set break point at this line. + + stringset ss; + thefoo_rw(1); // Set break point at this line. + + ss.insert("a"); + ss.insert("a very long string is right here"); + thefoo_rw(1); // Set break point at this line. + + ss.insert("b"); + ss.insert("c"); + thefoo_rw(1); // Set break point at this line. + + ss.erase("b"); + thefoo_rw(1); // Set break point at this line. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py new file mode 100644 index 000000000000..942124255f55 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py @@ -0,0 +1,86 @@ +#coding=utf8 +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxStringDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows yet + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect("frame variable", + substrs = ['(std::__1::wstring) s = L"hello world! מזל טוב!"', + '(std::__1::wstring) S = L"!!!!"', + '(const wchar_t *) mazeltov = 0x','L"מזל טוב"', + '(std::__1::string) q = "hello world"', + '(std::__1::string) Q = "quite a long std::strin with lots of info inside it"', + '(std::__1::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"', + '(std::__1::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"']) + + self.runCmd("n") + + TheVeryLongOne = self.frame().FindVariable("TheVeryLongOne"); + summaryOptions = lldb.SBTypeSummaryOptions() + summaryOptions.SetCapping(lldb.eTypeSummaryUncapped) + uncappedSummaryStream = lldb.SBStream() + TheVeryLongOne.GetSummary(uncappedSummaryStream,summaryOptions) + uncappedSummary = uncappedSummaryStream.GetData() + self.assertTrue(uncappedSummary.find("someText") > 0, "uncappedSummary does not include the full string") + summaryOptions.SetCapping(lldb.eTypeSummaryCapped) + cappedSummaryStream = lldb.SBStream() + TheVeryLongOne.GetSummary(cappedSummaryStream,summaryOptions) + cappedSummary = cappedSummaryStream.GetData() + self.assertTrue(cappedSummary.find("someText") <= 0, "cappedSummary includes the full string") + + self.expect("frame variable", + substrs = ['(std::__1::wstring) s = L"hello world! מזל טוב!"', + '(std::__1::wstring) S = L"!!!!!"', + '(const wchar_t *) mazeltov = 0x','L"מזל טוב"', + '(std::__1::string) q = "hello world"', + '(std::__1::string) Q = "quite a long std::strin with lots of info inside it"', + '(std::__1::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"', + '(std::__1::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp new file mode 100644 index 000000000000..9ca0da39cfc8 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp @@ -0,0 +1,15 @@ +#include + +int main() +{ + std::wstring s(L"hello world! מזל טוב!"); + std::wstring S(L"!!!!"); + const wchar_t *mazeltov = L"מזל טוב"; + std::string q("hello world"); + std::string Q("quite a long std::strin with lots of info inside it"); + std::string TheVeryLongOne("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890someText1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + std::string IHaveEmbeddedZeros("a\0b\0c\0d",7); + std::wstring IHaveEmbeddedZerosToo(L"hello world!\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監", 38); + S.assign(L"!!!!!"); // Set break point at this line. + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/TestDataFormatterUnordered.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/TestDataFormatterUnordered.py new file mode 100644 index 000000000000..5e6ec2519323 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/TestDataFormatterUnordered.py @@ -0,0 +1,75 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxUnorderedDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows # libc++ not ported to Windows yet + @skipIfGcc + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.") + + self.runCmd("run", RUN_SUCCEEDED) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list', substrs = self.getLibcPlusPlusLibs()) + + self.look_for_content_and_continue( + "map", ['size=5 {', 'hello', 'world', 'this', 'is', 'me']) + + self.look_for_content_and_continue( + "mmap", ['size=6 {', 'first = 3', 'second = "this"', + 'first = 2', 'second = "hello"']) + + self.look_for_content_and_continue( + "iset", ['size=5 {', '\[\d\] = 5', '\[\d\] = 3', '\[\d\] = 2']) + + self.look_for_content_and_continue( + "sset", ['size=5 {', '\[\d\] = "is"', '\[\d\] = "world"', + '\[\d\] = "hello"']) + + self.look_for_content_and_continue( + "imset", ['size=6 {', '(\[\d\] = 3(\\n|.)+){3}', + '\[\d\] = 2', '\[\d\] = 1']) + + self.look_for_content_and_continue( + "smset", + ['size=5 {', '(\[\d\] = "is"(\\n|.)+){2}', + '(\[\d\] = "world"(\\n|.)+){2}']) + + def look_for_content_and_continue(self, var_name, patterns): + self.expect( ("frame variable %s" % var_name), patterns=patterns) + self.runCmd("continue") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/main.cpp new file mode 100644 index 000000000000..4e8a1a779c0c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/main.cpp @@ -0,0 +1,84 @@ +#include +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include +#include + +using std::string; + +#define intstr_map std::unordered_map +#define intstr_mmap std::unordered_multimap + +#define int_set std::unordered_set +#define str_set std::unordered_set +#define int_mset std::unordered_multiset +#define str_mset std::unordered_multiset + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intstr_map map; + map.emplace(1,"hello"); + map.emplace(2,"world"); + map.emplace(3,"this"); + map.emplace(4,"is"); + map.emplace(5,"me"); + thefoo_rw(); // Set break point at this line. + + intstr_mmap mmap; + mmap.emplace(1,"hello"); + mmap.emplace(2,"hello"); + mmap.emplace(2,"world"); + mmap.emplace(3,"this"); + mmap.emplace(3,"this"); + mmap.emplace(3,"this"); + thefoo_rw(); // Set break point at this line. + + int_set iset; + iset.emplace(1); + iset.emplace(2); + iset.emplace(3); + iset.emplace(4); + iset.emplace(5); + thefoo_rw(); // Set break point at this line. + + str_set sset; + sset.emplace("hello"); + sset.emplace("world"); + sset.emplace("this"); + sset.emplace("is"); + sset.emplace("me"); + thefoo_rw(); // Set break point at this line. + + int_mset imset; + imset.emplace(1); + imset.emplace(2); + imset.emplace(2); + imset.emplace(3); + imset.emplace(3); + imset.emplace(3); + thefoo_rw(); // Set break point at this line. + + str_mset smset; + smset.emplace("hello"); + smset.emplace("world"); + smset.emplace("world"); + smset.emplace("is"); + smset.emplace("is"); + thefoo_rw(); // Set break point at this line. + + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/Makefile new file mode 100644 index 000000000000..637fa7e80bfd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/TestDataFormatterLibcxxVBool.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/TestDataFormatterLibcxxVBool.py new file mode 100644 index 000000000000..9771a819aa6b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/TestDataFormatterLibcxxVBool.py @@ -0,0 +1,58 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxVBoolDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows. + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect("frame variable vBool", + substrs = ['size=49','[0] = false','[1] = true','[18] = false','[27] = true','[36] = false','[47] = true','[48] = true']) + + self.expect("expr vBool", + substrs = ['size=49','[0] = false','[1] = true','[18] = false','[27] = true','[36] = false','[47] = true','[48] = true']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/main.cpp new file mode 100644 index 000000000000..7b9956ed36ea --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vbool/main.cpp @@ -0,0 +1,69 @@ +#include +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY + +#include + +int main() +{ + std::vector vBool; + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(true); + + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/Makefile new file mode 100644 index 000000000000..1f609a41d908 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -O0 diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/TestDataFormatterLibcxxVector.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/TestDataFormatterLibcxxVector.py new file mode 100644 index 000000000000..f8cd65be0931 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/TestDataFormatterLibcxxVector.py @@ -0,0 +1,180 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LibcxxVectorDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfGcc + @skipIfWindows # libc++ not ported to Windows yet + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.skip_if_library_missing(self, self.target(), lldbutil.PrintableRegex("libc\+\+")) + + bkpt = self.target().FindBreakpointByID(lldbutil.run_break_set_by_source_regexp (self, "break here")) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # empty vectors (and storage pointers SHOULD BOTH BE NULL..) + self.expect("frame variable numbers", + substrs = ['numbers = size=0']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + # first value added + self.expect("frame variable numbers", + substrs = ['numbers = size=1', + '[0] = 1', + '}']) + + # add some more data + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable numbers", + substrs = ['numbers = size=4', + '[0] = 1', + '[1] = 12', + '[2] = 123', + '[3] = 1234', + '}']) + + self.expect("p numbers", + substrs = ['$', 'size=4', + '[0] = 1', + '[1] = 12', + '[2] = 123', + '[3] = 1234', + '}']) + + + # check access to synthetic children + self.runCmd("type summary add --summary-string \"item 0 is ${var[0]}\" std::int_vect int_vect") + self.expect('frame variable numbers', + substrs = ['item 0 is 1']); + + self.runCmd("type summary add --summary-string \"item 0 is ${svar[0]}\" std::int_vect int_vect") + self.expect('frame variable numbers', + substrs = ['item 0 is 1']); + # move on with synths + self.runCmd("type summary delete std::int_vect") + self.runCmd("type summary delete int_vect") + + # add some more data + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable numbers", + substrs = ['numbers = size=7', + '[0] = 1', + '[1] = 12', + '[2] = 123', + '[3] = 1234', + '[4] = 12345', + '[5] = 123456', + '[6] = 1234567', + '}']) + + self.expect("p numbers", + substrs = ['$', 'size=7', + '[0] = 1', + '[1] = 12', + '[2] = 123', + '[3] = 1234', + '[4] = 12345', + '[5] = 123456', + '[6] = 1234567', + '}']) + + # check access-by-index + self.expect("frame variable numbers[0]", + substrs = ['1']); + self.expect("frame variable numbers[1]", + substrs = ['12']); + self.expect("frame variable numbers[2]", + substrs = ['123']); + self.expect("frame variable numbers[3]", + substrs = ['1234']); + + # clear out the vector and see that we do the right thing once again + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable numbers", + substrs = ['numbers = size=0']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + # first value added + self.expect("frame variable numbers", + substrs = ['numbers = size=1', + '[0] = 7', + '}']) + + # check if we can display strings + self.expect("frame variable strings", + substrs = ['goofy', + 'is', + 'smart']) + + self.expect("p strings", + substrs = ['goofy', + 'is', + 'smart']) + + # test summaries based on synthetic children + self.runCmd("type summary add std::string_vect string_vect --summary-string \"vector has ${svar%#} items\" -e") + self.expect("frame variable strings", + substrs = ['vector has 3 items', + 'goofy', + 'is', + 'smart']) + + self.expect("p strings", + substrs = ['vector has 3 items', + 'goofy', + 'is', + 'smart']) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable strings", + substrs = ['vector has 4 items']) + + # check access-by-index + self.expect("frame variable strings[0]", + substrs = ['goofy']); + self.expect("frame variable strings[1]", + substrs = ['is']); + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + self.expect("frame variable strings", + substrs = ['vector has 0 items']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/main.cpp new file mode 100644 index 000000000000..a9aeacf90e43 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/main.cpp @@ -0,0 +1,35 @@ +#include +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include +typedef std::vector int_vect; +typedef std::vector string_vect; + +int main() +{ + int_vect numbers; + (numbers.push_back(1)); // break here + (numbers.push_back(12)); // break here + (numbers.push_back(123)); + (numbers.push_back(1234)); + (numbers.push_back(12345)); // break here + (numbers.push_back(123456)); + (numbers.push_back(1234567)); + + numbers.clear(); // break here + + (numbers.push_back(7)); // break here + + string_vect strings; + (strings.push_back(std::string("goofy"))); + (strings.push_back(std::string("is"))); + (strings.push_back(std::string("smart"))); + + (strings.push_back(std::string("!!!"))); // break here + + strings.clear(); // break here + + return 0; // break here +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/Makefile new file mode 100644 index 000000000000..7fe01d004f03 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +CFLAGS_EXTRAS += -O0 +USE_LIBSTDCPP := 1 + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/TestDataFormatterStdIterator.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/TestDataFormatterStdIterator.py new file mode 100644 index 000000000000..6742c9e71701 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/TestDataFormatterStdIterator.py @@ -0,0 +1,62 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StdIteratorDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @skipIfWindows # libstdcpp not ported to Windows + @expectedFailureIcc # llvm.org/pr15301 LLDB prints incorrect sizes of STL containers + def test_with_run_command(self): + """Test that libstdcpp iterators format properly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('frame variable ivI', substrs = ['item = 3']) + self.expect('expr ivI', substrs = ['item = 3']) + + self.expect('frame variable iimI', substrs = ['first = 0','second = 12']) + self.expect('expr iimI', substrs = ['first = 0','second = 12']) + + self.expect('frame variable simI', substrs = ['first = "world"','second = 42']) + self.expect('expr simI', substrs = ['first = "world"','second = 42']) + + self.expect('frame variable svI', substrs = ['item = "hello"']) + self.expect('expr svI', substrs = ['item = "hello"']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/main.cpp new file mode 100644 index 000000000000..d7b046c5bff8 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/main.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +typedef std::map intint_map; +typedef std::map strint_map; + +typedef std::vector int_vector; +typedef std::vector string_vector; + +typedef intint_map::iterator iimter; +typedef strint_map::iterator simter; + +typedef int_vector::iterator ivter; +typedef string_vector::iterator svter; + +int main() +{ + intint_map iim; + iim[0] = 12; + + strint_map sim; + sim["world"] = 42; + + int_vector iv; + iv.push_back(3); + + string_vector sv; + sv.push_back("hello"); + + iimter iimI = iim.begin(); + simter simI = sim.begin(); + + ivter ivI = iv.begin(); + svter svI = sv.begin(); + + return 0; // Set break point at this line. +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/Makefile new file mode 100644 index 000000000000..7fe01d004f03 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +CFLAGS_EXTRAS += -O0 +USE_LIBSTDCPP := 1 + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/TestDataFormatterStdList.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/TestDataFormatterStdList.py new file mode 100644 index 000000000000..5147d18da0fe --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/TestDataFormatterStdList.py @@ -0,0 +1,188 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StdListDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break at for the different tests. + self.line = line_number('main.cpp', '// Set break point at this line.') + self.optional_line = line_number('main.cpp', '// Optional break point at this line.') + self.final_line = line_number('main.cpp', '// Set final break point at this line.') + + @skipIfWindows # libstdcpp not ported to Windows + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("frame variable numbers_list --show-types") + + self.runCmd("type format add -f hex int") + + self.expect("frame variable numbers_list --raw", matching=False, + substrs = ['size=0', + '{}']) + self.expect("frame variable &numbers_list._M_impl._M_node --raw", matching=False, + substrs = ['size=0', + '{}']) + + self.expect("frame variable numbers_list", + substrs = ['size=0', + '{}']) + + self.expect("p numbers_list", + substrs = ['size=0', + '{}']) + + self.runCmd("n") + + self.expect("frame variable numbers_list", + substrs = ['size=1', + '[0] = ', + '0x12345678']) + + self.runCmd("n");self.runCmd("n");self.runCmd("n"); + + self.expect("frame variable numbers_list", + substrs = ['size=4', + '[0] = ', + '0x12345678', + '[1] =', + '0x11223344', + '[2] =', + '0xbeeffeed', + '[3] =', + '0x00abba00']) + + self.runCmd("n");self.runCmd("n"); + + self.expect("frame variable numbers_list", + substrs = ['size=6', + '[0] = ', + '0x12345678', + '0x11223344', + '0xbeeffeed', + '0x00abba00', + '[4] =', + '0x0abcdef0', + '[5] =', + '0x0cab0cab']) + + self.expect("p numbers_list", + substrs = ['size=6', + '[0] = ', + '0x12345678', + '0x11223344', + '0xbeeffeed', + '0x00abba00', + '[4] =', + '0x0abcdef0', + '[5] =', + '0x0cab0cab']) + + # check access-by-index + self.expect("frame variable numbers_list[0]", + substrs = ['0x12345678']); + self.expect("frame variable numbers_list[1]", + substrs = ['0x11223344']); + + # but check that expression does not rely on us + self.expect("expression numbers_list[0]", matching=False, error=True, + substrs = ['0x12345678']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("numbers_list").MightHaveChildren(), "numbers_list.MightHaveChildren() says False for non empty!") + + self.runCmd("n") + + self.expect("frame variable numbers_list", + substrs = ['size=0', + '{}']) + + self.runCmd("n");self.runCmd("n");self.runCmd("n");self.runCmd("n"); + + self.expect("frame variable numbers_list", + substrs = ['size=4', + '[0] = ', '1', + '[1] = ', '2', + '[2] = ', '3', + '[3] = ', '4']) + + self.runCmd("type format delete int") + + self.runCmd("n") + + self.expect("frame variable text_list", + substrs = ['size=0', + '{}']) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.final_line, num_expected_locations=-1) + + self.runCmd("c", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("frame variable text_list", + substrs = ['size=4', + '[0]', 'goofy', + '[1]', 'is', + '[2]', 'smart', + '[3]', '!!!']) + + self.expect("p text_list", + substrs = ['size=4', + '\"goofy\"', + '\"is\"', + '\"smart\"', + '\"!!!\"']) + + # check access-by-index + self.expect("frame variable text_list[0]", + substrs = ['goofy']); + self.expect("frame variable text_list[3]", + substrs = ['!!!']); + + # but check that expression does not rely on us + self.expect("expression text_list[0]", matching=False, error=True, + substrs = ['goofy']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("text_list").MightHaveChildren(), "text_list.MightHaveChildren() says False for non empty!") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/main.cpp new file mode 100644 index 000000000000..191acdcc97be --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/main.cpp @@ -0,0 +1,34 @@ +#include +#include + +typedef std::list int_list; +typedef std::list string_list; + +int main() +{ + int_list numbers_list; + + numbers_list.push_back(0x12345678); // Set break point at this line. + numbers_list.push_back(0x11223344); + numbers_list.push_back(0xBEEFFEED); + numbers_list.push_back(0x00ABBA00); + numbers_list.push_back(0x0ABCDEF0); + numbers_list.push_back(0x0CAB0CAB); + + numbers_list.clear(); + + numbers_list.push_back(1); + numbers_list.push_back(2); + numbers_list.push_back(3); + numbers_list.push_back(4); + + string_list text_list; + text_list.push_back(std::string("goofy")); // Optional break point at this line. + text_list.push_back(std::string("is")); + text_list.push_back(std::string("smart")); + + text_list.push_back(std::string("!!!")); + + return 0; // Set final break point at this line. +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/Makefile new file mode 100644 index 000000000000..2c6c3cf72842 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/Makefile @@ -0,0 +1,14 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBSTDCPP := 1 + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/TestDataFormatterStdMap.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/TestDataFormatterStdMap.py new file mode 100644 index 000000000000..e21c4e94c2c4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/TestDataFormatterStdMap.py @@ -0,0 +1,322 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StdMapDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @expectedFailureIcc # llvm.org/pr15301: LLDB prints incorrect size of libstdc++ containers + @skipIfWindows # libstdcpp not ported to Windows + @skipIfFreeBSD + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("frame variable ii --show-types") + + self.runCmd("type summary add -x \"std::map<\" --summary-string \"map has ${svar%#} items\" -e") + + self.expect('frame variable ii', + substrs = ['map has 0 items', + '{}']) + + self.runCmd("c"); + + self.expect('frame variable ii', + substrs = ['map has 2 items', + '[0] = ', + 'first = 0', + 'second = 0', + '[1] = ', + 'first = 1', + 'second = 1']) + + self.runCmd("c"); + + self.expect('frame variable ii', + substrs = ['map has 4 items', + '[2] = ', + 'first = 2', + 'second = 0', + '[3] = ', + 'first = 3', + 'second = 1']) + + self.runCmd("c"); + + self.expect("frame variable ii", + substrs = ['map has 9 items', + '[5] = ', + 'first = 5', + 'second = 0', + '[7] = ', + 'first = 7', + 'second = 1']) + + self.expect("p ii", + substrs = ['map has 9 items', + '[5] = ', + 'first = 5', + 'second = 0', + '[7] = ', + 'first = 7', + 'second = 1']) + + # check access-by-index + self.expect("frame variable ii[0]", + substrs = ['first = 0', + 'second = 0']); + self.expect("frame variable ii[3]", + substrs = ['first =', + 'second =']); + + self.expect("frame variable ii[8]", matching=True, + substrs = ['1234567']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("ii").MightHaveChildren(), "ii.MightHaveChildren() says False for non empty!") + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression ii[8]", matching=False, error=True, + # substrs = ['1234567']) + + self.runCmd("c") + + self.expect('frame variable ii', + substrs = ['map has 0 items', + '{}']) + + self.runCmd("frame variable si --show-types") + + self.expect('frame variable si', + substrs = ['map has 0 items', + '{}']) + + self.runCmd("c") + + self.expect('frame variable si', + substrs = ['map has 1 items', + '[0] = ', + 'first = \"zero\"', + 'second = 0']) + + self.runCmd("c"); + + self.expect("frame variable si", + substrs = ['map has 5 items', + '[0] = ', + 'first = \"zero\"', + 'second = 0', + '[1] = ', + 'first = \"one\"', + 'second = 1', + '[2] = ', + 'first = \"two\"', + 'second = 2', + '[3] = ', + 'first = \"three\"', + 'second = 3', + '[4] = ', + 'first = \"four\"', + 'second = 4']) + + self.expect("p si", + substrs = ['map has 5 items', + '[0] = ', + 'first = \"zero\"', + 'second = 0', + '[1] = ', + 'first = \"one\"', + 'second = 1', + '[2] = ', + 'first = \"two\"', + 'second = 2', + '[3] = ', + 'first = \"three\"', + 'second = 3', + '[4] = ', + 'first = \"four\"', + 'second = 4']) + + # check access-by-index + self.expect("frame variable si[0]", + substrs = ['first = ', 'four', + 'second = 4']); + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("si").MightHaveChildren(), "si.MightHaveChildren() says False for non empty!") + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression si[0]", matching=False, error=True, + # substrs = ['first = ', 'zero']) + + self.runCmd("c") + + self.expect('frame variable si', + substrs = ['map has 0 items', + '{}']) + + self.runCmd("frame variable is --show-types") + + self.expect('frame variable is', + substrs = ['map has 0 items', + '{}']) + + self.runCmd("c"); + + self.expect("frame variable is", + substrs = ['map has 4 items', + '[0] = ', + 'second = \"goofy\"', + 'first = 85', + '[1] = ', + 'second = \"is\"', + 'first = 1', + '[2] = ', + 'second = \"smart\"', + 'first = 2', + '[3] = ', + 'second = \"!!!\"', + 'first = 3']) + + self.expect("p is", + substrs = ['map has 4 items', + '[0] = ', + 'second = \"goofy\"', + 'first = 85', + '[1] = ', + 'second = \"is\"', + 'first = 1', + '[2] = ', + 'second = \"smart\"', + 'first = 2', + '[3] = ', + 'second = \"!!!\"', + 'first = 3']) + + # check access-by-index + self.expect("frame variable is[0]", + substrs = ['first = ', + 'second =']); + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("is").MightHaveChildren(), "is.MightHaveChildren() says False for non empty!") + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression is[0]", matching=False, error=True, + # substrs = ['first = ', 'goofy']) + + self.runCmd("c") + + self.expect('frame variable is', + substrs = ['map has 0 items', + '{}']) + + self.runCmd("frame variable ss --show-types") + + self.expect('frame variable ss', + substrs = ['map has 0 items', + '{}']) + + self.runCmd("c"); + + self.expect("frame variable ss", + substrs = ['map has 4 items', + '[0] = ', + 'second = \"hello\"', + 'first = \"ciao\"', + '[1] = ', + 'second = \"house\"', + 'first = \"casa\"', + '[2] = ', + 'second = \"cat\"', + 'first = \"gatto\"', + '[3] = ', + 'second = \"..is always a Mac!\"', + 'first = \"a Mac..\"']) + + self.expect("p ss", + substrs = ['map has 4 items', + '[0] = ', + 'second = \"hello\"', + 'first = \"ciao\"', + '[1] = ', + 'second = \"house\"', + 'first = \"casa\"', + '[2] = ', + 'second = \"cat\"', + 'first = \"gatto\"', + '[3] = ', + 'second = \"..is always a Mac!\"', + 'first = \"a Mac..\"']) + + # check access-by-index + self.expect("frame variable ss[3]", + substrs = ['gatto', 'cat']); + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("ss").MightHaveChildren(), "ss.MightHaveChildren() says False for non empty!") + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression ss[3]", matching=False, error=True, + # substrs = ['gatto']) + + self.runCmd("c") + + self.expect('frame variable ss', + substrs = ['map has 0 items', + '{}']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/main.cpp new file mode 100644 index 000000000000..568c35efe072 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/main.cpp @@ -0,0 +1,55 @@ +#include +#include + +#define intint_map std::map +#define strint_map std::map +#define intstr_map std::map +#define strstr_map std::map + + +int main() +{ + intint_map ii; + + ii[0] = 0; // Set break point at this line. + ii[1] = 1; + ii[2] = 0;// Set break point at this line. + ii[3] = 1; + ii[4] = 0;// Set break point at this line. + ii[5] = 1; + ii[6] = 0; + ii[7] = 1; + ii[85] = 1234567; + + ii.clear();// Set break point at this line. + + strint_map si; + + si["zero"] = 0;// Set break point at this line. + si["one"] = 1;// Set break point at this line. + si["two"] = 2; + si["three"] = 3; + si["four"] = 4; + + si.clear();// Set break point at this line. + + intstr_map is; + + is[85] = "goofy";// Set break point at this line. + is[1] = "is"; + is[2] = "smart"; + is[3] = "!!!"; + + is.clear();// Set break point at this line. + + strstr_map ss; + + ss["ciao"] = "hello";// Set break point at this line. + ss["casa"] = "house"; + ss["gatto"] = "cat"; + ss["a Mac.."] = "..is always a Mac!"; + + ss.clear();// Set break point at this line. + + return 0;// Set break point at this line. +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/Makefile new file mode 100644 index 000000000000..7fe01d004f03 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +CFLAGS_EXTRAS += -O0 +USE_LIBSTDCPP := 1 + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py new file mode 100644 index 000000000000..2d6af24c6afc --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py @@ -0,0 +1,66 @@ +#coding=utf8 +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StdStringDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @skipIfWindows # libstdcpp not ported to Windows + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + var_s = self.frame().FindVariable('s') + var_S = self.frame().FindVariable('S') + var_mazeltov = self.frame().FindVariable('mazeltov') + var_q = self.frame().FindVariable('q') + var_Q = self.frame().FindVariable('Q') + + self.assertTrue(var_s.GetSummary() == 'L"hello world! מזל טוב!"', "s summary wrong") + self.assertTrue(var_S.GetSummary() == 'L"!!!!"', "S summary wrong") + self.assertTrue(var_mazeltov.GetSummary() == 'L"מזל טוב"', "mazeltov summary wrong") + self.assertTrue(var_q.GetSummary() == '"hello world"', "q summary wrong") + self.assertTrue(var_Q.GetSummary() == '"quite a long std::strin with lots of info inside it"', "Q summary wrong") + + self.runCmd("next") + + self.assertTrue(var_S.GetSummary() == 'L"!!!!!"', "new S summary wrong") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/main.cpp new file mode 100644 index 000000000000..4a9b4fc7d0db --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/main.cpp @@ -0,0 +1,12 @@ +#include + +int main() +{ + std::wstring s(L"hello world! מזל טוב!"); + std::wstring S(L"!!!!"); + const wchar_t *mazeltov = L"מזל טוב"; + std::string q("hello world"); + std::string Q("quite a long std::strin with lots of info inside it"); + S.assign(L"!!!!!"); // Set break point at this line. + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/Makefile new file mode 100644 index 000000000000..2e8bcb9079bd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +CFLAGS_EXTRAS += -O0 +USE_LIBSTDCPP := 1 + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/TestDataFormatterStdVBool.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/TestDataFormatterStdVBool.py new file mode 100644 index 000000000000..9e73009aba83 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/TestDataFormatterStdVBool.py @@ -0,0 +1,58 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StdVBoolDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @expectedFailureFreeBSD("llvm.org/pr20548") # fails to build on lab.llvm.org buildbot + @expectedFailureIcc # llvm.org/pr15301: lldb does not print the correct sizes of STL containers when building with ICC + @skipIfWindows # libstdcpp not ported to Windows. + @skipIfDarwin + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect("frame variable vBool", + substrs = ['size=49','[0] = false','[1] = true','[18] = false','[27] = true','[36] = false','[47] = true','[48] = true']) + + self.expect("expr vBool", + substrs = ['size=49','[0] = false','[1] = true','[18] = false','[27] = true','[36] = false','[47] = true','[48] = true']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/main.cpp new file mode 100644 index 000000000000..73956dd3fda3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/main.cpp @@ -0,0 +1,63 @@ +#include + +int main() +{ + std::vector vBool; + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(true); + + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/Makefile new file mode 100644 index 000000000000..88cb026aba1c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +CXXFLAGS := -O0 +USE_LIBSTDCPP := 1 + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/TestDataFormatterStdVector.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/TestDataFormatterStdVector.py new file mode 100644 index 000000000000..ed4313657e93 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/TestDataFormatterStdVector.py @@ -0,0 +1,207 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StdVectorDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @skipIfFreeBSD + @expectedFailureIcc # llvm.org/pr15301 LLDB prints incorrect sizes of STL containers + @skipIfWindows # libstdcpp not ported to Windows + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # empty vectors (and storage pointers SHOULD BOTH BE NULL..) + self.expect("frame variable numbers", + substrs = ['numbers = size=0']) + + self.runCmd("c") + + # first value added + self.expect("frame variable numbers", + substrs = ['numbers = size=1', + '[0] = 1', + '}']) + + # add some more data + self.runCmd("c"); + + self.expect("frame variable numbers", + substrs = ['numbers = size=4', + '[0] = 1', + '[1] = 12', + '[2] = 123', + '[3] = 1234', + '}']) + + self.expect("p numbers", + substrs = ['$', 'size=4', + '[0] = 1', + '[1] = 12', + '[2] = 123', + '[3] = 1234', + '}']) + + + # check access to synthetic children + self.runCmd("type summary add --summary-string \"item 0 is ${var[0]}\" std::int_vect int_vect") + self.expect('frame variable numbers', + substrs = ['item 0 is 1']); + + self.runCmd("type summary add --summary-string \"item 0 is ${svar[0]}\" std::int_vect int_vect") + #import time + #time.sleep(19) + self.expect('frame variable numbers', + substrs = ['item 0 is 1']); + # move on with synths + self.runCmd("type summary delete std::int_vect") + self.runCmd("type summary delete int_vect") + + # add some more data + self.runCmd("c"); + + self.expect("frame variable numbers", + substrs = ['numbers = size=7', + '[0] = 1', + '[1] = 12', + '[2] = 123', + '[3] = 1234', + '[4] = 12345', + '[5] = 123456', + '[6] = 1234567', + '}']) + + self.expect("p numbers", + substrs = ['$', 'size=7', + '[0] = 1', + '[1] = 12', + '[2] = 123', + '[3] = 1234', + '[4] = 12345', + '[5] = 123456', + '[6] = 1234567', + '}']) + + # check access-by-index + self.expect("frame variable numbers[0]", + substrs = ['1']); + self.expect("frame variable numbers[1]", + substrs = ['12']); + self.expect("frame variable numbers[2]", + substrs = ['123']); + self.expect("frame variable numbers[3]", + substrs = ['1234']); + + # but check that expression does not rely on us + # (when expression gets to call into STL code correctly, we will have to find + # another way to check this) + self.expect("expression numbers[6]", matching=False, error=True, + substrs = ['1234567']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("numbers").MightHaveChildren(), "numbers.MightHaveChildren() says False for non empty!") + + # clear out the vector and see that we do the right thing once again + self.runCmd("c") + + self.expect("frame variable numbers", + substrs = ['numbers = size=0']) + + self.runCmd("c") + + # first value added + self.expect("frame variable numbers", + substrs = ['numbers = size=1', + '[0] = 7', + '}']) + + # check if we can display strings + self.runCmd("c") + + self.expect("frame variable strings", + substrs = ['goofy', + 'is', + 'smart']) + + self.expect("p strings", + substrs = ['goofy', + 'is', + 'smart']) + + # test summaries based on synthetic children + self.runCmd("type summary add std::string_vect string_vect --summary-string \"vector has ${svar%#} items\" -e") + self.expect("frame variable strings", + substrs = ['vector has 3 items', + 'goofy', + 'is', + 'smart']) + + self.expect("p strings", + substrs = ['vector has 3 items', + 'goofy', + 'is', + 'smart']) + + self.runCmd("c"); + + self.expect("frame variable strings", + substrs = ['vector has 4 items']) + + # check access-by-index + self.expect("frame variable strings[0]", + substrs = ['goofy']); + self.expect("frame variable strings[1]", + substrs = ['is']); + + # but check that expression does not rely on us + # (when expression gets to call into STL code correctly, we will have to find + # another way to check this) + self.expect("expression strings[0]", matching=False, error=True, + substrs = ['goofy']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("strings").MightHaveChildren(), "strings.MightHaveChildren() says False for non empty!") + + self.runCmd("c") + + self.expect("frame variable strings", + substrs = ['vector has 0 items']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/main.cpp new file mode 100644 index 000000000000..010917995e40 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/main.cpp @@ -0,0 +1,31 @@ +#include +#include +typedef std::vector int_vect; +typedef std::vector string_vect; + +int main() +{ + int_vect numbers; + numbers.push_back(1); // Set break point at this line. + numbers.push_back(12); // Set break point at this line. + numbers.push_back(123); + numbers.push_back(1234); + numbers.push_back(12345); // Set break point at this line. + numbers.push_back(123456); + numbers.push_back(1234567); + + numbers.clear(); // Set break point at this line. + + numbers.push_back(7); // Set break point at this line. + + string_vect strings; // Set break point at this line. + strings.push_back(std::string("goofy")); + strings.push_back(std::string("is")); + strings.push_back(std::string("smart")); + + strings.push_back(std::string("!!!")); // Set break point at this line. + + strings.clear(); // Set break point at this line. + + return 0;// Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/Makefile new file mode 100644 index 000000000000..04f39271f0cd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/Makefile @@ -0,0 +1,12 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py new file mode 100644 index 000000000000..8cebf20ddd2a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py @@ -0,0 +1,205 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SynthDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Pick some values and check that the basics work + self.runCmd("type filter add BagOfInts --child x --child z") + self.expect("frame variable int_bag", + substrs = ['x = 6', + 'z = 8']) + + # Check we can still access the missing child by summary + self.runCmd("type summary add BagOfInts --summary-string \"y=${var.y}\"") + self.expect('frame variable int_bag', + substrs = ['y=7']) + + # Even if we have synth children, the summary prevails + self.expect("frame variable int_bag", matching=False, + substrs = ['x = 6', + 'z = 8']) + + # if we skip synth and summary show y + self.expect("frame variable int_bag --synthetic-type false --no-summary-depth=1", + substrs = ['x = 6', + 'y = 7', + 'z = 8']) + + # if we ask for raw output same happens + self.expect("frame variable int_bag --raw-output", + substrs = ['x = 6', + 'y = 7', + 'z = 8']) + + # Summary+Synth must work together + self.runCmd("type summary add BagOfInts --summary-string \"x=${var.x}\" -e") + self.expect('frame variable int_bag', + substrs = ['x=6', + 'x = 6', + 'z = 8']) + + # Same output, but using Python + self.runCmd("type summary add BagOfInts --python-script \"return 'x=%s' % valobj.GetChildMemberWithName('x').GetValue()\" -e") + self.expect('frame variable int_bag', + substrs = ['x=6', + 'x = 6', + 'z = 8']) + + # If I skip summaries, still give me the artificial children + self.expect("frame variable int_bag --no-summary-depth=1", + substrs = ['x = 6', + 'z = 8']) + + # Delete synth and check that the view reflects it immediately + self.runCmd("type filter delete BagOfInts") + self.expect("frame variable int_bag", + substrs = ['x = 6', + 'y = 7', + 'z = 8']) + + # Add the synth again and check that it's honored deeper in the hierarchy + self.runCmd("type filter add BagOfInts --child x --child z") + self.expect('frame variable bag_bag', + substrs = ['x = x=69 {', + 'x = 69', + 'z = 71', + 'y = x=66 {', + 'x = 66', + 'z = 68']) + self.expect('frame variable bag_bag', matching=False, + substrs = ['y = 70', + 'y = 67']) + + # Check that a synth can expand nested stuff + self.runCmd("type filter add BagOfBags --child x.y --child y.z") + self.expect('frame variable bag_bag', + substrs = ['x.y = 70', + 'y.z = 68']) + + # ...even if we get -> and . wrong + self.runCmd("type filter add BagOfBags --child x.y --child \"y->z\"") + self.expect('frame variable bag_bag', + substrs = ['x.y = 70', + 'y->z = 68']) + + # ...even bitfields + self.runCmd("type filter add BagOfBags --child x.y --child \"y->z[1-2]\"") + self.expect('frame variable bag_bag --show-types', + substrs = ['x.y = 70', + '(int:2) y->z[1-2] = 2']) + + # ...even if we format the bitfields + self.runCmd("type filter add BagOfBags --child x.y --child \"y->y[0-0]\"") + self.runCmd("type format add \"int:1\" -f bool") + self.expect('frame variable bag_bag --show-types', + substrs = ['x.y = 70', + '(int:1) y->y[0-0] = true']) + + # ...even if we use one-liner summaries + self.runCmd("type summary add -c BagOfBags") + self.expect('frame variable bag_bag', + substrs = ['(BagOfBags) bag_bag = (x.y = 70, y->y[0-0] = true)']) + + self.runCmd("type summary delete BagOfBags") + + # now check we are dynamic (and arrays work) + self.runCmd("type filter add Plenty --child bitfield --child array[0] --child array[2]") + self.expect('frame variable plenty_of_stuff', + substrs = ['bitfield = 1', + 'array[0] = 5', + 'array[2] = 3']) + + self.runCmd("n") + self.expect('frame variable plenty_of_stuff', + substrs = ['bitfield = 17', + 'array[0] = 5', + 'array[2] = 3']) + + # skip synthetic children + self.expect('frame variable plenty_of_stuff --synthetic-type no', + substrs = ['some_values = 0x', + 'array = 0x', + 'array_size = 5']) + + + # check flat printing with synthetic children + self.expect('frame variable plenty_of_stuff --flat', + substrs = ['plenty_of_stuff.bitfield = 17', + '*(plenty_of_stuff.array) = 5', + '*(plenty_of_stuff.array) = 3']) + + # check that we do not lose location information for our children + self.expect('frame variable plenty_of_stuff --location', + substrs = ['0x', + ': bitfield = 17']) + + # check we work across pointer boundaries + self.expect('frame variable plenty_of_stuff.some_values --ptr-depth=1', + substrs = ['(BagOfInts *) plenty_of_stuff.some_values', + 'x = 5', + 'z = 7']) + + # but not if we don't want to + self.runCmd("type filter add BagOfInts --child x --child z -p") + self.expect('frame variable plenty_of_stuff.some_values --ptr-depth=1', + substrs = ['(BagOfInts *) plenty_of_stuff.some_values', + 'x = 5', + 'y = 6', + 'z = 7']) + + # check we're dynamic even if nested + self.runCmd("type filter add BagOfBags --child x.z") + self.expect('frame variable bag_bag', + substrs = ['x.z = 71']) + + self.runCmd("n") + self.expect('frame variable bag_bag', + substrs = ['x.z = 12']) + + self.runCmd('type summary add -e -s "I am always empty but have" EmptyStruct') + self.expect('frame variable es', substrs = ["I am always empty but have {}"]) + self.runCmd('type summary add -e -h -s "I am really empty" EmptyStruct') + self.expect('frame variable es', substrs = ["I am really empty"]) + self.expect('frame variable es', substrs = ["I am really empty {}"], matching=False) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/main.cpp new file mode 100644 index 000000000000..bac38d84fae3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synth/main.cpp @@ -0,0 +1,86 @@ +//===-- 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 +#include + +struct BagOfInts +{ + int x; + int y; + int z; + BagOfInts(int X) : + x(X), + y(X+1), + z(X+2) {} +}; + +struct BagOfFloats +{ + float x; + float y; + float z; + BagOfFloats(float X) : + x(X+0.334), + y(X+0.500), + z(X+0.667) {} +}; + +struct BagOfBags +{ + BagOfInts x; + BagOfInts y; + BagOfFloats z; + BagOfFloats q; + BagOfBags() : + x('E'), + y('B'), + z(1.1), + q(20.11) {} +}; + +struct EmptyStruct {}; + +struct Plenty +{ + BagOfInts *some_values; + int* array; + int array_size; + int bitfield; + + Plenty(int N, bool flagA, bool flagB) : + some_values(new BagOfInts(N)), + array(new int[N]), + array_size(N), + bitfield( (flagA ? 0x01 : 0x00) | (flagB ? 0x10 : 0x00) ) + { + for (int j = 0; j < N; j++) + array[j] = N-j; + } +}; + +int main (int argc, const char * argv[]) +{ + BagOfInts int_bag(6); + BagOfFloats float_bag(2.71); + + BagOfBags bag_bag; + EmptyStruct es; + + Plenty plenty_of_stuff(5,true,false); + + plenty_of_stuff.bitfield = 0x11; // Set break point at this line. + + bag_bag.x.z = 12; + + return 0; + +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/TestDataFormatterSynthVal.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/TestDataFormatterSynthVal.py new file mode 100644 index 000000000000..8d6e5df37a5c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/TestDataFormatterSynthVal.py @@ -0,0 +1,96 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class DataFormatterSynthValueTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', 'break here') + + @skipIfFreeBSD # llvm.org/pr20545 bogus output confuses buildbot parser + @expectedFailureWindows("llvm.org/pr24462") # Data formatters have problems on Windows + def test_with_run_command(self): + """Test using Python synthetic children provider to provide a value.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + x = self.frame().FindVariable("x") + x.SetPreferSyntheticValue(True) + y = self.frame().FindVariable("y") + y.SetPreferSyntheticValue(True) + z = self.frame().FindVariable("z") + z.SetPreferSyntheticValue(True) + + x_val = x.GetValueAsUnsigned + y_val = y.GetValueAsUnsigned + z_val = z.GetValueAsUnsigned + + if self.TraceOn(): + print("x_val = %s; y_val = %s; z_val = %s" % (x_val(),y_val(),z_val())) + + self.assertFalse(x_val() == 3, "x == 3 before synthetics") + self.assertFalse(y_val() == 4, "y == 4 before synthetics") + self.assertFalse(z_val() == 7, "z == 7 before synthetics") + + # now set up the synth + self.runCmd("script from myIntSynthProvider import *") + self.runCmd("type synth add -l myIntSynthProvider myInt") + self.runCmd("type synth add -l myArraySynthProvider myArray") + + if self.TraceOn(): + print("x_val = %s; y_val = %s; z_val = %s" % (x_val(),y_val(),z_val())) + + self.assertTrue(x_val() == 3, "x != 3 after synthetics") + self.assertTrue(y_val() == 4, "y != 4 after synthetics") + self.assertTrue(z_val() == 7, "z != 7 after synthetics") + + self.expect("frame variable x", substrs=['3']) + self.expect("frame variable x", substrs=['theValue = 3'], matching=False) + + # check that an aptly defined synthetic provider does not affect one-lining + self.expect("expression struct S { myInt theInt{12}; }; S()", substrs = ['(theInt = 12)']) + + # check that we can use a synthetic value in a summary + self.runCmd("type summary add hasAnInt -s ${var.theInt}") + hi = self.frame().FindVariable("hi") + self.assertEqual(hi.GetSummary(), "42") + + ma = self.frame().FindVariable("ma") + self.assertTrue(ma.IsValid()) + self.assertEqual(ma.GetNumChildren(15), 15) + self.assertEqual(ma.GetNumChildren(16), 16) + self.assertEqual(ma.GetNumChildren(17), 16) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/main.cpp new file mode 100644 index 000000000000..accbf0a5a578 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/main.cpp @@ -0,0 +1,29 @@ +class myInt { + private: int theValue; + public: myInt() : theValue(0) {} + public: myInt(int _x) : theValue(_x) {} + int val() { return theValue; } +}; + +class myArray { +public: + int array[16]; +}; + +class hasAnInt { + public: + myInt theInt; + hasAnInt() : theInt(42) {} +}; + +myInt operator + (myInt x, myInt y) { return myInt(x.val() + y.val()); } + +int main() { + myInt x{3}; + myInt y{4}; + myInt z {x+y}; + hasAnInt hi; + myArray ma; + + return z.val(); // break here +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/myIntSynthProvider.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/myIntSynthProvider.py new file mode 100644 index 000000000000..c8517a442932 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-synthval/myIntSynthProvider.py @@ -0,0 +1,36 @@ +class myIntSynthProvider(object): + def __init__(self, valobj, dict): + self.valobj = valobj; + self.val = self.valobj.GetChildMemberWithName("theValue") + def num_children(self): + return 0; + def get_child_at_index(self, index): + return None + def get_child_index(self, name): + return None + def update(self): + return False + def has_children(self): + return False + def get_value(self): + return self.val + + +class myArraySynthProvider(object): + def __init__(self, valobj, dict): + self.valobj = valobj + self.array = self.valobj.GetChildMemberWithName("array") + + def num_children(self, max_count): + if 16 < max_count: + return 16 + return max_count + + def get_child_at_index(self, index): + return None # Keep it simple when this is not tested here. + + def get_child_index(self, name): + return None # Keep it simple when this is not tested here. + + def has_children(self): + return True diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/Makefile new file mode 100644 index 000000000000..69dde1b76184 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/Makefile @@ -0,0 +1,12 @@ +LEVEL = ../../../make +CXX_SOURCES := main.cpp +CXXFLAGS += -std=c++11 + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/TestDumpDynamic.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/TestDumpDynamic.py new file mode 100644 index 000000000000..8ae09d691940 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/TestDumpDynamic.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import + +from lldbsuite.test import lldbinline + +lldbinline.MakeInlineTest(__file__, globals(), [lldbinline.expectedFailureWindows("llvm.org/pr24663")]) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/main.cpp new file mode 100644 index 000000000000..bc8e05829316 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/dump_dynamic/main.cpp @@ -0,0 +1,35 @@ +//===-- 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 Base { +public: + Base () = default; + virtual int func() { return 1; } + virtual ~Base() = default; +}; + +class Derived : public Base { +private: + int m_derived_data; +public: + Derived () : Base(), m_derived_data(0x0fedbeef) {} + virtual ~Derived() = default; + virtual int func() { return m_derived_data; } +}; + +int main (int argc, char const *argv[]) +{ + Base *base = new Derived(); + return 0; //% stream = lldb.SBStream() + //% base = self.frame().FindVariable("base") + //% base.SetPreferDynamicValue(lldb.eDynamicDontRunTarget) + //% base.GetDescription(stream) + //% if self.TraceOn(): print(stream.GetData()) + //% self.assertTrue(stream.GetData().startswith("(Derived *")) +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/TestFormatPropagation.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/TestFormatPropagation.py new file mode 100644 index 000000000000..5cb7c82c9e09 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/TestFormatPropagation.py @@ -0,0 +1,75 @@ +""" +Check if changing Format on an SBValue correctly propagates that new format to children as it should +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class FormatPropagationTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + # rdar://problem/14035604 + def test_with_run_command(self): + """Check for an issue where capping does not work because the Target pointer appears to be changing behind our backs.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + pass + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # extract the parent and the children + frame = self.frame() + parent = self.frame().FindVariable("f") + self.assertTrue(parent != None and parent.IsValid(),"could not find f") + X = parent.GetChildMemberWithName("X") + self.assertTrue(X != None and X.IsValid(),"could not find X") + Y = parent.GetChildMemberWithName("Y") + self.assertTrue(Y != None and Y.IsValid(),"could not find Y") + # check their values now + self.assertTrue(X.GetValue() == "1", "X has an invalid value") + self.assertTrue(Y.GetValue() == "2", "Y has an invalid value") + # set the format on the parent + parent.SetFormat(lldb.eFormatHex) + self.assertTrue(X.GetValue() == "0x00000001", "X has not changed format") + self.assertTrue(Y.GetValue() == "0x00000002", "Y has not changed format") + # Step and check if the values make sense still + self.runCmd("next") + self.assertTrue(X.GetValue() == "0x00000004", "X has not become 4") + self.assertTrue(Y.GetValue() == "0x00000002", "Y has not stuck as hex") + # Check that children can still make their own choices + Y.SetFormat(lldb.eFormatDecimal) + self.assertTrue(X.GetValue() == "0x00000004", "X is still hex") + self.assertTrue(Y.GetValue() == "2", "Y has not been reset") + # Make a few more changes + parent.SetFormat(lldb.eFormatDefault) + X.SetFormat(lldb.eFormatHex) + Y.SetFormat(lldb.eFormatDefault) + self.assertTrue(X.GetValue() == "0x00000004", "X is not hex as it asked") + self.assertTrue(Y.GetValue() == "2", "Y is not defaulted") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/main.cpp new file mode 100644 index 000000000000..5822fbc2a710 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/format-propagation/main.cpp @@ -0,0 +1,13 @@ +struct foo +{ + int X; + int Y; + foo(int a, int b) : X(a), Y(b) {} +}; + +int main() +{ + foo f(1,2); + f.X = 4; // Set break point at this line. + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/TestFrameFormatSmallStruct.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/TestFrameFormatSmallStruct.py new file mode 100644 index 000000000000..555351998c2d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/TestFrameFormatSmallStruct.py @@ -0,0 +1,38 @@ +""" +Test that the user can input a format but it will not prevail over summary format's choices. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class FrameFormatSmallStructTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that the user can input a format but it will not prevail over summary format's choices.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("thread list", substrs = ['addPair(p=(x = 3, y = -3))']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/main.cpp new file mode 100644 index 000000000000..120ef0ea0910 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/frameformat_smallstruct/main.cpp @@ -0,0 +1,25 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +struct Pair { + int x; + int y; + + Pair(int _x, int _y) : x(_x), y(_y) {} +}; + +int addPair(Pair p) +{ + return p.x + p.y; // Set break point at this line. +} + +int main() { + Pair p1(3,-3); + return addPair(p1); +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/TestDataFormatterHexCaps.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/TestDataFormatterHexCaps.py new file mode 100644 index 000000000000..3c28b68056f5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/TestDataFormatterHexCaps.py @@ -0,0 +1,82 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class DataFormatterHexCapsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format delete hex', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type format add -f uppercase int") + + self.expect('frame variable mine', + substrs = ['mine = ', + 'first = 0x001122AA', 'second = 0x1122BB44']) + + self.runCmd("type format add -f hex int") + + self.expect('frame variable mine', + substrs = ['mine = ', + 'first = 0x001122aa', 'second = 0x1122bb44']) + + self.runCmd("type format delete int") + + self.runCmd("type summary add -s \"${var.first%X} and ${var.second%x}\" foo") + + self.expect('frame variable mine', + substrs = ['(foo) mine = 0x001122AA and 0x1122bb44']) + + self.runCmd("type summary add -s \"${var.first%X} and ${var.second%X}\" foo") + self.runCmd("next") + self.runCmd("next") + self.expect('frame variable mine', + substrs = ['(foo) mine = 0xAABBCCDD and 0x1122BB44']) + + self.runCmd("type summary add -s \"${var.first%x} and ${var.second%X}\" foo") + self.expect('frame variable mine', + substrs = ['(foo) mine = 0xaabbccdd and 0x1122BB44']) + self.runCmd("next") + self.runCmd("next") + self.runCmd("type summary add -s \"${var.first%x} and ${var.second%x}\" foo") + self.expect('frame variable mine', + substrs = ['(foo) mine = 0xaabbccdd and 0xff00ff00']) + self.runCmd("type summary add -s \"${var.first%X} and ${var.second%X}\" foo") + self.expect('frame variable mine', + substrs = ['(foo) mine = 0xAABBCCDD and 0xFF00FF00']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/main.cpp new file mode 100644 index 000000000000..5ee113c17b28 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/hexcaps/main.cpp @@ -0,0 +1,28 @@ +//===-- 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 + +struct foo +{ + int first; + int second; +}; + +int main () +{ + struct foo mine = {0x001122AA, 0x1122BB44}; + printf("main.first = 0x%8.8x, main.second = 0x%8.8x\n", mine.first, mine.second); + mine.first = 0xAABBCCDD; // Set break point at this line. + printf("main.first = 0x%8.8x, main.second = 0x%8.8x\n", mine.first, mine.second); + mine.second = 0xFF00FF00; + printf("main.first = 0x%8.8x, main.second = 0x%8.8x\n", mine.first, mine.second); + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/TestDataFormatterLanguageCategoryUpdates.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/TestDataFormatterLanguageCategoryUpdates.py new file mode 100644 index 000000000000..b7562c4336a6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/TestDataFormatterLanguageCategoryUpdates.py @@ -0,0 +1,60 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LanguageCategoryUpdatesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// break here') + + @expectedFailureWindows("llvm.org/pr24462") # Data formatters have problems on Windows + def test_with_run_command(self): + """Test that LLDB correctly cleans caches when language categories change.""" + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + if hasattr(self, 'type_category') and hasattr(self, 'type_specifier'): + self.type_category.DeleteTypeSummary(self.type_specifier) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("frame variable", substrs = ['(S)', 'object', '123', '456'], matching=True) + + self.type_category = self.dbg.GetCategory(lldb.eLanguageTypeC_plus_plus) + type_summary = lldb.SBTypeSummary.CreateWithSummaryString("this is an object of type S") + self.type_specifier = lldb.SBTypeNameSpecifier('S') + self.type_category.AddTypeSummary(self.type_specifier, type_summary) + + self.expect("frame variable", substrs = ['this is an object of type S'], matching=True) + + self.type_category.DeleteTypeSummary(self.type_specifier) + self.expect("frame variable", substrs = ['this is an object of type S'], matching=False) + self.expect("frame variable", substrs = ['(S)', 'object', '123', '456'], matching=True) + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/main.cpp new file mode 100644 index 000000000000..ac77e537b80a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/language_category_updates/main.cpp @@ -0,0 +1,20 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +struct S { + int x; + int y; + + S() : x(123), y(456) {} +}; + +int main() { + S object; + return 0; // break here +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/Makefile new file mode 100644 index 000000000000..9f7fb1ca6231 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/TestNSArraySynthetic.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/TestNSArraySynthetic.py new file mode 100644 index 000000000000..a12f9c841a7f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/TestNSArraySynthetic.py @@ -0,0 +1,67 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class NSArraySyntheticTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.m', '// Set break point at this line.') + + @skipUnlessDarwin + def test_rdar11086338_with_run_command(self): + """Test that NSArray reports its synthetic children properly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Now check that we are displaying Cocoa classes correctly + self.expect('frame variable arr', + substrs = ['@"6 elements"']) + self.expect('frame variable other_arr', + substrs = ['@"4 elements"']) + self.expect('frame variable arr --ptr-depth 1', + substrs = ['@"6 elements"','[0] = 0x','[1] = 0x','[2] = 0x','[3] = 0x','[4] = 0x','[5] = 0x']) + self.expect('frame variable other_arr --ptr-depth 1', + substrs = ['@"4 elements"','[0] = 0x','[1] = 0x','[2] = 0x','[3] = 0x']) + self.expect('frame variable arr --ptr-depth 1 -d no-run-target', + substrs = ['@"6 elements"','@"hello"','@"world"','@"this"','@"is"','@"me"','@"http://www.apple.com']) + self.expect('frame variable other_arr --ptr-depth 1 -d no-run-target', + substrs = ['@"4 elements"','(int)5','@"a string"','@"6 elements"']) + self.expect('frame variable other_arr --ptr-depth 2 -d no-run-target', + substrs = ['@"4 elements"','@"6 elements" {','@"hello"','@"world"','@"this"','@"is"','@"me"','@"http://www.apple.com']) + + self.assertTrue(self.frame().FindVariable("arr").MightHaveChildren(), "arr says it does not have children!") + self.assertTrue(self.frame().FindVariable("other_arr").MightHaveChildren(), "arr says it does not have children!") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/main.m b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/main.m new file mode 100644 index 000000000000..1b4a6e03857a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsarraysynth/main.m @@ -0,0 +1,35 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + + NSMutableArray* arr = [[NSMutableArray alloc] init]; + [arr addObject:@"hello"]; + [arr addObject:@"world"]; + [arr addObject:@"this"]; + [arr addObject:@"is"]; + [arr addObject:@"me"]; + [arr addObject:[NSURL URLWithString:@"http://www.apple.com/"]]; + + NSDate *aDate = [NSDate distantFuture]; + NSValue *aValue = [NSNumber numberWithInt:5]; + NSString *aString = @"a string"; + + NSArray *other_arr = [NSArray arrayWithObjects:aDate, aValue, aString, arr, nil]; + + [pool drain];// Set break point at this line. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/Makefile new file mode 100644 index 000000000000..9f7fb1ca6231 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/TestNSDictionarySynthetic.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/TestNSDictionarySynthetic.py new file mode 100644 index 000000000000..13d493ecbdcc --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/TestNSDictionarySynthetic.py @@ -0,0 +1,69 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class NSDictionarySyntheticTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.m', '// Set break point at this line.') + + @skipUnlessDarwin + def test_rdar11988289_with_run_command(self): + """Test that NSDictionary reports its synthetic children properly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Now check that we are displaying Cocoa classes correctly + self.expect('frame variable dictionary', + substrs = ['3 key/value pairs']) + self.expect('frame variable mutabledict', + substrs = ['4 key/value pairs']) + self.expect('frame variable dictionary --ptr-depth 1', + substrs = ['3 key/value pairs','[0] = ','key = 0x','value = 0x','[1] = ','[2] = ']) + self.expect('frame variable mutabledict --ptr-depth 1', + substrs = ['4 key/value pairs','[0] = ','key = 0x','value = 0x','[1] = ','[2] = ','[3] = ']) + self.expect('frame variable dictionary --ptr-depth 1 --dynamic-type no-run-target', + substrs = ['3 key/value pairs','@"bar"','@"2 elements"','@"baz"','2 key/value pairs']) + self.expect('frame variable mutabledict --ptr-depth 1 --dynamic-type no-run-target', + substrs = ['4 key/value pairs','(int)23','@"123"','@"http://www.apple.com"','@"sourceofstuff"','3 key/value pairs']) + self.expect('frame variable mutabledict --ptr-depth 2 --dynamic-type no-run-target', + substrs = ['4 key/value pairs','(int)23','@"123"','@"http://www.apple.com"','@"sourceofstuff"','3 key/value pairs','@"bar"','@"2 elements"']) + self.expect('frame variable mutabledict --ptr-depth 3 --dynamic-type no-run-target', + substrs = ['4 key/value pairs','(int)23','@"123"','@"http://www.apple.com"','@"sourceofstuff"','3 key/value pairs','@"bar"','@"2 elements"','(int)1','@"two"']) + + self.assertTrue(self.frame().FindVariable("dictionary").MightHaveChildren(), "dictionary says it does not have children!") + self.assertTrue(self.frame().FindVariable("mutabledict").MightHaveChildren(), "mutable says it does not have children!") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/main.m b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/main.m new file mode 100644 index 000000000000..fdc533aeaf24 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nsdictionarysynth/main.m @@ -0,0 +1,30 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + + NSArray* keys = @[@"foo",@"bar",@"baz"]; + NSArray* values = @[@"hello",@[@"X",@"Y"],@{@1 : @"one",@2 : @"two"}]; + NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:values forKeys:keys]; + NSMutableDictionary* mutabledict = [NSMutableDictionary dictionaryWithCapacity:5]; + [mutabledict setObject:@"123" forKey:@23]; + [mutabledict setObject:[NSURL URLWithString:@"http://www.apple.com"] forKey:@"foobar"]; + [mutabledict setObject:@[@"a",@12] forKey:@57]; + [mutabledict setObject:dictionary forKey:@"sourceofstuff"]; + + [pool drain];// Set break point at this line. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/Makefile new file mode 100644 index 000000000000..9f7fb1ca6231 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/TestNSSetSynthetic.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/TestNSSetSynthetic.py new file mode 100644 index 000000000000..6c558001bb04 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/TestNSSetSynthetic.py @@ -0,0 +1,74 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class NSSetSyntheticTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.m', '// Set break point at this line.') + + @skipUnlessDarwin + def test_rdar12529957_with_run_command(self): + """Test that NSSet reports its synthetic children properly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Now check that we are displaying Cocoa classes correctly + self.expect('frame variable set', + substrs = ['4 elements']) + self.expect('frame variable mutable', + substrs = ['9 elements']) + self.expect('frame variable set --ptr-depth 1 -d run -T', + substrs = ['4 elements','[0]','[1]','[2]','[3]','hello','world','(int)1','(int)2']) + self.expect('frame variable mutable --ptr-depth 1 -d run -T', + substrs = ['9 elements','(int)5','@"3 elements"','@"www.apple.com"','(int)3','@"world"','(int)4']) + + self.runCmd("next") + self.expect('frame variable mutable', + substrs = ['0 elements']) + + self.runCmd("next") + self.expect('frame variable mutable', + substrs = ['4 elements']) + self.expect('frame variable mutable --ptr-depth 1 -d run -T', + substrs = ['4 elements','[0]','[1]','[2]','[3]','hello','world','(int)1','(int)2']) + + self.runCmd("next") + self.expect('frame variable mutable', + substrs = ['4 elements']) + self.expect('frame variable mutable --ptr-depth 1 -d run -T', + substrs = ['4 elements','[0]','[1]','[2]','[3]','hello','world','(int)1','(int)2']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/main.m b/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/main.m new file mode 100644 index 000000000000..7bc49583606f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/nssetsynth/main.m @@ -0,0 +1,34 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + NSSet* set = [NSSet setWithArray:@[@1,@"hello",@2,@"world"]]; + NSMutableSet* mutable = [NSMutableSet setWithCapacity:5]; + [mutable addObject:@1]; + [mutable addObject:@2]; + [mutable addObject:@3]; + [mutable addObject:@4]; + [mutable addObject:@5]; + [mutable addObject:[NSURL URLWithString:@"www.apple.com"]]; + [mutable addObject:@[@1,@2,@3]]; + [mutable unionSet:set]; + [mutable removeAllObjects]; // Set break point at this line. + [mutable unionSet:set]; + [mutable addObject:@1]; + + [pool drain]; + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/Makefile new file mode 100644 index 000000000000..261658b10ae8 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +OBJCXX_SOURCES := main.mm + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/TestFormattersOsType.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/TestFormattersOsType.py new file mode 100644 index 000000000000..4fb6176e9e4f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/TestFormattersOsType.py @@ -0,0 +1,52 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class DataFormatterOSTypeTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.mm', '// Set break point at this line.') + + @skipUnlessDarwin + def test_ostype_with_run_command(self): + """Test the formatters we use for OSType.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.mm", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Now check that we use the right summary for OSType + self.expect('frame variable', + substrs = ["'test'","'best'"]) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/main.mm b/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/main.mm new file mode 100644 index 000000000000..8d22659374a7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/ostypeformatting/main.mm @@ -0,0 +1,23 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + OSType a = 'test'; + OSType b = 'best'; + + [pool drain];// Set break point at this line. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/Makefile new file mode 100644 index 000000000000..d85e665333ea --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +CFLAGS_EXTRAS += -std=c++11 + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/TestPtrRef2Typedef.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/TestPtrRef2Typedef.py new file mode 100644 index 000000000000..bf98559cd0c1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/TestPtrRef2Typedef.py @@ -0,0 +1,57 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class PtrRef2TypedefTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set breakpoint here') + + def test_with_run_command(self): + """Test that a pointer/reference to a typedef is formatted as we want.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd('type summary add --cascade true -s "IntPointer" "int *"') + self.runCmd('type summary add --cascade true -s "IntLRef" "int &"') + self.runCmd('type summary add --cascade true -s "IntRRef" "int &&"') + + self.expect("frame variable x", substrs = ['(Foo *) x = 0x','IntPointer']) + # note: Ubuntu 12.04 x86_64 build with gcc 4.8.2 is getting a + # const after the ref that isn't showing up on FreeBSD. This + # tweak changes the behavior so that the const is not part of + # the match. + self.expect("frame variable y", substrs = ['(Foo &', ') y = 0x','IntLRef']) + self.expect("frame variable z", substrs = ['(Foo &&', ') z = 0x','IntRRef']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/main.cpp new file mode 100644 index 000000000000..219f398da3f5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/ptr_ref_typedef/main.cpp @@ -0,0 +1,19 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +typedef int Foo; + +int main() { + int lval = 1; + Foo* x = &lval; + Foo& y = lval; + Foo&& z = 1; + return 0; // Set breakpoint here +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/TestDataFormatterRefPtrRecursion.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/TestDataFormatterRefPtrRecursion.py new file mode 100644 index 000000000000..13b84e9f74c7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/TestDataFormatterRefPtrRecursion.py @@ -0,0 +1,41 @@ +""" +Test that ValueObjectPrinter does not cause an infinite loop when a reference to a struct that contains a pointer to itself is printed. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class DataFormatterRefPtrRecursionTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that ValueObjectPrinter does not cause an infinite loop when a reference to a struct that contains a pointer to itself is printed.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("frame variable foo", substrs = []); + self.expect("frame variable foo --ptr-depth=1", substrs = ['ID = 1']); + self.expect("frame variable foo --ptr-depth=2", substrs = ['ID = 1']); + self.expect("frame variable foo --ptr-depth=3", substrs = ['ID = 1']); diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/main.cpp new file mode 100644 index 000000000000..4b576bd266dd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/refpointer-recursion/main.cpp @@ -0,0 +1,21 @@ +int _ID = 0; + +class Foo { + public: + Foo *next; + int ID; + + Foo () : next(0), ID(++_ID) {} +}; + +int evalFoo(Foo& foo) +{ + return foo.ID; // Set break point at this line. +} + +int main() { + Foo f; + f.next = &f; + return evalFoo(f); +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/Makefile new file mode 100644 index 000000000000..62a57f6cd9be --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make +OBJC_SOURCES := main.m +include $(LEVEL)/Makefile.rules +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/TestSetValueFromCString.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/TestSetValueFromCString.py new file mode 100644 index 000000000000..80305e303d03 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/TestSetValueFromCString.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.skipIfFreeBSD,lldbtest.skipIfLinux,lldbtest.skipIfWindows]) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/main.m b/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/main.m new file mode 100644 index 000000000000..3dd455081007 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/setvaluefromcstring/main.m @@ -0,0 +1,19 @@ +//===-- main.m ---------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#import + +int main() { + NSDictionary* dic = @{@1 : @2}; + NSLog(@"hello world"); //% dic = self.frame().FindVariable("dic") + //% dic.SetPreferSyntheticValue(True) + //% dic.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) + //% dic.SetValueFromCString("12") + return 0; //% dic = self.frame().FindVariable("dic") + //% self.assertTrue(dic.GetValueAsUnsigned() == 0xC, "failed to read what I wrote") +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/Makefile new file mode 100644 index 000000000000..69dde1b76184 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/Makefile @@ -0,0 +1,12 @@ +LEVEL = ../../../make +CXX_SOURCES := main.cpp +CXXFLAGS += -std=c++11 + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/TestStringPrinter.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/TestStringPrinter.py new file mode 100644 index 000000000000..69f7d48c8b6c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/TestStringPrinter.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.expectedFailureWindows("llvm.org/pr24772")]) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/main.cpp new file mode 100644 index 000000000000..4a449e9716c3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/stringprinter/main.cpp @@ -0,0 +1,40 @@ +//===-- 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 + +int main (int argc, char const *argv[]) +{ + std::string stdstring("Hello\t\tWorld\nI am here\t\tto say hello\n"); //%self.addTearDownHook(lambda x: x.runCmd("setting set escape-non-printables true")) + const char* constcharstar = stdstring.c_str(); + std::string longstring( +"I am a very long string; in fact I am longer than any reasonable length that a string should be; quite long indeed; oh my, so many words; so many letters; this is kind of like writing a poem; except in real life all that is happening" +" is just me producing a very very long set of words; there is text here, text there, text everywhere; it fills me with glee to see so much text; all around me it's just letters, and symbols, and other pleasant drawings that cause me" +" a large amount of joy upon visually seeing them with my eyes; well, this is now a lot of letters, but it is still not enough for the purpose of the test I want to test, so maybe I should copy and paste this a few times, you know.." +" for science, or something" + "I am a very long string; in fact I am longer than any reasonable length that a string should be; quite long indeed; oh my, so many words; so many letters; this is kind of like writing a poem; except in real life all that is happening" + " is just me producing a very very long set of words; there is text here, text there, text everywhere; it fills me with glee to see so much text; all around me it's just letters, and symbols, and other pleasant drawings that cause me" + " a large amount of joy upon visually seeing them with my eyes; well, this is now a lot of letters, but it is still not enough for the purpose of the test I want to test, so maybe I should copy and paste this a few times, you know.." + " for science, or something" + "I am a very long string; in fact I am longer than any reasonable length that a string should be; quite long indeed; oh my, so many words; so many letters; this is kind of like writing a poem; except in real life all that is happening" + " is just me producing a very very long set of words; there is text here, text there, text everywhere; it fills me with glee to see so much text; all around me it's just letters, and symbols, and other pleasant drawings that cause me" + " a large amount of joy upon visually seeing them with my eyes; well, this is now a lot of letters, but it is still not enough for the purpose of the test I want to test, so maybe I should copy and paste this a few times, you know.." + " for science, or something" + ); + const char* longconstcharstar = longstring.c_str(); + return 0; //% if self.TraceOn(): self.runCmd('frame variable') + //% self.assertTrue(self.frame().FindVariable('stdstring').GetSummary() == '"Hello\\t\\tWorld\\nI am here\\t\\tto say hello\\n"') + //% self.assertTrue(self.frame().FindVariable('constcharstar').GetSummary() == '"Hello\\t\\tWorld\\nI am here\\t\\tto say hello\\n"') + //% self.runCmd("setting set escape-non-printables false") + //% self.assertTrue(self.frame().FindVariable('stdstring').GetSummary() == '"Hello\t\tWorld\nI am here\t\tto say hello\n"') + //% self.assertTrue(self.frame().FindVariable('constcharstar').GetSummary() == '"Hello\t\tWorld\nI am here\t\tto say hello\n"') + //% self.assertTrue(self.frame().FindVariable('longstring').GetSummary().endswith('"...')) + //% self.assertTrue(self.frame().FindVariable('longconstcharstar').GetSummary().endswith('"...')) +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/Makefile new file mode 100644 index 000000000000..04f39271f0cd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/Makefile @@ -0,0 +1,12 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/Test-rdar-9974002.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/Test-rdar-9974002.py new file mode 100644 index 000000000000..2873e35368fb --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/Test-rdar-9974002.py @@ -0,0 +1,133 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class Radar9974002DataFormatterTestCase(TestBase): + + # test for rdar://problem/9974002 () + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + if "clang" in self.getCompiler() and "3.4" in self.getCompilerVersion(): + self.skipTest("llvm.org/pr16214 -- clang emits partial DWARF for structures referenced via typedef") + + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer.first}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '0x000000']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer%S}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '0x000000']) + + self.runCmd("type summary add -s foo contained") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', 'foo']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', 'foo']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer%V}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '0x000000']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer.first}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '']) + + self.runCmd("type summary delete contained") + self.runCmd("n") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '0x000000']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer%S}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '0x000000']) + + self.runCmd("type summary add -s foo contained") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', 'foo']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', 'foo']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer%V}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '0x000000']) + + self.runCmd("type summary add -s \"${var.scalar} and ${var.pointer.first}\" container") + + self.expect('frame variable mine', + substrs = ['mine = ', + '1', '']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/main.cpp new file mode 100644 index 000000000000..03a9f278b7ec --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/summary-string-onfail/main.cpp @@ -0,0 +1,30 @@ +//===-- 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 + +struct contained +{ + int first; + int second; +}; + +struct container +{ + int scalar; + struct contained *pointer; +}; + +int main () +{ + struct container mine = {1, 0}; + printf ("Mine's scalar is the only thing that is good: %d.\n", mine.scalar); // Set break point at this line. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/TestSyntheticCapping.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/TestSyntheticCapping.py new file mode 100644 index 000000000000..0550f57ae62a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/TestSyntheticCapping.py @@ -0,0 +1,75 @@ +""" +Check for an issue where capping does not work because the Target pointer appears to be changing behind our backs +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SyntheticCappingTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Check for an issue where capping does not work because the Target pointer appears to be changing behind our backs.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # set up the synthetic children provider + self.runCmd("script from fooSynthProvider import *") + self.runCmd("type synth add -l fooSynthProvider foo") + + # check that the synthetic children work, so we know we are doing the right thing + self.expect("frame variable f00_1", + substrs = ['r = 33', + 'fake_a = 16777216', + 'a = 0']); + + # check that capping works + self.runCmd("settings set target.max-children-count 2", check=False) + + self.expect("frame variable f00_1", + substrs = ['...', + 'fake_a = 16777216', + 'a = 0']); + + self.expect("frame variable f00_1", matching=False, + substrs = ['r = 33']); + + + self.runCmd("settings set target.max-children-count 256", check=False) + + self.expect("frame variable f00_1", matching=True, + substrs = ['r = 33']); diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/fooSynthProvider.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/fooSynthProvider.py new file mode 100644 index 000000000000..fb95ac2b54d0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/fooSynthProvider.py @@ -0,0 +1,21 @@ +import lldb +class fooSynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj; + self.int_type = valobj.GetType().GetBasicType(lldb.eBasicTypeInt) + def num_children(self): + return 3; + def get_child_at_index(self, index): + if index == 0: + child = self.valobj.GetChildMemberWithName('a'); + if index == 1: + child = self.valobj.CreateChildAtOffset ('fake_a', 1, self.int_type); + if index == 2: + child = self.valobj.GetChildMemberWithName('r'); + return child; + def get_child_index(self, name): + if name == 'a': + return 0; + if name == 'fake_a': + return 1; + return 2; diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/main.cpp new file mode 100644 index 000000000000..b921915b91c5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthcapping/main.cpp @@ -0,0 +1,62 @@ +struct foo +{ + int a; + int b; + int c; + int d; + int e; + int f; + int g; + int h; + int i; + int j; + int k; + int l; + int m; + int n; + int o; + int p; + int q; + int r; + + foo(int X) : + a(X), + b(X+1), + c(X+3), + d(X+5), + e(X+7), + f(X+9), + g(X+11), + h(X+13), + i(X+15), + j(X+17), + k(X+19), + l(X+21), + m(X+23), + n(X+25), + o(X+27), + p(X+29), + q(X+31), + r(X+33) {} +}; + +struct wrapint +{ + int x; + wrapint(int X) : x(X) {} +}; + +int main() +{ + foo f00_1(0); + foo *f00_ptr = new foo(12); + + f00_1.a++; // Set break point at this line. + + wrapint test_cast('A' + + 256*'B' + + 256*256*'C'+ + 256*256*256*'D'); + + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/Makefile new file mode 100644 index 000000000000..a8e1853a1290 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/Makefile @@ -0,0 +1,12 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation + +clean:: + rm -rf $(wildcard *.o *.d *.dSYM *.log) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/TestSyntheticFilterRecompute.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/TestSyntheticFilterRecompute.py new file mode 100644 index 000000000000..6b70a88b9f2c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/TestSyntheticFilterRecompute.py @@ -0,0 +1,74 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import datetime +import lldbsuite.test.lldbutil as lldbutil + +class SyntheticFilterRecomputingTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.m', '// Set break point at this line.') + + @skipUnlessDarwin + def test_rdar12437442_with_run_command(self): + """Test that we update SBValues correctly as dynamic types change.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Now run the bulk of the test + id_x = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable("x") + id_x.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) + id_x.SetPreferSyntheticValue(True) + + if self.TraceOn(): + self.runCmd("expr --dynamic-type run-target --ptr-depth 1 -- x") + + self.assertTrue(id_x.GetSummary() == '@"5 elements"', "array does not get correct summary") + + self.runCmd("next") + self.runCmd("frame select 0") + + id_x = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable("x") + id_x.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) + id_x.SetPreferSyntheticValue(True) + + if self.TraceOn(): + self.runCmd("expr --dynamic-type run-target --ptr-depth 1 -- x") + + self.assertTrue(id_x.GetNumChildren() == 7, "dictionary does not have 7 children") + id_x.SetPreferSyntheticValue(False) + self.assertFalse(id_x.GetNumChildren() == 7, "dictionary still looks synthetic") + id_x.SetPreferSyntheticValue(True) + self.assertTrue(id_x.GetSummary() == "7 key/value pairs", "dictionary does not get correct summary") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/main.m b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/main.m new file mode 100644 index 000000000000..a7e94d29d469 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/synthupdate/main.m @@ -0,0 +1,25 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +int main (int argc, const char * argv[]) +{ + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + NSArray* foo = [NSArray arrayWithObjects:@1,@2,@3,@4,@5, nil]; + NSDictionary *bar = @{@1 : @"one",@2 : @"two", @3 : @"three", @4 : @"four", @5 : @"five", @6 : @"six", @7 : @"seven"}; + id x = foo; + x = bar; // Set break point at this line. + + [pool drain]; + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/Makefile new file mode 100644 index 000000000000..3e2b0187b954 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make +CXX_SOURCES := main.cpp +CXXFLAGS += -std=c++11 +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/TestTypedefArray.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/TestTypedefArray.py new file mode 100644 index 000000000000..30b66e062827 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/TestTypedefArray.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.expectedFailureGcc]) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/main.cpp new file mode 100644 index 000000000000..649c1e09a6a5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/typedef_array/main.cpp @@ -0,0 +1,14 @@ +//===-- main.cpp --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +typedef int Foo; + +int main() { + Foo array[3] = {1,2,3}; + return 0; //% self.expect("frame variable array --show-types --", substrs = ['(Foo [3]) array = {','(Foo) [0] = 1','(Foo) [1] = 2','(Foo) [2] = 3']) +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/TestUserFormatVsSummary.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/TestUserFormatVsSummary.py new file mode 100644 index 000000000000..dba2816d2647 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/TestUserFormatVsSummary.py @@ -0,0 +1,59 @@ +""" +Test that the user can input a format but it will not prevail over summary format's choices. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class UserFormatVSSummaryTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that the user can input a format but it will not prevail over summary format's choices.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("frame variable p1", substrs = ['(Pair) p1 = (x = 3, y = -3)']); + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd('type summary add Pair -s "x=${var.x%d},y=${var.y%u}"') + + self.expect("frame variable p1", substrs = ['(Pair) p1 = x=3,y=4294967293']); + self.expect("frame variable -f x p1", substrs = ['(Pair) p1 = x=0x00000003,y=0xfffffffd'], matching=False); + self.expect("frame variable -f d p1", substrs = ['(Pair) p1 = x=3,y=-3'], matching=False); + self.expect("frame variable p1", substrs = ['(Pair) p1 = x=3,y=4294967293']); + + self.runCmd('type summary add Pair -s "x=${var.x%x},y=${var.y%u}"') + + self.expect("frame variable p1", substrs = ['(Pair) p1 = x=0x00000003,y=4294967293']); + self.expect("frame variable -f d p1", substrs = ['(Pair) p1 = x=3,y=-3'],matching=False); diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/main.cpp new file mode 100644 index 000000000000..41c934aec0c3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/user-format-vs-summary/main.cpp @@ -0,0 +1,20 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +struct Pair { + int x; + int y; + + Pair(int _x, int _y) : x(_x), y(_y) {} +}; + +int main() { + Pair p1(3,-3); + return p1.x + p1.y; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/Makefile new file mode 100644 index 000000000000..18c35a7d84a5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/Makefile @@ -0,0 +1,11 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +# Workaround for llvm.org/pr16214: clang doesn't emit structure definition DWARF +# information without the flag below. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/TestVarInAggregateMisuse.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/TestVarInAggregateMisuse.py new file mode 100644 index 000000000000..5899469651d3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/TestVarInAggregateMisuse.py @@ -0,0 +1,74 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class VarInAggregateMisuseTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type summary add --summary-string \"SUMMARY SUCCESS ${var}\" Summarize") + + self.expect('frame variable mine_ptr', + substrs = ['SUMMARY SUCCESS summarize_ptr_t @ ']) + + self.expect('frame variable *mine_ptr', + substrs = ['SUMMARY SUCCESS summarize_t @']) + + self.runCmd("type summary add --summary-string \"SUMMARY SUCCESS ${var.first}\" Summarize") + + self.expect('frame variable mine_ptr', + substrs = ['SUMMARY SUCCESS 10']) + + self.expect('frame variable *mine_ptr', + substrs = ['SUMMARY SUCCESS 10']) + + self.runCmd("type summary add --summary-string \"${var}\" Summarize") + self.runCmd("type summary add --summary-string \"${var}\" -e TwoSummarizes") + + self.expect('frame variable', + substrs = ['(TwoSummarizes) twos = TwoSummarizes @ ', + 'first = summarize_t @ ', + 'second = summarize_t @ ']) + + self.runCmd("type summary add --summary-string \"SUMMARY SUCCESS ${var.first}\" Summarize") + self.expect('frame variable', + substrs = ['(TwoSummarizes) twos = TwoSummarizes @ ', + 'first = SUMMARY SUCCESS 1', + 'second = SUMMARY SUCCESS 3']) diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/main.cpp new file mode 100644 index 000000000000..72c7e138205d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/var-in-aggregate-misuse/main.cpp @@ -0,0 +1,41 @@ +//===-- 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 +struct Summarize +{ + int first; + int second; +}; + +typedef struct Summarize summarize_t; +typedef summarize_t *summarize_ptr_t; + +summarize_t global_mine = {30, 40}; + +struct TwoSummarizes +{ + summarize_t first; + summarize_t second; +}; + +int +main() +{ + summarize_t mine = {10, 20}; + summarize_ptr_t mine_ptr = &mine; + + TwoSummarizes twos = { {1,2}, {3,4} }; + + printf ("Summarize: first: %d second: %d and address: 0x%p\n", mine.first, mine.second, mine_ptr); // Set break point at this line. + printf ("Global summarize: first: %d second: %d.\n", global_mine.first, global_mine.second); + return 0; +} + + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/TestDataFormatterVarScriptFormatting.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/TestDataFormatterVarScriptFormatting.py new file mode 100644 index 000000000000..4d060c332f93 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/TestDataFormatterVarScriptFormatting.py @@ -0,0 +1,56 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import os.path + +class PythonSynthDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', ' // Set breakpoint here.') + + @skipIfFreeBSD # llvm.org/pr20545 bogus output confuses buildbot parser + def test_with_run_command(self): + """Test using Python synthetic children provider.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("command script import helperfunc.py") + self.runCmd('type summary add -x "^something<.*>$" -s "T is a ${script.var:helperfunc.f}"') + + self.expect("frame variable x", substrs = ['T is a non-pointer type']); + + self.expect("frame variable y", substrs = ['T is a pointer type']); diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/helperfunc.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/helperfunc.py new file mode 100644 index 000000000000..01562c5baa8d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/helperfunc.py @@ -0,0 +1,5 @@ +import lldb + +def f(value,d): + return "pointer type" if value.GetType().GetTemplateArgumentType(0).IsPointerType() else "non-pointer type" + diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/main.cpp new file mode 100644 index 000000000000..4dc94af3566c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/varscript_formatting/main.cpp @@ -0,0 +1,8 @@ +template +struct something {}; + +int main() { + something x; + something y; + return 0; // Set breakpoint here. +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/Makefile b/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py b/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py new file mode 100644 index 000000000000..05899a24ad22 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/TestVectorTypesFormatting.py @@ -0,0 +1,73 @@ +""" +Check that vector types format properly +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class VectorTypesFormattingTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// break here') + + # rdar://problem/14035604 + @skipIf(compiler='gcc') # gcc don't have ext_vector_type extension + def test_with_run_command(self): + """Check that vector types format properly""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + pass + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + pass # my code never fails + + v = self.frame().FindVariable("v") + v.SetPreferSyntheticValue(True) + v.SetFormat(lldb.eFormatVectorOfFloat32) + + if self.TraceOn(): print(v) + + self.assertTrue(v.GetNumChildren() == 4, "v as float32[] has 4 children") + self.assertTrue(v.GetChildAtIndex(0).GetData().float[0] == 1.25, "child 0 == 1.25") + self.assertTrue(v.GetChildAtIndex(1).GetData().float[0] == 1.25, "child 1 == 1.25") + self.assertTrue(v.GetChildAtIndex(2).GetData().float[0] == 2.50, "child 2 == 2.50") + self.assertTrue(v.GetChildAtIndex(3).GetData().float[0] == 2.50, "child 3 == 2.50") + + self.expect("expr -f int16_t[] -- v", substrs=['(0, 16288, 0, 16288, 0, 16416, 0, 16416)']) + self.expect("expr -f uint128_t[] -- v", substrs=['(85236745249553456609335044694184296448)']) + self.expect("expr -f float32[] -- v", substrs=['(1.25, 1.25, 2.5, 2.5)']) + + oldValue = v.GetChildAtIndex(0).GetValue() + v.SetFormat(lldb.eFormatHex) + newValue = v.GetChildAtIndex(0).GetValue() + self.assertFalse(oldValue == newValue, "values did not change along with format") + + v.SetFormat(lldb.eFormatVectorOfFloat32) + oldValueAgain = v.GetChildAtIndex(0).GetValue() + self.assertTrue(oldValue == oldValueAgain, "same format but different values") diff --git a/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/main.cpp b/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/main.cpp new file mode 100644 index 000000000000..b9d67ad20ab2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/data-formatter/vector-types/main.cpp @@ -0,0 +1,17 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +typedef float float4 __attribute__((ext_vector_type(4))); +typedef unsigned char vec __attribute__((ext_vector_type(16))); + +int main() { + float4 f4 = {1.25, 1.25, 2.50, 2.50}; + vec v = (vec)f4; + return 0; // break here +} diff --git a/packages/Python/lldbsuite/test/functionalities/dead-strip/Makefile b/packages/Python/lldbsuite/test/functionalities/dead-strip/Makefile new file mode 100644 index 000000000000..c60ecd414635 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/dead-strip/Makefile @@ -0,0 +1,18 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +ifeq "$(OS)" "" + OS = $(shell uname -s) +endif + +ifeq "$(OS)" "Darwin" + LDFLAGS = $(CFLAGS) -Xlinker -dead_strip +else + CFLAGS += -fdata-sections -ffunction-sections + LDFLAGS = $(CFLAGS) -Wl,--gc-sections +endif + +MAKE_DSYM := NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/dead-strip/TestDeadStrip.py b/packages/Python/lldbsuite/test/functionalities/dead-strip/TestDeadStrip.py new file mode 100644 index 000000000000..f10736bd75c0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/dead-strip/TestDeadStrip.py @@ -0,0 +1,58 @@ +""" +Test that breakpoint works correctly in the presence of dead-code stripping. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class DeadStripTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr24778") + @expectedFailureDwo("llvm.org/pr25087") + @skipIfFreeBSD # The -dead_strip linker option isn't supported on FreeBSD versions of ld. + def test(self): + """Test breakpoint works correctly with dead-code stripping.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break by function name f1 (live code). + lldbutil.run_break_set_by_symbol (self, "f1", num_expected_locations=1, module_name="a.out") + + # Break by function name f2 (dead code). + lldbutil.run_break_set_by_symbol (self, "f2", num_expected_locations=0, module_name="a.out") + + # Break by function name f3 (live code). + lldbutil.run_break_set_by_symbol (self, "f3", num_expected_locations=1, module_name="a.out") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint (breakpoint #1). + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'a.out`f1', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f 1", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint (breakpoint #3). + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'a.out`f3', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f 3", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) diff --git a/packages/Python/lldbsuite/test/functionalities/dead-strip/cmds.txt b/packages/Python/lldbsuite/test/functionalities/dead-strip/cmds.txt new file mode 100644 index 000000000000..f6fd04502888 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/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/packages/Python/lldbsuite/test/functionalities/dead-strip/main.c b/packages/Python/lldbsuite/test/functionalities/dead-strip/main.c new file mode 100644 index 000000000000..fe8e7c7de582 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/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 20 +int f1 (char *s) +{ + return printf("f1: %s\n", s); +} + + + + + +// We want f2 to start on line 30, this should get stripped +int f2 (char *s) +{ + return printf("f2: %s\n", s); +} + + + + + +// We want f3 to start on line 40 +int f3 (char *s) +{ + return printf("f3: %s\n", s); +} + + + + + +// We want main to start on line 50 +int main (int argc, const char * argv[]) +{ + f1("carp"); + f3("dong"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/disassembly/Makefile b/packages/Python/lldbsuite/test/functionalities/disassembly/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/disassembly/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/disassembly/TestDisassembleBreakpoint.py b/packages/Python/lldbsuite/test/functionalities/disassembly/TestDisassembleBreakpoint.py new file mode 100644 index 000000000000..df2308de7605 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/disassembly/TestDisassembleBreakpoint.py @@ -0,0 +1,54 @@ +""" +Test some lldb command abbreviations. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class DisassemblyTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows # Function name prints fully demangled instead of name-only + def test(self): + self.build() + exe = os.path.join (os.getcwd(), "a.out") + self.expect("file " + exe, + patterns = [ "Current executable set to .*a.out.*" ]) + + match_object = lldbutil.run_break_set_command (self, "br s -n sum") + lldbutil.check_breakpoint_result (self, match_object, symbol_name='sum', symbol_match_exact=False, num_locations=1) + + self.expect("run", + patterns = [ "Process .* launched: "]) + + self.runCmd("dis -f") + disassembly = self.res.GetOutput() + + # ARCH, if not specified, defaults to x86_64. + arch = self.getArchitecture() + if arch in ["", 'x86_64', 'i386', 'i686']: + breakpoint_opcodes = ["int3"] + instructions = [' mov', ' addl ', 'ret'] + elif arch in ["arm", "aarch64"]: + breakpoint_opcodes = ["brk", "udf"] + instructions = [' add ', ' ldr ', ' str '] + elif re.match("mips" , arch): + breakpoint_opcodes = ["break"] + instructions = ['lw', 'sw', 'jr'] + else: + # TODO please add your arch here + self.fail('unimplemented for arch = "{arch}"'.format(arch=self.getArchitecture())) + + # make sure that the software breakpoint has been removed + for op in breakpoint_opcodes: + self.assertFalse(op in disassembly) + + # make sure a few reasonable assembly instructions are here + self.expect(disassembly, exe=False, startstr = "a.out`sum", substrs = instructions) diff --git a/packages/Python/lldbsuite/test/functionalities/disassembly/main.cpp b/packages/Python/lldbsuite/test/functionalities/disassembly/main.cpp new file mode 100644 index 000000000000..8813647f6b72 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/disassembly/main.cpp @@ -0,0 +1,28 @@ +//===-- 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 +sum (int a, int b) +{ + int result = a + b; + return result; +} + +int +main(int argc, char const *argv[]) +{ + + int array[3]; + + array[0] = sum (1238, 78392); + array[1] = sum (379265, 23674); + array[2] = sum (872934, 234); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/Makefile b/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/Makefile new file mode 100644 index 000000000000..ceb406ee2eab --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := pass-to-base.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/TestDynamicValueChildCount.py b/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/TestDynamicValueChildCount.py new file mode 100644 index 000000000000..ac039990b5bf --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/TestDynamicValueChildCount.py @@ -0,0 +1,78 @@ +""" +Test that dynamic values update their child count correctly +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class DynamicValueChildCountTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + + self.main_third_call_line = line_number('pass-to-base.cpp', + '// Break here and check b has 0 children') + self.main_fourth_call_line = line_number('pass-to-base.cpp', + '// Break here and check b still has 0 children') + self.main_fifth_call_line = line_number('pass-to-base.cpp', + '// Break here and check b has one child now') + self.main_sixth_call_line = line_number('pass-to-base.cpp', + '// Break here and check b has 0 children again') + + @expectedFailureLinux("llvm.org/pr23039") + @expectedFailureFreeBSD("llvm.org/pr19311") # continue at a breakpoint does not work + @expectedFailureWindows("llvm.org/pr24663") + @expectedFailurei386("to be figured out") + @add_test_categories(['pyapi']) + def test_get_dynamic_vals(self): + """Test fetching C++ dynamic values from pointers & references.""" + """Get argument vals for the call stack when stopped on a breakpoint.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoints: + + third_call_bpt = target.BreakpointCreateByLocation('pass-to-base.cpp', self.main_third_call_line) + self.assertTrue(third_call_bpt, + VALID_BREAKPOINT) + fourth_call_bpt = target.BreakpointCreateByLocation('pass-to-base.cpp', self.main_fourth_call_line) + self.assertTrue(fourth_call_bpt, + VALID_BREAKPOINT) + fifth_call_bpt = target.BreakpointCreateByLocation('pass-to-base.cpp', self.main_fifth_call_line) + self.assertTrue(fifth_call_bpt, + VALID_BREAKPOINT) + sixth_call_bpt = target.BreakpointCreateByLocation('pass-to-base.cpp', self.main_sixth_call_line) + self.assertTrue(sixth_call_bpt, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + b = self.frame().FindVariable("b").GetDynamicValue(lldb.eDynamicCanRunTarget) + self.assertTrue(b.GetNumChildren() == 0, "b has 0 children") + self.runCmd("continue") + self.assertTrue(b.GetNumChildren() == 0, "b still has 0 children") + self.runCmd("continue") + self.assertTrue(b.GetNumChildren() != 0, "b now has 1 child") + self.runCmd("continue") + self.assertTrue(b.GetNumChildren() == 0, "b didn't go back to 0 children") diff --git a/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/pass-to-base.cpp b/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/pass-to-base.cpp new file mode 100644 index 000000000000..d9dd3529821e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/dynamic_value_child_count/pass-to-base.cpp @@ -0,0 +1,36 @@ +#include +#include + +class BaseClass +{ +public: + BaseClass(); + virtual ~BaseClass() { } +}; + +class DerivedClass : public BaseClass +{ +public: + DerivedClass(); + virtual ~DerivedClass() { } +protected: + int mem; +}; + +BaseClass::BaseClass() +{ +} + +DerivedClass::DerivedClass() : BaseClass() +{ + mem = 101; +} + +int +main (int argc, char **argv) +{ + BaseClass *b = nullptr; // Break here and check b has 0 children + b = new DerivedClass(); // Break here and check b still has 0 children + b = nullptr; // Break here and check b has one child now + return 0; // Break here and check b has 0 children again +} diff --git a/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/Makefile b/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/TestConvenienceVariables.py b/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/TestConvenienceVariables.py new file mode 100644 index 000000000000..eddaa3cc9840 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/TestConvenienceVariables.py @@ -0,0 +1,78 @@ +"""Test convenience variables when you drop in from lldb prompt into an embedded interpreter.""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class ConvenienceVariablesCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break on inside main.cpp. + self.line = line_number('main.c', 'Hello world.') + + @skipIfFreeBSD # llvm.org/pr17228 + @skipIfRemote + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_with_run_commands(self): + """Test convenience variables lldb.debugger, lldb.target, lldb.process, lldb.thread, and lldb.frame.""" + self.build() + import pexpect + exe = os.path.join(os.getcwd(), "a.out") + prompt = "(lldb) " + python_prompt = ">>> " + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s %s' % (lldbtest_config.lldbExec, self.lldbOption, exe)) + child = self.child + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + # Set the breakpoint, run the inferior, when it breaks, issue print on + # the various convenience variables. + child.expect_exact(prompt) + child.sendline('breakpoint set -f main.c -l %d' % self.line) + child.expect_exact(prompt) + child.sendline('run') + child.expect_exact("stop reason = breakpoint 1.1") + child.expect_exact(prompt) + child.sendline('script') + child.expect_exact(python_prompt) + + # Set a flag so that we know during teardown time, we need to exit the + # Python interpreter, then the lldb interpreter. + self.child_in_script_interpreter = True + + child.sendline('print(lldb.debugger)') + child.expect_exact(python_prompt) + self.expect(child.before, exe=False, + patterns = ['Debugger \(instance: .*, id: \d\)']) + + child.sendline('print(lldb.target)') + child.expect_exact(python_prompt) + self.expect(child.before, exe=False, + substrs = ['a.out']) + + child.sendline('print(lldb.process)') + child.expect_exact(python_prompt) + self.expect(child.before, exe=False, + patterns = ['SBProcess: pid = \d+, state = stopped, threads = \d, executable = a.out']) + + child.sendline('print(lldb.thread)') + child.expect_exact(python_prompt) + # Linux outputs decimal tid and 'name' instead of 'queue' + self.expect(child.before, exe=False, + patterns = ['thread #1: tid = (0x[0-9a-f]+|[0-9]+), 0x[0-9a-f]+ a\.out`main\(argc=1, argv=0x[0-9a-f]+\) \+ \d+ at main\.c:%d, (name|queue) = \'.+\', stop reason = breakpoint 1\.1' % self.line]) + + child.sendline('print(lldb.frame)') + child.expect_exact(python_prompt) + self.expect(child.before, exe=False, + patterns = ['frame #0: 0x[0-9a-f]+ a\.out`main\(argc=1, argv=0x[0-9a-f]+\) \+ \d+ at main\.c:%d' % self.line]) diff --git a/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/main.c b/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/main.c new file mode 100644 index 000000000000..277aa54a4eea --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/embedded_interpreter/main.c @@ -0,0 +1,6 @@ +#include + +int main(int argc, char const *argv[]) { + printf("Hello world.\n"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/exec/Makefile b/packages/Python/lldbsuite/test/functionalities/exec/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/exec/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/exec/TestExec.py b/packages/Python/lldbsuite/test/functionalities/exec/TestExec.py new file mode 100644 index 000000000000..9321a308a83c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/exec/TestExec.py @@ -0,0 +1,85 @@ +""" +Test some lldb command abbreviations. +""" +from __future__ import print_function + + + +import lldb +import os +import time +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import lldbsuite.support.seven as seven + +def execute_command (command): + #print('%% %s' % (command)) + (exit_status, output) = seven.get_command_status_output(command) + #if output: + # print(output) + #print('status = %u' % (exit_status)) + return exit_status + +class ExecTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test(self): + if self.getArchitecture() == 'x86_64': + source = os.path.join (os.getcwd(), "main.cpp") + o_file = os.path.join (os.getcwd(), "main.o") + execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s' -c -o '%s'" % (os.environ["CC"], source, o_file)) + execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s'" % (os.environ["CC"], o_file)) + if self.debug_info != "dsym": + dsym_path = os.path.join (os.getcwd(), "a.out.dSYM") + execute_command ("rm -rf '%s'" % (dsym_path)) + else: + self.build() + + exe = os.path.join (os.getcwd(), "a.out") + + # Create the target + target = self.dbg.CreateTarget(exe) + + # Create any breakpoints we need + breakpoint = target.BreakpointCreateBySourceRegex ('Set breakpoint 1 here', lldb.SBFileSpec ("main.cpp", False)) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + for i in range(6): + # The stop reason of the thread should be breakpoint. + self.assertTrue(process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at 'main' should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonBreakpoint, + "Thread in process stopped in 'main' should have a stop reason of eStopReasonBreakpoint"); + + # Run and we should stop due to exec + process.Continue() + + self.assertTrue(process.GetState() == lldb.eStateStopped, + "Process should be stopped at __dyld_start") + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at exec should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonExec, + "Thread in process stopped on exec should have a stop reason of eStopReasonExec"); + + # Run and we should stop at breakpoint in main after exec + process.Continue() diff --git a/packages/Python/lldbsuite/test/functionalities/exec/main.cpp b/packages/Python/lldbsuite/test/functionalities/exec/main.cpp new file mode 100644 index 000000000000..700c5dd94b28 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/exec/main.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static void +exit_with_errno (int err, const char *prefix) +{ + if (err) + { + fprintf (stderr, + "%s%s", + prefix ? prefix : "", + strerror(err)); + exit (err); + } +} + +static pid_t +spawn_process (const char **argv, + const char **envp, + cpu_type_t cpu_type, + int &err) +{ + pid_t pid = 0; + + const posix_spawn_file_actions_t *file_actions = NULL; + posix_spawnattr_t attr; + err = posix_spawnattr_init (&attr); + if (err) + return pid; + + short flags = POSIX_SPAWN_SETEXEC | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + err = posix_spawnattr_setflags (&attr, flags); + if (err == 0) + { + // Use the default signal masks + sigset_t no_signals; + sigset_t all_signals; + sigemptyset (&no_signals); + sigfillset (&all_signals); + posix_spawnattr_setsigmask(&attr, &no_signals); + posix_spawnattr_setsigdefault(&attr, &all_signals); + + if (cpu_type != 0) + { + size_t ocount = 0; + err = posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount); + } + + if (err == 0) + { + err = posix_spawn (&pid, + argv[0], + file_actions, + &attr, + (char * const *)argv, + (char * const *)envp); + } + + posix_spawnattr_destroy(&attr); + } + return pid; +} + +int +main (int argc, char const **argv) +{ + printf ("pid %i: Pointer size is %zu.\n", getpid(), sizeof(void *)); + int err = 0; // Set breakpoint 1 here +#if defined (__x86_64__) + if (sizeof(void *) == 8) + { + spawn_process (argv, NULL, CPU_TYPE_I386, err); + if (err) + exit_with_errno (err, "posix_spawn i386 error"); + } + else + { + spawn_process (argv, NULL, CPU_TYPE_X86_64, err); + if (err) + exit_with_errno (err, "posix_spawn x86_64 error"); + } +#else + spawn_process (argv, NULL, 0, err); + if (err) + exit_with_errno (err, "posix_spawn x86_64 error"); +#endif + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/.categories b/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/.categories new file mode 100644 index 000000000000..897e40a99dda --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/.categories @@ -0,0 +1 @@ +expression diff --git a/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/Makefile b/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/Makefile new file mode 100644 index 000000000000..a10791d58907 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +C_SOURCES := locking.c +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/TestExprDoesntBlock.py b/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/TestExprDoesntBlock.py new file mode 100644 index 000000000000..a54b45822a6a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/TestExprDoesntBlock.py @@ -0,0 +1,57 @@ +""" +Test that expr will time out and allow other threads to run if it blocks. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ExprDoesntDeadlockTestCase(TestBase): + + def getCategories(self): + return ['basic_process'] + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureFreeBSD('llvm.org/pr17946') + @expectedFlakeyLinux # failed 1/365 test runs, line 61, thread.IsValid() + @expectedFailureWindows # Windows doesn't have pthreads, need to port this test. + def test_with_run_command(self): + """Test that expr will time out and allow other threads to run if it blocks.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint at source line before call_me_to_get_lock gets called. + + main_file_spec = lldb.SBFileSpec ("locking.c") + breakpoint = target.BreakpointCreateBySourceRegex('Break here', main_file_spec) + if self.TraceOn(): + print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.line1 and the break condition should hold. + from lldbsuite.test.lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + + frame0 = thread.GetFrameAtIndex(0) + + var = frame0.EvaluateExpression ("call_me_to_get_lock()") + self.assertTrue (var.IsValid()) + self.assertTrue (var.GetValueAsSigned (0) == 567) diff --git a/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/locking.c b/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/locking.c new file mode 100644 index 000000000000..fae9979611d5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/expr-doesnt-deadlock/locking.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +pthread_mutex_t contended_mutex = PTHREAD_MUTEX_INITIALIZER; + +pthread_mutex_t control_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t control_condition; + +pthread_mutex_t thread_started_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t thread_started_condition; + +// This function runs in a thread. The locking dance is to make sure that +// by the time the main thread reaches the pthread_join below, this thread +// has for sure acquired the contended_mutex. So then the call_me_to_get_lock +// function will block trying to get the mutex, and only succeed once it +// signals this thread, then lets it run to wake up from the cond_wait and +// release the mutex. + +void * +lock_acquirer_1 (void *input) +{ + pthread_mutex_lock (&contended_mutex); + + // Grab this mutex, that will ensure that the main thread + // is in its cond_wait for it (since that's when it drops the mutex. + + pthread_mutex_lock (&thread_started_mutex); + pthread_mutex_unlock(&thread_started_mutex); + + // Now signal the main thread that it can continue, we have the contended lock + // so the call to call_me_to_get_lock won't make any progress till this + // thread gets a chance to run. + + pthread_mutex_lock (&control_mutex); + + pthread_cond_signal (&thread_started_condition); + + pthread_cond_wait (&control_condition, &control_mutex); + + pthread_mutex_unlock (&contended_mutex); + return NULL; +} + +int +call_me_to_get_lock () +{ + pthread_cond_signal (&control_condition); + pthread_mutex_lock (&contended_mutex); + return 567; +} + +int main () +{ + pthread_t thread_1; + + pthread_cond_init (&control_condition, NULL); + pthread_cond_init (&thread_started_condition, NULL); + + pthread_mutex_lock (&thread_started_mutex); + + pthread_create (&thread_1, NULL, lock_acquirer_1, NULL); + + pthread_cond_wait (&thread_started_condition, &thread_started_mutex); + + pthread_mutex_lock (&control_mutex); + pthread_mutex_unlock (&control_mutex); + + // Break here. At this point the other thread will have the contended_mutex, + // and be sitting in its cond_wait for the control condition. So there is + // no way that our by-hand calling of call_me_to_get_lock will proceed + // without running the first thread at least somewhat. + + call_me_to_get_lock(); + pthread_join (thread_1, NULL); + + return 0; + +} diff --git a/packages/Python/lldbsuite/test/functionalities/fat_archives/Makefile b/packages/Python/lldbsuite/test/functionalities/fat_archives/Makefile new file mode 100644 index 000000000000..e1832fdefbee --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/fat_archives/Makefile @@ -0,0 +1,14 @@ +all: clean + $(CC) -arch i386 -g -c a.c + ar -q liba-i386.a a.o + ranlib liba-i386.a + $(CC) -arch x86_64 -g -c a.c + ar -q liba-x86_64.a a.o + ranlib liba-x86_64.a + lipo -create -output liba.a liba-i386.a liba-x86_64.a + $(CC) -g -c main.c + $(CC) -o a.out main.o -L. -la + +clean: + rm -rf a.o a.out liba-i386.a liba-x86_64.a liba.a $(wildcard *un~ .*un~ main.o *.pyc) + diff --git a/packages/Python/lldbsuite/test/functionalities/fat_archives/TestFatArchives.py b/packages/Python/lldbsuite/test/functionalities/fat_archives/TestFatArchives.py new file mode 100644 index 000000000000..19a2dba2109a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/fat_archives/TestFatArchives.py @@ -0,0 +1,58 @@ +""" +Test some lldb command abbreviations. +""" +from __future__ import print_function + + + +import lldb +import os +import time +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import lldbsuite.support.seven as seven + +def execute_command (command): + # print('%% %s' % (command)) + (exit_status, output) = seven.get_command_status_output(command) + # if output: + # print(output) + # print('status = %u' % (exit_status)) + return exit_status + +class FatArchiveTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test (self): + if self.getArchitecture() == 'x86_64': + execute_command ("make CC='%s'" % (os.environ["CC"])) + self.main () + else: + self.skipTest("This test requires x86_64 as the architecture for the inferior") + + def main (self): + '''This test compiles a quick example by making a fat file (universal) full of + skinny .o files and makes sure we can use them to resolve breakpoints when doing + DWARF in .o file debugging. The only thing this test needs to do is to compile and + set a breakpoint in the target and verify any breakpoint locations have valid debug + info for the function, and source file and line.''' + exe = os.path.join (os.getcwd(), "a.out") + + # Create the target + target = self.dbg.CreateTarget(exe) + + # Create a breakpoint by name + breakpoint = target.BreakpointCreateByName ('foo', exe) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Make sure the breakpoint resolves to a function, file and line + for bp_loc in breakpoint: + # Get a section offset address (lldb.SBAddress) from the breakpoint location + bp_loc_addr = bp_loc.GetAddress() + line_entry = bp_loc_addr.GetLineEntry() + function = bp_loc_addr.GetFunction() + self.assertTrue(function.IsValid(), "Verify breakpoint in fat BSD archive has valid function debug info") + self.assertTrue(line_entry.GetFileSpec(), "Verify breakpoint in fat BSD archive has source file information") + self.assertTrue(line_entry.GetLine() != 0, "Verify breakpoint in fat BSD archive has source line information") diff --git a/packages/Python/lldbsuite/test/functionalities/fat_archives/a.c b/packages/Python/lldbsuite/test/functionalities/fat_archives/a.c new file mode 100644 index 000000000000..c100f9a2c075 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/fat_archives/a.c @@ -0,0 +1,4 @@ +int foo () +{ + return 5; +} diff --git a/packages/Python/lldbsuite/test/functionalities/fat_archives/a.h b/packages/Python/lldbsuite/test/functionalities/fat_archives/a.h new file mode 100644 index 000000000000..a4536647cfc9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/fat_archives/a.h @@ -0,0 +1 @@ +int foo (); diff --git a/packages/Python/lldbsuite/test/functionalities/fat_archives/main.c b/packages/Python/lldbsuite/test/functionalities/fat_archives/main.c new file mode 100644 index 000000000000..328319d4fb8f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/fat_archives/main.c @@ -0,0 +1,6 @@ +#include "a.h" +#include +int main() +{ + printf ("%d\n", foo()); +} diff --git a/packages/Python/lldbsuite/test/functionalities/format/Makefile b/packages/Python/lldbsuite/test/functionalities/format/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/format/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/format/TestFormats.py b/packages/Python/lldbsuite/test/functionalities/format/TestFormats.py new file mode 100644 index 000000000000..cc962150bc8c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/format/TestFormats.py @@ -0,0 +1,59 @@ +""" +Test the command history mechanism +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class TestFormats(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureHostWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_formats(self): + """Test format string functionality.""" + self.build() + import pexpect + prompt = "(lldb) " + child = pexpect.spawn('%s %s -x -o "b main" -o r a.out' % (lldbtest_config.lldbExec, self.lldbOption)) + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + # So that the spawned lldb session gets shutdown durng teardown. + self.child = child + + # Substitute 'Help!' for 'help' using the 'commands regex' mechanism. + child.expect_exact(prompt + 'target create "a.out"') + child.expect_exact(prompt + 'b main') + child.expect_exact(prompt + 'r') + child.expect_exact(prompt) + child.sendline() + # child.expect_exact(prompt + "target create") + # + # child.sendline("command regex 'Help__'") + # child.expect_exact(regex_prompt) + # child.sendline('s/^$/help/') + # child.expect_exact(regex_prompt1) + # child.sendline('') + # child.expect_exact(prompt) + # # Help! + # child.sendline('Help__') + # # If we see the familiar 'help' output, the test is done. + # child.expect('Debugger commands:') + # # Try and incorrectly remove "Help__" using "command unalias" and verify we fail + # child.sendline('command unalias Help__') + # child.expect_exact("error: 'Help__' is not an alias, it is a debugger command which can be removed using the 'command delete' command") + # child.expect_exact(prompt) + # + # # Delete the regex command using "command delete" + # child.sendline('command delete Help__') + # child.expect_exact(prompt) + # # Verify the command was removed + # child.sendline('Help__') + # child.expect_exact("error: 'Help__' is not a valid command") + # child.expect_exact(prompt) diff --git a/packages/Python/lldbsuite/test/functionalities/format/main.c b/packages/Python/lldbsuite/test/functionalities/format/main.c new file mode 100644 index 000000000000..1dfc8ef1f3aa --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/format/main.c @@ -0,0 +1,15 @@ +//===-- 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[]) +{ + printf("testing\n"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-assert/Makefile b/packages/Python/lldbsuite/test/functionalities/inferior-assert/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-assert/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py b/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py new file mode 100644 index 000000000000..69e977e6343f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py @@ -0,0 +1,253 @@ +"""Test that lldb functions correctly after the inferior has asserted.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +import lldbsuite.test.lldbplatformutil as lldbplatformutil +from lldbsuite.test.lldbtest import * + +class AssertingInferiorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows") + @expectedFailurei386("llvm.org/pr25338") + def test_inferior_asserting(self): + """Test that lldb reliably catches the inferior asserting (command).""" + self.build() + self.inferior_asserting() + + @expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows") + @expectedFailureAndroid(api_levels=list(range(16 + 1))) # b.android.com/179836 + def test_inferior_asserting_register(self): + """Test that lldb reliably reads registers from the inferior after asserting (command).""" + self.build() + self.inferior_asserting_registers() + + @expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows") + @expectedFailurei386("llvm.org/pr25338") + def test_inferior_asserting_disassemble(self): + """Test that lldb reliably disassembles frames after asserting (command).""" + self.build() + self.inferior_asserting_disassemble() + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows") + def test_inferior_asserting_python(self): + """Test that lldb reliably catches the inferior asserting (Python API).""" + self.build() + self.inferior_asserting_python() + + @expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows") + @expectedFailurei386("llvm.org/pr25338") + def test_inferior_asserting_expr(self): + """Test that the lldb expression interpreter can read from the inferior after asserting (command).""" + self.build() + self.inferior_asserting_expr() + + @expectedFailureWindows("llvm.org/pr21793: need to implement support for detecting assertion / abort on Windows") + @expectedFailurei386("llvm.org/pr25338") + def test_inferior_asserting_step(self): + """Test that lldb functions correctly after stepping through a call to assert().""" + self.build() + self.inferior_asserting_step() + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True) + + def check_stop_reason(self): + if matchAndroid(api_levels=list(range(1, 16+1)))(self): + # On android until API-16 the abort() call ended in a sigsegv instead of in a sigabrt + stop_reason = 'stop reason = signal SIGSEGV' + else: + stop_reason = 'stop reason = signal SIGABRT' + + # The stop reason of the thread should be an abort signal or exception. + self.expect("thread list", STOPPED_DUE_TO_ASSERT, + substrs = ['stopped', + stop_reason]) + + return stop_reason + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number of the call to assert. + self.line = line_number('main.c', '// Assert here.') + + def inferior_asserting(self): + """Inferior asserts upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + stop_reason = self.check_stop_reason() + + # And it should report a backtrace that includes the assert site. + self.expect("thread backtrace all", + substrs = [stop_reason, 'main', 'argc', 'argv']) + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = [stop_reason, + 'main.c:%d' % self.line]) + + def inferior_asserting_python(self): + """Inferior asserts upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now launch the process, and do not stop at entry point. + # Both argv and envp are null. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal) + if not thread: + self.fail("Fail to stop the thread upon assert") + + if self.TraceOn(): + lldbutil.print_stacktrace(thread) + + def inferior_asserting_registers(self): + """Test that lldb can read registers after asserting.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # lldb should be able to read from registers from the inferior after asserting. + lldbplatformutil.check_first_register_readable(self) + + def inferior_asserting_disassemble(self): + """Test that lldb can disassemble frames after asserting.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process, and do not stop at the entry point. + target.LaunchSimple (None, None, self.get_process_working_directory()) + self.check_stop_reason() + + process = target.GetProcess() + self.assertTrue(process.IsValid(), "current process is valid") + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid(), "current thread is valid") + + lastframeID = thread.GetFrameAtIndex(thread.GetNumFrames() - 1).GetFrameID() + + isi386Arch = False + if "i386" in self.getArchitecture(): + isi386Arch = True + + # lldb should be able to disassemble frames from the inferior after asserting. + for frame in thread: + self.assertTrue(frame.IsValid(), "current frame is valid") + + self.runCmd("frame select " + str(frame.GetFrameID()), RUN_SUCCEEDED) + + # Don't expect the function name to be in the disassembly as the assert + # function might be a no-return function where the PC is past the end + # of the function and in the next function. We also can't back the PC up + # because we don't know how much to back it up by on targets with opcodes + # that have differing sizes + pc_backup_offset = 1 + if frame.GetFrameID() == 0: + pc_backup_offset = 0 + if isi386Arch == True: + if lastframeID == frame.GetFrameID(): + pc_backup_offset = 0 + self.expect("disassemble -a %s" % (frame.GetPC() - pc_backup_offset), + substrs = ['<+0>: ']) + + def check_expr_in_main(self, thread): + depth = thread.GetNumFrames() + for i in range(depth): + frame = thread.GetFrameAtIndex(i) + self.assertTrue(frame.IsValid(), "current frame is valid") + if self.TraceOn(): + print("Checking if function %s is main" % frame.GetFunctionName()) + + if 'main' == frame.GetFunctionName(): + frame_id = frame.GetFrameID() + self.runCmd("frame select " + str(frame_id), RUN_SUCCEEDED) + self.expect("p argc", substrs = ['(int)', ' = 1']) + self.expect("p hello_world", substrs = ['Hello']) + self.expect("p argv[0]", substrs = ['a.out']) + self.expect("p null_ptr", substrs = ['= 0x0']) + return True + return False + + def inferior_asserting_expr(self): + """Test that the lldb expression interpreter can read symbols after asserting.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process, and do not stop at the entry point. + target.LaunchSimple (None, None, self.get_process_working_directory()) + self.check_stop_reason() + + process = target.GetProcess() + self.assertTrue(process.IsValid(), "current process is valid") + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid(), "current thread is valid") + + # The lldb expression interpreter should be able to read from addresses of the inferior after a call to assert(). + self.assertTrue(self.check_expr_in_main(thread), "cannot find 'main' in the backtrace") + + def inferior_asserting_step(self): + """Test that lldb functions correctly after stepping through a call to assert().""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process, and do not stop at the entry point. + self.set_breakpoint(self.line) + target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['main.c:%d' % self.line, + 'stop reason = breakpoint']) + + self.runCmd("next") + stop_reason = self.check_stop_reason() + + # lldb should be able to read from registers from the inferior after asserting. + if "x86_64" in self.getArchitecture(): + self.expect("register read rbp", substrs = ['rbp = 0x']) + if "i386" in self.getArchitecture(): + self.expect("register read ebp", substrs = ['ebp = 0x']) + + process = target.GetProcess() + self.assertTrue(process.IsValid(), "current process is valid") + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid(), "current thread is valid") + + # The lldb expression interpreter should be able to read from addresses of the inferior after a call to assert(). + self.assertTrue(self.check_expr_in_main(thread), "cannot find 'main' in the backtrace") + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = [stop_reason, + 'main.c:%d' % self.line]) diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-assert/main.c b/packages/Python/lldbsuite/test/functionalities/inferior-assert/main.c new file mode 100644 index 000000000000..6aec4c1023a9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-assert/main.c @@ -0,0 +1,19 @@ +//===-- 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 + +const char *hello_world = "Hello, assertion!"; + +int main(int argc, const char* argv[]) +{ + int *null_ptr = 0; + printf("%s\n", hello_world); + assert(null_ptr); // Assert here. +} diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-changed/Makefile b/packages/Python/lldbsuite/test/functionalities/inferior-changed/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-changed/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-changed/TestInferiorChanged.py b/packages/Python/lldbsuite/test/functionalities/inferior-changed/TestInferiorChanged.py new file mode 100644 index 000000000000..830c7f69355f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-changed/TestInferiorChanged.py @@ -0,0 +1,80 @@ +"""Test lldb reloads the inferior after it was changed during the session.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ChangedInferiorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfHostWindows + def test_inferior_crashing(self): + """Test lldb reloads the inferior after it was changed during the session.""" + self.build() + self.inferior_crashing() + self.cleanup() + # lldb needs to recognize the inferior has changed. If lldb needs to check the + # new module timestamp, make sure it is not the same as the old one, so add a + # 1 second delay. + time.sleep(1) + d = {'C_SOURCES': 'main2.c'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.inferior_not_crashing() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number of the crash. + self.line1 = line_number('main.c', '// Crash here.') + self.line2 = line_number('main2.c', '// Not crash here.') + + def inferior_crashing(self): + """Inferior crashes upon launching; lldb should catch the event and stop.""" + self.exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + self.exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + + # We should have one crashing thread + self.assertEqual( + len(lldbutil.get_crashed_threads(self, self.dbg.GetSelectedTarget().GetProcess())), + 1, + STOPPED_DUE_TO_EXC_BAD_ACCESS) + + # And it should report the correct line number. + self.expect("thread backtrace all", substrs = ['main.c:%d' % self.line1]) + + def inferior_not_crashing(self): + """Test lldb reloads the inferior after it was changed during the session.""" + self.runCmd("process kill") + self.runCmd("run", RUN_SUCCEEDED) + self.runCmd("process status") + + self.assertNotEqual( + len(lldbutil.get_crashed_threads(self, self.dbg.GetSelectedTarget().GetProcess())), + 1, + "Inferior changed, but lldb did not perform a reload") + + # Break inside the main. + lldbutil.run_break_set_by_file_and_line (self, "main2.c", self.line2, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.runCmd("frame variable int_ptr") + self.expect("frame variable *int_ptr", + substrs = ['= 7']) + self.expect("expression *int_ptr", + substrs = ['= 7']) diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-changed/main.c b/packages/Python/lldbsuite/test/functionalities/inferior-changed/main.c new file mode 100644 index 000000000000..9d0706a0862d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-changed/main.c @@ -0,0 +1,16 @@ +//===-- 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, const char* argv[]) +{ + int *null_ptr = 0; + printf("Hello, segfault!\n"); + printf("Now crash %d\n", *null_ptr); // Crash here. +} diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-changed/main2.c b/packages/Python/lldbsuite/test/functionalities/inferior-changed/main2.c new file mode 100644 index 000000000000..9173e8c30b59 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-changed/main2.c @@ -0,0 +1,18 @@ +//===-- main2.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 + +int main(int argc, const char* argv[]) +{ + int *int_ptr = (int *)malloc(sizeof(int)); + *int_ptr = 7; + printf("Hello, world!\n"); + printf("Now not crash %d\n", *int_ptr); // Not crash here. +} diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-crashing/Makefile b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-crashing/TestInferiorCrashing.py b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/TestInferiorCrashing.py new file mode 100644 index 000000000000..c547df116c16 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/TestInferiorCrashing.py @@ -0,0 +1,220 @@ +"""Test that lldb functions correctly after the inferior has crashed.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +import lldbsuite.test.lldbplatformutil as lldbplatformutil +from lldbsuite.test.lldbtest import * + +class CrashingInferiorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureFreeBSD("llvm.org/pr23699 SIGSEGV is reported as exception, not signal") + @expectedFailureWindows("llvm.org/pr24778") # This actually works, but the test relies on the output format instead of the API + def test_inferior_crashing(self): + """Test that lldb reliably catches the inferior crashing (command).""" + self.build() + self.inferior_crashing() + + @expectedFailureWindows("llvm.org/pr24778") + def test_inferior_crashing_register(self): + """Test that lldb reliably reads registers from the inferior after crashing (command).""" + self.build() + self.inferior_crashing_registers() + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24778") + def test_inferior_crashing_python(self): + """Test that lldb reliably catches the inferior crashing (Python API).""" + self.build() + self.inferior_crashing_python() + + @expectedFailureWindows("llvm.org/pr24778") + def test_inferior_crashing_expr(self): + """Test that the lldb expression interpreter can read from the inferior after crashing (command).""" + self.build() + self.inferior_crashing_expr() + + @expectedFailureWindows("llvm.org/pr24778") + def test_inferior_crashing_step(self): + """Test that stepping after a crash behaves correctly.""" + self.build() + self.inferior_crashing_step() + + @expectedFailureFreeBSD('llvm.org/pr24939') + @expectedFailureWindows("llvm.org/pr24778") + @expectedFailureAndroid(archs=['aarch64'], api_levels=list(range(21 + 1))) # No eh_frame for sa_restorer + def test_inferior_crashing_step_after_break(self): + """Test that lldb functions correctly after stepping through a crash.""" + self.build() + self.inferior_crashing_step_after_break() + + @expectedFailureWindows("llvm.org/pr24778") + @skipIfLinux # Inferior exits after stepping after a segfault. This is working as intended IMHO. + def test_inferior_crashing_expr_step_and_expr(self): + """Test that lldb expressions work before and after stepping after a crash.""" + self.build() + self.inferior_crashing_expr_step_expr() + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True) + + def check_stop_reason(self): + # We should have one crashing thread + self.assertEqual( + len(lldbutil.get_crashed_threads(self, self.dbg.GetSelectedTarget().GetProcess())), + 1, + STOPPED_DUE_TO_EXC_BAD_ACCESS) + + def get_api_stop_reason(self): + return lldb.eStopReasonException + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number of the crash. + self.line = line_number('main.c', '// Crash here.') + + def inferior_crashing(self): + """Inferior crashes upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + # The exact stop reason depends on the platform + if self.platformIsDarwin(): + stop_reason = 'stop reason = EXC_BAD_ACCESS' + elif self.getPlatform() == "linux": + stop_reason = 'stop reason = signal SIGSEGV' + else: + stop_reason = 'stop reason = invalid address' + self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS, + substrs = ['stopped', + stop_reason]) + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = [stop_reason, + 'main.c:%d' % self.line]) + + def inferior_crashing_python(self): + """Inferior crashes upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now launch the process, and do not stop at entry point. + # Both argv and envp are null. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + threads = lldbutil.get_crashed_threads(self, process) + self.assertEqual(len(threads), 1, "Failed to stop the thread upon bad access exception") + + if self.TraceOn(): + lldbutil.print_stacktrace(threads[0]) + + def inferior_crashing_registers(self): + """Test that lldb can read registers after crashing.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # lldb should be able to read from registers from the inferior after crashing. + lldbplatformutil.check_first_register_readable(self) + + def inferior_crashing_expr(self): + """Test that the lldb expression interpreter can read symbols after crashing.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p argc", + startstr = '(int) $0 = 1') + + self.expect("p hello_world", + substrs = ['Hello']) + + def inferior_crashing_step(self): + """Test that lldb functions correctly after stepping through a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.set_breakpoint(self.line) + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['main.c:%d' % self.line, + 'stop reason = breakpoint']) + + self.runCmd("next") + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p argv[0]", + substrs = ['a.out']) + self.expect("p null_ptr", + substrs = ['= 0x0']) + + # lldb should be able to read from registers from the inferior after crashing. + lldbplatformutil.check_first_register_readable(self) + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = ['main.c:%d' % self.line]) + + def inferior_crashing_step_after_break(self): + """Test that lldb behaves correctly when stepping after a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + expected_state = 'exited' # Provide the exit code. + if self.platformIsDarwin(): + expected_state = 'stopped' # TODO: Determine why 'next' and 'continue' have no effect after a crash. + elif re.match(".*-.*-.*-android", self.dbg.GetSelectedPlatform().GetTriple()): + expected_state = 'stopped' # android has a default SEGV handler, which will re-raise the signal, so we come up stopped again + + self.expect("next", + substrs = ['Process', expected_state]) + + if expected_state == 'exited': + self.expect("thread list", error=True,substrs = ['Process must be launched']) + else: + self.check_stop_reason() + + def inferior_crashing_expr_step_expr(self): + """Test that lldb expressions work before and after stepping after a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p argv[0]", + substrs = ['a.out']) + + self.runCmd("next") + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p argv[0]", + substrs = ['a.out']) diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-crashing/main.c b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/main.c new file mode 100644 index 000000000000..3b7cfe4012bd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/main.c @@ -0,0 +1,18 @@ +//===-- 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 + +const char *hello_world = "Hello, segfault!"; + +int main(int argc, const char* argv[]) +{ + int *null_ptr = 0; + printf("%s\n", hello_world); + printf("Now crash %d\n", *null_ptr); // Crash here. +} diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/Makefile b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/Makefile new file mode 100644 index 000000000000..0f8e92e9193c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +CFLAGS_EXTRAS += -fomit-frame-pointer + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferior.py b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferior.py new file mode 100644 index 000000000000..8afc81f6892a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferior.py @@ -0,0 +1,220 @@ +"""Test that lldb functions correctly after the inferior has crashed while in a recursive routine.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +import lldbsuite.test.lldbplatformutil as lldbplatformutil +from lldbsuite.test.lldbtest import * + +class CrashingRecursiveInferiorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureFreeBSD("llvm.org/pr23699 SIGSEGV is reported as exception, not signal") + @expectedFailureWindows("llvm.org/pr24778") + def test_recursive_inferior_crashing(self): + """Test that lldb reliably catches the inferior crashing (command).""" + self.build() + self.recursive_inferior_crashing() + + @expectedFailureWindows("llvm.org/pr24778") + def test_recursive_inferior_crashing_register(self): + """Test that lldb reliably reads registers from the inferior after crashing (command).""" + self.build() + self.recursive_inferior_crashing_registers() + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24778") + def test_recursive_inferior_crashing_python(self): + """Test that lldb reliably catches the inferior crashing (Python API).""" + self.build() + self.recursive_inferior_crashing_python() + + @expectedFailureWindows("llvm.org/pr24778") + def test_recursive_inferior_crashing_expr(self): + """Test that the lldb expression interpreter can read from the inferior after crashing (command).""" + self.build() + self.recursive_inferior_crashing_expr() + + @expectedFailureWindows("llvm.org/pr24778") + def test_recursive_inferior_crashing_step(self): + """Test that stepping after a crash behaves correctly.""" + self.build() + self.recursive_inferior_crashing_step() + + @expectedFailureFreeBSD('llvm.org/pr24939') + @expectedFailureWindows("llvm.org/pr24778") + @expectedFailureAndroid(archs=['aarch64'], api_levels=list(range(21 + 1))) # No eh_frame for sa_restorer + def test_recursive_inferior_crashing_step_after_break(self): + """Test that lldb functions correctly after stepping through a crash.""" + self.build() + self.recursive_inferior_crashing_step_after_break() + + @expectedFailureFreeBSD('llvm.org/pr15989') # Couldn't allocate space for the stack frame + @skipIfLinux # Inferior exits after stepping after a segfault. This is working as intended IMHO. + @expectedFailureWindows("llvm.org/pr24778") + def test_recursive_inferior_crashing_expr_step_and_expr(self): + """Test that lldb expressions work before and after stepping after a crash.""" + self.build() + self.recursive_inferior_crashing_expr_step_expr() + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True) + + def check_stop_reason(self): + # We should have one crashing thread + self.assertEqual( + len(lldbutil.get_crashed_threads(self, self.dbg.GetSelectedTarget().GetProcess())), + 1, + STOPPED_DUE_TO_EXC_BAD_ACCESS) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number of the crash. + self.line = line_number('main.c', '// Crash here.') + + def recursive_inferior_crashing(self): + """Inferior crashes upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + + # The exact stop reason depends on the platform + if self.platformIsDarwin(): + stop_reason = 'stop reason = EXC_BAD_ACCESS' + elif self.getPlatform() == "linux": + stop_reason = 'stop reason = signal SIGSEGV' + else: + stop_reason = 'stop reason = invalid address' + self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS, + substrs = ['stopped', + stop_reason]) + + # And it should report a backtrace that includes main and the crash site. + self.expect("thread backtrace all", + substrs = [stop_reason, 'main', 'argc', 'argv', 'recursive_function']) + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = [stop_reason, + 'main.c:%d' % self.line]) + + def recursive_inferior_crashing_python(self): + """Inferior crashes upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now launch the process, and do not stop at entry point. + # Both argv and envp are null. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + threads = lldbutil.get_crashed_threads(self, process) + self.assertEqual(len(threads), 1, "Failed to stop the thread upon bad access exception") + + if self.TraceOn(): + lldbutil.print_stacktrace(threads[0]) + + def recursive_inferior_crashing_registers(self): + """Test that lldb can read registers after crashing.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # lldb should be able to read from registers from the inferior after crashing. + lldbplatformutil.check_first_register_readable(self) + + def recursive_inferior_crashing_expr(self): + """Test that the lldb expression interpreter can read symbols after crashing.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p i", + startstr = '(int) $0 =') + + def recursive_inferior_crashing_step(self): + """Test that lldb functions correctly after stepping through a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.set_breakpoint(self.line) + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['main.c:%d' % self.line, + 'stop reason = breakpoint']) + + self.runCmd("next") + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p i", + substrs = ['(int) $0 =']) + + # lldb should be able to read from registers from the inferior after crashing. + lldbplatformutil.check_first_register_readable(self) + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = ['main.c:%d' % self.line]) + + def recursive_inferior_crashing_step_after_break(self): + """Test that lldb behaves correctly when stepping after a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + expected_state = 'exited' # Provide the exit code. + if self.platformIsDarwin(): + expected_state = 'stopped' # TODO: Determine why 'next' and 'continue' have no effect after a crash. + elif re.match(".*-.*-.*-android", self.dbg.GetSelectedPlatform().GetTriple()): + expected_state = 'stopped' # android has a default SEGV handler, which will re-raise the signal, so we come up stopped again + + + self.expect("next", + substrs = ['Process', expected_state]) + + if expected_state == 'exited': + self.expect("thread list", error=True,substrs = ['Process must be launched']) + else: + self.check_stop_reason() + + def recursive_inferior_crashing_expr_step_expr(self): + """Test that lldb expressions work before and after stepping after a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p null", + startstr = '(char *) $0 = 0x0') + + self.runCmd("next") + + # The lldb expression interpreter should be able to read from addresses of the inferior after a step. + self.expect("p null", + startstr = '(char *) $1 = 0x0') + + self.check_stop_reason() diff --git a/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/main.c b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/main.c new file mode 100644 index 000000000000..25e6e8df0b9b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inferior-crashing/recursive-inferior/main.c @@ -0,0 +1,19 @@ +void recursive_function(int i) +{ + if (i < 10) + { + recursive_function(i + 1); + } + else + { + char *null=0; + *null = 0; // Crash here. + } +} + +int main(int argc, char *argv[]) +{ + recursive_function(0); + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/inline-stepping/Makefile b/packages/Python/lldbsuite/test/functionalities/inline-stepping/Makefile new file mode 100644 index 000000000000..532f49555e5f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inline-stepping/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../make + +CXX_SOURCES := calling.cpp + +ifneq (,$(findstring icc,$(CC))) + CXXFLAGS += -debug inline-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/inline-stepping/TestInlineStepping.py b/packages/Python/lldbsuite/test/functionalities/inline-stepping/TestInlineStepping.py new file mode 100644 index 000000000000..3bdf6ec70e34 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inline-stepping/TestInlineStepping.py @@ -0,0 +1,270 @@ +"""Test stepping over and into inlined functions.""" + +from __future__ import print_function + + + +import os, time, sys +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestInlineStepping(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @expectedFailureFreeBSD('llvm.org/pr17214') + @expectedFailureIcc # Not really a bug. ICC combines two inlined functions. + @expectedFailureWindows("llvm.org/pr24778") + # failed 1/365 dosep runs, (i386-clang), TestInlineStepping.py:237 failed to stop at first breakpoint in main + @expectedFailureAll(oslist=["linux"], archs=["i386"]) + def test_with_python_api(self): + """Test stepping over and into inlined functions.""" + self.build() + self.inline_stepping() + + @add_test_categories(['pyapi']) + def test_step_over_with_python_api(self): + """Test stepping over and into inlined functions.""" + self.build() + self.inline_stepping_step_over() + + @add_test_categories(['pyapi']) + def test_step_in_template_with_python_api(self): + """Test stepping in to templated functions.""" + self.build() + self.step_in_template() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers that we will step to in main: + self.main_source = "calling.cpp" + self.source_lines = {} + functions = ['caller_ref_1', 'caller_ref_2', 'inline_ref_1', 'inline_ref_2', 'called_by_inline_ref', 'caller_trivial_1', 'caller_trivial_2', 'inline_trivial_1', 'inline_trivial_2', 'called_by_inline_trivial' ] + for name in functions: + self.source_lines[name] = line_number(self.main_source, "// In " + name + ".") + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + def do_step(self, step_type, destination_line_entry, test_stack_depth): + expected_stack_depth = self.thread.GetNumFrames() + if step_type == "into": + expected_stack_depth += 1 + self.thread.StepInto() + elif step_type == "out": + expected_stack_depth -= 1 + self.thread.StepOut() + elif step_type == "over": + self.thread.StepOver() + else: + self.fail ("Unrecognized step type: " + step_type) + + threads = lldbutil.get_stopped_threads (self.process, lldb.eStopReasonPlanComplete) + if len(threads) != 1: + destination_description = lldb.SBStream() + destination_line_entry.GetDescription(destination_description) + self.fail ("Failed to stop due to step " + step_type + " operation stepping to: " + destination_description.GetData()) + + self.thread = threads[0] + + stop_line_entry = self.thread.GetFrameAtIndex(0).GetLineEntry() + self.assertTrue (stop_line_entry.IsValid(), "Stop line entry was not valid.") + + # Don't use the line entry equal operator because we don't care about the column number. + stop_at_right_place = (stop_line_entry.GetFileSpec() == destination_line_entry.GetFileSpec() and stop_line_entry.GetLine() == destination_line_entry.GetLine()) + if stop_at_right_place == False: + destination_description = lldb.SBStream() + destination_line_entry.GetDescription(destination_description) + + actual_description = lldb.SBStream() + stop_line_entry.GetDescription(actual_description) + + self.fail ("Step " + step_type + " stopped at wrong place: expected: " + destination_description.GetData() + " got: " + actual_description.GetData() + ".") + + real_stack_depth = self.thread.GetNumFrames() + + if test_stack_depth and real_stack_depth != expected_stack_depth: + destination_description = lldb.SBStream() + destination_line_entry.GetDescription(destination_description) + self.fail ("Step %s to %s got wrong number of frames, should be: %d was: %d."%(step_type, destination_description.GetData(), expected_stack_depth, real_stack_depth)) + + def run_step_sequence(self, step_sequence): + """This function takes a list of duples instructing how to run the program. The first element in each duple is + a source pattern for the target location, and the second is the operation that will take you from the current + source location to the target location. It will then run all the steps in the sequence. + It will check that you arrived at the expected source location at each step, and that the stack depth changed + correctly for the operation in the sequence.""" + + target_line_entry = lldb.SBLineEntry() + target_line_entry.SetFileSpec(self.main_source_spec) + + test_stack_depth = True + # Work around for , the darwin unwinder seems flakey about whether it duplicates the first frame + # or not, which makes counting stack depth unreliable. + if self.platformIsDarwin(): + test_stack_depth = False + + for step_pattern in step_sequence: + step_stop_line = line_number (self.main_source, step_pattern[0]) + target_line_entry.SetLine(step_stop_line) + self.do_step (step_pattern[1], target_line_entry, test_stack_depth) + + def inline_stepping(self): + """Use Python APIs to test stepping over and hitting breakpoints.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + break_1_in_main = target.BreakpointCreateBySourceRegex ('// Stop here and step over to set up stepping over.', self.main_source_spec) + self.assertTrue(break_1_in_main, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + self.process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(self.process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, break_1_in_main) + + if len(threads) != 1: + self.fail ("Failed to stop at first breakpoint in main.") + + self.thread = threads[0] + + # Step over the inline_value = 0 line to get us to inline_trivial_1 called from main. Doing it this way works + # around a bug in lldb where the breakpoint on the containing line of an inlined function with no return value + # gets set past the insertion line in the function. + # Then test stepping over a simple inlined function. Note, to test all the parts of the inlined stepping + # the calls inline_stepping_1 and inline_stepping_2 should line up at the same address, that way we will test + # the "virtual" stepping. + # FIXME: Put in a check to see if that is true and warn if it is not. + + step_sequence = [["// At inline_trivial_1 called from main.", "over"], + ["// At first call of caller_trivial_1 in main.", "over"]] + self.run_step_sequence(step_sequence) + + # Now step from caller_ref_1 all the way into called_by_inline_trivial + + step_sequence = [["// In caller_trivial_1.", "into"], + ["// In caller_trivial_2.", "into"], + ["// In inline_trivial_1.", "into"], + ["// In inline_trivial_2.", "into"], + ["// At caller_by_inline_trivial in inline_trivial_2.", "over"], + ["// In called_by_inline_trivial.", "into"]] + self.run_step_sequence(step_sequence) + + # Now run to the inline_trivial_1 just before the immediate step into inline_trivial_2: + + break_2_in_main = target.BreakpointCreateBySourceRegex ('// At second call of caller_trivial_1 in main.', self.main_source_spec) + self.assertTrue(break_2_in_main, VALID_BREAKPOINT) + + threads = lldbutil.continue_to_breakpoint (self.process, break_2_in_main) + self.assertTrue (len(threads) == 1, "Successfully ran to call site of second caller_trivial_1 call.") + self.thread = threads[0] + + step_sequence = [["// In caller_trivial_1.", "into"], + ["// In caller_trivial_2.", "into"], + ["// In inline_trivial_1.", "into"]] + self.run_step_sequence(step_sequence) + + # Then call some trivial function, and make sure we end up back where we were in the inlined call stack: + + frame = self.thread.GetFrameAtIndex(0) + before_line_entry = frame.GetLineEntry() + value = frame.EvaluateExpression ("function_to_call()") + after_line_entry = frame.GetLineEntry() + + self.assertTrue (before_line_entry.GetLine() == after_line_entry.GetLine(), "Line entry before and after function calls are the same.") + + # Now make sure stepping OVER in the middle of the stack works, and then check finish from the inlined frame: + + step_sequence = [["// At increment in inline_trivial_1.", "over"], + ["// At increment in caller_trivial_2.", "out"]] + self.run_step_sequence(step_sequence) + + + # Now run to the place in main just before the first call to caller_ref_1: + + break_3_in_main = target.BreakpointCreateBySourceRegex ('// At first call of caller_ref_1 in main.', self.main_source_spec) + self.assertTrue(break_3_in_main, VALID_BREAKPOINT) + + threads = lldbutil.continue_to_breakpoint (self.process, break_3_in_main) + self.assertTrue (len(threads) == 1, "Successfully ran to call site of first caller_ref_1 call.") + self.thread = threads[0] + + step_sequence = [["// In caller_ref_1.", "into"], + ["// In caller_ref_2.", "into"], + ["// In inline_ref_1.", "into"], + ["// In inline_ref_2.", "into"], + ["// In called_by_inline_ref.", "into"], + ["// In inline_ref_2.", "out"], + ["// In inline_ref_1.", "out"], + ["// At increment in inline_ref_1.", "over"], + ["// In caller_ref_2.", "out"], + ["// At increment in caller_ref_2.", "over"]] + self.run_step_sequence (step_sequence) + + def inline_stepping_step_over(self): + """Use Python APIs to test stepping over and hitting breakpoints.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + break_1_in_main = target.BreakpointCreateBySourceRegex ('// At second call of caller_ref_1 in main.', self.main_source_spec) + self.assertTrue(break_1_in_main, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + self.process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(self.process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, break_1_in_main) + + if len(threads) != 1: + self.fail ("Failed to stop at first breakpoint in main.") + + self.thread = threads[0] + + step_sequence = [["// In caller_ref_1.", "into"], + ["// In caller_ref_2.", "into"], + ["// At increment in caller_ref_2.", "over"]] + self.run_step_sequence (step_sequence) + + def step_in_template(self): + """Use Python APIs to test stepping in to templated functions.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + break_1_in_main = target.BreakpointCreateBySourceRegex ('// Call max_value template', self.main_source_spec) + self.assertTrue(break_1_in_main, VALID_BREAKPOINT) + + break_2_in_main = target.BreakpointCreateBySourceRegex ('// Call max_value specialized', self.main_source_spec) + self.assertTrue(break_2_in_main, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + self.process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(self.process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, break_1_in_main) + + if len(threads) != 1: + self.fail ("Failed to stop at first breakpoint in main.") + + self.thread = threads[0] + + step_sequence = [["// In max_value template", "into"]] + self.run_step_sequence(step_sequence) + + threads = lldbutil.continue_to_breakpoint (self.process, break_2_in_main) + self.assertEqual(len(threads), 1, "Successfully ran to call site of second caller_trivial_1 call.") + self.thread = threads[0] + + step_sequence = [["// In max_value specialized", "into"]] + self.run_step_sequence(step_sequence) diff --git a/packages/Python/lldbsuite/test/functionalities/inline-stepping/calling.cpp b/packages/Python/lldbsuite/test/functionalities/inline-stepping/calling.cpp new file mode 100644 index 000000000000..9982fbf42734 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/inline-stepping/calling.cpp @@ -0,0 +1,136 @@ +#include +#include +#include + +inline int inline_ref_1 (int &value) __attribute__((always_inline)); +inline int inline_ref_2 (int &value) __attribute__((always_inline)); + +int caller_ref_1 (int &value); +int caller_ref_2 (int &value); + +int called_by_inline_ref (int &value); + +inline void inline_trivial_1 () __attribute__((always_inline)); +inline void inline_trivial_2 () __attribute__((always_inline)); + +void caller_trivial_1 (); +void caller_trivial_2 (); + +void called_by_inline_trivial (); + +static int inline_value; + +int +function_to_call () +{ + return inline_value; +} + +int +caller_ref_1 (int &value) +{ + int increment = caller_ref_2(value); // In caller_ref_1. + value += increment; // At increment in caller_ref_1. + return value; +} + +int +caller_ref_2 (int &value) +{ + int increment = inline_ref_1 (value); // In caller_ref_2. + value += increment; // At increment in caller_ref_2. + return value; +} + +int +called_by_inline_ref (int &value) +{ + value += 1; // In called_by_inline_ref. + return value; +} + +int +inline_ref_1 (int &value) +{ + int increment = inline_ref_2(value); // In inline_ref_1. + value += increment; // At increment in inline_ref_1. + return value; +} + +int +inline_ref_2 (int &value) +{ + int increment = called_by_inline_ref (value); // In inline_ref_2. + value += 1; // At increment in inline_ref_2. + return value; +} + +void +caller_trivial_1 () +{ + caller_trivial_2(); // In caller_trivial_1. + inline_value += 1; +} + +void +caller_trivial_2 () +{ + inline_trivial_1 (); // In caller_trivial_2. + inline_value += 1; // At increment in caller_trivial_2. +} + +void +called_by_inline_trivial () +{ + inline_value += 1; // In called_by_inline_trivial. +} + +void +inline_trivial_1 () +{ + inline_trivial_2(); // In inline_trivial_1. + inline_value += 1; // At increment in inline_trivial_1. +} + +void +inline_trivial_2 () +{ + inline_value += 1; // In inline_trivial_2. + called_by_inline_trivial (); // At caller_by_inline_trivial in inline_trivial_2. +} + +template T +max_value(const T& lhs, const T& rhs) +{ + return std::max(lhs, rhs); // In max_value template +} + +template<> std::string +max_value(const std::string& lhs, const std::string& rhs) +{ + return (lhs.size() > rhs.size()) ? lhs : rhs; // In max_value specialized +} + +int +main (int argc, char **argv) +{ + + inline_value = 0; // Stop here and step over to set up stepping over. + + inline_trivial_1 (); // At inline_trivial_1 called from main. + + caller_trivial_1(); // At first call of caller_trivial_1 in main. + + caller_trivial_1(); // At second call of caller_trivial_1 in main. + + caller_ref_1 (argc); // At first call of caller_ref_1 in main. + + caller_ref_1 (argc); // At second call of caller_ref_1 in main. + + function_to_call (); // Make sure debug info for this function gets generated. + + max_value(123, 456); // Call max_value template + max_value(std::string("abc"), std::string("0022")); // Call max_value specialized + + return 0; // About to return from main. +} diff --git a/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/Makefile b/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/TestJITLoaderGDB.py b/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/TestJITLoaderGDB.py new file mode 100644 index 000000000000..c7abf3464412 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/TestJITLoaderGDB.py @@ -0,0 +1,37 @@ +"""Test for the JITLoaderGDB interface""" + +from __future__ import print_function + + + +import unittest2 +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import re + + +class JITLoaderGDBTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipTestIfFn(lambda x: True, "llvm.org/pr24702", "Skipped because the test crashes the test runner") + @unittest2.expectedFailure("llvm.org/pr24702") + def test_bogus_values(self): + """Test that we handle inferior misusing the GDB JIT interface""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # launch the process, do not stop at entry point. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The inferior will now pass bogus values over the interface. Make sure we don't crash. + + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) diff --git a/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/main.c b/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/main.c new file mode 100644 index 000000000000..6a8ec50e6637 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/jitloader_gdb/main.c @@ -0,0 +1,44 @@ +#include + +// GDB JIT interface +enum JITAction { JIT_NOACTION, JIT_REGISTER_FN, JIT_UNREGISTER_FN }; + +struct JITCodeEntry +{ + struct JITCodeEntry* next; + struct JITCodeEntry* prev; + const char *symfile_addr; + uint64_t symfile_size; +}; + +struct JITDescriptor +{ + uint32_t version; + uint32_t action_flag; + struct JITCodeEntry* relevant_entry; + struct JITCodeEntry* first_entry; +}; + +struct JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, 0, 0 }; + +void __jit_debug_register_code() +{ +} +// end GDB JIT interface + +struct JITCodeEntry entry; + +int main() +{ + // Create a code entry with a bogus size + entry.next = entry.prev = 0; + entry.symfile_addr = (char *)&entry; + entry.symfile_size = (uint64_t)47<<32; + + __jit_debug_descriptor.relevant_entry = __jit_debug_descriptor.first_entry = &entry; + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + + __jit_debug_register_code(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/Makefile b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/TestLaunchWithShellExpand.py b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/TestLaunchWithShellExpand.py new file mode 100644 index 000000000000..8f712dc21110 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/TestLaunchWithShellExpand.py @@ -0,0 +1,97 @@ +""" +Test that argdumper is a viable launching strategy. +""" +from __future__ import print_function + + + +import lldb +import os +import time +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LaunchWithShellExpandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureFreeBSD("llvm.org/pr22627 process launch w/ shell expansion not working") + @expectedFailureLinux("llvm.org/pr22627 process launch w/ shell expansion not working") + @expectedFailureWindows("llvm.org/pr24778") + def test(self): + self.build() + exe = os.path.join (os.getcwd(), "a.out") + + self.runCmd("target create %s" % exe) + + # Create the target + target = self.dbg.CreateTarget(exe) + + # Create any breakpoints we need + breakpoint = target.BreakpointCreateBySourceRegex ('break here', lldb.SBFileSpec ("main.cpp", False)) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + self.runCmd("process launch -X true -w %s -- fi*.tx?" % (os.getcwd())) + + process = self.process() + + self.assertTrue(process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at 'main' should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonBreakpoint, + "Thread in process stopped in 'main' should have a stop reason of eStopReasonBreakpoint"); + + self.expect("frame variable argv[1]", substrs=['file1.txt']) + self.expect("frame variable argv[2]", substrs=['file2.txt']) + self.expect("frame variable argv[3]", substrs=['file3.txt']) + self.expect("frame variable argv[4]", substrs=['file4.txy']) + self.expect("frame variable argv[5]", substrs=['file5.tyx'], matching=False) + + self.runCmd("process kill") + + self.runCmd('process launch -X true -w %s -- "foo bar"' % (os.getcwd())) + + process = self.process() + + self.assertTrue(process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at 'main' should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonBreakpoint, + "Thread in process stopped in 'main' should have a stop reason of eStopReasonBreakpoint"); + + self.expect("frame variable argv[1]", substrs=['foo bar']) + + self.runCmd("process kill") + + self.runCmd('process launch -X true -w %s -- foo\ bar' % (os.getcwd())) + + process = self.process() + + self.assertTrue(process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at 'main' should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonBreakpoint, + "Thread in process stopped in 'main' should have a stop reason of eStopReasonBreakpoint"); + + self.expect("frame variable argv[1]", substrs=['foo bar']) diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file1.txt b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file1.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file2.txt b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file2.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file3.txt b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file3.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file4.txy b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file4.txy new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file5.tyx b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/file5.tyx new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/foo bar b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/foo bar new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/main.cpp b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/main.cpp new file mode 100644 index 000000000000..cbef8d1e6da1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/launch_with_shellexpand/main.cpp @@ -0,0 +1,5 @@ +int +main (int argc, char const **argv) +{ + return 0; // break here +} diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/Makefile b/packages/Python/lldbsuite/test/functionalities/load_unload/Makefile new file mode 100644 index 000000000000..779745c4d26c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/Makefile @@ -0,0 +1,24 @@ +LEVEL := ../../make + +LIB_PREFIX := loadunload_ + +LD_EXTRAS := -L. -l$(LIB_PREFIX)d -ldl +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + +.PHONY: +a.out: lib_a lib_b lib_c lib_d hidden_lib_d + +lib_%: + $(MAKE) -f $*.mk + +hidden_lib_d: + $(MAKE) -C hidden + +clean:: + $(MAKE) -f a.mk clean + $(MAKE) -f b.mk clean + $(MAKE) -f c.mk clean + $(MAKE) -f d.mk clean + $(MAKE) -C hidden clean diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py b/packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py new file mode 100644 index 000000000000..481e7c887bd0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py @@ -0,0 +1,357 @@ +""" +Test that breakpoint by symbol name works correctly with dynamic libs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently +class LoadUnloadTestCase(TestBase): + + def getCategories (self): + return ['basic_process'] + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.line = line_number('main.cpp', + '// Set break point at this line for test_lldb_process_load_and_unload_commands().') + self.line_d_function = line_number('d.cpp', + '// Find this line number within d_dunction().') + if not self.platformIsDarwin(): + if not lldb.remote_platform and "LD_LIBRARY_PATH" in os.environ: + self.runCmd("settings set target.env-vars " + self.dylibPath + "=" + os.environ["LD_LIBRARY_PATH"] + ":" + os.getcwd()) + else: + if lldb.remote_platform: + wd = lldb.remote_platform.GetWorkingDirectory() + else: + wd = os.getcwd() + self.runCmd("settings set target.env-vars " + self.dylibPath + "=" + wd) + + def copy_shlibs_to_remote(self, hidden_dir=False): + """ Copies the shared libs required by this test suite to remote. + Does nothing in case of non-remote platforms. + """ + if lldb.remote_platform: + cwd = os.getcwd() + shlibs = ['libloadunload_a.so', 'libloadunload_b.so', + 'libloadunload_c.so', 'libloadunload_d.so'] + wd = lldb.remote_platform.GetWorkingDirectory() + for f in shlibs: + err = lldb.remote_platform.Put( + lldb.SBFileSpec(os.path.join(cwd, f)), + lldb.SBFileSpec(os.path.join(wd, f))) + if err.Fail(): + raise RuntimeError( + "Unable copy '%s' to '%s'.\n>>> %s" % + (f, wd, err.GetCString())) + if hidden_dir: + shlib = 'libloadunload_d.so' + hidden_dir = os.path.join(wd, 'hidden') + hidden_file = os.path.join(hidden_dir, shlib) + err = lldb.remote_platform.MakeDirectory(hidden_dir) + if err.Fail(): + raise RuntimeError( + "Unable to create a directory '%s'." % hidden_dir) + err = lldb.remote_platform.Put( + lldb.SBFileSpec(os.path.join(cwd, 'hidden', shlib)), + lldb.SBFileSpec(hidden_file)) + if err.Fail(): + raise RuntimeError( + "Unable copy 'libloadunload_d.so' to '%s'.\n>>> %s" % + (wd, err.GetCString())) + + @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @not_remote_testsuite_ready + @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently + def test_modules_search_paths(self): + """Test target modules list after loading a different copy of the library libd.dylib, and verifies that it works with 'target modules search-paths add'.""" + + # Invoke the default build rule. + self.build() + + if self.platformIsDarwin(): + dylibName = 'libloadunload_d.dylib' + else: + dylibName = 'libloadunload_d.so' + + # The directory with the dynamic library we did not link to. + new_dir = os.path.join(os.getcwd(), "hidden") + + old_dylib = os.path.join(os.getcwd(), dylibName) + new_dylib = os.path.join(new_dir, dylibName) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.expect("target modules list", + substrs = [old_dylib]) + #self.expect("target modules list -t 3", + # patterns = ["%s-[^-]*-[^-]*" % self.getArchitecture()]) + # Add an image search path substitution pair. + self.runCmd("target modules search-paths add %s %s" % (os.getcwd(), new_dir)) + + self.expect("target modules search-paths list", + substrs = [os.getcwd(), new_dir]) + + self.expect("target modules search-paths query %s" % os.getcwd(), "Image search path successfully transformed", + substrs = [new_dir]) + + # Obliterate traces of libd from the old location. + os.remove(old_dylib) + # Inform (DY)LD_LIBRARY_PATH of the new path, too. + env_cmd_string = "settings set target.env-vars " + self.dylibPath + "=" + new_dir + if self.TraceOn(): + print("Set environment to: ", env_cmd_string) + self.runCmd(env_cmd_string) + self.runCmd("settings show target.env-vars") + + remove_dyld_path_cmd = "settings remove target.env-vars " + self.dylibPath + self.addTearDownHook(lambda: self.dbg.HandleCommand(remove_dyld_path_cmd)) + + self.runCmd("run") + + self.expect("target modules list", "LLDB successfully locates the relocated dynamic library", + substrs = [new_dylib]) + + @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @skipUnlessListedRemote(['android']) + @expectedFailureAndroid # wrong source file shows up for hidden library + @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently + def test_dyld_library_path(self): + """Test (DY)LD_LIBRARY_PATH after moving libd.dylib, which defines d_function, somewhere else.""" + + # Invoke the default build rule. + self.build() + self.copy_shlibs_to_remote(hidden_dir=True) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + if self.platformIsDarwin(): + dylibName = 'libloadunload_d.dylib' + dsymName = 'libloadunload_d.dylib.dSYM' + else: + dylibName = 'libloadunload_d.so' + + # The directory to relocate the dynamic library and its debugging info. + special_dir = "hidden" + if lldb.remote_platform: + wd = lldb.remote_platform.GetWorkingDirectory() + else: + wd = os.getcwd() + + old_dir = wd + new_dir = os.path.join(wd, special_dir) + old_dylib = os.path.join(old_dir, dylibName) + + remove_dyld_path_cmd = "settings remove target.env-vars " + self.dylibPath + self.addTearDownHook(lambda: self.dbg.HandleCommand(remove_dyld_path_cmd)) + + # For now we don't track (DY)LD_LIBRARY_PATH, so the old library will be in + # the modules list. + self.expect("target modules list", + substrs = [os.path.basename(old_dylib)], + matching=True) + + lldbutil.run_break_set_by_file_and_line (self, "d.cpp", self.line_d_function, num_expected_locations=1) + # After run, make sure the non-hidden library is picked up. + self.expect("run", substrs=["return", "700"]) + + self.runCmd("continue") + + # Add the hidden directory first in the search path. + env_cmd_string = ("settings set target.env-vars %s=%s" % + (self.dylibPath, new_dir)) + if not self.platformIsDarwin(): + env_cmd_string += ":" + wd + self.runCmd(env_cmd_string) + + # This time, the hidden library should be picked up. + self.expect("run", substrs=["return", "12345"]) + + @expectedFailureAll(bugnumber="llvm.org/pr25805", hostoslist=["windows"], compiler="gcc", archs=["i386"], triple='.*-android') + @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @skipUnlessListedRemote(['android']) + @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently + def test_lldb_process_load_and_unload_commands(self): + """Test that lldb process load/unload command work correctly.""" + + # Invoke the default build rule. + self.build() + self.copy_shlibs_to_remote() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break at main.cpp before the call to dlopen(). + # Use lldb's process load command to load the dylib, instead. + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + if lldb.remote_platform: + shlib_dir = lldb.remote_platform.GetWorkingDirectory() + else: + shlib_dir = self.mydir + + if self.platformIsDarwin(): + dylibName = 'libloadunload_a.dylib' + else: + dylibName = 'libloadunload_a.so' + + # Make sure that a_function does not exist at this point. + self.expect("image lookup -n a_function", "a_function should not exist yet", + error=True, matching=False, patterns = ["1 match found"]) + + # Use lldb 'process load' to load the dylib. + self.expect("process load %s --install" % dylibName, "%s loaded correctly" % dylibName, + patterns = ['Loading "%s".*ok' % dylibName, + 'Image [0-9]+ loaded']) + + # Search for and match the "Image ([0-9]+) loaded" pattern. + output = self.res.GetOutput() + pattern = re.compile("Image ([0-9]+) loaded") + for l in output.split(os.linesep): + #print("l:", l) + match = pattern.search(l) + if match: + break + index = match.group(1) + + # Now we should have an entry for a_function. + self.expect("image lookup -n a_function", "a_function should now exist", + patterns = ["1 match found .*%s" % dylibName]) + + # Use lldb 'process unload' to unload the dylib. + self.expect("process unload %s" % index, "%s unloaded correctly" % dylibName, + patterns = ["Unloading .* with index %s.*ok" % index]) + + self.runCmd("process continue") + + @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @skipUnlessListedRemote(['android']) + def test_load_unload(self): + """Test breakpoint by name works correctly with dlopen'ing.""" + + # Invoke the default build rule. + self.build() + self.copy_shlibs_to_remote() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break by function name a_function (not yet loaded). + lldbutil.run_break_set_by_symbol (self, "a_function", num_expected_locations=0) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint and at a_function. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'a_function', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Issue the 'contnue' command. We should stop agaian at a_function. + # The stop reason of the thread should be breakpoint and at a_function. + self.runCmd("continue") + + # rdar://problem/8508987 + # The a_function breakpoint should be encountered twice. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'a_function', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 2. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 2']) + + @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @skipUnlessListedRemote(['android']) + @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently + def test_step_over_load (self): + """Test stepping over code that loads a shared library works correctly.""" + + # Invoke the default build rule. + self.build() + self.copy_shlibs_to_remote() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break by function name a_function (not yet loaded). + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint and at a_function. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.runCmd("thread step-over", "Stepping over function that loads library") + + # The stop reason should be step end. + self.expect("thread list", "step over succeeded.", + substrs = ['stopped', + 'stop reason = step over']) + + @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @skipUnlessListedRemote(['android']) + @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently + @unittest2.expectedFailure("llvm.org/pr25806") + def test_static_init_during_load (self): + """Test that we can set breakpoints correctly in static initializers""" + + self.build() + self.copy_shlibs_to_remote() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + a_init_bp_num = lldbutil.run_break_set_by_symbol(self, "a_init", num_expected_locations=0) + b_init_bp_num = lldbutil.run_break_set_by_symbol(self, "b_init", num_expected_locations=0) + d_init_bp_num = lldbutil.run_break_set_by_symbol(self, "d_init", num_expected_locations=1) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'd_init', + 'stop reason = breakpoint %d' % d_init_bp_num]) + + self.runCmd("continue") + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'a_init', + 'stop reason = breakpoint %d' % a_init_bp_num]) + self.expect("thread backtrace", + substrs = ['a_init', + 'dlopen', + 'main']) + + self.runCmd("continue") + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'b_init', + 'stop reason = breakpoint %d' % b_init_bp_num]) + self.expect("thread backtrace", + substrs = ['b_init', + 'dlopen', + 'main']) diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/a.cpp b/packages/Python/lldbsuite/test/functionalities/load_unload/a.cpp new file mode 100644 index 000000000000..235749aef74a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/a.cpp @@ -0,0 +1,22 @@ +//===-- 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_init() +{ + return 234; +} + +int a_global = a_init(); + +extern "C" int +a_function () +{ + return b_function (); +} diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/a.mk b/packages/Python/lldbsuite/test/functionalities/load_unload/a.mk new file mode 100644 index 000000000000..0eb810e21787 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/a.mk @@ -0,0 +1,23 @@ +LEVEL := ../../make + +LIB_PREFIX := loadunload_ + +CFLAGS_EXTRAS := -fPIC +LD_EXTRAS := -L. -l$(LIB_PREFIX)b + +DYLIB_NAME := $(LIB_PREFIX)a +DYLIB_CXX_SOURCES := a.cpp +DYLIB_ONLY := YES + +CXXFLAGS += -fPIC + +include $(LEVEL)/Makefile.rules + +.PHONY: +$(DYLIB_FILENAME): lib_b + +lib_b: + "$(MAKE)" -f b.mk + +clean:: + "$(MAKE)" -f b.mk clean diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/b.cpp b/packages/Python/lldbsuite/test/functionalities/load_unload/b.cpp new file mode 100644 index 000000000000..4d383169b79c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/b.cpp @@ -0,0 +1,21 @@ +//===-- 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_init() +{ + return 345; +} + +int b_global = b_init(); + +int +b_function () +{ + return 500; +} diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/b.mk b/packages/Python/lldbsuite/test/functionalities/load_unload/b.mk new file mode 100644 index 000000000000..c1b0877d72a4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/b.mk @@ -0,0 +1,11 @@ +LEVEL := ../../make + +LIB_PREFIX := loadunload_ + +DYLIB_NAME := $(LIB_PREFIX)b +DYLIB_CXX_SOURCES := b.cpp +DYLIB_ONLY := YES + +CXXFLAGS += -fPIC + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/c.cpp b/packages/Python/lldbsuite/test/functionalities/load_unload/c.cpp new file mode 100644 index 000000000000..f0dfb4ec0d38 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/c.cpp @@ -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. +// +//===----------------------------------------------------------------------===// +extern "C" int +c_function () +{ + return 600; +} diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/c.mk b/packages/Python/lldbsuite/test/functionalities/load_unload/c.mk new file mode 100644 index 000000000000..5b5691efeef4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/c.mk @@ -0,0 +1,11 @@ +LEVEL := ../../make + +LIB_PREFIX := loadunload_ + +DYLIB_NAME := $(LIB_PREFIX)c +DYLIB_CXX_SOURCES := c.cpp +DYLIB_ONLY := YES + +CXXFLAGS += -fPIC + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/cmds.txt b/packages/Python/lldbsuite/test/functionalities/load_unload/cmds.txt new file mode 100644 index 000000000000..1e4b198dc0d3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/cmds.txt @@ -0,0 +1,2 @@ +breakpoint set -n a_function +run diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/d.cpp b/packages/Python/lldbsuite/test/functionalities/load_unload/d.cpp new file mode 100644 index 000000000000..55f2a6b404b3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/d.cpp @@ -0,0 +1,21 @@ +//===-- 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 d_init() +{ + return 123; +} + +int d_global = d_init(); + +int +d_function () +{ // Find this line number within d_dunction(). + return 700; +} diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/d.mk b/packages/Python/lldbsuite/test/functionalities/load_unload/d.mk new file mode 100644 index 000000000000..b6b6eeacba26 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/d.mk @@ -0,0 +1,13 @@ +LEVEL := ../../make + +LIB_PREFIX := loadunload_ + +DYLIB_EXECUTABLE_PATH := $(CURDIR) + +DYLIB_NAME := $(LIB_PREFIX)d +DYLIB_CXX_SOURCES := d.cpp +DYLIB_ONLY := YES + +CXXFLAGS += -fPIC + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/hidden/Makefile b/packages/Python/lldbsuite/test/functionalities/load_unload/hidden/Makefile new file mode 100644 index 000000000000..f84d8300843f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/hidden/Makefile @@ -0,0 +1,11 @@ +LEVEL := ../../../make + +LIB_PREFIX := loadunload_ + +DYLIB_NAME := $(LIB_PREFIX)d +DYLIB_CXX_SOURCES := d.cpp +DYLIB_ONLY := YES + +CXXFLAGS += -fPIC + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/hidden/d.cpp b/packages/Python/lldbsuite/test/functionalities/load_unload/hidden/d.cpp new file mode 100644 index 000000000000..6a7642c08b93 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/hidden/d.cpp @@ -0,0 +1,21 @@ +//===-- 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 d_init() +{ + return 456; +} + +int d_global = d_init(); + +int +d_function () +{ // Find this line number within d_dunction(). + return 12345; +} diff --git a/packages/Python/lldbsuite/test/functionalities/load_unload/main.cpp b/packages/Python/lldbsuite/test/functionalities/load_unload/main.cpp new file mode 100644 index 000000000000..bff9a3176060 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/load_unload/main.cpp @@ -0,0 +1,80 @@ +//===-- 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[]) +{ +#if defined (__APPLE__) + const char *a_name = "@executable_path/libloadunload_a.dylib"; + const char *c_name = "@executable_path/libloadunload_c.dylib"; +#else + const char *a_name = "libloadunload_a.so"; + const char *c_name = "libloadunload_c.so"; +#endif + void *a_dylib_handle = NULL; + void *c_dylib_handle = NULL; + int (*a_function) (void); + + a_dylib_handle = dlopen (a_name, RTLD_NOW); // Set break point at this line for test_lldb_process_load_and_unload_commands(). + 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); + + int d_function(void); + printf ("d_function returns: %d\n", d_function()); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/longjmp/Makefile b/packages/Python/lldbsuite/test/functionalities/longjmp/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/longjmp/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/longjmp/TestLongjmp.py b/packages/Python/lldbsuite/test/functionalities/longjmp/TestLongjmp.py new file mode 100644 index 000000000000..1316a01c924c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/longjmp/TestLongjmp.py @@ -0,0 +1,85 @@ +""" +Test the use of setjmp/longjmp for non-local goto operations in a single-threaded inferior. +""" + +from __future__ import print_function + + + +import os +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class LongjmpTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + + @skipIfDarwin # llvm.org/pr16769: LLDB on Mac OS X dies in function ReadRegisterBytes in GDBRemoteRegisterContext.cpp + @skipIfFreeBSD # llvm.org/pr17214 + @expectedFailureLinux("llvm.org/pr20231") + @expectedFailureWindows("llvm.org/pr24778") + def test_step_out(self): + """Test stepping when the inferior calls setjmp/longjmp, in particular, thread step-out.""" + self.build() + self.step_out() + + @skipIfDarwin # llvm.org/pr16769: LLDB on Mac OS X dies in function ReadRegisterBytes in GDBRemoteRegisterContext.cpp + @skipIfFreeBSD # llvm.org/pr17214 + @expectedFailureLinux("llvm.org/pr20231") + @expectedFailureWindows("llvm.org/pr24778") + def test_step_over(self): + """Test stepping when the inferior calls setjmp/longjmp, in particular, thread step-over a longjmp.""" + self.build() + self.step_over() + + @skipIfDarwin # llvm.org/pr16769: LLDB on Mac OS X dies in function ReadRegisterBytes in GDBRemoteRegisterContext.cpp + @skipIfFreeBSD # llvm.org/pr17214 + @expectedFailureLinux("llvm.org/pr20231") + @expectedFailureWindows("llvm.org/pr24778") + def test_step_back_out(self): + """Test stepping when the inferior calls setjmp/longjmp, in particular, thread step-out after thread step-in.""" + self.build() + self.step_back_out() + + def start_test(self, symbol): + exe = os.path.join(os.getcwd(), "a.out") + + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break in main(). + lldbutil.run_break_set_by_symbol (self, symbol, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + def check_status(self): + # Note: Depending on the generated mapping of DWARF to assembly, + # the process may have stopped or exited. + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* exited with status = 0']) + + def step_out(self): + self.start_test("do_jump") + self.runCmd("thread step-out", RUN_SUCCEEDED) + self.check_status() + + def step_over(self): + self.start_test("do_jump") + self.runCmd("thread step-over", RUN_SUCCEEDED) + self.runCmd("thread step-over", RUN_SUCCEEDED) + self.check_status() + + def step_back_out(self): + self.start_test("main") + + self.runCmd("thread step-over", RUN_SUCCEEDED) + self.runCmd("thread step-in", RUN_SUCCEEDED) + self.runCmd("thread step-out", RUN_SUCCEEDED) + self.check_status() diff --git a/packages/Python/lldbsuite/test/functionalities/longjmp/main.c b/packages/Python/lldbsuite/test/functionalities/longjmp/main.c new file mode 100644 index 000000000000..3879311eb452 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/longjmp/main.c @@ -0,0 +1,31 @@ +//===-- 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 + +jmp_buf j; + +void do_jump(void) +{ + // We can't let the compiler know this will always happen or it might make + // optimizations that break our test. + if (!clock()) + longjmp(j, 1); // non-local goto +} + +int main (void) +{ + if (setjmp(j) == 0) + do_jump(); + else + return 0; // destination of longjmp + + return 1; +} diff --git a/packages/Python/lldbsuite/test/functionalities/memory/read/Makefile b/packages/Python/lldbsuite/test/functionalities/memory/read/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/memory/read/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/memory/read/TestMemoryRead.py b/packages/Python/lldbsuite/test/functionalities/memory/read/TestMemoryRead.py new file mode 100644 index 000000000000..7410650ad652 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/memory/read/TestMemoryRead.py @@ -0,0 +1,99 @@ +""" +Test the 'memory read' command. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class MemoryReadTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_memory_read(self): + """Test the 'memory read' command with plain and vector formats.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break in main() aftre the variables are assigned values. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Test the memory read commands. + + # (lldb) memory read -f d -c 1 `&argc` + # 0x7fff5fbff9a0: 1 + self.runCmd("memory read -f d -c 1 `&argc`") + + # Find the starting address for variable 'argc' to verify later that the + # '--format uint32_t[] --size 4 --count 4' option increments the address + # correctly. + line = self.res.GetOutput().splitlines()[0] + items = line.split(':') + address = int(items[0], 0) + argc = int(items[1], 0) + self.assertTrue(address > 0 and argc == 1) + + # (lldb) memory read --format uint32_t[] --size 4 --count 4 `&argc` + # 0x7fff5fbff9a0: {0x00000001} + # 0x7fff5fbff9a4: {0x00000000} + # 0x7fff5fbff9a8: {0x0ec0bf27} + # 0x7fff5fbff9ac: {0x215db505} + self.runCmd("memory read --format uint32_t[] --size 4 --count 4 `&argc`") + lines = self.res.GetOutput().splitlines() + for i in range(4): + if i == 0: + # Verify that the printout for argc is correct. + self.assertTrue(argc == int(lines[i].split(':')[1].strip(' {}'), 0)) + addr = int(lines[i].split(':')[0], 0) + # Verify that the printout for addr is incremented correctly. + self.assertTrue(addr == (address + i*4)) + + # (lldb) memory read --format char[] --size 7 --count 1 `&my_string` + # 0x7fff5fbff990: {abcdefg} + self.expect("memory read --format char[] --size 7 --count 1 `&my_string`", + substrs = ['abcdefg']) + + # (lldb) memory read --format 'hex float' --size 16 `&argc` + # 0x7fff5fbff5b0: error: unsupported byte size (16) for hex float format + self.expect("memory read --format 'hex float' --size 16 `&argc`", + substrs = ['unsupported byte size (16) for hex float format']) + + self.expect("memory read --format 'float' --count 1 --size 8 `&my_double`", + substrs = ['1234.']) + + # (lldb) memory read --format 'float' --count 1 --size 20 `&my_double` + # 0x7fff5fbff598: error: unsupported byte size (20) for float format + self.expect("memory read --format 'float' --count 1 --size 20 `&my_double`", + substrs = ['unsupported byte size (20) for float format']) + + self.expect('memory read --type int --count 5 `&my_ints[0]`', + substrs=['(int) 0x', '2','4','6','8','10']) + + self.expect('memory read --type int --count 5 --format hex `&my_ints[0]`', + substrs=['(int) 0x', '0x','0a']) + + self.expect('memory read --type int --count 5 --offset 5 `&my_ints[0]`', + substrs=['(int) 0x', '12', '14','16','18', '20']) diff --git a/packages/Python/lldbsuite/test/functionalities/memory/read/main.cpp b/packages/Python/lldbsuite/test/functionalities/memory/read/main.cpp new file mode 100644 index 000000000000..cd367ff318ab --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/memory/read/main.cpp @@ -0,0 +1,19 @@ +//===-- 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 + +int main (int argc, char const *argv[]) +{ + char my_string[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 0}; + double my_double = 1234.5678; + int my_ints[] = {2,4,6,8,10,12,14,16,18,20,22}; + printf("my_string=%s\n", my_string); // Set break point at this line. + printf("my_double=%g\n", my_double); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/Makefile b/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/TestIndexVariable.py b/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/TestIndexVariable.py new file mode 100644 index 000000000000..5c53cfaf9120 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/TestIndexVariable.py @@ -0,0 +1,43 @@ +"""Test evaluating expressions which ref. index variable 'i' which just goes +from out of scope to in scope when stopped at the breakpoint.""" + +from __future__ import print_function + + + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class NonOverlappingIndexVariableCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.cpp' + self.line_to_break = line_number(self.source, '// Set breakpoint here.') + + # rdar://problem/9890530 + def test_eval_index_variable(self): + """Test expressions of variable 'i' which appears in two for loops.""" + self.build() + self.exe_name = 'a.out' + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file %s" % exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, self.source, self.line_to_break, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.runCmd('frame variable i') + self.runCmd('expr i') + self.runCmd('expr ptr[0]->point.x') + self.runCmd('expr ptr[0]->point.y') + self.runCmd('expr ptr[i]->point.x') + self.runCmd('expr ptr[i]->point.y') diff --git a/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/main.cpp b/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/main.cpp new file mode 100644 index 000000000000..b4f519cd25a8 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/non-overlapping-index-variable-i/main.cpp @@ -0,0 +1,51 @@ +//===-- 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 + +class Point { +public: + int x; + int y; + Point(int a, int b): + x(a), + y(b) + {} +}; + +class Data { +public: + int id; + Point point; + Data(int i): + id(i), + point(0, 0) + {} +}; + +int main(int argc, char const *argv[]) { + Data *data[1000]; + Data **ptr = data; + for (int i = 0; i < 1000; ++i) { + ptr[i] = new Data(i); + ptr[i]->point.x = i; + ptr[i]->point.y = i+1; + } + + printf("Finished populating data.\n"); + for (int i = 0; i < 1000; ++i) { + bool dump = argc > 1; // Set breakpoint here. + // Evaluate a couple of expressions (2*1000 = 2000 exprs): + // expr ptr[i]->point.x + // expr ptr[i]->point.y + if (dump) { + printf("data[%d] = %d (%d, %d)\n", i, ptr[i]->id, ptr[i]->point.x, ptr[i]->point.y); + } + } + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/nosucharch/Makefile b/packages/Python/lldbsuite/test/functionalities/nosucharch/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/nosucharch/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/nosucharch/TestNoSuchArch.py b/packages/Python/lldbsuite/test/functionalities/nosucharch/TestNoSuchArch.py new file mode 100644 index 000000000000..7a6c98d92c3e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/nosucharch/TestNoSuchArch.py @@ -0,0 +1,29 @@ +""" +Test that using a non-existent architecture name does not crash LLDB. +""" +from __future__ import print_function + + + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class NoSuchArchTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test (self): + self.build() + exe = os.path.join (os.getcwd(), "a.out") + + # Check that passing an invalid arch via the command-line fails but doesn't crash + self.expect("target crete --arch nothingtoseehere %s" % (exe), error=True) + + # Check that passing an invalid arch via the SB API fails but doesn't crash + target = self.dbg.CreateTargetWithFileAndArch(exe,"nothingtoseehere") + self.assertFalse(target.IsValid(), "This target should not be valid") + + # Now just create the target with the default arch and check it's fine + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid(), "This target should now be valid") diff --git a/packages/Python/lldbsuite/test/functionalities/nosucharch/main.cpp b/packages/Python/lldbsuite/test/functionalities/nosucharch/main.cpp new file mode 100644 index 000000000000..4cce7f667ff7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/nosucharch/main.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/object-file/TestImageListMultiArchitecture.py b/packages/Python/lldbsuite/test/functionalities/object-file/TestImageListMultiArchitecture.py new file mode 100644 index 000000000000..4f4a22ed6067 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/object-file/TestImageListMultiArchitecture.py @@ -0,0 +1,41 @@ +""" +Test lldb 'image list' on object files across multiple architectures. +This exercises classes like ObjectFileELF and their support for opening +foreign-architecture object files. +""" + +from __future__ import print_function + + + +import os.path +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import re + +class TestImageListMultiArchitecture(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_image_list_shows_multiple_architectures(self): + """Test that image list properly shows the correct architecture for a set of different architecture object files.""" + images = { + "hello-freebsd-10.0-x86_64-clang-3.3": re.compile(r"x86_64-(\*)?-freebsd10.0(-unknown)? x86_64"), + "hello-freebsd-10.0-x86_64-gcc-4.7.3": re.compile(r"x86_64-(\*)?-freebsd10.0(-unknown)? x86_64"), + "hello-netbsd-6.1-x86_64-gcc-4.5.3": re.compile(r"x86_64-(\*)?-netbsd(-unknown)? x86_64"), + "hello-ubuntu-14.04-x86_64-gcc-4.8.2": re.compile(r"x86_64-(\*)?-linux(-unknown)? x86_64"), + "hello-ubuntu-14.04-x86_64-clang-3.5pre": re.compile(r"x86_64-(\*)?-linux(-unknown)? x86_64"), + "hello-unknown-kalimba_arch4-kcc-36": re.compile(r"kalimba4-csr-(unknown|\*)(-unknown)? kalimba"), + "hello-unknown-kalimba_arch5-kcc-39": re.compile(r"kalimba5-csr-(unknown|\*)(-unknown)? kalimba"), + } + + for image_name in images: + file_name = os.path.abspath(os.path.join(os.path.dirname(__file__), "bin", image_name)) + expected_triple_and_arch_regex = images[image_name] + + self.runCmd("file {}".format(file_name)) + self.match("image list -t -A", [expected_triple_and_arch_regex]) + # Revert to the host platform after all of this is done + self.runCmd("platform select host") diff --git a/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-freebsd-10.0-x86_64-clang-3.3 b/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-freebsd-10.0-x86_64-clang-3.3 new file mode 100644 index 0000000000000000000000000000000000000000..cea323639b460c6545c1debf935a104988f6b89b GIT binary patch literal 7477 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 Dr$l(xl;1CD64gD`VBgv9FCc$BxeD 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 literal 0 HcmV?d00001 diff --git a/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-ubuntu-14.04-x86_64-gcc-4.8.2 b/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-ubuntu-14.04-x86_64-gcc-4.8.2 new file mode 100644 index 0000000000000000000000000000000000000000..01efbb061b73f7face31161516e64fb25f0a0a40 GIT binary patch literal 8056 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 literal 0 HcmV?d00001 diff --git a/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-unknown-kalimba_arch5-kcc-39 b/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello-unknown-kalimba_arch5-kcc-39 new file mode 100644 index 0000000000000000000000000000000000000000..f80268a08e5e4029837339dfc9bb29d3efe1626f GIT binary patch literal 28356 zcmeI53v?b=k>{&FeR@m&Bw3c^M^MX>B{^~Y$&dI!Oe8+ZgNZ&lr@x^?T;t(Lo&uUg@Go<+Ze{n)~%%v>sa|IDm9 zVK&!%tF%@tvO@R%TI!^}a1@royj- zQjkCDy%s=DJ;vqfA+T6z{1Lb1GJ!7pwn6 ztiCW-|Jzu7DpvoCSbcr0{wJ~e_E`O)SbcY_zB^W*jn!wbu;`b`E{<+3lYLk0erfFf z^4R^AvHSV4`_-}g^vJy*ZlYtPP2(Hw_O6n@x4-|3Z8oJq^})NN zziYR3wO_Zci9fY(l~LzE!#lG}e%Wr7+n=@PHul=MNga0Rdy}b6+Dk+2_N`ApXfIy< z9sB&;4%`2g|7`zp+a~-f_?#T)5o6y`{shs^4LM`CQgoA8falSG(=ig^%0# zV;%OrE5B(2uVn1R#qD;_ib4C?GdJ54`xo0E{_$h>lh;3Q9q+%wrY)Ui6F+j3{ll82 z_SJn8?Nj5vXcb>sWV2tp%KonL$5zH%?`OWeoG_chyw!75RIfT+<7FjnRR5i@uiib6 zvW{-AHf@Y_7G*YN4n?-^GRi!Pq_Kdqkg}0-BjqN_&6F<6CW?M<6^1lp31ul|8D%+z z>FRzf`ByMCT~)SHW>9>}c8Z?wpy-|OVgP#yBv> zfiVt@abS!CV;mUcz!(R{I55V6F%FDzV2lG}92n!k7zf5U@c%vsPTB#l;PiosMU)bX z6+P`0>S~!ap6yqatChX*icJqfUV*{2+45Y05dCPe2+IiY9*7K3H4W|-$ z!zt^v6qyb+wut+E+<(d*PnX2NmxRv=o?qkn z((u{i`Bk3VczzkWDtLa0=ae;DD#TIm#=6Z0v(qa}tah*PrR{dcK7FttZ1XyOHRhR& zxfME6cEHq5Gz-=n6#>0dcy}?l>bdHDjnxg4*~4`T<2y*(QoqhR;pGA9PP(?QI)06A zdpm6l{eQ@Ip3oS&ZJYlBbPr~my%L)o(s&RW1N_g2_#b#dp^!5vx*KAI-A-(v6F1K#5)d$)o6IJl3m3~3vYGw@P2 z$?S8MSUmcjH4LV$bcnPl^rMZdbg_+G&I4YXRd^cr4z3gJiZylIw_69aR+!o)ZP`E? z+S7Jz<`i$Xfspq+Xw$IVu?1%@vp9!CR z-V!M?ueR9KJ;GyF+BlweX5aTJX_F#TDrL*yU6T2?k9pMTN%r%(t|oh`-L41Tn#T1n zFa343Eg87S=1`^%kj2#}S`WRP_v>=IXLuZTK=N7b^z;sUmlw!0;+bSFn}9C()4hrl zQJ)_{qv|zZeuQmk5BpzA{|b9%xYE8%pU*k?Rp1Hi*#XAkdt_TtI=l=#kv)?H(Ze9m{g?AWRFg~0g6|P@^uRPXA`Yu>o z!?}yC_Nr-H9OC#;SP#9fUOHA0-is#<^f%i84?8K_ylzYSTP?3?y(OBY3!+)N5a^BC zZF6mT?{7fgw$Wa8VFGlHvvu$z9~&Z?*1?bbh8Fl_-nyRg)Jg7jTvuC%-)?P9Y4e-V zKk7^Ul$E>*pX$lL^Iw54Y3m%CU^fi^+}U*xJva3J0D8Z#=~Bj{IaLCz1hNtSOTxN* z>XOvuQ|E_ub>W&+M12GG;@^ M^?%jQCo|7$(rBhHEL~(ws`!gku!ehsq(XZ&6#V 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

Vf+Q-l;y|(RAvH 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 + +int main(int argc, char **argv) +{ + printf("Hello, world\n"); + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello.cpp b/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello.cpp new file mode 100644 index 000000000000..8c804005afe3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/object-file/bin/hello.cpp @@ -0,0 +1,8 @@ +#include + +int main(int argc, char **argv) +{ + printf("Hello, world\n"); + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/paths/TestPaths.py b/packages/Python/lldbsuite/test/functionalities/paths/TestPaths.py new file mode 100644 index 000000000000..f718b62f95ea --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/paths/TestPaths.py @@ -0,0 +1,48 @@ +""" +Test some lldb command abbreviations. +""" +from __future__ import print_function + + + +import lldb +import os +import time +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestPaths(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_paths (self): + '''Test to make sure no file names are set in the lldb.SBFileSpec objects returned by lldb.SBHostOS.GetLLDBPath() for paths that are directories''' + dir_path_types = [lldb.ePathTypeLLDBShlibDir, + lldb.ePathTypeSupportExecutableDir, + lldb.ePathTypeHeaderDir, + lldb.ePathTypePythonDir, + lldb.ePathTypeLLDBSystemPlugins, + lldb.ePathTypeLLDBUserPlugins, + lldb.ePathTypeLLDBTempSystemDir] + + for path_type in dir_path_types: + f = lldb.SBHostOS.GetLLDBPath(path_type); + # No directory path types should have the filename set + self.assertTrue (f.GetFilename() == None); + + @no_debug_info_test + def test_directory_doesnt_end_with_slash(self): + current_directory_spec = lldb.SBFileSpec(os.path.curdir) + current_directory_string = current_directory_spec.GetDirectory() + self.assertNotEqual(current_directory_string[-1:], '/') + pass + + @skipUnlessPlatform(["windows"]) + @no_debug_info_test + def test_windows_double_slash (self): + '''Test to check the path with double slash is handled correctly ''' + # Create a path and see if lldb gets the directory and file right + fspec = lldb.SBFileSpec("C:\\dummy1\\dummy2//unknown_file", True); + self.assertEqual(os.path.normpath(fspec.GetDirectory()), os.path.normpath("C:/dummy1/dummy2")); + self.assertEqual(fspec.GetFilename(), "unknown_file"); diff --git a/packages/Python/lldbsuite/test/functionalities/platform/TestPlatformCommand.py b/packages/Python/lldbsuite/test/functionalities/platform/TestPlatformCommand.py new file mode 100644 index 000000000000..6b3eedaf603a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/platform/TestPlatformCommand.py @@ -0,0 +1,65 @@ +""" +Test some lldb platform commands. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class PlatformCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_help_platform(self): + self.runCmd("help platform") + + @no_debug_info_test + def test_list(self): + self.expect("platform list", + patterns = ['^Available platforms:']) + + @no_debug_info_test + def test_process_list(self): + self.expect("platform process list", + substrs = ['PID', 'TRIPLE', 'NAME']) + + @no_debug_info_test + def test_process_info_with_no_arg(self): + """This is expected to fail and to return a proper error message.""" + self.expect("platform process info", error=True, + substrs = ['one or more process id(s) must be specified']) + + @no_debug_info_test + def test_status(self): + self.expect("platform status", + substrs = ['Platform', 'Triple', 'OS Version', 'Kernel', 'Hostname']) + + @no_debug_info_test + def test_shell(self): + """ Test that the platform shell command can invoke ls. """ + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-windows", triple): + self.expect("platform shell dir c:\\", substrs = ["Windows", "Program Files"]) + elif re.match(".*-.*-.*-android", triple): + self.expect("platform shell ls /", substrs = ["cache", "dev", "system"]) + else: + self.expect("platform shell ls /", substrs = ["dev", "tmp", "usr"]) + + @no_debug_info_test + def test_shell_builtin(self): + """ Test a shell built-in command (echo) """ + self.expect("platform shell echo hello lldb", + substrs = ["hello lldb"]) + + #FIXME: re-enable once platform shell -t can specify the desired timeout + @no_debug_info_test + def test_shell_timeout(self): + """ Test a shell built-in command (sleep) that times out """ + self.skipTest("due to taking too long to complete.") + self.expect("platform shell sleep 15", error=True, + substrs = ["error: timed out waiting for shell command to complete"]) diff --git a/packages/Python/lldbsuite/test/functionalities/plugins/commands/Makefile b/packages/Python/lldbsuite/test/functionalities/plugins/commands/Makefile new file mode 100644 index 000000000000..8af06446ecef --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/plugins/commands/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +DYLIB_CXX_SOURCES := plugin.cpp +DYLIB_NAME := plugin +DYLIB_ONLY := YES +MAKE_DSYM := NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/plugins/commands/TestPluginCommands.py b/packages/Python/lldbsuite/test/functionalities/plugins/commands/TestPluginCommands.py new file mode 100644 index 000000000000..b62c30bcf4e7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/plugins/commands/TestPluginCommands.py @@ -0,0 +1,58 @@ +""" +Test that plugins that load commands work correctly. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class PluginCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfNoSBHeaders + @skipIfHostIncompatibleWithRemote # Requires a compatible arch and platform to link against the host's built lldb lib. + @expectedFailureWindows("llvm.org/pr24778") + @no_debug_info_test + def test_load_plugin(self): + """Test that plugins that load commands work correctly.""" + + plugin_name = "plugin" + if sys.platform.startswith("darwin"): + plugin_lib_name = "lib%s.dylib" % plugin_name + else: + plugin_lib_name = "lib%s.so" % plugin_name + + # Invoke the library build rule. + self.buildLibrary("plugin.cpp", plugin_name) + + debugger = lldb.SBDebugger.Create() + + retobj = lldb.SBCommandReturnObject() + + retval = debugger.GetCommandInterpreter().HandleCommand("plugin load %s" % plugin_lib_name, retobj) + + retobj.Clear() + + retval = debugger.GetCommandInterpreter().HandleCommand("plugin_loaded_command child abc def ghi",retobj) + + if self.TraceOn(): + print(retobj.GetOutput()) + + self.expect(retobj,substrs = ['abc def ghi'], exe=False) + + retobj.Clear() + + # check that abbreviations work correctly in plugin commands. + retval = debugger.GetCommandInterpreter().HandleCommand("plugin_loaded_ ch abc def ghi",retobj) + + if self.TraceOn(): + print(retobj.GetOutput()) + + self.expect(retobj,substrs = ['abc def ghi'], exe=False) diff --git a/packages/Python/lldbsuite/test/functionalities/plugins/commands/plugin.cpp b/packages/Python/lldbsuite/test/functionalities/plugins/commands/plugin.cpp new file mode 100644 index 000000000000..be3d29325de1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/plugins/commands/plugin.cpp @@ -0,0 +1,62 @@ +//===-- plugin.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* +An example plugin for LLDB that provides a new foo command with a child subcommand +Compile this into a dylib foo.dylib and load by placing in appropriate locations on disk or +by typing plugin load foo.dylib at the LLDB command line +*/ + +#if defined (__APPLE__) +#include +#include +#include +#else +#include +#include +#include +#endif + +namespace lldb { + bool + PluginInitialize (lldb::SBDebugger debugger); +} + +class ChildCommand : public lldb::SBCommandPluginInterface +{ +public: + virtual bool + DoExecute (lldb::SBDebugger debugger, + char** command, + lldb::SBCommandReturnObject &result) + { + if (command) + { + const char* arg = *command; + while (arg) + { + result.Printf("%s ",arg); + arg = *(++command); + } + result.Printf("\n"); + return true; + } + return false; + } + +}; + +bool +lldb::PluginInitialize (lldb::SBDebugger debugger) +{ + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + lldb::SBCommand foo = interpreter.AddMultiwordCommand("plugin_loaded_command",NULL); + foo.AddCommand("child",new ChildCommand(),"a child of plugin_loaded_command"); + return true; +} diff --git a/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/Makefile b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/Makefile new file mode 100644 index 000000000000..cd9ca5c86d84 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/Makefile @@ -0,0 +1,3 @@ +LEVEL = ../../../make +C_SOURCES := main.c +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/TestPythonOSPlugin.py b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/TestPythonOSPlugin.py new file mode 100644 index 000000000000..ebd96d9d4c02 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/TestPythonOSPlugin.py @@ -0,0 +1,150 @@ +""" +Test that the Python operating system plugin works correctly +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class PluginPythonOSPlugin(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_python_os_plugin(self): + """Test that the Python operating system plugin works correctly""" + self.build() + self.run_python_os_funcionality() + + def run_python_os_step(self): + """Test that the Python operating system plugin works correctly when single stepping a virtual thread""" + self.build() + self.run_python_os_step() + + def verify_os_thread_registers(self, thread): + frame = thread.GetFrameAtIndex(0) + registers = frame.GetRegisters().GetValueAtIndex(0) + reg_value = thread.GetThreadID() + 1 + for reg in registers: + self.assertTrue(reg.GetValueAsUnsigned() == reg_value, "Verify the registers contains the correct value") + reg_value = reg_value + 1 + + def run_python_os_funcionality(self): + """Test that the Python operating system plugin works correctly""" + + # Set debugger into synchronous mode + self.dbg.SetAsync(False) + + # Create a target by the debugger. + cwd = os.getcwd() + exe = os.path.join(cwd, "a.out") + python_os_plugin_path = os.path.join(cwd, "operating_system.py") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Set breakpoints inside and outside methods that take pointers to the containing struct. + lldbutil.run_break_set_by_source_regexp (self, "// Set breakpoint here") + + # Register our shared libraries for remote targets so they get automatically uploaded + arguments = None + environment = None + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (arguments, environment, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Make sure there are no OS plug-in created thread when we first stop at our breakpoint in main + thread = process.GetThreadByID(0x111111111); + self.assertFalse (thread.IsValid(), "Make sure there is no thread 0x111111111 before we load the python OS plug-in"); + thread = process.GetThreadByID(0x222222222); + self.assertFalse (thread.IsValid(), "Make sure there is no thread 0x222222222 before we load the python OS plug-in"); + thread = process.GetThreadByID(0x333333333); + self.assertFalse (thread.IsValid(), "Make sure there is no thread 0x333333333 before we load the python OS plug-in"); + + + # Now load the python OS plug-in which should update the thread list and we should have + # OS plug-in created threads with the IDs: 0x111111111, 0x222222222, 0x333333333 + command = "settings set target.process.python-os-plugin-path '%s'" % python_os_plugin_path + self.dbg.HandleCommand(command) + + # Verify our OS plug-in threads showed up + thread = process.GetThreadByID(0x111111111); + self.assertTrue (thread.IsValid(), "Make sure there is a thread 0x111111111 after we load the python OS plug-in"); + self.verify_os_thread_registers(thread) + thread = process.GetThreadByID(0x222222222); + self.assertTrue (thread.IsValid(), "Make sure there is a thread 0x222222222 after we load the python OS plug-in"); + self.verify_os_thread_registers(thread) + thread = process.GetThreadByID(0x333333333); + self.assertTrue (thread.IsValid(), "Make sure there is a thread 0x333333333 after we load the python OS plug-in"); + self.verify_os_thread_registers(thread) + + # Now clear the OS plug-in path to make the OS plug-in created threads dissappear + self.dbg.HandleCommand("settings clear target.process.python-os-plugin-path") + + # Verify the threads are gone after unloading the python OS plug-in + thread = process.GetThreadByID(0x111111111); + self.assertFalse (thread.IsValid(), "Make sure there is no thread 0x111111111 after we unload the python OS plug-in"); + thread = process.GetThreadByID(0x222222222); + self.assertFalse (thread.IsValid(), "Make sure there is no thread 0x222222222 after we unload the python OS plug-in"); + thread = process.GetThreadByID(0x333333333); + self.assertFalse (thread.IsValid(), "Make sure there is no thread 0x333333333 after we unload the python OS plug-in"); + + def run_python_os_step(self): + """Test that the Python operating system plugin works correctly and allows single stepping of a virtual thread that is backed by a real thread""" + + # Set debugger into synchronous mode + self.dbg.SetAsync(False) + + # Create a target by the debugger. + cwd = os.getcwd() + exe = os.path.join(cwd, "a.out") + python_os_plugin_path = os.path.join(cwd, "operating_system2.py") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Set breakpoints inside and outside methods that take pointers to the containing struct. + lldbutil.run_break_set_by_source_regexp (self, "// Set breakpoint here") + + # Register our shared libraries for remote targets so they get automatically uploaded + arguments = None + environment = None + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (arguments, environment, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Make sure there are no OS plug-in created thread when we first stop at our breakpoint in main + thread = process.GetThreadByID(0x111111111); + self.assertFalse (thread.IsValid(), "Make sure there is no thread 0x111111111 before we load the python OS plug-in"); + + + # Now load the python OS plug-in which should update the thread list and we should have + # OS plug-in created threads with the IDs: 0x111111111, 0x222222222, 0x333333333 + command = "settings set target.process.python-os-plugin-path '%s'" % python_os_plugin_path + self.dbg.HandleCommand(command) + + # Verify our OS plug-in threads showed up + thread = process.GetThreadByID(0x111111111); + self.assertTrue (thread.IsValid(), "Make sure there is a thread 0x111111111 after we load the python OS plug-in"); + + frame = thread.GetFrameAtIndex(0) + self.assertTrue(frame.IsValid(), "Make sure we get a frame from thread 0x111111111") + line_entry = frame.GetLineEntry() + + self.assertTrue(line_entry.GetFileSpec().GetFilename() == 'main.c', "Make sure we stopped on line 5 in main.c") + self.assertTrue(line_entry.GetLine() == 5, "Make sure we stopped on line 5 in main.c") + + # Now single step thread 0x111111111 and make sure it does what we need it to + thread.StepOver() + + frame = thread.GetFrameAtIndex(0) + self.assertTrue(frame.IsValid(), "Make sure we get a frame from thread 0x111111111") + line_entry = frame.GetLineEntry() + + self.assertTrue(line_entry.GetFileSpec().GetFilename() == 'main.c', "Make sure we stepped from line 5 to line 6 in main.c") + self.assertTrue(line_entry.GetLine() == 6, "Make sure we stepped from line 5 to line 6 in main.c") diff --git a/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/main.c b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/main.c new file mode 100644 index 000000000000..faa6dd58ecd6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/main.c @@ -0,0 +1,7 @@ +#include + +int main (int argc, char const *argv[], char const *envp[]) +{ + puts("stop here"); // Set breakpoint here + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/operating_system.py b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/operating_system.py new file mode 100644 index 000000000000..536092e40b38 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/operating_system.py @@ -0,0 +1,90 @@ +#!/usr/bin/python + +import lldb +import struct + +class OperatingSystemPlugIn(object): + """Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class""" + + def __init__(self, process): + '''Initialization needs a valid.SBProcess object. + + This plug-in will get created after a live process is valid and has stopped for the + first time.''' + self.process = None + self.registers = None + self.threads = None + if type(process) is lldb.SBProcess and process.IsValid(): + self.process = process + self.threads = None # Will be an dictionary containing info for each thread + + def get_target(self): + # NOTE: Don't use "lldb.target" when trying to get your target as the "lldb.target" + # tracks the current target in the LLDB command interpreter which isn't the + # correct thing to use for this plug-in. + return self.process.target + + def create_thread(self, tid, context): + if tid == 0x444444444: + thread_info = { 'tid' : tid, 'name' : 'four' , 'queue' : 'queue4', 'state' : 'stopped', 'stop_reason' : 'none' } + self.threads.append(thread_info) + return thread_info + return None + + def get_thread_info(self): + if not self.threads: + # The sample dictionary below shows the values that can be returned for a thread + # tid => thread ID (mandatory) + # name => thread name (optional key/value pair) + # queue => thread dispatch queue name (optional key/value pair) + # state => thred state (mandatory, set to 'stopped' for now) + # stop_reason => thread stop reason. (mandatory, usually set to 'none') + # Possible values include: + # 'breakpoint' if the thread is stopped at a breakpoint + # 'none' thread is just stopped because the process is stopped + # 'trace' the thread just single stepped + # The usual value for this while threads are in memory is 'none' + # register_data_addr => the address of the register data in memory (optional key/value pair) + # Specifying this key/value pair for a thread will avoid a call to get_register_data() + # and can be used when your registers are in a thread context structure that is contiguous + # in memory. Don't specify this if your register layout in memory doesn't match the layout + # described by the dictionary returned from a call to the get_register_info() method. + self.threads = [ + { 'tid' : 0x111111111, 'name' : 'one' , 'queue' : 'queue1', 'state' : 'stopped', 'stop_reason' : 'breakpoint'}, + { 'tid' : 0x222222222, 'name' : 'two' , 'queue' : 'queue2', 'state' : 'stopped', 'stop_reason' : 'none' }, + { 'tid' : 0x333333333, 'name' : 'three', 'queue' : 'queue3', 'state' : 'stopped', 'stop_reason' : 'trace' } + ] + return self.threads + + def get_register_info(self): + if self.registers == None: + self.registers = dict() + self.registers['sets'] = ['GPR'] + self.registers['registers'] = [ + { 'name':'rax' , 'bitsize' : 64, 'offset' : 0, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 0, 'dwarf' : 0}, + { 'name':'rbx' , 'bitsize' : 64, 'offset' : 8, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 3, 'dwarf' : 3}, + { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 2, 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, + { 'name':'rdx' , 'bitsize' : 64, 'offset' : 24, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 1, 'dwarf' : 1, 'generic':'arg3', 'alt-name':'arg3', }, + { 'name':'rdi' , 'bitsize' : 64, 'offset' : 32, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 5, 'dwarf' : 5, 'generic':'arg1', 'alt-name':'arg1', }, + { 'name':'rsi' , 'bitsize' : 64, 'offset' : 40, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 4, 'dwarf' : 4, 'generic':'arg2', 'alt-name':'arg2', }, + { 'name':'rbp' , 'bitsize' : 64, 'offset' : 48, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 6, 'dwarf' : 6, 'generic':'fp' , 'alt-name':'fp', }, + { 'name':'rsp' , 'bitsize' : 64, 'offset' : 56, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 7, 'dwarf' : 7, 'generic':'sp' , 'alt-name':'sp', }, + { 'name':'r8' , 'bitsize' : 64, 'offset' : 64, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 8, 'dwarf' : 8, 'generic':'arg5', 'alt-name':'arg5', }, + { 'name':'r9' , 'bitsize' : 64, 'offset' : 72, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 9, 'dwarf' : 9, 'generic':'arg6', 'alt-name':'arg6', }, + { 'name':'r10' , 'bitsize' : 64, 'offset' : 80, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 10, 'dwarf' : 10}, + { 'name':'r11' , 'bitsize' : 64, 'offset' : 88, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 11, 'dwarf' : 11}, + { 'name':'r12' , 'bitsize' : 64, 'offset' : 96, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 12, 'dwarf' : 12}, + { 'name':'r13' , 'bitsize' : 64, 'offset' : 104, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 13, 'dwarf' : 13}, + { 'name':'r14' , 'bitsize' : 64, 'offset' : 112, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 14, 'dwarf' : 14}, + { 'name':'r15' , 'bitsize' : 64, 'offset' : 120, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 15, 'dwarf' : 15}, + { 'name':'rip' , 'bitsize' : 64, 'offset' : 128, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 16, 'dwarf' : 16, 'generic':'pc', 'alt-name':'pc' }, + { 'name':'rflags' , 'bitsize' : 64, 'offset' : 136, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'generic':'flags', 'alt-name':'flags' }, + { 'name':'cs' , 'bitsize' : 64, 'offset' : 144, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + { 'name':'fs' , 'bitsize' : 64, 'offset' : 152, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + { 'name':'gs' , 'bitsize' : 64, 'offset' : 160, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + ] + return self.registers + + def get_register_data(self, tid): + return struct.pack('21Q',tid + 1,tid + 2,tid + 3,tid + 4,tid + 5,tid + 6,tid + 7,tid + 8,tid + 9,tid + 10,tid + 11,tid + 12,tid + 13,tid + 14,tid + 15,tid + 16,tid + 17,tid + 18,tid + 19,tid + 20,tid + 21); + diff --git a/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/operating_system2.py b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/operating_system2.py new file mode 100644 index 000000000000..7a9435d44ed4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/plugins/python_os_plugin/operating_system2.py @@ -0,0 +1,88 @@ +#!/usr/bin/python + +import lldb +import struct + +class OperatingSystemPlugIn(object): + """Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class""" + + def __init__(self, process): + '''Initialization needs a valid.SBProcess object. + + This plug-in will get created after a live process is valid and has stopped for the + first time.''' + self.process = None + self.registers = None + self.threads = None + if type(process) is lldb.SBProcess and process.IsValid(): + self.process = process + self.threads = None # Will be an dictionary containing info for each thread + + def get_target(self): + # NOTE: Don't use "lldb.target" when trying to get your target as the "lldb.target" + # tracks the current target in the LLDB command interpreter which isn't the + # correct thing to use for this plug-in. + return self.process.target + + def create_thread(self, tid, context): + if tid == 0x444444444: + thread_info = { 'tid' : tid, 'name' : 'four' , 'queue' : 'queue4', 'state' : 'stopped', 'stop_reason' : 'none' } + self.threads.append(thread_info) + return thread_info + return None + + def get_thread_info(self): + if not self.threads: + # The sample dictionary below shows the values that can be returned for a thread + # tid => thread ID (mandatory) + # name => thread name (optional key/value pair) + # queue => thread dispatch queue name (optional key/value pair) + # state => thred state (mandatory, set to 'stopped' for now) + # stop_reason => thread stop reason. (mandatory, usually set to 'none') + # Possible values include: + # 'breakpoint' if the thread is stopped at a breakpoint + # 'none' thread is just stopped because the process is stopped + # 'trace' the thread just single stepped + # The usual value for this while threads are in memory is 'none' + # register_data_addr => the address of the register data in memory (optional key/value pair) + # Specifying this key/value pair for a thread will avoid a call to get_register_data() + # and can be used when your registers are in a thread context structure that is contiguous + # in memory. Don't specify this if your register layout in memory doesn't match the layout + # described by the dictionary returned from a call to the get_register_info() method. + self.threads = [ + { 'tid' : 0x111111111, 'core' : 0 } + ] + return self.threads + + def get_register_info(self): + if self.registers == None: + self.registers = dict() + self.registers['sets'] = ['GPR'] + self.registers['registers'] = [ + { 'name':'rax' , 'bitsize' : 64, 'offset' : 0, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 0, 'dwarf' : 0}, + { 'name':'rbx' , 'bitsize' : 64, 'offset' : 8, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 3, 'dwarf' : 3}, + { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 2, 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, + { 'name':'rdx' , 'bitsize' : 64, 'offset' : 24, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 1, 'dwarf' : 1, 'generic':'arg3', 'alt-name':'arg3', }, + { 'name':'rdi' , 'bitsize' : 64, 'offset' : 32, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 5, 'dwarf' : 5, 'generic':'arg1', 'alt-name':'arg1', }, + { 'name':'rsi' , 'bitsize' : 64, 'offset' : 40, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 4, 'dwarf' : 4, 'generic':'arg2', 'alt-name':'arg2', }, + { 'name':'rbp' , 'bitsize' : 64, 'offset' : 48, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 6, 'dwarf' : 6, 'generic':'fp' , 'alt-name':'fp', }, + { 'name':'rsp' , 'bitsize' : 64, 'offset' : 56, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 7, 'dwarf' : 7, 'generic':'sp' , 'alt-name':'sp', }, + { 'name':'r8' , 'bitsize' : 64, 'offset' : 64, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 8, 'dwarf' : 8, 'generic':'arg5', 'alt-name':'arg5', }, + { 'name':'r9' , 'bitsize' : 64, 'offset' : 72, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 9, 'dwarf' : 9, 'generic':'arg6', 'alt-name':'arg6', }, + { 'name':'r10' , 'bitsize' : 64, 'offset' : 80, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 10, 'dwarf' : 10}, + { 'name':'r11' , 'bitsize' : 64, 'offset' : 88, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 11, 'dwarf' : 11}, + { 'name':'r12' , 'bitsize' : 64, 'offset' : 96, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 12, 'dwarf' : 12}, + { 'name':'r13' , 'bitsize' : 64, 'offset' : 104, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 13, 'dwarf' : 13}, + { 'name':'r14' , 'bitsize' : 64, 'offset' : 112, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 14, 'dwarf' : 14}, + { 'name':'r15' , 'bitsize' : 64, 'offset' : 120, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 15, 'dwarf' : 15}, + { 'name':'rip' , 'bitsize' : 64, 'offset' : 128, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 16, 'dwarf' : 16, 'generic':'pc', 'alt-name':'pc' }, + { 'name':'rflags' , 'bitsize' : 64, 'offset' : 136, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'generic':'flags', 'alt-name':'flags' }, + { 'name':'cs' , 'bitsize' : 64, 'offset' : 144, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + { 'name':'fs' , 'bitsize' : 64, 'offset' : 152, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + { 'name':'gs' , 'bitsize' : 64, 'offset' : 160, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + ] + return self.registers + + def get_register_data(self, tid): + return struct.pack('21Q',tid + 1,tid + 2,tid + 3,tid + 4,tid + 5,tid + 6,tid + 7,tid + 8,tid + 9,tid + 10,tid + 11,tid + 12,tid + 13,tid + 14,tid + 15,tid + 16,tid + 17,tid + 18,tid + 19,tid + 20,tid + 21); + diff --git a/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/Makefile b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/Makefile new file mode 100644 index 000000000000..b3034c12abd9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + diff --git a/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/TestMiniDump.py b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/TestMiniDump.py new file mode 100644 index 000000000000..53888502a3aa --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/TestMiniDump.py @@ -0,0 +1,128 @@ +""" +Test basics of mini dump debugging. +""" + +from __future__ import print_function + + + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class MiniDumpTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessWindows # for now mini-dump debugging is limited to Windows hosts + @no_debug_info_test + def test_process_info_in_mini_dump(self): + """Test that lldb can read the process information from the minidump.""" + # target create -c fizzbuzz_no_heap.dmp + self.dbg.CreateTarget("") + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("fizzbuzz_no_heap.dmp") + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertEqual(self.process.GetNumThreads(), 1) + self.assertEqual(self.process.GetProcessID(), 4440) + + @skipUnlessWindows # for now mini-dump debugging is limited to Windows hosts + @no_debug_info_test + def test_thread_info_in_mini_dump(self): + """Test that lldb can read the thread information from the minidump.""" + # target create -c fizzbuzz_no_heap.dmp + self.dbg.CreateTarget("") + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("fizzbuzz_no_heap.dmp") + # This process crashed due to an access violation (0xc0000005) in its one and only thread. + self.assertEqual(self.process.GetNumThreads(), 1) + thread = self.process.GetThreadAtIndex(0) + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonException) + stop_description = thread.GetStopDescription(256); + self.assertTrue("0xc0000005" in stop_description); + + @skipUnlessWindows # for now mini-dump debugging is limited to Windows hosts + @no_debug_info_test + def test_stack_info_in_mini_dump(self): + """Test that we can see a trivial stack in a VS-generate mini dump.""" + # target create -c fizzbuzz_no_heap.dmp + self.dbg.CreateTarget("") + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("fizzbuzz_no_heap.dmp") + self.assertEqual(self.process.GetNumThreads(), 1) + thread = self.process.GetThreadAtIndex(0) + # The crash is in main, so there should be one frame on the stack. + self.assertEqual(thread.GetNumFrames(), 1) + frame = thread.GetFrameAtIndex(0) + self.assertTrue(frame.IsValid()) + pc = frame.GetPC() + eip = frame.FindRegister("pc") + self.assertTrue(eip.IsValid()) + self.assertEqual(pc, eip.GetValueAsUnsigned()) + + @skipUnlessWindows + @not_remote_testsuite_ready + def test_deeper_stack_in_mini_dump(self): + """Test that we can examine a more interesting stack in a mini dump.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + core = os.path.join(os.getcwd(), "core.dmp") + try: + # Set a breakpoint and capture a mini dump. + target = self.dbg.CreateTarget(exe) + breakpoint = target.BreakpointCreateByName("bar") + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertEqual(process.GetState(), lldb.eStateStopped) + self.assertTrue(process.SaveCore(core)) + self.assertTrue(os.path.isfile(core)) + self.assertTrue(process.Kill().Success()) + + # Launch with the mini dump, and inspect the stack. + target = self.dbg.CreateTarget(None) + process = target.LoadCore(core) + thread = process.GetThreadAtIndex(0) + + expected_stack = { 0: 'bar', 1: 'foo', 2: 'main' } + self.assertEqual(thread.GetNumFrames(), len(expected_stack)) + for index, name in expected_stack.iteritems(): + frame = thread.GetFrameAtIndex(index) + self.assertTrue(frame.IsValid()) + function_name = frame.GetFunctionName() + self.assertTrue(name in function_name) + + finally: + # Clean up the mini dump file. + self.assertTrue(self.dbg.DeleteTarget(target)) + if (os.path.isfile(core)): + os.unlink(core) + + @skipUnlessWindows + @not_remote_testsuite_ready + def test_local_variables_in_mini_dump(self): + """Test that we can examine local variables in a mini dump.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + core = os.path.join(os.getcwd(), "core.dmp") + try: + # Set a breakpoint and capture a mini dump. + target = self.dbg.CreateTarget(exe) + breakpoint = target.BreakpointCreateByName("bar") + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertEqual(process.GetState(), lldb.eStateStopped) + self.assertTrue(process.SaveCore(core)) + self.assertTrue(os.path.isfile(core)) + self.assertTrue(process.Kill().Success()) + + # Launch with the mini dump, and inspect a local variable. + target = self.dbg.CreateTarget(None) + process = target.LoadCore(core) + thread = process.GetThreadAtIndex(0) + frame = thread.GetFrameAtIndex(0) + value = frame.EvaluateExpression('x') + self.assertEqual(value.GetValueAsSigned(), 3) + + finally: + # Clean up the mini dump file. + self.assertTrue(self.dbg.DeleteTarget(target)) + if (os.path.isfile(core)): + os.unlink(core) diff --git a/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/fizzbuzz.cpp b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/fizzbuzz.cpp new file mode 100644 index 000000000000..eb6476bfd9a9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/fizzbuzz.cpp @@ -0,0 +1,31 @@ +// A sample program for getting minidumps on Windows. + +#include + +bool +fizz(int x) +{ + return x % 3 == 0; +} + +bool +buzz(int x) +{ + return x % 5 == 0; +} + +int +main() +{ + int *buggy = 0; + + for (int i = 1; i <= 100; ++i) + { + if (fizz(i)) std::cout << "fizz"; + if (buzz(i)) std::cout << "buzz"; + if (!fizz(i) && !buzz(i)) std::cout << i; + std::cout << '\n'; + } + + return *buggy; +} diff --git a/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/fizzbuzz_no_heap.dmp b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/fizzbuzz_no_heap.dmp new file mode 100644 index 0000000000000000000000000000000000000000..19008c91fc3ea056d7b1e10bcf2079bfdf51ed08 GIT binary patch literal 6297 zcmeHKeN0nV6h8&TPlgOZHxV1g%(6K`DKSQ!AQToOjM{?8Rve{$LZL0#_Q52|5ZvNY z(YZMx`(v5^Y)dqoX71AwmWYdsBx8wLNR}o3Ap%MwZf0VPbiZ?7A74SpG&5V)`*QBN z_uO-S=iKw|JMX=GL%#9dk560Y6Ok6gZU5nsB3i7B!@<%)g<(W7;CY|}7)O8~1iu8D zgK;vbl0Y8V6Nbb195;HiuqK=7jEmK|!f zN&!bYz7M()Y)O8+;9`Fh7asAr8vz``O-D*eja940&~vYmhgbg82}uGD++G|Cws{%% zXAz{CpozAu7!E;_Vb=$vH+pz!+O{!P**{VgUfM1lOUdIeZSXaMYsvw+|Ix!^>&=u>|UauUwp4^?s>7R6d)plo@;_ zAP6%mhllD!b&*ILtT)vd@w4y|D4W)ig^I{4G`$#GQEzOxxB~f5F)U=D8q8%;hun~{ z);3^zA+w^gD0NFly>dWPOKFq}d_A;`z^I`rVB3OrTEuwd1PbtkTrAXlDnWv{c_SHq(RC=w9#*v&YK z;crB-g>uM5FQLz)EZT&7kuw1)YZfFQyt{;V3*H)Lq)MG z+3PGtUdiLNSY0-o)#IzQ7;0>?OLF_XmTeBNY;|hQKH2W5(Wa&9^cJJ1rb@D5ZKtE5 zp@t*ssDu=qxg>DRpa$BB}+@<;r)D(wh05I3Z_ zDg=N4WHSI7CIKq2lY0F3j5oYiCoTR*C;XYHhR&Pgal(j>cJGN7FNE4Q18G|f)H6IP zJ|9E^uy^L;KelO#7@r&?!#XO1eL+k-oze6Pn`F#gjq#KUUZr0w^Cw9y#?9?=J&#M$ z%NzoLJIookzwU9FX#@1qZ|YFC1A!df?s~`krxRK20OUom7eAB1cv!Bs`^nYy5D#S_ Slz~tNLKz5UAe4cJ&%j@zKl0cB literal 0 HcmV?d00001 diff --git a/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/main.cpp b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/main.cpp new file mode 100644 index 000000000000..4037ea522cac --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/postmortem/minidump/main.cpp @@ -0,0 +1,21 @@ +int global = 42; + +int +bar(int x) +{ + int y = 4*x + global; + return y; +} + +int +foo(int x) +{ + int y = 2*bar(3*x); + return y; +} + +int +main() +{ + return 0 * foo(1); +} diff --git a/packages/Python/lldbsuite/test/functionalities/process_attach/Makefile b/packages/Python/lldbsuite/test/functionalities/process_attach/Makefile new file mode 100644 index 000000000000..a964853f534b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_attach/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +EXE := ProcessAttach + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/process_attach/TestProcessAttach.py b/packages/Python/lldbsuite/test/functionalities/process_attach/TestProcessAttach.py new file mode 100644 index 000000000000..83906b546300 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_attach/TestProcessAttach.py @@ -0,0 +1,58 @@ +""" +Test process attach. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +exe_name = "ProcessAttach" # Must match Makefile + +class ProcessAttachTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfiOSSimulator + def test_attach_to_process_by_id(self): + """Test attach by process id""" + self.build() + exe = os.path.join(os.getcwd(), exe_name) + + # Spawn a new process + popen = self.spawnSubprocess(exe) + self.addTearDownHook(self.cleanupSubprocesses) + + self.runCmd("process attach -p " + str(popen.pid)) + + target = self.dbg.GetSelectedTarget() + + process = target.GetProcess() + self.assertTrue(process, PROCESS_IS_VALID) + + def test_attach_to_process_by_name(self): + """Test attach by process name""" + self.build() + exe = os.path.join(os.getcwd(), exe_name) + + # Spawn a new process + popen = self.spawnSubprocess(exe) + self.addTearDownHook(self.cleanupSubprocesses) + + self.runCmd("process attach -n " + exe_name) + + target = self.dbg.GetSelectedTarget() + + process = target.GetProcess() + self.assertTrue(process, PROCESS_IS_VALID) + + def tearDown(self): + # Destroy process before TestBase.tearDown() + self.dbg.GetSelectedTarget().GetProcess().Destroy() + + # Call super's tearDown(). + TestBase.tearDown(self) diff --git a/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/Makefile b/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/Makefile new file mode 100644 index 000000000000..87600bbccf9d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +EXE := AttachDenied + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/TestAttachDenied.py b/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/TestAttachDenied.py new file mode 100644 index 000000000000..ed9d58f90888 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/TestAttachDenied.py @@ -0,0 +1,60 @@ +""" +Test denied process attach. +""" + +from __future__ import print_function + + + +import os +import time +import lldb +from lldbsuite.test.lldbtest import * + +exe_name = 'AttachDenied' # Must match Makefile + +class AttachDeniedTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def run_platform_command(self, cmd): + platform = self.dbg.GetSelectedPlatform() + shell_command = lldb.SBPlatformShellCommand(cmd) + err = platform.Run(shell_command) + return (err, shell_command.GetStatus(), shell_command.GetOutput()) + + @skipIfWindows + @skipIfiOSSimulator + def test_attach_to_process_by_id_denied(self): + """Test attach by process id denied""" + self.build() + exe = os.path.join(os.getcwd(), exe_name) + + # Use a file as a synchronization point between test and inferior. + pid_file_path = lldbutil.append_to_process_working_directory( + "pid_file_%d" % (int(time.time()))) + self.addTearDownHook(lambda: self.run_platform_command("rm %s" % (pid_file_path))) + + # Spawn a new process + popen = self.spawnSubprocess(exe, [pid_file_path]) + self.addTearDownHook(self.cleanupSubprocesses) + + max_attempts = 5 + for i in range(max_attempts): + err, retcode, msg = self.run_platform_command("ls %s" % pid_file_path) + if err.Success() and retcode == 0: + break + else: + print(msg) + if i < max_attempts: + # Exponential backoff! + time.sleep(pow(2, i) * 0.25) + else: + self.fail("Child PID file %s not found even after %d attempts." % (pid_file_path, max_attempts)) + err, retcode, pid = self.run_platform_command("cat %s" % (pid_file_path)) + self.assertTrue(err.Success() and retcode == 0, + "Failed to read file %s: %s, retcode: %d" % (pid_file_path, err.GetCString(), retcode)) + + self.expect('process attach -p ' + pid, + startstr = 'error: attach failed:', + error = True) diff --git a/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/main.cpp b/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/main.cpp new file mode 100644 index 000000000000..ff1fccae4b1c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_attach/attach_denied/main.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(PTRACE_ATTACH) +#define ATTACH_REQUEST PTRACE_ATTACH +#define DETACH_REQUEST PTRACE_DETACH +#elif defined(PT_ATTACH) +#define ATTACH_REQUEST PT_ATTACH +#define DETACH_REQUEST PT_DETACH +#else +#error "Unsupported platform" +#endif + +bool writePid (const char* file_name, const pid_t pid) +{ + char *tmp_file_name = (char *)malloc(strlen(file_name) + 16); + strcpy(tmp_file_name, file_name); + strcat(tmp_file_name, "_tmp"); + int fd = open (tmp_file_name, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd == -1) + { + fprintf (stderr, "open(%s) failed: %s\n", tmp_file_name, strerror (errno)); + free(tmp_file_name); + return false; + } + char buffer[64]; + snprintf (buffer, sizeof(buffer), "%ld", (long)pid); + + bool res = true; + if (write (fd, buffer, strlen (buffer)) == -1) + { + fprintf (stderr, "write(%s) failed: %s\n", buffer, strerror (errno)); + res = false; + } + close (fd); + + if (rename (tmp_file_name, file_name) == -1) + { + fprintf (stderr, "rename(%s, %s) failed: %s\n", tmp_file_name, file_name, strerror (errno)); + res = false; + } + free(tmp_file_name); + + return res; +} + +void signal_handler (int) +{ +} + +int main (int argc, char const *argv[]) +{ + if (argc < 2) + { + fprintf (stderr, "invalid number of command line arguments\n"); + return 1; + } + + const pid_t pid = fork (); + if (pid == -1) + { + fprintf (stderr, "fork failed: %s\n", strerror (errno)); + return 1; + } + + if (pid > 0) + { + // Make pause call to return when a signal is received. Normally this happens when the + // test runner tries to terminate us. + signal (SIGHUP, signal_handler); + signal (SIGTERM, signal_handler); + if (ptrace (ATTACH_REQUEST, pid, NULL, 0) == -1) + { + fprintf (stderr, "ptrace(ATTACH) failed: %s\n", strerror (errno)); + } + else + { + if (writePid (argv[1], pid)) + pause (); // Waiting for the debugger trying attach to the child. + + if (ptrace (DETACH_REQUEST, pid, NULL, 0) != 0) + fprintf (stderr, "ptrace(DETACH) failed: %s\n", strerror (errno)); + } + + kill (pid, SIGTERM); + int status = 0; + if (waitpid (pid, &status, 0) == -1) + fprintf (stderr, "waitpid failed: %s\n", strerror (errno)); + } + else + { + // child inferior. + pause (); + } + + printf ("Exiting now\n"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/process_attach/main.cpp b/packages/Python/lldbsuite/test/functionalities/process_attach/main.cpp new file mode 100644 index 000000000000..8021feac5c71 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_attach/main.cpp @@ -0,0 +1,37 @@ +#include + +#if defined(__linux__) +#include +#endif + +#include +#include + +int main(int argc, char const *argv[]) { + int temp; +#if defined(__linux__) + // Immediately enable any ptracer so that we can allow the stub attach + // operation to succeed. Some Linux kernels are locked down so that + // only an ancestor process can be a ptracer of a process. This disables that + // restriction. Without it, attach-related stub tests will fail. +#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY) + int prctl_result; + + // For now we execute on best effort basis. If this fails for + // some reason, so be it. + prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); + (void) prctl_result; +#endif +#endif + + // Waiting to be attached by the debugger. + temp = 0; + + while (temp < 30) // Waiting to be attached... + { + std::this_thread::sleep_for(std::chrono::seconds(2)); + temp++; + } + + printf("Exiting now\n"); +} diff --git a/packages/Python/lldbsuite/test/functionalities/process_group/Makefile b/packages/Python/lldbsuite/test/functionalities/process_group/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_group/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/process_group/TestChangeProcessGroup.py b/packages/Python/lldbsuite/test/functionalities/process_group/TestChangeProcessGroup.py new file mode 100644 index 000000000000..25be37b2b12d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_group/TestChangeProcessGroup.py @@ -0,0 +1,107 @@ +"""Test that we handle inferiors which change their process group""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + + +class ChangeProcessGroupTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.c. + self.line = line_number('main.c', '// Set breakpoint here') + + @skipIfFreeBSD # Times out on FreeBSD llvm.org/pr23731 + @skipIfWindows # setpgid call does not exist on Windows + @expectedFailureAndroid("http://llvm.org/pr23762", api_levels=[16]) + def test_setpgid(self): + self.build() + exe = os.path.join(os.getcwd(), 'a.out') + + # Use a file as a synchronization point between test and inferior. + pid_file_path = lldbutil.append_to_process_working_directory( + "pid_file_%d" % (int(time.time()))) + self.addTearDownHook(lambda: self.run_platform_command("rm %s" % (pid_file_path))) + + popen = self.spawnSubprocess(exe, [pid_file_path]) + self.addTearDownHook(self.cleanupSubprocesses) + + max_attempts = 5 + for i in range(max_attempts): + err, retcode, msg = self.run_platform_command("ls %s" % pid_file_path) + if err.Success() and retcode == 0: + break + else: + print(msg) + if i < max_attempts: + # Exponential backoff! + time.sleep(pow(2, i) * 0.25) + else: + self.fail("Child PID file %s not found even after %d attempts." % (pid_file_path, max_attempts)) + + err, retcode, pid = self.run_platform_command("cat %s" % (pid_file_path)) + + self.assertTrue(err.Success() and retcode == 0, + "Failed to read file %s: %s, retcode: %d" % (pid_file_path, err.GetCString(), retcode)) + + # make sure we cleanup the forked child also + def cleanupChild(): + if lldb.remote_platform: + lldb.remote_platform.Kill(int(pid)) + else: + if os.path.exists("/proc/" + pid): + os.kill(int(pid), signal.SIGKILL) + self.addTearDownHook(cleanupChild) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + listener = lldb.SBListener("my.attach.listener") + error = lldb.SBError() + process = target.AttachToProcessWithID(listener, int(pid), error) + self.assertTrue(error.Success() and process, PROCESS_IS_VALID) + + # set a breakpoint just before the setpgid() call + lldbutil.run_break_set_by_file_and_line(self, 'main.c', self.line, num_expected_locations=-1) + + thread = process.GetSelectedThread() + + # release the child from its loop + value = thread.GetSelectedFrame().EvaluateExpression("release_child_flag = 1") + self.assertTrue(value.IsValid() and value.GetValueAsUnsigned(0) == 1); + process.Continue() + + # make sure the child's process group id is different from its pid + value = thread.GetSelectedFrame().EvaluateExpression("(int)getpgid(0)") + self.assertTrue(value.IsValid()) + self.assertNotEqual(value.GetValueAsUnsigned(0), int(pid)); + + # step over the setpgid() call + thread.StepOver() + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonPlanComplete) + + # verify that the process group has been set correctly + # this also checks that we are still in full control of the child + value = thread.GetSelectedFrame().EvaluateExpression("(int)getpgid(0)") + self.assertTrue(value.IsValid()) + self.assertEqual(value.GetValueAsUnsigned(0), int(pid)); + + # run to completion + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + + def run_platform_command(self, cmd): + platform = self.dbg.GetSelectedPlatform() + shell_command = lldb.SBPlatformShellCommand(cmd) + err = platform.Run(shell_command) + return (err, shell_command.GetStatus(), shell_command.GetOutput()) diff --git a/packages/Python/lldbsuite/test/functionalities/process_group/main.c b/packages/Python/lldbsuite/test/functionalities/process_group/main.c new file mode 100644 index 000000000000..c730c629e8b0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_group/main.c @@ -0,0 +1,86 @@ +#include +#include +#include + +#if defined(__linux__) +#include +#endif + +volatile int release_child_flag = 0; + +int main(int argc, char const *argv[]) +{ + pid_t child = fork(); + if (child == -1) + { + perror("fork"); + return 1; + } + + if (child > 0) + { // parent + if (argc < 2) + { + fprintf(stderr, "Need pid filename.\n"); + return 2; + } + + // Let the test suite know the child's pid. + FILE *pid_file = fopen(argv[1], "w"); + if (pid_file == NULL) + { + perror("fopen"); + return 3; + } + + fprintf(pid_file, "%d\n", child); + if (fclose(pid_file) == EOF) + { + perror("fclose"); + return 4; + } + + // And wait for the child to finish it's work. + int status = 0; + pid_t wpid = wait(&status); + if (wpid == -1) + { + perror("wait"); + return 5; + } + if (wpid != child) + { + fprintf(stderr, "wait() waited for wrong child\n"); + return 6; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + { + fprintf(stderr, "child did not exit correctly\n"); + return 7; + } + } + else + { // child +#if defined(__linux__) + // Immediately enable any ptracer so that we can allow the stub attach + // operation to succeed. Some Linux kernels are locked down so that + // only an ancestor process can be a ptracer of a process. This disables that + // restriction. Without it, attach-related stub tests will fail. +#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY) + // For now we execute on best effort basis. If this fails for + // some reason, so be it. + const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); + (void) prctl_result; +#endif +#endif + + while (! release_child_flag) // Wait for debugger to attach + sleep(1); + + printf("Child's previous process group is: %d\n", getpgid(0)); + setpgid(0, 0); // Set breakpoint here + printf("Child's process group set to: %d\n", getpgid(0)); + } + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/process_launch/Makefile b/packages/Python/lldbsuite/test/functionalities/process_launch/Makefile new file mode 100644 index 000000000000..313da706b4a2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_launch/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp +#CXX_SOURCES := print-cwd.cpp + +include $(LEVEL)/Makefile.rules + diff --git a/packages/Python/lldbsuite/test/functionalities/process_launch/TestProcessLaunch.py b/packages/Python/lldbsuite/test/functionalities/process_launch/TestProcessLaunch.py new file mode 100644 index 000000000000..3131000be428 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_launch/TestProcessLaunch.py @@ -0,0 +1,207 @@ +""" +Test lldb process launch flags. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class ProcessLaunchTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # disable "There is a running process, kill it and restart?" prompt + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + @not_remote_testsuite_ready + def test_io (self): + """Test that process launch I/O redirection flags work properly.""" + self.build () + exe = os.path.join (os.getcwd(), "a.out") + self.expect("file " + exe, + patterns = [ "Current executable set to .*a.out" ]) + + in_file = "input-file.txt" + out_file = "output-test.out" + err_file = "output-test.err" + + # Make sure the output files do not exist before launching the process + try: + os.remove (out_file) + except OSError: + pass + + try: + os.remove (err_file) + except OSError: + pass + + launch_command = "process launch -i " + in_file + " -o " + out_file + " -e " + err_file + + if lldb.remote_platform: + self.runCmd('platform put-file "{local}" "{remote}"'.format( + local=in_file, remote=in_file)) + + self.expect (launch_command, + patterns = [ "Process .* launched: .*a.out" ]) + + if lldb.remote_platform: + self.runCmd('platform get-file "{remote}" "{local}"'.format( + remote=out_file, local=out_file)) + self.runCmd('platform get-file "{remote}" "{local}"'.format( + remote=err_file, local=err_file)) + + success = True + err_msg = "" + + # Check to see if the 'stdout' file was created + try: + out_f = open (out_file) + except IOError: + success = False + err_msg = err_msg + " ERROR: stdout file was not created.\n" + else: + # Check to see if the 'stdout' file contains the right output + line = out_f.readline (); + if line != "This should go to stdout.\n": + success = False + err_msg = err_msg + " ERROR: stdout file does not contain correct output.\n" + out_f.close(); + + # Try to delete the 'stdout' file + try: + os.remove (out_file) + except OSError: + pass + + # Check to see if the 'stderr' file was created + try: + err_f = open (err_file) + except IOError: + success = False + err_msg = err_msg + " ERROR: stderr file was not created.\n" + else: + # Check to see if the 'stderr' file contains the right output + line = err_f.readline () + if line != "This should go to stderr.\n": + success = False + err_msg = err_msg + " ERROR: stderr file does not contain correct output.\n\ +" + err_f.close() + + # Try to delete the 'stderr' file + try: + os.remove (err_file) + except OSError: + pass + + if not success: + self.fail (err_msg) + + # rdar://problem/9056462 + # The process launch flag '-w' for setting the current working directory not working? + @not_remote_testsuite_ready + @expectedFailureLinux("llvm.org/pr20265") + def test_set_working_dir (self): + """Test that '-w dir' sets the working dir when running the inferior.""" + d = {'CXX_SOURCES' : 'print_cwd.cpp'} + self.build(dictionary=d) + self.setTearDownCleanup(d) + exe = os.path.join (os.getcwd(), "a.out") + self.runCmd("file " + exe) + + mywd = 'my_working_dir' + out_file_name = "my_working_dir_test.out" + err_file_name = "my_working_dir_test.err" + + my_working_dir_path = os.path.join(os.getcwd(), mywd) + out_file_path = os.path.join(my_working_dir_path, out_file_name) + err_file_path = os.path.join(my_working_dir_path, err_file_name) + + # Make sure the output files do not exist before launching the process + try: + os.remove (out_file_path) + os.remove (err_file_path) + except OSError: + pass + + # Check that we get an error when we have a nonexisting path + launch_command = "process launch -w %s -o %s -e %s" % (my_working_dir_path + 'z', + out_file_path, + err_file_path) + + self.expect(launch_command, error=True, + patterns = ["error:.* No such file or directory: %sz" % my_working_dir_path]) + + # Really launch the process + launch_command = "process launch -w %s -o %s -e %s" % (my_working_dir_path, + out_file_path, + err_file_path) + + self.expect(launch_command, + patterns = [ "Process .* launched: .*a.out" ]) + + success = True + err_msg = "" + + # Check to see if the 'stdout' file was created + try: + out_f = open(out_file_path) + except IOError: + success = False + err_msg = err_msg + "ERROR: stdout file was not created.\n" + else: + # Check to see if the 'stdout' file contains the right output + line = out_f.readline(); + if self.TraceOn(): + print("line:", line) + if not re.search(mywd, line): + success = False + err_msg = err_msg + "The current working directory was not set correctly.\n" + out_f.close(); + + # Try to delete the 'stdout' and 'stderr' files + try: + os.remove(out_file_path) + os.remove(err_file_path) + pass + except OSError: + pass + + if not success: + self.fail(err_msg) + + def test_environment_with_special_char (self): + """Test that environment variables containing '*' and '}' are communicated correctly to the lldb-server.""" + d = {'CXX_SOURCES' : 'print_env.cpp'} + self.build(dictionary=d) + self.setTearDownCleanup(d) + exe = os.path.join (os.getcwd(), "a.out") + + evil_var = 'INIT*MIDDLE}TAIL' + + target = self.dbg.CreateTarget(exe) + process = target.LaunchSimple(None, ['EVIL=' + evil_var], self.get_process_working_directory()) + self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED) + + out = process.GetSTDOUT(len(evil_var)) + self.assertIsNotNone(out, "Encountered an error reading the process's output") + + out = out[:len(evil_var)] + if out != evil_var: + self.fail('The environment variable was mis-coded: %s\n' % repr(out)) + + newline = process.GetSTDOUT(1) + self.assertIsNotNone(newline, "Encountered an error reading the process's output") + + newline = newline[0] + if newline != '\r' and newline != '\n': + self.fail('Garbage at end of environment variable') diff --git a/packages/Python/lldbsuite/test/functionalities/process_launch/input-file.txt b/packages/Python/lldbsuite/test/functionalities/process_launch/input-file.txt new file mode 100644 index 000000000000..cc269ba0ff8c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_launch/input-file.txt @@ -0,0 +1,2 @@ +This should go to stdout. +This should go to stderr. diff --git a/packages/Python/lldbsuite/test/functionalities/process_launch/main.cpp b/packages/Python/lldbsuite/test/functionalities/process_launch/main.cpp new file mode 100644 index 000000000000..f2035d551679 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_launch/main.cpp @@ -0,0 +1,17 @@ +#include +#include + +int +main (int argc, char **argv) +{ + char buffer[1024]; + + fgets (buffer, sizeof (buffer), stdin); + fprintf (stdout, "%s", buffer); + + + fgets (buffer, sizeof (buffer), stdin); + fprintf (stderr, "%s", buffer); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/process_launch/my_working_dir/.keep b/packages/Python/lldbsuite/test/functionalities/process_launch/my_working_dir/.keep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/functionalities/process_launch/print_cwd.cpp b/packages/Python/lldbsuite/test/functionalities/process_launch/print_cwd.cpp new file mode 100644 index 000000000000..b4b073fbcb8d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_launch/print_cwd.cpp @@ -0,0 +1,21 @@ +#include + +#ifdef _MSC_VER +#define _CRT_NONSTDC_NO_WARNINGS +#include +#undef getcwd +#define getcwd(buffer, length) _getcwd(buffer, length) +#else +#include +#endif + +int +main (int argc, char **argv) +{ + char buffer[1024]; + + fprintf(stdout, "stdout: %s\n", getcwd(buffer, 1024)); + fprintf(stderr, "stderr: %s\n", getcwd(buffer, 1024)); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/process_launch/print_env.cpp b/packages/Python/lldbsuite/test/functionalities/process_launch/print_env.cpp new file mode 100644 index 000000000000..cbb9b2175916 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_launch/print_env.cpp @@ -0,0 +1,11 @@ +#include +#include +#include + +int main (int argc, char **argv) +{ + char *evil = getenv("EVIL"); + puts(evil); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/process_save_core/Makefile b/packages/Python/lldbsuite/test/functionalities/process_save_core/Makefile new file mode 100644 index 000000000000..b76d2cdb93f1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_save_core/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + diff --git a/packages/Python/lldbsuite/test/functionalities/process_save_core/TestProcessSaveCore.py b/packages/Python/lldbsuite/test/functionalities/process_save_core/TestProcessSaveCore.py new file mode 100644 index 000000000000..0578bcf44b4c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_save_core/TestProcessSaveCore.py @@ -0,0 +1,58 @@ +""" +Test saving a core file (or mini dump). +""" + +from __future__ import print_function + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class ProcessSaveCoreTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @not_remote_testsuite_ready + @skipUnlessWindows + def test_cannot_save_core_unless_process_stopped(self): + """Test that SaveCore fails if the process isn't stopped.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + core = os.path.join(os.getcwd(), "core.dmp") + target = self.dbg.CreateTarget(exe) + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertNotEqual(process.GetState(), lldb.eStateStopped) + error = process.SaveCore(core) + self.assertTrue(error.Fail()) + + @not_remote_testsuite_ready + @skipUnlessWindows + def test_save_windows_mini_dump(self): + """Test that we can save a Windows mini dump.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + core = os.path.join(os.getcwd(), "core.dmp") + try: + target = self.dbg.CreateTarget(exe) + breakpoint = target.BreakpointCreateByName("bar") + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertEqual(process.GetState(), lldb.eStateStopped) + self.assertTrue(process.SaveCore(core)) + self.assertTrue(os.path.isfile(core)) + self.assertTrue(process.Kill().Success()) + + # To verify, we'll launch with the mini dump, and ensure that we see + # the executable in the module list. + target = self.dbg.CreateTarget(None) + process = target.LoadCore(core) + files = [target.GetModuleAtIndex(i).GetFileSpec() for i in range(0, target.GetNumModules())] + paths = [os.path.join(f.GetDirectory(), f.GetFilename()) for f in files] + self.assertTrue(exe in paths) + + finally: + # Clean up the mini dump file. + self.assertTrue(self.dbg.DeleteTarget(target)) + if (os.path.isfile(core)): + os.unlink(core) + + diff --git a/packages/Python/lldbsuite/test/functionalities/process_save_core/main.cpp b/packages/Python/lldbsuite/test/functionalities/process_save_core/main.cpp new file mode 100644 index 000000000000..4037ea522cac --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/process_save_core/main.cpp @@ -0,0 +1,21 @@ +int global = 42; + +int +bar(int x) +{ + int y = 4*x + global; + return y; +} + +int +foo(int x) +{ + int y = 2*bar(3*x); + return y; +} + +int +main() +{ + return 0 * foo(1); +} diff --git a/packages/Python/lldbsuite/test/functionalities/recursion/Makefile b/packages/Python/lldbsuite/test/functionalities/recursion/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/recursion/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/recursion/TestValueObjectRecursion.py b/packages/Python/lldbsuite/test/functionalities/recursion/TestValueObjectRecursion.py new file mode 100644 index 000000000000..fcb493bc9d95 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/recursion/TestValueObjectRecursion.py @@ -0,0 +1,59 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ValueObjectRecursionTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test that deeply nested ValueObjects still work.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + root = self.frame().FindVariable("root") + child = root.GetChildAtIndex(1) + if self.TraceOn(): + print(root) + print(child) + for i in range(0,15000): + child = child.GetChildAtIndex(1) + if self.TraceOn(): + print(child) + self.assertTrue(child.IsValid(),"could not retrieve the deep ValueObject") + self.assertTrue(child.GetChildAtIndex(0).IsValid(),"the deep ValueObject has no value") + self.assertTrue(child.GetChildAtIndex(0).GetValueAsUnsigned() != 0,"the deep ValueObject has a zero value") + self.assertTrue(child.GetChildAtIndex(1).GetValueAsUnsigned() != 0, "the deep ValueObject has no next") diff --git a/packages/Python/lldbsuite/test/functionalities/recursion/main.cpp b/packages/Python/lldbsuite/test/functionalities/recursion/main.cpp new file mode 100644 index 000000000000..f75a7f8698bb --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/recursion/main.cpp @@ -0,0 +1,41 @@ +//===-- 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 +#include + +struct node; +struct node { + int value; + node* next; + node () : value(1),next(NULL) {} + node (int v) : value(v), next(NULL) {} +}; + +void make_tree(node* root, int count) +{ + int countdown=1; + if (!root) + return; + root->value = countdown; + while (count > 0) + { + root->next = new node(++countdown); + root = root->next; + count--; + } +} + +int main (int argc, const char * argv[]) +{ + node root(1); + make_tree(&root,25000); + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/register/Makefile b/packages/Python/lldbsuite/test/functionalities/register/Makefile new file mode 100644 index 000000000000..7144b25c58c9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/register/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp a.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/register/TestRegisters.py b/packages/Python/lldbsuite/test/functionalities/register/TestRegisters.py new file mode 100644 index 000000000000..c5d4650aa371 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/register/TestRegisters.py @@ -0,0 +1,349 @@ +""" +Test the 'register' command. +""" + +from __future__ import print_function + + + +import os, sys, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class RegisterCommandsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.has_teardown = False + + def tearDown(self): + self.dbg.GetSelectedTarget().GetProcess().Destroy() + TestBase.tearDown(self) + + @skipIfiOSSimulator + @skipUnlessArch(['amd64', 'arm', 'i386', 'x86_64']) + def test_register_commands(self): + """Test commands related to registers, in particular vector registers.""" + self.build() + self.common_setup() + + # verify that logging does not assert + self.log_enable("registers") + + self.expect("register read -a", MISSING_EXPECTED_REGISTERS, + substrs = ['registers were unavailable'], matching = False) + + if self.getArchitecture() in ['amd64', 'i386', 'x86_64']: + self.runCmd("register read xmm0") + self.runCmd("register read ymm15") # may be available + elif self.getArchitecture() in ['arm']: + self.runCmd("register read s0") + self.runCmd("register read q15") # may be available + + self.expect("register read -s 3", substrs = ['invalid register set index: 3'], error = True) + + @skipIfiOSSimulator + @skipIfTargetAndroid(archs=["i386"]) # Writing of mxcsr register fails, presumably due to a kernel/hardware problem + @skipUnlessArch(['amd64', 'arm', 'i386', 'x86_64']) + def test_fp_register_write(self): + """Test commands that write to registers, in particular floating-point registers.""" + self.build() + self.fp_register_write() + + @skipIfiOSSimulator + @expectedFailureAndroid(archs=["i386"]) # "register read fstat" always return 0xffff + @skipIfFreeBSD #llvm.org/pr25057 + @skipUnlessArch(['amd64', 'i386', 'x86_64']) + def test_fp_special_purpose_register_read(self): + """Test commands that read fpu special purpose registers.""" + self.build() + self.fp_special_purpose_register_read() + + @skipIfiOSSimulator + @skipUnlessArch(['amd64', 'arm', 'i386', 'x86_64']) + def test_register_expressions(self): + """Test expression evaluation with commands related to registers.""" + self.build() + self.common_setup() + + if self.getArchitecture() in ['amd64', 'i386', 'x86_64']: + gpr = "eax" + vector = "xmm0" + elif self.getArchitecture() in ['arm']: + gpr = "r0" + vector = "q0" + + self.expect("expr/x $%s" % gpr, substrs = ['unsigned int', ' = 0x']) + self.expect("expr $%s" % vector, substrs = ['vector_type']) + self.expect("expr (unsigned int)$%s[0]" % vector, substrs = ['unsigned int']) + + if self.getArchitecture() in ['amd64', 'x86_64']: + self.expect("expr -- ($rax & 0xffffffff) == $eax", substrs = ['true']) + + @skipIfiOSSimulator + @skipUnlessArch(['amd64', 'x86_64']) + def test_convenience_registers(self): + """Test convenience registers.""" + self.build() + self.convenience_registers() + + @skipIfiOSSimulator + @skipUnlessArch(['amd64', 'x86_64']) + def test_convenience_registers_with_process_attach(self): + """Test convenience registers after a 'process attach'.""" + self.build() + self.convenience_registers_with_process_attach(test_16bit_regs=False) + + @skipIfiOSSimulator + @skipUnlessArch(['amd64', 'x86_64']) + def test_convenience_registers_16bit_with_process_attach(self): + """Test convenience registers after a 'process attach'.""" + self.build() + self.convenience_registers_with_process_attach(test_16bit_regs=True) + + def common_setup(self): + exe = os.path.join(os.getcwd(), "a.out") + + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break in main(). + lldbutil.run_break_set_by_symbol (self, "main", num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # platform specific logging of the specified category + def log_enable(self, category): + # This intentionally checks the host platform rather than the target + # platform as logging is host side. + self.platform = "" + if sys.platform.startswith("darwin"): + self.platform = "" # TODO: add support for "log enable darwin registers" + + if sys.platform.startswith("freebsd"): + self.platform = "freebsd" + + if sys.platform.startswith("linux"): + self.platform = "linux" + + if sys.platform.startswith("netbsd"): + self.platform = "netbsd" + + if self.platform != "": + self.log_file = os.path.join(os.getcwd(), 'TestRegisters.log') + self.runCmd("log enable " + self.platform + " " + str(category) + " registers -v -f " + self.log_file, RUN_SUCCEEDED) + if not self.has_teardown: + def remove_log(self): + if os.path.exists(self.log_file): + os.remove(self.log_file) + self.has_teardown = True + self.addTearDownHook(remove_log) + + def write_and_read(self, frame, register, new_value, must_exist = True): + value = frame.FindValue(register, lldb.eValueTypeRegister) + if must_exist: + self.assertTrue(value.IsValid(), "finding a value for register " + register) + elif not value.IsValid(): + return # If register doesn't exist, skip this test + + self.runCmd("register write " + register + " \'" + new_value + "\'") + self.expect("register read " + register, substrs = [register + ' = ', new_value]) + + def fp_special_purpose_register_read(self): + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process and stop. + self.expect ("run", PROCESS_STOPPED, substrs = ['stopped']) + + # Check stop reason; Should be either signal SIGTRAP or EXC_BREAKPOINT + output = self.res.GetOutput() + matched = False + substrs = ['stop reason = EXC_BREAKPOINT', 'stop reason = signal SIGTRAP'] + for str1 in substrs: + matched = output.find(str1) != -1 + with recording(self, False) as sbuf: + print("%s sub string: %s" % ('Expecting', str1), file=sbuf) + print("Matched" if matched else "Not Matched", file=sbuf) + if matched: + break + self.assertTrue(matched, STOPPED_DUE_TO_SIGNAL) + + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid(), "current thread is valid") + + currentFrame = thread.GetFrameAtIndex(0) + self.assertTrue(currentFrame.IsValid(), "current frame is valid") + + # Extract the value of fstat and ftag flag at the point just before + # we start pushing floating point values on st% register stack + value = currentFrame.FindValue("fstat", lldb.eValueTypeRegister) + error = lldb.SBError() + reg_value_fstat_initial = value.GetValueAsUnsigned(error, 0) + + self.assertTrue(error.Success(), "reading a value for fstat") + value = currentFrame.FindValue("ftag", lldb.eValueTypeRegister) + error = lldb.SBError() + reg_value_ftag_initial = value.GetValueAsUnsigned(error, 0) + + self.assertTrue(error.Success(), "reading a value for ftag") + fstat_top_pointer_initial = (reg_value_fstat_initial & 0x3800)>>11 + + # Execute 'si' aka 'thread step-inst' instruction 5 times and with + # every execution verify the value of fstat and ftag registers + for x in range(0,5): + # step into the next instruction to push a value on 'st' register stack + self.runCmd ("si", RUN_SUCCEEDED) + + # Verify fstat and save it to be used for verification in next execution of 'si' command + if not (reg_value_fstat_initial & 0x3800): + self.expect("register read fstat", + substrs = ['fstat' + ' = ', str("0x%0.4x" %((reg_value_fstat_initial & ~(0x3800))| 0x3800))]) + reg_value_fstat_initial = ((reg_value_fstat_initial & ~(0x3800))| 0x3800) + fstat_top_pointer_initial = 7 + else : + self.expect("register read fstat", + substrs = ['fstat' + ' = ', str("0x%0.4x" % (reg_value_fstat_initial - 0x0800))]) + reg_value_fstat_initial = (reg_value_fstat_initial - 0x0800) + fstat_top_pointer_initial -= 1 + + # Verify ftag and save it to be used for verification in next execution of 'si' command + self.expect("register read ftag", + substrs = ['ftag' + ' = ', str("0x%0.2x" % (reg_value_ftag_initial | (1<< fstat_top_pointer_initial)))]) + reg_value_ftag_initial = reg_value_ftag_initial | (1<< fstat_top_pointer_initial) + + def fp_register_write(self): + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + lldbutil.run_break_set_by_symbol (self, "main", num_expected_locations=-1) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid(), "current thread is valid") + + currentFrame = thread.GetFrameAtIndex(0) + self.assertTrue(currentFrame.IsValid(), "current frame is valid") + + if self.getArchitecture() in ['amd64', 'i386', 'x86_64']: + reg_list = [ + # reg value must-have + ("fcw", "0x0000ff0e", False), + ("fsw", "0x0000ff0e", False), + ("ftw", "0x0000ff0e", False), + ("ip", "0x0000ff0e", False), + ("dp", "0x0000ff0e", False), + ("mxcsr", "0x0000ff0e", False), + ("mxcsrmask", "0x0000ff0e", False), + ] + + st0regname = None + if currentFrame.FindRegister("st0").IsValid(): + st0regname = "st0" + elif currentFrame.FindRegister("stmm0").IsValid(): + st0regname = "stmm0" + if st0regname is not None: + # reg value must-have + reg_list.append((st0regname, "{0x01 0x02 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00}", True)) + reg_list.append(("xmm0", "{0x01 0x02 0x03 0x00 0x00 0x00 0x00 0x00 0x09 0x0a 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f}", True)) + reg_list.append(("xmm15", "{0x01 0x02 0x03 0x00 0x00 0x00 0x00 0x00 0x09 0x0a 0x2f 0x2f 0x2f 0x2f 0x0e 0x0f}", False)) + elif self.getArchitecture() in ['arm']: + reg_list = [ + # reg value must-have + ("fpscr", "0xfbf79f9f", True), + ("s0", "1.25", True), + ("s31", "0.75", True), + ("d1", "123", True), + ("d17", "987", False), + ("q1", "{0x01 0x02 0x03 0x00 0x00 0x00 0x00 0x00 0x09 0x0a 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f}", True), + ("q14", "{0x01 0x02 0x03 0x00 0x00 0x00 0x00 0x00 0x09 0x0a 0x2f 0x2f 0x2f 0x2f 0x0e 0x0f}", False), + ] + + for (reg, val, must) in reg_list: + self.write_and_read(currentFrame, reg, val, must) + + if self.getArchitecture() in ['amd64', 'i386', 'x86_64']: + self.runCmd("register write " + st0regname + " \"{0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00}\"") + self.expect("register read " + st0regname + " --format f", substrs = [st0regname + ' = 0']) + + has_avx = False + registerSets = currentFrame.GetRegisters() # Returns an SBValueList. + for registerSet in registerSets: + if 'advanced vector extensions' in registerSet.GetName().lower(): + has_avx = True + break + + if has_avx: + new_value = "{0x01 0x02 0x03 0x00 0x00 0x00 0x00 0x00 0x09 0x0a 0x2f 0x2f 0x2f 0x2f 0x0e 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0c 0x0d 0x0e 0x0f}" + self.write_and_read(currentFrame, "ymm0", new_value) + self.write_and_read(currentFrame, "ymm7", new_value) + self.expect("expr $ymm0", substrs = ['vector_type']) + else: + self.runCmd("register read ymm0") + + def convenience_registers(self): + """Test convenience registers.""" + self.common_setup() + + # The command "register read -a" does output a derived register like eax... + self.expect("register read -a", matching=True, + substrs = ['eax']) + + # ...however, the vanilla "register read" command should not output derived registers like eax. + self.expect("register read", matching=False, + substrs = ['eax']) + + # Test reading of rax and eax. + self.expect("register read rax eax", + substrs = ['rax = 0x', 'eax = 0x']) + + # Now write rax with a unique bit pattern and test that eax indeed represents the lower half of rax. + self.runCmd("register write rax 0x1234567887654321") + self.expect("register read rax 0x1234567887654321", + substrs = ['0x1234567887654321']) + + def convenience_registers_with_process_attach(self, test_16bit_regs): + """Test convenience registers after a 'process attach'.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Spawn a new process + pid = self.spawnSubprocess(exe, ['wait_for_attach']).pid + self.addTearDownHook(self.cleanupSubprocesses) + + if self.TraceOn(): + print("pid of spawned process: %d" % pid) + + self.runCmd("process attach -p %d" % pid) + + # Check that "register read eax" works. + self.runCmd("register read eax") + + if self.getArchitecture() in ['amd64', 'x86_64']: + self.expect("expr -- ($rax & 0xffffffff) == $eax", + substrs = ['true']) + + if test_16bit_regs: + self.expect("expr -- $ax == (($ah << 8) | $al)", + substrs = ['true']) diff --git a/packages/Python/lldbsuite/test/functionalities/register/a.cpp b/packages/Python/lldbsuite/test/functionalities/register/a.cpp new file mode 100644 index 000000000000..fbacec1918e8 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/register/a.cpp @@ -0,0 +1,44 @@ +//===-- a.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include + +long double +return_long_double (long double value) +{ +#if defined (__i386__) || defined (__x86_64__) + float a=2, b=4,c=8, d=16, e=32, f=64, k=128, l=256, add=0; + __asm__ ( + "int3 ;" + "flds %1 ;" + "flds %2 ;" + "flds %3 ;" + "flds %4 ;" + "flds %5 ;" + "flds %6 ;" + "flds %7 ;" + "faddp ;" : "=g" (add) : "g" (a), "g" (b), "g" (c), "g" (d), "g" (e), "g" (f), "g" (k), "g" (l) ); // Set break point at this line. +#endif // #if defined (__i386__) || defined (__x86_64__) + return value; +} + +long double +outer_return_long_double (long double value) +{ + long double val = return_long_double(value); + val *= 2 ; + return val; +} + +long double +outermost_return_long_double (long double value) +{ + long double val = outer_return_long_double(value); + val *= 2 ; + return val; +} diff --git a/packages/Python/lldbsuite/test/functionalities/register/main.cpp b/packages/Python/lldbsuite/test/functionalities/register/main.cpp new file mode 100644 index 000000000000..876dd0833e54 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/register/main.cpp @@ -0,0 +1,51 @@ +//===-- 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 + +#if defined(__linux__) +#include +#endif + +#include +#include + +long double outermost_return_long_double (long double my_long_double); + +int main (int argc, char const *argv[]) +{ +#if defined(__linux__) + // Immediately enable any ptracer so that we can allow the stub attach + // operation to succeed. Some Linux kernels are locked down so that + // only an ancestor process can be a ptracer of a process. This disables that + // restriction. Without it, attach-related stub tests will fail. +#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY) + // For now we execute on best effort basis. If this fails for + // some reason, so be it. + const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); + static_cast (prctl_result); +#endif +#endif + + char my_string[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 0}; + double my_double = 1234.5678; + long double my_long_double = 1234.5678; + + // For simplicity assume that any cmdline argument means wait for attach. + if (argc > 1) + { + volatile int wait_for_attach=1; + while (wait_for_attach) + std::this_thread::sleep_for(std::chrono::microseconds(1)); + } + + printf("my_string=%s\n", my_string); + printf("my_double=%g\n", my_double); + outermost_return_long_double (my_long_double); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/rerun/Makefile b/packages/Python/lldbsuite/test/functionalities/rerun/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/rerun/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/rerun/TestRerun.py b/packages/Python/lldbsuite/test/functionalities/rerun/TestRerun.py new file mode 100644 index 000000000000..0ed56de35699 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/rerun/TestRerun.py @@ -0,0 +1,76 @@ +""" +Test that argdumper is a viable launching strategy. +""" +from __future__ import print_function + + + +import lldb +import os +import time +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestRerun(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test (self): + self.build() + exe = os.path.join (os.getcwd(), "a.out") + + self.runCmd("target create %s" % exe) + + # Create the target + target = self.dbg.CreateTarget(exe) + + # Create any breakpoints we need + breakpoint = target.BreakpointCreateBySourceRegex ('break here', lldb.SBFileSpec ("main.cpp", False)) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + self.runCmd("process launch 1 2 3") + + process = self.process() + + self.assertTrue(process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at 'main' should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonBreakpoint, + "Thread in process stopped in 'main' should have a stop reason of eStopReasonBreakpoint"); + + self.expect("frame variable argv[1]", substrs=['1']) + self.expect("frame variable argv[2]", substrs=['2']) + self.expect("frame variable argv[3]", substrs=['3']) + + # Let program exit + self.runCmd("continue") + + # Re-run with no args and make sure we still run with 1 2 3 as arguments as + # they should have been stored in "target.run-args" + self.runCmd("process launch") + + process = self.process() + + self.assertTrue(process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at 'main' should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonBreakpoint, + "Thread in process stopped in 'main' should have a stop reason of eStopReasonBreakpoint"); + + self.expect("frame variable argv[1]", substrs=['1']) + self.expect("frame variable argv[2]", substrs=['2']) + self.expect("frame variable argv[3]", substrs=['3']) diff --git a/packages/Python/lldbsuite/test/functionalities/rerun/main.cpp b/packages/Python/lldbsuite/test/functionalities/rerun/main.cpp new file mode 100644 index 000000000000..cbef8d1e6da1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/rerun/main.cpp @@ -0,0 +1,5 @@ +int +main (int argc, char const **argv) +{ + return 0; // break here +} diff --git a/packages/Python/lldbsuite/test/functionalities/return-value/Makefile b/packages/Python/lldbsuite/test/functionalities/return-value/Makefile new file mode 100644 index 000000000000..cb03eabfc274 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/return-value/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := call-func.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py b/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py new file mode 100644 index 000000000000..246eb5c3fdbd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py @@ -0,0 +1,214 @@ +""" +Test getting return-values correctly when stepping out +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ReturnValueTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureAll(oslist=["macosx","freebsd"], archs=["i386"]) + @expectedFailureAll(oslist=["linux"], compiler="clang", compiler_version=["<=", "3.6"], archs=["i386"]) + @expectedFailureAll(bugnumber="llvm.org/pr25785", hostoslist=["windows"], compiler="gcc", archs=["i386"], triple='.*-android') + @expectedFailureWindows("llvm.org/pr24778") + @add_test_categories(['pyapi']) + def test_with_python(self): + """Test getting return values from stepping out.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + error = lldb.SBError() + + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + inner_sint_bkpt = self.target.BreakpointCreateByName("inner_sint", exe) + self.assertTrue(inner_sint_bkpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + self.process = self.target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(self.process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.assertTrue(self.process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + # Now finish, and make sure the return value is correct. + thread = lldbutil.get_stopped_thread (self.process, lldb.eStopReasonBreakpoint) + + # inner_sint returns the variable value, so capture that here: + in_int = thread.GetFrameAtIndex(0).FindVariable ("value").GetValueAsSigned(error) + self.assertTrue (error.Success()) + + thread.StepOut(); + + self.assertTrue (self.process.GetState() == lldb.eStateStopped) + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete) + + frame = thread.GetFrameAtIndex(0) + fun_name = frame.GetFunctionName() + self.assertTrue (fun_name == "outer_sint") + + return_value = thread.GetStopReturnValue() + self.assertTrue (return_value.IsValid()) + + ret_int = return_value.GetValueAsSigned(error) + self.assertTrue (error.Success()) + self.assertTrue (in_int == ret_int) + + # Run again and we will stop in inner_sint the second time outer_sint is called. + #Then test stepping out two frames at once: + + self.process.Continue() + thread_list = lldbutil.get_threads_stopped_at_breakpoint (self.process, inner_sint_bkpt) + self.assertTrue(len(thread_list) == 1) + thread = thread_list[0] + + # We are done with the inner_sint breakpoint: + self.target.BreakpointDelete (inner_sint_bkpt.GetID()) + + frame = thread.GetFrameAtIndex(1) + fun_name = frame.GetFunctionName () + self.assertTrue (fun_name == "outer_sint") + in_int = frame.FindVariable ("value").GetValueAsSigned(error) + self.assertTrue (error.Success()) + + thread.StepOutOfFrame (frame) + + self.assertTrue (self.process.GetState() == lldb.eStateStopped) + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete) + frame = thread.GetFrameAtIndex(0) + fun_name = frame.GetFunctionName() + self.assertTrue (fun_name == "main") + + ret_value = thread.GetStopReturnValue() + self.assertTrue (return_value.IsValid()) + ret_int = ret_value.GetValueAsSigned (error) + self.assertTrue (error.Success()) + self.assertTrue (2 * in_int == ret_int) + + # Now try some simple returns that have different types: + inner_float_bkpt = self.target.BreakpointCreateByName("inner_float", exe) + self.assertTrue(inner_float_bkpt, VALID_BREAKPOINT) + self.process.Continue() + thread_list = lldbutil.get_threads_stopped_at_breakpoint (self.process, inner_float_bkpt) + self.assertTrue (len(thread_list) == 1) + thread = thread_list[0] + + self.target.BreakpointDelete (inner_float_bkpt.GetID()) + + frame = thread.GetFrameAtIndex(0) + in_value = frame.FindVariable ("value") + in_float = float (in_value.GetValue()) + thread.StepOut() + + self.assertTrue (self.process.GetState() == lldb.eStateStopped) + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete) + + frame = thread.GetFrameAtIndex(0) + fun_name = frame.GetFunctionName() + self.assertTrue (fun_name == "outer_float") + + return_value = thread.GetStopReturnValue() + self.assertTrue (return_value.IsValid()) + return_float = float (return_value.GetValue()) + + self.assertTrue(in_float == return_float) + + self.return_and_test_struct_value ("return_one_int") + self.return_and_test_struct_value ("return_two_int") + self.return_and_test_struct_value ("return_three_int") + self.return_and_test_struct_value ("return_four_int") + self.return_and_test_struct_value ("return_five_int") + + self.return_and_test_struct_value ("return_two_double") + self.return_and_test_struct_value ("return_one_double_two_float") + self.return_and_test_struct_value ("return_one_int_one_float_one_int") + + self.return_and_test_struct_value ("return_one_pointer") + self.return_and_test_struct_value ("return_two_pointer") + self.return_and_test_struct_value ("return_one_float_one_pointer") + self.return_and_test_struct_value ("return_one_int_one_pointer") + self.return_and_test_struct_value ("return_three_short_one_float") + + self.return_and_test_struct_value ("return_one_int_one_double") + self.return_and_test_struct_value ("return_one_int_one_double_one_int") + self.return_and_test_struct_value ("return_one_short_one_double_one_short") + self.return_and_test_struct_value ("return_one_float_one_int_one_float") + self.return_and_test_struct_value ("return_two_float") + # I am leaving out the packed test until we have a way to tell CLANG + # about alignment when reading DWARF for packed types. + #self.return_and_test_struct_value ("return_one_int_one_double_packed") + self.return_and_test_struct_value ("return_one_int_one_long") + + # icc and gcc don't support this extension. + if self.getCompiler().endswith('clang'): + self.return_and_test_struct_value ("return_vector_size_float32_8") + self.return_and_test_struct_value ("return_vector_size_float32_16") + self.return_and_test_struct_value ("return_vector_size_float32_32") + self.return_and_test_struct_value ("return_ext_vector_size_float32_2") + self.return_and_test_struct_value ("return_ext_vector_size_float32_4") + self.return_and_test_struct_value ("return_ext_vector_size_float32_8") + + def return_and_test_struct_value (self, func_name): + """Pass in the name of the function to return from - takes in value, returns value.""" + + # Set the breakpoint, run to it, finish out. + bkpt = self.target.BreakpointCreateByName (func_name) + self.assertTrue (bkpt.GetNumResolvedLocations() > 0) + + self.process.Continue () + + thread_list = lldbutil.get_threads_stopped_at_breakpoint (self.process, bkpt) + + self.assertTrue (len(thread_list) == 1) + thread = thread_list[0] + + self.target.BreakpointDelete (bkpt.GetID()) + + in_value = thread.GetFrameAtIndex(0).FindVariable ("value") + + self.assertTrue (in_value.IsValid()) + num_in_children = in_value.GetNumChildren() + + # This is a little hokey, but if we don't get all the children now, then + # once we've stepped we won't be able to get them? + + for idx in range(0, num_in_children): + in_child = in_value.GetChildAtIndex (idx) + in_child_str = in_child.GetValue() + + thread.StepOut() + + self.assertTrue (self.process.GetState() == lldb.eStateStopped) + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete) + + # Assuming all these functions step out to main. Could figure out the caller dynamically + # if that would add something to the test. + frame = thread.GetFrameAtIndex(0) + fun_name = frame.GetFunctionName() + self.assertTrue (fun_name == "main") + + frame = thread.GetFrameAtIndex(0) + ret_value = thread.GetStopReturnValue() + + self.assertTrue (ret_value.IsValid()) + + num_ret_children = ret_value.GetNumChildren() + self.assertTrue (num_in_children == num_ret_children) + for idx in range(0, num_ret_children): + in_child = in_value.GetChildAtIndex(idx) + ret_child = ret_value.GetChildAtIndex(idx) + in_child_str = in_child.GetValue() + ret_child_str = ret_child.GetValue() + + self.assertEqual(in_child_str, ret_child_str) diff --git a/packages/Python/lldbsuite/test/functionalities/return-value/call-func.c b/packages/Python/lldbsuite/test/functionalities/return-value/call-func.c new file mode 100644 index 000000000000..0c026ffcca17 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/return-value/call-func.c @@ -0,0 +1,407 @@ +// Some convenient things to return: +static char *g_first_pointer = "I am the first"; +static char *g_second_pointer = "I am the second"; + +// First we have some simple functions that return standard types, ints, floats and doubles. +// We have a function calling a function in a few cases to test that if you stop in the +// inner function then do "up/fin" you get the return value from the outer-most frame. + +int +inner_sint (int value) +{ + return value; +} + +int +outer_sint (int value) +{ + int outer_value = 2 * inner_sint (value); + return outer_value; +} + +float +inner_float (float value) +{ + return value; +} + +float +outer_float (float value) +{ + float outer_value = 2 * inner_float(value); + return outer_value; +} + +double +return_double (double value) +{ + return value; +} + +long double +return_long_double (long double value) +{ + return value; +} + +char * +return_pointer (char *value) +{ + return value; +} + +struct one_int +{ + int one_field; +}; + +struct one_int +return_one_int (struct one_int value) +{ + return value; +} + +struct two_int +{ + int first_field; + int second_field; +}; + +struct two_int +return_two_int (struct two_int value) +{ + return value; +} + +struct three_int +{ + int first_field; + int second_field; + int third_field; +}; + +struct three_int +return_three_int (struct three_int value) +{ + return value; +} + +struct four_int +{ + int first_field; + int second_field; + int third_field; + int fourth_field; +}; + +struct four_int +return_four_int (struct four_int value) +{ + return value; +} + +struct five_int +{ + int first_field; + int second_field; + int third_field; + int fourth_field; + int fifth_field; +}; + +struct five_int +return_five_int (struct five_int value) +{ + return value; +} + +struct one_int_one_double +{ + int first_field; + double second_field; +}; + +struct one_int_one_double +return_one_int_one_double (struct one_int_one_double value) +{ + return value; +} + +struct one_int_one_double_one_int +{ + int one_field; + double second_field; + int third_field; +}; + +struct one_int_one_double_one_int +return_one_int_one_double_one_int (struct one_int_one_double_one_int value) +{ + return value; +} + +struct one_short_one_double_one_short +{ + int one_field; + double second_field; + int third_field; +}; + +struct one_short_one_double_one_short +return_one_short_one_double_one_short (struct one_short_one_double_one_short value) +{ + return value; +} + +struct three_short_one_float +{ + short one_field; + short second_field; + short third_field; + float fourth_field; +}; + +struct three_short_one_float +return_three_short_one_float (struct three_short_one_float value) +{ + return value; +} + +struct one_int_one_float_one_int +{ + int one_field; + float second_field; + int third_field; +}; + +struct one_int_one_float_one_int +return_one_int_one_float_one_int (struct one_int_one_float_one_int value) +{ + return value; +} + +struct one_float_one_int_one_float +{ + float one_field; + int second_field; + float third_field; +}; + +struct one_float_one_int_one_float +return_one_float_one_int_one_float (struct one_float_one_int_one_float value) +{ + return value; +} + +struct one_double_two_float +{ + double one_field; + float second_field; + float third_field; +}; + +struct one_double_two_float +return_one_double_two_float (struct one_double_two_float value) +{ + return value; +} + +struct two_double +{ + double first_field; + double second_field; +}; + +struct two_double +return_two_double (struct two_double value) +{ + return value; +} + +struct two_float +{ + float first_field; + float second_field; +}; + +struct two_float +return_two_float (struct two_float value) +{ + return value; +} + +struct one_int_one_double_packed +{ + int first_field; + double second_field; +} __attribute__((__packed__)); + +struct one_int_one_double_packed +return_one_int_one_double_packed (struct one_int_one_double_packed value) +{ + return value; +} + +struct one_int_one_long +{ + int first_field; + long second_field; +}; + +struct one_int_one_long +return_one_int_one_long (struct one_int_one_long value) +{ + return value; +} + +struct one_pointer +{ + char *first_field; +}; + +struct one_pointer +return_one_pointer (struct one_pointer value) +{ + return value; +} + +struct two_pointer +{ + char *first_field; + char *second_field; +}; + +struct two_pointer +return_two_pointer (struct two_pointer value) +{ + return value; +} + +struct one_float_one_pointer +{ + float first_field; + char *second_field; +}; + +struct one_float_one_pointer +return_one_float_one_pointer (struct one_float_one_pointer value) +{ + return value; +} + +struct one_int_one_pointer +{ + int first_field; + char *second_field; +}; + +struct one_int_one_pointer +return_one_int_one_pointer (struct one_int_one_pointer value) +{ + return value; +} + +typedef float vector_size_float32_8 __attribute__((__vector_size__(8))); +typedef float vector_size_float32_16 __attribute__((__vector_size__(16))); +typedef float vector_size_float32_32 __attribute__((__vector_size__(32))); + +typedef float ext_vector_size_float32_2 __attribute__((ext_vector_type(2))); +typedef float ext_vector_size_float32_4 __attribute__((ext_vector_type(4))); +typedef float ext_vector_size_float32_8 __attribute__((ext_vector_type(8))); + +vector_size_float32_8 +return_vector_size_float32_8 (vector_size_float32_8 value) +{ + return value; +} + +vector_size_float32_16 +return_vector_size_float32_16 (vector_size_float32_16 value) +{ + return value; +} + +vector_size_float32_32 +return_vector_size_float32_32 (vector_size_float32_32 value) +{ + return value; +} + +ext_vector_size_float32_2 +return_ext_vector_size_float32_2 (ext_vector_size_float32_2 value) +{ + return value; +} + +ext_vector_size_float32_4 +return_ext_vector_size_float32_4 (ext_vector_size_float32_4 value) +{ + return value; +} + +ext_vector_size_float32_8 +return_ext_vector_size_float32_8 (ext_vector_size_float32_8 value) +{ + return value; +} + +int +main () +{ + int first_int = 123456; + int second_int = 234567; + + outer_sint (first_int); + outer_sint (second_int); + + float first_float_value = 12.34; + float second_float_value = 23.45; + + outer_float (first_float_value); + outer_float (second_float_value); + + double double_value = -23.45; + + return_double (double_value); + + return_pointer(g_first_pointer); + + long double long_double_value = -3456789.987654321; + + return_long_double (long_double_value); + + // Okay, now the structures: + return_one_int ((struct one_int) {10}); + return_two_int ((struct two_int) {10, 20}); + return_three_int ((struct three_int) {10, 20, 30}); + return_four_int ((struct four_int) {10, 20, 30, 40}); + return_five_int ((struct five_int) {10, 20, 30, 40, 50}); + + return_two_double ((struct two_double) {10.0, 20.0}); + return_one_double_two_float ((struct one_double_two_float) {10.0, 20.0, 30.0}); + return_one_int_one_float_one_int ((struct one_int_one_float_one_int) {10, 20.0, 30}); + + return_one_pointer ((struct one_pointer) {g_first_pointer}); + return_two_pointer ((struct two_pointer) {g_first_pointer, g_second_pointer}); + return_one_float_one_pointer ((struct one_float_one_pointer) {10.0, g_first_pointer}); + return_one_int_one_pointer ((struct one_int_one_pointer) {10, g_first_pointer}); + return_three_short_one_float ((struct three_short_one_float) {10, 20, 30, 40.0}); + + return_one_int_one_double ((struct one_int_one_double) {10, 20.0}); + return_one_int_one_double_one_int ((struct one_int_one_double_one_int) {10, 20.0, 30}); + return_one_short_one_double_one_short ((struct one_short_one_double_one_short) {10, 20.0, 30}); + return_one_float_one_int_one_float ((struct one_float_one_int_one_float) {10.0, 20, 30.0}); + return_two_float ((struct two_float) { 10.0, 20.0}); + return_one_int_one_double_packed ((struct one_int_one_double_packed) {10, 20.0}); + return_one_int_one_long ((struct one_int_one_long) {10, 20}); + + return_vector_size_float32_8 (( vector_size_float32_8 ){1.5, 2.25}); + return_vector_size_float32_16 (( vector_size_float32_16 ){1.5, 2.25, 4.125, 8.0625}); + return_vector_size_float32_32 (( vector_size_float32_32 ){1.5, 2.25, 4.125, 8.0625, 7.89, 8.52, 6.31, 9.12}); + + return_ext_vector_size_float32_2 ((ext_vector_size_float32_2){ 16.5, 32.25}); + return_ext_vector_size_float32_4 ((ext_vector_size_float32_4){ 16.5, 32.25, 64.125, 128.0625}); + return_ext_vector_size_float32_8 ((ext_vector_size_float32_8){ 16.5, 32.25, 64.125, 128.0625, 1.59, 3.57, 8.63, 9.12 }); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/set-data/Makefile b/packages/Python/lldbsuite/test/functionalities/set-data/Makefile new file mode 100644 index 000000000000..9e1d63a183bc --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/set-data/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/functionalities/set-data/TestSetData.py b/packages/Python/lldbsuite/test/functionalities/set-data/TestSetData.py new file mode 100644 index 000000000000..3acad5f87be0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/set-data/TestSetData.py @@ -0,0 +1,62 @@ +""" +Set the contents of variables and registers using raw data +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SetDataTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_set_data(self): + """Test setting the contents of variables and registers using raw data.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("br s -p First"); + self.runCmd("br s -p Second"); + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("p myFoo.x", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['2']) + + process = self.dbg.GetSelectedTarget().GetProcess() + frame = process.GetSelectedThread().GetFrameAtIndex(0) + + x = frame.FindVariable("myFoo").GetChildMemberWithName("x") + + my_data = lldb.SBData.CreateDataFromSInt32Array(lldb.eByteOrderLittle, 8, [4]) + err = lldb.SBError() + + self.assertTrue (x.SetData(my_data, err)) + + self.runCmd("continue") + + self.expect("p myFoo.x", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['4']) + + frame = process.GetSelectedThread().GetFrameAtIndex(0) + + x = frame.FindVariable("string") + + if process.GetAddressByteSize() == 8: + my_data = lldb.SBData.CreateDataFromUInt64Array(process.GetByteOrder(), 8, [0]) + else: + my_data = lldb.SBData.CreateDataFromUInt32Array(process.GetByteOrder(), 4, [0]) + + err = lldb.SBError() + + self.assertTrue (x.SetData(my_data, err)) + + self.expect("fr var -d run-target string", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['NSString *', 'nil']) diff --git a/packages/Python/lldbsuite/test/functionalities/set-data/main.m b/packages/Python/lldbsuite/test/functionalities/set-data/main.m new file mode 100644 index 000000000000..e1e69dc55715 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/set-data/main.m @@ -0,0 +1,19 @@ +#import + +int main () +{ + @autoreleasepool + { + struct foo { + int x; + int y; + } myFoo; + + myFoo.x = 2; + myFoo.y = 3; // First breakpoint + + NSString *string = [NSString stringWithFormat:@"%s", "Hello world!"]; + + NSLog(@"%d %@", myFoo.x, string); // Second breakpoint + } +} diff --git a/packages/Python/lldbsuite/test/functionalities/signal/Makefile b/packages/Python/lldbsuite/test/functionalities/signal/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/signal/TestSendSignal.py b/packages/Python/lldbsuite/test/functionalities/signal/TestSendSignal.py new file mode 100644 index 000000000000..971b82a7c75d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/TestSendSignal.py @@ -0,0 +1,105 @@ +"""Test that lldb command 'process signal SIGUSR1' to send a signal to the inferior works.""" + +from __future__ import print_function + + + +import os, time, signal +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + + +class SendSignalTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', 'Put breakpoint here') + + @expectedFailureFreeBSD("llvm.org/pr23318: does not report running state") + @skipIfWindows # Windows does not support signals + def test_with_run_command(self): + """Test that lldb command 'process signal SIGUSR1' sends a signal to the inferior process.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByLocation('main.c', self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Get the breakpoint location from breakpoint after we verified that, + # indeed, it has one location. + location = breakpoint.GetLocationAtIndex(0) + self.assertTrue(location and + location.IsEnabled(), + VALID_BREAKPOINT_LOCATION) + + # Now launch the process, no arguments & do not stop at entry point. + launch_info = lldb.SBLaunchInfo([exe]) + launch_info.SetWorkingDirectory(self.get_process_working_directory()) + + process_listener = lldb.SBListener("signal_test_listener") + launch_info.SetListener(process_listener) + error = lldb.SBError() + process = target.Launch(launch_info, error) + self.assertTrue(process, PROCESS_IS_VALID) + + self.runCmd("process handle -n False -p True -s True SIGUSR1") + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "We hit the first breakpoint.") + + # After resuming the process, send it a SIGUSR1 signal. + + self.setAsync(True) + + self.assertTrue(process_listener.IsValid(), "Got a good process listener") + + # Disable our breakpoint, we don't want to hit it anymore... + breakpoint.SetEnabled(False) + + # Now continue: + process.Continue() + + # If running remote test, there should be a connected event + if lldb.remote_platform: + self.match_state(process_listener, lldb.eStateConnected) + + self.match_state(process_listener, lldb.eStateRunning) + + # Now signal the process, and make sure it stops: + process.Signal(lldbutil.get_signal_number('SIGUSR1')) + + self.match_state(process_listener, lldb.eStateStopped) + + # Now make sure the thread was stopped with a SIGUSR1: + threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonSignal) + self.assertTrue(len(threads) == 1, "One thread stopped for a signal.") + thread = threads[0] + + self.assertTrue(thread.GetStopReasonDataCount() >= 1, "There was data in the event.") + self.assertTrue(thread.GetStopReasonDataAtIndex(0) == lldbutil.get_signal_number('SIGUSR1'), + "The stop signal was SIGUSR1") + + def match_state(self, process_listener, expected_state): + num_seconds = 5 + broadcaster = self.process().GetBroadcaster() + event_type_mask = lldb.SBProcess.eBroadcastBitStateChanged + event = lldb.SBEvent() + got_event = process_listener.WaitForEventForBroadcasterWithType( + num_seconds, broadcaster, event_type_mask, event) + self.assertTrue(got_event, "Got an event") + state = lldb.SBProcess.GetStateFromEvent(event) + self.assertTrue(state == expected_state, + "It was the %s state." % + lldb.SBDebugger_StateAsCString(expected_state)) diff --git a/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/Makefile b/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/TestHandleSegv.py b/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/TestHandleSegv.py new file mode 100644 index 000000000000..feed5bfb3c76 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/TestHandleSegv.py @@ -0,0 +1,43 @@ +"""Test that we can debug inferiors that handle SIGSEGV by themselves""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import re + + +class HandleSegvTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows # signals do not exist on Windows + @skipIfDarwin + @expectedFailureFreeBSD("llvm.org/pr23699 SIGSEGV is reported as exception, not signal") + def test_inferior_handle_sigsegv(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # launch + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + self.assertEqual(process.GetState(), lldb.eStateStopped) + signo = process.GetUnixSignals().GetSignalNumberFromName("SIGSEGV") + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal) + self.assertTrue(thread and thread.IsValid(), "Thread should be stopped due to a signal") + self.assertTrue(thread.GetStopReasonDataCount() >= 1, "There was data in the event.") + self.assertEqual(thread.GetStopReasonDataAtIndex(0), signo, "The stop signal was SIGSEGV") + + # Continue until we exit. + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) diff --git a/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/main.c b/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/main.c new file mode 100644 index 000000000000..27d9b8e500ab --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/handle-segv/main.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +enum { + kMmapSize = 0x1000, + kMagicValue = 47, +}; + +void *address; +volatile sig_atomic_t signaled = 0; + +void handler(int sig) +{ + signaled = 1; + if (munmap(address, kMmapSize) != 0) + { + perror("munmap"); + _exit(5); + } + + void* newaddr = mmap(address, kMmapSize, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_FIXED | MAP_PRIVATE, -1, 0); + if (newaddr != address) + { + fprintf(stderr, "Newly mmaped address (%p) does not equal old address (%p).\n", + newaddr, address); + _exit(6); + } + *(int*)newaddr = kMagicValue; +} + +int main() +{ + if (signal(SIGSEGV, handler) == SIG_ERR) + { + perror("signal"); + return 1; + } + + address = mmap(NULL, kMmapSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + if (address == MAP_FAILED) + { + perror("mmap"); + return 2; + } + + // This should first trigger a segfault. Our handler will make the memory readable and write + // the magic value into memory. + if (*(int*)address != kMagicValue) + return 3; + + if (! signaled) + return 4; + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/signal/main.c b/packages/Python/lldbsuite/test/functionalities/signal/main.c new file mode 100644 index 000000000000..77e760503410 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/main.c @@ -0,0 +1,27 @@ +#include +#include +#include + +void handler_usr1 (int i) +{ + puts ("got signal usr1"); +} + +void handler_alrm (int i) +{ + puts ("got signal ALRM"); +} + +int main () +{ + int i = 0; + + signal (SIGUSR1, handler_usr1); + signal (SIGALRM, handler_alrm); + + puts ("Put breakpoint here"); + + while (i++ < 20) + sleep (1); +} + diff --git a/packages/Python/lldbsuite/test/functionalities/signal/raise/Makefile b/packages/Python/lldbsuite/test/functionalities/signal/raise/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/raise/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/signal/raise/TestRaise.py b/packages/Python/lldbsuite/test/functionalities/signal/raise/TestRaise.py new file mode 100644 index 000000000000..39e7753397d1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/raise/TestRaise.py @@ -0,0 +1,221 @@ +"""Test that we handle inferiors that send signals to themselves""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import re + + +@skipIfWindows # signals do not exist on Windows +class RaiseTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_sigstop(self): + self.build() + self.signal_test('SIGSTOP', False) + # passing of SIGSTOP is not correctly handled, so not testing that scenario: https://llvm.org/bugs/show_bug.cgi?id=23574 + + @skipIfDarwin # darwin does not support real time signals + @skipIfTargetAndroid() + def test_sigsigrtmin(self): + self.build() + self.signal_test('SIGRTMIN', True) + + def launch(self, target, signal): + # launch the process, do not stop at entry point. + process = target.LaunchSimple([signal], None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + self.assertEqual(process.GetState(), lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "Thread should be stopped due to a breakpoint") + return process + + def set_handle(self, signal, pass_signal, stop_at_signal, notify_signal): + return_obj = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand( + "process handle %s -p %s -s %s -n %s" % (signal, pass_signal, stop_at_signal, notify_signal), + return_obj) + self.assertTrue (return_obj.Succeeded() == True, "Setting signal handling failed") + + + def signal_test(self, signal, test_passing): + """Test that we handle inferior raising signals""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + lldbutil.run_break_set_by_symbol(self, "main") + + # launch + process = self.launch(target, signal) + signo = process.GetUnixSignals().GetSignalNumberFromName(signal) + + # retrieve default signal disposition + return_obj = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand("process handle %s " % signal, return_obj) + match = re.match('NAME *PASS *STOP *NOTIFY.*(false|true) *(false|true) *(false|true)', + return_obj.GetOutput(), re.IGNORECASE | re.DOTALL) + if not match: + self.fail('Unable to retrieve default signal disposition.') + default_pass = match.group(1) + default_stop = match.group(2) + default_notify = match.group(3) + + # Make sure we stop at the signal + self.set_handle(signal, "false", "true", "true") + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal) + self.assertTrue(thread.IsValid(), "Thread should be stopped due to a signal") + self.assertTrue(thread.GetStopReasonDataCount() >= 1, "There was data in the event.") + self.assertEqual(thread.GetStopReasonDataAtIndex(0), signo, + "The stop signal was %s" % signal) + + # Continue until we exit. + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) + + # launch again + process = self.launch(target, signal) + + # Make sure we do not stop at the signal. We should still get the notification. + self.set_handle(signal, "false", "false", "true") + self.expect("process continue", substrs=["stopped and restarted", signal]) + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) + + # launch again + process = self.launch(target, signal) + + # Make sure we do not stop at the signal, and we do not get the notification. + self.set_handle(signal, "false", "false", "false") + self.expect("process continue", substrs=["stopped and restarted"], matching=False) + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) + + if not test_passing: + # reset signal handling to default + self.set_handle(signal, default_pass, default_stop, default_notify) + return + + # launch again + process = self.launch(target, signal) + + # Make sure we stop at the signal + self.set_handle(signal, "true", "true", "true") + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal) + self.assertTrue(thread.IsValid(), "Thread should be stopped due to a signal") + self.assertTrue(thread.GetStopReasonDataCount() >= 1, "There was data in the event.") + self.assertEqual(thread.GetStopReasonDataAtIndex(0), + process.GetUnixSignals().GetSignalNumberFromName(signal), + "The stop signal was %s" % signal) + + # Continue until we exit. The process should receive the signal. + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), signo) + + # launch again + process = self.launch(target, signal) + + # Make sure we do not stop at the signal. We should still get the notification. Process + # should receive the signal. + self.set_handle(signal, "true", "false", "true") + self.expect("process continue", substrs=["stopped and restarted", signal]) + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), signo) + + # launch again + process = self.launch(target, signal) + + # Make sure we do not stop at the signal, and we do not get the notification. Process + # should receive the signal. + self.set_handle(signal, "true", "false", "false") + self.expect("process continue", substrs=["stopped and restarted"], matching=False) + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), signo) + + # reset signal handling to default + self.set_handle(signal, default_pass, default_stop, default_notify) + + @expectedFailureLinux("llvm.org/pr24530") # the signal the inferior generates gets lost + @expectedFailureDarwin("llvm.org/pr24530") # the signal the inferior generates gets lost + def test_restart_bug(self): + """Test that we catch a signal in the edge case where the process receives it while we are + about to interrupt it""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + bkpt = target.BreakpointCreateByName("main") + self.assertTrue(bkpt.IsValid(), VALID_BREAKPOINT) + + # launch the inferior and don't wait for it to stop + self.dbg.SetAsync(True) + error = lldb.SBError() + listener = lldb.SBListener("my listener") + process = target.Launch (listener, + ["SIGSTOP"], # argv + None, # envp + None, # stdin_path + None, # stdout_path + None, # stderr_path + None, # working directory + 0, # launch flags + False, # Stop at entry + error) # error + + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + + event = lldb.SBEvent() + + # Give the child enough time to reach the breakpoint, + # while clearing out all the pending events. + # The last WaitForEvent call will time out after 2 seconds. + while listener.WaitForEvent(2, event): + if self.TraceOn(): + print("Process changing state to:", self.dbg.StateAsCString(process.GetStateFromEvent(event))) + + # now the process should be stopped + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + self.assertEqual(len(lldbutil.get_threads_stopped_at_breakpoint(process, bkpt)), 1, + "A thread should be stopped at breakpoint") + + # Remove all breakpoints. This makes sure we don't have to single-step over them when we + # resume the process below + target.DeleteAllBreakpoints() + + # resume the process and immediately try to set another breakpoint. When using the remote + # stub, this will trigger a request to stop the process just as it is about to stop + # naturally due to a SIGSTOP signal it raises. Make sure we do not lose this signal. + process.Continue() + self.assertTrue(target.BreakpointCreateByName("handler").IsValid(), VALID_BREAKPOINT) + + # Clear the events again + while listener.WaitForEvent(2, event): + if self.TraceOn(): + print("Process changing state to:", self.dbg.StateAsCString(process.GetStateFromEvent(event))) + + # The process should be stopped due to a signal + self.assertEqual(process.GetState(), lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal) + self.assertTrue(thread.IsValid(), "Thread should be stopped due to a signal") + self.assertTrue(thread.GetStopReasonDataCount() >= 1, "There was data in the event.") + signo = process.GetUnixSignals().GetSignalNumberFromName("SIGSTOP") + self.assertEqual(thread.GetStopReasonDataAtIndex(0), signo, + "The stop signal was %s" % signal) + + # We are done + process.Kill() diff --git a/packages/Python/lldbsuite/test/functionalities/signal/raise/main.c b/packages/Python/lldbsuite/test/functionalities/signal/raise/main.c new file mode 100644 index 000000000000..8827174e758e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/signal/raise/main.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +void handler(int signo) +{ + _exit(signo); +} + +int main (int argc, char *argv[]) +{ +#ifndef __APPLE__ + // Real time signals not supported on apple platforms. + if (signal(SIGRTMIN, handler) == SIG_ERR) + { + perror("signal(SIGRTMIN)"); + return 1; + } +#endif + + if (argc < 2) + { + puts("Please specify a signal to raise"); + return 1; + } + + if (strcmp(argv[1], "SIGSTOP") == 0) + raise(SIGSTOP); +#ifndef __APPLE__ + else if (strcmp(argv[1], "SIGRTMIN") == 0) + raise(SIGRTMIN); +#endif + else + { + printf("Unknown signal: %s\n", argv[1]); + return 1; + } + + return 0; +} + diff --git a/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/Makefile b/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/TestSingleQuoteInFilename.py b/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/TestSingleQuoteInFilename.py new file mode 100644 index 000000000000..6a2dd74d3956 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/TestSingleQuoteInFilename.py @@ -0,0 +1,71 @@ +""" +Test the lldb command line takes a filename with single quote chars. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class SingleQuoteInCommandLineTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + myexe = "path with '09/a.out" + + @classmethod + def classCleanup(cls): + """Cleanup the test byproducts.""" + try: + os.remove("child_send.txt") + os.remove("child_read.txt") + os.remove(cls.myexe) + except: + pass + + @expectedFailureHostWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @no_debug_info_test + def test_lldb_invocation_with_single_quote_in_filename(self): + """Test that 'lldb my_file_name' works where my_file_name is a string with a single quote char in it.""" + import pexpect + self.buildDefault() + system([["cp", "a.out", "\"%s\"" % self.myexe]]) + + # The default lldb prompt. + prompt = "(lldb) " + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s "%s"' % (lldbtest_config.lldbExec, self.lldbOption, self.myexe)) + child = self.child + child.setecho(True) + # Turn on logging for input/output to/from the child. + with open('child_send.txt', 'w') as f_send: + with open('child_read.txt', 'w') as f_read: + child.logfile_send = f_send + child.logfile_read = f_read + + child.expect_exact(prompt) + + child.send("help watchpoint") + child.sendline('') + child.expect_exact(prompt) + + # Now that the necessary logging is done, restore logfile to None to + # stop further logging. + child.logfile_send = None + child.logfile_read = None + + with open('child_send.txt', 'r') as fs: + if self.TraceOn(): + print("\n\nContents of child_send.txt:") + print(fs.read()) + with open('child_read.txt', 'r') as fr: + from_child = fr.read() + if self.TraceOn(): + print("\n\nContents of child_read.txt:") + print(from_child) + + self.expect(from_child, exe=False, + substrs = ["Current executable set to"]) diff --git a/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/main.c b/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/main.c new file mode 100644 index 000000000000..7cee7306547a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/main.c @@ -0,0 +1,7 @@ +#include + +int main(int argc, const char *argv[]) +{ + printf("Hello, world!\n"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/path with '09/.keep b/packages/Python/lldbsuite/test/functionalities/single-quote-in-filename-to-lldb/path with '09/.keep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/Makefile b/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/Makefile new file mode 100644 index 000000000000..45b69a5bb6eb --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +C_SOURCES := with-debug.c without-debug.c + +include $(LEVEL)/Makefile.rules + +without-debug.o: without-debug.c + $(CC) $(CFLAGS_NO_DEBUG) -c without-debug.c diff --git a/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/TestStepNoDebug.py b/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/TestStepNoDebug.py new file mode 100644 index 000000000000..6c1f2c3da41b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/TestStepNoDebug.py @@ -0,0 +1,113 @@ +""" +Test thread step-in, step-over and step-out work with the "Avoid no debug" option. +""" + +from __future__ import print_function + + + +import os +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +import sys +from lldbsuite.test.lldbtest import * + +class ReturnValueTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + def test_step_out_with_python(self): + """Test stepping out using avoid-no-debug with dsyms.""" + self.build() + self.get_to_starting_point() + self.do_step_out_past_nodebug() + + @add_test_categories(['pyapi']) + @expectedFailureGcc("llvm.org/pr19247") + def test_step_over_with_python(self): + """Test stepping over using avoid-no-debug with dwarf.""" + self.build() + self.get_to_starting_point() + self.do_step_over_past_nodebug() + + @add_test_categories(['pyapi']) + @expectedFailureGcc("llvm.org/pr19247") + def test_step_in_with_python(self): + """Test stepping in using avoid-no-debug with dwarf.""" + self.build() + self.get_to_starting_point() + self.do_step_in_past_nodebug() + + def setUp (self): + TestBase.setUp(self) + self.main_source = "with-debug.c" + self.main_source_spec = lldb.SBFileSpec("with-debug.c") + self.dbg.HandleCommand ("settings set target.process.thread.step-out-avoid-nodebug true") + + def tearDown (self): + self.dbg.HandleCommand ("settings set target.process.thread.step-out-avoid-nodebug false") + TestBase.tearDown(self) + + def hit_correct_line (self, pattern): + target_line = line_number (self.main_source, pattern) + self.assertTrue (target_line != 0, "Could not find source pattern " + pattern) + cur_line = self.thread.frames[0].GetLineEntry().GetLine() + self.assertTrue (cur_line == target_line, "Stepped to line %d instead of expected %d with pattern '%s'."%(cur_line, target_line, pattern)) + + def hit_correct_function (self, pattern): + name = self.thread.frames[0].GetFunctionName() + self.assertTrue (pattern in name, "Got to '%s' not the expected function '%s'."%(name, pattern)) + + def get_to_starting_point (self): + exe = os.path.join(os.getcwd(), "a.out") + error = lldb.SBError() + + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + inner_bkpt = self.target.BreakpointCreateBySourceRegex("Stop here and step out of me", self.main_source_spec) + self.assertTrue(inner_bkpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + self.process = self.target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(self.process, PROCESS_IS_VALID) + + # Now finish, and make sure the return value is correct. + threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, inner_bkpt) + self.assertTrue(len(threads) == 1, "Stopped at inner breakpoint.") + self.thread = threads[0] + + def do_step_out_past_nodebug(self): + # The first step out takes us to the called_from_nodebug frame, just to make sure setting + # step-out-avoid-nodebug doesn't change the behavior in frames with debug info. + self.thread.StepOut() + self.hit_correct_line ("intermediate_return_value = called_from_nodebug_actual(some_value)") + self.thread.StepOut() + self.hit_correct_line ("int return_value = no_debug_caller(5, called_from_nodebug)") + + def do_step_over_past_nodebug (self): + self.thread.StepOver() + self.hit_correct_line ("intermediate_return_value = called_from_nodebug_actual(some_value)") + self.thread.StepOver() + self.hit_correct_line ("return intermediate_return_value") + self.thread.StepOver() + # Note, lldb doesn't follow gdb's distinction between "step-out" and "step-over/step-in" + # when exiting a frame. In all cases we leave the pc at the point where we exited the + # frame. In gdb, step-over/step-in move to the end of the line they stepped out to. + # If we ever change this we will need to fix this test. + self.hit_correct_line ("int return_value = no_debug_caller(5, called_from_nodebug)") + + def do_step_in_past_nodebug (self): + self.thread.StepInto() + self.hit_correct_line ("intermediate_return_value = called_from_nodebug_actual(some_value)") + self.thread.StepInto() + self.hit_correct_line ("return intermediate_return_value") + self.thread.StepInto() + # Note, lldb doesn't follow gdb's distinction between "step-out" and "step-over/step-in" + # when exiting a frame. In all cases we leave the pc at the point where we exited the + # frame. In gdb, step-over/step-in move to the end of the line they stepped out to. + # If we ever change this we will need to fix this test. + self.hit_correct_line ("int return_value = no_debug_caller(5, called_from_nodebug)") diff --git a/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/with-debug.c b/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/with-debug.c new file mode 100644 index 000000000000..c7ac309d2c1a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/with-debug.c @@ -0,0 +1,29 @@ +#include + +typedef int (*debug_callee) (int); + +extern int no_debug_caller (int, debug_callee); + +int +called_from_nodebug_actual(int some_value) +{ + int return_value = 0; + return_value = printf ("Length: %d.\n", some_value); + return return_value; // Stop here and step out of me +} + +int +called_from_nodebug(int some_value) +{ + int intermediate_return_value = 0; + intermediate_return_value = called_from_nodebug_actual(some_value); + return intermediate_return_value; +} + +int +main() +{ + int return_value = no_debug_caller(5, called_from_nodebug); + printf ("I got: %d.\n", return_value); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/without-debug.c b/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/without-debug.c new file mode 100644 index 000000000000..d71d74af5e90 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/step-avoids-no-debug/without-debug.c @@ -0,0 +1,17 @@ +typedef int (*debug_callee) (int); + +int +no_debug_caller_intermediate(int input, debug_callee callee) +{ + int return_value = 0; + return_value = callee(input); + return return_value; +} + +int +no_debug_caller (int input, debug_callee callee) +{ + int return_value = 0; + return_value = no_debug_caller_intermediate (input, callee); + return return_value; +} diff --git a/packages/Python/lldbsuite/test/functionalities/stop-hook/Makefile b/packages/Python/lldbsuite/test/functionalities/stop-hook/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/stop-hook/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/stop-hook/TestStopHookCmd.py b/packages/Python/lldbsuite/test/functionalities/stop-hook/TestStopHookCmd.py new file mode 100644 index 000000000000..753491339c4c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/stop-hook/TestStopHookCmd.py @@ -0,0 +1,65 @@ +""" +Test lldb target stop-hook command. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StopHookCmdTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers inside main.cpp. + self.begl = line_number('main.cpp', '// Set breakpoint here to test target stop-hook.') + self.endl = line_number('main.cpp', '// End of the line range for which stop-hook is to be run.') + self.line = line_number('main.cpp', '// Another breakpoint which is outside of the stop-hook range.') + + @no_debug_info_test + def test_not_crashing_if_no_target(self): + """target stop-hook list should not crash if no target has been set.""" + self.runCmd("target stop-hook list", check=False) + + def test(self): + """Test a sequence of target stop-hook commands.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.begl, num_expected_locations=1, loc_exact=True) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("target stop-hook add -f main.cpp -l %d -e %d -o 'expr ptr'" % (self.begl, self.endl)) + + self.expect('target stop-hook list', 'Stop Hook added successfully', + substrs = ['State: enabled', + 'expr ptr']) + + self.runCmd('target stop-hook disable') + + self.expect('target stop-hook list', 'Stop Hook disabled successfully', + substrs = ['State: disabled', + 'expr ptr']) + + self.runCmd('target stop-hook enable') + + self.expect('target stop-hook list', 'Stop Hook enabled successfully', + substrs = ['State: enabled', + 'expr ptr']) + + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + self.runCmd('target stop-hook delete') + + self.expect('target stop-hook list', 'Stop Hook deleted successfully', + substrs = ['No stop hooks.']) diff --git a/packages/Python/lldbsuite/test/functionalities/stop-hook/TestStopHookMechanism.py b/packages/Python/lldbsuite/test/functionalities/stop-hook/TestStopHookMechanism.py new file mode 100644 index 000000000000..7785d85772e2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/stop-hook/TestStopHookMechanism.py @@ -0,0 +1,101 @@ +""" +Test lldb target stop-hook mechanism to see whether it fires off correctly . +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.lldbtest import * + +class StopHookMechanismTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers inside main.cpp. + self.begl = line_number('main.cpp', '// Set breakpoint here to test target stop-hook.') + self.endl = line_number('main.cpp', '// End of the line range for which stop-hook is to be run.') + self.correct_step_line = line_number ('main.cpp', '// We should stop here after stepping.') + self.line = line_number('main.cpp', '// Another breakpoint which is outside of the stop-hook range.') + + @skipIfFreeBSD # llvm.org/pr15037 + @expectedFlakeyLinux('llvm.org/pr15037') # stop-hooks sometimes fail to fire on Linux + @expectedFailureHostWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test(self): + """Test the stop-hook mechanism.""" + self.build() + + import pexpect + exe = os.path.join(os.getcwd(), "a.out") + prompt = "(lldb) " + add_prompt = "Enter your stop hook command(s). Type 'DONE' to end." + add_prompt1 = "> " + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s' % (lldbtest_config.lldbExec, self.lldbOption)) + child = self.child + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + if lldb.remote_platform: + child.expect_exact(prompt) + child.sendline('platform select %s' % lldb.remote_platform.GetName()) + child.expect_exact(prompt) + child.sendline('platform connect %s' % configuration.lldb_platform_url) + child.expect_exact(prompt) + child.sendline('platform settings -w %s' % configuration.lldb_platform_working_dir) + + child.expect_exact(prompt) + child.sendline('target create %s' % exe) + + # Set the breakpoint, followed by the target stop-hook commands. + child.expect_exact(prompt) + child.sendline('breakpoint set -f main.cpp -l %d' % self.begl) + child.expect_exact(prompt) + child.sendline('breakpoint set -f main.cpp -l %d' % self.line) + child.expect_exact(prompt) + child.sendline('target stop-hook add -f main.cpp -l %d -e %d' % (self.begl, self.endl)) + child.expect_exact(add_prompt) + child.expect_exact(add_prompt1) + child.sendline('expr ptr') + child.expect_exact(add_prompt1) + child.sendline('DONE') + child.expect_exact(prompt) + child.sendline('target stop-hook list') + + # Now run the program, expect to stop at the first breakpoint which is within the stop-hook range. + child.expect_exact(prompt) + child.sendline('run') + # Make sure we see the stop hook text from the stop of the process from the run hitting the first breakpoint + child.expect_exact('(void *) $') + child.expect_exact(prompt) + child.sendline('thread step-over') + # Expecting to find the output emitted by the firing of our stop hook. + child.expect_exact('(void *) $') + # This is orthogonal to the main stop hook test, but this example shows a bug in + # CLANG where the line table entry for the "return -1" actually includes some code + # from the other branch of the if/else, so we incorrectly stop at the "return -1" line. + # I fixed that in lldb and I'm sticking in a test here because I don't want to have to + # make up a whole nother test case for it. + child.sendline('frame info') + at_line = 'at main.cpp:%d' % (self.correct_step_line) + print('expecting "%s"' % at_line) + child.expect_exact(at_line) + + # Now continue the inferior, we'll stop at another breakpoint which is outside the stop-hook range. + child.sendline('process continue') + child.expect_exact('// Another breakpoint which is outside of the stop-hook range.') + #self.DebugPExpect(child) + child.sendline('thread step-over') + child.expect_exact('// Another breakpoint which is outside of the stop-hook range.') + #self.DebugPExpect(child) + # Verify that the 'Stop Hooks' mechanism is NOT BEING fired off. + self.expect(child.before, exe=False, matching=False, + substrs = ['(void *) $']) diff --git a/packages/Python/lldbsuite/test/functionalities/stop-hook/main.cpp b/packages/Python/lldbsuite/test/functionalities/stop-hook/main.cpp new file mode 100644 index 000000000000..c10c1e5ef0d2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/stop-hook/main.cpp @@ -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 +#include + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); + + return val; +} + +int b(int val) +{ + int rc = c(val); + void *ptr = malloc(1024); + if (!ptr) // Set breakpoint here to test target stop-hook. + return -1; + else + printf("ptr=%p\n", ptr); // We should stop here after stepping. + return rc; // End of the line range for which stop-hook is to be run. +} + +int c(int val) +{ + return val + 3; +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); + printf("a(1) returns %d\n", A1); + + int C2 = c(2); // Another breakpoint which is outside of the stop-hook range. + printf("c(2) returns %d\n", C2); + + int A3 = a(3); + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/Makefile b/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/Makefile new file mode 100644 index 000000000000..035413ff763d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/TestStopHookMultipleThreads.py b/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/TestStopHookMultipleThreads.py new file mode 100644 index 000000000000..c7fb53d495e6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/TestStopHookMultipleThreads.py @@ -0,0 +1,77 @@ +""" +Test that lldb stop-hook works for multiple threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.lldbtest import * + +class StopHookForMultipleThreadsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.first_stop = line_number(self.source, '// Set break point at this line, and add a stop-hook.') + self.thread_function = line_number(self.source, '// Break here to test that the stop-hook mechanism works for multiple threads.') + # Build dictionary to have unique executable names for each test method. + self.exe_name = self.testMethodName + self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name} + + @expectedFlakeyFreeBSD("llvm.org/pr15037") + @expectedFlakeyLinux("llvm.org/pr15037") # stop hooks sometimes fail to fire on Linux + @expectedFailureHostWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_stop_hook_multiple_threads(self): + """Test that lldb stop-hook works for multiple threads.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + import pexpect + exe = os.path.join(os.getcwd(), self.exe_name) + prompt = "(lldb) " + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('%s %s' % (lldbtest_config.lldbExec, self.lldbOption)) + child = self.child + # Turn on logging for what the child sends back. + if self.TraceOn(): + child.logfile_read = sys.stdout + + if lldb.remote_platform: + child.expect_exact(prompt) + child.sendline('platform select %s' % lldb.remote_platform.GetName()) + child.expect_exact(prompt) + child.sendline('platform connect %s' % configuration.lldb_platform_url) + child.expect_exact(prompt) + child.sendline('platform settings -w %s' % configuration.lldb_platform_working_dir) + + child.expect_exact(prompt) + child.sendline('target create %s' % exe) + + # Set the breakpoint, followed by the target stop-hook commands. + child.expect_exact(prompt) + child.sendline('breakpoint set -f main.cpp -l %d' % self.first_stop) + child.expect_exact(prompt) + child.sendline('breakpoint set -f main.cpp -l %d' % self.thread_function) + child.expect_exact(prompt) + + # Now run the program, expect to stop at the first breakpoint which is within the stop-hook range. + child.sendline('run') + child.expect_exact("Process") # 'Process 2415 launched', 'Process 2415 stopped' + child.expect_exact(prompt) + child.sendline('target stop-hook add -o "frame variable --show-globals g_val"') + child.expect_exact("Stop hook") # 'Stop hook #1 added.' + child.expect_exact(prompt) + + # Continue and expect to find the output emitted by the firing of our stop hook. + child.sendline('continue') + child.expect_exact('(uint32_t) ::g_val = ') diff --git a/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/main.cpp b/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/main.cpp new file mode 100644 index 000000000000..e193ae18e2c1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/stop-hook/multiple_threads/main.cpp @@ -0,0 +1,77 @@ +//===-- 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 +#include +#include +#include + +std::default_random_engine g_random_engine{std::random_device{}()}; +std::uniform_int_distribution<> g_distribution{0, 3000000}; + +uint32_t g_val = 0; + +uint32_t +access_pool (bool flag = false) +{ + static std::mutex g_access_mutex; + if (!flag) + g_access_mutex.lock(); + + uint32_t old_val = g_val; + if (flag) + g_val = old_val + 1; + + if (!flag) + g_access_mutex.unlock(); + return g_val; +} + +void +thread_func (uint32_t thread_index) +{ + // Break here to test that the stop-hook mechanism works for multiple threads. + printf ("%s (thread index = %u) startng...\n", __FUNCTION__, thread_index); + + uint32_t count = 0; + uint32_t val; + while (count++ < 15) + { + // random micro second sleep from zero to 3 seconds + int usec = g_distribution(g_random_engine); + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + std::this_thread::sleep_for(std::chrono::microseconds{usec}); + + if (count < 7) + val = access_pool (); + else + val = access_pool (true); + + printf ("%s (thread = %u) after usleep access_pool returns %d (count=%d)...\n", __FUNCTION__, thread_index, val, count); + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); +} + + +int main (int argc, char const *argv[]) +{ + std::thread threads[3]; + + printf ("Before turning all three threads loose...\n"); // Set break point at this line, and add a stop-hook. + // Create 3 threads + for (auto &thread : threads) + thread = std::thread{thread_func, std::distance(threads, &thread)}; + + // Join all of our threads + for (auto &thread : threads) + thread.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/target_command/Makefile b/packages/Python/lldbsuite/test/functionalities/target_command/Makefile new file mode 100644 index 000000000000..9117ab9388b7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/target_command/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +# Example: +# +# C_SOURCES := b.c +# EXE := b.out + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/target_command/TestTargetCommand.py b/packages/Python/lldbsuite/test/functionalities/target_command/TestTargetCommand.py new file mode 100644 index 000000000000..0ab965d2aa16 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/target_command/TestTargetCommand.py @@ -0,0 +1,201 @@ +""" +Test some target commands: create, list, select, variable. +""" + +from __future__ import print_function + + + +import lldb +import sys +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class targetCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.line_b = line_number('b.c', '// Set break point at this line.') + self.line_c = line_number('c.c', '// Set break point at this line.') + + def test_target_command(self): + """Test some target commands: create, list, select.""" + da = {'C_SOURCES': 'a.c', 'EXE': 'a.out'} + self.build(dictionary=da) + self.addTearDownCleanup(dictionary=da) + + db = {'C_SOURCES': 'b.c', 'EXE': 'b.out'} + self.build(dictionary=db) + self.addTearDownCleanup(dictionary=db) + + dc = {'C_SOURCES': 'c.c', 'EXE': 'c.out'} + self.build(dictionary=dc) + self.addTearDownCleanup(dictionary=dc) + + self.do_target_command() + + # rdar://problem/9763907 + # 'target variable' command fails if the target program has been run + @expectedFailureAndroid(archs=['aarch64']) + def test_target_variable_command(self): + """Test 'target variable' command before and after starting the inferior.""" + d = {'C_SOURCES': 'globals.c', 'EXE': 'globals'} + self.build(dictionary=d) + self.addTearDownCleanup(dictionary=d) + + self.do_target_variable_command('globals') + + @expectedFailureAndroid(archs=['aarch64']) + def test_target_variable_command_no_fail(self): + """Test 'target variable' command before and after starting the inferior.""" + d = {'C_SOURCES': 'globals.c', 'EXE': 'globals'} + self.build(dictionary=d) + self.addTearDownCleanup(dictionary=d) + + self.do_target_variable_command_no_fail('globals') + + def do_target_command(self): + """Exercise 'target create', 'target list', 'target select' commands.""" + exe_a = os.path.join(os.getcwd(), "a.out") + exe_b = os.path.join(os.getcwd(), "b.out") + exe_c = os.path.join(os.getcwd(), "c.out") + + self.runCmd("target list") + output = self.res.GetOutput() + if output.startswith("No targets"): + # We start from index 0. + base = 0 + else: + # Find the largest index of the existing list. + import re + pattern = re.compile("target #(\d+):") + for line in reversed(output.split(os.linesep)): + match = pattern.search(line) + if match: + # We will start from (index + 1) .... + base = int(match.group(1), 10) + 1 + #print("base is:", base) + break; + + self.runCmd("target create " + exe_a, CURRENT_EXECUTABLE_SET) + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("target create " + exe_b, CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line (self, 'b.c', self.line_b, num_expected_locations=1, loc_exact=True) + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("target create " + exe_c, CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line (self, 'c.c', self.line_c, num_expected_locations=1, loc_exact=True) + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("target list") + + self.runCmd("target select %d" % base) + self.runCmd("thread backtrace") + + self.runCmd("target select %d" % (base + 2)) + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['c.c:%d' % self.line_c, + 'stop reason = breakpoint']) + + self.runCmd("target select %d" % (base + 1)) + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['b.c:%d' % self.line_b, + 'stop reason = breakpoint']) + + self.runCmd("target list") + + def do_target_variable_command(self, exe_name): + """Exercise 'target variable' command before and after starting the inferior.""" + self.runCmd("file " + exe_name, CURRENT_EXECUTABLE_SET) + + self.expect("target variable my_global_char", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["my_global_char", "'X'"]) + self.expect("target variable my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_global_str', '"abc"']) + self.expect("target variable my_static_int", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_static_int', '228']) + self.expect("target variable my_global_str_ptr", matching=False, + substrs = ['"abc"']) + self.expect("target variable *my_global_str_ptr", matching=True, + substrs = ['"abc"']) + self.expect("target variable *my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['a']) + + self.runCmd("b main") + self.runCmd("run") + + self.expect("target variable my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_global_str', '"abc"']) + self.expect("target variable my_static_int", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_static_int', '228']) + self.expect("target variable my_global_str_ptr", matching=False, + substrs = ['"abc"']) + self.expect("target variable *my_global_str_ptr", matching=True, + substrs = ['"abc"']) + self.expect("target variable *my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['a']) + self.expect("target variable my_global_char", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["my_global_char", "'X'"]) + + self.runCmd("c") + + # rdar://problem/9763907 + # 'target variable' command fails if the target program has been run + self.expect("target variable my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_global_str', '"abc"']) + self.expect("target variable my_static_int", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_static_int', '228']) + self.expect("target variable my_global_str_ptr", matching=False, + substrs = ['"abc"']) + self.expect("target variable *my_global_str_ptr", matching=True, + substrs = ['"abc"']) + self.expect("target variable *my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['a']) + self.expect("target variable my_global_char", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["my_global_char", "'X'"]) + + def do_target_variable_command_no_fail(self, exe_name): + """Exercise 'target variable' command before and after starting the inferior.""" + self.runCmd("file " + exe_name, CURRENT_EXECUTABLE_SET) + + self.expect("target variable my_global_char", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["my_global_char", "'X'"]) + self.expect("target variable my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_global_str', '"abc"']) + self.expect("target variable my_static_int", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_static_int', '228']) + self.expect("target variable my_global_str_ptr", matching=False, + substrs = ['"abc"']) + self.expect("target variable *my_global_str_ptr", matching=True, + substrs = ['"abc"']) + self.expect("target variable *my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['a']) + + self.runCmd("b main") + self.runCmd("run") + + # New feature: you don't need to specify the variable(s) to 'target vaiable'. + # It will find all the global and static variables in the current compile unit. + self.expect("target variable", + substrs = ['my_global_char', + 'my_global_str', + 'my_global_str_ptr', + 'my_static_int']) + + self.expect("target variable my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_global_str', '"abc"']) + self.expect("target variable my_static_int", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['my_static_int', '228']) + self.expect("target variable my_global_str_ptr", matching=False, + substrs = ['"abc"']) + self.expect("target variable *my_global_str_ptr", matching=True, + substrs = ['"abc"']) + self.expect("target variable *my_global_str", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['a']) + self.expect("target variable my_global_char", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["my_global_char", "'X'"]) diff --git a/packages/Python/lldbsuite/test/functionalities/target_command/a.c b/packages/Python/lldbsuite/test/functionalities/target_command/a.c new file mode 100644 index 000000000000..9d0706a0862d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/target_command/a.c @@ -0,0 +1,16 @@ +//===-- 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, const char* argv[]) +{ + int *null_ptr = 0; + printf("Hello, segfault!\n"); + printf("Now crash %d\n", *null_ptr); // Crash here. +} diff --git a/packages/Python/lldbsuite/test/functionalities/target_command/b.c b/packages/Python/lldbsuite/test/functionalities/target_command/b.c new file mode 100644 index 000000000000..62ec97f43284 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/target_command/b.c @@ -0,0 +1,13 @@ +//===-- 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[]) +{ + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/target_command/c.c b/packages/Python/lldbsuite/test/functionalities/target_command/c.c new file mode 100644 index 000000000000..7c362cc437af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/target_command/c.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; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/functionalities/target_command/globals.c b/packages/Python/lldbsuite/test/functionalities/target_command/globals.c new file mode 100644 index 000000000000..6902bc415681 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/target_command/globals.c @@ -0,0 +1,25 @@ +//===-- 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 + +char my_global_char = 'X'; +const char* my_global_str = "abc"; +const char **my_global_str_ptr = &my_global_str; +static int my_static_int = 228; + +int main (int argc, char const *argv[]) +{ + printf("global char: %c\n", my_global_char); + + printf("global str: %s\n", my_global_str); + + printf("argc + my_static_int = %d\n", (argc + my_static_int)); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/Makefile new file mode 100644 index 000000000000..644e2971a2c1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py b/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py new file mode 100644 index 000000000000..8184ddcfbf48 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py @@ -0,0 +1,53 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class NumberOfThreadsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test(self): + """Test number of threads.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 1 location. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1) + + # The breakpoint list should show 3 locations. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.line]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped once. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Using std::thread may involve extra threads, so we assert that there are + # at least 4 rather than exactly 4. + self.assertTrue(num_threads >= 4, 'Number of expected threads and actual threads do not match.') diff --git a/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/Makefile new file mode 100644 index 000000000000..24e68012ebd3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXXFLAGS += -std=c++11 +CXX_SOURCES := ParallelTask.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/ParallelTask.cpp b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/ParallelTask.cpp new file mode 100755 index 000000000000..71fb8e3bb565 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/ParallelTask.cpp @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include +#include +#include + +class TaskPoolImpl +{ +public: + TaskPoolImpl(uint32_t num_threads) : + m_stop(false) + { + for (uint32_t i = 0; i < num_threads; ++i) + m_threads.emplace_back(Worker, this); + } + + ~TaskPoolImpl() + { + Stop(); + } + + template + std::future::type> + AddTask(F&& f, Args&&... args) + { + auto task = std::make_shared::type()>>( + std::bind(std::forward(f), std::forward(args)...)); + + std::unique_lock lock(m_tasks_mutex); + assert(!m_stop && "Can't add task to TaskPool after it is stopped"); + m_tasks.emplace([task](){ (*task)(); }); + lock.unlock(); + m_tasks_cv.notify_one(); + + return task->get_future(); + } + + void + Stop() + { + std::unique_lock lock(m_tasks_mutex); + m_stop = true; + m_tasks_mutex.unlock(); + m_tasks_cv.notify_all(); + for (auto& t : m_threads) + t.join(); + } + +private: + static void + Worker(TaskPoolImpl* pool) + { + while (true) + { + std::unique_lock lock(pool->m_tasks_mutex); + if (pool->m_tasks.empty()) + pool->m_tasks_cv.wait(lock, [pool](){ return !pool->m_tasks.empty() || pool->m_stop; }); + if (pool->m_tasks.empty()) + break; + + std::function f = pool->m_tasks.front(); + pool->m_tasks.pop(); + lock.unlock(); + + f(); + } + } + + std::queue> m_tasks; + std::mutex m_tasks_mutex; + std::condition_variable m_tasks_cv; + bool m_stop; + std::vector m_threads; +}; + +class TaskPool +{ +public: + // Add a new task to the thread pool and return a std::future belongs for the newly created task. + // The caller of this function have to wait on the future for this task to complete. + template + static std::future::type> + AddTask(F&& f, Args&&... args) + { + return GetImplementation().AddTask(std::forward(f), std::forward(args)...); + } + + // Run all of the specified tasks on the thread pool and wait until all of them are finished + // before returning + template + static void + RunTasks(T&&... t) + { + RunTaskImpl::Run(std::forward(t)...); + } + +private: + static TaskPoolImpl& + GetImplementation() + { + static TaskPoolImpl g_task_pool_impl(std::thread::hardware_concurrency()); + return g_task_pool_impl; + } + + template + struct RunTaskImpl; +}; + +template +struct TaskPool::RunTaskImpl +{ + static void + Run(H&& h, T&&... t) + { + auto f = AddTask(std::forward(h)); + RunTaskImpl::Run(std::forward(t)...); + f.wait(); + } +}; + +template<> +struct TaskPool::RunTaskImpl<> +{ + static void + Run() {} +}; + +int main() +{ + std::vector> tasks; + for (int i = 0; i < 100000; ++i) + { + tasks.emplace_back(TaskPool::AddTask([](int i){ + uint32_t s = 0; + for (int j = 0; j <= i; ++j) + s += j; + return s; + }, + i)); + } + + for (auto& it : tasks) // Set breakpoint here + it.wait(); + + TaskPool::RunTasks( + []() { return 1; }, + []() { return "aaaa"; } + ); +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/TestBacktraceAll.py b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/TestBacktraceAll.py new file mode 100644 index 000000000000..91bc68577a43 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/TestBacktraceAll.py @@ -0,0 +1,57 @@ +""" +Test regression for Bug 25251. +""" + +import os, time +import unittest2 +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointAfterJoinTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('ParallelTask.cpp', '// Set breakpoint here') + + @skipIfTargetAndroid(archs=["arm"]) # The android-arm compiler can't compile the inferior + # because of an issue around std::future. + # TODO: Change the test to don't depend on std::future + def test(self): + """Test breakpoint handling after a thread join.""" + self.build(dictionary=self.getBuildFlags()) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint + lldbutil.run_break_set_by_file_and_line (self, "ParallelTask.cpp", self.breakpoint, num_expected_locations=-1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'ParallelTask.cpp', line = %d, exact_match = 0" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This should not result in a segmentation fault + self.expect("thread backtrace all", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Run to completion + self.runCmd("continue") + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/Makefile new file mode 100644 index 000000000000..67aa16625bff --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py new file mode 100644 index 000000000000..43397a122391 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py @@ -0,0 +1,77 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointAfterJoinTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + def test(self): + """Test breakpoint handling after a thread join.""" + self.build(dictionary=self.getBuildFlags()) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # The exit probably occurred during breakpoint handling, but it isn't + # guaranteed. The main thing we're testing here is that the debugger + # handles this cleanly is some way. + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see at least six threads + self.assertTrue(num_threads >= 6, 'Number of expected threads and actual threads do not match.') + + # Make sure all threads are stopped + for i in range(0, num_threads): + self.assertTrue(process.GetThreadAtIndex(i).IsStopped(), + "Thread {0} didn't stop during breakpoint.".format(i)) + + # Run to completion + self.runCmd("continue") + + # If the process hasn't exited, collect some information + if process.GetState() != lldb.eStateExited: + self.runCmd("thread list") + self.runCmd("process status") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/main.cpp new file mode 100644 index 000000000000..a63079524ee2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/main.cpp @@ -0,0 +1,118 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while a breakpoint is being handled in another thread. This may not always +// happen because it's possible that the exiting thread will exit before the +// breakpoint is hit. The test case should be flexible enough to treat that +// as success. + +#include +#include +#include + +volatile int g_test = 0; + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize all the threads. +std::atomic_int g_barrier1; + +// A barrier to keep the threads from exiting until after the breakpoint has +// been passed. +std::atomic_int g_barrier2; + +void * +break_thread_func () +{ + // Wait until all the threads are running + pseudo_barrier_wait(g_barrier1); + + // Wait for the join thread to join + std::this_thread::sleep_for(std::chrono::microseconds(50)); + + // Do something + g_test++; // Set breakpoint here + + // Synchronize after the breakpoint + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; +} + +void * +wait_thread_func () +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait until the breakpoint has been passed + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; +} + +void * +join_thread_func (void *input) +{ + std::thread *thread_to_join = (std::thread *)input; + + // Sync up with the rest of the threads. + pseudo_barrier_wait(g_barrier1); + + // Join the other thread + thread_to_join->join(); + + // Return + return NULL; +} + +int main () +{ + // The first barrier waits for the non-joining threads to start. + // This thread will also participate in that barrier. + // The idea here is to guarantee that the joining thread will be + // last in the internal list maintained by the debugger. + pseudo_barrier_init(g_barrier1, 5); + + // The second barrier keeps the waiting threads around until the breakpoint + // has been passed. + pseudo_barrier_init(g_barrier2, 4); + + // Create a thread to hit the breakpoint + std::thread thread_1(break_thread_func); + + // Create more threads to slow the debugger down during processing. + std::thread thread_2(wait_thread_func); + std::thread thread_3(wait_thread_func); + std::thread thread_4(wait_thread_func); + + // Create a thread to join the breakpoint thread + std::thread thread_5(join_thread_func, &thread_1); + + // Wait for the threads to finish + thread_5.join(); // implies thread_1 is already finished + thread_4.join(); + thread_3.join(); + thread_2.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile new file mode 100644 index 000000000000..469c0809aa24 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py new file mode 100644 index 000000000000..9eb25b68765a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py @@ -0,0 +1,491 @@ +""" +A stress-test of sorts for LLDB's handling of threads in the inferior. + +This test sets a breakpoint in the main thread where test parameters (numbers of +threads) can be adjusted, runs the inferior to that point, and modifies the +locals that control the event thread counts. This test also sets a breakpoint in +breakpoint_func (the function executed by each 'breakpoint' thread) and a +watchpoint on a global modified in watchpoint_func. The inferior is continued +until exit or a crash takes place, and the number of events seen by LLDB is +verified to match the expected number of events. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipIfWindows +@expectedFailureAll(archs=['mips64', 'mips64el']) # Atomic sequences are not supported yet for MIPS in LLDB. +class ConcurrentEventsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # + ## Tests for multiple threads that generate a single event. + # + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_breakpoints(self): + """Test 100 breakpoints from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_watchpoints(self): + """Test 100 watchpoints from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_signals(self): + """Test 100 signals from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_crash(self): + """Test 100 threads that cause a segfault.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=100) + + + # + ## Tests for concurrent signal and breakpoint + # + @skipIfFreeBSD # timing out on buildbot + def test_signal_break(self): + """Test signal and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_delay_signal_break(self): + """Test (1-second delay) signal and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_delay_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_signal_delay_break(self): + """Test signal and a (1 second delay) breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_breakpoint_threads=1, num_signal_threads=1) + + + # + ## Tests for concurrent watchpoint and breakpoint + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watch_break(self): + """Test watchpoint and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delay_watch_break(self): + """Test (1-second delay) watchpoint and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_delay_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watch_break_delay(self): + """Test watchpoint and a (1 second delay) breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_breakpoint_threads=1, num_watchpoint_threads=1) + + # + ## Tests for concurrent signal and watchpoint + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch(self): + """Test a watchpoint and a signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delay_signal_watch(self): + """Test a watchpoint and a (1 second delay) signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_signal_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + @expectedFailureAll("llvm.org/pr16714", oslist=["linux"], archs=["i386"]) + def test_signal_delay_watch(self): + """Test a (1 second delay) watchpoint and a signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, num_delay_watchpoint_threads=1) + + + # + ## Tests for multiple breakpoint threads + # + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoint_threads(self): + """Test two threads that trigger a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2) + + @skipIfFreeBSD # timing out on buildbot + def test_breakpoint_one_delay_breakpoint_threads(self): + """Test threads that trigger a breakpoint where one thread has a 1 second delay. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoints_one_signal(self): + """Test two threads that trigger a breakpoint and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_breakpoint_delay_breakpoint_one_signal(self): + """Test two threads that trigger a breakpoint (one with a 1 second delay) and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1, + num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoints_one_delay_signal(self): + """Test two threads that trigger a breakpoint and one (1 second delay) signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_delay_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_breakpoints_one_watchpoint(self): + """Test two threads that trigger a breakpoint and one watchpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_breakpoints_delayed_breakpoint_one_watchpoint(self): + """Test a breakpoint, a delayed breakpoint, and one watchpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1, + num_watchpoint_threads=1) + + # + ## Tests for multiple watchpoint threads + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoint_threads(self): + """Test two threads that trigger a watchpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watchpoint_with_delay_watchpoint_threads(self): + """Test two threads that trigger a watchpoint where one thread has a 1 second delay. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=1, + num_delay_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_breakpoint(self): + """Test two threads that trigger a watchpoint and one breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_delay_breakpoint(self): + """Test two threads that trigger a watchpoint and one (1 second delay) breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_delay_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watchpoint_delay_watchpoint_one_breakpoint(self): + """Test two threads that trigger a watchpoint (one with a 1 second delay) and one breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=1, + num_delay_watchpoint_threads=1, + num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_signal(self): + """Test two threads that trigger a watchpoint and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_signal_threads=1) + + # + ## Test for watchpoint, signal and breakpoint happening concurrently + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test a signal/watchpoint/breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, + num_watchpoint_threads=1, + num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test one signal thread with 5 watchpoint and breakpoint threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, + num_watchpoint_threads=5, + num_breakpoint_threads=5) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test with 5 watchpoint and breakpoint threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=5, + num_breakpoint_threads=5) + + + # + ## Test for crashing threads happening concurrently with other events + # + @skipIfFreeBSD # timing out on buildbot + def test_crash_with_break(self): + """ Test a thread that crashes while another thread hits a breakpoint.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_crash_with_watchpoint(self): + """ Test a thread that crashes while another thread hits a watchpoint.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_crash_with_signal(self): + """ Test a thread that crashes while another thread generates a signal.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_crash_with_watchpoint_breakpoint_signal(self): + """ Test a thread that crashes while other threads generate a signal and hit a watchpoint and breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, + num_breakpoint_threads=1, + num_signal_threads=1, + num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delayed_crash_with_breakpoint_watchpoint(self): + """ Test a thread with a delayed crash while other threads hit a watchpoint and a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_crash_threads=1, + num_breakpoint_threads=1, + num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_delayed_crash_with_breakpoint_signal(self): + """ Test a thread with a delayed crash while other threads generate a signal and hit a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_crash_threads=1, + num_breakpoint_threads=1, + num_signal_threads=1) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.filename = 'main.cpp' + self.thread_breakpoint_line = line_number(self.filename, '// Set breakpoint here') + self.setup_breakpoint_line = line_number(self.filename, '// Break here and adjust num') + self.finish_breakpoint_line = line_number(self.filename, '// Break here and verify one thread is active') + + def describe_threads(self): + ret = [] + for x in self.inferior_process: + id = x.GetIndexID() + reason = x.GetStopReason() + status = "stopped" if x.IsStopped() else "running" + reason_str = lldbutil.stop_reason_to_str(reason) + if reason == lldb.eStopReasonBreakpoint: + bpid = x.GetStopReasonDataAtIndex(0) + bp = self.inferior_target.FindBreakpointByID(bpid) + reason_str = "%s hit %d times" % (lldbutil.get_description(bp), bp.GetHitCount()) + elif reason == lldb.eStopReasonWatchpoint: + watchid = x.GetStopReasonDataAtIndex(0) + watch = self.inferior_target.FindWatchpointByID(watchid) + reason_str = "%s hit %d times" % (lldbutil.get_description(watch), watch.GetHitCount()) + elif reason == lldb.eStopReasonSignal: + signals = self.inferior_process.GetUnixSignals() + signal_name = signals.GetSignalAsCString(x.GetStopReasonDataAtIndex(0)) + reason_str = "signal %s" % signal_name + + location = "\t".join([lldbutil.get_description(x.GetFrameAtIndex(i)) for i in range(x.GetNumFrames())]) + ret.append("thread %d %s due to %s at\n\t%s" % (id, status, reason_str, location)) + return ret + + def add_breakpoint(self, line, descriptions): + """ Adds a breakpoint at self.filename:line and appends its description to descriptions, and + returns the LLDB SBBreakpoint object. + """ + + bpno = lldbutil.run_break_set_by_file_and_line(self, self.filename, line, num_expected_locations=-1) + bp = self.inferior_target.FindBreakpointByID(bpno) + descriptions.append(": file = 'main.cpp', line = %d" % self.finish_breakpoint_line) + return bp + + def inferior_done(self): + """ Returns true if the inferior is done executing all the event threads (and is stopped at self.finish_breakpoint, + or has terminated execution. + """ + return self.finish_breakpoint.GetHitCount() > 0 or \ + self.crash_count > 0 or \ + self.inferior_process.GetState() == lldb.eStateExited + + def count_signaled_threads(self): + count = 0 + for thread in self.inferior_process: + if thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex(0) == self.inferior_process.GetUnixSignals().GetSignalNumberFromName('SIGUSR1'): + count += 1 + return count + + def do_thread_actions(self, + num_breakpoint_threads = 0, + num_signal_threads = 0, + num_watchpoint_threads = 0, + num_crash_threads = 0, + num_delay_breakpoint_threads = 0, + num_delay_signal_threads = 0, + num_delay_watchpoint_threads = 0, + num_delay_crash_threads = 0): + """ Sets a breakpoint in the main thread where test parameters (numbers of threads) can be adjusted, runs the inferior + to that point, and modifies the locals that control the event thread counts. Also sets a breakpoint in + breakpoint_func (the function executed by each 'breakpoint' thread) and a watchpoint on a global modified in + watchpoint_func. The inferior is continued until exit or a crash takes place, and the number of events seen by LLDB + is verified to match the expected number of events. + """ + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Get the target + self.inferior_target = self.dbg.GetSelectedTarget() + + expected_bps = [] + + # Initialize all the breakpoints (main thread/aux thread) + self.setup_breakpoint = self.add_breakpoint(self.setup_breakpoint_line, expected_bps) + self.finish_breakpoint = self.add_breakpoint(self.finish_breakpoint_line, expected_bps) + + # Set the thread breakpoint + if num_breakpoint_threads + num_delay_breakpoint_threads > 0: + self.thread_breakpoint = self.add_breakpoint(self.thread_breakpoint_line, expected_bps) + + # Verify breakpoints + self.expect("breakpoint list -f", "Breakpoint locations shown correctly", substrs = expected_bps) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Check we are at line self.setup_breakpoint + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Initialize the (single) watchpoint on the global variable (g_watchme) + if num_watchpoint_threads + num_delay_watchpoint_threads > 0: + self.runCmd("watchpoint set variable g_watchme") + for w in self.inferior_target.watchpoint_iter(): + self.thread_watchpoint = w + self.assertTrue("g_watchme" in str(self.thread_watchpoint), "Watchpoint location not shown correctly") + + # Get the process + self.inferior_process = self.inferior_target.GetProcess() + + # We should be stopped at the setup site where we can set the number of + # threads doing each action (break/crash/signal/watch) + self.assertEqual(self.inferior_process.GetNumThreads(), 1, 'Expected to stop before any additional threads are spawned.') + + self.runCmd("expr num_breakpoint_threads=%d" % num_breakpoint_threads) + self.runCmd("expr num_crash_threads=%d" % num_crash_threads) + self.runCmd("expr num_signal_threads=%d" % num_signal_threads) + self.runCmd("expr num_watchpoint_threads=%d" % num_watchpoint_threads) + + self.runCmd("expr num_delay_breakpoint_threads=%d" % num_delay_breakpoint_threads) + self.runCmd("expr num_delay_crash_threads=%d" % num_delay_crash_threads) + self.runCmd("expr num_delay_signal_threads=%d" % num_delay_signal_threads) + self.runCmd("expr num_delay_watchpoint_threads=%d" % num_delay_watchpoint_threads) + + # Continue the inferior so threads are spawned + self.runCmd("continue") + + # Make sure we see all the threads. The inferior program's threads all synchronize with a pseudo-barrier; that is, + # the inferior program ensures all threads are started and running before any thread triggers its 'event'. + num_threads = self.inferior_process.GetNumThreads() + expected_num_threads = num_breakpoint_threads + num_delay_breakpoint_threads \ + + num_signal_threads + num_delay_signal_threads \ + + num_watchpoint_threads + num_delay_watchpoint_threads \ + + num_crash_threads + num_delay_crash_threads + 1 + self.assertEqual(num_threads, expected_num_threads, + 'Expected to see %d threads, but seeing %d. Details:\n%s' % (expected_num_threads, + num_threads, + "\n\t".join(self.describe_threads()))) + + self.signal_count = self.count_signaled_threads() + self.crash_count = len(lldbutil.get_crashed_threads(self, self.inferior_process)) + + # Run to completion (or crash) + while not self.inferior_done(): + if self.TraceOn(): + self.runCmd("thread backtrace all") + self.runCmd("continue") + self.signal_count += self.count_signaled_threads() + self.crash_count += len(lldbutil.get_crashed_threads(self, self.inferior_process)) + + if num_crash_threads > 0 or num_delay_crash_threads > 0: + # Expecting a crash + self.assertTrue(self.crash_count > 0, + "Expecting at least one thread to crash. Details: %s" % "\t\n".join(self.describe_threads())) + + # Ensure the zombie process is reaped + self.runCmd("process kill") + + elif num_crash_threads == 0 and num_delay_crash_threads == 0: + # There should be a single active thread (the main one) which hit the breakpoint after joining + self.assertEqual(1, self.finish_breakpoint.GetHitCount(), "Expected main thread (finish) breakpoint to be hit once") + + num_threads = self.inferior_process.GetNumThreads() + self.assertEqual(1, num_threads, "Expecting 1 thread but seeing %d. Details:%s" % (num_threads, + "\n\t".join(self.describe_threads()))) + self.runCmd("continue") + + # The inferior process should have exited without crashing + self.assertEqual(0, self.crash_count, "Unexpected thread(s) in crashed state") + self.assertEqual(self.inferior_process.GetState(), lldb.eStateExited, PROCESS_EXITED) + + # Verify the number of actions took place matches expected numbers + expected_breakpoint_threads = num_delay_breakpoint_threads + num_breakpoint_threads + breakpoint_hit_count = self.thread_breakpoint.GetHitCount() if expected_breakpoint_threads > 0 else 0 + self.assertEqual(expected_breakpoint_threads, breakpoint_hit_count, + "Expected %d breakpoint hits, but got %d" % (expected_breakpoint_threads, breakpoint_hit_count)) + + expected_signal_threads = num_delay_signal_threads + num_signal_threads + self.assertEqual(expected_signal_threads, self.signal_count, + "Expected %d stops due to signal delivery, but got %d" % (expected_signal_threads, self.signal_count)) + + expected_watchpoint_threads = num_delay_watchpoint_threads + num_watchpoint_threads + watchpoint_hit_count = self.thread_watchpoint.GetHitCount() if expected_watchpoint_threads > 0 else 0 + self.assertEqual(expected_watchpoint_threads, watchpoint_hit_count, + "Expected %d watchpoint hits, got %d" % (expected_watchpoint_threads, watchpoint_hit_count)) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp new file mode 100644 index 000000000000..ac2535cd2bff --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp @@ -0,0 +1,200 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which multiple events +// (breakpoints, watchpoints, crashes, and signal generation/delivery) happen +// from multiple threads. The test expects the debugger to set a breakpoint on +// the main thread (before any worker threads are spawned) and modify variables +// which control the number of threads that are spawned for each action. + +#include +#include +using namespace std; + +#include + +#include +#include +#include + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +typedef std::vector > action_counts; +typedef std::vector thread_vector; + +std::atomic_int g_barrier; +int g_breakpoint = 0; +int g_sigusr1_count = 0; +std::atomic_int g_watchme; + +struct action_args { + int delay; +}; + +// Perform any extra actions required by thread 'input' arg +void do_action_args(void *input) { + if (input) { + action_args *args = static_cast(input); + sleep(args->delay); + } +} + +void * +breakpoint_func (void *input) +{ + // Wait until all threads are running + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + // Do something + g_breakpoint++; // Set breakpoint here + return 0; +} + +void * +signal_func (void *input) { + // Wait until all threads are running + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + // Send a user-defined signal to the current process + //kill(getpid(), SIGUSR1); + // Send a user-defined signal to the current thread + pthread_kill(pthread_self(), SIGUSR1); + + return 0; +} + +void * +watchpoint_func (void *input) { + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + g_watchme += 1; // watchpoint triggers here + return 0; +} + +void * +crash_func (void *input) { + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + int *a = 0; + *a = 5; // crash happens here + return 0; +} + +void sigusr1_handler(int sig) { + if (sig == SIGUSR1) + g_sigusr1_count += 1; // Break here in signal handler +} + +/// Register a simple function for to handle signal +void register_signal_handler(int signal, void (*handler)(int)) +{ + sigset_t empty_sigset; + sigemptyset(&empty_sigset); + + struct sigaction action; + action.sa_sigaction = 0; + action.sa_mask = empty_sigset; + action.sa_flags = 0; + action.sa_handler = handler; + sigaction(SIGUSR1, &action, 0); +} + +void start_threads(thread_vector& threads, + action_counts& actions, + void* args = 0) { + action_counts::iterator b = actions.begin(), e = actions.end(); + for(action_counts::iterator i = b; i != e; ++i) { + for(unsigned count = 0; count < i->first; ++count) { + pthread_t t; + pthread_create(&t, 0, i->second, args); + threads.push_back(t); + } + } +} + +int dotest() +{ + g_watchme = 0; + + // Actions are triggered immediately after the thread is spawned + unsigned num_breakpoint_threads = 1; + unsigned num_watchpoint_threads = 0; + unsigned num_signal_threads = 1; + unsigned num_crash_threads = 0; + + // Actions below are triggered after a 1-second delay + unsigned num_delay_breakpoint_threads = 0; + unsigned num_delay_watchpoint_threads = 0; + unsigned num_delay_signal_threads = 0; + unsigned num_delay_crash_threads = 0; + + register_signal_handler(SIGUSR1, sigusr1_handler); // Break here and adjust num_[breakpoint|watchpoint|signal|crash]_threads + + unsigned total_threads = num_breakpoint_threads \ + + num_watchpoint_threads \ + + num_signal_threads \ + + num_crash_threads \ + + num_delay_breakpoint_threads \ + + num_delay_watchpoint_threads \ + + num_delay_signal_threads \ + + num_delay_crash_threads; + + // Don't let either thread do anything until they're both ready. + pseudo_barrier_init(g_barrier, total_threads); + + action_counts actions; + actions.push_back(std::make_pair(num_breakpoint_threads, breakpoint_func)); + actions.push_back(std::make_pair(num_watchpoint_threads, watchpoint_func)); + actions.push_back(std::make_pair(num_signal_threads, signal_func)); + actions.push_back(std::make_pair(num_crash_threads, crash_func)); + + action_counts delay_actions; + delay_actions.push_back(std::make_pair(num_delay_breakpoint_threads, breakpoint_func)); + delay_actions.push_back(std::make_pair(num_delay_watchpoint_threads, watchpoint_func)); + delay_actions.push_back(std::make_pair(num_delay_signal_threads, signal_func)); + delay_actions.push_back(std::make_pair(num_delay_crash_threads, crash_func)); + + // Create threads that handle instant actions + thread_vector threads; + start_threads(threads, actions); + + // Create threads that handle delayed actions + action_args delay_arg; + delay_arg.delay = 1; + start_threads(threads, delay_actions, &delay_arg); + + // Join all threads + typedef std::vector::iterator thread_iterator; + for(thread_iterator t = threads.begin(); t != threads.end(); ++t) + pthread_join(*t, 0); + + return 0; +} + +int main () +{ + dotest(); + return 0; // Break here and verify one thread is active. +} + + diff --git a/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/Makefile new file mode 100644 index 000000000000..26db4816b6ea --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/TestCrashDuringStep.py b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/TestCrashDuringStep.py new file mode 100644 index 000000000000..24b5bf0dad36 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/TestCrashDuringStep.py @@ -0,0 +1,53 @@ +""" +Test that step-inst over a crash behaves correctly. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CreateDuringStepTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + @expectedFailureWindows("llvm.org/pr24778") + @expectedFailureAndroid("llvm.org/pr24497", archs=['arm', 'aarch64']) + @expectedFailureAll(archs=['mips', 'mipsel', 'mips64', 'mips64el']) # IO error due to breakpoint at invalid address + def test_step_inst_with(self): + """Test thread creation during step-inst handling.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target and target.IsValid(), "Target is valid") + + self.bp_num = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # Run the program. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + + # The stop reason should be breakpoint. + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + self.assertEqual(lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint).IsValid(), 1, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread and thread.IsValid(), "Thread is valid") + + # Keep stepping until the inferior crashes + while process.GetState() == lldb.eStateStopped and not lldbutil.is_thread_crashed(self, thread): + thread.StepInstruction(False) + + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + self.assertTrue(lldbutil.is_thread_crashed(self, thread), "Thread has crashed") + process.Kill() diff --git a/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/main.cpp new file mode 100644 index 000000000000..02f3ce338229 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/main.cpp @@ -0,0 +1,16 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +void (*crash)() = nullptr; + +int main() +{ + crash(); // Set breakpoint here + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/Makefile new file mode 100644 index 000000000000..67aa16625bff --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py new file mode 100644 index 000000000000..977254343aa0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py @@ -0,0 +1,122 @@ +""" +Test thread creation after process attach. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CreateAfterAttachTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfFreeBSD # Hangs. May be the same as Linux issue llvm.org/pr16229 but + # not yet investigated. Revisit once required functionality + # is implemented for FreeBSD. + @skipIfWindows # Occasionally hangs on Windows, may be same as other issues. + @skipIfiOSSimulator + def test_create_after_attach_with_popen(self): + """Test thread creation after process attach.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.create_after_attach(use_fork=False) + + @skipIfFreeBSD # Hangs. Revisit once required functionality is implemented + # for FreeBSD. + @skipIfRemote + @skipIfWindows # Windows doesn't have fork. + @expectedFlakeyLinux("llvm.org/pr16229") # 1/100 dosep, build 3546, clang-3.5 x84_64 + @skipIfiOSSimulator + def test_create_after_attach_with_fork(self): + """Test thread creation after process attach.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.create_after_attach(use_fork=True) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.break_1 = line_number('main.cpp', '// Set first breakpoint here') + self.break_2 = line_number('main.cpp', '// Set second breakpoint here') + self.break_3 = line_number('main.cpp', '// Set third breakpoint here') + + def create_after_attach(self, use_fork): + """Test thread creation after process attach.""" + + exe = os.path.join(os.getcwd(), "a.out") + + # Spawn a new process + if use_fork: + pid = self.forkSubprocess(exe) + else: + popen = self.spawnSubprocess(exe) + pid = popen.pid + self.addTearDownHook(self.cleanupSubprocesses) + + # Attach to the spawned process + self.runCmd("process attach -p " + str(pid)) + + target = self.dbg.GetSelectedTarget() + + process = target.GetProcess() + self.assertTrue(process, PROCESS_IS_VALID) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + + # This should create a breakpoint in the second child thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + + # This should create a breakpoint in the first child thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_3, num_expected_locations=1) + + # Note: With std::thread, we cannot rely on particular thread numbers. Using + # std::thread may cause the program to spin up a thread pool (and it does on + # Windows), so the thread numbers are non-deterministic. + + # Run to the first breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #', + 'main', + 'stop reason = breakpoint']) + + # Change a variable to escape the loop + self.runCmd("expression main_thread_continue = 1") + + # Run to the second breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #', + 'thread_2_func', + 'stop reason = breakpoint']) + + # Change a variable to escape the loop + self.runCmd("expression child_thread_continue = 1") + + # Run to the third breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + # Thread 3 may or may not have already exited. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #', + 'thread_1_func', + 'stop reason = breakpoint']) + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/main.cpp new file mode 100644 index 000000000000..8434458f0648 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/main.cpp @@ -0,0 +1,78 @@ +#include +#include +#include + +using std::chrono::microseconds; + +#if defined(__linux__) +#include +#endif + +volatile int g_thread_2_continuing = 0; + +void * +thread_1_func (void *input) +{ + // Waiting to be released by the debugger. + while (!g_thread_2_continuing) // Another thread will change this value + { + std::this_thread::sleep_for(microseconds(1)); + } + + // Return + return NULL; // Set third breakpoint here +} + +void * +thread_2_func (void *input) +{ + // Waiting to be released by the debugger. + int child_thread_continue = 0; + while (!child_thread_continue) // The debugger will change this value + { + std::this_thread::sleep_for(microseconds(1)); // Set second breakpoint here + } + + // Release thread 1 + g_thread_2_continuing = 1; + + // Return + return NULL; +} + +int main(int argc, char const *argv[]) +{ +#if defined(__linux__) + // Immediately enable any ptracer so that we can allow the stub attach + // operation to succeed. Some Linux kernels are locked down so that + // only an ancestor process can be a ptracer of a process. This disables that + // restriction. Without it, attach-related stub tests will fail. +#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY) + int prctl_result; + + // For now we execute on best effort basis. If this fails for + // some reason, so be it. + prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); + (void) prctl_result; +#endif +#endif + + // Create a new thread + std::thread thread_1(thread_1_func, nullptr); + + // Waiting to be attached by the debugger. + int main_thread_continue = 0; + while (!main_thread_continue) // The debugger will change this value + { + std::this_thread::sleep_for(microseconds(1)); // Set first breakpoint here + } + + // Create another new thread + std::thread thread_2(thread_2_func, nullptr); + + // Wait for the threads to finish. + thread_1.join(); + thread_2.join(); + + printf("Exiting now\n"); +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/Makefile new file mode 100644 index 000000000000..67aa16625bff --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py new file mode 100644 index 000000000000..046a86509c0f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py @@ -0,0 +1,127 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CreateDuringStepTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_step_inst(self): + """Test thread creation during step-inst handling.""" + self.build(dictionary=self.getBuildFlags()) + self.create_during_step_base("thread step-inst -m all-threads", 'stop reason = instruction step') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_step_over(self): + """Test thread creation during step-over handling.""" + self.build(dictionary=self.getBuildFlags()) + self.create_during_step_base("thread step-over -m all-threads", 'stop reason = step over') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_step_in(self): + """Test thread creation during step-in handling.""" + self.build(dictionary=self.getBuildFlags()) + self.create_during_step_base("thread step-in -m all-threads", 'stop reason = step in') + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break and continue. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + self.continuepoint = line_number('main.cpp', '// Continue from here') + + def create_during_step_base(self, step_cmd, step_stop_reason): + """Test thread creation while using step-in.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the stepping thread. + self.bp_num = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see only two threads + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + + # Make sure both threads are stopped + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + + # Find the thread that is stopped at the breakpoint + stepping_thread = None + for thread in process: + expected_bp_desc = "breakpoint %s." % self.bp_num + if expected_bp_desc in thread.GetStopDescription(100): + stepping_thread = thread + break + self.assertTrue(stepping_thread != None, "unable to find thread stopped at %s" % expected_bp_desc) + current_line = self.breakpoint + # Keep stepping until we've reached our designated continue point + while current_line != self.continuepoint: + if stepping_thread != process.GetSelectedThread(): + process.SetSelectedThread(stepping_thread) + + self.runCmd(step_cmd) + + frame = stepping_thread.GetFrameAtIndex(0) + current_line = frame.GetLineEntry().GetLine() + + # Make sure we're still where we thought we were + self.assertTrue(current_line >= self.breakpoint, "Stepped to unexpected line, " + str(current_line)) + self.assertTrue(current_line <= self.continuepoint, "Stepped to unexpected line, " + str(current_line)) + + # Update the number of threads + num_threads = process.GetNumThreads() + + # Check to see that we increased the number of threads as expected + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match after thread exit.') + + self.expect("thread list", 'Process state is stopped due to step', + substrs = ['stopped', + step_stop_reason]) + + # Run to completion + self.runCmd("process continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/main.cpp new file mode 100644 index 000000000000..3a00248c022a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/main.cpp @@ -0,0 +1,89 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will be +// created while the debugger is stepping in another thread. + +#include +#include + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier; + +volatile int g_thread_created = 0; +volatile int g_test = 0; + +void * +step_thread_func () +{ + g_test = 0; // Set breakpoint here + + while (!g_thread_created) + g_test++; + + // One more time to provide a continue point + g_test++; // Continue from here + + // Return + return NULL; +} + +void * +create_thread_func (void *input) +{ + std::thread *step_thread = (std::thread*)input; + + // Wait until the main thread knows this thread is started. + pseudo_barrier_wait(g_barrier); + + // Wait until the other thread is done. + step_thread->join(); + + // Return + return NULL; +} + +int main () +{ + // Use a simple count to simulate a barrier. + pseudo_barrier_init(g_barrier, 2); + + // Create a thread to hit the breakpoint. + std::thread thread_1(step_thread_func); + + // Wait until the step thread is stepping + while (g_test < 1) + do_nothing(); + + // Create a thread to exit while we're stepping. + std::thread thread_2(create_thread_func, &thread_1); + + // Wait until that thread is started + pseudo_barrier_wait(g_barrier); + + // Let the stepping thread know the other thread is there + g_thread_created = 1; + + // Wait for the threads to finish. + thread_2.join(); + thread_1.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/Makefile new file mode 100644 index 000000000000..67aa16625bff --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py new file mode 100644 index 000000000000..f999ffe108f3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py @@ -0,0 +1,81 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ExitDuringBreakpointTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test(self): + """Test thread exit during breakpoint handling.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # The exit probably occurred during breakpoint handling, but it isn't + # guaranteed. The main thing we're testing here is that the debugger + # handles this cleanly is some way. + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see at least five threads + self.assertTrue(num_threads >= 5, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + thread4 = process.GetThreadAtIndex(3) + thread5 = process.GetThreadAtIndex(4) + + # Make sure all threads are stopped + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Thread 3 didn't stop during breakpoint") + self.assertTrue(thread4.IsStopped(), "Thread 4 didn't stop during breakpoint") + self.assertTrue(thread5.IsStopped(), "Thread 5 didn't stop during breakpoint") + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/main.cpp new file mode 100644 index 000000000000..3570637207d2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/main.cpp @@ -0,0 +1,130 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while a breakpoint is being handled in another thread. This may not always +// happen because it's possible that the exiting thread will exit before the +// breakpoint is hit. The test case should be flexible enough to treat that +// as success. + +#include +#include +#include + +volatile int g_test = 0; + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize all the threads except the one that will exit. +std::atomic_int g_barrier1; + +// A barrier to synchronize all the threads including the one that will exit. +std::atomic_int g_barrier2; + +// A barrier to keep the first group of threads from exiting until after the +// breakpoint has been passed. +std::atomic_int g_barrier3; + +void * +break_thread_func () +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait for the exiting thread to start + pseudo_barrier_wait(g_barrier2); + + // Do something + g_test++; // Set breakpoint here + + // Synchronize after the breakpoint + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +void * +wait_thread_func () +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait for the exiting thread to start + pseudo_barrier_wait(g_barrier2); + + // Wait until the breakpoint has been passed + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +void * +exit_thread_func () +{ + // Sync up with the rest of the threads. + pseudo_barrier_wait(g_barrier2); + + // Try to make sure this thread doesn't exit until the breakpoint is hit. + std::this_thread::sleep_for(std::chrono::microseconds(1)); + + // Return + return NULL; +} + +int main () +{ + + // The first barrier waits for the non-exiting threads to start. + // This thread will also participate in that barrier. + // The idea here is to guarantee that the exiting thread will be + // last in the internal list maintained by the debugger. + pseudo_barrier_init(g_barrier1, 5); + + // The second break synchronyizes thread exection with the breakpoint. + pseudo_barrier_init(g_barrier2, 5); + + // The third barrier keeps the waiting threads around until the breakpoint + // has been passed. + pseudo_barrier_init(g_barrier3, 4); + + // Create a thread to hit the breakpoint + std::thread thread_1(break_thread_func); + + // Create more threads to slow the debugger down during processing. + std::thread thread_2(wait_thread_func); + std::thread thread_3(wait_thread_func); + std::thread thread_4(wait_thread_func); + + // Wait for all these threads to get started. + pseudo_barrier_wait(g_barrier1); + + // Create a thread to exit during the breakpoint + std::thread thread_5(exit_thread_func); + + // Wait for the threads to finish + thread_5.join(); + thread_4.join(); + thread_3.join(); + thread_2.join(); + thread_1.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/Makefile new file mode 100644 index 000000000000..d06a7d4685f3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/TestExitDuringStep.py b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/TestExitDuringStep.py new file mode 100644 index 000000000000..67d1c96fd342 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/TestExitDuringStep.py @@ -0,0 +1,144 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ExitDuringStepTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24681") + def test_thread_state_is_stopped(self): + """Test thread exit during step handling.""" + self.build(dictionary=self.getBuildFlags()) + self.exit_during_step_base("thread step-in -m all-threads", 'stop reason = step in', True) + + @skipIfFreeBSD # llvm.org/pr21411: test is hanging + @expectedFailureWindows("llvm.org/pr24681") + def test(self): + """Test thread exit during step handling.""" + self.build(dictionary=self.getBuildFlags()) + self.exit_during_step_base("thread step-inst -m all-threads", 'stop reason = instruction step', False) + + @skipIfFreeBSD # llvm.org/pr21411: test is hanging + @expectedFailureWindows("llvm.org/pr24681") + def test_step_over(self): + """Test thread exit during step-over handling.""" + self.build(dictionary=self.getBuildFlags()) + self.exit_during_step_base("thread step-over -m all-threads", 'stop reason = step over', False) + + @skipIfFreeBSD # llvm.org/pr21411: test is hanging + @expectedFailureWindows("llvm.org/pr24681") + def test_step_in(self): + """Test thread exit during step-in handling.""" + self.build(dictionary=self.getBuildFlags()) + self.exit_during_step_base("thread step-in -m all-threads", 'stop reason = step in', False) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break and continue. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + self.continuepoint = line_number('main.cpp', '// Continue from here') + + def exit_during_step_base(self, step_cmd, step_stop_reason, test_thread_state): + """Test thread exit during step handling.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + self.bp_num = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see all three threads + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + + # Make sure all threads are stopped + if test_thread_state: + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Thread 3 didn't stop during breakpoint") + return + + # Find the thread that is stopped at the breakpoint + stepping_thread = None + for thread in process: + expected_bp_desc = "breakpoint %s." % self.bp_num + stop_desc = thread.GetStopDescription(100) + if stop_desc and (expected_bp_desc in stop_desc): + stepping_thread = thread + break + self.assertTrue(stepping_thread != None, "unable to find thread stopped at %s" % expected_bp_desc) + + current_line = self.breakpoint + stepping_frame = stepping_thread.GetFrameAtIndex(0) + self.assertTrue(current_line == stepping_frame.GetLineEntry().GetLine(), "Starting line for stepping doesn't match breakpoint line.") + + # Keep stepping until we've reached our designated continue point + while current_line != self.continuepoint: + # Since we're using the command interpreter to issue the thread command + # (on the selected thread) we need to ensure the selected thread is the + # stepping thread. + if stepping_thread != process.GetSelectedThread(): + process.SetSelectedThread(stepping_thread) + + self.runCmd(step_cmd) + + frame = stepping_thread.GetFrameAtIndex(0) + + current_line = frame.GetLineEntry().GetLine() + + self.assertTrue(current_line >= self.breakpoint, "Stepped to unexpected line, " + str(current_line)) + self.assertTrue(current_line <= self.continuepoint, "Stepped to unexpected line, " + str(current_line)) + + self.runCmd("thread list") + + # Update the number of threads + num_threads = process.GetNumThreads() + + # Check to see that we reduced the number of threads as expected + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match after thread exit.') + + self.expect("thread list", 'Process state is stopped due to step', + substrs = ['stopped', + step_stop_reason]) + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/main.cpp new file mode 100644 index 000000000000..d1b364b8baa2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/main.cpp @@ -0,0 +1,87 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while the debugger is stepping in another thread. + +#include + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize thread start. +volatile int g_barrier; + +volatile int g_thread_exited = 0; + +volatile int g_test = 0; + +void * +step_thread_func () +{ + // Wait until both threads are started. + pseudo_barrier_wait(g_barrier); + + g_test = 0; // Set breakpoint here + + while (!g_thread_exited) + g_test++; + + // One more time to provide a continue point + g_test++; // Continue from here + + // Return + return NULL; +} + +void * +exit_thread_func () +{ + // Wait until both threads are started. + pseudo_barrier_wait(g_barrier); + + // Wait until the other thread is stepping. + while (g_test == 0) + do_nothing(); + + // Return + return NULL; +} + +int main () +{ + // Synchronize thread start so that doesn't happen during stepping. + pseudo_barrier_init(g_barrier, 2); + + // Create a thread to hit the breakpoint. + std::thread thread_1(step_thread_func); + + // Create a thread to exit while we're stepping. + std::thread thread_2(exit_thread_func); + + // Wait for the exit thread to finish. + thread_2.join(); + + // Let the stepping thread know the other thread is gone. + g_thread_exited = 1; + + // Wait for the stepping thread to finish. + thread_1.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/jump/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/jump/Makefile new file mode 100644 index 000000000000..b726fc3695fd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/jump/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp other.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/jump/TestThreadJump.py b/packages/Python/lldbsuite/test/functionalities/thread/jump/TestThreadJump.py new file mode 100644 index 000000000000..be49a210f0f0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/jump/TestThreadJump.py @@ -0,0 +1,60 @@ +""" +Test jumping to different places. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadJumpTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test(self): + """Test thread jump handling.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Find the line numbers for our breakpoints. + self.mark1 = line_number('main.cpp', '// 1st marker') + self.mark2 = line_number('main.cpp', '// 2nd marker') + self.mark3 = line_number('main.cpp', '// 3rd marker') + self.mark4 = line_number('main.cpp', '// 4th marker') + self.mark5 = line_number('other.cpp', '// other marker') + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.mark3, num_expected_locations=1) + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 1", + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint 1']) + + self.do_min_test(self.mark3, self.mark1, "i", "4"); # Try the int path, force it to return 'a' + self.do_min_test(self.mark3, self.mark2, "i", "5"); # Try the int path, force it to return 'b' + self.do_min_test(self.mark4, self.mark1, "j", "7"); # Try the double path, force it to return 'a' + self.do_min_test(self.mark4, self.mark2, "j", "8"); # Try the double path, force it to return 'b' + + # Try jumping to another function in a different file. + self.runCmd("thread jump --file other.cpp --line %i --force" % self.mark5) + self.expect("process status", + substrs = ["at other.cpp:%i" % self.mark5]) + + # Try jumping to another function (without forcing) + self.expect("j main.cpp:%i" % self.mark1, COMMAND_FAILED_AS_EXPECTED, error = True, + substrs = ["error"]) + + def do_min_test(self, start, jump, var, value): + self.runCmd("j %i" % start) # jump to the start marker + self.runCmd("thread step-in") # step into the min fn + self.runCmd("j %i" % jump) # jump to the branch we're interested in + self.runCmd("thread step-out") # return out + self.runCmd("thread step-over") # assign to the global + self.expect("expr %s" % var, substrs = [value]) # check it diff --git a/packages/Python/lldbsuite/test/functionalities/thread/jump/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/jump/main.cpp new file mode 100644 index 000000000000..3497155a98f2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/jump/main.cpp @@ -0,0 +1,35 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test verifies the correct handling of program counter jumps. + +int otherfn(); + +template +T min(T a, T b) +{ + if (a < b) + { + return a; // 1st marker + } else { + return b; // 2nd marker + } +} + +int main () +{ + int i; + double j; + int min_i_a = 4, min_i_b = 5; + double min_j_a = 7.0, min_j_b = 8.0; + i = min(min_i_a, min_i_b); // 3rd marker + j = min(min_j_a, min_j_b); // 4th marker + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/jump/other.cpp b/packages/Python/lldbsuite/test/functionalities/thread/jump/other.cpp new file mode 100644 index 000000000000..8108a52c163d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/jump/other.cpp @@ -0,0 +1,13 @@ +//===-- other.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int otherfn() +{ + return 4; // other marker +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/main.cpp new file mode 100644 index 000000000000..6a0ea4e0d119 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/main.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +std::mutex mutex; +std::condition_variable cond; + +void * +thread3(void *input) +{ + std::unique_lock lock(mutex); + cond.notify_all(); // Set break point at this line. + return NULL; +} + +void * +thread2(void *input) +{ + std::unique_lock lock(mutex); + cond.notify_all(); + cond.wait(lock); + return NULL; +} + +void * +thread1(void *input) +{ + std::thread thread_2(thread2, nullptr); + thread_2.join(); + + return NULL; +} + +int main() +{ + std::unique_lock lock(mutex); + + std::thread thread_1(thread1, nullptr); + cond.wait(lock); + + std::thread thread_3(thread3, nullptr); + cond.wait(lock); + + lock.unlock(); + + thread_1.join(); + thread_3.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/multi_break/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/Makefile new file mode 100644 index 000000000000..67aa16625bff --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py new file mode 100644 index 000000000000..9dd212412216 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py @@ -0,0 +1,77 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class MultipleBreakpointTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test(self): + """Test simultaneous breakpoints in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + # The breakpoint may be hit in either thread 2 or thread 3. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see all three threads + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + + # Make sure both threads are stopped + self.assertTrue(thread1.IsStopped(), "Primary thread didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Secondary thread didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Tertiary thread didn't stop during breakpoint") + + # Delete the first breakpoint then continue + self.runCmd("breakpoint delete 1") + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/multi_break/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/main.cpp new file mode 100644 index 000000000000..01f4b8f98ea7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/main.cpp @@ -0,0 +1,61 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which a breakpoint will be +// hit in two threads at nearly the same moment. The expected result is that +// the breakpoint in the second thread will be hit while the breakpoint handler +// in the first thread is trying to stop all threads. + +#include +#include + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier; + +volatile int g_test = 0; + +void * +thread_func () +{ + // Wait until both threads are running + pseudo_barrier_wait(g_barrier); + + // Do something + g_test++; // Set breakpoint here + + // Return + return NULL; +} + +int main () +{ + // Don't let either thread do anything until they're both ready. + pseudo_barrier_init(g_barrier, 2); + + // Create two threads + std::thread thread_1(thread_func); + std::thread thread_2(thread_func); + + // Wait for the threads to finish + thread_1.join(); + thread_2.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/state/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/state/Makefile new file mode 100644 index 000000000000..26db4816b6ea --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/state/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/state/TestThreadStates.py b/packages/Python/lldbsuite/test/functionalities/thread/state/TestThreadStates.py new file mode 100644 index 000000000000..623c659ffa5c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/state/TestThreadStates.py @@ -0,0 +1,333 @@ +""" +Test thread states. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadStateTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureDarwin("rdar://15367566") + @expectedFailureFreeBSD('llvm.org/pr15824') + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_state_after_breakpoint(self): + """Test thread state after breakpoint.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.thread_state_after_breakpoint_test() + + @skipIfDarwin # 'llvm.org/pr23669', cause Python crash randomly + @expectedFailureDarwin('llvm.org/pr23669') + @expectedFailureFreeBSD('llvm.org/pr15824') + @expectedFailureWindows("llvm.org/pr24660") + def test_state_after_continue(self): + """Test thread state after continue.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.thread_state_after_continue_test() + + @skipIfDarwin # 'llvm.org/pr23669', cause Python crash randomly + @expectedFailureDarwin('llvm.org/pr23669') + @expectedFailureWindows("llvm.org/pr24660") + @unittest2.expectedFailure("llvm.org/pr16712") # thread states not properly maintained + def test_state_after_expression(self): + """Test thread state after expression.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.thread_state_after_expression_test() + + @unittest2.expectedFailure("llvm.org/pr16712") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_process_interrupt(self): + """Test process interrupt.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.process_interrupt_test() + + @unittest2.expectedFailure("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_process_state(self): + """Test thread states (comprehensive).""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.thread_states_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.break_1 = line_number('main.cpp', '// Set first breakpoint here') + self.break_2 = line_number('main.cpp', '// Set second breakpoint here') + + def thread_state_after_breakpoint_test(self): + """Test thread state after breakpoint.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Get the thread object + thread = process.GetThreadAtIndex(0) + + # Make sure the thread is in the stopped state. + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' during breakpoint 1.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' during breakpoint 1.") + + # Kill the process + self.runCmd("process kill") + + def wait_for_running_event(self): + listener = self.dbg.GetListener() + if lldb.remote_platform: + lldbutil.expect_state_changes(self, listener, [lldb.eStateConnected]) + lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning]) + + def thread_state_after_continue_test(self): + """Test thread state after continue.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Get the thread object + thread = process.GetThreadAtIndex(0) + + # Continue, the inferior will go into an infinite loop waiting for 'g_test' to change. + self.dbg.SetAsync(True) + self.runCmd("continue") + self.wait_for_running_event() + + # Check the thread state. It should be running. + self.assertFalse(thread.IsStopped(), "Thread state is \'stopped\' when it should be running.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' when it should be running.") + + # Go back to synchronous interactions + self.dbg.SetAsync(False) + + # Kill the process + self.runCmd("process kill") + + def thread_state_after_expression_test(self): + """Test thread state after expression.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Get the thread object + thread = process.GetThreadAtIndex(0) + + # Get the inferior out of its loop + self.runCmd("expression g_test = 1") + + # Check the thread state + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' after expression evaluation.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' after expression evaluation.") + + # Let the process run to completion + self.runCmd("process continue") + + + def process_interrupt_test(self): + """Test process interrupt and continue.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Continue, the inferior will go into an infinite loop waiting for 'g_test' to change. + self.dbg.SetAsync(True) + self.runCmd("continue") + self.wait_for_running_event() + + # Go back to synchronous interactions + self.dbg.SetAsync(False) + + # Stop the process + self.runCmd("process interrupt") + + # The stop reason of the thread should be signal. + self.expect("process status", STOPPED_DUE_TO_SIGNAL, + substrs = ['stopped', + '* thread #1', + 'stop reason = signal']) + + # Get the inferior out of its loop + self.runCmd("expression g_test = 1") + + # Run to completion + self.runCmd("continue") + + def thread_states_test(self): + """Test thread states (comprehensive).""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Get the thread object + thread = process.GetThreadAtIndex(0) + + # Make sure the thread is in the stopped state. + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' during breakpoint 1.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' during breakpoint 1.") + + # Continue, the inferior will go into an infinite loop waiting for 'g_test' to change. + self.dbg.SetAsync(True) + self.runCmd("continue") + self.wait_for_running_event() + + # Check the thread state. It should be running. + self.assertFalse(thread.IsStopped(), "Thread state is \'stopped\' when it should be running.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' when it should be running.") + + # Go back to synchronous interactions + self.dbg.SetAsync(False) + + # Stop the process + self.runCmd("process interrupt") + + # The stop reason of the thread should be signal. + self.expect("process status", STOPPED_DUE_TO_SIGNAL, + substrs = ['stopped', + '* thread #1', + 'stop reason = signal']) + + # Check the thread state + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' after process stop.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' after process stop.") + + # Get the inferior out of its loop + self.runCmd("expression g_test = 1") + + # Check the thread state + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' after expression evaluation.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' after expression evaluation.") + + # The stop reason of the thread should be signal. + self.expect("process status", STOPPED_DUE_TO_SIGNAL, + substrs = ['stopped', + '* thread #1', + 'stop reason = signal']) + + # Run to breakpoint 2 + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Make sure both threads are stopped + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' during breakpoint 2.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' during breakpoint 2.") + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/state/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/state/main.cpp new file mode 100644 index 000000000000..081203da8da9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/state/main.cpp @@ -0,0 +1,45 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to verify that thread states are properly maintained +// when transitional actions are performed in the debugger. Most of the logic +// is in the test script. This program merely provides places where the test +// can create the intended states. + +#include +#include + +volatile int g_test = 0; + +int addSomething(int a) +{ + return a + g_test; +} + +int doNothing() +{ + int temp = 0; // Set first breakpoint here + + while (!g_test && temp < 5) + { + ++temp; + std::this_thread::sleep_for(std::chrono::seconds(2)); + } + + return temp; // Set second breakpoint here +} + +int main () +{ + int result = doNothing(); + + int i = addSomething(result); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/step_out/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/step_out/Makefile new file mode 100644 index 000000000000..035413ff763d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/step_out/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/step_out/TestThreadStepOut.py b/packages/Python/lldbsuite/test/functionalities/thread/step_out/TestThreadStepOut.py new file mode 100644 index 000000000000..b2d966c4c0f3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/step_out/TestThreadStepOut.py @@ -0,0 +1,131 @@ +""" +Test stepping out from a function in a multi-threaded program. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadStepOutTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfLinux # Test occasionally times out on the Linux build bot + @expectedFailureLinux("llvm.org/pr23477") # Test occasionally times out on the Linux build bot + @expectedFailureFreeBSD("llvm.org/pr18066") # inferior does not exit + @expectedFailureWindows # Test crashes + def test_step_single_thread(self): + """Test thread step out on one thread via command interpreter. """ + self.build(dictionary=self.getBuildFlags()) + self.step_out_test(self.step_out_single_thread_with_cmd) + + @skipIfLinux # Test occasionally times out on the Linux build bot + @expectedFailureLinux("llvm.org/pr23477") # Test occasionally times out on the Linux build bot + @expectedFailureFreeBSD("llvm.org/pr19347") # 2nd thread stops at breakpoint + @expectedFailureWindows # Test crashes + def test_step_all_threads(self): + """Test thread step out on all threads via command interpreter. """ + self.build(dictionary=self.getBuildFlags()) + self.step_out_test(self.step_out_all_threads_with_cmd) + + @skipIfLinux # Test occasionally times out on the Linux build bot + @expectedFailureLinux("llvm.org/pr23477") # Test occasionally times out on the Linux build bot + @expectedFailureFreeBSD("llvm.org/pr19347") + @expectedFailureWindows("llvm.org/pr24681") + def test_python(self): + """Test thread step out on one thread via Python API (dwarf).""" + self.build(dictionary=self.getBuildFlags()) + self.step_out_test(self.step_out_with_python) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + if "gcc" in self.getCompiler() or self.isIntelCompiler(): + self.step_out_destination = line_number('main.cpp', '// Expect to stop here after step-out (icc and gcc)') + else: + self.step_out_destination = line_number('main.cpp', '// Expect to stop here after step-out (clang)') + + def step_out_single_thread_with_cmd(self): + self.step_out_with_cmd("this-thread") + self.expect("thread backtrace all", "Thread location after step out is correct", + substrs = ["main.cpp:%d" % self.step_out_destination, + "main.cpp:%d" % self.breakpoint]) + + def step_out_all_threads_with_cmd(self): + self.step_out_with_cmd("all-threads") + self.expect("thread backtrace all", "Thread location after step out is correct", + substrs = ["main.cpp:%d" % self.step_out_destination]) + + def step_out_with_cmd(self, run_mode): + self.runCmd("thread select %d" % self.step_out_thread.GetIndexID()) + self.runCmd("thread step-out -m %s" % run_mode) + self.expect("process status", "Expected stop reason to be step-out", + substrs = ["stop reason = step out"]) + + self.expect("thread list", "Selected thread did not change during step-out", + substrs = ["* thread #%d" % self.step_out_thread.GetIndexID()]) + + def step_out_with_python(self): + self.step_out_thread.StepOut() + + reason = self.step_out_thread.GetStopReason() + self.assertEqual(lldb.eStopReasonPlanComplete, reason, + "Expected thread stop reason 'plancomplete', but got '%s'" % lldbutil.stop_reason_to_str(reason)) + + # Verify location after stepping out + frame = self.step_out_thread.GetFrameAtIndex(0) + desc = lldbutil.get_description(frame.GetLineEntry()) + expect = "main.cpp:%d" % self.step_out_destination + self.assertTrue(expect in desc, "Expected %s but thread stopped at %s" % (expect, desc)) + + def step_out_test(self, step_out_func): + """Test single thread step out of a function.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Get the target process + self.inferior_target = self.dbg.GetSelectedTarget() + self.inferior_process = self.inferior_target.GetProcess() + + # Get the number of threads, ensure we see all three. + num_threads = self.inferior_process.GetNumThreads() + self.assertEqual(num_threads, 3, 'Number of expected threads and actual threads do not match.') + + (breakpoint_threads, other_threads) = ([], []) + lldbutil.sort_stopped_threads(self.inferior_process, + breakpoint_threads=breakpoint_threads, + other_threads=other_threads) + + while len(breakpoint_threads) < 2: + self.runCmd("thread continue %s" % " ".join([str(x.GetIndexID()) for x in other_threads])) + lldbutil.sort_stopped_threads(self.inferior_process, + breakpoint_threads=breakpoint_threads, + other_threads=other_threads) + + self.step_out_thread = breakpoint_threads[0] + + # Step out of thread stopped at breakpoint + step_out_func() + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(self.inferior_process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/step_out/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/step_out/main.cpp new file mode 100644 index 000000000000..b4c6216d6bfe --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/step_out/main.cpp @@ -0,0 +1,63 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which two threads are stopped +// at a breakpoint and the debugger issues a step-out command. + +#include +#include + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier; + +volatile int g_test = 0; + +void step_out_of_here() { + g_test += 5; // Set breakpoint here +} + +void * +thread_func () +{ + // Wait until both threads are running + pseudo_barrier_wait(g_barrier); + + // Do something + step_out_of_here(); // Expect to stop here after step-out (clang) + + // Return + return NULL; // Expect to stop here after step-out (icc and gcc) +} + +int main () +{ + // Don't let either thread do anything until they're both ready. + pseudo_barrier_init(g_barrier, 2); + + // Create two threads + std::thread thread_1(thread_func); + std::thread thread_2(thread_func); + + // Wait for the threads to finish + thread_1.join(); + thread_2.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/Makefile new file mode 100644 index 000000000000..d06a7d4685f3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/TestThreadExit.py b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/TestThreadExit.py new file mode 100644 index 000000000000..f785401277a4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/TestThreadExit.py @@ -0,0 +1,116 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadExitTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.break_1 = line_number('main.cpp', '// Set first breakpoint here') + self.break_2 = line_number('main.cpp', '// Set second breakpoint here') + self.break_3 = line_number('main.cpp', '// Set third breakpoint here') + self.break_4 = line_number('main.cpp', '// Set fourth breakpoint here') + + @expectedFailureWindows("llvm.org/pr24681") + def test(self): + """Test thread exit handling.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 1 location. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_3, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_4, num_expected_locations=1) + + # The breakpoint list should show 1 locations. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.break_1, + "2: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.break_2, + "3: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.break_3, + "4: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.break_4]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 1", + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint 1', + 'thread #2']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match at breakpoint 1.') + + # Run to the second breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 2", + substrs = ['stopped', + 'thread #1', + 'thread #2', + 'stop reason = breakpoint 2', + 'thread #3']) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match at breakpoint 2.') + + # Run to the third breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 3. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 3", + substrs = ['stopped', + 'thread #1', + 'stop reason = breakpoint 3', + 'thread #3', + ]) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match at breakpoint 3.') + + # Run to the fourth breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 4. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 4", + substrs = ['stopped', + 'thread #1', + 'stop reason = breakpoint 4']) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match at breakpoint 4.') + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/main.cpp new file mode 100644 index 000000000000..e498db7895d2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/main.cpp @@ -0,0 +1,85 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test verifies the correct handling of child thread exits. + +#include +#include + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier1; +std::atomic_int g_barrier2; +std::atomic_int g_barrier3; + +void * +thread1 () +{ + // Synchronize with the main thread. + pseudo_barrier_wait(g_barrier1); + + // Synchronize with the main thread and thread2. + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; // Set second breakpoint here +} + +void * +thread2 () +{ + // Synchronize with thread1 and the main thread. + pseudo_barrier_wait(g_barrier2); + + // Synchronize with the main thread. + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +int main () +{ + pseudo_barrier_init(g_barrier1, 2); + pseudo_barrier_init(g_barrier2, 3); + pseudo_barrier_init(g_barrier3, 2); + + // Create a thread. + std::thread thread_1(thread1); + + // Wait for thread1 to start. + pseudo_barrier_wait(g_barrier1); + + // Create another thread. + std::thread thread_2(thread2); // Set first breakpoint here + + // Wait for thread2 to start. + pseudo_barrier_wait(g_barrier2); + + // Wait for the first thread to finish + thread_1.join(); + + // Synchronize with the remaining thread + pseudo_barrier_wait(g_barrier3); // Set third breakpoint here + + // Wait for the second thread to finish + thread_2.join(); + + return 0; // Set fourth breakpoint here +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/Makefile new file mode 100644 index 000000000000..035413ff763d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py new file mode 100644 index 000000000000..3c69b6667f7d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py @@ -0,0 +1,63 @@ +""" +Test that we obey thread conditioned breakpoints. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ThreadSpecificBreakTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @expectedFailureWindows # Thread specific breakpoints cause the inferior to crash + def test_python(self): + """Test that we obey thread conditioned breakpoints.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # This test works by setting a breakpoint in a function conditioned to stop only on + # the main thread, and then calling this function on a secondary thread, joining, + # and then calling again on the main thread. If the thread specific breakpoint works + # then it should not be hit on the secondary thread, only on the main thread. + + main_source_spec = lldb.SBFileSpec ("main.cpp") + + main_breakpoint = target.BreakpointCreateBySourceRegex("Set main breakpoint here", main_source_spec); + thread_breakpoint = target.BreakpointCreateBySourceRegex("Set thread-specific breakpoint here", main_source_spec) + + self.assertTrue(main_breakpoint.IsValid(), "Failed to set main breakpoint.") + self.assertGreater(main_breakpoint.GetNumLocations(), 0, "main breakpoint has no locations associated with it.") + self.assertTrue(thread_breakpoint.IsValid(), "Failed to set thread breakpoint.") + self.assertGreater(thread_breakpoint.GetNumLocations(), 0, "thread breakpoint has no locations associated with it.") + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + stopped_threads = lldbutil.get_threads_stopped_at_breakpoint(process, main_breakpoint) + self.assertEqual(len(stopped_threads), 1, "main breakpoint stopped at unexpected number of threads") + main_thread = stopped_threads[0] + main_thread_id = main_thread.GetThreadID() + + # Set the thread-specific breakpoint to only stop on the main thread. The run the function + # on another thread and join on it. If the thread-specific breakpoint works, the next + # stop should be on the main thread. + thread_breakpoint.SetThreadID(main_thread_id) + + process.Continue() + next_stop_state = process.GetState() + self.assertEqual(next_stop_state, lldb.eStateStopped, "We should have stopped at the thread breakpoint.") + stopped_threads = lldbutil.get_threads_stopped_at_breakpoint(process, thread_breakpoint) + self.assertEqual(len(stopped_threads), 1, "thread breakpoint stopped at unexpected number of threads") + self.assertEqual(stopped_threads[0].GetThreadID(), main_thread_id, "thread breakpoint stopped at the wrong thread") diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/main.cpp new file mode 100644 index 000000000000..7721b5d84322 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/main.cpp @@ -0,0 +1,20 @@ +#include +#include + +void +thread_function () +{ + // Set thread-specific breakpoint here. + std::this_thread::sleep_for(std::chrono::microseconds(100)); +} + +int +main () +{ + // Set main breakpoint here. + std::thread t(thread_function); + t.join(); + + thread_function(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/Makefile new file mode 100644 index 000000000000..035413ff763d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/TestThreadSpecificBpPlusCondition.py b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/TestThreadSpecificBpPlusCondition.py new file mode 100644 index 000000000000..b4a0b7a5dd4e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/TestThreadSpecificBpPlusCondition.py @@ -0,0 +1,65 @@ +""" +Test that we obey thread conditioned breakpoints and expression +conditioned breakpoints simultaneously +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ThreadSpecificBreakPlusConditionTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfFreeBSD # test frequently times out or hangs + @expectedFailureFreeBSD('llvm.org/pr18522') # hits break in another thread in testrun + @add_test_categories(['pyapi']) + @expectedFailureWindows # Thread specific breakpoints cause the inferior to crash. + @expectedFlakeyLinux # this test fails 6/100 dosep runs + def test_python(self): + """Test that we obey thread conditioned breakpoints.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + main_source_spec = lldb.SBFileSpec ("main.cpp") + + # Set a breakpoint in the thread body, and make it active for only the first thread. + break_thread_body = target.BreakpointCreateBySourceRegex ("Break here in thread body.", main_source_spec) + self.assertTrue (break_thread_body.IsValid() and break_thread_body.GetNumLocations() > 0, "Failed to set thread body breakpoint.") + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_thread_body) + + victim_thread = threads[0] + + # Pick one of the threads, and change the breakpoint so it ONLY stops for this thread, + # but add a condition that it won't stop for this thread's my_value. The other threads + # pass the condition, so they should stop, but if the thread-specification is working + # they should not stop. So nobody should hit the breakpoint anymore, and we should + # just exit cleanly. + + frame = victim_thread.GetFrameAtIndex(0) + value = frame.FindVariable("my_value").GetValueAsSigned(0) + self.assertTrue (value > 0 and value < 11, "Got a reasonable value for my_value.") + + cond_string = "my_value != %d"%(value) + + break_thread_body.SetThreadID(victim_thread.GetThreadID()) + break_thread_body.SetCondition (cond_string) + + process.Continue() + + next_stop_state = process.GetState() + self.assertTrue (next_stop_state == lldb.eStateExited, "We should have not hit the breakpoint again.") diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/main.cpp new file mode 100644 index 000000000000..af8ab84157fd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/main.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +void * +thread_function (void *thread_marker) +{ + int keep_going = 1; + int my_value = *((int *)thread_marker); + int counter = 0; + + while (counter < 20) + { + counter++; // Break here in thread body. + std::this_thread::sleep_for(std::chrono::microseconds(10)); + } + return NULL; +} + + +int +main () +{ + std::vector threads; + + int thread_value = 0; + int i; + + for (i = 0; i < 10; i++) + { + thread_value += 1; + threads.push_back(std::thread(thread_function, &thread_value)); + } + + for (i = 0; i < 10; i++) + threads[i].join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/tty/TestTerminal.py b/packages/Python/lldbsuite/test/functionalities/tty/TestTerminal.py new file mode 100644 index 000000000000..5697c2c37283 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/tty/TestTerminal.py @@ -0,0 +1,40 @@ +""" +Test lldb command aliases. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class LaunchInTerminalTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # Darwin is the only platform that I know of that supports optionally launching + # a program in a separate terminal window. It would be great if other platforms + # added support for this. + @skipUnlessDarwin + @expectedFailureDarwin("llvm.org/pr25484") + # If the test is being run under sudo, the spawned terminal won't retain that elevated + # privilege so it can't open the socket to talk back to the test case + @unittest2.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, "test cannot be run as root") + # Do we need to disable this test if the testsuite is being run on a remote system? + # This env var is only defined when the shell is running in a local mac terminal window + @unittest2.skipUnless('TERM_PROGRAM' in os.environ, "test must be run on local system") + @no_debug_info_test + def test_launch_in_terminal (self): + exe = "/bin/ls" + target = self.dbg.CreateTarget(exe) + launch_info = lldb.SBLaunchInfo(["-lAF", "/tmp/"]) + launch_info.SetLaunchFlags(lldb.eLaunchFlagLaunchInTTY | lldb.eLaunchFlagCloseTTYOnExit) + error = lldb.SBError() + process = target.Launch (launch_info, error) + self.assertTrue(error.Success(), "Make sure launch happened successfully in a terminal window") + # Running in synchronous mode our process should have run and already exited by the time target.Launch() returns + self.assertTrue(process.GetState() == lldb.eStateExited) diff --git a/packages/Python/lldbsuite/test/functionalities/type_completion/Makefile b/packages/Python/lldbsuite/test/functionalities/type_completion/Makefile new file mode 100644 index 000000000000..b69775dc199a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/type_completion/Makefile @@ -0,0 +1,12 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/type_completion/TestTypeCompletion.py b/packages/Python/lldbsuite/test/functionalities/type_completion/TestTypeCompletion.py new file mode 100644 index 000000000000..5a238b7d3542 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/type_completion/TestTypeCompletion.py @@ -0,0 +1,108 @@ +""" +Check that types only get completed when necessary. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TypeCompletionTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureIcc # often fails with 'NameAndAddress should be valid' + # Fails with gcc 4.8.1 with llvm.org/pr15301 LLDB prints incorrect sizes of STL containers + def test_with_run_command(self): + """Check that types only get completed when necessary.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, "// Set break point at this line.") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type category enable -l c++', check=False) + + self.runCmd('type category disable -l c++', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + p_vector = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('p') + p_type = p_vector.GetType() + self.assertFalse(p_type.IsTypeComplete(), 'vector complete but it should not be') + + self.runCmd("continue") + + p_vector = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('p') + p_type = p_vector.GetType() + self.assertFalse(p_type.IsTypeComplete(), 'vector complete but it should not be') + + self.runCmd("continue") + + self.runCmd("frame variable p --show-types") + + p_vector = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('p') + p_type = p_vector.GetType() + self.assertTrue(p_type.IsTypeComplete(), 'vector should now be complete') + name_address_type = p_type.GetTemplateArgumentType(0) + self.assertTrue(name_address_type.IsValid(), 'NameAndAddress should be valid') + self.assertFalse(name_address_type.IsTypeComplete(), 'NameAndAddress complete but it should not be') + + self.runCmd("continue") + + self.runCmd("frame variable guy --show-types") + + p_vector = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('p') + p_type = p_vector.GetType() + self.assertTrue(p_type.IsTypeComplete(), 'vector should now be complete') + name_address_type = p_type.GetTemplateArgumentType(0) + self.assertTrue(name_address_type.IsValid(), 'NameAndAddress should be valid') + self.assertTrue(name_address_type.IsTypeComplete(), 'NameAndAddress should now be complete') + field0 = name_address_type.GetFieldAtIndex(0) + self.assertTrue(field0.IsValid(), 'NameAndAddress::m_name should be valid') + string = field0.GetType().GetPointeeType() + self.assertTrue(string.IsValid(), 'CustomString should be valid') + self.assertFalse(string.IsTypeComplete(), 'CustomString complete but it should not be') + + self.runCmd("continue") + + p_vector = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('p') + p_type = p_vector.GetType() + self.assertTrue(p_type.IsTypeComplete(), 'vector should now be complete') + name_address_type = p_type.GetTemplateArgumentType(0) + self.assertTrue(name_address_type.IsValid(), 'NameAndAddress should be valid') + self.assertTrue(name_address_type.IsTypeComplete(), 'NameAndAddress should now be complete') + field0 = name_address_type.GetFieldAtIndex(0) + self.assertTrue(field0.IsValid(), 'NameAndAddress::m_name should be valid') + string = field0.GetType().GetPointeeType() + self.assertTrue(string.IsValid(), 'CustomString should be valid') + self.assertFalse(string.IsTypeComplete(), 'CustomString complete but it should not be') + + self.runCmd('type category enable -l c++', check=False) + self.runCmd('frame variable guy --show-types --ptr-depth=1') + + p_vector = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('p') + p_type = p_vector.GetType() + self.assertTrue(p_type.IsTypeComplete(), 'vector should now be complete') + name_address_type = p_type.GetTemplateArgumentType(0) + self.assertTrue(name_address_type.IsValid(), 'NameAndAddress should be valid') + self.assertTrue(name_address_type.IsTypeComplete(), 'NameAndAddress should now be complete') + field0 = name_address_type.GetFieldAtIndex(0) + self.assertTrue(field0.IsValid(), 'NameAndAddress::m_name should be valid') + string = field0.GetType().GetPointeeType() + self.assertTrue(string.IsValid(), 'CustomString should be valid') + self.assertTrue(string.IsTypeComplete(), 'CustomString should now be complete') diff --git a/packages/Python/lldbsuite/test/functionalities/type_completion/main.cpp b/packages/Python/lldbsuite/test/functionalities/type_completion/main.cpp new file mode 100644 index 000000000000..80329737928e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/type_completion/main.cpp @@ -0,0 +1,81 @@ +//===-- 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 +#include + +class CustomString +{ +public: + CustomString (const char* buffer) : + m_buffer(nullptr) + { + if (buffer) + { + auto l = strlen(buffer); + m_buffer = new char[1 + l]; + strcpy(m_buffer, buffer); + } + } + + ~CustomString () + { + delete[] m_buffer; + } + + const char* + GetBuffer () + { + return m_buffer; + } + +private: + char *m_buffer; +}; + +class NameAndAddress + { + public: + CustomString& GetName() { return *m_name; } + CustomString& GetAddress() { return *m_address; } + NameAndAddress(const char* N, const char* A) : m_name(new CustomString(N)), m_address(new CustomString(A)) + { + } + ~NameAndAddress() + { + } + + private: + CustomString* m_name; + CustomString* m_address; +}; + +typedef std::vector People; + +int main (int argc, const char * argv[]) +{ + People p; + p.push_back(NameAndAddress("Enrico","123 Main Street")); + p.push_back(NameAndAddress("Foo","10710 Johnson Avenue")); // Set break point at this line. + p.push_back(NameAndAddress("Arpia","6956 Florey Street")); + p.push_back(NameAndAddress("Apple","1 Infinite Loop")); // Set break point at this line. + p.push_back(NameAndAddress("Richard","9500 Gilman Drive")); + p.push_back(NameAndAddress("Bar","3213 Windsor Rd")); + + for (int j = 0; j + +int main (int argc, const char * argv[]) +{ + return 0; // break here +} + diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/Makefile b/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/Makefile new file mode 100644 index 000000000000..ede25f029bcd --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +CFLAGS ?= -g -Os + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py b/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py new file mode 100644 index 000000000000..a419500f7a17 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py @@ -0,0 +1,76 @@ +""" +Test that we can backtrace correctly with 'noreturn' functions on the stack +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class NoreturnUnwind(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @expectedFailurei386 #xfail to get buildbot green, failing config: i386 binary running on ubuntu 14.04 x86_64 + @skipIfWindows # clang-cl does not support gcc style attributes. + def test (self): + """Test that we can backtrace correctly with 'noreturn' functions on the stack""" + self.build() + self.setTearDownCleanup() + + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + thread = process.GetThreadAtIndex(0) + abort_frame_number = 0 + for f in thread.frames: + # Some C libraries mangle the abort symbol into __GI_abort. + if f.GetFunctionName() in ["abort", "__GI_abort"]: + break + abort_frame_number = abort_frame_number + 1 + + if self.TraceOn(): + print("Backtrace once we're stopped:") + for f in thread.frames: + print(" %d %s" % (f.GetFrameID(), f.GetFunctionName())) + + # I'm going to assume that abort() ends up calling/invoking another + # function before halting the process. In which case if abort_frame_number + # equals 0, we didn't find abort() in the backtrace. + if abort_frame_number == len(thread.frames): + self.fail("Unable to find abort() in backtrace.") + + func_c_frame_number = abort_frame_number + 1 + if thread.GetFrameAtIndex (func_c_frame_number).GetFunctionName() != "func_c": + self.fail("Did not find func_c() above abort().") + + # This depends on whether we see the func_b inlined function in the backtrace + # or not. I'm not interested in testing that aspect of the backtrace here + # right now. + + if thread.GetFrameAtIndex (func_c_frame_number + 1).GetFunctionName() == "func_b": + func_a_frame_number = func_c_frame_number + 2 + else: + func_a_frame_number = func_c_frame_number + 1 + + if thread.GetFrameAtIndex (func_a_frame_number).GetFunctionName() != "func_a": + self.fail("Did not find func_a() above func_c().") + + main_frame_number = func_a_frame_number + 1 + + if thread.GetFrameAtIndex (main_frame_number).GetFunctionName() != "main": + self.fail("Did not find main() above func_a().") diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/main.c b/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/main.c new file mode 100644 index 000000000000..7190e38f5d8c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/main.c @@ -0,0 +1,37 @@ +#include +#include +#include + +static void func_a (void) __attribute__((noinline)); +static void func_b (void) __attribute__((noreturn)); +static void func_c (void) __attribute__((noinline)); + +static void +func_c (void) +{ + abort (); +} + +static void +func_b (void) +{ + func_c (); + while (1) + ; +} + +static void +func_a (void) +{ + func_b (); +} + +int +main (int argc, char *argv[]) +{ + sleep (2); + + func_a (); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/Makefile b/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/TestSigtrampUnwind.py b/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/TestSigtrampUnwind.py new file mode 100644 index 000000000000..ddfb1122b6f1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/TestSigtrampUnwind.py @@ -0,0 +1,81 @@ +""" +Test that we can backtrace correctly with 'sigtramp' functions on the stack +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SigtrampUnwind(TestBase): + mydir = TestBase.compute_mydir(__file__) + + # On different platforms the "_sigtramp" and "__kill" frames are likely to be different. + # This test could probably be adapted to run on linux/*bsd easily enough. + @skipUnlessDarwin + def test (self): + """Test that we can backtrace correctly with _sigtramp on the stack""" + self.build() + self.setTearDownCleanup() + + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + + lldbutil.run_break_set_by_file_and_line (self, "main.c", line_number('main.c', '// Set breakpoint here'), num_expected_locations=1) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + self.expect("pro handle -n false -p true -s false SIGUSR1", "Have lldb pass SIGUSR1 signals", + substrs = ["SIGUSR1", "true", "false", "false"]) + + lldbutil.run_break_set_by_symbol (self, "handler", num_expected_locations=1, module_name="a.out") + + self.runCmd("continue") + + thread = process.GetThreadAtIndex(0) + + found_handler = False + found_sigtramp = False + found_kill = False + found_main = False + + for f in thread.frames: + if f.GetFunctionName() == "handler": + found_handler = True + if f.GetFunctionName() == "_sigtramp": + found_sigtramp = True + if f.GetFunctionName() == "__kill": + found_kill = True + if f.GetFunctionName() == "main": + found_main = True + + if self.TraceOn(): + print("Backtrace once we're stopped:") + for f in thread.frames: + print(" %d %s" % (f.GetFrameID(), f.GetFunctionName())) + + if found_handler == False: + self.fail("Unable to find handler() in backtrace.") + + if found_sigtramp == False: + self.fail("Unable to find _sigtramp() in backtrace.") + + if found_kill == False: + self.fail("Unable to find kill() in backtrace.") + + if found_main == False: + self.fail("Unable to find main() in backtrace.") diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/main.c b/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/main.c new file mode 100644 index 000000000000..aaa03e7aa843 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/sigtramp/main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +void handler (int in) +{ + puts ("in handler routine"); + while (1) + ; +} + +void +foo () +{ + puts ("in foo ()"); + kill (getpid(), SIGUSR1); +} +int main () +{ + puts ("in main"); // Set breakpoint here + signal (SIGUSR1, handler); + puts ("signal handler set up"); + foo(); + puts ("exiting"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/standard/Makefile b/packages/Python/lldbsuite/test/functionalities/unwind/standard/Makefile new file mode 100644 index 000000000000..146da30b12cb --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/standard/Makefile @@ -0,0 +1,3 @@ +LEVEL = ../../../make + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/standard/TestStandardUnwind.py b/packages/Python/lldbsuite/test/functionalities/unwind/standard/TestStandardUnwind.py new file mode 100644 index 000000000000..3f88e7b6e1aa --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/standard/TestStandardUnwind.py @@ -0,0 +1,145 @@ +""" +Test that we can backtrace correctly from standard functions. + +This test suit is a collection of automatically generated tests from the source files in the +directory. Please DON'T add individual test cases to this file. + +To add a new test case to this test suit please create a simple C/C++ application and put the +source file into the directory of the test cases. The test suit will automatically pick the +file up and generate a test case from it in run time (with name test_standard_unwind_ +after escaping some special characters). +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +test_source_dirs = ["."] + +class StandardUnwindTest(TestBase): + mydir = TestBase.compute_mydir(__file__) + + def standard_unwind_tests (self): + # The following variables have to be defined for each architecture and OS we testing for: + # base_function_names: List of function names where we accept that the stack unwinding is + # correct if they are on the stack. It should include the bottom most + # function on the stack and a list of functions where we know we can't + # unwind for any reason (list of expected failure functions) + # no_step_function_names: The list of functions where we don't want to step through + # instruction by instruction for any reason. (A valid reason is if + # it is impossible to step through a function instruction by + # instruction because it is special for some reason.) For these + # functions we will immediately do a step-out when we hit them. + + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match("arm-.*-.*-android", triple): + base_function_names = [ + "_start", # Base function on the stack + "__memcpy_base", # Function reached by a fall through from the previous function + "__memcpy_base_aligned", # Function reached by a fall through from the previous function + ] + no_step_function_names = [ + "__sync_fetch_and_add_4", # Calls into a special SO where we can't set a breakpoint + "pthread_mutex_lock", # Uses ldrex and strex what interferes with the software single stepping + "pthread_mutex_unlock", # Uses ldrex and strex what interferes with the software single stepping + "pthread_once", # Uses ldrex and strex what interferes with the software single stepping + ] + elif re.match("aarch64-.*-.*-android", triple): + base_function_names = [ + "do_arm64_start", # Base function on the stack + ] + no_step_function_names = [ + None, + "__cxa_guard_acquire", # Uses ldxr and stxr what interferes with the software single stepping + "__cxa_guard_release", # Uses ldxr and stxr what interferes with the software single stepping + "pthread_mutex_lock", # Uses ldxr and stxr what interferes with the software single stepping + "pthread_mutex_unlock", # Uses ldxr and stxr what interferes with the software single stepping + "pthread_once", # Uses ldxr and stxr what interferes with the software single stepping + ] + else: + self.skipTest("No expectations for the current architecture") + + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + target.BreakpointCreateByName("main") + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process is not None, "SBTarget.Launch() failed") + self.assertEqual(process.GetState(), lldb.eStateStopped, "The process didn't hit main") + + index = 0 + while process.GetState() == lldb.eStateStopped: + index += 1 + if process.GetNumThreads() > 1: + # In case of a multi threaded inferior if one of the thread is stopped in a blocking + # syscall and we try to step it then SBThread::StepInstruction() will block forever + self.skipTest("Multi threaded inferiors are not supported by this test") + + thread = process.GetThreadAtIndex(0) + + if self.TraceOn(): + print("INDEX: %u" % index) + for f in thread.frames: + print(f) + + if thread.GetFrameAtIndex(0).GetFunctionName() is not None: + found_main = False + for f in thread.frames: + if f.GetFunctionName() in base_function_names: + found_main = True + break + self.assertTrue(found_main, "Main function isn't found on the backtrace") + + if thread.GetFrameAtIndex(0).GetFunctionName() in no_step_function_names: + thread.StepOut() + else: + thread.StepInstruction(False) + +# Collect source files in the specified directories +test_source_files = set([]) +for d in test_source_dirs: + if os.path.isabs(d): + dirname = d + else: + dirname = os.path.join(os.path.dirname(__file__), d) + + for root, _, files in os.walk(dirname): + test_source_files = test_source_files | set(os.path.abspath(os.path.join(root, f)) for f in files) + +# Generate test cases based on the collected source files +for f in test_source_files: + if f.endswith(".cpp") or f.endswith(".c"): + @add_test_categories(["dwarf"]) + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_function_dwarf(self, f=f): + if f.endswith(".cpp"): + d = {'CXX_SOURCES': f} + elif f.endswith(".c"): + d = {'C_SOURCES': f} + + # If we can't compile the inferior just skip the test instead of failing it. + # It makes the test suit more robust when testing on several different architecture + # avoid the hassle of skipping tests manually. + try: + self.buildDwarf(dictionary=d) + self.setTearDownCleanup(d) + except: + if self.TraceOn(): + print(sys.exc_info()[0]) + self.skipTest("Inferior not supported") + self.standard_unwind_tests() + + test_name = "test_unwind_" + str(f) + for c in ".=()/\\": + test_name = test_name.replace(c, '_') + + test_function_dwarf.__name__ = test_name + setattr(StandardUnwindTest, test_function_dwarf.__name__, test_function_dwarf) diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/divmod.cpp b/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/divmod.cpp new file mode 100644 index 000000000000..75df6ba89372 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/divmod.cpp @@ -0,0 +1,15 @@ +//===-- divmod.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[]) +{ + signed long long a = 123456789, b = 12, c = a / b, d = a % b; + unsigned long long e = 123456789, f = 12, g = e / f, h = e % f; +} diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/fprintf.cpp b/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/fprintf.cpp new file mode 100644 index 000000000000..188738cb9bab --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/fprintf.cpp @@ -0,0 +1,16 @@ +//===-- 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 + +int +main(int argc, char const *argv[]) +{ + fprintf(stderr, "%d %p %s\n", argc, argv, argv[0]); +} diff --git a/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/new_delete.cpp b/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/new_delete.cpp new file mode 100644 index 000000000000..d240b89e50ab --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/unwind/standard/hand_written/new_delete.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. +// +//===----------------------------------------------------------------------===// + +int +main(int argc, char const *argv[]) +{ + int* p = new int; + delete p; +} diff --git a/packages/Python/lldbsuite/test/functionalities/value_md5_crash/Makefile b/packages/Python/lldbsuite/test/functionalities/value_md5_crash/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/value_md5_crash/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/value_md5_crash/TestValueMD5Crash.py b/packages/Python/lldbsuite/test/functionalities/value_md5_crash/TestValueMD5Crash.py new file mode 100644 index 000000000000..763ecc94c2eb --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/value_md5_crash/TestValueMD5Crash.py @@ -0,0 +1,52 @@ +""" +Verify that the hash computing logic for ValueObject's values can't crash us. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ValueMD5CrashTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// break here') + + @expectedFailureWindows("llvm.org/pr24663") + def test_with_run_command(self): + """Verify that the hash computing logic for ValueObject's values can't crash us.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + value = self.frame().FindVariable("a") + value.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) + + v = value.GetValue() + type_name = value.GetTypeName() + self.assertTrue(type_name == "B *", "a is a B*") + + self.runCmd("next") + self.runCmd("process kill") + + # now the process is dead, and value needs updating + v = value.GetValue() + + # if we are here, instead of crashed, the test succeeded diff --git a/packages/Python/lldbsuite/test/functionalities/value_md5_crash/main.cpp b/packages/Python/lldbsuite/test/functionalities/value_md5_crash/main.cpp new file mode 100644 index 000000000000..ba596b8653cf --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/value_md5_crash/main.cpp @@ -0,0 +1,29 @@ +//===-- 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: + virtual int foo() { return 1; } + virtual ~A () = default; + A() = default; +}; + +class B : public A { +public: + virtual int foo() { return 2; } + virtual ~B () = default; + B() = default; +}; + +int main() { + A* a = new B(); + a->foo(); // break here + return 0; // break here +} + diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/Makefile new file mode 100644 index 000000000000..8817fff55e8c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py new file mode 100644 index 000000000000..f0a0b5ab99d0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py @@ -0,0 +1,98 @@ +""" +Test lldb watchpoint that uses '-s size' to watch a pointed location with size. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class HelloWatchLocationTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # This is for verifying that watch location works. + self.violating_func = "do_bad_thing_with_location"; + # Build dictionary to have unique executable names for each test method. + self.exe_name = self.testMethodName + self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name} + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + @expectedFailureAll(archs=['mips', 'mipsel', 'mips64', 'mips64el']) # Most of the MIPS boards provide only one H/W watchpoints, and S/W watchpoints are not supported yet + def test_hello_watchlocation(self): + """Test watching a location with '-s size' option.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1, loc_exact=False) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint pointed to by 'g_char_ptr'. + # The main.cpp, by design, misbehaves by not following the agreed upon + # protocol of using a mutex while accessing the global pool and by not + # incrmenting the global pool by 2. + self.expect("watchpoint set expression -w write -s 1 -- g_char_ptr", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 1', 'type = w']) + # Get a hold of the watchpoint id just created, it is used later on to + # match the watchpoint id which is expected to be fired. + match = re.match("Watchpoint created: Watchpoint (.*):", self.res.GetOutput().splitlines()[0]) + if match: + expected_wp_id = int(match.group(1), 0) + else: + self.fail("Grokking watchpoint id faailed!") + + self.runCmd("expr unsigned val = *g_char_ptr; val") + self.expect(self.res.GetOutput().splitlines()[0], exe=False, + endstr = ' = 0') + + self.runCmd("watchpoint set expression -w write -s 4 -- &threads[0]") + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type), but + # only once. The stop reason of the thread should be watchpoint. + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stopped', + 'stop reason = watchpoint %d' % expected_wp_id]) + + # Switch to the thread stopped due to watchpoint and issue some commands. + self.switch_to_thread_with_stop_reason(lldb.eStopReasonWatchpoint) + self.runCmd("thread backtrace") + self.expect("frame info", + substrs = [self.violating_func]) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 1. + self.expect("watchpoint list -v", + substrs = ['hit_count = 1']) + + self.runCmd("thread backtrace all") diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/main.cpp b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/main.cpp new file mode 100644 index 000000000000..59b0afc6d5f7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchlocation/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. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include + +std::default_random_engine g_random_engine{std::random_device{}()}; +std::uniform_int_distribution<> g_distribution{0, 3000000}; +std::condition_variable g_condition_variable; +std::mutex g_mutex; +int g_count; + +char *g_char_ptr = nullptr; + +void +barrier_wait() +{ + std::unique_lock lock{g_mutex}; + if (--g_count > 0) + g_condition_variable.wait(lock); + else + g_condition_variable.notify_all(); +} + +void +do_bad_thing_with_location(char *char_ptr, char new_val) +{ + unsigned what = new_val; + printf("new value written to location(%p) = %u\n", char_ptr, what); + *char_ptr = new_val; +} + +uint32_t +access_pool (bool flag = false) +{ + static std::mutex g_access_mutex; + if (!flag) + g_access_mutex.lock(); + + char old_val = *g_char_ptr; + if (flag) + do_bad_thing_with_location(g_char_ptr, old_val + 1); + + if (!flag) + g_access_mutex.unlock(); + return *g_char_ptr; +} + +void +thread_func (uint32_t thread_index) +{ + printf ("%s (thread index = %u) startng...\n", __FUNCTION__, thread_index); + + barrier_wait(); + + uint32_t count = 0; + uint32_t val; + while (count++ < 15) + { + // random micro second sleep from zero to 3 seconds + int usec = g_distribution(g_random_engine); + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + std::this_thread::sleep_for(std::chrono::microseconds{usec}); + + if (count < 7) + val = access_pool (); + else + val = access_pool (true); + + printf ("%s (thread = %u) after usleep access_pool returns %d (count=%d)...\n", __FUNCTION__, thread_index, val, count); + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); +} + + +int main (int argc, char const *argv[]) +{ + g_count = 4; + std::thread threads[3]; + + g_char_ptr = new char{}; + + // Create 3 threads + for (auto &thread : threads) + thread = std::thread{thread_func, std::distance(threads, &thread)}; + + printf ("Before turning all three threads loose...\n"); // Set break point at this line. + barrier_wait(); + + // Join all of our threads + for (auto &thread : threads) + thread.join(); + + delete g_char_ptr; + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py new file mode 100644 index 000000000000..57467c38d5f8 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py @@ -0,0 +1,84 @@ +""" +Test my first lldb watchpoint. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class HelloWatchpointTestCase(TestBase): + + def getCategories (self): + return ['basic_process'] + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # And the watchpoint variable declaration line number. + self.decl = line_number(self.source, '// Watchpoint variable declaration.') + self.exe_name = 'a.out' + self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name} + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") + def test_hello_watchpoint_using_watchpoint_set(self): + """Test a simple sequence of watchpoint creation and watchpoint hit.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint for 'global'. + # There should be only one watchpoint hit (see main.c). + self.expect("watchpoint set variable -w write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = w', + '%s:%d' % (self.source, self.decl)]) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type), but + # only once. The stop reason of the thread should be watchpoint. + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stopped', + 'stop reason = watchpoint']) + + self.runCmd("process continue") + + # Don't expect the read of 'global' to trigger a stop exception. + process = self.dbg.GetSelectedTarget().GetProcess() + if process.GetState() == lldb.eStateStopped: + self.assertFalse(lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint)) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 1. + self.expect("watchpoint list -v", + substrs = ['hit_count = 1']) diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/main.c b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/main.c new file mode 100644 index 000000000000..11a738a0de43 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/hello_watchpoint/main.c @@ -0,0 +1,30 @@ +//===-- 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 + +int32_t global = 10; // Watchpoint variable declaration. +char gchar1 = 'a'; +char gchar2 = 'b'; + +int main(int argc, char** argv) { + int local = 0; + printf("&global=%p\n", &global); + printf("about to write to 'global'...\n"); // Set break point at this line. + // When stopped, watch 'global' for write. + global = 20; + gchar1 += 1; + gchar2 += 1; + local += argc; + ++local; + printf("local: %d\n", local); + printf("global=%d\n", global); + printf("gchar1='%c'\n", gchar1); + printf("gchar2='%c'\n", gchar2); +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/Makefile new file mode 100644 index 000000000000..8817fff55e8c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py new file mode 100644 index 000000000000..2318214a0d5c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py @@ -0,0 +1,137 @@ +""" +Test that lldb watchpoint works for multiple threads. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class WatchpointForMultipleThreadsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watchpoint_multiple_threads(self): + """Test that lldb watchpoint works for multiple threads.""" + self.build() + self.setTearDownCleanup() + self.hello_multiple_threads() + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watchpoint_multiple_threads_wp_set_and_then_delete(self): + """Test that lldb watchpoint works for multiple threads, and after the watchpoint is deleted, the watchpoint event should no longer fires.""" + self.build() + self.setTearDownCleanup() + self.hello_multiple_threads_wp_set_and_then_delete() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.first_stop = line_number(self.source, '// Set break point at this line') + + def hello_multiple_threads(self): + """Test that lldb watchpoint works for multiple threads.""" + self.runCmd("file %s" % os.path.join(os.getcwd(), 'a.out'), CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.first_stop, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint for variable 'g_val'. + # The main.cpp, by design, misbehaves by not following the agreed upon + # protocol of using a mutex while accessing the global pool and by not + # writing to the variable. + self.expect("watchpoint set variable -w write g_val", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = w']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + while True: + self.runCmd("process continue") + + self.runCmd("thread list") + if "stop reason = watchpoint" in self.res.GetOutput(): + # Good, we verified that the watchpoint works! + self.runCmd("thread backtrace all") + break + else: + self.fail("The stop reason should be either break or watchpoint") + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 1. + self.expect("watchpoint list -v", + substrs = ['hit_count = 1']) + + def hello_multiple_threads_wp_set_and_then_delete(self): + """Test that lldb watchpoint works for multiple threads, and after the watchpoint is deleted, the watchpoint event should no longer fires.""" + self.runCmd("file %s" % os.path.join(os.getcwd(), 'a.out'), CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.first_stop, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint for variable 'g_val'. + # The main.cpp, by design, misbehaves by not following the agreed upon + # protocol of using a mutex while accessing the global pool and by not + # writing to the variable. + self.expect("watchpoint set variable -w write g_val", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = w']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + watchpoint_stops = 0 + while True: + self.runCmd("process continue") + self.runCmd("process status") + if re.search("Process .* exited", self.res.GetOutput()): + # Great, we are done with this test! + break + + self.runCmd("thread list") + if "stop reason = watchpoint" in self.res.GetOutput(): + self.runCmd("thread backtrace all") + watchpoint_stops += 1 + if watchpoint_stops > 1: + self.fail("Watchpoint hits not supposed to exceed 1 by design!") + # Good, we verified that the watchpoint works! Now delete the watchpoint. + if self.TraceOn(): + print("watchpoint_stops=%d at the moment we delete the watchpoint" % watchpoint_stops) + self.runCmd("watchpoint delete 1") + self.expect("watchpoint list -v", + substrs = ['No watchpoints currently set.']) + continue + else: + self.fail("The stop reason should be either break or watchpoint") diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/main.cpp b/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/main.cpp new file mode 100644 index 000000000000..8a31041f8fca --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/multiple_threads/main.cpp @@ -0,0 +1,83 @@ +//===-- 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 +#include +#include +#include + +std::default_random_engine g_random_engine{std::random_device{}()}; +std::uniform_int_distribution<> g_distribution{0, 3000000}; + +uint32_t g_val = 0; + + +uint32_t +access_pool (bool flag = false) +{ + static std::mutex g_access_mutex; + if (!flag) + g_access_mutex.lock(); + + uint32_t old_val = g_val; + if (flag) + { + printf("changing g_val to %d...\n", old_val + 1); + g_val = old_val + 1; + } + + if (!flag) + g_access_mutex.unlock(); + return g_val; +} + +void +thread_func (uint32_t thread_index) +{ + // Break here in order to allow the thread + // to inherit the global watchpoint state. + printf ("%s (thread index = %u) startng...\n", __FUNCTION__, thread_index); + + uint32_t count = 0; + uint32_t val; + while (count++ < 15) + { + // random micro second sleep from zero to 3 seconds + int usec = g_distribution(g_random_engine); + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + std::this_thread::sleep_for(std::chrono::microseconds{usec}); + + if (count < 7) + val = access_pool (); + else + val = access_pool (true); + + printf ("%s (thread = %u) after usleep access_pool returns %d (count=%d)...\n", __FUNCTION__, thread_index, val, count); + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); +} + + +int main (int argc, char const *argv[]) +{ + std::thread threads[3]; + + printf ("Before turning all three threads loose...\n"); // Set break point at this line, + // in order to set our watchpoint. + // Create 3 threads + for (auto &thread : threads) + thread = std::thread{thread_func, std::distance(threads, &thread)}; + + // Join all of our threads + for (auto &thread : threads) + thread.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py new file mode 100644 index 000000000000..dd18a6482066 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py @@ -0,0 +1,113 @@ +"""Test stepping over watchpoints.""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + + +class TestStepOverWatchpoint(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def getCategories(self): + return ['basic_process'] + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") + def test(self): + """Test stepping over watchpoints.""" + self.build() + exe = os.path.join(os.getcwd(), 'a.out') + + target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + lldbutil.run_break_set_by_symbol(self, 'main') + + process = target.LaunchSimple(None, None, + self.get_process_working_directory()) + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + thread = lldbutil.get_stopped_thread(process, + lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "Failed to get thread.") + + frame = thread.GetFrameAtIndex(0) + self.assertTrue(frame.IsValid(), "Failed to get frame.") + + read_value = frame.FindValue('g_watch_me_read', + lldb.eValueTypeVariableGlobal) + self.assertTrue(read_value.IsValid(), "Failed to find read value.") + + error = lldb.SBError() + + # resolve_location=True, read=True, write=False + read_watchpoint = read_value.Watch(True, True, False, error) + self.assertTrue(error.Success(), + "Error while setting watchpoint: %s" % + error.GetCString()) + self.assertTrue(read_watchpoint, "Failed to set read watchpoint.") + + thread.StepOver() + self.assertTrue(thread.GetStopReason() == lldb.eStopReasonWatchpoint, + STOPPED_DUE_TO_WATCHPOINT) + self.assertTrue(thread.GetStopDescription(20) == 'watchpoint 1') + + process.Continue() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + self.assertTrue(thread.GetStopDescription(20) == 'step over') + + self.step_inst_for_watchpoint(1) + + write_value = frame.FindValue('g_watch_me_write', + lldb.eValueTypeVariableGlobal) + self.assertTrue(write_value, "Failed to find write value.") + + # Most of the MIPS boards provide only one H/W watchpoints, and S/W watchpoints are not supported yet + arch = self.getArchitecture() + if arch in ['mips', 'mipsel', 'mips64', 'mips64el']: + self.runCmd("watchpoint delete 1") + + # resolve_location=True, read=False, write=True + write_watchpoint = write_value.Watch(True, False, True, error) + self.assertTrue(read_watchpoint, "Failed to set write watchpoint.") + self.assertTrue(error.Success(), + "Error while setting watchpoint: %s" % + error.GetCString()) + + thread.StepOver() + self.assertTrue(thread.GetStopReason() == lldb.eStopReasonWatchpoint, + STOPPED_DUE_TO_WATCHPOINT) + self.assertTrue(thread.GetStopDescription(20) == 'watchpoint 2') + + process.Continue() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + self.assertTrue(thread.GetStopDescription(20) == 'step over') + + self.step_inst_for_watchpoint(2) + + def step_inst_for_watchpoint(self, wp_id): + watchpoint_hit = False + current_line = self.frame().GetLineEntry().GetLine() + while self.frame().GetLineEntry().GetLine() == current_line: + self.thread().StepInstruction(False) # step_over=False + stop_reason = self.thread().GetStopReason() + if stop_reason == lldb.eStopReasonWatchpoint: + self.assertFalse(watchpoint_hit, "Watchpoint already hit.") + expected_stop_desc = "watchpoint %d" % wp_id + actual_stop_desc = self.thread().GetStopDescription(20) + self.assertTrue(actual_stop_desc == expected_stop_desc, + "Watchpoint ID didn't match.") + watchpoint_hit = True + else: + self.assertTrue(stop_reason == lldb.eStopReasonPlanComplete, + STOPPED_DUE_TO_STEP_IN) + self.assertTrue(watchpoint_hit, "Watchpoint never hit.") diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/main.c b/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/main.c new file mode 100644 index 000000000000..2d87d9a2f73f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/step_over_watchpoint/main.c @@ -0,0 +1,19 @@ +char g_watch_me_read; +char g_watch_me_write; +char g_temp; + +void watch_read() { + g_temp = g_watch_me_read; +} + +void watch_write() { + g_watch_me_write = g_temp; +} + +int main() { + watch_read(); + g_temp = g_watch_me_read; + watch_write(); + g_watch_me_write = g_temp; + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/TestWatchedVarHitWhenInScope.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/TestWatchedVarHitWhenInScope.py new file mode 100644 index 000000000000..21f1fb68426b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/TestWatchedVarHitWhenInScope.py @@ -0,0 +1,83 @@ +""" +Test that a variable watchpoint should only hit when in scope. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class WatchedVariableHitWhenInScopeTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # + # This test depends on not tracking watchpoint expression hits if we have + # left the watchpoint scope. We will provide such an ability at some point + # but the way this was done was incorrect, and it is unclear that for the + # most part that's not what folks mostly want, so we have to provide a + # clearer API to express this. + # + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + self.exe_name = self.testMethodName + self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name} + + @unittest2.expectedFailure("rdar://problem/18685649") + def test_watched_var_should_only_hit_when_in_scope(self): + """Test that a variable watchpoint should only hit when in scope.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped in main. + lldbutil.run_break_set_by_symbol (self, "main", num_expected_locations=-1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a watchpoint for 'c.a'. + # There should be only one watchpoint hit (see main.c). + self.expect("watchpoint set variable c.a", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = w']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type), but + # only once. The stop reason of the thread should be watchpoint. + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stopped', + 'stop reason = watchpoint']) + + self.runCmd("process continue") + # Don't expect the read of 'global' to trigger a stop exception. + # The process status should be 'exited'. + self.expect("process status", + substrs = ['exited']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 1. + self.expect("watchpoint list -v", + substrs = ['hit_count = 1']) diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/main.c b/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/main.c new file mode 100644 index 000000000000..1bf7a00ac837 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/variable_out_of_scope/main.c @@ -0,0 +1,15 @@ +typedef struct +{ + int a; + float b; +} mystruct; + +int main() +{ + mystruct c; + + c.a = 5; + c.b = 3.6; + + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/TestWatchpointCommands.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/TestWatchpointCommands.py new file mode 100644 index 000000000000..339de45a085a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/TestWatchpointCommands.py @@ -0,0 +1,313 @@ +""" +Test watchpoint list, enable, disable, and delete commands. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class WatchpointCommandsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + self.line2 = line_number(self.source, '// Set 2nd break point for disable_then_enable test case.') + # And the watchpoint variable declaration line number. + self.decl = line_number(self.source, '// Watchpoint variable declaration.') + # Build dictionary to have unique executable names for each test method. + self.exe_name = self.testMethodName + self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name} + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_rw_watchpoint(self): + """Test read_write watchpoint and expect to stop two times.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a read_write-type watchpoint for 'global'. + # There should be two watchpoint hits (see main.c). + self.expect("watchpoint set variable -w read_write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = rw', + '%s:%d' % (self.source, self.decl)]) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['Number of supported hardware watchpoints:', + 'hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (read_write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stop reason = watchpoint']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (read_write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stop reason = watchpoint']) + + self.runCmd("process continue") + + # There should be no more watchpoint hit and the process status should + # be 'exited'. + self.expect("process status", + substrs = ['exited']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 2. + self.expect("watchpoint list -v", + substrs = ['hit_count = 2']) + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_rw_watchpoint_delete(self): + """Test delete watchpoint and expect not to stop for watchpoint.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a read_write-type watchpoint for 'global'. + # There should be two watchpoint hits (see main.c). + self.expect("watchpoint set variable -w read_write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = rw', + '%s:%d' % (self.source, self.decl)]) + + # Delete the watchpoint immediately, but set auto-confirm to true first. + self.runCmd("settings set auto-confirm true") + self.expect("watchpoint delete", + substrs = ['All watchpoints removed.']) + # Restore the original setting of auto-confirm. + self.runCmd("settings clear auto-confirm") + + # Use the '-v' option to do verbose listing of the watchpoint. + self.runCmd("watchpoint list -v") + + self.runCmd("process continue") + + # There should be no more watchpoint hit and the process status should + # be 'exited'. + self.expect("process status", + substrs = ['exited']) + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_rw_watchpoint_set_ignore_count(self): + """Test watchpoint ignore count and expect to not to stop at all.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a read_write-type watchpoint for 'global'. + # There should be two watchpoint hits (see main.c). + self.expect("watchpoint set variable -w read_write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = rw', + '%s:%d' % (self.source, self.decl)]) + + # Set the ignore count of the watchpoint immediately. + self.expect("watchpoint ignore -i 2", + substrs = ['All watchpoints ignored.']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # Expect to find an ignore_count of 2. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0', 'ignore_count = 2']) + + self.runCmd("process continue") + + # There should be no more watchpoint hit and the process status should + # be 'exited'. + self.expect("process status", + substrs = ['exited']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # Expect to find a hit_count of 2 as well. + self.expect("watchpoint list -v", + substrs = ['hit_count = 2', 'ignore_count = 2']) + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_rw_disable_after_first_stop(self): + """Test read_write watchpoint but disable it after the first stop.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a read_write-type watchpoint for 'global'. + # There should be two watchpoint hits (see main.c). + self.expect("watchpoint set variable -w read_write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = rw', + '%s:%d' % (self.source, self.decl)]) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['state = enabled', 'hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (read_write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stop reason = watchpoint']) + + # Before continuing, we'll disable the watchpoint, which means we won't + # stop again after this. + self.runCmd("watchpoint disable") + + self.expect("watchpoint list -v", + substrs = ['state = disabled', 'hit_count = 1']) + + self.runCmd("process continue") + + # There should be no more watchpoint hit and the process status should + # be 'exited'. + self.expect("process status", + substrs = ['exited']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 1. + self.expect("watchpoint list -v", + substrs = ['hit_count = 1']) + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_rw_disable_then_enable(self): + """Test read_write watchpoint, disable initially, then enable it.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, None, self.line2, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a read_write-type watchpoint for 'global'. + # There should be two watchpoint hits (see main.c). + self.expect("watchpoint set variable -w read_write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = rw', + '%s:%d' % (self.source, self.decl)]) + + # Immediately, we disable the watchpoint. We won't be stopping due to a + # watchpoint after this. + self.runCmd("watchpoint disable") + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['state = disabled', 'hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the breakpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stop reason = breakpoint']) + + # Before continuing, we'll enable the watchpoint, which means we will + # stop again after this. + self.runCmd("watchpoint enable") + + self.expect("watchpoint list -v", + substrs = ['state = enabled', 'hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (read_write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stop reason = watchpoint']) + + self.runCmd("process continue") + + # There should be no more watchpoint hit and the process status should + # be 'exited'. + self.expect("process status", + substrs = ['exited']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 1. + self.expect("watchpoint list -v", + substrs = ['hit_count = 1']) diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/Makefile new file mode 100644 index 000000000000..ee6b9cc62b40 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/TestWatchpointCommandLLDB.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/TestWatchpointCommandLLDB.py new file mode 100644 index 000000000000..f2bf90866330 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/TestWatchpointCommandLLDB.py @@ -0,0 +1,139 @@ +""" +Test 'watchpoint command'. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class WatchpointLLDBCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # And the watchpoint variable declaration line number. + self.decl = line_number(self.source, '// Watchpoint variable declaration.') + # Build dictionary to have unique executable names for each test method. + self.exe_name = 'a%d.out' % self.test_number + self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name} + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watchpoint_command(self): + """Test 'watchpoint command'.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint for 'global'. + self.expect("watchpoint set variable -w write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = w', + '%s:%d' % (self.source, self.decl)]) + + self.runCmd('watchpoint command add 1 -o "expr -- cookie = 777"') + + # List the watchpoint command we just added. + self.expect("watchpoint command list 1", + substrs = ['expr -- cookie = 777']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stop reason = watchpoint']) + + # Check that the watchpoint snapshoting mechanism is working. + self.expect("watchpoint list -v", + substrs = ['old value:', ' = 0', + 'new value:', ' = 1']) + + # The watchpoint command "forced" our global variable 'cookie' to become 777. + self.expect("frame variable --show-globals cookie", + substrs = ['(int32_t)', 'cookie = 777']) + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watchpoint_command_can_disable_a_watchpoint(self): + """Test that 'watchpoint command' action can disable a watchpoint after it is triggered.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint for 'global'. + self.expect("watchpoint set variable -w write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = w', + '%s:%d' % (self.source, self.decl)]) + + self.runCmd('watchpoint command add 1 -o "watchpoint disable 1"') + + # List the watchpoint command we just added. + self.expect("watchpoint command list 1", + substrs = ['watchpoint disable 1']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stop reason = watchpoint']) + + # Check that the watchpoint has been disabled. + self.expect("watchpoint list -v", + substrs = ['disabled']) + + self.runCmd("process continue") + + # There should be no more watchpoint hit and the process status should + # be 'exited'. + self.expect("process status", + substrs = ['exited']) diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/TestWatchpointCommandPython.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/TestWatchpointCommandPython.py new file mode 100644 index 000000000000..a476aebb8db9 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/TestWatchpointCommandPython.py @@ -0,0 +1,87 @@ +""" +Test 'watchpoint command'. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class WatchpointPythonCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # And the watchpoint variable declaration line number. + self.decl = line_number(self.source, '// Watchpoint variable declaration.') + # Build dictionary to have unique executable names for each test method. + self.exe_name = self.testMethodName + self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name} + + @skipIfFreeBSD # timing out on buildbot + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + def test_watchpoint_command(self): + """Test 'watchpoint command'.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) +# self.expect("breakpoint set -l %d" % self.line, BREAKPOINT_CREATED, +# startstr = "Breakpoint created: 1: file ='%s', line = %d, locations = 1" % +# (self.source, self.line))# + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint for 'global'. + self.expect("watchpoint set variable -w write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = w', + '%s:%d' % (self.source, self.decl)]) + + self.runCmd('watchpoint command add -s python 1 -o \'frame.EvaluateExpression("cookie = 777")\'') + + # List the watchpoint command we just added. + self.expect("watchpoint command list 1", + substrs = ['frame.EvaluateExpression', 'cookie = 777']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stop reason = watchpoint']) + + # Check that the watchpoint snapshoting mechanism is working. + self.expect("watchpoint list -v", + substrs = ['old value:', ' = 0', + 'new value:', ' = 1']) + + # The watchpoint command "forced" our global variable 'cookie' to become 777. + self.expect("frame variable --show-globals cookie", + substrs = ['(int32_t)', 'cookie = 777']) diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/main.cpp b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/main.cpp new file mode 100644 index 000000000000..6cb80c62217e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/command/main.cpp @@ -0,0 +1,28 @@ +//===-- 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 + +int32_t global = 0; // Watchpoint variable declaration. +int32_t cookie = 0; + +static void modify(int32_t &var) { + ++var; +} + +int main(int argc, char** argv) { + int local = 0; + printf("&global=%p\n", &global); + printf("about to write to 'global'...\n"); // Set break point at this line. + for (int i = 0; i < 10; ++i) + modify(global); + + printf("global=%d\n", global); + printf("cookie=%d\n", cookie); +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/Makefile new file mode 100644 index 000000000000..ee6b9cc62b40 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/TestWatchpointConditionCmd.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/TestWatchpointConditionCmd.py new file mode 100644 index 000000000000..355204a4ce1e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/TestWatchpointConditionCmd.py @@ -0,0 +1,78 @@ +""" +Test watchpoint modify command to set condition on a watchpoint. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class WatchpointConditionCmdTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # And the watchpoint variable declaration line number. + self.decl = line_number(self.source, '// Watchpoint variable declaration.') + # Build dictionary to have unique executable names for each test method. + self.exe_name = self.testMethodName + self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name} + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watchpoint_cond(self): + """Test watchpoint condition.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint for 'global'. + # With a condition of 'global==5'. + self.expect("watchpoint set variable -w write global", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 4', 'type = w', + '%s:%d' % (self.source, self.decl)]) + + self.runCmd("watchpoint modify -c 'global==5'") + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0', 'global==5']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stop reason = watchpoint']) + self.expect("frame variable --show-globals global", + substrs = ['(int32_t)', 'global = 5']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 2. + self.expect("watchpoint list -v", + substrs = ['hit_count = 5']) diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp new file mode 100644 index 000000000000..f4c3527f8af2 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp @@ -0,0 +1,28 @@ +//===-- 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 + +int32_t global = 0; // Watchpoint variable declaration. + +static void modify(int32_t &var) { + ++var; +} + +int main(int argc, char** argv) { + int local = 0; + printf("&global=%p\n", &global); + printf("about to write to 'global'...\n"); // Set break point at this line. + // When stopped, watch 'global', + // for the condition "global == 5". + for (int i = 0; i < 10; ++i) + modify(global); + + printf("global=%d\n", global); +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/main.c b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/main.c new file mode 100644 index 000000000000..b20eaf494fbf --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_commands/main.c @@ -0,0 +1,24 @@ +//===-- 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 + +int32_t global = 10; // Watchpoint variable declaration. + +int main(int argc, char** argv) { + int local = 0; + printf("&global=%p\n", &global); + printf("about to write to 'global'...\n"); // Set break point at this line. + // When stopped, watch 'global'. + global = 20; + local += argc; + ++local; // Set 2nd break point for disable_then_enable test case. + printf("local: %d\n", local); + printf("global=%d\n", global); +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/TestWatchpointEvents.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/TestWatchpointEvents.py new file mode 100644 index 000000000000..e4d6e019c20e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/TestWatchpointEvents.py @@ -0,0 +1,89 @@ +"""Test that adding, deleting and modifying watchpoints sends the appropriate events.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestWatchpointEvents (TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers that we will step to in main: + self.main_source = "main.c" + + @add_test_categories(['pyapi']) + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_with_python_api(self): + """Test that adding, deleting and modifying watchpoints sends the appropriate events.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + break_in_main = target.BreakpointCreateBySourceRegex ('// Put a breakpoint here.', self.main_source_spec) + self.assertTrue(break_in_main, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_in_main) + + if len(threads) != 1: + self.fail ("Failed to stop at first breakpoint in main.") + + thread = threads[0] + frame = thread.GetFrameAtIndex(0) + local_var = frame.FindVariable ("local_var") + self.assertTrue (local_var.IsValid()) + + self.listener = lldb.SBListener("com.lldb.testsuite_listener") + self.target_bcast = target.GetBroadcaster() + self.target_bcast.AddListener (self.listener, lldb.SBTarget.eBroadcastBitWatchpointChanged) + self.listener.StartListeningForEvents (self.target_bcast, lldb.SBTarget.eBroadcastBitWatchpointChanged) + + error = lldb.SBError() + local_watch = local_var.Watch(True, True, True, error) + if not error.Success(): + self.fail ("Failed to make watchpoint for local_var: %s"%(error.GetCString())) + + self.GetWatchpointEvent (lldb.eWatchpointEventTypeAdded) + # Now change some of the features of this watchpoint and make sure we get events: + local_watch.SetEnabled(False) + self.GetWatchpointEvent (lldb.eWatchpointEventTypeDisabled) + + local_watch.SetIgnoreCount(10) + self.GetWatchpointEvent (lldb.eWatchpointEventTypeIgnoreChanged) + + local_watch.SetCondition ("1 == 2") + self.GetWatchpointEvent (lldb.eWatchpointEventTypeConditionChanged) + + def GetWatchpointEvent (self, event_type): + # We added a watchpoint so we should get a watchpoint added event. + event = lldb.SBEvent() + success = self.listener.WaitForEvent (1, event) + self.assertTrue(success == True, "Successfully got watchpoint event") + self.assertTrue (lldb.SBWatchpoint.EventIsWatchpointEvent(event), "Event is a watchpoint event.") + found_type = lldb.SBWatchpoint.GetWatchpointEventTypeFromEvent (event) + self.assertTrue (found_type == event_type, "Event is not correct type, expected: %d, found: %d"%(event_type, found_type)) + # There shouldn't be another event waiting around: + found_event = self.listener.PeekAtNextEventForBroadcasterWithType (self.target_bcast, lldb.SBTarget.eBroadcastBitBreakpointChanged, event) + if found_event: + print("Found an event I didn't expect: ", event) + + self.assertTrue (not found_event, "Only one event per change.") diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/main.c b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/main.c new file mode 100644 index 000000000000..4b917536a161 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_events/main.c @@ -0,0 +1,9 @@ +#include + +int +main (int argc, char **argv) +{ + int local_var = 10; + printf ("local_var is: %d.\n", local_var++); // Put a breakpoint here. + return local_var; +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/TestValueOfVectorVariable.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/TestValueOfVectorVariable.py new file mode 100644 index 000000000000..73752d2d18d5 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/TestValueOfVectorVariable.py @@ -0,0 +1,47 @@ +""" +Test displayed value of a vector variable while doing watchpoint operations +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestValueOfVectorVariableTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_value_of_vector_variable_using_watchpoint_set(self): + """Test verify displayed value of vector variable.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + self.value_of_vector_variable_with_watchpoint_set() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + self.exe_name = 'a.out' + self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name} + + def value_of_vector_variable_with_watchpoint_set(self): + """Test verify displayed value of vector variable""" + exe = os.path.join(os.getcwd(), 'a.out') + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Set break to get a frame + self.runCmd("b main") + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Value of a vector variable should be displayed correctly + self.expect("watchpoint set variable global_vector", WATCHPOINT_CREATED, + substrs = ['new value: (1, 2, 3, 4)']) diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/main.c b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/main.c new file mode 100644 index 000000000000..98f8c477eb31 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_on_vectors/main.c @@ -0,0 +1,16 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +typedef char v4i8 __attribute__ ((vector_size(4))); +v4i8 global_vector = {1, 2, 3, 4}; + +int +main () +{ + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/Makefile b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/Makefile new file mode 100644 index 000000000000..8817fff55e8c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py new file mode 100644 index 000000000000..6bdac7024847 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py @@ -0,0 +1,89 @@ +""" +Test lldb watchpoint that uses 'watchpoint set -w write -s size' to watch a pointed location with size. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class WatchLocationUsingWatchpointSetTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # This is for verifying that watch location works. + self.violating_func = "do_bad_thing_with_location"; + # Build dictionary to have unique executable names for each test method. + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watchlocation_using_watchpoint_set(self): + """Test watching a location with 'watchpoint set expression -w write -s size' option.""" + self.build() + self.setTearDownCleanup() + + exe = os.path.join(os.getcwd(), 'a.out') + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint pointed to by 'g_char_ptr' and + # with offset as 7. + # The main.cpp, by design, misbehaves by not following the agreed upon + # protocol of only accessing the allowable index range of [0, 6]. + self.expect("watchpoint set expression -w write -s 1 -- g_char_ptr + 7", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 1', 'type = w']) + self.runCmd("expr unsigned val = g_char_ptr[7]; val") + self.expect(self.res.GetOutput().splitlines()[0], exe=False, + endstr = ' = 0') + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type), but + # only once. The stop reason of the thread should be watchpoint. + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stopped', + 'stop reason = watchpoint', + self.violating_func]) + + # Switch to the thread stopped due to watchpoint and issue some commands. + self.switch_to_thread_with_stop_reason(lldb.eStopReasonWatchpoint) + self.runCmd("thread backtrace") + self.runCmd("expr unsigned val = g_char_ptr[7]; val") + self.expect(self.res.GetOutput().splitlines()[0], exe=False, + endstr = ' = 99') + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be the same as the number of threads that + # stopped on a watchpoint. + threads = lldbutil.get_stopped_threads(self.process(), lldb.eStopReasonWatchpoint) + self.expect("watchpoint list -v", + substrs = ['hit_count = %d' % len(threads)]) + + self.runCmd("thread backtrace all") diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/TestWatchpointSetErrorCases.py b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/TestWatchpointSetErrorCases.py new file mode 100644 index 000000000000..27c759e6fb64 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/TestWatchpointSetErrorCases.py @@ -0,0 +1,67 @@ +""" +Test error cases for the 'watchpoint set' command to make sure it errors out when necessary. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class WatchpointSetErrorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # Build dictionary to have unique executable names for each test method. + + def test_error_cases_with_watchpoint_set(self): + """Test error cases with the 'watchpoint set' command.""" + self.build() + self.setTearDownCleanup() + + exe = os.path.join(os.getcwd(), 'a.out') + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Try some error conditions: + + # 'watchpoint set' is now a multiword command. + self.expect("watchpoint set", + substrs = ['The following subcommands are supported:', + 'expression', + 'variable']) + self.runCmd("watchpoint set variable -w read_write", check=False) + + # 'watchpoint set expression' with '-w' or '-s' specified now needs + # an option terminator and a raw expression after that. + self.expect("watchpoint set expression -w write --", error=True, + startstr = 'error: ') + + # It's an error if the expression did not evaluate to an address. + self.expect("watchpoint set expression MyAggregateDataType", error=True, + startstr = 'error: expression did not evaluate to an address') + + # Wrong size parameter is an error. + self.expect("watchpoint set variable -s -128", error=True, + substrs = ['invalid enumeration value']) diff --git a/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/main.cpp b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/main.cpp new file mode 100644 index 000000000000..796c0ef359df --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_set_command/main.cpp @@ -0,0 +1,121 @@ +//===-- 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 +#include +#include +#include + +std::default_random_engine g_random_engine{std::random_device{}()}; +std::uniform_int_distribution<> g_distribution{0, 3000000}; +std::condition_variable g_condition_variable; +std::mutex g_mutex; +int g_count; + +char *g_char_ptr = nullptr; + +void +barrier_wait() +{ + std::unique_lock lock{g_mutex}; + if (--g_count > 0) + g_condition_variable.wait(lock); + else + g_condition_variable.notify_all(); +} + +void +do_bad_thing_with_location(unsigned index, char *char_ptr, char new_val) +{ + unsigned what = new_val; + printf("new value written to array(%p) and index(%u) = %u\n", char_ptr, index, what); + char_ptr[index] = new_val; +} + +uint32_t +access_pool (bool flag = false) +{ + static std::mutex g_access_mutex; + static unsigned idx = 0; // Well-behaving thread only writes into indexs from 0..6. + if (!flag) + g_access_mutex.lock(); + + // idx valid range is [0, 6]. + if (idx > 6) + idx = 0; + + if (flag) + { + // Write into a forbidden area. + do_bad_thing_with_location(7, g_char_ptr, 99); + } + + unsigned index = idx++; + + if (!flag) + g_access_mutex.unlock(); + return g_char_ptr[index]; +} + +void +thread_func (uint32_t thread_index) +{ + printf ("%s (thread index = %u) startng...\n", __FUNCTION__, thread_index); + + barrier_wait(); + + uint32_t count = 0; + uint32_t val; + while (count++ < 15) + { + // random micro second sleep from zero to 3 seconds + int usec = g_distribution(g_random_engine); + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + std::this_thread::sleep_for(std::chrono::microseconds{usec}); + + if (count < 7) + val = access_pool (); + else + val = access_pool (true); + + printf ("%s (thread = %u) after usleep access_pool returns %d (count=%d)...\n", __FUNCTION__, thread_index, val, count); + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); +} + + +int main (int argc, char const *argv[]) +{ + g_count = 4; + std::thread threads[3]; + + g_char_ptr = new char[10]{}; + + // Create 3 threads + for (auto &thread : threads) + thread = std::thread{thread_func, std::distance(threads, &thread)}; + + struct { + int a; + int b; + int c; + } MyAggregateDataType; + + printf ("Before turning all three threads loose...\n"); // Set break point at this line. + barrier_wait(); + + // Join all of our threads + for (auto &thread : threads) + thread.join(); + + delete[] g_char_ptr; + + return 0; +} diff --git a/packages/Python/lldbsuite/test/help/TestHelp.py b/packages/Python/lldbsuite/test/help/TestHelp.py new file mode 100644 index 000000000000..f51b6aa59f12 --- /dev/null +++ b/packages/Python/lldbsuite/test/help/TestHelp.py @@ -0,0 +1,165 @@ +""" +Test some lldb help commands. + +See also CommandInterpreter::OutputFormattedHelpText(). +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class HelpCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_simplehelp(self): + """A simple test of 'help' command and its output.""" + self.expect("help", + startstr = 'Debugger commands:') + + self.expect("help -a", matching=False, + substrs = ['next']) + + self.expect("help", matching=True, + substrs = ['next']) + + @no_debug_info_test + def test_help_on_help(self): + """Testing the help on the help facility.""" + self.expect("help help", matching=True, + substrs = ['--hide-aliases', + '--hide-user-commands']) + + @no_debug_info_test + def version_number_string(self): + """Helper function to find the version number string of lldb.""" + plist = os.path.join(os.environ["LLDB_SRC"], "resources", "LLDB-Info.plist") + try: + CFBundleVersionSegFound = False + with open(plist, 'r') as f: + for line in f: + if CFBundleVersionSegFound: + version_line = line.strip() + import re + m = re.match("(.*)", version_line) + if m: + version = m.group(1) + return version + else: + # Unsuccessful, let's juts break out of the for loop. + break + + if line.find("CFBundleVersion") != -1: + # Found our match. The next line contains our version + # string, for example: + # + # 38 + CFBundleVersionSegFound = True + + except: + # Just fallthrough... + import traceback + traceback.print_exc() + pass + + # Use None to signify that we are not able to grok the version number. + return None + + @no_debug_info_test + def test_help_arch(self): + """Test 'help arch' which should list of supported architectures.""" + self.expect("help arch", + substrs = ['arm', 'x86_64', 'i386']) + + @no_debug_info_test + def test_help_version(self): + """Test 'help version' and 'version' commands.""" + self.expect("help version", + substrs = ['Show version of LLDB debugger.']) + version_str = self.version_number_string() + import re + match = re.match('[0-9]+', version_str) + if sys.platform.startswith("darwin"): + search_regexp = ['lldb-' + (version_str if match else '[0-9]+')] + else: + search_regexp = ['lldb version (\d|\.)+.*$'] + + self.expect("version", + patterns = search_regexp) + + @no_debug_info_test + def test_help_should_not_crash_lldb(self): + """Command 'help disasm' should not crash lldb.""" + self.runCmd("help disasm", check=False) + self.runCmd("help unsigned-integer") + + @no_debug_info_test + def test_help_should_not_hang_emacsshell(self): + """Command 'settings set term-width 0' should not hang the help command.""" + self.expect("settings set term-width 0", + COMMAND_FAILED_AS_EXPECTED, error=True, + substrs = ['error: 0 is out of range, valid values must be between']) + # self.runCmd("settings set term-width 0") + self.expect("help", + startstr = 'Debugger commands:') + + @no_debug_info_test + def test_help_breakpoint_set(self): + """Test that 'help breakpoint set' does not print out redundant lines of: + 'breakpoint set [-s ] ...'.""" + self.expect("help breakpoint set", matching=False, + substrs = ['breakpoint set [-s ]']) + + @no_debug_info_test + def test_help_image_dump_symtab_should_not_crash(self): + """Command 'help image dump symtab' should not crash lldb.""" + # 'image' is an alias for 'target modules'. + self.expect("help image dump symtab", + substrs = ['dump symtab', + 'sort-order']) + + @no_debug_info_test + def test_help_image_du_sym_is_ambiguous(self): + """Command 'help image du sym' is ambiguous and spits out the list of candidates.""" + self.expect("help image du sym", + COMMAND_FAILED_AS_EXPECTED, error=True, + substrs = ['error: ambiguous command image du sym', + 'symfile', + 'symtab']) + + @no_debug_info_test + def test_help_image_du_line_should_work(self): + """Command 'help image du line-table' is not ambiguous and should work.""" + # 'image' is an alias for 'target modules'. + self.expect("help image du line", + substrs = ['Dump the line table for one or more compilation units']) + + @no_debug_info_test + def test_help_target_variable_syntax(self): + """Command 'help target variable' should display ...""" + self.expect("help target variable", + substrs = [' [ [...]]']) + + @no_debug_info_test + def test_help_watchpoint_and_its_args(self): + """Command 'help watchpoint', 'help watchpt-id', and 'help watchpt-id-list' should work.""" + self.expect("help watchpoint", + substrs = ['delete', 'disable', 'enable', 'list']) + self.expect("help watchpt-id", + substrs = ['']) + self.expect("help watchpt-id-list", + substrs = ['']) + + @no_debug_info_test + def test_help_watchpoint_set(self): + """Test that 'help watchpoint set' prints out 'expression' and 'variable' + as the possible subcommands.""" + self.expect("help watchpoint set", + substrs = ['The following subcommands are supported:'], + patterns = ['expression +--', + 'variable +--']) diff --git a/packages/Python/lldbsuite/test/issue_verification/Makefile b/packages/Python/lldbsuite/test/issue_verification/Makefile new file mode 100644 index 000000000000..e7bd3f4dd792 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../make +CXX_SOURCES := inline_rerun_inferior.cpp +CXXFLAGS += -std=c++11 +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/issue_verification/README.txt b/packages/Python/lldbsuite/test/issue_verification/README.txt new file mode 100644 index 000000000000..0f1ae7f0ecfc --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/README.txt @@ -0,0 +1,5 @@ +Tests in this directory are intentionally setup to +fail, error, timeout, etc. to verify that the buildbots +pick up errors. The tests in this directory will be +parked/removed/renamed after verifying they trigger +as expected. diff --git a/packages/Python/lldbsuite/test/issue_verification/TestExpectedTimeout.py.park b/packages/Python/lldbsuite/test/issue_verification/TestExpectedTimeout.py.park new file mode 100644 index 000000000000..67db8149f850 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/TestExpectedTimeout.py.park @@ -0,0 +1,20 @@ +"""Tests that a timeout is detected by the testbot.""" +from __future__ import print_function + +import time + +import lldbsuite.test.lldbtest as lldbtest + + +class ExpectedTimeoutTestCase(lldbtest.TestBase): + """Forces test timeout.""" + mydir = lldbtest.TestBase.compute_mydir(__file__) + + @lldbtest.expectedFailureAll() + def test_buildbot_sees_expected_timeout(self): + """Tests that expected timeout logic kicks in and is picked up.""" + while True: + try: + time.sleep(1) + except: + print("ignoring exception during sleep") diff --git a/packages/Python/lldbsuite/test/issue_verification/TestFail.py.park b/packages/Python/lldbsuite/test/issue_verification/TestFail.py.park new file mode 100644 index 000000000000..da64bc0ea8c1 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/TestFail.py.park @@ -0,0 +1,16 @@ +"""Tests that a FAIL is detected by the testbot.""" + +from __future__ import print_function + +import lldbsuite.test.lldbtest as lldbtest + + +class FailTestCase(lldbtest.TestBase): + """Forces test failure.""" + mydir = lldbtest.TestBase.compute_mydir(__file__) + + def test_buildbot_catches_failure(self): + """Issues a failing test assertion.""" + self.assertTrue( + False, + "This will always fail, buildbot should flag this.") diff --git a/packages/Python/lldbsuite/test/issue_verification/TestRerunFail.py.park b/packages/Python/lldbsuite/test/issue_verification/TestRerunFail.py.park new file mode 100644 index 000000000000..bcd1926d7404 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/TestRerunFail.py.park @@ -0,0 +1,23 @@ +"""Tests that a flakey fail is rerun, and will pass on the rerun. +Run this test with --rerun-all-issues specified to test that +the tests fail on the first run, then pass on the second. +Do not mark them as flakey as, at this time, flakey tests will +run twice, thus causing the second run to succeed.""" + +from __future__ import print_function + +import rerun_base + +import lldbsuite.test.lldbtest as lldbtest + + +class RerunFailTestCase(rerun_base.RerunBaseTestCase): + """Forces test failure on first run, success on rerun.""" + @lldbtest.no_debug_info_test + def test_buildbot_catches_failure(self): + """Issues a failing test assertion.""" + if self.should_generate_issue(): + self.assertTrue( + False, + "This will fail on the first call, succeed on rerun, and " + "alternate thereafter.") diff --git a/packages/Python/lldbsuite/test/issue_verification/TestRerunInline.py.park b/packages/Python/lldbsuite/test/issue_verification/TestRerunInline.py.park new file mode 100644 index 000000000000..4c50495a2ec4 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/TestRerunInline.py.park @@ -0,0 +1,13 @@ +"""Tests that the rerun mechanism respects lldbinline-created tests. + +The current implementation of this test is expected to fail both on +the initial run and on the rerun, assuming --rerun-all-issues is provided +to the dotest.py run. + +This test could be improved by doing something in the test inferior +C++ program that could look for the "should an issue be raised" marker +file, and then really pass on the rerun. +""" +import lldbsuite.test.lldbinline as lldbinline + +lldbinline.MakeInlineTest(__file__, globals()) diff --git a/packages/Python/lldbsuite/test/issue_verification/TestRerunTimeout.py.park b/packages/Python/lldbsuite/test/issue_verification/TestRerunTimeout.py.park new file mode 100644 index 000000000000..1cf5373ac491 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/TestRerunTimeout.py.park @@ -0,0 +1,22 @@ +"""Tests that a timeout is detected by the testbot.""" +from __future__ import print_function + +import time + +import lldbsuite.test.lldbtest as lldbtest +import rerun_base + + +class RerunTimeoutTestCase(rerun_base.RerunBaseTestCase): + @lldbtest.no_debug_info_test + def test_timeout_rerun_succeeds(self): + """Tests that timeout logic kicks in and is picked up.""" + if not self.should_generate_issue(): + # We pass this time. + return + # We time out this time. + while True: + try: + time.sleep(1) + except: + print("ignoring exception during sleep") diff --git a/packages/Python/lldbsuite/test/issue_verification/TestSignal.py.park b/packages/Python/lldbsuite/test/issue_verification/TestSignal.py.park new file mode 100644 index 000000000000..d73ac74b47b2 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/TestSignal.py.park @@ -0,0 +1,26 @@ +"""Tests that an exceptional exit is detected by the testbot.""" + +from __future__ import print_function + +import os +import signal +import time + +import lldbsuite.test.lldbtest as lldbtest + + +class ExceptionalExitTestCase(lldbtest.TestBase): + """Forces exceptional exit.""" + mydir = lldbtest.TestBase.compute_mydir(__file__) + + @lldbtest.skipIfWindows + def test_buildbot_catches_exceptional_exit(self): + """Force process to die with exceptional exit.""" + + # Sleep for a couple seconds + try: + time.sleep(5) + except: + pass + + os.kill(os.getpid(), signal.SIGKILL) diff --git a/packages/Python/lldbsuite/test/issue_verification/TestSignalOutsideTestMethod.py.park b/packages/Python/lldbsuite/test/issue_verification/TestSignalOutsideTestMethod.py.park new file mode 100644 index 000000000000..7a5b2ba99f4b --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/TestSignalOutsideTestMethod.py.park @@ -0,0 +1,24 @@ +"""Tests that an exceptional exit is detected by the testbot.""" + +from __future__ import print_function + +import atexit +import os +import signal +import time + +import lldbsuite.test.lldbtest as lldbtest + + +class ExceptionalExitOutOfTestMethodTestCase(lldbtest.TestBase): + """Forces exceptional exit.""" + mydir = lldbtest.TestBase.compute_mydir(__file__) + + @lldbtest.skipIfWindows + def test_buildbot_catches_exceptional_exit(self): + pass + +def cleanup(): + os.kill(os.getpid(), signal.SIGKILL) + +atexit.register(cleanup) diff --git a/packages/Python/lldbsuite/test/issue_verification/TestTimeout.py.park b/packages/Python/lldbsuite/test/issue_verification/TestTimeout.py.park new file mode 100644 index 000000000000..ba7be454f9a9 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/TestTimeout.py.park @@ -0,0 +1,19 @@ +"""Tests that a timeout is detected by the testbot.""" +from __future__ import print_function + +import time + +import lldbsuite.test.lldbtest as lldbtest + + +class TimeoutTestCase(lldbtest.TestBase): + """Forces test timeout.""" + mydir = lldbtest.TestBase.compute_mydir(__file__) + + def test_buildbot_catches_timeout(self): + """Tests that timeout logic kicks in and is picked up.""" + while True: + try: + time.sleep(1) + except: + print("ignoring exception during sleep") diff --git a/packages/Python/lldbsuite/test/issue_verification/disable.py b/packages/Python/lldbsuite/test/issue_verification/disable.py new file mode 100755 index 000000000000..6d1f93e8b15d --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/disable.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +"""Renames *.py files to *.py.park.""" +import os +import sys + + +def main(): + """Drives the main script behavior.""" + script_dir = os.path.dirname(os.path.realpath(__file__)) + for filename in os.listdir(script_dir): + basename, extension = os.path.splitext(filename) + if basename.startswith("Test") and extension == '.py': + source_path = os.path.join(script_dir, filename) + dest_path = source_path + ".park" + sys.stdout.write("renaming {} to {}\n".format( + source_path, dest_path)) + os.rename(source_path, dest_path) + +if __name__ == "__main__": + main() diff --git a/packages/Python/lldbsuite/test/issue_verification/enable.py b/packages/Python/lldbsuite/test/issue_verification/enable.py new file mode 100755 index 000000000000..eb19276de1f7 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/enable.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +"""Renames *.py.park files to *.py.""" +import os +import sys + + +def main(): + """Drives the main script behavior.""" + script_dir = os.path.dirname(os.path.realpath(__file__)) + for filename in os.listdir(script_dir): + basename, extension = os.path.splitext(filename) + if basename.startswith("Test") and extension == '.park': + source_path = os.path.join(script_dir, filename) + dest_path = os.path.join(script_dir, basename) + sys.stdout.write("renaming {} to {}\n".format( + source_path, dest_path)) + os.rename(source_path, dest_path) + +if __name__ == "__main__": + main() diff --git a/packages/Python/lldbsuite/test/issue_verification/inline_rerun_inferior.cpp b/packages/Python/lldbsuite/test/issue_verification/inline_rerun_inferior.cpp new file mode 100644 index 000000000000..933911f7b28d --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/inline_rerun_inferior.cpp @@ -0,0 +1,14 @@ +//===-- main.cpp --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +typedef int Foo; + +int main() { + Foo array[3] = {1,2,3}; + return 0; //% self.expect("frame variable array --show-types --", substrs = ['(Foo [3]) wrong_type_here = {','(Foo) [0] = 1','(Foo) [1] = 2','(Foo) [2] = 3']) +} diff --git a/packages/Python/lldbsuite/test/issue_verification/rerun_base.py b/packages/Python/lldbsuite/test/issue_verification/rerun_base.py new file mode 100644 index 000000000000..2ce775dced13 --- /dev/null +++ b/packages/Python/lldbsuite/test/issue_verification/rerun_base.py @@ -0,0 +1,28 @@ +from __future__ import print_function + +import os + +import lldbsuite.test.lldbtest as lldbtest + + +# pylint: disable=too-few-public-methods +class RerunBaseTestCase(lldbtest.TestBase): + """Forces test failure.""" + mydir = lldbtest.TestBase.compute_mydir(__file__) + + def should_generate_issue(self): + """Returns whether a test issue should be generated. + + @returns True on the first and every other call via a given + test method. + """ + should_pass_filename = "{}.{}.succeed-marker".format( + __file__, self.id()) + fail = not os.path.exists(should_pass_filename) + if fail: + # Create the marker so that next call to this passes. + open(should_pass_filename, 'w').close() + else: + # Delete the marker so next time we fail. + os.remove(should_pass_filename) + return fail diff --git a/packages/Python/lldbsuite/test/lang/c/anonymous/Makefile b/packages/Python/lldbsuite/test/lang/c/anonymous/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/anonymous/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/anonymous/TestAnonymous.py b/packages/Python/lldbsuite/test/lang/c/anonymous/TestAnonymous.py new file mode 100644 index 000000000000..7cb66ba8238b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/anonymous/TestAnonymous.py @@ -0,0 +1,147 @@ +"""Test that anonymous structs/unions are transparent to member access""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class AnonymousTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfIcc # llvm.org/pr15036: LLDB generates an incorrect AST layout for an anonymous struct when DWARF is generated by ICC + def test_expr_nest(self): + self.build() + self.common_setup(self.line0) + + # These should display correctly. + self.expect("expression n->foo.d", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 4"]) + + self.expect("expression n->b", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 2"]) + + def test_expr_child(self): + self.build() + self.common_setup(self.line1) + + # These should display correctly. + self.expect("expression c->foo.d", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 4"]) + + self.expect("expression c->grandchild.b", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 2"]) + + @skipIfIcc # llvm.org/pr15036: This particular regression was introduced by r181498 + def test_expr_grandchild(self): + self.build() + self.common_setup(self.line2) + + # These should display correctly. + self.expect("expression g.child.foo.d", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 4"]) + + self.expect("expression g.child.b", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 2"]) + + def test_expr_parent(self): + self.build() + if "clang" in self.getCompiler() and "3.4" in self.getCompilerVersion(): + self.skipTest("llvm.org/pr16214 -- clang emits partial DWARF for structures referenced via typedef") + self.common_setup(self.line2) + + # These should display correctly. + self.expect("expression pz", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(type_z *) $", " = 0x0000"]) + + self.expect("expression z.y", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(type_y) $", "dummy = 2"]) + + @expectedFailureWindows('llvm.org/pr21550') + def test_expr_null(self): + self.build() + self.common_setup(self.line2) + + # This should fail because pz is 0, but it succeeds on OS/X. + # This fails on Linux with an upstream error "Couldn't dematerialize struct", as does "p *n" with "int *n = 0". + # Note that this can also trigger llvm.org/pr15036 when run interactively at the lldb command prompt. + self.expect("expression *(type_z *)pz", error = True) + + def test_child_by_name(self): + self.build() + + # Set debugger into synchronous mode + self.dbg.SetAsync(False) + + # Create a target + exe = os.path.join (os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + break_in_main = target.BreakpointCreateBySourceRegex ('// Set breakpoint 2 here.', lldb.SBFileSpec(self.source)) + self.assertTrue(break_in_main, VALID_BREAKPOINT) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue (process, PROCESS_IS_VALID) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_in_main) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint in main.") + + thread = threads[0] + frame = thread.frames[0] + + if not frame.IsValid(): + self.fail ("Failed to get frame 0.") + + var_n = frame.FindVariable("n") + if not var_n.IsValid(): + self.fail ("Failed to get the variable 'n'") + + elem_a = var_n.GetChildMemberWithName("a") + if not elem_a.IsValid(): + self.fail ("Failed to get the element a in n") + + error = lldb.SBError() + value = elem_a.GetValueAsSigned(error, 1000) + if not error.Success() or value != 0: + self.fail ("failed to get the correct value for element a in n") + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break in main.c. + self.source = 'main.c' + self.line0 = line_number(self.source, '// Set breakpoint 0 here.') + self.line1 = line_number(self.source, '// Set breakpoint 1 here.') + self.line2 = line_number(self.source, '// Set breakpoint 2 here.') + + def common_setup(self, line): + + # Set debugger into synchronous mode + self.dbg.SetAsync(False) + + # Create a target + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Set breakpoints inside and outside methods that take pointers to the containing struct. + lldbutil.run_break_set_by_file_and_line (self, self.source, line, num_expected_locations=1, loc_exact=True) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) diff --git a/packages/Python/lldbsuite/test/lang/c/anonymous/main.c b/packages/Python/lldbsuite/test/lang/c/anonymous/main.c new file mode 100644 index 000000000000..58ac85b7d431 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/anonymous/main.c @@ -0,0 +1,82 @@ +#include + +struct anonymous_nest { + struct { + struct { + int a; + int b; + }; // anonymous + struct { + int c; + int d; + } foo; + }; // anonymous +}; + +struct anonymous_child { + struct { + struct { + int a; + int b; + } grandchild; + struct { + int c; + int d; + } foo; + }; // anonymous +}; + +struct anonymous_grandchild { + struct { + struct { + int a; + int b; + }; // anonymous + struct { + int c; + int d; + } foo; + } child; +}; + +int processor_nest (struct anonymous_nest *n) +{ + return n->foo.d + n->b; // Set breakpoint 0 here. +} + +int processor_child (struct anonymous_child *c) +{ + return c->foo.d + c->grandchild.b; // Set breakpoint 1 here. +} + +int processor_grandchild (struct anonymous_grandchild *g) +{ + return g->child.foo.d + g->child.b; +} + + + +typedef struct { + int dummy; +} type_y; + +typedef struct { + type_y y; +} type_z; + + + +int main() +{ + struct anonymous_nest n = { 0, 2, 0, 4 }; + struct anonymous_child c = { 0, 2, 0, 4 }; + struct anonymous_grandchild g = { 0, 2, 0, 4 }; + type_z *pz = 0; + type_z z = {{2}}; + + printf("%d\n", processor_nest(&n)); + printf("%d\n", processor_child(&c)); + printf("%d\n", processor_grandchild(&g)); // Set breakpoint 2 here. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/array_types/Makefile b/packages/Python/lldbsuite/test/lang/c/array_types/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/array_types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/array_types/TestArrayTypes.py b/packages/Python/lldbsuite/test/lang/c/array_types/TestArrayTypes.py new file mode 100644 index 000000000000..e835fb096496 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/array_types/TestArrayTypes.py @@ -0,0 +1,198 @@ +"""Test breakpoint by file/line number; and list variables with array types.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ArrayTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set break point at this line.') + + def test_and_run_command(self): + """Test 'frame variable var_name' on some variables with array types.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=False) + + self.runCmd("run", RUN_SUCCEEDED) + + # The test suite sometimes shows that the process has exited without stopping. + # + # CC=clang ./dotest.py -v -t array_types + # ... + # Process 76604 exited with status = 0 (0x00000000) + self.runCmd("process status") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = ['resolved, hit count = 1']) + + # Issue 'variable list' command on several array-type variables. + + self.expect("frame variable --show-types strings", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(char *[4])', + substrs = ['(char *) [0]', + '(char *) [1]', + '(char *) [2]', + '(char *) [3]', + 'Hello', + 'Hola', + 'Bonjour', + 'Guten Tag']) + + self.expect("frame variable --show-types --raw -- char_16", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(char) [0]', + '(char) [15]']) + + self.expect("frame variable --show-types ushort_matrix", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(unsigned short [2][3])') + + self.expect("frame variable --show-types long_6", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(long [6])') + + @add_test_categories(['pyapi']) + def test_and_python_api(self): + """Use Python APIs to inspect variables with array types.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.c", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Sanity check the print representation of breakpoint. + bp = str(breakpoint) + self.expect(bp, msg="Breakpoint looks good", exe=False, + substrs = ["file = 'main.c'", + "line = %d" % self.line, + "locations = 1"]) + self.expect(bp, msg="Breakpoint is not resolved as yet", exe=False, matching=False, + substrs = ["resolved = 1"]) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Sanity check the print representation of process. + proc = str(process) + self.expect(proc, msg="Process looks good", exe=False, + substrs = ["state = stopped", + "executable = a.out"]) + + # The stop reason of the thread should be breakpoint. + thread = process.GetThreadAtIndex(0) + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + from lldbsuite.test.lldbutil import stop_reason_to_str + self.fail(STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS % + stop_reason_to_str(thread.GetStopReason())) + + # Sanity check the print representation of thread. + thr = str(thread) + # TODO(zturner): Whether the TID is printed in hex or decimal should be controlled by a setting, + # and this test should read the value of the setting. This check is currently hardcoded to + # match the check in Core/FormatEntity.cpp in the function FormatEntity::Format() for + # the Entry::Type::ThreadID case of the switch statement. + if self.getPlatform() == "linux" or self.getPlatform() == "freebsd": + tidstr = "tid = %u" % thread.GetThreadID() + else: + tidstr = "tid = 0x%4.4x" % thread.GetThreadID() + self.expect(thr, "Thread looks good with stop reason = breakpoint", exe=False, + substrs = [tidstr]) + + # The breakpoint should have a hit count of 1. + self.assertTrue(breakpoint.GetHitCount() == 1, BREAKPOINT_HIT_ONCE) + + # The breakpoint should be resolved by now. + bp = str(breakpoint) + self.expect(bp, "Breakpoint looks good and is resolved", exe=False, + substrs = ["file = 'main.c'", + "line = %d" % self.line, + "locations = 1"]) + + # Sanity check the print representation of frame. + frame = thread.GetFrameAtIndex(0) + frm = str(frame) + self.expect(frm, + "Frame looks good with correct index %d" % frame.GetFrameID(), + exe=False, + substrs = ["#%d" % frame.GetFrameID()]) + + # Lookup the "strings" string array variable and sanity check its print + # representation. + variable = frame.FindVariable("strings") + var = str(variable) + self.expect(var, "Variable for 'strings' looks good with correct name", exe=False, + substrs = ["%s" % variable.GetName()]) + self.DebugSBValue(variable) + self.assertTrue(variable.GetNumChildren() == 4, + "Variable 'strings' should have 4 children") + + child3 = variable.GetChildAtIndex(3) + self.DebugSBValue(child3) + self.assertTrue(child3.GetSummary() == '"Guten Tag"', + 'strings[3] == "Guten Tag"') + + # Lookup the "char_16" char array variable. + variable = frame.FindVariable("char_16") + self.DebugSBValue(variable) + self.assertTrue(variable.GetNumChildren() == 16, + "Variable 'char_16' should have 16 children") + + # Lookup the "ushort_matrix" ushort[] array variable. + # Notice the pattern of int(child0_2.GetValue(), 0). We pass a + # base of 0 so that the proper radix is determined based on the contents + # of the string. Same applies to long(). + variable = frame.FindVariable("ushort_matrix") + self.DebugSBValue(variable) + self.assertTrue(variable.GetNumChildren() == 2, + "Variable 'ushort_matrix' should have 2 children") + child0 = variable.GetChildAtIndex(0) + self.DebugSBValue(child0) + self.assertTrue(child0.GetNumChildren() == 3, + "Variable 'ushort_matrix[0]' should have 3 children") + child0_2 = child0.GetChildAtIndex(2) + self.DebugSBValue(child0_2) + self.assertTrue(int(child0_2.GetValue(), 0) == 3, + "ushort_matrix[0][2] == 3") + + # Lookup the "long_6" char array variable. + variable = frame.FindVariable("long_6") + self.DebugSBValue(variable) + self.assertTrue(variable.GetNumChildren() == 6, + "Variable 'long_6' should have 6 children") + child5 = variable.GetChildAtIndex(5) + self.DebugSBValue(child5) + self.assertTrue(int(child5.GetValue(), 0) == 6, + "long_6[5] == 6") + + # Last, check that "long_6" has a value type of eValueTypeVariableLocal + # and "argc" has eValueTypeVariableArgument. + from lldbsuite.test.lldbutil import value_type_to_str + self.assertTrue(variable.GetValueType() == lldb.eValueTypeVariableLocal, + "Variable 'long_6' should have '%s' value type." % + value_type_to_str(lldb.eValueTypeVariableLocal)) + argc = frame.FindVariable("argc") + self.DebugSBValue(argc) + self.assertTrue(argc.GetValueType() == lldb.eValueTypeVariableArgument, + "Variable 'argc' should have '%s' value type." % + value_type_to_str(lldb.eValueTypeVariableArgument)) diff --git a/packages/Python/lldbsuite/test/lang/c/array_types/cmds.txt b/packages/Python/lldbsuite/test/lang/c/array_types/cmds.txt new file mode 100644 index 000000000000..8feebe212044 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/array_types/cmds.txt @@ -0,0 +1,3 @@ +break main.c:42 +continue +var diff --git a/packages/Python/lldbsuite/test/lang/c/array_types/main.c b/packages/Python/lldbsuite/test/lang/c/array_types/main.c new file mode 100644 index 000000000000..5f0680a43b84 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/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 }; + unsigned 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] = { // Set break point at this line. + {{ 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/packages/Python/lldbsuite/test/lang/c/bitfields/Makefile b/packages/Python/lldbsuite/test/lang/c/bitfields/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/bitfields/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/bitfields/TestBitfields.py b/packages/Python/lldbsuite/test/lang/c/bitfields/TestBitfields.py new file mode 100644 index 000000000000..de7a333a18fa --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/bitfields/TestBitfields.py @@ -0,0 +1,162 @@ +"""Show bitfields and check that they display correctly.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BitfieldsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set break point at this line.') + + @skipIfWindows # BitFields exhibit crashes in record layout on Windows (http://llvm.org/pr21800) + def test_and_run_command(self): + """Test 'frame variable ...' on a variable with bitfields.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the main. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # This should display correctly. + self.expect("frame variable --show-types bits", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(uint32_t:1) b1 = 1', + '(uint32_t:2) b2 = 3', + '(uint32_t:3) b3 = 7', + '(uint32_t) b4 = 15', + '(uint32_t:5) b5 = 31', + '(uint32_t:6) b6 = 63', + '(uint32_t:7) b7 = 127', + '(uint32_t:4) four = 15']) + + # And so should this. + # rdar://problem/8348251 + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(uint32_t:1) b1 = 1', + '(uint32_t:2) b2 = 3', + '(uint32_t:3) b3 = 7', + '(uint32_t) b4 = 15', + '(uint32_t:5) b5 = 31', + '(uint32_t:6) b6 = 63', + '(uint32_t:7) b7 = 127', + '(uint32_t:4) four = 15']) + + self.expect("expr (bits.b1)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '1']) + self.expect("expr (bits.b2)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '3']) + self.expect("expr (bits.b3)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '7']) + self.expect("expr (bits.b4)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '15']) + self.expect("expr (bits.b5)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '31']) + self.expect("expr (bits.b6)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '63']) + self.expect("expr (bits.b7)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '127']) + self.expect("expr (bits.four)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '15']) + + self.expect("frame variable --show-types more_bits", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(uint32_t:3) a = 3', + '(uint8_t:1) b = \'\\0\'', + '(uint8_t:1) c = \'\\x01\'', + '(uint8_t:1) d = \'\\0\'']) + + self.expect("expr (more_bits.a)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint32_t', '3']) + self.expect("expr (more_bits.b)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint8_t', '\\0']) + self.expect("expr (more_bits.c)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint8_t', '\\x01']) + self.expect("expr (more_bits.d)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['uint8_t', '\\0']) + + @add_test_categories(['pyapi']) + @skipIfWindows # BitFields exhibit crashes in record layout on Windows (http://llvm.org/pr21800) + def test_and_python_api(self): + """Use Python APIs to inspect a bitfields variable.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.c", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread = target.GetProcess().GetThreadAtIndex(0) + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + from lldbsuite.test.lldbutil import stop_reason_to_str + self.fail(STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS % + stop_reason_to_str(thread.GetStopReason())) + + # The breakpoint should have a hit count of 1. + self.assertTrue(breakpoint.GetHitCount() == 1, BREAKPOINT_HIT_ONCE) + + # Lookup the "bits" variable which contains 8 bitfields. + frame = thread.GetFrameAtIndex(0) + bits = frame.FindVariable("bits") + self.DebugSBValue(bits) + self.assertTrue(bits.GetTypeName() == 'Bits', "bits.GetTypeName() == 'Bits'"); + self.assertTrue(bits.GetNumChildren() == 10, "bits.GetNumChildren() == 10"); + test_compiler = self.getCompiler() + self.assertTrue(bits.GetByteSize() == 32, "bits.GetByteSize() == 32"); + + # Notice the pattern of int(b1.GetValue(), 0). We pass a base of 0 + # so that the proper radix is determined based on the contents of the + # string. + b1 = bits.GetChildMemberWithName("b1") + self.DebugSBValue(b1) + self.assertTrue(b1.GetName() == "b1" and + b1.GetTypeName() == "uint32_t:1" and + b1.IsInScope() and + int(b1.GetValue(), 0) == 1, + 'bits.b1 has type uint32_t:1, is in scope, and == 1') + + b7 = bits.GetChildMemberWithName("b7") + self.DebugSBValue(b7) + self.assertTrue(b7.GetName() == "b7" and + b7.GetTypeName() == "uint32_t:7" and + b7.IsInScope() and + int(b7.GetValue(), 0) == 127, + 'bits.b7 has type uint32_t:7, is in scope, and == 127') + + four = bits.GetChildMemberWithName("four") + self.DebugSBValue(four) + self.assertTrue(four.GetName() == "four" and + four.GetTypeName() == "uint32_t:4" and + four.IsInScope() and + int(four.GetValue(), 0) == 15, + 'bits.four has type uint32_t:4, is in scope, and == 15') + + # Now kill the process, and we are done. + rc = target.GetProcess().Kill() + self.assertTrue(rc.Success()) diff --git a/packages/Python/lldbsuite/test/lang/c/bitfields/main.c b/packages/Python/lldbsuite/test/lang/c/bitfields/main.c new file mode 100644 index 000000000000..26c0176d759c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/bitfields/main.c @@ -0,0 +1,67 @@ +//===-- 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 +int main (int argc, char const *argv[]) +{ + struct Bits + { + uint32_t : 1, // Unnamed bitfield + b1 : 1, + b2 : 2, + : 2, // Unnamed bitfield + b3 : 3, + : 2, // Unnamed bitfield (this will get removed) + b4 __attribute__ ((aligned(16))), + b5 : 5, + b6 : 6, + b7 : 7, + four : 4; + }; + + printf("%lu", sizeof(struct Bits)); + + 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.four = i; //// break $source:$line + + struct MoreBits + { + uint32_t a : 3; + uint8_t : 1; + uint8_t b : 1; + uint8_t c : 1; + uint8_t d : 1; + }; + + struct MoreBits more_bits; + + more_bits.a = 3; + more_bits.b = 0; + more_bits.c = 1; + more_bits.d = 0; + + return 0; //// Set break point at this line. + +} diff --git a/packages/Python/lldbsuite/test/lang/c/blocks/Makefile b/packages/Python/lldbsuite/test/lang/c/blocks/Makefile new file mode 100644 index 000000000000..752b7aed3970 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/blocks/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +CFLAGS_EXTRAS += -fblocks + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/blocks/TestBlocks.py b/packages/Python/lldbsuite/test/lang/c/blocks/TestBlocks.py new file mode 100644 index 000000000000..8f1c3be22836 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/blocks/TestBlocks.py @@ -0,0 +1,61 @@ +"""Test that lldb can invoke blocks and access variables inside them""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BlocksTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + lines = [] + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break at. + self.lines.append(line_number('main.c', '// Set breakpoint 0 here.')) + self.lines.append(line_number('main.c', '// Set breakpoint 1 here.')) + + @unittest2.expectedFailure("rdar://problem/10413887 - Call blocks in expressions") + def test_expr(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.is_started = False + + # Break inside the foo function which takes a bar_ptr argument. + for line in self.lines: + lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True) + + self.wait_for_breakpoint() + + self.expect("expression a + b", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 7"]) + + self.expect("expression c", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 1"]) + + self.wait_for_breakpoint() + + # This should display correctly. + self.expect("expression (int)neg (-12)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 12"]) + + def wait_for_breakpoint(self): + if self.is_started == False: + self.is_started = True + self.runCmd("process launch", RUN_SUCCEEDED) + else: + self.runCmd("process continue", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) diff --git a/packages/Python/lldbsuite/test/lang/c/blocks/main.c b/packages/Python/lldbsuite/test/lang/c/blocks/main.c new file mode 100644 index 000000000000..415e6c6d033d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/blocks/main.c @@ -0,0 +1,21 @@ +#include + +int main() +{ + int c = 1; + + int (^add)(int, int) = ^int(int a, int b) + { + return a + b + c; // Set breakpoint 0 here. + }; + + int (^neg)(int) = ^int(int a) + { + return -a; + }; + + printf("%d\n", add(3, 4)); + printf("%d\n", neg(-5)); // Set breakpoint 1 here. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/const_variables/Makefile b/packages/Python/lldbsuite/test/lang/c/const_variables/Makefile new file mode 100644 index 000000000000..51adad1d0622 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/const_variables/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +C_SOURCES := main.c functions.c + +CFLAGS_EXTRAS += -O3 + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/const_variables/TestConstVariables.py b/packages/Python/lldbsuite/test/lang/c/const_variables/TestConstVariables.py new file mode 100644 index 000000000000..a112b2a1777c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/const_variables/TestConstVariables.py @@ -0,0 +1,64 @@ +"""Check that compiler-generated constant values work correctly""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ConstVariableTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureAll( + oslist=["freebsd", "linux"], + compiler="clang", compiler_version=["<", "3.5"]) + @expectedFailureAll( + oslist=["freebsd", "linux"], + compiler="clang", compiler_version=["=", "3.7"]) + @expectedFailureAll( + oslist=["freebsd", "linux"], + compiler="clang", compiler_version=["=", "3.8"]) + @expectedFailureAll(oslist=["freebsd", "linux"], compiler="icc") + @expectedFailureAll(archs=['mips', 'mipsel', 'mips64', 'mips64el']) + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + @expectedFailureWindows("llvm.org/pr24490: We shouldn't be using platform-specific names like `getpid` in tests") + def test_and_run_command(self): + """Test interpreted and JITted expressions on constant values.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the main. + lldbutil.run_break_set_by_symbol (self, "main", num_expected_locations=1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.runCmd("next") + self.runCmd("next") + + # Try frame variable. + self.expect("frame variable index", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int32_t) index = 512']) + + # Try an interpreted expression. + self.expect("expr (index + 512)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['1024']) + + # Try a JITted expression. + self.expect("expr (int)getpid(); (index - 256)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['256']) + + self.runCmd("kill") diff --git a/packages/Python/lldbsuite/test/lang/c/const_variables/functions.c b/packages/Python/lldbsuite/test/lang/c/const_variables/functions.c new file mode 100644 index 000000000000..c9ea63837c8b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/const_variables/functions.c @@ -0,0 +1,18 @@ +#include + +void foo() +{ + printf("foo()\n"); +} + +int bar() +{ + int ret = 3; + printf("bar()->%d\n", ret); + return ret; +} + +void baaz(int i) +{ + printf("baaz(%d)\n", i); +} diff --git a/packages/Python/lldbsuite/test/lang/c/const_variables/main.c b/packages/Python/lldbsuite/test/lang/c/const_variables/main.c new file mode 100644 index 000000000000..50a924e01d94 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/const_variables/main.c @@ -0,0 +1,23 @@ +#include +#include + +extern int foo(); +extern int bar(); +extern int baaz(int i); + +int main() +{ + int32_t index; + + foo(); + + index = 512; + + if (bar()) + { + printf("COMPILER PLEASE STOP HERE\n"); + index = 256; + } + + baaz(index); +} diff --git a/packages/Python/lldbsuite/test/lang/c/enum_types/Makefile b/packages/Python/lldbsuite/test/lang/c/enum_types/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/enum_types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py b/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py new file mode 100644 index 000000000000..b0c5c882f6a9 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py @@ -0,0 +1,71 @@ +"""Look up enum type information and check for correct display.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class EnumTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set break point at this line.') + + def test(self): + """Test 'image lookup -t days' and check for correct display and enum value printing.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the main. + bkpt_id = lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Look up information about the 'days' enum type. + # Check for correct display. + self.expect("image lookup -t days", DATA_TYPES_DISPLAYED_CORRECTLY, + substrs = ['enum days {', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + 'kNumDays', + '}']) + + enum_values = [ '-4', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + 'kNumDays', + '5']; + + bkpt = self.target().FindBreakpointByID(bkpt_id) + for enum_value in enum_values: + self.expect("frame variable day", 'check for valid enumeration value', + substrs = [enum_value]) + lldbutil.continue_to_breakpoint (self.process(), bkpt) diff --git a/packages/Python/lldbsuite/test/lang/c/enum_types/main.c b/packages/Python/lldbsuite/test/lang/c/enum_types/main.c new file mode 100644 index 000000000000..3d59654eff69 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/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 = -3, + 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); // Set break point at this line. + } + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/forward/Makefile b/packages/Python/lldbsuite/test/lang/c/forward/Makefile new file mode 100644 index 000000000000..1db43ab479b2 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/forward/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c foo.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/forward/README.txt b/packages/Python/lldbsuite/test/lang/c/forward/README.txt new file mode 100644 index 000000000000..b7b66f75162b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/forward/README.txt @@ -0,0 +1,5 @@ +This example has a function call in foo.c named "foo" that takes a forward +declaration to "struct bar" and uses it as a pointer argument. In main.c +we have a real declaration for "struct bar". We want to be able to find the +real definition of "struct bar" when we are stopped in foo in foo.c such that +when we stop in "foo" we see the contents of the "bar_ptr". diff --git a/packages/Python/lldbsuite/test/lang/c/forward/TestForwardDeclaration.py b/packages/Python/lldbsuite/test/lang/c/forward/TestForwardDeclaration.py new file mode 100644 index 000000000000..33c0de2d72d3 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/forward/TestForwardDeclaration.py @@ -0,0 +1,47 @@ +"""Test that forward declaration of a data structure gets resolved correctly.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ForwardDeclarationTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_and_run_command(self): + """Display *bar_ptr when stopped on a function with forward declaration of struct bar.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_symbol (self, "foo", num_expected_locations=1, sym_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # This should display correctly. + # Note that the member fields of a = 1 and b = 2 is by design. + self.expect("frame variable --show-types *bar_ptr", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(bar) *bar_ptr = ', + '(int) a = 1', + '(int) b = 2']) + + # And so should this. + self.expect("expression --show-types -- *bar_ptr", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(bar)', + '(int) a = 1', + '(int) b = 2']) diff --git a/packages/Python/lldbsuite/test/lang/c/forward/foo.c b/packages/Python/lldbsuite/test/lang/c/forward/foo.c new file mode 100644 index 000000000000..2e050e77778d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/forward/foo.c @@ -0,0 +1,8 @@ +#include +#include "foo.h" + +int +foo (struct bar *bar_ptr) +{ + return printf ("bar_ptr = %p\n", bar_ptr); +} diff --git a/packages/Python/lldbsuite/test/lang/c/forward/foo.h b/packages/Python/lldbsuite/test/lang/c/forward/foo.h new file mode 100644 index 000000000000..3040927cb47a --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/forward/foo.h @@ -0,0 +1,4 @@ + +struct bar; + +int foo (struct bar *bar_ptr); diff --git a/packages/Python/lldbsuite/test/lang/c/forward/main.c b/packages/Python/lldbsuite/test/lang/c/forward/main.c new file mode 100644 index 000000000000..6f9787c30a36 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/forward/main.c @@ -0,0 +1,18 @@ +#include +#include "foo.h" + +struct bar +{ + int a; + int b; +}; + +int +main (int argc, char const *argv[]) +{ + struct bar b= { 1, 2 }; + + foo (&b); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/function_types/Makefile b/packages/Python/lldbsuite/test/lang/c/function_types/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/function_types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/function_types/TestFunctionTypes.py b/packages/Python/lldbsuite/test/lang/c/function_types/TestFunctionTypes.py new file mode 100644 index 000000000000..2f9f1d10b58c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/function_types/TestFunctionTypes.py @@ -0,0 +1,76 @@ +"""Test variable with function ptr type and that break on the function works.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class FunctionTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set break point at this line.') + + def test(self): + """Test 'callback' has function ptr type, then break on the function.""" + self.build() + self.runToBreakpoint() + + # Check that the 'callback' variable display properly. + self.expect("frame variable --show-types callback", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(int (*)(const char *)) callback =') + + # And that we can break on the callback function. + lldbutil.run_break_set_by_symbol (self, "string_not_empty", num_expected_locations=1, sym_exact=True) + self.runCmd("continue") + + # Check that we do indeed stop on the string_not_empty function. + self.expect("process status", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['a.out`string_not_empty', + 'stop reason = breakpoint']) + + @expectedFailureWindows("llvm.org/pr21765") + def test_pointers(self): + """Test that a function pointer to 'printf' works and can be called.""" + self.build() + self.runToBreakpoint() + + self.expect("expr string_not_empty", + substrs = ['(int (*)(const char *)) $0 = ', '(a.out`']) + + if self.platformIsDarwin(): + regexps = ['lib.*\.dylib`printf'] + else: + regexps = ['printf'] + self.expect("expr (int (*)(const char*, ...))printf", + substrs = ['(int (*)(const char *, ...)) $1 = '], + patterns = regexps) + + self.expect("expr $1(\"Hello world\\n\")", + startstr = '(int) $2 = 12') + + def runToBreakpoint(self): + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the main. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) diff --git a/packages/Python/lldbsuite/test/lang/c/function_types/main.c b/packages/Python/lldbsuite/test/lang/c/function_types/main.c new file mode 100644 index 000000000000..8be3c4d46b3c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/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); // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/lang/c/global_variables/Makefile b/packages/Python/lldbsuite/test/lang/c/global_variables/Makefile new file mode 100644 index 000000000000..b1b77dacc01e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/global_variables/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +DYLIB_NAME := a +DYLIB_C_SOURCES := a.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/global_variables/TestGlobalVariables.py b/packages/Python/lldbsuite/test/lang/c/global_variables/TestGlobalVariables.py new file mode 100644 index 000000000000..1f91cd1660c3 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/global_variables/TestGlobalVariables.py @@ -0,0 +1,76 @@ +"""Show global variables and check that they do indeed have global scopes.""" + +from __future__ import print_function + + +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class GlobalVariablesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.source = 'main.c' + self.line = line_number(self.source, '// Set break point at this line.') + self.shlib_names = ["a"] + + @expectedFailureWindows("llvm.org/pr24764") + @expectedFailureAll("llvm.org/pr25872", oslist=["macosx"], debug_info="dwarf") + def test_c_global_variables(self): + """Test 'frame variable --scope --no-args' which omits args and shows scopes.""" + self.build() + + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + # Break inside the main. + lldbutil.run_break_set_by_file_and_line (self, self.source, self.line, num_expected_locations=1, loc_exact=True) + + # Register our shared libraries for remote targets so they get automatically uploaded + environment = self.registerSharedLibrariesWithTarget(target, self.shlib_names) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, environment, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Check that GLOBAL scopes are indicated for the variables. + self.expect("frame variable --show-types --scope --show-globals --no-args", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['GLOBAL: (int) g_file_global_int = 42', + 'STATIC: (const int) g_file_static_int = 2', + 'GLOBAL: (const char *) g_file_global_cstr', + '"g_file_global_cstr"', + 'STATIC: (const char *) g_file_static_cstr', + '"g_file_static_cstr"', + 'GLOBAL: (int) g_common_1 = 21']) + + # 'frame variable' should support address-of operator. + self.runCmd("frame variable &g_file_global_int") + + # Exercise the 'target variable' command to display globals in a.c file. + self.expect("target variable g_a", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['g_a', '123']) + self.expect("target variable g_marked_spot.x", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['g_marked_spot.x', '20']) + + # rdar://problem/9747668 + # runCmd: target variable g_marked_spot.y + # output: (int) g_marked_spot.y = + self.expect("target variable g_marked_spot.y", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['g_marked_spot.y', '21']) + self.expect("target variable g_marked_spot.y", VARIABLES_DISPLAYED_CORRECTLY, matching=False, + substrs = ["can't be resolved"]) diff --git a/packages/Python/lldbsuite/test/lang/c/global_variables/a.c b/packages/Python/lldbsuite/test/lang/c/global_variables/a.c new file mode 100644 index 000000000000..d169d5d63240 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/global_variables/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. +// +//===----------------------------------------------------------------------===// +int g_a = 123; +struct Point { + int x; + int y; +}; +struct Point g_marked_spot = { 20, 21 }; + diff --git a/packages/Python/lldbsuite/test/lang/c/global_variables/cmds.txt b/packages/Python/lldbsuite/test/lang/c/global_variables/cmds.txt new file mode 100644 index 000000000000..6906a0729aeb --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/global_variables/cmds.txt @@ -0,0 +1,3 @@ +break main.c:5 +continue +var -global g_a -global g_global_int diff --git a/packages/Python/lldbsuite/test/lang/c/global_variables/main.c b/packages/Python/lldbsuite/test/lang/c/global_variables/main.c new file mode 100644 index 000000000000..499b2504774f --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/global_variables/main.c @@ -0,0 +1,24 @@ +//===-- 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_common_1; // Not initialized on purpose to cause it to be undefined external in .o file +int g_file_global_int = 42; +static const int g_file_static_int = 2; +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[]) +{ + g_common_1 = g_file_global_int / g_file_static_int; + static const char *g_func_static_cstr = "g_func_static_cstr"; + printf ("%s %s\n", g_file_global_cstr, g_file_static_cstr); + return g_file_global_int + g_a + g_common_1; // Set break point at this line. //// break $source:$line; continue; var -global g_a -global g_global_int +} diff --git a/packages/Python/lldbsuite/test/lang/c/inlines/Makefile b/packages/Python/lldbsuite/test/lang/c/inlines/Makefile new file mode 100644 index 000000000000..c5b0d18f995d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/inlines/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := inlines.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/inlines/inlines.c b/packages/Python/lldbsuite/test/lang/c/inlines/inlines.c new file mode 100644 index 000000000000..1e920f1ef2f6 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/inlines/inlines.c @@ -0,0 +1,53 @@ +#include +#include "inlines.h" + +#define INLINE_ME __inline__ __attribute__((always_inline)) + +int +not_inlined_2 (int input) +{ + printf ("Called in not_inlined_2 with : %d.\n", input); + return input; +} + +int +not_inlined_1 (int input) +{ + printf ("Called in not_inlined_1 with %d.\n", input); + return not_inlined_2(input); +} + +INLINE_ME int +inner_inline (int inner_input, int mod_value) +{ + int inner_result; + inner_result = inner_input % mod_value; + printf ("Returning: %d.\n", inner_result); + return not_inlined_1 (inner_result); +} + +INLINE_ME int +outer_inline (int outer_input) +{ + int outer_result; + + outer_result = inner_inline (outer_input, outer_input % 3); + return outer_result; +} + +int +main (int argc, char **argv) +{ + printf ("Starting...\n"); + + int (*func_ptr) (int); + func_ptr = outer_inline; + + outer_inline (argc); + + func_ptr (argc); + + return 0; +} + + diff --git a/packages/Python/lldbsuite/test/lang/c/inlines/inlines.h b/packages/Python/lldbsuite/test/lang/c/inlines/inlines.h new file mode 100644 index 000000000000..265d7b4966ed --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/inlines/inlines.h @@ -0,0 +1,4 @@ +int inner_inline (int inner_input, int mod_value); +int outer_inline (int outer_input); +int not_inlined_2 (int input); +int not_inlined_1 (int input); diff --git a/packages/Python/lldbsuite/test/lang/c/modules/Makefile b/packages/Python/lldbsuite/test/lang/c/modules/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/modules/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/modules/TestCModules.py b/packages/Python/lldbsuite/test/lang/c/modules/TestCModules.py new file mode 100644 index 000000000000..cd31f9dbacfe --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/modules/TestCModules.py @@ -0,0 +1,65 @@ +"""Test that importing modules in C works as expected.""" + +from __future__ import print_function + + + +import os, time +import lldb +import platform +import lldbsuite.test.lldbutil as lldbutil + +from distutils.version import StrictVersion + +from lldbsuite.test.lldbtest import * + +class CModulesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfFreeBSD + @expectedFailureDarwin('http://llvm.org/pr24302') + @expectedFailureLinux('http://llvm.org/pr23456') # 'fopen' has unknown return type + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test_expr(self): + if platform.system() == "Darwin" and platform.release() < StrictVersion('12.0.0'): + self.skipTest() + + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.expect("expr @import Darwin; 3", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["int", "3"]) + + self.expect("expr *fopen(\"/dev/zero\", \"w\")", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["FILE", "_close", "__sclose"]) + + self.expect("expr *myFile", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["a", "5", "b", "9"]) + + self.expect("expr MIN((uint64_t)2, (uint64_t)3)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["uint64_t", "2"]) + + self.expect("expr stdin", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(FILE *)", "0x"]) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set breakpoint 0 here.') diff --git a/packages/Python/lldbsuite/test/lang/c/modules/main.c b/packages/Python/lldbsuite/test/lang/c/modules/main.c new file mode 100644 index 000000000000..2b244bc38d02 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/modules/main.c @@ -0,0 +1,20 @@ +#include + +int printf(const char * __restrict format, ...); + +typedef struct { + int a; + int b; +} FILE; + +int main() +{ + FILE *myFile = malloc(sizeof(FILE)); + + myFile->a = 5; + myFile->b = 9; + + printf("%d\n", myFile->a + myFile->b); // Set breakpoint 0 here. + + free(myFile); +} diff --git a/packages/Python/lldbsuite/test/lang/c/recurse/Makefile b/packages/Python/lldbsuite/test/lang/c/recurse/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/recurse/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/recurse/main.c b/packages/Python/lldbsuite/test/lang/c/recurse/main.c new file mode 100644 index 000000000000..1159669ebf15 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/recurse/main.c @@ -0,0 +1,28 @@ +#include +#include + +uint32_t +recurse_crash (uint32_t depth) +{ + if (depth > 0) + return recurse_crash (depth - 1); + return 0; +} + +int +main (int argc, char const *argv[]) +{ + // If we have more than one argument, then it should a depth to recurse to. + // If we have just the program name as an argument, use UINT32_MAX so we + // eventually crash the program by overflowing the stack + uint32_t depth = UINT32_MAX; + if (argc > 1) + { + char *end = NULL; + depth = strtoul (argv[1], &end, 0); + if (end == NULL || *end != '\0') + depth = UINT32_MAX; + } + recurse_crash (depth); + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/c/register_variables/Makefile b/packages/Python/lldbsuite/test/lang/c/register_variables/Makefile new file mode 100644 index 000000000000..12e55616b540 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/register_variables/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +C_SOURCES := test.c + +CFLAGS ?= -g -O1 + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py b/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py new file mode 100644 index 000000000000..7ef1f246bfd3 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py @@ -0,0 +1,70 @@ +"""Check that compiler-generated register values work correctly""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class RegisterVariableTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureAll(oslist=['macosx'], compiler='clang', compiler_version=['<', '7.0.0'], debug_info="dsym") + @expectedFailureClang(None, ['<', '3.5']) + @expectedFailureGcc(None, ['is', '4.8.2']) + def test_and_run_command(self): + """Test expressions on register values.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the main. + lldbutil.run_break_set_by_source_regexp(self, "break", num_expected_locations=2) + + #################### + # First breakpoint + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Try some variables that should be visible + self.expect("expr a", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int) $0 = 2']) + + self.expect("expr b->m1", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int) $1 = 3']) + + ##################### + # Second breakpoint + + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Try some variables that should be visible + self.expect("expr b->m2", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int) $2 = 5']) + + self.expect("expr c", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(int) $3 = 5']) + + self.runCmd("kill") diff --git a/packages/Python/lldbsuite/test/lang/c/register_variables/test.c b/packages/Python/lldbsuite/test/lang/c/register_variables/test.c new file mode 100644 index 000000000000..e467ac48f745 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/register_variables/test.c @@ -0,0 +1,27 @@ +#include + +struct bar { + int m1; + int m2; +}; + +void f1(int a, struct bar *b) __attribute__ ((noinline)); +void f1(int a, struct bar *b) +{ + b->m2 = b->m1 + a; // set breakpoint here +} + +void f2(struct bar *b) __attribute__ ((noinline)); +void f2(struct bar *b) +{ + int c = b->m2; + printf("%d\n", c); // set breakpoint here +} + +int main() +{ + struct bar myBar = { 3, 4 }; + f1(2, &myBar); + f2(&myBar); + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/set_values/Makefile b/packages/Python/lldbsuite/test/lang/c/set_values/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/set_values/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/set_values/TestSetValues.py b/packages/Python/lldbsuite/test/lang/c/set_values/TestSetValues.py new file mode 100644 index 000000000000..ab8102496091 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/set_values/TestSetValues.py @@ -0,0 +1,111 @@ +"""Test settings and readings of program variables.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SetValuesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.line1 = line_number('main.c', '// Set break point #1.') + self.line2 = line_number('main.c', '// Set break point #2.') + self.line3 = line_number('main.c', '// Set break point #3.') + self.line4 = line_number('main.c', '// Set break point #4.') + self.line5 = line_number('main.c', '// Set break point #5.') + + @expectedFailureWindows("llvm.org/pr21765") + def test(self): + """Test settings and readings of program variables.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Set breakpoints on several places to set program variables. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line1, num_expected_locations=1, loc_exact=True) + + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line2, num_expected_locations=1, loc_exact=True) + + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line3, num_expected_locations=1, loc_exact=True) + + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line4, num_expected_locations=1, loc_exact=True) + + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line5, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # main.c:15 + # Check that 'frame variable --show-types' displays the correct data type and value. + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(char) i = 'a'") + + # Now set variable 'i' and check that it is correctly displayed. + self.runCmd("expression i = 'b'") + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(char) i = 'b'") + + self.runCmd("continue") + + # main.c:36 + # Check that 'frame variable --show-types' displays the correct data type and value. + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\((short unsigned int|unsigned short)\) i = 33"]) + + # Now set variable 'i' and check that it is correctly displayed. + self.runCmd("expression i = 333") + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\((short unsigned int|unsigned short)\) i = 333"]) + + self.runCmd("continue") + + # main.c:57 + # Check that 'frame variable --show-types' displays the correct data type and value. + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(long) i = 33") + + # Now set variable 'i' and check that it is correctly displayed. + self.runCmd("expression i = 33333") + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(long) i = 33333") + + self.runCmd("continue") + + # main.c:78 + # Check that 'frame variable --show-types' displays the correct data type and value. + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(double) i = 2.25") + + # Now set variable 'i' and check that it is correctly displayed. + self.runCmd("expression i = 1.5") + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(double) i = 1.5") + + self.runCmd("continue") + + # main.c:85 + # Check that 'frame variable --show-types' displays the correct data type and value. + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(long double) i = 2.25") + + # Now set variable 'i' and check that it is correctly displayed. + self.runCmd("expression i = 1.5") + self.expect("frame variable --show-types", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(long double) i = 1.5") diff --git a/packages/Python/lldbsuite/test/lang/c/set_values/main.c b/packages/Python/lldbsuite/test/lang/c/set_values/main.c new file mode 100644 index 000000000000..64f01a97a286 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/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); // Set break point #1. //// 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); // Set break point #2. //// 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); // Set break point #3. //// 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 = 2.25; + printf("before (float) i = %g\n", i); + printf("after (float) i = %g\n", i); //// break $source:$line +} + +void set_double(void) +{ + double i = 2.25; + printf("before (double) i = %g\n", i); + printf("after (double) i = %g\n", i); // Set break point #4. //// break $source:$line +} + +void set_long_double(void) +{ + long double i = 2.25; + printf("before (long double) i = %Lg\n", i); + printf("after (long double) i = %Lg\n", i); // Set break point #5. //// 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/packages/Python/lldbsuite/test/lang/c/shared_lib/Makefile b/packages/Python/lldbsuite/test/lang/c/shared_lib/Makefile new file mode 100644 index 000000000000..854002e64708 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +DYLIB_NAME := foo +DYLIB_C_SOURCES := foo.c +C_SOURCES := main.c +CFLAGS_EXTRAS += -fPIC + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib/TestSharedLib.py b/packages/Python/lldbsuite/test/lang/c/shared_lib/TestSharedLib.py new file mode 100644 index 000000000000..954d1bac6362 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib/TestSharedLib.py @@ -0,0 +1,71 @@ +"""Test that types defined in shared libraries work correctly.""" + +from __future__ import print_function + + + +import unittest2 +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SharedLibTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_expr(self): + """Test that types work when defined in a shared library and forward-declared in the main executable""" + if "clang" in self.getCompiler() and "3.4" in self.getCompilerVersion(): + self.skipTest("llvm.org/pr16214 -- clang emits partial DWARF for structures referenced via typedef") + + self.build() + self.common_setup() + + # This should display correctly. + self.expect("expression --show-types -- *my_foo_ptr", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(foo)", "(sub_foo)", "other_element = 3"]) + + @unittest2.expectedFailure("rdar://problem/10704639") + def test_frame_variable(self): + """Test that types work when defined in a shared library and forward-declared in the main executable""" + self.build() + self.common_setup() + + # This should display correctly. + self.expect("frame variable --show-types -- *my_foo_ptr", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(foo)", "(sub_foo)", "other_element = 3"]) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.source = 'main.c' + self.line = line_number(self.source, '// Set breakpoint 0 here.') + self.shlib_names = ["foo"] + + def common_setup(self): + # Run in synchronous mode + self.dbg.SetAsync(False) + + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, self.source, self.line, num_expected_locations=1, loc_exact=True) + + # Register our shared libraries for remote targets so they get automatically uploaded + environment = self.registerSharedLibrariesWithTarget(target, self.shlib_names) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, environment, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib/foo.c b/packages/Python/lldbsuite/test/lang/c/shared_lib/foo.c new file mode 100644 index 000000000000..6431bc496c30 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib/foo.c @@ -0,0 +1,22 @@ +#include "foo.h" +#include + +struct foo +{ + struct sub_foo sub_element; + int other_element; +}; + +struct foo * +GetMeAFoo() +{ + struct foo *ret_val = (struct foo *) malloc (sizeof (struct foo)); + ret_val->other_element = 3; + return ret_val; +} + +struct sub_foo * +GetMeASubFoo (struct foo *in_foo) +{ + return &(in_foo->sub_element); +} diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib/foo.h b/packages/Python/lldbsuite/test/lang/c/shared_lib/foo.h new file mode 100644 index 000000000000..78b9e3f9c0d9 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib/foo.h @@ -0,0 +1,10 @@ +struct foo; + +struct sub_foo +{ + int sub_1; + char *sub_2; +}; + +LLDB_TEST_API struct foo *GetMeAFoo(); +LLDB_TEST_API struct sub_foo *GetMeASubFoo(struct foo *in_foo); diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib/main.c b/packages/Python/lldbsuite/test/lang/c/shared_lib/main.c new file mode 100644 index 000000000000..b4377de18c16 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib/main.c @@ -0,0 +1,13 @@ +#include +#include "foo.h" + +int +main () +{ + struct foo *my_foo_ptr; + my_foo_ptr = GetMeAFoo(); + + printf ("My sub foo has: %d.\n", GetMeASubFoo(my_foo_ptr)->sub_1); // Set breakpoint 0 here. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/Makefile b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/Makefile new file mode 100644 index 000000000000..51347d73f09c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/Makefile @@ -0,0 +1,10 @@ +LEVEL = ../../../make + +DYLIB_NAME := foo +DYLIB_C_SOURCES := foo.c +C_SOURCES := main.c +CFLAGS_EXTRAS += -fPIC + +SPLIT_DEBUG_SYMBOLS = YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py new file mode 100644 index 000000000000..a9cb46ca137b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py @@ -0,0 +1,73 @@ +"""Test that types defined in shared libraries with stripped symbols work correctly.""" + +from __future__ import print_function + + + +import unittest2 +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SharedLibStrippedTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows # Test crashes + def test_expr(self): + """Test that types work when defined in a shared library and forward-declared in the main executable""" + if "clang" in self.getCompiler() and "3.4" in self.getCompilerVersion(): + self.skipTest("llvm.org/pr16214 -- clang emits partial DWARF for structures referenced via typedef") + + self.build() + self.common_setup() + + # This should display correctly. + self.expect("expression --show-types -- *my_foo_ptr", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(foo)", "(sub_foo)", "other_element = 3"]) + + @expectedFailureWindows # Test crashes + @unittest2.expectedFailure("rdar://problem/10381325") + def test_frame_variable(self): + """Test that types work when defined in a shared library and forward-declared in the main executable""" + self.build() + self.common_setup() + + # This should display correctly. + self.expect("frame variable --show-types -- *my_foo_ptr", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(foo)", "(sub_foo)", "other_element = 3"]) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.source = 'main.c' + self.line = line_number(self.source, '// Set breakpoint 0 here.') + self.shlib_names = ["foo"] + + def common_setup(self): + # Run in synchronous mode + self.dbg.SetAsync(False) + + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, self.source, self.line, num_expected_locations=1, loc_exact=True) + + # Register our shared libraries for remote targets so they get automatically uploaded + environment = self.registerSharedLibrariesWithTarget(target, self.shlib_names) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, environment, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/foo.c b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/foo.c new file mode 100644 index 000000000000..6431bc496c30 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/foo.c @@ -0,0 +1,22 @@ +#include "foo.h" +#include + +struct foo +{ + struct sub_foo sub_element; + int other_element; +}; + +struct foo * +GetMeAFoo() +{ + struct foo *ret_val = (struct foo *) malloc (sizeof (struct foo)); + ret_val->other_element = 3; + return ret_val; +} + +struct sub_foo * +GetMeASubFoo (struct foo *in_foo) +{ + return &(in_foo->sub_element); +} diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/foo.h b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/foo.h new file mode 100644 index 000000000000..78b3c1245387 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/foo.h @@ -0,0 +1,12 @@ +struct foo; + +struct sub_foo +{ + int sub_1; + char *sub_2; +}; + +struct foo *GetMeAFoo(); +struct sub_foo *GetMeASubFoo (struct foo *in_foo); + + diff --git a/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/main.c b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/main.c new file mode 100644 index 000000000000..b4377de18c16 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/shared_lib_stripped_symbols/main.c @@ -0,0 +1,13 @@ +#include +#include "foo.h" + +int +main () +{ + struct foo *my_foo_ptr; + my_foo_ptr = GetMeAFoo(); + + printf ("My sub foo has: %d.\n", GetMeASubFoo(my_foo_ptr)->sub_1); // Set breakpoint 0 here. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/stepping/Makefile b/packages/Python/lldbsuite/test/lang/c/stepping/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/stepping/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/stepping/TestStepAndBreakpoints.py b/packages/Python/lldbsuite/test/lang/c/stepping/TestStepAndBreakpoints.py new file mode 100644 index 000000000000..c7a3de4b6846 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/stepping/TestStepAndBreakpoints.py @@ -0,0 +1,242 @@ +"""Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestCStepping(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def getCategories(self): + return ['basic_process'] + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers that we will step to in main: + self.main_source = "main.c" + + @expectedFailureFreeBSD('llvm.org/pr17932') + @expectedFailureLinux # llvm.org/pr14437 + @expectedFailureWindows("llvm.org/pr24777") + @add_test_categories(['pyapi']) + def test_and_python_api(self): + """Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + breakpoints_to_disable = [] + + break_1_in_main = target.BreakpointCreateBySourceRegex ('// frame select 2, thread step-out while stopped at .c.1..', self.main_source_spec) + self.assertTrue(break_1_in_main, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_1_in_main) + + break_in_a = target.BreakpointCreateBySourceRegex ('// break here to stop in a before calling b', self.main_source_spec) + self.assertTrue(break_in_a, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_in_a) + + break_in_b = target.BreakpointCreateBySourceRegex ('// thread step-out while stopped at .c.2..', self.main_source_spec) + self.assertTrue(break_in_b, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_in_b) + + break_in_c = target.BreakpointCreateBySourceRegex ('// Find the line number of function .c. here.', self.main_source_spec) + self.assertTrue(break_in_c, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_in_c) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_1_in_main) + + if len(threads) != 1: + self.fail ("Failed to stop at first breakpoint in main.") + + thread = threads[0] + + # Get the stop id and for fun make sure it increases: + old_stop_id = process.GetStopID() + + # Now step over, which should cause us to hit the breakpoint in "a" + thread.StepOver() + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_in_a) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint in a.") + + # Check that the stop ID increases: + new_stop_id = process.GetStopID() + self.assertTrue(new_stop_id > old_stop_id, "Stop ID increases monotonically.") + + thread = threads[0] + + # Step over, and we should hit the breakpoint in b: + thread.StepOver() + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_in_b) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint in b.") + thread = threads[0] + + # Now try running some function, and make sure that we still end up in the same place + # and with the same stop reason. + frame = thread.GetFrameAtIndex(0) + current_line = frame.GetLineEntry().GetLine() + current_file = frame.GetLineEntry().GetFileSpec() + current_bp = [] + current_bp.append(thread.GetStopReasonDataAtIndex(0)) + current_bp.append(thread.GetStopReasonDataAtIndex(1)) + + stop_id_before_expression = process.GetStopID() + stop_id_before_including_expressions = process.GetStopID(True) + + frame.EvaluateExpression ("(int) printf (print_string)") + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (current_line == frame.GetLineEntry().GetLine(), "The line stayed the same after expression.") + self.assertTrue (current_file == frame.GetLineEntry().GetFileSpec(), "The file stayed the same after expression.") + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonBreakpoint, "We still say we stopped for a breakpoint.") + self.assertTrue (thread.GetStopReasonDataAtIndex(0) == current_bp[0] and thread.GetStopReasonDataAtIndex(1) == current_bp[1], "And it is the same breakpoint.") + + # Also make sure running the expression didn't change the public stop id + # but did change if we are asking for expression stops as well. + stop_id_after_expression = process.GetStopID() + stop_id_after_including_expressions = process.GetStopID(True) + + self.assertTrue (stop_id_before_expression == stop_id_after_expression, "Expression calling doesn't change stop ID") + + self.assertTrue (stop_id_after_including_expressions > stop_id_before_including_expressions, "Stop ID including expressions increments over expression call.") + + # Do the same thing with an expression that's going to crash, and make sure we are still unchanged. + + frame.EvaluateExpression ("((char *) 0)[0] = 'a'") + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (current_line == frame.GetLineEntry().GetLine(), "The line stayed the same after expression.") + self.assertTrue (current_file == frame.GetLineEntry().GetFileSpec(), "The file stayed the same after expression.") + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonBreakpoint, "We still say we stopped for a breakpoint.") + self.assertTrue (thread.GetStopReasonDataAtIndex(0) == current_bp[0] and thread.GetStopReasonDataAtIndex(1) == current_bp[1], "And it is the same breakpoint.") + + # Now continue and make sure we just complete the step: + # Disable all our breakpoints first - sometimes the compiler puts two line table entries in for the + # breakpoint a "b" and we don't want to hit that. + for bkpt in breakpoints_to_disable: + bkpt.SetEnabled(False) + + process.Continue() + + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "a") + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete) + + # And one more time should get us back to main: + process.Continue() + + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "main") + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete) + + # Now make sure we can call a function, break in the called function, then have "continue" get us back out again: + frame = thread.GetFrameAtIndex(0) + frame = thread.GetFrameAtIndex(0) + current_line = frame.GetLineEntry().GetLine() + current_file = frame.GetLineEntry().GetFileSpec() + + break_in_b.SetEnabled(True) + frame.EvaluateExpression ("b (4)", lldb.eNoDynamicValues, False) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_in_b) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint in b when calling b.") + thread = threads[0] + + # So do a step over here to make sure we can still do that: + + thread.StepOver() + + # See that we are still in b: + func_name = thread.GetFrameAtIndex(0).GetFunctionName() + self.assertTrue (func_name == "b", "Should be in 'b', were in %s"%(func_name)) + + # Okay, now if we continue, we will finish off our function call and we should end up back in "a" as if nothing had happened: + process.Continue () + + self.assertTrue (thread.GetFrameAtIndex(0).GetLineEntry().GetLine() == current_line) + self.assertTrue (thread.GetFrameAtIndex(0).GetLineEntry().GetFileSpec() == current_file) + + # Now we are going to test step in targeting a function: + + break_in_b.SetEnabled (False) + + break_before_complex_1 = target.BreakpointCreateBySourceRegex ('// Stop here to try step in targeting b.', self.main_source_spec) + self.assertTrue(break_before_complex_1, VALID_BREAKPOINT) + + break_before_complex_2 = target.BreakpointCreateBySourceRegex ('// Stop here to try step in targeting complex.', self.main_source_spec) + self.assertTrue(break_before_complex_2, VALID_BREAKPOINT) + + break_before_complex_3 = target.BreakpointCreateBySourceRegex ('// Stop here to step targeting b and hitting breakpoint.', self.main_source_spec) + self.assertTrue(break_before_complex_3, VALID_BREAKPOINT) + + break_before_complex_4 = target.BreakpointCreateBySourceRegex ('// Stop here to make sure bogus target steps over.', self.main_source_spec) + self.assertTrue(break_before_complex_4, VALID_BREAKPOINT) + + threads = lldbutil.continue_to_breakpoint(process, break_before_complex_1) + self.assertTrue (len(threads) == 1) + thread = threads[0] + break_before_complex_1.SetEnabled(False) + + thread.StepInto ("b") + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "b") + + # Now continue out and stop at the next call to complex. This time step all the way into complex: + threads = lldbutil.continue_to_breakpoint (process, break_before_complex_2) + self.assertTrue (len(threads) == 1) + thread = threads[0] + break_before_complex_2.SetEnabled(False) + + thread.StepInto ("complex") + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "complex") + + # Now continue out and stop at the next call to complex. This time enable breakpoints in a and c and then step targeting b: + threads = lldbutil.continue_to_breakpoint (process, break_before_complex_3) + self.assertTrue (len(threads) == 1) + thread = threads[0] + break_before_complex_3.SetEnabled(False) + + break_at_start_of_a = target.BreakpointCreateByName ('a') + break_at_start_of_c = target.BreakpointCreateByName ('c') + + thread.StepInto ("b") + threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonBreakpoint); + + self.assertTrue (len(threads) == 1) + thread = threads[0] + stop_break_id = thread.GetStopReasonDataAtIndex(0) + self.assertTrue(stop_break_id == break_at_start_of_a.GetID() or stop_break_id == break_at_start_of_c.GetID()) + + break_at_start_of_a.SetEnabled(False) + break_at_start_of_c.SetEnabled(False) + + process.Continue() + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "b") + + # Now continue out and stop at the next call to complex. This time enable breakpoints in a and c and then step targeting b: + threads = lldbutil.continue_to_breakpoint (process, break_before_complex_4) + self.assertTrue (len(threads) == 1) + thread = threads[0] + break_before_complex_4.SetEnabled(False) + + thread.StepInto("NoSuchFunction") + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "main") diff --git a/packages/Python/lldbsuite/test/lang/c/stepping/TestThreadStepping.py b/packages/Python/lldbsuite/test/lang/c/stepping/TestThreadStepping.py new file mode 100644 index 000000000000..c3ed3f933ab0 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/stepping/TestThreadStepping.py @@ -0,0 +1,81 @@ +""" +Test thread stepping features in combination with frame select. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadSteppingTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.line1 = line_number('main.c', '// Find the line number of function "c" here.') + self.line2 = line_number('main.c', '// frame select 2, thread step-out while stopped at "c(1)"') + self.line3 = line_number('main.c', '// thread step-out while stopped at "c(2)"') + self.line4 = line_number('main.c', '// frame select 1, thread step-out while stopped at "c(3)"') + + def test_step_out_with_run_command(self): + """Exercise thread step-out and frame select followed by thread step-out.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Create a breakpoint inside function 'c'. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line1, num_expected_locations=1, loc_exact=True) + + # Now run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The process should be stopped at this point. + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* stopped']) + + # The frame #0 should correspond to main.c:32, the executable statement + # in function name 'c'. And frame #3 should point to main.c:37. + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint"], + patterns = ["frame #0.*main.c:%d" % self.line1, + "frame #3.*main.c:%d" % self.line2]) + + # We want to move the pc to frame #3. This can be accomplished by + # 'frame select 2', followed by 'thread step-out'. + self.runCmd("frame select 2") + self.runCmd("thread step-out") + self.expect("thread backtrace", STEP_OUT_SUCCEEDED, + substrs = ["stop reason = step out"], + patterns = ["frame #0.*main.c:%d" % self.line2]) + + # Let's move on to a single step-out case. + self.runCmd("process continue") + + # The process should be stopped at this point. + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* stopped']) + self.runCmd("thread step-out") + self.expect("thread backtrace", STEP_OUT_SUCCEEDED, + substrs = ["stop reason = step out"], + patterns = ["frame #0.*main.c:%d" % self.line3]) + + # Do another frame selct, followed by thread step-out. + self.runCmd("process continue") + + # The process should be stopped at this point. + self.expect("process status", PROCESS_STOPPED, + patterns = ['Process .* stopped']) + self.runCmd("frame select 1") + self.runCmd("thread step-out") + self.expect("thread backtrace", STEP_OUT_SUCCEEDED, + substrs = ["stop reason = step out"], + patterns = ["frame #0.*main.c:%d" % self.line4]) diff --git a/packages/Python/lldbsuite/test/lang/c/stepping/main.c b/packages/Python/lldbsuite/test/lang/c/stepping/main.c new file mode 100644 index 000000000000..d8b4a2da761e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/stepping/main.c @@ -0,0 +1,69 @@ +//===-- 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 a(int); +int b(int); +int c(int); +const char *print_string = "aaaaaaaaaa\n"; + +int a(int val) +{ + int return_value = val; // basic break at the start of b + + if (val <= 1) + { + return_value = b(val); // break here to stop in a before calling b + } + else if (val >= 3) + { + return_value = c(val); + } + + return return_value; +} + +int b(int val) +{ + int rc = c(val); // thread step-out while stopped at "c(2)" + return rc; +} + +int c(int val) +{ + return val + 3; // Find the line number of function "c" here. +} + +int complex (int first, int second, int third) +{ + return first + second + third; // Step in targeting complex should stop here +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // frame select 2, thread step-out while stopped at "c(1)" + printf("a(1) returns %d\n", A1); + + int B2 = b(2); + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // frame select 1, thread step-out while stopped at "c(3)" + printf("a(3) returns %d\n", A3); + + int A4 = complex (a(1), b(2), c(3)); // Stop here to try step in targeting b. + + int A5 = complex (a(2), b(3), c(4)); // Stop here to try step in targeting complex. + + int A6 = complex (a(4), b(5), c(6)); // Stop here to step targeting b and hitting breakpoint. + + int A7 = complex (a(5), b(6), c(7)); // Stop here to make sure bogus target steps over. + + printf ("I am using print_string: %s.\n", print_string); + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/strings/Makefile b/packages/Python/lldbsuite/test/lang/c/strings/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/strings/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/strings/TestCStrings.py b/packages/Python/lldbsuite/test/lang/c/strings/TestCStrings.py new file mode 100644 index 000000000000..6f2a9ff03be0 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/strings/TestCStrings.py @@ -0,0 +1,54 @@ +""" +Tests that C strings work as expected in expressions +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CStringsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr21765") + def test_with_run_command(self): + """Tests that C strings work as expected in expressions""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + line = line_number('main.c', '// breakpoint 1') + lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True) + + self.runCmd("process launch", RUN_SUCCEEDED) + + self.expect("expression -- a[2]", + patterns = ["\((const )?char\) \$0 = 'c'"]) + + self.expect("expression -- z[2]", + startstr = "(const char) $1 = 'x'") + + # On Linux, the expression below will test GNU indirect function calls. + self.expect("expression -- (int)strlen(\"hello\")", + startstr = "(int) $2 = 5") + + self.expect("expression -- \"world\"[2]", + startstr = "(const char) $3 = 'r'") + + self.expect("expression -- \"\"[0]", + startstr = "(const char) $4 = '\\0'") + + self.expect("expr --raw -- \"hello\"", + substrs = ['[0] = \'h\'', + '[5] = \'\\0\'']) + + self.expect("p \"hello\"", + substrs = ['[6]) $', 'hello']) + + self.expect("p (char*)\"hello\"", + substrs = ['(char *) $', ' = 0x', + 'hello']) + + self.expect("p (int)strlen(\"\")", + substrs = ['(int) $', ' = 0']) + + self.expect("expression !z", + substrs = ['false']) diff --git a/packages/Python/lldbsuite/test/lang/c/strings/main.c b/packages/Python/lldbsuite/test/lang/c/strings/main.c new file mode 100644 index 000000000000..e02580b407f5 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/strings/main.c @@ -0,0 +1,18 @@ +//===-- 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() +{ + const char a[] = "abcde"; + const char *z = "vwxyz"; + + printf("%s %s", a, z); // breakpoint 1 +} diff --git a/packages/Python/lldbsuite/test/lang/c/struct_types/Makefile b/packages/Python/lldbsuite/test/lang/c/struct_types/Makefile new file mode 100644 index 000000000000..cd9ca5c86d84 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/struct_types/Makefile @@ -0,0 +1,3 @@ +LEVEL = ../../../make +C_SOURCES := main.c +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/struct_types/TestStructTypes.py b/packages/Python/lldbsuite/test/lang/c/struct_types/TestStructTypes.py new file mode 100644 index 000000000000..87ad326f3181 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/struct_types/TestStructTypes.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.expectedFailureWindows("llvm.org/pr24764")] ) diff --git a/packages/Python/lldbsuite/test/lang/c/struct_types/main.c b/packages/Python/lldbsuite/test/lang/c/struct_types/main.c new file mode 100644 index 000000000000..29ac10cb94a8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/struct_types/main.c @@ -0,0 +1,43 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +struct things_to_sum { + int a; + int b; + int c; +}; + +int sum_things(struct things_to_sum tts) +{ + return tts.a + tts.b + tts.c; +} + +int main (int argc, char const *argv[]) +{ + struct point_tag { + int x; + int y; + char padding[0]; + }; //% self.expect("frame variable pt.padding[0]", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["pt.padding[0] = "]) + //% self.expect("frame variable pt.padding[1]", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["pt.padding[1] = "]) + //% self.expect("expression -- (pt.padding[0])", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["(char)", " = "]) + //% self.expect("image lookup -t point_tag", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ['padding[]']) # Once rdar://problem/12566646 is fixed, this should display correctly + + 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, {}}}; + struct things_to_sum tts = { 2, 3, 4 }; + + int sum = sum_things(tts); //% self.expect("expression -- &pt == (struct point_tag*)0", substrs = ['false']) + //% self.expect("expression -- sum_things(tts)", substrs = ['9']) + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/tls_globals/Makefile b/packages/Python/lldbsuite/test/lang/c/tls_globals/Makefile new file mode 100644 index 000000000000..90affed8e3ad --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/tls_globals/Makefile @@ -0,0 +1,11 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +CFLAGS_EXTRAS += -fPIC + +DYLIB_NAME := a +DYLIB_C_SOURCES := a.c + +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/tls_globals/TestTlsGlobals.py b/packages/Python/lldbsuite/test/lang/c/tls_globals/TestTlsGlobals.py new file mode 100644 index 000000000000..0d9e22ec3adf --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/tls_globals/TestTlsGlobals.py @@ -0,0 +1,75 @@ +"""Test that thread-local storage can be read correctly.""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TlsGlobalTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + + if self.getPlatform() == "freebsd" or self.getPlatform() == "linux": + # LD_LIBRARY_PATH must be set so the shared libraries are found on startup + if "LD_LIBRARY_PATH" in os.environ: + self.runCmd("settings set target.env-vars " + self.dylibPath + "=" + os.environ["LD_LIBRARY_PATH"] + ":" + os.getcwd()) + else: + self.runCmd("settings set target.env-vars " + self.dylibPath + "=" + os.getcwd()) + self.addTearDownHook(lambda: self.runCmd("settings remove target.env-vars " + self.dylibPath)) + + @unittest2.expectedFailure("rdar://7796742") + @skipIfWindows # TLS works differently on Windows, this would need to be implemented separately. + def test(self): + """Test thread-local storage.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + line1 = line_number('main.c', '// thread breakpoint') + lldbutil.run_break_set_by_file_and_line (self, "main.c", line1, num_expected_locations=1, loc_exact=True) + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.runCmd("process status", "Get process status") + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # BUG: sometimes lldb doesn't change threads to the stopped thread. + # (unrelated to this test). + self.runCmd("thread select 2", "Change thread") + + # Check that TLS evaluates correctly within the thread. + self.expect("expr var_static", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\(int\) \$.* = 88"]) + self.expect("expr var_shared", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\(int\) \$.* = 66"]) + + # Continue on the main thread + line2 = line_number('main.c', '// main breakpoint') + lldbutil.run_break_set_by_file_and_line (self, "main.c", line2, num_expected_locations=1, loc_exact=True) + self.runCmd("continue", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.runCmd("process status", "Get process status") + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # BUG: sometimes lldb doesn't change threads to the stopped thread. + # (unrelated to this test). + self.runCmd("thread select 1", "Change thread") + + # Check that TLS evaluates correctly within the main thread. + self.expect("expr var_static", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\(int\) \$.* = 44"]) + self.expect("expr var_shared", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\(int\) \$.* = 33"]) diff --git a/packages/Python/lldbsuite/test/lang/c/tls_globals/a.c b/packages/Python/lldbsuite/test/lang/c/tls_globals/a.c new file mode 100644 index 000000000000..b9a85902d117 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/tls_globals/a.c @@ -0,0 +1,18 @@ +//===-- a.c -----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +__thread int var_shared = 33; + +void shared_check() +{ + var_shared *= 2; + usleep(1); // shared thread breakpoint +} diff --git a/packages/Python/lldbsuite/test/lang/c/tls_globals/main.c b/packages/Python/lldbsuite/test/lang/c/tls_globals/main.c new file mode 100644 index 000000000000..cbe01b89b7ef --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/tls_globals/main.c @@ -0,0 +1,36 @@ +//===-- 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 + +void shared_check(); + +// Create some TLS storage within the static executable. +__thread int var_static = 44; + +void *fn_static(void *param) +{ + var_static *= 2; + shared_check(); + usleep(1); // thread breakpoint + for(;;) + usleep(1); +} + +int main (int argc, char const *argv[]) +{ + pthread_t handle; + pthread_create(&handle, NULL, &fn_static, NULL); + + for(;;) + usleep(1); // main breakpoint + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/c/typedef/Makefile b/packages/Python/lldbsuite/test/lang/c/typedef/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/typedef/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/c/typedef/Testtypedef.py b/packages/Python/lldbsuite/test/lang/c/typedef/Testtypedef.py new file mode 100644 index 000000000000..a4870de1d177 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/typedef/Testtypedef.py @@ -0,0 +1,40 @@ +"""Look up type information for typedefs of same name at different lexical scope and check for correct display.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TypedefTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureAll(bugnumber="llvm.org/pr19238", compiler="clang") + @expectedFailureAll(bugnumber="llvm.org/pr25626 expectedFailureClang fails on FreeBSD", oslist=["freebsd"]) + def test_typedef(self): + """Test 'image lookup -t a' and check for correct display at different scopes.""" + self.build() + self.image_lookup_for_multiple_typedefs() + + def image_lookup_for_multiple_typedefs(self): + """Test 'image lookup -t a' at different scopes and check for correct display.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + typearray = ("float", "float", "char", "double *", "float", "int", "double", "float", "float") + arraylen = len(typearray)+1 + for i in range(1,arraylen): + loc_line = line_number('main.c', '// Set break point ' + str(i) + '.') + lldbutil.run_break_set_by_file_and_line (self, "main.c",loc_line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + for t in typearray: + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + self.expect("image lookup -t a", DATA_TYPES_DISPLAYED_CORRECTLY, + substrs = ['name = "' + t + '"']) + self.runCmd("continue") diff --git a/packages/Python/lldbsuite/test/lang/c/typedef/main.c b/packages/Python/lldbsuite/test/lang/c/typedef/main.c new file mode 100644 index 000000000000..62f8a00e6644 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/c/typedef/main.c @@ -0,0 +1,46 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +void test() +{ + typedef double * a; + a b = 0; // Set break point 4. +} +int main (int argc, char const *argv[]) +{ + typedef float a; + int i = 0; // Set break point 1. + i++; + a floatvariable = 2.7; // Set break point 2. + { + typedef char a; + i++; + a charvariable = 'a'; // Set break point 3. + test(); + } + { + int c = 0; + c++; // Set break point 5. + for(i = 0 ; i < 1 ; i++) + { + typedef int a; + a b; + b = 7; // Set break point 6. + } + for(i = 0 ; i < 1 ; i++) + { + typedef double a; + a b; + b = 3.14; // Set break point 7. + } + c = 1; // Set break point 8. + } + floatvariable = 2.5; + floatvariable = 2.8; // Set break point 9. + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/auto/Makefile b/packages/Python/lldbsuite/test/lang/cpp/auto/Makefile new file mode 100644 index 000000000000..194af7b32398 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/auto/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +CFLAGS_EXTRAS += $(NO_LIMIT_DEBUG_INFO_FLAGS) + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/auto/TestCPPAuto.py b/packages/Python/lldbsuite/test/lang/cpp/auto/TestCPPAuto.py new file mode 100644 index 000000000000..9746dec2fa3d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/auto/TestCPPAuto.py @@ -0,0 +1,25 @@ +""" +Tests that auto types work +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CPPAutoTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureGcc("GCC does not generate complete debug info") + def test_with_run_command(self): + """Test that auto types work in the expression parser""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + line = line_number('main.cpp', '// break here') + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=-1, loc_exact=False) + + self.runCmd("process launch", RUN_SUCCEEDED) + + self.expect('expr auto f = 123456; f', substrs=['int', '123456']) + self.expect('expr struct Test { int x; int y; Test() : x(123), y(456) {} }; auto t = Test(); t', substrs=['Test', '123', '456']) + self.expect('expr auto s = helloworld; s', substrs=['string', 'hello world']) diff --git a/packages/Python/lldbsuite/test/lang/cpp/auto/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/auto/main.cpp new file mode 100644 index 000000000000..76fb95e0a34c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/auto/main.cpp @@ -0,0 +1,16 @@ +//===-- 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 + +int main() +{ + std::string helloworld("hello world"); + return 0; // break here +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/bool/Makefile b/packages/Python/lldbsuite/test/lang/cpp/bool/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/bool/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/bool/TestCPPBool.py b/packages/Python/lldbsuite/test/lang/cpp/bool/TestCPPBool.py new file mode 100644 index 000000000000..ef7135cfaa07 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/bool/TestCPPBool.py @@ -0,0 +1,26 @@ +""" +Tests that bool types work +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CPPBoolTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_with_run_command(self): + """Test that bool types work in the expression parser""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + line = line_number('main.cpp', '// breakpoint 1') + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=-1, loc_exact=False) + + self.runCmd("process launch", RUN_SUCCEEDED) + + self.expect("expression -- bool second_bool = my_bool; second_bool", + startstr = "(bool) $0 = false") + + self.expect("expression -- my_bool = true", + startstr = "(bool) $1 = true") diff --git a/packages/Python/lldbsuite/test/lang/cpp/bool/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/bool/main.cpp new file mode 100644 index 000000000000..88d02d5b2894 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/bool/main.cpp @@ -0,0 +1,17 @@ +//===-- 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 + +int main() +{ + bool my_bool = false; + + printf("%s\n", my_bool ? "true" : "false"); // breakpoint 1 +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/Makefile b/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/Makefile new file mode 100644 index 000000000000..1d1f38f7fd0e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := nested.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/TestCPPBreakpointCommands.py b/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/TestCPPBreakpointCommands.py new file mode 100644 index 000000000000..6c06f3750d13 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/TestCPPBreakpointCommands.py @@ -0,0 +1,84 @@ +""" +Test lldb breakpoint command for CPP methods & functions in a namespace. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class CPPBreakpointCommandsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows + def test(self): + """Test a sequence of breakpoint command add, list, and delete.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + a_out_module = lldb.SBFileSpecList() + a_out_module.Append(lldb.SBFileSpec(exe)) + + nested_comp_unit = lldb.SBFileSpecList() + nested_comp_unit.Append (lldb.SBFileSpec("nested.cpp")) + + # First provide ONLY the method name. This should get everybody... + auto_break = target.BreakpointCreateByName ("Function", + lldb.eFunctionNameTypeAuto, + a_out_module, + nested_comp_unit) + self.assertTrue (auto_break.GetNumLocations() == 5) + + # Now add the Baz class specifier. This should get the version contained in Bar, + # AND the one contained in :: + auto_break = target.BreakpointCreateByName ("Baz::Function", + lldb.eFunctionNameTypeAuto, + a_out_module, + nested_comp_unit) + self.assertTrue (auto_break.GetNumLocations() == 2) + + # Then add the Bar::Baz specifier. This should get the version contained in Bar only + auto_break = target.BreakpointCreateByName ("Bar::Baz::Function", + lldb.eFunctionNameTypeAuto, + a_out_module, + nested_comp_unit) + self.assertTrue (auto_break.GetNumLocations() == 1) + + plain_method_break = target.BreakpointCreateByName ("Function", + lldb.eFunctionNameTypeMethod, + a_out_module, + nested_comp_unit) + self.assertTrue (plain_method_break.GetNumLocations() == 3) + + plain_method_break = target.BreakpointCreateByName ("Baz::Function", + lldb.eFunctionNameTypeMethod, + a_out_module, + nested_comp_unit) + self.assertTrue (plain_method_break.GetNumLocations() == 2) + + plain_method_break = target.BreakpointCreateByName ("Bar::Baz::Function", + lldb.eFunctionNameTypeMethod, + a_out_module, + nested_comp_unit) + self.assertTrue (plain_method_break.GetNumLocations() == 1) + + plain_method_break = target.BreakpointCreateByName ("Function", + lldb.eFunctionNameTypeBase, + a_out_module, + nested_comp_unit) + self.assertTrue (plain_method_break.GetNumLocations() == 2) + + plain_method_break = target.BreakpointCreateByName ("Bar::Function", + lldb.eFunctionNameTypeBase, + a_out_module, + nested_comp_unit) + self.assertTrue (plain_method_break.GetNumLocations() == 1) diff --git a/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/nested.cpp b/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/nested.cpp new file mode 100644 index 000000000000..29d4b4cb19e7 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/breakpoint-commands/nested.cpp @@ -0,0 +1,76 @@ +#include + +namespace Foo +{ + namespace Bar + { + class Baz + { + public: + Baz (int value):m_value(value) {} + int Function () + { + printf ("%s returning: %d.\n", __FUNCTION__, m_value); + return m_value; + } + private: + int m_value; + }; + + class Baz2 + { + public: + Baz2 (int value):m_value(value) {} + int Function () + { + printf ("%s returning: %d.\n", __FUNCTION__, m_value); + return m_value; + } + private: + int m_value; + }; + + static int bar_value = 20; + int Function () + { + printf ("%s returning: %d.\n", __FUNCTION__, bar_value); + return bar_value; + } + } +} + +class Baz +{ +public: + Baz (int value):m_value(value) {} + int Function () + { + printf ("%s returning: %d.\n", __FUNCTION__, m_value); + return m_value; + } +private: + int m_value; +}; + +int +Function () +{ + printf ("I am a global function, I return 333.\n"); + return 333; +} + +int main () +{ + Foo::Bar::Baz mine(200); + Foo::Bar::Baz2 mine2(300); + ::Baz bare_baz (500); + + printf ("Yup, got %d from Baz.\n", mine.Function()); + printf ("Yup, got %d from Baz.\n", mine2.Function()); + printf ("Yup, got %d from Baz.\n", bare_baz.Function()); + printf ("And got %d from Bar.\n", Foo::Bar::Function()); + printf ("And got %d from ::.\n", ::Function()); + + return 0; + +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/call-function/Makefile b/packages/Python/lldbsuite/test/lang/cpp/call-function/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/call-function/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/call-function/TestCallCPPFunction.py b/packages/Python/lldbsuite/test/lang/cpp/call-function/TestCallCPPFunction.py new file mode 100644 index 000000000000..3aa97af68819 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/call-function/TestCallCPPFunction.py @@ -0,0 +1,33 @@ +""" +Tests calling a function by basename +""" + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CallCPPFunctionTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.line = line_number('main.cpp', '// breakpoint') + + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test_with_run_command(self): + """Test calling a function by basename""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("process launch", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", + STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + self.expect("expression -- a_function_to_call()", + startstr = "(int) $0 = 0") diff --git a/packages/Python/lldbsuite/test/lang/cpp/call-function/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/call-function/main.cpp new file mode 100644 index 000000000000..61a5e9d21abb --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/call-function/main.cpp @@ -0,0 +1,11 @@ +#include + +int a_function_to_call() +{ + return 0; +} + +int main() +{ + printf("%d\n", a_function_to_call()); // breakpoint +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/chained-calls/Makefile b/packages/Python/lldbsuite/test/lang/cpp/chained-calls/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/chained-calls/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/chained-calls/TestCppChainedCalls.py b/packages/Python/lldbsuite/test/lang/cpp/chained-calls/TestCppChainedCalls.py new file mode 100644 index 000000000000..272665a78577 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/chained-calls/TestCppChainedCalls.py @@ -0,0 +1,73 @@ +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestCppChainedCalls(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr21765") + def test_with_run_command(self): + self.build() + + # Get main source file + src_file = "main.cpp" + src_file_spec = lldb.SBFileSpec(src_file) + self.assertTrue(src_file_spec.IsValid(), "Main source file") + + # Get the path of the executable + cwd = os.getcwd() + exe_file = "a.out" + exe_path = os.path.join(cwd, exe_file) + + # Load the executable + target = self.dbg.CreateTarget(exe_path) + self.assertTrue(target.IsValid(), VALID_TARGET) + + # Break on main function + main_breakpoint = target.BreakpointCreateBySourceRegex("break here", src_file_spec) + self.assertTrue(main_breakpoint.IsValid() and main_breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT) + + # Launch the process + args = None + env = None + process = target.LaunchSimple(args, env, self.get_process_working_directory()) + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + + # Get the thread of the process + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + + # Get frame for current thread + frame = thread.GetSelectedFrame() + + # Test chained calls + test_result = frame.EvaluateExpression("get(set(true))") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "true", "get(set(true)) = true") + + test_result = frame.EvaluateExpression("get(set(false))") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "get(set(false)) = false") + + test_result = frame.EvaluateExpression("get(t & f)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "get(t & f) = false") + + test_result = frame.EvaluateExpression("get(f & t)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "get(f & t) = false") + + test_result = frame.EvaluateExpression("get(t & t)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "true", "get(t & t) = true") + + test_result = frame.EvaluateExpression("get(f & f)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "get(f & f) = false") + + test_result = frame.EvaluateExpression("get(t & f)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "get(t & f) = false") + + test_result = frame.EvaluateExpression("get(f) && get(t)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "get(f) && get(t) = false") + + test_result = frame.EvaluateExpression("get(f) && get(f)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "get(f) && get(t) = false") + + test_result = frame.EvaluateExpression("get(t) && get(t)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "true", "get(t) && get(t) = true") diff --git a/packages/Python/lldbsuite/test/lang/cpp/chained-calls/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/chained-calls/main.cpp new file mode 100644 index 000000000000..a888c3f6c557 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/chained-calls/main.cpp @@ -0,0 +1,33 @@ +class Bool { +public: + Bool operator&(const Bool other) + { + Bool result; + result.value = value && other.value; + return result; + } + + bool value; +}; + +bool get(Bool object) +{ + return object.value; +} + +Bool set(bool value) +{ + Bool result; + result.value = value; + return result; +} + +int main() +{ + Bool t = set(true); + Bool f = set(false); + get(t); + get(f); + get(t & f); + return 0; // break here +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/char1632_t/.categories b/packages/Python/lldbsuite/test/lang/cpp/char1632_t/.categories new file mode 100644 index 000000000000..fe1da0247c62 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/char1632_t/.categories @@ -0,0 +1 @@ +dataformatters diff --git a/packages/Python/lldbsuite/test/lang/cpp/char1632_t/Makefile b/packages/Python/lldbsuite/test/lang/cpp/char1632_t/Makefile new file mode 100644 index 000000000000..932046f26968 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/char1632_t/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +CFLAGS :=-g -O0 -std=c++11 + +clean: OBJECTS+=$(wildcard main.d.*) + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/char1632_t/TestChar1632T.py b/packages/Python/lldbsuite/test/lang/cpp/char1632_t/TestChar1632T.py new file mode 100644 index 000000000000..9bb1faf4ba6d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/char1632_t/TestChar1632T.py @@ -0,0 +1,90 @@ +# coding=utf8 +""" +Test that the C++11 support for char16_t and char32_t works correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class Char1632TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.source = 'main.cpp' + self.lines = [ line_number(self.source, '// breakpoint1'), + line_number(self.source, '// breakpoint2') ] + + @expectedFailureIcc # ICC (13.1) does not emit the DW_TAG_base_type for char16_t and char32_t. + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test(self): + """Test that the C++11 support for char16_t and char32_t works correctly.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Set breakpoints + for line in self.lines: + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line) + + # Now launch the process, and do not stop at entry point and stop at breakpoint1 + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + if self.TraceOn(): + self.runCmd("frame variable") + + # Check that we correctly report the const types + self.expect("frame variable cs16 cs32", + substrs = ['(const char16_t *) cs16 = ','(const char32_t *) cs32 = ','u"hello world ྒྙྐ"','U"hello world ྒྙྐ"']) + + # Check that we correctly report the non-const types + self.expect("frame variable s16 s32", + substrs = ['(char16_t *) s16 = ','(char32_t *) s32 = ','u"ﺸﺵۻ"','U"ЕЙРГЖО"']) + + # Check that we correctly report the array types + self.expect("frame variable as16 as32", + patterns = ['\(char16_t \[[0-9]+\]\) as16 = ', '\(char32_t \[[0-9]+\]\) as32 = '], + substrs = ['u"ﺸﺵۻ"','U"ЕЙРГЖО"']) + + self.runCmd("next") # step to after the string is nullified + + # check that we don't crash on NULL + self.expect("frame variable s32", + substrs = ['(char32_t *) s32 = 0x00000000']) + + # continue and hit breakpoint2 + self.runCmd("continue") + + # check that the new strings show + self.expect("frame variable s16 s32", + substrs = ['(char16_t *) s16 = 0x','(char32_t *) s32 = ','"色ハ匂ヘト散リヌルヲ"','"෴"']) + + # check the same as above for arrays + self.expect("frame variable as16 as32", + patterns = ['\(char16_t \[[0-9]+\]\) as16 = ', '\(char32_t \[[0-9]+\]\) as32 = '], + substrs = ['"色ハ匂ヘト散リヌルヲ"','"෴"']) + + # check that zero values are properly handles + self.expect('frame variable cs16_zero', substrs=["U+0000 u'\\0'"]) + self.expect('frame variable cs32_zero', substrs=["U+0x00000000 U'\\0'"]) + self.expect('expression cs16_zero', substrs=["U+0000 u'\\0'"]) + self.expect('expression cs32_zero', substrs=["U+0x00000000 U'\\0'"]) + + # Check that we can run expressions that return charN_t + self.expect("expression u'a'",substrs = ['(char16_t) $',"61 u'a'"]) + self.expect("expression U'a'",substrs = ['(char32_t) $',"61 U'a'"]) diff --git a/packages/Python/lldbsuite/test/lang/cpp/char1632_t/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/char1632_t/main.cpp new file mode 100644 index 000000000000..b92c2d572923 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/char1632_t/main.cpp @@ -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 +#include + +#define UASZ 64 + +template +void copy_char_seq (T (&arr)[N], const T* src) +{ + size_t src_len = std::char_traits::length(src); + assert(src_len < N); + + std::char_traits::copy(arr, src, src_len); + arr[src_len] = 0; +} + +int main (int argc, char const *argv[]) +{ + char16_t as16[UASZ]; + char32_t as32[UASZ]; + auto cs16_zero = (char16_t)0; + auto cs32_zero = (char32_t)0; + auto cs16 = u"hello world ྒྙྐ"; + auto cs32 = U"hello world ྒྙྐ"; + char16_t *s16 = (char16_t *)u"ﺸﺵۻ"; + char32_t *s32 = (char32_t *)U"ЕЙРГЖО"; + copy_char_seq(as16, s16); + copy_char_seq(as32, s32); + s32 = nullptr; // breakpoint1 + s32 = (char32_t *)U"෴"; + s16 = (char16_t *)u"色ハ匂ヘト散リヌルヲ"; + copy_char_seq(as16, s16); + copy_char_seq(as32, s32); + s32 = nullptr; // breakpoint2 + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/class_static/Makefile b/packages/Python/lldbsuite/test/lang/cpp/class_static/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/class_static/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/class_static/TestStaticVariables.py b/packages/Python/lldbsuite/test/lang/cpp/class_static/TestStaticVariables.py new file mode 100644 index 000000000000..d47d1b7dde41 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/class_static/TestStaticVariables.py @@ -0,0 +1,120 @@ +""" +Test display and Python APIs on file and class static variables. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StaticVariableTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @expectedFailureWindows("llvm.org/pr24764") + def test_with_run_command(self): + """Test that file and class static variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # global variables are no longer displayed with the "frame variable" command. + self.expect('target variable A::g_points', VARIABLES_DISPLAYED_CORRECTLY, + patterns=['\(PointType \[[1-9]*\]\) A::g_points = {.*}']) + self.expect('target variable g_points', VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['(PointType [2]) g_points']) + + # On Mac OS X, gcc 4.2 emits the wrong debug info for A::g_points. + # A::g_points is an array of two elements. + if self.platformIsDarwin() or self.getPlatform() == "linux": + self.expect("target variable A::g_points[1].x", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(int) A::g_points[1].x = 11") + + @expectedFailureDarwin(9980907) + @expectedFailureClang('Clang emits incomplete debug info.') + @expectedFailureFreeBSD('llvm.org/pr20550 failing on FreeBSD-11') + @expectedFailureGcc('GCC emits incomplete debug info.') + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test Python APIs on file and class static variables.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread = process.GetThreadAtIndex(0) + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + from lldbsuite.test.lldbutil import stop_reason_to_str + self.fail(STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS % + stop_reason_to_str(thread.GetStopReason())) + + # Get the SBValue of 'A::g_points' and 'g_points'. + frame = thread.GetFrameAtIndex(0) + + # arguments => False + # locals => False + # statics => True + # in_scope_only => False + valList = frame.GetVariables(False, False, True, False) + + for val in valList: + self.DebugSBValue(val) + name = val.GetName() + self.assertTrue(name in ['g_points', 'A::g_points']) + if name == 'g_points': + self.assertTrue(val.GetValueType() == lldb.eValueTypeVariableStatic) + self.assertTrue(val.GetNumChildren() == 2) + elif name == 'A::g_points': + self.assertTrue(val.GetValueType() == lldb.eValueTypeVariableGlobal) + self.assertTrue(val.GetNumChildren() == 2) + child1 = val.GetChildAtIndex(1) + self.DebugSBValue(child1) + child1_x = child1.GetChildAtIndex(0) + self.DebugSBValue(child1_x) + self.assertTrue(child1_x.GetTypeName() == 'int' and + child1_x.GetValue() == '11') + + # SBFrame.FindValue() should also work. + val = frame.FindValue("A::g_points", lldb.eValueTypeVariableGlobal) + self.DebugSBValue(val) + self.assertTrue(val.GetName() == 'A::g_points') + + # Also exercise the "parameter" and "local" scopes while we are at it. + val = frame.FindValue("argc", lldb.eValueTypeVariableArgument) + self.DebugSBValue(val) + self.assertTrue(val.GetName() == 'argc') + + val = frame.FindValue("argv", lldb.eValueTypeVariableArgument) + self.DebugSBValue(val) + self.assertTrue(val.GetName() == 'argv') + + val = frame.FindValue("hello_world", lldb.eValueTypeVariableLocal) + self.DebugSBValue(val) + self.assertTrue(val.GetName() == 'hello_world') diff --git a/packages/Python/lldbsuite/test/lang/cpp/class_static/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/class_static/main.cpp new file mode 100644 index 000000000000..2068eadcac5f --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/class_static/main.cpp @@ -0,0 +1,53 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// I made this example after noting that I was unable to display an unsized +// static class array. It turns out that gcc 4.2 will emit DWARF that correctly +// describes the PointType, but it will incorrectly emit debug info for the +// "g_points" array where the following things are wrong: +// - the DW_TAG_array_type won't have a subrange info +// - the DW_TAG_variable for "g_points" won't have a valid byte size, so even +// though we know the size of PointType, we can't infer the actual size +// of the array by dividing the size of the variable by the number of +// elements. + +#include + +typedef struct PointType +{ + int x, y; +} PointType; + +class A +{ +public: + static PointType g_points[]; +}; + +PointType A::g_points[] = +{ + { 1, 2 }, + { 11, 22 } +}; + +static PointType g_points[] = +{ + { 3, 4 }, + { 33, 44 } +}; + +int +main (int argc, char const *argv[]) +{ + const char *hello_world = "Hello, world!"; + printf ("A::g_points[1].x = %i\n", A::g_points[1].x); // Set break point at this line. + printf ("::g_points[1].x = %i\n", g_points[1].x); + printf ("%s\n", hello_world); + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/class_types/Makefile b/packages/Python/lldbsuite/test/lang/cpp/class_types/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/class_types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/class_types/TestClassTypes.py b/packages/Python/lldbsuite/test/lang/cpp/class_types/TestClassTypes.py new file mode 100644 index 000000000000..b67e53c30745 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/class_types/TestClassTypes.py @@ -0,0 +1,215 @@ +"""Test breakpoint on a class constructor; and variable list the this object.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ClassTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Test 'frame variable this' when stopped on a class constructor.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break on the ctor function of class C. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The test suite sometimes shows that the process has exited without stopping. + # + # CC=clang ./dotest.py -v -t class_types + # ... + # Process 76604 exited with status = 0 (0x00000000) + self.runCmd("process status") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # We should be stopped on the ctor function of class C. + self.expect("frame variable --show-types this", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['C *', + ' this = ']) + + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Use Python APIs to create a breakpoint by (filespec, line).""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + filespec = target.GetExecutable() + self.assertTrue(filespec, VALID_FILESPEC) + + fsDir = os.path.normpath(filespec.GetDirectory()) + fsFile = filespec.GetFilename() + + self.assertTrue(fsDir == os.getcwd() and fsFile == "a.out", + "FileSpec matches the executable") + + bpfilespec = lldb.SBFileSpec("main.cpp", False) + + breakpoint = target.BreakpointCreateByLocation(bpfilespec, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Verify the breakpoint just created. + self.expect(str(breakpoint), BREAKPOINT_CREATED, exe=False, + substrs = ['main.cpp', + str(self.line)]) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + # The stop reason of the thread should be breakpoint. + thread = process.GetThreadAtIndex(0) + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + from lldbsuite.test.lldbutil import stop_reason_to_str + self.fail(STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS % + stop_reason_to_str(thread.GetStopReason())) + + # The filename of frame #0 should be 'main.cpp' and the line number + # should be 93. + self.expect("%s:%d" % (lldbutil.get_filenames(thread)[0], + lldbutil.get_line_numbers(thread)[0]), + "Break correctly at main.cpp:%d" % self.line, exe=False, + startstr = "main.cpp:") + ### clang compiled code reported main.cpp:94? + ### startstr = "main.cpp:93") + + # We should be stopped on the breakpoint with a hit count of 1. + self.assertTrue(breakpoint.GetHitCount() == 1, BREAKPOINT_HIT_ONCE) + + process.Continue() + + def test_with_expr_parser(self): + """Test 'frame variable this' and 'expr this' when stopped inside a constructor.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # rdar://problem/8516141 + # Is this a case of clang (116.1) generating bad debug info? + # + # Break on the ctor function of class C. + #self.expect("breakpoint set -M C", BREAKPOINT_CREATED, + # startstr = "Breakpoint created: 1: name = 'C'") + + # Make the test case more robust by using line number to break, instead. + lldbutil.run_break_set_by_file_and_line (self, None, self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Continue on inside the ctor() body... + self.runCmd("register read pc") + self.runCmd("thread step-over") + + # Verify that 'frame variable this' gets the data type correct. + self.expect("frame variable this",VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['C *']) + + # Verify that frame variable --show-types this->m_c_int behaves correctly. + self.runCmd("register read pc") + self.runCmd("expr m_c_int") + self.expect("frame variable --show-types this->m_c_int", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(int) this->m_c_int = 66') + + # Verify that 'expression this' gets the data type correct. + self.expect("expression this", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ['C *']) + + # rdar://problem/8430916 + # expr this->m_c_int returns an incorrect value + # + # Verify that expr this->m_c_int behaves correctly. + self.expect("expression this->m_c_int", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ['\(int\) \$[0-9]+ = 66']) + + def test_with_constructor_name (self): + """Test 'frame variable this' and 'expr this' when stopped inside a constructor.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + filespec = target.GetExecutable() + self.assertTrue(filespec, VALID_FILESPEC) + + fsDir = os.path.normpath(filespec.GetDirectory()) + fsFile = filespec.GetFilename() + + self.assertTrue(fsDir == os.getcwd() and fsFile == "a.out", + "FileSpec matches the executable") + + bpfilespec = lldb.SBFileSpec("main.cpp", False) + + breakpoint = target.BreakpointCreateByLocation(bpfilespec, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Verify the breakpoint just created. + self.expect(str(breakpoint), BREAKPOINT_CREATED, exe=False, + substrs = ['main.cpp', + str(self.line)]) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + # The stop reason of the thread should be breakpoint. + thread = process.GetThreadAtIndex(0) + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + from lldbsuite.test.lldbutil import stop_reason_to_str + self.fail(STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS % + stop_reason_to_str(thread.GetStopReason())) + + frame = thread.frames[0] + self.assertTrue (frame.IsValid(), "Got a valid frame.") + + self.assertTrue ("C::C" in frame.name, "Constructor name includes class name.") diff --git a/packages/Python/lldbsuite/test/lang/cpp/class_types/TestClassTypesDisassembly.py b/packages/Python/lldbsuite/test/lang/cpp/class_types/TestClassTypesDisassembly.py new file mode 100644 index 000000000000..595d075d5180 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/class_types/TestClassTypesDisassembly.py @@ -0,0 +1,94 @@ +""" +Test the lldb disassemble command on each call frame when stopped on C's ctor. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class IterateFrameAndDisassembleTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_and_run_command(self): + """Disassemble each call frame when stopped on C's constructor.""" + self.build() + self.breakOnCtor() + + raw_output = self.res.GetOutput() + frameRE = re.compile(r""" + ^\s\sframe # heading for the frame info, + .* # wildcard, and + 0x[0-9a-f]{16} # the frame pc, and + \sa.out`(.+) # module`function, and + \s\+\s # the rest ' + ....' + """, re.VERBOSE) + for line in raw_output.split(os.linesep): + match = frameRE.search(line) + if match: + function = match.group(1) + #print("line:", line) + #print("function:", function) + self.runCmd("disassemble -n '%s'" % function) + + @add_test_categories(['pyapi']) + def test_and_python_api(self): + """Disassemble each call frame when stopped on C's constructor.""" + self.build() + self.breakOnCtor() + + # Now use the Python API to get at each function on the call stack and + # disassemble it. + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetThreadAtIndex(0) + depth = thread.GetNumFrames() + for i in range(depth - 1): + frame = thread.GetFrameAtIndex(i) + function = frame.GetFunction() + # Print the function header. + if self.TraceOn(): + print() + print(function) + if function: + # Get all instructions for this function and print them out. + insts = function.GetInstructions(target) + for inst in insts: + # We could simply do 'print inst' to print out the disassembly. + # But we want to print to stdout only if self.TraceOn() is True. + disasm = str(inst) + if self.TraceOn(): + print(disasm) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def breakOnCtor(self): + """Setup/run the program so it stops on C's constructor.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break on the ctor function of class C. + bpno = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint %d.'%(bpno)]) + + # This test was failing because we fail to put the C:: in front of constructore. + # We should maybe make another testcase to cover that specifically, but we shouldn't + # fail this whole testcase for an inessential issue. + # We should be stopped on the ctor function of class C. + # self.expect("thread backtrace", BACKTRACE_DISPLAYED_CORRECTLY, + # substrs = ['C::C']) diff --git a/packages/Python/lldbsuite/test/lang/cpp/class_types/cmds.txt b/packages/Python/lldbsuite/test/lang/cpp/class_types/cmds.txt new file mode 100644 index 000000000000..1c7ef9f1c8ad --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/class_types/cmds.txt @@ -0,0 +1,3 @@ +b main.cpp:97 +c +var diff --git a/packages/Python/lldbsuite/test/lang/cpp/class_types/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/class_types/main.cpp new file mode 100644 index 000000000000..251e66c3c9f7 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/class_types/main.cpp @@ -0,0 +1,126 @@ +//===-- 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 Conversion +{ +public: + Conversion (int i) : + m_i (i) + {} + + operator bool() + { + return m_i != 0; + } + +private: + int m_i; +}; + +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; +}; + +#include +class C : public B +{ +public: + C(int ai, int bi, int ci) : + B(ai, bi), + m_c_int(ci) + { + printf("Within C::ctor() m_c_int=%d\n", m_c_int); // Set break point at this line. + } + + //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); + Conversion conv(1); + if (conv) + return b.GetIntegerB() - a.GetInteger() + c.GetInteger(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/diamond/Makefile b/packages/Python/lldbsuite/test/lang/cpp/diamond/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/diamond/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/diamond/TestDiamond.py b/packages/Python/lldbsuite/test/lang/cpp/diamond/TestDiamond.py new file mode 100644 index 000000000000..67de03b54b20 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/diamond/TestDiamond.py @@ -0,0 +1,41 @@ +""" +Tests that bool types work +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CPPTestDiamondInheritance(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_with_run_command(self): + """Test that virtual base classes work in when SBValue objects are used to explore the variable value""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + self.set_breakpoint(line_number('main.cpp', '// breakpoint 1')) + self.set_breakpoint(line_number('main.cpp', '// breakpoint 2')) + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + thread = process.GetThreadAtIndex(0) + frame = thread.GetFrameAtIndex(0) + j1 = frame.FindVariable("j1") + j1_Derived1 = j1.GetChildAtIndex(0) + j1_Derived2 = j1.GetChildAtIndex(1) + j1_Derived1_VBase = j1_Derived1.GetChildAtIndex(0) + j1_Derived2_VBase = j1_Derived2.GetChildAtIndex(0) + j1_Derived1_VBase_m_value = j1_Derived1_VBase.GetChildAtIndex(0) + j1_Derived2_VBase_m_value = j1_Derived2_VBase.GetChildAtIndex(0) + self.assertTrue(j1_Derived1_VBase.GetLoadAddress() == j1_Derived2_VBase.GetLoadAddress(), "ensure virtual base class is the same between Derived1 and Derived2") + self.assertTrue(j1_Derived1_VBase_m_value.GetValueAsUnsigned(1) == j1_Derived2_VBase_m_value.GetValueAsUnsigned(2), "ensure m_value in VBase is the same") + self.assertTrue(frame.FindVariable("d").GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned(0) == 12345, "ensure Derived2 from j1 is correct"); + thread.StepOver() + self.assertTrue(frame.FindVariable("d").GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned(0) == 12346, "ensure Derived2 from j2 is correct"); + + def set_breakpoint(self, line): + # Some compilers (for example GCC 4.4.7 and 4.6.1) emit multiple locations for the statement with the ternary + # operator in the test program, while others emit only 1. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=-1, loc_exact=False) diff --git a/packages/Python/lldbsuite/test/lang/cpp/diamond/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/diamond/main.cpp new file mode 100644 index 000000000000..bfe098a089f5 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/diamond/main.cpp @@ -0,0 +1,85 @@ +//===-- 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 + +static int g_next_value = 12345; + +class VBase +{ +public: + VBase() : m_value(g_next_value++) {} + virtual ~VBase() {} + void Print() + { + printf("%p: %s\n%p: m_value = 0x%8.8x\n", this, __PRETTY_FUNCTION__, &m_value, m_value); + } + int m_value; +}; + +class Derived1 : public virtual VBase +{ +public: + Derived1() {}; + void Print () + { + printf("%p: %s\n", this, __PRETTY_FUNCTION__); + VBase::Print(); + } + +}; + +class Derived2 : public virtual VBase +{ +public: + Derived2() {}; + + void Print () + { + printf("%p: %s\n", this, __PRETTY_FUNCTION__); + VBase::Print(); + } +}; + +class Joiner1 : public Derived1, public Derived2 +{ +public: + Joiner1() : + m_joiner1(3456), + m_joiner2(6789) {} + void Print () + { + printf("%p: %s \n%p: m_joiner1 = 0x%8.8x\n%p: m_joiner2 = 0x%8.8x\n", + this, + __PRETTY_FUNCTION__, + &m_joiner1, + m_joiner1, + &m_joiner2, + m_joiner2); + Derived1::Print(); + Derived2::Print(); + } + int m_joiner1; + int m_joiner2; +}; + +class Joiner2 : public Derived2 +{ + int m_stuff[32]; +}; + +int main(int argc, const char * argv[]) +{ + Joiner1 j1; + Joiner2 j2; + j1.Print(); + j2.Print(); + Derived2 *d = &j1; + d = &j2; // breakpoint 1 + return 0; // breakpoint 2 +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/Makefile b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/Makefile new file mode 100644 index 000000000000..8770b2343ef0 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := pass-to-base.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/TestCppValueCast.py b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/TestCppValueCast.py new file mode 100644 index 000000000000..4e23cd899285 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/TestCppValueCast.py @@ -0,0 +1,129 @@ +""" +Test lldb Python API SBValue::Cast(SBType) for C++ types. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class CppValueCastTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @unittest2.expectedFailure("rdar://problem/10808472 SBValue::Cast test case is failing (virtual inheritance)") + @add_test_categories(['pyapi']) + def test_value_cast_with_virtual_inheritance(self): + """Test SBValue::Cast(SBType) API for C++ types with virtual inheritance.""" + self.build(dictionary=self.d_virtual) + self.setTearDownCleanup(dictionary=self.d_virtual) + self.do_sbvalue_cast(self.exe_name) + + @add_test_categories(['pyapi']) + def test_value_cast_with_regular_inheritance(self): + """Test SBValue::Cast(SBType) API for C++ types with regular inheritance.""" + self.build(dictionary=self.d_regular) + self.setTearDownCleanup(dictionary=self.d_regular) + self.do_sbvalue_cast(self.exe_name) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + self.source = 'sbvalue-cast.cpp'; + self.line = line_number(self.source, '// Set breakpoint here.') + self.exe_name = self.testMethodName + self.d_virtual = {'CXX_SOURCES': self.source, 'EXE': self.exe_name, 'CFLAGS_EXTRAS': '-DDO_VIRTUAL_INHERITANCE'} + self.d_regular = {'CXX_SOURCES': self.source, 'EXE': self.exe_name} + + def do_sbvalue_cast (self, exe_name): + """Test SBValue::Cast(SBType) API for C++ types.""" + exe = os.path.join(os.getcwd(), exe_name) + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoints: + + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + # Find DerivedA and DerivedB types. + typeA = target.FindFirstType('DerivedA') + typeB = target.FindFirstType('DerivedB') + self.DebugSBType(typeA) + self.DebugSBType(typeB) + self.assertTrue(typeA) + self.assertTrue(typeB) + error = lldb.SBError() + + # First stop is for DerivedA instance. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue (len(threads) == 1) + thread = threads[0] + frame0 = thread.GetFrameAtIndex(0) + + tellerA = frame0.FindVariable('teller', lldb.eNoDynamicValues) + self.DebugSBValue(tellerA) + self.assertTrue(tellerA.GetChildMemberWithName('m_base_val').GetValueAsUnsigned(error, 0) == 20) + + if self.TraceOn(): + for child in tellerA: + print("child name:", child.GetName()) + print(child) + + # Call SBValue.Cast() to obtain instanceA. + instanceA = tellerA.Cast(typeA.GetPointerType()) + self.DebugSBValue(instanceA) + + # Iterate through all the children and print their values. + if self.TraceOn(): + for child in instanceA: + print("child name:", child.GetName()) + print(child) + a_member_val = instanceA.GetChildMemberWithName('m_a_val') + self.DebugSBValue(a_member_val) + self.assertTrue(a_member_val.GetValueAsUnsigned(error, 0) == 10) + + # Second stop is for DerivedB instance. + threads = lldbutil.continue_to_breakpoint (process, breakpoint) + self.assertTrue (len(threads) == 1) + thread = threads[0] + frame0 = thread.GetFrameAtIndex(0) + + tellerB = frame0.FindVariable('teller', lldb.eNoDynamicValues) + self.DebugSBValue(tellerB) + self.assertTrue(tellerB.GetChildMemberWithName('m_base_val').GetValueAsUnsigned(error, 0) == 12) + + if self.TraceOn(): + for child in tellerB: + print("child name:", child.GetName()) + print(child) + + # Call SBValue.Cast() to obtain instanceB. + instanceB = tellerB.Cast(typeB.GetPointerType()) + self.DebugSBValue(instanceB) + + # Iterate through all the children and print their values. + if self.TraceOn(): + for child in instanceB: + print("child name:", child.GetName()) + print(child) + b_member_val = instanceB.GetChildMemberWithName('m_b_val') + self.DebugSBValue(b_member_val) + self.assertTrue(b_member_val.GetValueAsUnsigned(error, 0) == 36) diff --git a/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/TestDynamicValue.py b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/TestDynamicValue.py new file mode 100644 index 000000000000..56e81c56cfa6 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/TestDynamicValue.py @@ -0,0 +1,223 @@ +""" +Use lldb Python API to test dynamic values in C++ +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class DynamicValueTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + + self.do_something_line = line_number('pass-to-base.cpp', '// Break here in doSomething.') + self.main_first_call_line = line_number('pass-to-base.cpp', + '// Break here and get real addresses of myB and otherB.') + self.main_second_call_line = line_number('pass-to-base.cpp', + '// Break here and get real address of reallyA.') + + @expectedFailureFreeBSD # FIXME: This needs to be root-caused. + @expectedFailureWindows("llvm.org/pr24663") + @add_test_categories(['pyapi']) + def test_get_dynamic_vals(self): + """Test fetching C++ dynamic values from pointers & references.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoints: + + do_something_bpt = target.BreakpointCreateByLocation('pass-to-base.cpp', self.do_something_line) + self.assertTrue(do_something_bpt, + VALID_BREAKPOINT) + + first_call_bpt = target.BreakpointCreateByLocation('pass-to-base.cpp', self.main_first_call_line) + self.assertTrue(first_call_bpt, + VALID_BREAKPOINT) + + second_call_bpt = target.BreakpointCreateByLocation('pass-to-base.cpp', self.main_second_call_line) + self.assertTrue(second_call_bpt, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, first_call_bpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + frame = thread.GetFrameAtIndex(0) + + # Now find the dynamic addresses of myB and otherB so we can compare them + # with the dynamic values we get in doSomething: + + use_dynamic = lldb.eDynamicCanRunTarget + no_dynamic = lldb.eNoDynamicValues + + myB = frame.FindVariable ('myB', no_dynamic); + self.assertTrue (myB) + myB_loc = int (myB.GetLocation(), 16) + + otherB = frame.FindVariable('otherB', no_dynamic) + self.assertTrue (otherB) + otherB_loc = int (otherB.GetLocation(), 16) + + # Okay now run to doSomething: + + threads = lldbutil.continue_to_breakpoint (process, do_something_bpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + frame = thread.GetFrameAtIndex(0) + + # Get "this" using FindVariable: + + this_static = frame.FindVariable ('this', no_dynamic) + this_dynamic = frame.FindVariable ('this', use_dynamic) + self.examine_value_object_of_this_ptr (this_static, this_dynamic, myB_loc) + + # Now make sure that the "GetDynamicValue" works: + # This doesn't work currently because we can't get dynamic values from ConstResult objects. + fetched_dynamic_value = this_static.GetDynamicValue(use_dynamic) + self.examine_value_object_of_this_ptr (this_static, fetched_dynamic_value, myB_loc) + + # And conversely that the GetDynamicValue() interface also works: + fetched_static_value = this_dynamic.GetStaticValue() + self.examine_value_object_of_this_ptr (fetched_static_value, this_dynamic, myB_loc) + + # Get "this" using FindValue, make sure that works too: + this_static = frame.FindValue ('this', lldb.eValueTypeVariableArgument, no_dynamic) + this_dynamic = frame.FindValue ('this', lldb.eValueTypeVariableArgument, use_dynamic) + self.examine_value_object_of_this_ptr (this_static, this_dynamic, myB_loc) + + # Get "this" using the EvaluateExpression: + this_static = frame.EvaluateExpression ('this', False) + this_dynamic = frame.EvaluateExpression ('this', True) + self.examine_value_object_of_this_ptr (this_static, this_dynamic, myB_loc) + + # The "frame var" code uses another path to get into children, so let's + # make sure that works as well: + + self.expect('frame var -d run-target --ptr-depth=2 --show-types anotherA.m_client_A', 'frame var finds its way into a child member', + patterns = ['\(B \*\)']) + + # Now make sure we also get it right for a reference as well: + + anotherA_static = frame.FindVariable ('anotherA', False) + self.assertTrue (anotherA_static) + anotherA_static_addr = int (anotherA_static.GetValue(), 16) + + anotherA_dynamic = frame.FindVariable ('anotherA', True) + self.assertTrue (anotherA_dynamic) + anotherA_dynamic_addr = int (anotherA_dynamic.GetValue(), 16) + anotherA_dynamic_typename = anotherA_dynamic.GetTypeName() + self.assertTrue (anotherA_dynamic_typename.find('B') != -1) + + self.assertTrue(anotherA_dynamic_addr < anotherA_static_addr) + + anotherA_m_b_value_dynamic = anotherA_dynamic.GetChildMemberWithName('m_b_value', True) + self.assertTrue (anotherA_m_b_value_dynamic) + anotherA_m_b_val = int (anotherA_m_b_value_dynamic.GetValue(), 10) + self.assertTrue (anotherA_m_b_val == 300) + + anotherA_m_b_value_static = anotherA_static.GetChildMemberWithName('m_b_value', True) + self.assertFalse (anotherA_m_b_value_static) + + # Okay, now continue again, and when we hit the second breakpoint in main + + threads = lldbutil.continue_to_breakpoint (process, second_call_bpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + frame = thread.GetFrameAtIndex(0) + reallyA_value = frame.FindVariable ('reallyA', False) + self.assertTrue(reallyA_value) + reallyA_loc = int (reallyA_value.GetLocation(), 16) + + # Finally continue to doSomething again, and make sure we get the right value for anotherA, + # which this time around is just an "A". + + threads = lldbutil.continue_to_breakpoint (process, do_something_bpt) + self.assertTrue(len(threads) == 1) + thread = threads[0] + + frame = thread.GetFrameAtIndex(0) + anotherA_value = frame.FindVariable ('anotherA', True) + self.assertTrue(anotherA_value) + anotherA_loc = int (anotherA_value.GetValue(), 16) + self.assertTrue (anotherA_loc == reallyA_loc) + self.assertTrue (anotherA_value.GetTypeName().find ('B') == -1) + + def examine_value_object_of_this_ptr (self, this_static, this_dynamic, dynamic_location): + # Get "this" as its static value + self.assertTrue (this_static) + this_static_loc = int (this_static.GetValue(), 16) + + # Get "this" as its dynamic value + + self.assertTrue (this_dynamic) + this_dynamic_typename = this_dynamic.GetTypeName() + self.assertTrue (this_dynamic_typename.find('B') != -1) + this_dynamic_loc = int (this_dynamic.GetValue(), 16) + + # Make sure we got the right address for "this" + + self.assertTrue (this_dynamic_loc == dynamic_location) + + # And that the static address is greater than the dynamic one + + self.assertTrue (this_static_loc > this_dynamic_loc) + + # Now read m_b_value which is only in the dynamic value: + + use_dynamic = lldb.eDynamicCanRunTarget + no_dynamic = lldb.eNoDynamicValues + + this_dynamic_m_b_value = this_dynamic.GetChildMemberWithName('m_b_value', use_dynamic) + self.assertTrue (this_dynamic_m_b_value) + + m_b_value = int (this_dynamic_m_b_value.GetValue(), 0) + self.assertTrue (m_b_value == 10) + + # Make sure it is not in the static version + + this_static_m_b_value = this_static.GetChildMemberWithName('m_b_value', no_dynamic) + self.assertFalse (this_static_m_b_value) + + # Okay, now let's make sure that we can get the dynamic type of a child element: + + contained_auto_ptr = this_dynamic.GetChildMemberWithName ('m_client_A', use_dynamic) + self.assertTrue (contained_auto_ptr) + contained_b = contained_auto_ptr.GetChildMemberWithName ('_M_ptr', use_dynamic) + if not contained_b: + contained_b = contained_auto_ptr.GetChildMemberWithName ('__ptr_', use_dynamic) + self.assertTrue (contained_b) + + contained_b_static = contained_auto_ptr.GetChildMemberWithName ('_M_ptr', no_dynamic) + if not contained_b_static: + contained_b_static = contained_auto_ptr.GetChildMemberWithName ('__ptr_', no_dynamic) + self.assertTrue (contained_b_static) + + contained_b_addr = int (contained_b.GetValue(), 16) + contained_b_static_addr = int (contained_b_static.GetValue(), 16) + + self.assertTrue (contained_b_addr < contained_b_static_addr) diff --git a/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/pass-to-base.cpp b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/pass-to-base.cpp new file mode 100644 index 000000000000..2bccf3303823 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/pass-to-base.cpp @@ -0,0 +1,69 @@ +#include +#include + +class Extra +{ +public: + Extra (int in_one, int in_two) : m_extra_one(in_one), m_extra_two(in_two) {} + +private: + int m_extra_one; + int m_extra_two; +}; + +class A +{ +public: + A(int value) : m_a_value (value) {} + A(int value, A* client_A) : m_a_value (value), m_client_A (client_A) {} + + virtual ~A() {} + + virtual void + doSomething (A &anotherA) + { + printf ("In A %p doing something with %d.\n", this, m_a_value); + int tmp_value = anotherA.Value(); + printf ("Also have another A at %p: %d.\n", &anotherA, tmp_value); // Break here in doSomething. + } + + int + Value() + { + return m_a_value; + } + +private: + int m_a_value; + std::auto_ptr m_client_A; +}; + +class B : public Extra, public virtual A +{ +public: + B (int b_value, int a_value) : Extra(b_value, a_value), A(a_value), m_b_value(b_value) {} + B (int b_value, int a_value, A *client_A) : Extra(b_value, a_value), A(a_value, client_A), m_b_value(b_value) {} + + virtual ~B () {} + +private: + int m_b_value; +}; + +static A* my_global_A_ptr; + +int +main (int argc, char **argv) +{ + my_global_A_ptr = new B (100, 200); + B myB (10, 20, my_global_A_ptr); + B *second_fake_A_ptr = new B (150, 250); + B otherB (300, 400, second_fake_A_ptr); + + myB.doSomething(otherB); // Break here and get real addresses of myB and otherB. + + A reallyA (500); + myB.doSomething (reallyA); // Break here and get real address of reallyA. + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/sbvalue-cast.cpp b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/sbvalue-cast.cpp new file mode 100644 index 000000000000..00fd7dad438c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/dynamic-value/sbvalue-cast.cpp @@ -0,0 +1,80 @@ +//===-- sbvalue-cast.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifdef DO_VIRTUAL_INHERITANCE +#define VIRTUAL virtual +#else +#define VIRTUAL +#endif + +#include + +class Base +{ +public: + Base(int val) : m_base_val (val) {} + virtual ~Base() {} + + virtual void + forcast(int input) { + int future_val = m_base_val + input * 1; + printf("Forcasting %d\n", future_val); + } + +protected: + int m_base_val; +}; + +class DerivedA : public VIRTUAL Base +{ +public: + DerivedA(int val) : Base(val*2), m_a_val(val) { + printf("DerivedA::ctor()->\n"); + printf("m_base_val=%d\n", m_base_val); + printf("m_a_val=%d\n", m_a_val); + } + virtual ~DerivedA() {} + +private: + int m_a_val; +}; + +class DerivedB : public VIRTUAL Base +{ +public: + DerivedB(int val) : Base(val), m_b_val(val*3) { + printf("DerivedB::ctor()->\n"); + printf("m_base_val=%d\n", m_base_val); + printf("m_b_val=%d\n", m_b_val); + } + virtual ~DerivedB() {} + + virtual void + forcast(int input) { + int future_val = m_b_val + input * 2; + printf("Forcasting %d\n", future_val); + } + +private: + int m_b_val; +}; + +int +main(int argc, char **argv) +{ + DerivedA* dA = new DerivedA(10); + DerivedB* dB = new DerivedB(12); + Base *array[2] = {dA, dB}; + Base *teller = NULL; + for (int i = 0; i < 2; ++i) { + teller = array[i]; + teller->forcast(i); // Set breakpoint here. + } + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/enum_types/Makefile b/packages/Python/lldbsuite/test/lang/cpp/enum_types/Makefile new file mode 100644 index 000000000000..0e0f5d1dad3d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/enum_types/Makefile @@ -0,0 +1,10 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +CXXFLAGS += -std=c++11 + +clean: OBJECTS+=$(wildcard main.d.*) + +include $(LEVEL)/Makefile.rules + diff --git a/packages/Python/lldbsuite/test/lang/cpp/enum_types/TestCPP11EnumTypes.py b/packages/Python/lldbsuite/test/lang/cpp/enum_types/TestCPP11EnumTypes.py new file mode 100644 index 000000000000..51c145c7b7cd --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/enum_types/TestCPP11EnumTypes.py @@ -0,0 +1,110 @@ +"""Look up enum type information and check for correct display.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CPP11EnumTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_int8_t(self): + """Test C++11 enumeration class types as int8_t types.""" + self.build(dictionary={'CFLAGS_EXTRAS': '"-DTEST_BLOCK_CAPTURED_VARS=int8_t"'}) + self.image_lookup_for_enum_type() + + def test_int16_t(self): + """Test C++11 enumeration class types as int16_t types.""" + self.build(dictionary={'CFLAGS_EXTRAS': '"-DTEST_BLOCK_CAPTURED_VARS=int16_t"'}) + self.image_lookup_for_enum_type() + + def test_int32_t(self): + """Test C++11 enumeration class types as int32_t types.""" + self.build(dictionary={'CFLAGS_EXTRAS': '"-DTEST_BLOCK_CAPTURED_VARS=int32_t"'}) + self.image_lookup_for_enum_type() + + def test_int64_t(self): + """Test C++11 enumeration class types as int64_t types.""" + self.build(dictionary={'CFLAGS_EXTRAS': '"-DTEST_BLOCK_CAPTURED_VARS=int64_t"'}) + self.image_lookup_for_enum_type() + + def test_uint8_t(self): + """Test C++11 enumeration class types as uint8_t types.""" + self.build(dictionary={'CFLAGS_EXTRAS': '"-DTEST_BLOCK_CAPTURED_VARS=uint8_t"'}) + self.image_lookup_for_enum_type() + + def test_uint16_t(self): + """Test C++11 enumeration class types as uint16_t types.""" + self.build(dictionary={'CFLAGS_EXTRAS': '"-DTEST_BLOCK_CAPTURED_VARS=uint16_t"'}) + self.image_lookup_for_enum_type() + + def test_uint32_t(self): + """Test C++11 enumeration class types as uint32_t types.""" + self.build(dictionary={'CFLAGS_EXTRAS': '"-DTEST_BLOCK_CAPTURED_VARS=uint32_t"'}) + self.image_lookup_for_enum_type() + + def test_uint64_t(self): + """Test C++11 enumeration class types as uint64_t types.""" + self.build(dictionary={'CFLAGS_EXTRAS': '"-DTEST_BLOCK_CAPTURED_VARS=uint64_t"'}) + self.image_lookup_for_enum_type() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + def image_lookup_for_enum_type(self): + """Test C++11 enumeration class types.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the main. + bkpt_id = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Look up information about the 'DayType' enum type. + # Check for correct display. + self.expect("image lookup -t DayType", DATA_TYPES_DISPLAYED_CORRECTLY, + substrs = ['enum DayType {', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + 'kNumDays', + '}']) + + enum_values = [ '-4', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + 'kNumDays', + '5']; + + bkpt = self.target().FindBreakpointByID(bkpt_id) + for enum_value in enum_values: + self.expect("frame variable day", 'check for valid enumeration value', + substrs = [enum_value]) + lldbutil.continue_to_breakpoint (self.process(), bkpt) diff --git a/packages/Python/lldbsuite/test/lang/cpp/enum_types/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/enum_types/main.cpp new file mode 100644 index 000000000000..8595050afbbf --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/enum_types/main.cpp @@ -0,0 +1,33 @@ +//===-- 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 + + +int main (int argc, char const *argv[]) +{ + typedef int16_t enum_integer_t; + enum class DayType : enum_integer_t { + Monday = -3, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday, + kNumDays + }; + enum_integer_t day_value; + for (day_value = (enum_integer_t)DayType::Monday - 1; day_value <= (enum_integer_t)DayType::kNumDays + 1; ++day_value) + { + DayType day = (DayType)day_value; + printf("day as int is %i\n", (int)day); // Set break point at this line. + } + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/exceptions/Makefile b/packages/Python/lldbsuite/test/lang/cpp/exceptions/Makefile new file mode 100644 index 000000000000..a6bd8463ad54 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/exceptions/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := exceptions.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/exceptions/TestCPPExceptionBreakpoints.py b/packages/Python/lldbsuite/test/lang/cpp/exceptions/TestCPPExceptionBreakpoints.py new file mode 100644 index 000000000000..65cf0b361d84 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/exceptions/TestCPPExceptionBreakpoints.py @@ -0,0 +1,65 @@ +""" +Test lldb exception breakpoint command for CPP. +""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class CPPBreakpointTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + self.source = 'exceptions.cpp' + self.catch_line = line_number(self.source, '// This is the line you should stop at for catch') + + @expectedFailureWindows("llvm.org/pr24538") # clang-cl does not support throw or catch + def test(self): + """Test lldb exception breakpoint command for CPP.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + exception_bkpt = target.BreakpointCreateForException (lldb.eLanguageTypeC_plus_plus, True, True) + self.assertTrue (exception_bkpt, "Made an exception breakpoint") + + # Now run, and make sure we hit our breakpoint: + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue (process, "Got a valid process") + + stopped_threads = [] + stopped_threads = lldbutil.get_threads_stopped_at_breakpoint (process, exception_bkpt) + self.assertTrue (len(stopped_threads) == 1, "Stopped at our exception breakpoint.") + thread = stopped_threads[0] + # Make sure our throw function is still above us on the stack: + + frame_functions = lldbutil.get_function_names(thread) + self.assertTrue (frame_functions.count ("throws_exception_on_even(int)") == 1, "Our throw function is still on the stack.") + + # Okay we hit our exception throw breakpoint, now make sure we get our catch breakpoint. + # One potential complication is that we might hit a couple of the exception breakpoints in getting out of the throw. + # so loop till we don't see the throws function on the stack. We should stop one more time for our exception breakpoint + # and that should be the catch... + + while frame_functions.count ("throws_exception_on_even(int)") == 1: + stopped_threads = lldbutil.continue_to_breakpoint (process, exception_bkpt) + self.assertTrue (len(stopped_threads) == 1) + + thread = stopped_threads[0] + frame_functions = lldbutil.get_function_names(thread) + + self.assertTrue (frame_functions.count ("throws_exception_on_even(int)") == 0, "At catch our throw function is off the stack") + self.assertTrue (frame_functions.count ("intervening_function(int)") == 0, "At catch our intervening function is off the stack") + self.assertTrue (frame_functions.count ("catches_exception(int)") == 1, "At catch our catch function is on the stack") diff --git a/packages/Python/lldbsuite/test/lang/cpp/exceptions/exceptions.cpp b/packages/Python/lldbsuite/test/lang/cpp/exceptions/exceptions.cpp new file mode 100644 index 000000000000..150d420b241b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/exceptions/exceptions.cpp @@ -0,0 +1,42 @@ +#include +#include + +int throws_exception_on_even (int value); +int intervening_function (int value); +int catches_exception (int value); + +int +catches_exception (int value) +{ + try + { + return intervening_function(value); // This is the line you should stop at for catch + } + catch (int value) + { + return value; + } +} + +int +intervening_function (int value) +{ + return throws_exception_on_even (2 * value); +} + +int +throws_exception_on_even (int value) +{ + printf ("Mod two works: %d.\n", value%2); + if (value % 2 == 0) + throw 30; + else + return value; +} + +int +main () +{ + catches_exception (10); // Stop here + return 5; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/Makefile b/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/TestFrameVariableAnonymousUnions.py b/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/TestFrameVariableAnonymousUnions.py new file mode 100644 index 000000000000..396a637041cb --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/TestFrameVariableAnonymousUnions.py @@ -0,0 +1,27 @@ +""" +Tests that frame variable looks into anonymous unions +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class FrameVariableAnonymousUnionsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_with_run_command(self): + """Tests that frame variable looks into anonymous unions""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + line = line_number('main.cpp', '// break here') + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=-1, loc_exact=False) + + self.runCmd("process launch", RUN_SUCCEEDED) + + self.expect('frame variable -f x i', substrs=['ffffff41']) + self.expect('frame variable c', substrs=["'A"]) + + self.expect('frame variable x', matching=False, substrs=['3']) + self.expect('frame variable y', matching=False, substrs=["'B'"]) + self.expect('frame variable z', matching=False, substrs=['14']) diff --git a/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/main.cpp new file mode 100644 index 000000000000..494b846336ff --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/frame-var-anon-unions/main.cpp @@ -0,0 +1,23 @@ +//===-- 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() { + union { + int i; + char c; + }; + struct { + int x; + char y; + short z; + } s{3,'B',14}; + i = 0xFFFFFF00; + c = 'A'; + return c; // break here +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/global_operators/Makefile b/packages/Python/lldbsuite/test/lang/cpp/global_operators/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/global_operators/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/global_operators/TestCppGlobalOperators.py b/packages/Python/lldbsuite/test/lang/cpp/global_operators/TestCppGlobalOperators.py new file mode 100644 index 000000000000..de56cd6a6c49 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/global_operators/TestCppGlobalOperators.py @@ -0,0 +1,54 @@ +""" +Test that global operators are found and evaluated. +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestCppGlobalOperators(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr21765") + def test_with_run_command(self): + self.build() + + # Get main source file + src_file = "main.cpp" + src_file_spec = lldb.SBFileSpec(src_file) + self.assertTrue(src_file_spec.IsValid(), "Main source file") + + # Get the path of the executable + cwd = os.getcwd() + exe_file = "a.out" + exe_path = os.path.join(cwd, exe_file) + + # Load the executable + target = self.dbg.CreateTarget(exe_path) + self.assertTrue(target.IsValid(), VALID_TARGET) + + # Break on main function + main_breakpoint = target.BreakpointCreateBySourceRegex("// break here", src_file_spec) + self.assertTrue(main_breakpoint.IsValid() and main_breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT) + + # Launch the process + args = None + env = None + process = target.LaunchSimple(args, env, self.get_process_working_directory()) + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + + # Get the thread of the process + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + + # Check if global operators are evaluated + frame = thread.GetSelectedFrame() + + test_result = frame.EvaluateExpression("operator==(s1, s2)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "operator==(s1, s2) = false") + + test_result = frame.EvaluateExpression("operator==(s1, s3)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "true", "operator==(s1, s3) = true") + + test_result = frame.EvaluateExpression("operator==(s2, s3)") + self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "operator==(s2, s3) = false") diff --git a/packages/Python/lldbsuite/test/lang/cpp/global_operators/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/global_operators/main.cpp new file mode 100644 index 000000000000..a0dd0787fa32 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/global_operators/main.cpp @@ -0,0 +1,16 @@ +struct Struct { + int value; +}; + +bool operator==(const Struct &a, const Struct &b) { + return a.value == b.value; +} + +int main() { + Struct s1, s2, s3; + s1.value = 3; + s2.value = 5; + s3.value = 3; + return 0; // break here +} + diff --git a/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/Makefile b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/Makefile new file mode 100644 index 000000000000..6595e33b7269 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/Makefile @@ -0,0 +1,35 @@ +LEVEL = ../../../make + +CXX_SOURCES = main.cpp length.cpp a.cpp + +CFLAGS_LIMIT = -c $(CXXFLAGS) +CFLAGS_NO_LIMIT = -c $(CXXFLAGS) + +ifneq (,$(findstring clang,$(CC))) + CFLAGS_LIMIT += -flimit-debug-info + CFLAGS_NO_LIMIT += -fno-limit-debug-info +endif + +all: limit nolimit + +limit: main.o length_limit.o a.o + $(CXX) $(LDFLAGS) main.o length_limit.o a.o -o limit + +nolimit: main.o length_nolimit.o a.o + $(CXX) $(LDFLAGS) main.o length_nolimit.o a.o -o nolimit + +main.o: main.cpp + $(CXX) $(CFLAGS_LIMIT) main.cpp -o main.o + +length_limit.o: length.cpp + $(CXX) $(CFLAGS_LIMIT) length.cpp -o length_limit.o + +length_nolimit.o: length.cpp + $(CXX) $(CFLAGS_NO_LIMIT) length.cpp -o length_nolimit.o + +a.o: a.cpp + $(CXX) $(CFLAGS_NO_DEBUG) -c a.cpp -o a.o + +clean: OBJECTS += limit nolimit length_limit.o length_nolimit.o length_limit.dwo length_nolimit.dwo + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/TestCppIncompleteTypes.py b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/TestCppIncompleteTypes.py new file mode 100644 index 000000000000..324f476efb9b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/TestCppIncompleteTypes.py @@ -0,0 +1,66 @@ +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestCppIncompleteTypes(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureFreeBSD("llvm.org/pr25626 test executable not built correctly on FreeBSD") + @skipIfGcc + def test_limit_debug_info(self): + self.build() + frame = self.get_test_frame('limit') + + value_f = frame.EvaluateExpression("f") + self.assertTrue(value_f.IsValid(), "'expr f' results in a valid SBValue object") + self.assertTrue(value_f.GetError().Success(), "'expr f' is successful") + + value_a = frame.EvaluateExpression("a") + self.assertTrue(value_a.IsValid(), "'expr a' results in a valid SBValue object") + self.assertTrue(value_a.GetError().Success(), "'expr a' is successful") + + @skipIfGcc + @skipIfWindows # Clang on Windows asserts in external record layout in this case. + def test_partial_limit_debug_info(self): + self.build() + frame = self.get_test_frame('nolimit') + + value_f = frame.EvaluateExpression("f") + self.assertTrue(value_f.IsValid(), "'expr f' results in a valid SBValue object") + self.assertTrue(value_f.GetError().Success(), "'expr f' is successful") + + value_a = frame.EvaluateExpression("a") + self.assertTrue(value_a.IsValid(), "'expr a' results in a valid SBValue object") + self.assertTrue(value_a.GetError().Success(), "'expr a' is successful") + + def get_test_frame(self, exe): + # Get main source file + src_file = "main.cpp" + src_file_spec = lldb.SBFileSpec(src_file) + self.assertTrue(src_file_spec.IsValid(), "Main source file") + + # Get the path of the executable + cwd = os.getcwd() + exe_path = os.path.join(cwd, exe) + + # Load the executable + target = self.dbg.CreateTarget(exe_path) + self.assertTrue(target.IsValid(), VALID_TARGET) + + # Break on main function + main_breakpoint = target.BreakpointCreateBySourceRegex("break here", src_file_spec) + self.assertTrue(main_breakpoint.IsValid() and main_breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT) + + # Launch the process + args = None + env = None + process = target.LaunchSimple(args, env, self.get_process_working_directory()) + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + + # Get the thread of the process + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + + # Get frame for current thread + return thread.GetSelectedFrame() diff --git a/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/a.cpp b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/a.cpp new file mode 100644 index 000000000000..36b374be6f33 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/a.cpp @@ -0,0 +1,10 @@ + +#include "a.h" + +A::A () { } + +int +A::length () +{ + return 123; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/a.h b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/a.h new file mode 100644 index 000000000000..13e9496e3fd7 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/a.h @@ -0,0 +1,11 @@ +#ifndef __A_H__ +#define __A_H__ + +class A +{ +public: + A(); + virtual int length(); +}; + +#endif diff --git a/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/length.cpp b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/length.cpp new file mode 100644 index 000000000000..90a3b640f732 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/length.cpp @@ -0,0 +1,8 @@ + +#include "length.h" + +int +length (A &a) +{ + return a.length(); +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/length.h b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/length.h new file mode 100644 index 000000000000..96df4f021808 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/length.h @@ -0,0 +1,8 @@ +#ifndef __LENGTH_H__ +#define __LENGTH_H__ + +#include "a.h" + +int length (A &a); + +#endif diff --git a/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/main.cpp new file mode 100644 index 000000000000..ad324c905818 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/main.cpp @@ -0,0 +1,18 @@ + +#include "length.h" + +class Foo { +public: + A a; +}; + +class MyA : public A { +}; + +int main() +{ + Foo f; + MyA a; + + return length(a); // break here +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/Makefile b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/Makefile new file mode 100644 index 000000000000..b9a3d3fe1c81 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES = main.cpp derived.cpp base.cpp + +CFLAGS_EXTRAS += $(LIMIT_DEBUG_INFO_FLAGS) + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/TestWithLimitDebugInfo.py b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/TestWithLimitDebugInfo.py new file mode 100644 index 000000000000..ec26f9efe907 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/TestWithLimitDebugInfo.py @@ -0,0 +1,48 @@ +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestWithLimitDebugInfo(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIf(debug_info=not_in(["dwarf"])) + def test_limit_debug_info(self): + self.build() + + cwd = os.getcwd() + + src_file = os.path.join(cwd, "main.cpp") + src_file_spec = lldb.SBFileSpec(src_file) + self.assertTrue(src_file_spec.IsValid(), "breakpoint file") + + # Get the path of the executable + exe_path = os.path.join(cwd, 'a.out') + + # Load the executable + target = self.dbg.CreateTarget(exe_path) + self.assertTrue(target.IsValid(), VALID_TARGET) + + # Break on main function + breakpoint = target.BreakpointCreateBySourceRegex("break here", src_file_spec) + self.assertTrue(breakpoint.IsValid() and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT) + + # Launch the process + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + + # Get the thread of the process + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + thread.StepInto() + + # Get frame for current thread + frame = thread.GetSelectedFrame() + + v1 = frame.EvaluateExpression("1") + self.assertTrue(v1.IsValid(), "'expr 1' results in a valid SBValue object") + self.assertTrue(v1.GetError().Success(), "'expr 1' succeeds without an error.") + + v2 = frame.EvaluateExpression("this") + self.assertTrue(v2.IsValid(), "'expr this' results in a valid SBValue object") + self.assertTrue(v2.GetError().Success(), "'expr this' succeeds without an error.") diff --git a/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/base.cpp b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/base.cpp new file mode 100644 index 000000000000..4023bdbc64af --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/base.cpp @@ -0,0 +1,6 @@ +#include "base.h" + +void FooNS::bar() { + x = 54321; +} + diff --git a/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/base.h b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/base.h new file mode 100644 index 000000000000..d3a09572bd25 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/base.h @@ -0,0 +1,10 @@ +class FooNS +{ +public: + virtual void bar(); + virtual char baz() = 0; + +protected: + int x; +}; + diff --git a/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/derived.cpp b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/derived.cpp new file mode 100644 index 000000000000..9d773593eb51 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/derived.cpp @@ -0,0 +1,6 @@ +#include "derived.h" + +char Foo::baz() { + return (char)(x&0xff); +} + diff --git a/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/derived.h b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/derived.h new file mode 100644 index 000000000000..46b3f83b9f74 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/derived.h @@ -0,0 +1,13 @@ +#include "base.h" + +class Foo : public FooNS +{ +public: + Foo() { + a = 12345; + } + + char baz() override; + int a; +}; + diff --git a/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/main.cpp new file mode 100644 index 000000000000..64e0349b5826 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/limit-debug-info/main.cpp @@ -0,0 +1,7 @@ +#include "derived.h" + +int main() { + Foo f; // break here + f.bar(); + return f.baz(); +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile b/packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile new file mode 100644 index 000000000000..7dd5eb4c11f1 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp ns.cpp ns2.cpp ns3.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespace.py b/packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespace.py new file mode 100644 index 000000000000..a60d8252e9f0 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespace.py @@ -0,0 +1,125 @@ +""" +Test the printing of anonymous and named namespace variables. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class NamespaceTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for declarations of namespace variables i and j. + self.line_var_i = line_number('main.cpp', + '// Find the line number for anonymous namespace variable i.') + self.line_var_j = line_number('main.cpp', + '// Find the line number for named namespace variable j.') + # And the line number to break at. + self.line_break = line_number('main.cpp', + '// Set break point at this line.') + # Break inside do {} while and evaluate value + self.line_break_ns1 = line_number('main.cpp', '// Evaluate ns1::value') + self.line_break_ns2 = line_number('main.cpp', '// Evaluate ns2::value') + + def runToBkpt(self, command): + self.runCmd(command, RUN_SUCCEEDED) + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # rdar://problem/8668674 + @expectedFailureWindows("llvm.org/pr24764") + def test_with_run_command(self): + """Test that anonymous and named namespace variables display correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line_break_ns1, num_expected_locations=1, loc_exact=True) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line_break_ns2, num_expected_locations=1, loc_exact=True) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line_break, num_expected_locations=1, loc_exact=True) + + self.runToBkpt("run") + # Evaluate ns1::value + self.expect("expression -- value", startstr = "(int) $0 = 100") + + self.runToBkpt("continue") + # Evaluate ns2::value + self.expect("expression -- value", startstr = "(int) $1 = 200") + + self.runToBkpt("continue") + # On Mac OS X, gcc 4.2 emits the wrong debug info with respect to types. + slist = ['(int) a = 12', 'anon_uint', 'a_uint', 'b_uint', 'y_uint'] + if self.platformIsDarwin() and self.getCompiler() in ['clang', 'llvm-gcc']: + slist = ['(int) a = 12', + '::my_uint_t', 'anon_uint = 0', + '(A::uint_t) a_uint = 1', + '(A::B::uint_t) b_uint = 2', + '(Y::uint_t) y_uint = 3'] + + # 'frame variable' displays the local variables with type information. + self.expect('frame variable', VARIABLES_DISPLAYED_CORRECTLY, + substrs = slist) + + # 'frame variable' with basename 'i' should work. + self.expect("frame variable --show-declaration --show-globals i", + startstr = "main.cpp:%d: (int) (anonymous namespace)::i = 3" % self.line_var_i) + # main.cpp:12: (int) (anonymous namespace)::i = 3 + + # 'frame variable' with basename 'j' should work, too. + self.expect("frame variable --show-declaration --show-globals j", + startstr = "main.cpp:%d: (int) A::B::j = 4" % self.line_var_j) + # main.cpp:19: (int) A::B::j = 4 + + # 'frame variable' should support address-of operator. + self.runCmd("frame variable &i") + + # 'frame variable' with fully qualified name 'A::B::j' should work. + self.expect("frame variable A::B::j", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(int) A::B::j = 4', + patterns = [' = 4']) + + # So should the anonymous namespace case. + self.expect("frame variable '(anonymous namespace)::i'", VARIABLES_DISPLAYED_CORRECTLY, + startstr = '(int) (anonymous namespace)::i = 3', + patterns = [' = 3']) + + # rdar://problem/8660275 + # test/namespace: 'expression -- i+j' not working + # This has been fixed. + self.expect("expression -- i + j", + startstr = "(int) $2 = 7") + # (int) $2 = 7 + + self.runCmd("expression -- i") + self.runCmd("expression -- j") + + # rdar://problem/8668674 + # expression command with fully qualified namespace for a variable does not work + self.expect("expression -- ::i", VARIABLES_DISPLAYED_CORRECTLY, + patterns = [' = 3']) + self.expect("expression -- A::B::j", VARIABLES_DISPLAYED_CORRECTLY, + patterns = [' = 4']) + + # expression command with function in anonymous namespace + self.expect("expression -- myanonfunc(3)", + patterns = [' = 6']) + + # global namespace qualification with function in anonymous namespace + self.expect("expression -- ::myanonfunc(4)", + patterns = [' = 8']) + + self.expect("p myanonfunc", + patterns = ['\(anonymous namespace\)::myanonfunc\(int\)']) + + self.expect("p variadic_sum", + patterns = ['\(anonymous namespace\)::variadic_sum\(int, ...\)']) diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespaceLookup.py b/packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespaceLookup.py new file mode 100644 index 000000000000..4cad45564c87 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespaceLookup.py @@ -0,0 +1,223 @@ +""" +Test the printing of anonymous and named namespace variables. +""" + +from __future__ import print_function + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class NamespaceLookupTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Break inside different scopes and evaluate value + self.line_break_global_scope = line_number('ns.cpp', '// BP_global_scope') + self.line_break_file_scope = line_number('ns2.cpp', '// BP_file_scope') + self.line_break_ns_scope = line_number('ns2.cpp', '// BP_ns_scope') + self.line_break_nested_ns_scope = line_number('ns2.cpp', '// BP_nested_ns_scope') + self.line_break_nested_ns_scope_after_using = line_number('ns2.cpp', '// BP_nested_ns_scope_after_using') + self.line_break_before_using_directive = line_number('ns3.cpp', '// BP_before_using_directive') + self.line_break_after_using_directive = line_number('ns3.cpp', '// BP_after_using_directive') + + def runToBkpt(self, command): + self.runCmd(command, RUN_SUCCEEDED) + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + @expectedFailureFreeBSD("llvm.org/pr25819") + @expectedFailureLinux("llvm.org/pr25819") + def test_scope_lookup_with_run_command(self): + """Test scope lookup of functions in lldb.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "ns.cpp", self.line_break_global_scope, num_expected_locations=1, loc_exact=False) + lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_ns_scope, num_expected_locations=1, loc_exact=False) + lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_nested_ns_scope, num_expected_locations=1, loc_exact=False) + lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_nested_ns_scope_after_using, num_expected_locations=1, loc_exact=False) + lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_before_using_directive, num_expected_locations=1, loc_exact=False) + lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_after_using_directive, num_expected_locations=1, loc_exact=False) + + # Run to BP_global_scope at global scope + self.runToBkpt("run") + # Evaluate func() - should call ::func() + self.expect("expr -- func()", startstr = "(int) $0 = 1") + # Evaluate A::B::func() - should call A::B::func() + self.expect("expr -- A::B::func()", startstr = "(int) $1 = 4") + # Evaluate func(10) - should call ::func(int) + self.expect("expr -- func(10)", startstr = "(int) $2 = 11") + # Evaluate ::func() - should call A::func() + self.expect("expr -- ::func()", startstr = "(int) $3 = 1") + # Evaluate A::foo() - should call A::foo() + self.expect("expr -- A::foo()", startstr = "(int) $4 = 42") + + # Continue to BP_ns_scope at ns scope + self.runToBkpt("continue") + # Evaluate func(10) - should call A::func(int) + self.expect("expr -- func(10)", startstr = "(int) $5 = 13") + # Evaluate B::func() - should call B::func() + self.expect("expr -- B::func()", startstr = "(int) $6 = 4") + # Evaluate func() - should call A::func() + self.expect("expr -- func()", startstr = "(int) $7 = 3") + + # Continue to BP_nested_ns_scope at nested ns scope + self.runToBkpt("continue") + # Evaluate func() - should call A::B::func() + self.expect("expr -- func()", startstr = "(int) $8 = 4") + # Evaluate A::func() - should call A::func() + self.expect("expr -- A::func()", startstr = "(int) $9 = 3") + + # Evaluate func(10) - should call A::func(10) + # NOTE: Under the rules of C++, this test would normally get an error + # because A::B::func() hides A::func(), but lldb intentionally + # disobeys these rules so that the intended overload can be found + # by only removing duplicates if they have the same type. + self.expect("expr -- func(10)", startstr = "(int) $10 = 13") + + # Continue to BP_nested_ns_scope_after_using at nested ns scope after using declaration + self.runToBkpt("continue") + # Evaluate A::func(10) - should call A::func(int) + self.expect("expr -- A::func(10)", startstr = "(int) $11 = 13") + + # Continue to BP_before_using_directive at global scope before using declaration + self.runToBkpt("continue") + # Evaluate ::func() - should call ::func() + self.expect("expr -- ::func()", startstr = "(int) $12 = 1") + # Evaluate B::func() - should call B::func() + self.expect("expr -- B::func()", startstr = "(int) $13 = 4") + + # Continue to BP_after_using_directive at global scope after using declaration + self.runToBkpt("continue") + # Evaluate ::func() - should call ::func() + self.expect("expr -- ::func()", startstr = "(int) $14 = 1") + # Evaluate B::func() - should call B::func() + self.expect("expr -- B::func()", startstr = "(int) $15 = 4") + + @unittest2.expectedFailure("lldb scope lookup of functions bugs") + def test_function_scope_lookup_with_run_command(self): + """Test scope lookup of functions in lldb.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "ns.cpp", self.line_break_global_scope, num_expected_locations=1, loc_exact=False) + lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_ns_scope, num_expected_locations=1, loc_exact=False) + + # Run to BP_global_scope at global scope + self.runToBkpt("run") + # Evaluate foo() - should call ::foo() + # FIXME: lldb finds Y::foo because lookup for variables is done + # before functions. + self.expect("expr -- foo()", startstr = "(int) $0 = 42") + # Evaluate ::foo() - should call ::foo() + # FIXME: lldb finds Y::foo because lookup for variables is done + # before functions and :: is ignored. + self.expect("expr -- ::foo()", startstr = "(int) $1 = 42") + + # Continue to BP_ns_scope at ns scope + self.runToBkpt("continue") + # Evaluate foo() - should call A::foo() + # FIXME: lldb finds Y::foo because lookup for variables is done + # before functions. + self.expect("expr -- foo()", startstr = "(int) $2 = 42") + + @unittest2.expectedFailure("lldb file scope lookup bugs") + def test_file_scope_lookup_with_run_command(self): + """Test file scope lookup in lldb.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_file_scope, num_expected_locations=1, loc_exact=False) + + # Run to BP_file_scope at file scope + self.runToBkpt("run") + # Evaluate func() - should call static ns2.cpp:func() + # FIXME: This test fails because lldb doesn't know about file scopes so + # finds the global ::func(). + self.expect("expr -- func()", startstr = "(int) $0 = 2") + + @expectedFailureLinux("llvm.org/pr25819") + def test_scope_lookup_before_using_with_run_command(self): + """Test scope lookup before using in lldb.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_before_using_directive, num_expected_locations=1, loc_exact=False) + + # Run to BP_before_using_directive at global scope before using declaration + self.runToBkpt("run") + # Evaluate func() - should call ::func() + self.expect("expr -- func()", startstr = "(int) $0 = 1") + + # NOTE: this test may fail on older systems that don't emit import + # emtries in DWARF - may need to add checks for compiler versions here. + @expectedFailureFreeBSD("llvm.org/pr25819") + @expectedFailureLinux("llvm.org/pr25819") + def test_scope_after_using_directive_lookup_with_run_command(self): + """Test scope lookup after using directive in lldb.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_after_using_directive, num_expected_locations=1, loc_exact=False) + + # Run to BP_after_using_directive at global scope after using declaration + self.runToBkpt("run") + # Evaluate func2() - should call A::func2() + self.expect("expr -- func2()", startstr = "(int) $0 = 3") + + @unittest2.expectedFailure("lldb scope lookup after using declaration bugs") + # NOTE: this test may fail on older systems that don't emit import + # emtries in DWARF - may need to add checks for compiler versions here. + def test_scope_after_using_declaration_lookup_with_run_command(self): + """Test scope lookup after using declaration in lldb.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_nested_ns_scope_after_using, num_expected_locations=1, loc_exact=False) + + # Run to BP_nested_ns_scope_after_using at nested ns scope after using declaration + self.runToBkpt("run") + # Evaluate func() - should call A::func() + self.expect("expr -- func()", startstr = "(int) $0 = 3") + + @unittest2.expectedFailure("lldb scope lookup ambiguity after using bugs") + def test_scope_ambiguity_after_using_lookup_with_run_command(self): + """Test scope lookup ambiguity after using in lldb.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_after_using_directive, num_expected_locations=1, loc_exact=False) + + # Run to BP_after_using_directive at global scope after using declaration + self.runToBkpt("run") + # Evaluate func() - should get error: ambiguous + # FIXME: This test fails because lldb removes duplicates if they have + # the same type. + self.expect("expr -- func()", startstr = "error") + + @expectedFailureFreeBSD("llvm.org/pr25819") + @expectedFailureLinux("llvm.org/pr25819") + def test_scope_lookup_shadowed_by_using_with_run_command(self): + """Test scope lookup shadowed by using in lldb.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_nested_ns_scope, num_expected_locations=1, loc_exact=False) + + # Run to BP_nested_ns_scope at nested ns scope + self.runToBkpt("run") + # Evaluate func(10) - should call A::func(10) + # NOTE: Under the rules of C++, this test would normally get an error + # because A::B::func() shadows A::func(), but lldb intentionally + # disobeys these rules so that the intended overload can be found + # by only removing duplicates if they have the same type. + self.expect("expr -- func(10)", startstr = "(int) $0 = 13") + diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/cmds.txt b/packages/Python/lldbsuite/test/lang/cpp/namespace/cmds.txt new file mode 100644 index 000000000000..76bb1bcba759 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/cmds.txt @@ -0,0 +1,3 @@ +b main.cpp:54 +c +var diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/namespace/main.cpp new file mode 100644 index 000000000000..560ec40f4733 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/main.cpp @@ -0,0 +1,124 @@ +//===-- 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 "ns.h" + +namespace { + typedef unsigned int my_uint_t; + int i; // Find the line number for anonymous namespace variable i. + + int myanonfunc (int a) + { + return a + a; + } + + int + variadic_sum (int arg_count...) + { + int sum = 0; + va_list args; + va_start(args, arg_count); + + for (int i = 0; i < arg_count; i++) + sum += va_arg(args, int); + + va_end(args); + return sum; + } +} + +namespace A { + typedef unsigned int uint_t; + namespace B { + typedef unsigned int uint_t; + int j; // Find the line number for named namespace variable 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; + } +} + +namespace ns1 { + int value = 100; +} + +namespace ns2 { + int value = 200; +} + +void test_namespace_scopes() { + do { + using namespace ns1; + printf("ns1::value = %d\n", value); // Evaluate ns1::value + } while(0); + + do { + using namespace ns2; + printf("ns2::value = %d\n", value); // Evaluate ns2::value + } while(0); +} + +int Foo::myfunc(int a) +{ + test_namespace_scopes(); + + ::my_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; + printf("::i=%d\n", ::i); + printf("A::B::j=%d\n", A::B::j); + printf("variadic_sum=%d\n", variadic_sum(3, 1, 2, 3)); + myanonfunc(3); + return myfunc2(3) + j + i + a + 2 + anon_uint + a_uint + b_uint + y_uint; // Set break point at this line. +} + +int +main (int argc, char const *argv[]) +{ + test_lookup_at_global_scope(); + test_lookup_at_file_scope(); + A::test_lookup_at_ns_scope(); + A::B::test_lookup_at_nested_ns_scope(); + A::B::test_lookup_at_nested_ns_scope_after_using(); + test_lookup_before_using_directive(); + test_lookup_after_using_directive(); + return Foo::myfunc(12); +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/ns.cpp b/packages/Python/lldbsuite/test/lang/cpp/namespace/ns.cpp new file mode 100644 index 000000000000..9e5637d02b4b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/ns.cpp @@ -0,0 +1,32 @@ +//===-- ns.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ns.h" + +int foo() +{ + printf("global foo()\n"); + return 42; +} +int func() +{ + printf("global func()\n"); + return 1; +} +int func(int a) +{ + printf("global func(int)\n"); + return a + 1; +} +void test_lookup_at_global_scope() +{ + // BP_global_scope + printf("at global scope: foo() = %d\n", foo()); // eval foo(), exp: 42 + printf("at global scope: func() = %d\n", func()); // eval func(), exp: 1 +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/ns.h b/packages/Python/lldbsuite/test/lang/cpp/namespace/ns.h new file mode 100644 index 000000000000..a07b600efa30 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/ns.h @@ -0,0 +1,36 @@ +//===-- ns.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +void test_lookup_at_global_scope(); +void test_lookup_at_file_scope(); +void test_lookup_before_using_directive(); +void test_lookup_after_using_directive(); +int func(int a); +namespace A { + int foo(); + int func(int a); + inline int func() + { + printf("A::func()\n"); + return 3; + } + inline int func2() + { + printf("A::func2()\n"); + return 3; + } + void test_lookup_at_ns_scope(); + namespace B { + int func(); + void test_lookup_at_nested_ns_scope(); + void test_lookup_at_nested_ns_scope_after_using(); + } +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp b/packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp new file mode 100644 index 000000000000..04046ad9b7f4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp @@ -0,0 +1,65 @@ +//===-- ns2.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ns.h" + +static int func() +{ + printf("static m2.cpp func()\n"); + return 2; +} +void test_lookup_at_file_scope() +{ + // BP_file_scope + printf("at file scope: func() = %d\n", func()); // eval func(), exp: 2 + printf("at file scope: func(10) = %d\n", func(10)); // eval func(10), exp: 11 +} +namespace A { + namespace B { + int func() + { + printf("A::B::func()\n"); + return 4; + } + void test_lookup_at_nested_ns_scope() + { + // BP_nested_ns_scope + printf("at nested ns scope: func() = %d\n", func()); // eval func(), exp: 4 + + //printf("func(10) = %d\n", func(10)); // eval func(10), exp: 13 + // NOTE: Under the rules of C++, this test would normally get an error + // because A::B::func() hides A::func(), but lldb intentionally + // disobeys these rules so that the intended overload can be found + // by only removing duplicates if they have the same type. + } + void test_lookup_at_nested_ns_scope_after_using() + { + // BP_nested_ns_scope_after_using + using A::func; + printf("at nested ns scope after using: func() = %d\n", func()); // eval func(), exp: 3 + } + } +} +int A::foo() +{ + printf("A::foo()\n"); + return 42; +} +int A::func(int a) +{ + printf("A::func(int)\n"); + return a + 3; +} +void A::test_lookup_at_ns_scope() +{ + // BP_ns_scope + printf("at nested ns scope: func() = %d\n", func()); // eval func(), exp: 3 + printf("at nested ns scope: func(10) = %d\n", func(10)); // eval func(10), exp: 13 + printf("at nested ns scope: foo() = %d\n", foo()); // eval foo(), exp: 42 +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp b/packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp new file mode 100644 index 000000000000..10b0df784225 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp @@ -0,0 +1,27 @@ +//===-- ns3.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ns.h" +extern int func(); + +// Note: the following function must be before the using. +void test_lookup_before_using_directive() +{ + // BP_before_using_directive + printf("before using directive: func() = %d\n", func()); // eval func(), exp: 1 +} +using namespace A; +void test_lookup_after_using_directive() +{ + // BP_after_using_directive + //printf("func() = %d\n", func()); // eval func(), exp: error, amiguous + printf("after using directive: func2() = %d\n", func2()); // eval func2(), exp: 3 + printf("after using directive: ::func() = %d\n", ::func()); // eval ::func(), exp: 1 + printf("after using directive: B::func() = %d\n", B::func()); // eval B::func(), exp: 4 +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/nsimport/Makefile b/packages/Python/lldbsuite/test/lang/cpp/nsimport/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/nsimport/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/nsimport/TestCppNsImport.py b/packages/Python/lldbsuite/test/lang/cpp/nsimport/TestCppNsImport.py new file mode 100644 index 000000000000..97e9e27a6e98 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/nsimport/TestCppNsImport.py @@ -0,0 +1,101 @@ +""" +Tests imported namespaces in C++. +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestCppNsImport(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureFreeBSD("llvm.org/pr25925") + @expectedFailureGcc(None, ['>=', '4.9']) + def test_with_run_command(self): + """Tests imported namespaces in C++.""" + self.build() + + # Get main source file + src_file = "main.cpp" + src_file_spec = lldb.SBFileSpec(src_file) + self.assertTrue(src_file_spec.IsValid(), "Main source file") + + # Get the path of the executable + cwd = os.getcwd() + exe_file = "a.out" + exe_path = os.path.join(cwd, exe_file) + + # Load the executable + target = self.dbg.CreateTarget(exe_path) + self.assertTrue(target.IsValid(), VALID_TARGET) + + # Break on main function + break_0 = target.BreakpointCreateBySourceRegex("// break 0", src_file_spec) + self.assertTrue(break_0.IsValid() and break_0.GetNumLocations() >= 1, VALID_BREAKPOINT) + break_1 = target.BreakpointCreateBySourceRegex("// break 1", src_file_spec) + self.assertTrue(break_1.IsValid() and break_1.GetNumLocations() >= 1, VALID_BREAKPOINT) + + # Launch the process + args = None + env = None + process = target.LaunchSimple(args, env, self.get_process_working_directory()) + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + + # Get the thread of the process + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + + # Get current fream of the thread at the breakpoint + frame = thread.GetSelectedFrame() + + # Test imported namespaces + test_result = frame.EvaluateExpression("n") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 1, "n = 1") + + test_result = frame.EvaluateExpression("N::n") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 1, "N::n = 1") + + test_result = frame.EvaluateExpression("nested") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 3, "nested = 3") + + test_result = frame.EvaluateExpression("anon") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 2, "anon = 2") + + test_result = frame.EvaluateExpression("global") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 4, "global = 4") + + test_result = frame.EvaluateExpression("fun_var") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 9, "fun_var = 9") + + test_result = frame.EvaluateExpression("Fun::fun_var") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 0, "Fun::fun_var = 0") + + test_result = frame.EvaluateExpression("not_imported") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 35, "not_imported = 35") + + # Currently there is no way to distinguish between "::imported" and "imported" in ClangExpressionDeclMap so this fails + #test_result = frame.EvaluateExpression("::imported") + #self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 89, "::imported = 89") + + test_result = frame.EvaluateExpression("Imported::imported") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 99, "Imported::imported = 99") + + test_result = frame.EvaluateExpression("imported") + self.assertTrue(test_result.IsValid() and test_result.GetError().Fail(), "imported is ambiguous") + + test_result = frame.EvaluateExpression("single") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 3, "single = 3") + + # Continue to second breakpoint + process.Continue() + + # Get the thread of the process + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + + # Get current fream of the thread at the breakpoint + frame = thread.GetSelectedFrame() + + # Test function inside namespace + test_result = frame.EvaluateExpression("fun_var") + self.assertTrue(test_result.IsValid() and test_result.GetValueAsSigned() == 5, "fun_var = 5") diff --git a/packages/Python/lldbsuite/test/lang/cpp/nsimport/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/nsimport/main.cpp new file mode 100644 index 000000000000..e125ebaa2430 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/nsimport/main.cpp @@ -0,0 +1,72 @@ +namespace N +{ + int n; +} + +namespace +{ + int anon; +} + +namespace Nested +{ + namespace + { + int nested; + } +} + +namespace Global +{ + int global; +} + +namespace Fun +{ + int fun_var; + int fun() + { + fun_var = 5; + return 0; // break 1 + } +} + +namespace Single +{ + int single = 3; +} + +namespace NotImportedBefore +{ + int not_imported = 45; +} + +using namespace Global; + +int not_imported = 35; +int fun_var = 9; + +namespace NotImportedAfter +{ + int not_imported = 55; +} + +namespace Imported +{ + int imported = 99; +} + +int imported = 89; + +int main() +{ + using namespace N; + using namespace Nested; + using namespace Imported; + using Single::single; + n = 1; + anon = 2; + nested = 3; + global = 4; + return Fun::fun(); // break 0 +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/Makefile b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/Makefile new file mode 100644 index 000000000000..a8d5c4eb0268 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp static-a.cpp static-b.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/TestOverloadedFunctions.py b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/TestOverloadedFunctions.py new file mode 100644 index 000000000000..d485dcd0f652 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/TestOverloadedFunctions.py @@ -0,0 +1,36 @@ +""" +Tests that functions with the same name are resolved correctly. +""" + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CPPStaticMethodsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.line = line_number('main.cpp', '// breakpoint') + + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test_with_run_command(self): + """Test that functions with the same name are resolved correctly""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("process launch", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", + STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + self.expect("expression -- Dump(myB)", + startstr = "(int) $0 = 2") + + self.expect("expression -- Static()", + startstr = "(int) $1 = 1") diff --git a/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/main.cpp new file mode 100644 index 000000000000..250e2cd1d96c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/main.cpp @@ -0,0 +1,43 @@ +#include + +struct A { + int aa; + char ab; +}; + +struct B { + int ba; + int bb; +}; + +struct C { + int ca; + int cb; +}; + +int Dump (A &a) +{ + return 1; +} + +int Dump (B &b) +{ + return 2; +} + +int Dump (C &c) +{ + return 3; +} + +extern int CallStaticA(); +extern int CallStaticB(); + +int main() +{ + A myA; + B myB; + C myC; + + printf("%d\n", CallStaticA() + CallStaticB()); // breakpoint +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/static-a.cpp b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/static-a.cpp new file mode 100644 index 000000000000..7250fa4bed5e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/static-a.cpp @@ -0,0 +1,9 @@ +static int Static() +{ + return 1; +} + +int CallStaticA() +{ + return Static(); +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/static-b.cpp b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/static-b.cpp new file mode 100644 index 000000000000..90a20f69e6db --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/overloaded-functions/static-b.cpp @@ -0,0 +1,9 @@ +static int Static() +{ + return 1; +} + +int CallStaticB() +{ + return Static(); +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/Makefile b/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/Makefile new file mode 100644 index 000000000000..1476447db355 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +CFLAGS := -g -O0 -std=c++11 + +clean: OBJECTS+=$(wildcard main.d.*) + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/TestRdar12991846.py b/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/TestRdar12991846.py new file mode 100644 index 000000000000..636a82b425da --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/TestRdar12991846.py @@ -0,0 +1,85 @@ +# coding=utf8 +""" +Test that the expression parser returns proper Unicode strings. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +# this test case fails because of rdar://12991846 +# the expression parser does not deal correctly with Unicode expressions +# e.g. +#(lldb) expr L"Hello" +#(const wchar_t [6]) $0 = { +# [0] = \0\0\0\0 +# [1] = \0\0\0\0 +# [2] = \0\0\0\0 +# [3] = \0\0\0\0 +# [4] = H\0\0\0 +# [5] = e\0\0\0 +#} + +class Rdar12991846TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @unittest2.expectedFailure("rdar://18684408") + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test_expr1(self): + """Test that the expression parser returns proper Unicode strings.""" + self.build() + self.rdar12991846(expr=1) + + @unittest2.expectedFailure("rdar://18684408") + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test_expr2(self): + """Test that the expression parser returns proper Unicode strings.""" + self.build() + self.rdar12991846(expr=2) + + @unittest2.expectedFailure("rdar://18684408") + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test_expr3(self): + """Test that the expression parser returns proper Unicode strings.""" + self.build() + self.rdar12991846(expr=3) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.source = 'main.cpp' + self.line = line_number(self.source, '// Set break point at this line.') + + def rdar12991846(self, expr=None): + """Test that the expression parser returns proper Unicode strings.""" + if self.getArchitecture() in ['i386']: + self.skipTest("Skipping because this test is known to crash on i386") + + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Break on the struct declration statement in main.cpp. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + if expr == 1: self.expect('expression L"hello"', substrs = ['hello']) + + if expr == 2: self.expect('expression u"hello"', substrs = ['hello']) + + if expr == 3: self.expect('expression U"hello"', substrs = ['hello']) diff --git a/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/main.cpp new file mode 100644 index 000000000000..fda951a78991 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/rdar12991846/main.cpp @@ -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. +// +//===----------------------------------------------------------------------===// + + +int main (int argc, char const *argv[]) +{ + auto cs16 = u"hello world ྒྙྐ"; + auto cs32 = U"hello world ྒྙྐ"; + char16_t *s16 = (char16_t *)u"ﺸﺵۻ"; + char32_t *s32 = (char32_t *)U"ЕЙРГЖО"; + s32 = nullptr; // Set break point at this line. + s32 = (char32_t *)U"෴"; + s16 = (char16_t *)u"色ハ匂ヘト散リヌルヲ"; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/Makefile b/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/Makefile new file mode 100644 index 000000000000..3a1d8a579350 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +CXXFLAGS += -std=c++11 + +include $(LEVEL)/Makefile.rules \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/TestRvalueReferences.py b/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/TestRvalueReferences.py new file mode 100644 index 000000000000..d02c34ecb0fe --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/TestRvalueReferences.py @@ -0,0 +1,49 @@ +""" +Tests that rvalue references are supported in C++ +""" + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class RvalueReferencesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + #rdar://problem/11479676 + @expectedFailureIcc("ICC (13.1, 14-beta) do not emit DW_TAG_rvalue_reference_type.") + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + def test_with_run_command(self): + """Test that rvalues are supported in the C++ expression parser""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.set_breakpoint(line_number('main.cpp', '// breakpoint 1')) + self.set_breakpoint(line_number('main.cpp', '// breakpoint 2')) + + self.runCmd("process launch", RUN_SUCCEEDED) + + # Note that clang as of r187480 doesn't emit DW_TAG_const_type, unlike gcc 4.8.1 + # With gcc 4.8.1, lldb reports the type as (int &&const) + self.expect("frame variable i", + startstr = "(int &&", + substrs = ["i = 0x", "&i = 3"]) + + self.expect("expression -- i", + startstr = "(int) ", + substrs = ["3"]) + + self.expect("breakpoint delete 1") + + self.runCmd("process continue") + + self.expect("expression -- foo(2)") + + self.expect("expression -- int &&j = 3; foo(j)", + error = True) + + self.expect("expression -- int &&k = 6; k", + startstr = "(int) $1 = 6") + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=1, loc_exact=True) diff --git a/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/main.cpp new file mode 100644 index 000000000000..6da34c73f101 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/rvalue-references/main.cpp @@ -0,0 +1,12 @@ +#include + +void foo (int &&i) +{ + printf("%d\n", i); // breakpoint 1 +} + +int main() +{ + foo(3); + return 0; // breakpoint 2 +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/scope/Makefile b/packages/Python/lldbsuite/test/lang/cpp/scope/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/scope/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/scope/TestCppScope.py b/packages/Python/lldbsuite/test/lang/cpp/scope/TestCppScope.py new file mode 100644 index 000000000000..66d4a1591575 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/scope/TestCppScope.py @@ -0,0 +1,67 @@ +""" +Test scopes in C++. +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestCppScopes(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureDarwin + @expectedFailureWindows("llvm.org/pr24764") + def test_with_run_command(self): + self.build() + + # Get main source file + src_file = "main.cpp" + src_file_spec = lldb.SBFileSpec(src_file) + self.assertTrue(src_file_spec.IsValid(), "Main source file") + + # Get the path of the executable + cwd = os.getcwd() + exe_file = "a.out" + exe_path = os.path.join(cwd, exe_file) + + # Load the executable + target = self.dbg.CreateTarget(exe_path) + self.assertTrue(target.IsValid(), VALID_TARGET) + + # Break on main function + main_breakpoint = target.BreakpointCreateBySourceRegex("// break here", src_file_spec) + self.assertTrue(main_breakpoint.IsValid() and main_breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT) + + # Launch the process + args = None + env = None + process = target.LaunchSimple(args, env, self.get_process_working_directory()) + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + + # Get the thread of the process + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + + # Get current fream of the thread at the breakpoint + frame = thread.GetSelectedFrame() + + # Test result for scopes of variables + + global_variables = frame.GetVariables(True, True, True, False) + global_variables_assert = { + 'A::a': 1111, + 'B::a': 2222, + 'C::a': 3333, + '::a': 4444, + 'a': 4444 + } + + self.assertTrue(global_variables.GetSize() == 4, "target variable returns all variables") + for variable in global_variables: + name = variable.GetName() + self.assertTrue(name in global_variables_assert, "target variable returns wrong variable " + name) + + for name in global_variables_assert: + value = frame.EvaluateExpression(name) + assert_value = global_variables_assert[name] + self.assertTrue(value.IsValid() and value.GetValueAsSigned() == assert_value, name + " = " + str(assert_value)) diff --git a/packages/Python/lldbsuite/test/lang/cpp/scope/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/scope/main.cpp new file mode 100644 index 000000000000..da5d7ed529d1 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/scope/main.cpp @@ -0,0 +1,25 @@ +class A { +public: + static int a; + int b; +}; + +class B { +public: + static int a; + int b; +}; + +struct C { + static int a; +}; + +int A::a = 1111; +int B::a = 2222; +int C::a = 3333; +int a = 4444; + +int main() // break here +{ + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/signed_types/Makefile b/packages/Python/lldbsuite/test/lang/cpp/signed_types/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/signed_types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/signed_types/TestSignedTypes.py b/packages/Python/lldbsuite/test/lang/cpp/signed_types/TestSignedTypes.py new file mode 100644 index 000000000000..51e6d4579e28 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/signed_types/TestSignedTypes.py @@ -0,0 +1,60 @@ +""" +Test that variables with signed types display correctly. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class UnsignedTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.source = 'main.cpp' + self.line = line_number(self.source, '// Set break point at this line.') + + def test(self): + """Test that variables with signed types display correctly.""" + self.build() + + # Run in synchronous mode + self.dbg.SetAsync(False) + + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + lldbutil.run_break_set_by_file_and_line (self, self.source, self.line, num_expected_locations=1, loc_exact=True) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Execute the assignment statement. + self.runCmd("thread step-over") + + # Test that signed types display correctly. + self.expect("frame variable --show-types --no-args", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\((short int|short)\) the_signed_short = 99", + "\((signed char|char)\) the_signed_char = 'c'"], + substrs = ["(int) the_signed_int = 99", + "(long) the_signed_long = 99", + "(long long) the_signed_long_long = 99"]) diff --git a/packages/Python/lldbsuite/test/lang/cpp/signed_types/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/signed_types/main.cpp new file mode 100644 index 000000000000..dbf3606aeba8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/signed_types/main.cpp @@ -0,0 +1,33 @@ +//===-- 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 + +int main (int argc, char const *argv[]) +{ + char the_char = 'c'; + short the_short = 'c'; + wchar_t the_wchar_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'; + puts(""); // Set break point at this line. + 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/packages/Python/lldbsuite/test/lang/cpp/static_members/Makefile b/packages/Python/lldbsuite/test/lang/cpp/static_members/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/static_members/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/static_members/TestCPPStaticMembers.py b/packages/Python/lldbsuite/test/lang/cpp/static_members/TestCPPStaticMembers.py new file mode 100644 index 000000000000..1c41b1b6f571 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/static_members/TestCPPStaticMembers.py @@ -0,0 +1,59 @@ +""" +Tests that C++ member and static variables have correct layout and scope. +""" + +from __future__ import print_function + + + +import unittest2 +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CPPStaticMembersTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @unittest2.expectedFailure # llvm.org/pr15401 + @expectedFailureWindows("llvm.org/pr21765") + def test_with_run_command(self): + """Test that member variables have the correct layout, scope and qualifiers when stopped inside and outside C++ methods""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.set_breakpoint(line_number('main.cpp', '// breakpoint 1')) + self.set_breakpoint(line_number('main.cpp', '// breakpoint 2')) + + self.runCmd("process launch", RUN_SUCCEEDED) + self.expect("expression my_a.access()", + startstr = "(long) $0 = 10") + + self.expect("expression my_a.m_a", + startstr = "(short) $1 = 1") + + # Note: SymbolFileDWARF::ParseChildMembers doesn't call AddFieldToRecordType, consistent with clang's AST layout. + self.expect("expression my_a.s_d", + startstr = "(int) $2 = 4") + + self.expect("expression my_a.s_b", + startstr = "(long) $3 = 2") + + self.expect("expression A::s_b", + startstr = "(long) $4 = 2") + + # should not be available in global scope + self.expect("expression s_d", + startstr = "error: use of undeclared identifier 's_d'") + + self.runCmd("process continue") + self.expect("expression m_c", + startstr = "(char) $5 = \'\\x03\'") + + self.expect("expression s_b", + startstr = "(long) $6 = 2") + + self.runCmd("process continue") + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=1, loc_exact=False) diff --git a/packages/Python/lldbsuite/test/lang/cpp/static_members/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/static_members/main.cpp new file mode 100644 index 000000000000..7ccc2f9a328b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/static_members/main.cpp @@ -0,0 +1,36 @@ +//===-- 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 + +struct A +{ + short m_a; + static long s_b; + char m_c; + static int s_d; + + long access() { + return m_a + s_b + m_c + s_d; // breakpoint 2 + } +}; + +long A::s_b = 2; +int A::s_d = 4; + +int main() +{ + A my_a; + my_a.m_a = 1; + my_a.m_c = 3; + + my_a.access(); // breakpoint 1 + return 0; +} + diff --git a/packages/Python/lldbsuite/test/lang/cpp/static_methods/Makefile b/packages/Python/lldbsuite/test/lang/cpp/static_methods/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/static_methods/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/static_methods/TestCPPStaticMethods.py b/packages/Python/lldbsuite/test/lang/cpp/static_methods/TestCPPStaticMethods.py new file mode 100644 index 000000000000..dba556431e39 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/static_methods/TestCPPStaticMethods.py @@ -0,0 +1,36 @@ +""" +Tests expressions that distinguish between static and non-static methods. +""" + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CPPStaticMethodsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.line = line_number('main.cpp', '// Break at this line') + + @expectedFailureWindows + def test_with_run_command(self): + """Test that static methods are properly distinguished from regular methods""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("process launch", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", + STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + self.expect("expression -- A::getStaticValue()", + startstr = "(int) $0 = 5") + + self.expect("expression -- my_a.getMemberValue()", + startstr = "(int) $1 = 3") diff --git a/packages/Python/lldbsuite/test/lang/cpp/static_methods/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/static_methods/main.cpp new file mode 100644 index 000000000000..5141a407d111 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/static_methods/main.cpp @@ -0,0 +1,38 @@ +//===-- 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 + +class A +{ +public: + static int getStaticValue(); + int getMemberValue(); + int a; +}; + +int A::getStaticValue() +{ + return 5; +} + +int A::getMemberValue() +{ + return a; +} + +int main() +{ + A my_a; + + my_a.a = 3; + + printf("%d\n", A::getStaticValue()); // Break at this line + printf("%d\n", my_a.getMemberValue()); +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/stl/Makefile b/packages/Python/lldbsuite/test/lang/cpp/stl/Makefile new file mode 100644 index 000000000000..f61a6300a994 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/stl/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +CFLAGS := -g -O0 + +clean: OBJECTS+=$(wildcard main.d.*) + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/stl/TestSTL.py b/packages/Python/lldbsuite/test/lang/cpp/stl/TestSTL.py new file mode 100644 index 000000000000..6dba1398947e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/stl/TestSTL.py @@ -0,0 +1,118 @@ +""" +Test some expressions involving STL data types. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class STLTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.source = 'main.cpp' + self.line = line_number(self.source, '// Set break point at this line.') + + # rdar://problem/10400981 + @unittest2.expectedFailure + def test(self): + """Test some expressions involving STL data types.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # The following two lines, if uncommented, will enable loggings. + #self.ci.HandleCommand("log enable -f /tmp/lldb.log lldb default", res) + #self.assertTrue(res.Succeeded()) + + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # rdar://problem/8543077 + # test/stl: clang built binaries results in the breakpoint locations = 3, + # is this a problem with clang generated debug info? + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Stop at 'std::string hello_world ("Hello World!");'. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['main.cpp:%d' % self.line, + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Now try some expressions.... + + self.runCmd('expr for (int i = 0; i < hello_world.length(); ++i) { (void)printf("%c\\n", hello_world[i]); }') + + # rdar://problem/10373783 + # rdar://problem/10400981 + self.expect('expr associative_array.size()', + substrs = [' = 3']) + self.expect('expr associative_array.count(hello_world)', + substrs = [' = 1']) + self.expect('expr associative_array[hello_world]', + substrs = [' = 1']) + self.expect('expr associative_array["hello"]', + substrs = [' = 2']) + + @expectedFailureIcc # icc 13.1 and 14-beta do not emit DW_TAG_template_type_parameter + @add_test_categories(['pyapi']) + def test_SBType_template_aspects(self): + """Test APIs for getting template arguments from an SBType.""" + self.build() + exe = os.path.join(os.getcwd(), 'a.out') + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Get Frame #0. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + + # Get the type for variable 'associative_array'. + associative_array = frame0.FindVariable('associative_array') + self.DebugSBValue(associative_array) + self.assertTrue(associative_array, VALID_VARIABLE) + map_type = associative_array.GetType() + self.DebugSBType(map_type) + self.assertTrue(map_type, VALID_TYPE) + num_template_args = map_type.GetNumberOfTemplateArguments() + self.assertTrue(num_template_args > 0) + + # We expect the template arguments to contain at least 'string' and 'int'. + expected_types = { 'string': False, 'int': False } + for i in range(num_template_args): + t = map_type.GetTemplateArgumentType(i) + self.DebugSBType(t) + self.assertTrue(t, VALID_TYPE) + name = t.GetName() + if 'string' in name: + expected_types['string'] = True + elif 'int' == name: + expected_types['int'] = True + + # Check that both entries of the dictionary have 'True' as the value. + self.assertTrue(all(expected_types.values())) diff --git a/packages/Python/lldbsuite/test/lang/cpp/stl/TestStdCXXDisassembly.py b/packages/Python/lldbsuite/test/lang/cpp/stl/TestStdCXXDisassembly.py new file mode 100644 index 000000000000..d7435c467274 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/stl/TestStdCXXDisassembly.py @@ -0,0 +1,111 @@ +""" +Test the lldb disassemble command on lib stdc++. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class StdCXXDisassembleTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + # rdar://problem/8504895 + # Crash while doing 'disassemble -n "-[NSNumber descriptionWithLocale:]" + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_stdcxx_disasm(self): + """Do 'disassemble' on each and every 'Code' symbol entry from the std c++ lib.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # rdar://problem/8543077 + # test/stl: clang built binaries results in the breakpoint locations = 3, + # is this a problem with clang generated debug info? + # + # Break on line 13 of main.cpp. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Now let's get the target as well as the process objects. + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # The process should be in a 'stopped' state. + self.expect(str(process), STOPPED_DUE_TO_BREAKPOINT, exe=False, + substrs = ["a.out", + "stopped"]) + + # Disassemble the functions on the call stack. + self.runCmd("thread backtrace") + thread = process.GetThreadAtIndex(0) + depth = thread.GetNumFrames() + for i in range(depth - 1): + frame = thread.GetFrameAtIndex(i) + function = frame.GetFunction() + if function.GetName(): + self.runCmd("disassemble -n '%s'" % function.GetName()) + + lib_stdcxx = "FAILHORRIBLYHERE" + # Iterate through the available modules, looking for stdc++ library... + for i in range(target.GetNumModules()): + module = target.GetModuleAtIndex(i) + fs = module.GetFileSpec() + if (fs.GetFilename().startswith("libstdc++") or fs.GetFilename().startswith("libc++")): + lib_stdcxx = str(fs) + break + + # At this point, lib_stdcxx is the full path to the stdc++ library and + # module is the corresponding SBModule. + + self.expect(lib_stdcxx, "Libraray StdC++ is located", exe=False, + substrs = ["lib"]) + + self.runCmd("image dump symtab '%s'" % lib_stdcxx) + raw_output = self.res.GetOutput() + # Now, look for every 'Code' symbol and feed its load address into the + # command: 'disassemble -s load_address -e end_address', where the + # end_address is taken from the next consecutive 'Code' symbol entry's + # load address. + # + # The load address column comes after the file address column, with both + # looks like '0xhhhhhhhh', i.e., 8 hexadecimal digits. + codeRE = re.compile(r""" + \ Code\ {9} # ' Code' followed by 9 SPCs, + 0x[0-9a-f]{16} # the file address column, and + \ # a SPC, and + (0x[0-9a-f]{16}) # the load address column, and + .* # the rest. + """, re.VERBOSE) + # Maintain a start address variable; if we arrive at a consecutive Code + # entry, then the load address of the that entry is fed as the end + # address to the 'disassemble -s SA -e LA' command. + SA = None + for line in raw_output.split(os.linesep): + match = codeRE.search(line) + if match: + LA = match.group(1) + if self.TraceOn(): + print("line:", line) + print("load address:", LA) + print("SA:", SA) + if SA and LA: + if int(LA, 16) > int(SA, 16): + self.runCmd("disassemble -s %s -e %s" % (SA, LA)) + SA = LA + else: + # This entry is not a Code entry. Reset SA = None. + SA = None diff --git a/packages/Python/lldbsuite/test/lang/cpp/stl/cmds.txt b/packages/Python/lldbsuite/test/lang/cpp/stl/cmds.txt new file mode 100644 index 000000000000..9c9c2e3db57b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/stl/cmds.txt @@ -0,0 +1,3 @@ +b main.cpp:6 +continue +var diff --git a/packages/Python/lldbsuite/test/lang/cpp/stl/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/stl/main.cpp new file mode 100644 index 000000000000..cfdb7b2d3db5 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/stl/main.cpp @@ -0,0 +1,30 @@ +//===-- 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 +#include +#include +int main (int argc, char const *argv[]) +{ + std::string hello_world ("Hello World!"); + std::cout << hello_world << std::endl; + std::cout << hello_world.length() << std::endl; + std::cout << hello_world[11] << std::endl; + + std::map associative_array; + std::cout << "size of upon construction associative_array: " << associative_array.size() << std::endl; + associative_array[hello_world] = 1; + associative_array["hello"] = 2; + associative_array["world"] = 3; + + std::cout << "size of associative_array: " << associative_array.size() << std::endl; + printf("associative_array[\"hello\"]=%d\n", associative_array["hello"]); + + printf("before returning....\n"); // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/this/Makefile b/packages/Python/lldbsuite/test/lang/cpp/this/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/this/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/this/TestCPPThis.py b/packages/Python/lldbsuite/test/lang/cpp/this/TestCPPThis.py new file mode 100644 index 000000000000..07cc2e8e9788 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/this/TestCPPThis.py @@ -0,0 +1,53 @@ +""" +Tests that C++ member and static variables are available where they should be. +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CPPThisTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + #rdar://problem/9962849 + @expectedFailureGcc # llvm.org/pr15439 The 'this' pointer isn't available during expression evaluation when stopped in an inlined member function. + @expectedFailureIcc # ICC doesn't emit correct DWARF inline debug info for inlined member functions + @expectedFailureWindows("llvm.org/pr24489: Name lookup not working correctly on Windows") + @expectedFailureWindows("llvm.org/pr24490: We shouldn't be using platform-specific names like `getpid` in tests") + @expectedFlakeyClang(bugnumber='llvm.org/pr23012', compiler_version=['>=','3.6']) # failed with totclang - clang3.7 + def test_with_run_command(self): + """Test that the appropriate member variables are available when stopped in C++ static, inline, and const methods""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.set_breakpoint(line_number('main.cpp', '// breakpoint 1')) + self.set_breakpoint(line_number('main.cpp', '// breakpoint 2')) + self.set_breakpoint(line_number('main.cpp', '// breakpoint 3')) + self.set_breakpoint(line_number('main.cpp', '// breakpoint 4')) + + self.runCmd("process launch", RUN_SUCCEEDED) + + self.expect("expression -- m_a = 2", + startstr = "(int) $0 = 2") + + self.runCmd("process continue") + + # This would be disallowed if we enforced const. But we don't. + self.expect("expression -- m_a = 2", + startstr = "(int) $1 = 2") + + self.expect("expression -- (int)getpid(); m_a", + startstr = "(int) $2 = 2") + + self.runCmd("process continue") + + self.expect("expression -- s_a", + startstr = "(int) $3 = 5") + + self.runCmd("process continue") + + self.expect("expression -- m_a", + startstr = "(int) $4 = 2") + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=1, loc_exact=False) diff --git a/packages/Python/lldbsuite/test/lang/cpp/this/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/this/main.cpp new file mode 100644 index 000000000000..4448a5cadf57 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/this/main.cpp @@ -0,0 +1,53 @@ +//===-- 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 + +template class A +{ +public: + void accessMember(T a); + T accessMemberConst() const; + static int accessStaticMember(); + + void accessMemberInline(T a) __attribute__ ((always_inline)) + { + m_a = a; // breakpoint 4 + } + + T m_a; + static int s_a; +}; + +template int A::s_a = 5; + +template void A::accessMember(T a) +{ + m_a = a; // breakpoint 1 +} + +template T A::accessMemberConst() const +{ + return m_a; // breakpoint 2 +} + +template int A::accessStaticMember() +{ + return s_a; // breakpoint 3 +} + +int main() +{ + A my_a; + + my_a.accessMember(3); + my_a.accessMemberConst(); + A::accessStaticMember(); + my_a.accessMemberInline(5); +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/unique-types/Makefile b/packages/Python/lldbsuite/test/lang/cpp/unique-types/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/unique-types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/unique-types/TestUniqueTypes.py b/packages/Python/lldbsuite/test/lang/cpp/unique-types/TestUniqueTypes.py new file mode 100644 index 000000000000..2cbb1a191e68 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/unique-types/TestUniqueTypes.py @@ -0,0 +1,62 @@ +""" +Test that template instaniations of std::vector and in the same module have the correct types. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class UniqueTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number inside main.cpp. + self.line = line_number("main.cpp", + "// Set breakpoint here to verify that std::vector 'longs' and 'shorts' have unique types.") + + def test(self): + """Test for unique types of std::vector and std::vector.""" + self.build() + + compiler = self.getCompiler() + compiler_basename = os.path.basename(compiler) + if "clang" in compiler_basename and int(self.getCompilerVersion().split('.')[0]) < 3: + self.skipTest("rdar://problem/9173060 lldb hangs while running unique-types for clang version < 3") + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Do a "frame variable --show-types longs" and verify "long" is in each line of output. + self.runCmd("frame variable --show-types longs") + output = self.res.GetOutput() + for x in [line.strip() for line in output.split(os.linesep)]: + # Skip empty line, closing brace, and messages about more variables than can be displayed. + if not x or x == '}' or x == '...' or "Some of your variables have more members than the debugger will show by default" in x: + continue + self.expect(x, "Expect type 'long'", exe=False, + substrs = ['long']) + + # Do a "frame variable --show-types shorts" and verify "short" is in each line of output. + self.runCmd("frame variable --show-types shorts") + output = self.res.GetOutput() + for x in [line.strip() for line in output.split(os.linesep)]: + # Skip empty line, closing brace, and messages about more variables than can be displayed. + if not x or x == '}' or x == '...' or "Some of your variables have more members than the debugger will show by default" in x: + continue + self.expect(x, "Expect type 'short'", exe=False, + substrs = ['short']) diff --git a/packages/Python/lldbsuite/test/lang/cpp/unique-types/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/unique-types/main.cpp new file mode 100644 index 000000000000..c551c0e2c0d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/unique-types/main.cpp @@ -0,0 +1,24 @@ +//===-- 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 + +int main (int argc, char const *argv[], char const *envp[]) +{ + std::vector longs; + std::vector shorts; + for (int i=0; i<12; i++) + { + longs.push_back(i); + shorts.push_back(i); + } + return 0; // Set breakpoint here to verify that std::vector 'longs' and 'shorts' have unique types. +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/unsigned_types/Makefile b/packages/Python/lldbsuite/test/lang/cpp/unsigned_types/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/unsigned_types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/unsigned_types/TestUnsignedTypes.py b/packages/Python/lldbsuite/test/lang/cpp/unsigned_types/TestUnsignedTypes.py new file mode 100644 index 000000000000..c137592558ed --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/unsigned_types/TestUnsignedTypes.py @@ -0,0 +1,54 @@ +""" +Test that variables with unsigned types display correctly. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class UnsignedTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test(self): + """Test that variables with unsigned types display correctly.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # GCC puts a breakpoint on the last line of a multi-line expression, so + # if GCC is the target compiler, we cannot rely on an exact line match. + need_exact = "gcc" not in self.getCompiler() + # Break on line 19 in main() aftre the variables are assigned values. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=need_exact) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # Test that unsigned types display correctly. + self.expect("frame variable --show-types --no-args", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(unsigned char) the_unsigned_char = 'c'", + patterns = ["\((short unsigned int|unsigned short)\) the_unsigned_short = 99"], + substrs = ["(unsigned int) the_unsigned_int = 99", + "(unsigned long) the_unsigned_long = 99", + "(unsigned long long) the_unsigned_long_long = 99", + "(uint32_t) the_uint32 = 99"]) diff --git a/packages/Python/lldbsuite/test/lang/cpp/unsigned_types/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/unsigned_types/main.cpp new file mode 100644 index 000000000000..b0d68377e983 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/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 + // Set break point at this line. + the_unsigned_int - the_unsigned_long + + the_unsigned_long_long - the_uint32; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/virtual/Makefile b/packages/Python/lldbsuite/test/lang/cpp/virtual/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/virtual/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/virtual/TestVirtual.py b/packages/Python/lldbsuite/test/lang/cpp/virtual/TestVirtual.py new file mode 100644 index 000000000000..1553a43e1a73 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/virtual/TestVirtual.py @@ -0,0 +1,90 @@ +""" +Test C++ virtual function and virtual inheritance. +""" + +from __future__ import print_function + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +def Msg(expr, val): + return "'expression %s' matches the output (from compiled code): %s" % (expr, val) + +class CppVirtualMadness(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # This is the pattern by design to match the "my_expr = 'value'" output from + # printf() stmts (see main.cpp). + pattern = re.compile("^([^=]*) = '([^=]*)'$") + + # Assert message. + PRINTF_OUTPUT_GROKKED = "The printf output from compiled code is parsed correctly" + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.source = 'main.cpp' + self.line = line_number(self.source, '// Set first breakpoint here.') + + @expectedFailureIcc('llvm.org/pr16808') # lldb does not call the correct virtual function with icc + @expectedFailureAll(oslist=['windows']) + def test_virtual_madness(self): + """Test that expression works correctly with virtual inheritance as well as virtual function.""" + self.build() + + # Bring the program to the point where we can issue a series of + # 'expression' command to compare against the golden output. + self.dbg.SetAsync(False) + + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + + # First, capture the golden output from the program itself from the + # series of printf statements. + stdout = process.GetSTDOUT(1024) + + self.assertIsNotNone(stdout, "Encountered an error reading the process's output") + + # This golden list contains a list of "my_expr = 'value' pairs extracted + # from the golden output. + gl = [] + + # Scan the golden output line by line, looking for the pattern: + # + # my_expr = 'value' + # + for line in stdout.split(os.linesep): + match = self.pattern.search(line) + if match: + my_expr, val = match.group(1), match.group(2) + gl.append((my_expr, val)) + #print("golden list:", gl) + + # Now iterate through the golden list, comparing against the output from + # 'expression var'. + for my_expr, val in gl: + + self.runCmd("expression %s" % my_expr) + output = self.res.GetOutput() + + # The expression output must match the oracle. + self.expect(output, Msg(my_expr, val), exe=False, + substrs = [val]) diff --git a/packages/Python/lldbsuite/test/lang/cpp/virtual/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/virtual/main.cpp new file mode 100644 index 000000000000..bed1422dcbdd --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/virtual/main.cpp @@ -0,0 +1,113 @@ +#include +#include + +class A +{ +public: + A () : m_pad ('c') {} + + virtual ~A () {} + + virtual const char * a() + { + return __PRETTY_FUNCTION__; + } + + virtual const char * b() + { + return __PRETTY_FUNCTION__; + } + + virtual const char * c() + { + return __PRETTY_FUNCTION__; + } +protected: + char m_pad; +}; + +class AA +{ +public: + AA () : m_pad('A') {} + virtual ~AA () {} + + virtual const char * aa() + { + return __PRETTY_FUNCTION__; + } + +protected: + char m_pad; +}; + +class B : virtual public A, public AA +{ +public: + B () : m_pad ('c') {} + + virtual ~B () {} + + virtual const char * a() + { + return __PRETTY_FUNCTION__; + } + + virtual const char * b() + { + return __PRETTY_FUNCTION__; + } +protected: + char m_pad; +}; + +class C : public B, virtual public A +{ +public: + C () : m_pad ('c') {} + + virtual ~C () {} + + virtual const char * a() + { + return __PRETTY_FUNCTION__; + } +protected: + char m_pad; +}; + +int main (int argc, char const *argv[], char const *envp[]) +{ + A *a_as_A = new A(); + B *b_as_B = new B(); + A *b_as_A = b_as_B; + C *c_as_C = new C(); + A *c_as_A = c_as_C; + + printf ("a_as_A->a() = '%s'\n", a_as_A->a()); + printf ("a_as_A->b() = '%s'\n", a_as_A->b()); + printf ("a_as_A->c() = '%s'\n", a_as_A->c()); + printf ("b_as_A->a() = '%s'\n", b_as_A->a()); + printf ("b_as_A->b() = '%s'\n", b_as_A->b()); + printf ("b_as_A->c() = '%s'\n", b_as_A->c()); + printf ("b_as_B->aa() = '%s'\n", b_as_B->aa()); + printf ("c_as_A->a() = '%s'\n", c_as_A->a()); + printf ("c_as_A->b() = '%s'\n", c_as_A->b()); + printf ("c_as_A->c() = '%s'\n", c_as_A->c()); + printf ("c_as_C->aa() = '%s'\n", c_as_C->aa()); + puts("");// Set first breakpoint here. + // then evaluate: + // expression a_as_A->a() + // expression a_as_A->b() + // expression a_as_A->c() + // expression b_as_A->a() + // expression b_as_A->b() + // expression b_as_A->c() + // expression b_as_B->aa() + // expression c_as_A->a() + // expression c_as_A->b() + // expression c_as_A->c() + // expression c_as_C->aa() + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/cpp/wchar_t/.categories b/packages/Python/lldbsuite/test/lang/cpp/wchar_t/.categories new file mode 100644 index 000000000000..fe1da0247c62 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/wchar_t/.categories @@ -0,0 +1 @@ +dataformatters diff --git a/packages/Python/lldbsuite/test/lang/cpp/wchar_t/Makefile b/packages/Python/lldbsuite/test/lang/cpp/wchar_t/Makefile new file mode 100644 index 000000000000..fe27980afe9b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/wchar_t/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +CFLAGS := -g -O0 + +clean: OBJECTS+=$(wildcard main.d.*) + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/cpp/wchar_t/TestCxxWCharT.py b/packages/Python/lldbsuite/test/lang/cpp/wchar_t/TestCxxWCharT.py new file mode 100644 index 000000000000..f9cdbca16e55 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/wchar_t/TestCxxWCharT.py @@ -0,0 +1,74 @@ +#coding=utf8 +""" +Test that C++ supports wchar_t correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CxxWCharTTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break for main.cpp. + self.source = 'main.cpp' + self.line = line_number(self.source, '// Set break point at this line.') + + def test(self): + """Test that C++ supports wchar_t correctly.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Break on the struct declration statement in main.cpp. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.Launch() failed") + + # Check that we correctly report templates on wchar_t + self.expect("frame variable foo_y", + substrs = ['(Foo) foo_y = ']) + + # Check that we correctly report templates on int + self.expect("frame variable foo_x", + substrs = ['(Foo) foo_x = ']) + + # Check that we correctly report wchar_t + self.expect("frame variable foo_y.object", + substrs = ['(wchar_t) foo_y.object = ']) + + # Check that we correctly report int + self.expect("frame variable foo_x.object", + substrs = ['(int) foo_x.object = ']) + + # Check that we can run expressions that return wchar_t + self.expect("expression L'a'",substrs = ['(wchar_t) $',"L'a'"]) + + # Mazel Tov if this works! + self.expect("frame variable mazeltov", + substrs = ['(const wchar_t *) mazeltov = ','L"מזל טוב"']) + + self.expect("frame variable ws_NULL",substrs = ['(wchar_t *) ws_NULL = 0x0']) + self.expect("frame variable ws_empty",substrs = [' L""']) + + self.expect("frame variable array",substrs = ['L"Hey, I\'m a super wchar_t string']) + self.expect("frame variable array",substrs = ['[0]'], matching=False) + + self.expect('frame variable wchar_zero', substrs=["L'\\0'"]) + self.expect('expression wchar_zero', substrs=["L'\\0'"]) diff --git a/packages/Python/lldbsuite/test/lang/cpp/wchar_t/main.cpp b/packages/Python/lldbsuite/test/lang/cpp/wchar_t/main.cpp new file mode 100644 index 000000000000..e249c37b678f --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/cpp/wchar_t/main.cpp @@ -0,0 +1,35 @@ +//===-- 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 + +template +class Foo +{ +public: + Foo () : object() {} + Foo (T x) : object(x) {} + T getObject() { return object; } +private: + T object; +}; + + +int main (int argc, char const *argv[]) +{ + Foo foo_x('a'); + Foo foo_y(L'a'); + const wchar_t *mazeltov = L"מזל טוב"; + wchar_t *ws_NULL = nullptr; + wchar_t *ws_empty = L""; + wchar_t array[200], * array_source = L"Hey, I'm a super wchar_t string, éõñž"; + wchar_t wchar_zero = (wchar_t)0; + memcpy(array, array_source, 39 * sizeof(wchar_t)); + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/lang/go/expressions/TestExpressions.py b/packages/Python/lldbsuite/test/lang/go/expressions/TestExpressions.py new file mode 100644 index 000000000000..cbd244a7e056 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/expressions/TestExpressions.py @@ -0,0 +1,113 @@ +"""Test the go expression parser/interpreter.""" + +import os, time +import unittest2 +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestGoUserExpression(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @skipIfRemote # Not remote test suit ready + @skipUnlessGoInstalled + def test_with_dsym_and_python_api(self): + """Test GoASTUserExpress.""" + self.buildGo() + self.launchProcess() + self.go_expressions() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.go" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + def check_builtin(self, name, size=0, typeclass=lldb.eTypeClassBuiltin): + tl = self.target().FindTypes(name) + self.assertEqual(1, len(tl)) + t = list(tl)[0] + self.assertEqual(name, t.name) + self.assertEqual(typeclass, t.type) + if size > 0: + self.assertEqual(size, t.size) + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + def go_expressions(self): + frame = self.frame() + v = frame.EvaluateExpression("1") + self.assertEqual(1, v.GetValueAsSigned()) + x = frame.EvaluateExpression("x") + self.assertEqual(22, x.GetValueAsSigned()) + + a = frame.EvaluateExpression("a") + self.assertEqual(3, a.GetNumChildren()) + a0 = a.GetChildAtIndex(0) + self.assertEqual(8, a0.GetValueAsSigned()) + + # Array indexing + a0 = frame.EvaluateExpression("a[0]") + self.assertEqual(8, a0.GetValueAsSigned()) + + # Slice indexing + b1 = frame.EvaluateExpression("b[1]") + self.assertEqual(9, b1.GetValueAsSigned()) + + # Test global in this package + g = frame.EvaluateExpression("myGlobal") + self.assertEqual(17, g.GetValueAsSigned(), str(g)) + + # Global with package name + g = frame.EvaluateExpression("main.myGlobal") + self.assertEqual(17, g.GetValueAsSigned(), str(g)) + + # Global with quoted package name + g = frame.EvaluateExpression('"main".myGlobal') + self.assertEqual(17, g.GetValueAsSigned(), str(g)) + + # Casting with package local type + s = frame.EvaluateExpression("*(*myStruct)(i.data)") + sb = s.GetChildMemberWithName("a") + self.assertEqual(2, sb.GetValueAsSigned()) + + # casting with explicit package + s = frame.EvaluateExpression("*(*main.myStruct)(i.data)") + sb = s.GetChildMemberWithName("a") + self.assertEqual(2, sb.GetValueAsSigned()) + + # Casting quoted package + s = frame.EvaluateExpression('*(*"main".myStruct)(i.data)') + sb = s.GetChildMemberWithName("b") + self.assertEqual(-1, sb.GetValueAsSigned()) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/packages/Python/lldbsuite/test/lang/go/expressions/main.go b/packages/Python/lldbsuite/test/lang/go/expressions/main.go new file mode 100644 index 000000000000..c8b97fe07d7d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/expressions/main.go @@ -0,0 +1,21 @@ +package main + +import "fmt" + +type myStruct struct { + a, b int +} + +var myGlobal = 17 + +func myFunc(i interface{}) { + a := [...]int{8, 9, 10} + b := a[:] + x := 22 + fmt.Println(a, b, x, i, myGlobal) // Set breakpoint here. +} + +func main() { + s := myStruct {2, -1} + myFunc(s) +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/go/formatters/TestGoFormatters.py b/packages/Python/lldbsuite/test/lang/go/formatters/TestGoFormatters.py new file mode 100644 index 000000000000..8b869df4ef8f --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/formatters/TestGoFormatters.py @@ -0,0 +1,65 @@ +"""Test the Go Data Formatter Plugin.""" + +import os, time +import unittest2 +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestGoLanguage(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfFreeBSD # llvm.org/pr24895 triggers assertion failure + @skipIfRemote # Not remote test suite ready + @no_debug_info_test + @skipUnlessGoInstalled + def test_go_formatter_plugin(self): + """Test go data formatters.""" + self.buildGo() + self.launchProcess() + self.check_formatters() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.go" + self.break_line = line_number(self.main_source, '// stop here') + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(self.bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, self.bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + def check_formatters(self): + a = self.frame().FindVariable('a') + self.assertEqual('(string) a = "my string"', str(a)) + b = self.frame().FindVariable('b') + self.assertEqual("([]int) b = (len 2, cap 7) {\n [0] = 0\n [1] = 0\n}", str(b)) + + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/packages/Python/lldbsuite/test/lang/go/formatters/main.go b/packages/Python/lldbsuite/test/lang/go/formatters/main.go new file mode 100644 index 000000000000..7956ad66bcb4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/formatters/main.go @@ -0,0 +1,9 @@ +package main + +import "fmt" + +func main() { + a := "my string" + b := make([]int, 2, 7) + fmt.Println(a, b) // stop here +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/go/goroutines/TestGoroutines.py b/packages/Python/lldbsuite/test/lang/go/goroutines/TestGoroutines.py new file mode 100644 index 000000000000..35961ebd1d92 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/goroutines/TestGoroutines.py @@ -0,0 +1,85 @@ +"""Test the Go OS Plugin.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestGoASTContext(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @skipIfFreeBSD # llvm.org/pr24895 triggers assertion failure + @skipIfRemote # Not remote test suite ready + @no_debug_info_test + @skipUnlessGoInstalled + def test_goroutine_plugin(self): + """Test goroutine as threads support.""" + self.buildGo() + self.launchProcess() + self.check_goroutines() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.go" + self.break_line1 = line_number(self.main_source, '// stop1') + self.break_line2 = line_number(self.main_source, '// stop2') + self.break_line3 = line_number(self.main_source, '// stop3') + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.bpt1 = target.BreakpointCreateByLocation(self.main_source, self.break_line1) + self.assertTrue(self.bpt1, VALID_BREAKPOINT) + self.bpt2 = target.BreakpointCreateByLocation(self.main_source, self.break_line2) + self.assertTrue(self.bpt2, VALID_BREAKPOINT) + self.bpt3 = target.BreakpointCreateByLocation(self.main_source, self.break_line3) + self.assertTrue(self.bpt3, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, self.bpt1) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + def check_goroutines(self): + self.assertLess(len(self.process().threads), 20) + self.process().Continue() + + # Make sure we stopped at the 2nd breakpoint + thread_list = lldbutil.get_threads_stopped_at_breakpoint (self.process(), self.bpt2) + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + # There's (at least) 21 goroutines. + self.assertGreater(len(self.process().threads), 20) + # self.dbg.HandleCommand("log enable lldb os") + + # Now test that stepping works if the memory thread moves to a different backing thread. + for i in list(range(11)): + self.thread().StepOver() + self.assertEqual(lldb.eStopReasonPlanComplete, self.thread().GetStopReason(), self.thread().GetStopDescription(100)) + + # Disable the plugin and make sure the goroutines disappear + self.dbg.HandleCommand("settings set plugin.os.goroutines.enable false") + self.thread().StepInstruction(False) + self.assertLess(len(self.process().threads), 20) diff --git a/packages/Python/lldbsuite/test/lang/go/goroutines/main.go b/packages/Python/lldbsuite/test/lang/go/goroutines/main.go new file mode 100644 index 000000000000..bb44f7b8b716 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/goroutines/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "fmt" + "runtime" +) + +type philosopher struct { + i int + forks [2]chan bool + eating chan int + done chan struct{} +} + +func (p philosopher) run() { + for { + select { + case <-p.done: + return + case <-p.forks[0]: + p.eat() + } + } +} + +func (p philosopher) eat() { + select { + case <-p.done: + return + case <-p.forks[1]: + p.eating <- p.i + p.forks[0] <- true + p.forks[1] <- true + runtime.Gosched() + } +} + +func startPhilosophers(n int) (chan struct{}, chan int) { + philosophers := make([]*philosopher, n) + chans := make([]chan bool, n) + for i := range chans { + chans[i] = make(chan bool, 1) + chans[i] <- true + } + eating := make(chan int, n) + done := make(chan struct{}) + for i := range philosophers { + var min, max int + if i == n - 1 { + min = 0 + max = i + } else { + min = i + max = i + 1 + } + philosophers[i] = &philosopher{i: i, forks: [2]chan bool{chans[min], chans[max]}, eating: eating, done: done} + go philosophers[i].run() + } + return done, eating +} + +func wait(c chan int) { + fmt.Println(<- c) + runtime.Gosched() +} + +func main() { + // Restrict go to 1 real thread so we can be sure we're seeing goroutines + // and not threads. + runtime.GOMAXPROCS(1) + // Create a bunch of goroutines + done, eating := startPhilosophers(20) // stop1 + // Now turn up the number of threads so this goroutine is likely to get + // scheduled on a different thread. + runtime.GOMAXPROCS(runtime.NumCPU()) // stop2 + // Now let things run. Hopefully we'll bounce around + wait(eating) + wait(eating) + wait(eating) + wait(eating) + wait(eating) + wait(eating) + wait(eating) + wait(eating) + wait(eating) + wait(eating) + close(done) + fmt.Println("done") // stop3 +} diff --git a/packages/Python/lldbsuite/test/lang/go/runtime/TestGoLanguageRuntime b/packages/Python/lldbsuite/test/lang/go/runtime/TestGoLanguageRuntime new file mode 100644 index 000000000000..44797077a641 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/runtime/TestGoLanguageRuntime @@ -0,0 +1,80 @@ +"""Test the go dynamic type handling.""" + +import os, time +import unittest2 +import lldb +import lldbutil +from lldbtest import * + +class TestGoLanguageRuntime(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @python_api_test + @expectedFailureFreeBSD('llvm.org/pr24895') + @skipIfRemote # Not remote test suite ready + @skipUnlessGoInstalled + def test_with_dsym_and_python_api(self): + """Test GoASTContext dwarf parsing.""" + self.buildGo() + self.launchProcess() + self.go_interface_types() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.go" + self.break_line1 = line_number(self.main_source, '// Set breakpoint 1') + self.break_line2 = line_number(self.main_source, '// Set breakpoint 2') + + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt1 = target.BreakpointCreateByLocation(self.main_source, self.break_line1) + self.assertTrue(bpt1, VALID_BREAKPOINT) + bpt2 = target.BreakpointCreateByLocation(self.main_source, self.break_line2) + self.assertTrue(bpt2, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt1) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + def go_interface_types(self): + f = self.frame() + v = f.FindVariable("a", lldb.eDynamicCanRunTarget) + self.assertEqual("*int", v.GetType().name) + self.assertEqual(1, v.Dereference().GetValueAsSigned()) + v = f.FindVariable("b", lldb.eDynamicCanRunTarget) + self.assertEqual("*float64", v.GetType().name) + err = lldb.SBError() + self.assertEqual(2.0, v.Dereference().GetData().GetDouble(err, 0)) + v = f.FindVariable("c", lldb.eDynamicCanRunTarget) + self.assertEqual("*main.SomeFooer", v.GetType().name) + self.assertEqual(9, v.Dereference().GetChildAtIndex(0).GetValueAsSigned()) + v = f.FindVariable("d", lldb.eDynamicCanRunTarget) + self.assertEqual("*main.AnotherFooer", v.GetType().name) + self.assertEqual(-1, v.Dereference().GetChildAtIndex(0).GetValueAsSigned()) + self.assertEqual(-2, v.Dereference().GetChildAtIndex(1).GetValueAsSigned()) + self.assertEqual(-3, v.Dereference().GetChildAtIndex(2).GetValueAsSigned()) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/packages/Python/lldbsuite/test/lang/go/runtime/main.go b/packages/Python/lldbsuite/test/lang/go/runtime/main.go new file mode 100644 index 000000000000..227c8c377ed7 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/runtime/main.go @@ -0,0 +1,38 @@ +package main + +import "fmt" + +type Fooer interface { + Foo() int +} + +type SomeFooer struct { + val int +} + +func (s SomeFooer) Foo() int { + return s.val +} + +type AnotherFooer struct { + a, b, c int +} + +func (s AnotherFooer) Foo() int { + return s.a +} + + +func printEface(a, b, c, d interface{}) { + fmt.Println(a, b, c, d) // Set breakpoint 1 +} + +func printIface(a, b Fooer) { + fmt.Println(a, b) // Set breakpoint 2 +} +func main() { + sf := SomeFooer{9} + af := AnotherFooer{-1, -2, -3} + printEface(1,2.0, sf, af) + printIface(sf, af) +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/go/types/TestGoASTContext.py b/packages/Python/lldbsuite/test/lang/go/types/TestGoASTContext.py new file mode 100644 index 000000000000..8da31e9e81f7 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/types/TestGoASTContext.py @@ -0,0 +1,133 @@ +"""Test the go DWARF type parsing.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestGoASTContext(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @skipIfFreeBSD # llvm.org/pr24895 triggers assertion failure + @skipIfRemote # Not remote test suit ready + @no_debug_info_test + @skipUnlessGoInstalled + def test_with_dsym_and_python_api(self): + """Test GoASTContext dwarf parsing.""" + self.buildGo() + self.launchProcess() + self.go_builtin_types() + self.check_main_vars() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.go" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + def check_builtin(self, name, size=0, typeclass=lldb.eTypeClassBuiltin): + tl = self.target().FindTypes(name) + self.assertEqual(1, len(tl)) + t = list(tl)[0] + self.assertEqual(name, t.name) + self.assertEqual(typeclass, t.type) + if size > 0: + self.assertEqual(size, t.size) + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + def go_builtin_types(self): + address_size = self.target().GetAddressByteSize() + self.check_builtin('bool') + self.check_builtin('uint8', 1) + self.check_builtin('int8', 1) + self.check_builtin('uint16', 2) + self.check_builtin('int16', 2) + self.check_builtin('uint32', 4) + self.check_builtin('int32', 4) + self.check_builtin('uint64', 8) + self.check_builtin('int64', 8) + self.check_builtin('uintptr', address_size) + self.check_builtin('int', address_size) + self.check_builtin('uint', address_size) + self.check_builtin('float32', 4) + self.check_builtin('float64', 8) + self.check_builtin('complex64', 8, lldb.eTypeClassComplexFloat) + self.check_builtin('complex128', 16, lldb.eTypeClassComplexFloat) + + def var(self, name): + var = self.frame().FindVariable(name) + self.assertTrue(var.IsValid(), "%s %s" % (VALID_VARIABLE, name)) + return var + + def check_main_vars(self): + v = self.var('theBool') + self.assertEqual('true', v.value) + + v = self.var('theInt') + self.assertEqual('-7', v.value) + + v = self.var('theComplex') + self.assertEqual('1 + 2i', v.value) + + v = self.var('thePointer') + self.assertTrue(v.TypeIsPointerType()) + self.assertEqual('-10', v.Dereference().value) + self.assertEqual(1, v.GetNumChildren()) + self.assertEqual('-10', v.GetChildAtIndex(0).value) + + # print() + # print(os.getpid()) + # time.sleep(60) + v = self.var('theStruct') + if v.TypeIsPointerType(): + v = v.Dereference() + self.assertEqual(2, v.GetNumChildren()) + self.assertEqual('7', v.GetChildAtIndex(0).value) + self.assertEqual('7', v.GetChildMemberWithName('myInt').value) + self.assertEqual(v.load_addr, v.GetChildAtIndex(1).GetValueAsUnsigned()) + self.assertEqual(v.load_addr, v.GetChildMemberWithName('myPointer').GetValueAsUnsigned()) + + # Test accessing struct fields through pointers. + v = v.GetChildMemberWithName('myPointer') + self.assertTrue(v.TypeIsPointerType()) + self.assertEqual(2, v.GetNumChildren()) + self.assertEqual('7', v.GetChildAtIndex(0).value) + c = v.GetChildMemberWithName('myPointer') + self.assertTrue(c.TypeIsPointerType()) + self.assertEqual(2, c.GetNumChildren()) + self.assertEqual('7', c.GetChildAtIndex(0).value) + + v = self.var('theArray') + self.assertEqual(5, v.GetNumChildren()) + for i in list(range(5)): + self.assertEqual(str(i + 1), v.GetChildAtIndex(i).value) diff --git a/packages/Python/lldbsuite/test/lang/go/types/main.go b/packages/Python/lldbsuite/test/lang/go/types/main.go new file mode 100644 index 000000000000..c74650721d7d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/go/types/main.go @@ -0,0 +1,47 @@ +package main + +import "fmt" + +type Fooer interface { + Foo() int +} + +type SomeFooer struct { + val int +} + +func (s SomeFooer) Foo() int { + return s.val +} + +type mystruct struct { + myInt int + myPointer *mystruct +} + +func main() { + theBool := true + theInt := -7 + theComplex := 1 + 2i + pointee := -10 + thePointer := &pointee + theStruct := &mystruct { myInt: 7} + theStruct.myPointer = theStruct + theArray := [5]byte{1, 2, 3, 4, 5} + theSlice := theArray[1:2] + theString := "abc" + + f := SomeFooer {9} + var theEface interface{} = f + var theFooer Fooer = f + + theChan := make(chan int) + theMap := make(map[int]string) + theMap[1] = "1" + + fmt.Println(theBool) // Set breakpoint here. + // Reference all the variables so the compiler is happy. + fmt.Println(theInt, theComplex, thePointer, theStruct.myInt) + fmt.Println(theArray[0], theSlice[0], theString) + fmt.Println(theEface, theFooer, theChan, theMap) +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/mixed/Makefile b/packages/Python/lldbsuite/test/lang/mixed/Makefile new file mode 100644 index 000000000000..860343ee907c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/mixed/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +CXX_SOURCES := foo.cpp +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/mixed/TestMixedLanguages.py b/packages/Python/lldbsuite/test/lang/mixed/TestMixedLanguages.py new file mode 100644 index 000000000000..d11f03b878ae --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/mixed/TestMixedLanguages.py @@ -0,0 +1,56 @@ +"""Test that lldb works correctly on compile units form different languages.""" + +from __future__ import print_function + + + +import os, time, re +import lldb +from lldbsuite.test.lldbtest import * + +class MixedLanguagesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_language_of_frame(self): + """Test that the language defaults to the language of the current frame.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Execute the cleanup function during test case tear down + # to restore the frame format. + def cleanup(): + self.runCmd("settings set frame-format %s" % self.format_string, check=False) + self.addTearDownHook(cleanup) + self.runCmd("settings show frame-format") + m = re.match( + '^frame-format \(format-string\) = "(.*)\"$', + self.res.GetOutput()) + self.assertTrue(m, "Bad settings string") + self.format_string = m.group(1) + + # Change the default format to print the language. + format_string = "frame #${frame.index}: ${frame.pc}{ ${module.file.basename}`${function.name}{${function.pc-offset}}}{, lang=${language}}\n" + self.runCmd("settings set frame-format %s" % format_string) + self.expect("settings show frame-format", SETTING_MSG("frame-format"), + substrs = [format_string]) + + # Run to BP at main (in main.c) and test that the language is C. + self.runCmd("breakpoint set -n main") + self.runCmd("run") + self.expect("thread backtrace", + substrs = ["`main", "lang=c"]) + # Make sure evaluation of C++11 fails. + self.expect("expr foo != nullptr", error=True, + startstr = "error") + + # Run to BP at foo (in foo.cpp) and test that the language is C++. + self.runCmd("breakpoint set -n foo") + self.runCmd("continue") + self.expect("thread backtrace", + substrs = ["`::foo()", "lang=c++"]) + # Make sure we can evaluate an expression requiring C++11 + # (note: C++11 is enabled by default for C++). + self.expect("expr foo != nullptr", + patterns = ["true"]) diff --git a/packages/Python/lldbsuite/test/lang/mixed/foo.cpp b/packages/Python/lldbsuite/test/lang/mixed/foo.cpp new file mode 100644 index 000000000000..8a5a6a2b5416 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/mixed/foo.cpp @@ -0,0 +1,11 @@ +namespace ns { + int func(void) + { + return 0; + } +} + +extern "C" int foo(void) +{ + return ns::func(); +} diff --git a/packages/Python/lldbsuite/test/lang/mixed/main.c b/packages/Python/lldbsuite/test/lang/mixed/main.c new file mode 100644 index 000000000000..f5c5d19f2c89 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/mixed/main.c @@ -0,0 +1,15 @@ +int foo(void); +static int static_value = 0; + +int +bar() +{ + static_value++; + return static_value; +} + +int main (int argc, char const *argv[]) +{ + bar(); // breakpoint_in_main + return foo(); +} diff --git a/packages/Python/lldbsuite/test/lang/objc/.categories b/packages/Python/lldbsuite/test/lang/objc/.categories new file mode 100644 index 000000000000..72cf07c1efea --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/.categories @@ -0,0 +1 @@ +objc diff --git a/packages/Python/lldbsuite/test/lang/objc/blocks/Makefile b/packages/Python/lldbsuite/test/lang/objc/blocks/Makefile new file mode 100644 index 000000000000..0af83591826f --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/blocks/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := ivars-in-blocks.m main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/blocks/TestObjCIvarsInBlocks.py b/packages/Python/lldbsuite/test/lang/objc/blocks/TestObjCIvarsInBlocks.py new file mode 100644 index 000000000000..6a1cde1a3cb3 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/blocks/TestObjCIvarsInBlocks.py @@ -0,0 +1,103 @@ +"""Test printing ivars and ObjC objects captured in blocks that are made in methods of an ObjC class.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestObjCIvarsInBlocks(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.m" + self.class_source = "ivars-in-blocks.m" + self.class_source_file_spec = lldb.SBFileSpec(self.class_source) + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + @expectedFailurei386 # This test requires the 2.0 runtime, so it will fail on i386. + def test_with_python_api(self): + """Test printing the ivars of the self when captured in blocks""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateBySourceRegex ('// Break here inside the block.', self.class_source_file_spec) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + breakpoint_two = target.BreakpointCreateBySourceRegex ('// Break here inside the class method block.', self.class_source_file_spec) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue (process, "Created a process.") + self.assertTrue (process.GetState() == lldb.eStateStopped, "Stopped it too.") + + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue (len(thread_list) == 1) + thread = thread_list[0] + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (frame, "frame 0 is valid") + + # First use the FindVariable API to see if we can find the ivar by undecorated name: + direct_blocky = frame.GetValueForVariablePath ("blocky_ivar") + self.assertTrue(direct_blocky, "Found direct access to blocky_ivar.") + + # Now get it as a member of "self" and make sure the two values are equal: + self_var = frame.GetValueForVariablePath ("self") + self.assertTrue (self_var, "Found self in block.") + indirect_blocky = self_var.GetChildMemberWithName ("blocky_ivar") + self.assertTrue (indirect_blocky, "Found blocky_ivar through self") + + error = lldb.SBError() + direct_value = direct_blocky.GetValueAsSigned(error) + self.assertTrue (error.Success(), "Got direct value for blocky_ivar") + + indirect_value = indirect_blocky.GetValueAsSigned (error) + self.assertTrue (error.Success(), "Got indirect value for blocky_ivar") + + self.assertTrue (direct_value == indirect_value, "Direct and indirect values are equal.") + + # Now make sure that we can get at the captured ivar through the expression parser. + # Doing a little trivial math will force this into the real expression parser: + direct_expr = frame.EvaluateExpression ("blocky_ivar + 10") + self.assertTrue (direct_expr, "Got blocky_ivar through the expression parser") + + # Again, get the value through self directly and make sure they are the same: + indirect_expr = frame.EvaluateExpression ("self->blocky_ivar + 10") + self.assertTrue (indirect_expr, "Got blocky ivar through expression parser using self.") + + direct_value = direct_expr.GetValueAsSigned (error) + self.assertTrue (error.Success(), "Got value from direct use of expression parser") + + indirect_value = indirect_expr.GetValueAsSigned (error) + self.assertTrue (error.Success(), "Got value from indirect access using the expression parser") + + self.assertTrue (direct_value == indirect_value, "Direct ivar access and indirect through expression parser produce same value.") + + process.Continue() + self.assertTrue (process.GetState() == lldb.eStateStopped, "Stopped at the second breakpoint.") + + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint_two) + self.assertTrue (len(thread_list) == 1) + thread = thread_list[0] + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (frame, "frame 0 is valid") + + expr = frame.EvaluateExpression("(ret)") + self.assertTrue (expr, "Successfully got a local variable in a block in a class method.") + + ret_value_signed = expr.GetValueAsSigned (error) + # print('ret_value_signed = %i' % (ret_value_signed)) + self.assertTrue (ret_value_signed == 5, "The local variable in the block was what we expected.") diff --git a/packages/Python/lldbsuite/test/lang/objc/blocks/ivars-in-blocks.h b/packages/Python/lldbsuite/test/lang/objc/blocks/ivars-in-blocks.h new file mode 100644 index 000000000000..1ceac3361ac0 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/blocks/ivars-in-blocks.h @@ -0,0 +1,11 @@ +#import + +@interface IAmBlocky : NSObject +{ + @public + int blocky_ivar; +} ++ (void) classMethod; +- (IAmBlocky *) init; +- (int) callABlock: (int) block_value; +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/blocks/ivars-in-blocks.m b/packages/Python/lldbsuite/test/lang/objc/blocks/ivars-in-blocks.m new file mode 100644 index 000000000000..1098a9136ae2 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/blocks/ivars-in-blocks.m @@ -0,0 +1,57 @@ +#import "ivars-in-blocks.h" + +typedef int (^my_block_ptr_type) (int); + +@interface IAmBlocky() +{ + int _hidden_ivar; + my_block_ptr_type _block_ptr; +} + +@end + +@implementation IAmBlocky + ++ (int) addend +{ + return 3; +} + ++ (void) classMethod +{ + int (^my_block)(int) = ^(int foo) + { + int ret = foo + [self addend]; + return ret; // Break here inside the class method block. + }; + printf("%d\n", my_block(2)); +} + +- (void) makeBlockPtr; +{ + _block_ptr = ^(int inval) + { + _hidden_ivar += inval; + return blocky_ivar * inval; // Break here inside the block. + }; +} + +- (IAmBlocky *) init +{ + blocky_ivar = 10; + _hidden_ivar = 20; + // Interesting... Apparently you can't make a block in your init method. This crashes... + // [self makeBlockPtr]; + return self; +} + +- (int) callABlock: (int) block_value +{ + if (_block_ptr == NULL) + [self makeBlockPtr]; + int ret = _block_ptr (block_value); + [IAmBlocky classMethod]; + return ret; +} +@end + diff --git a/packages/Python/lldbsuite/test/lang/objc/blocks/main.m b/packages/Python/lldbsuite/test/lang/objc/blocks/main.m new file mode 100644 index 000000000000..0c56f45da464 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/blocks/main.m @@ -0,0 +1,10 @@ +#import "ivars-in-blocks.h" + +int +main (int argc, char **argv) +{ + IAmBlocky *my_blocky = [[IAmBlocky alloc] init]; + int blocky_value; + blocky_value = [my_blocky callABlock: 33]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/forward-decl/Container.h b/packages/Python/lldbsuite/test/lang/objc/forward-decl/Container.h new file mode 100644 index 000000000000..85bbd06b161c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/forward-decl/Container.h @@ -0,0 +1,13 @@ +#import + +@class ForwardDeclaredClass; + +@interface Container : NSObject { +@public + ForwardDeclaredClass *member; +} + +-(id)init; +-(ForwardDeclaredClass*)getMember; + +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/forward-decl/Container.m b/packages/Python/lldbsuite/test/lang/objc/forward-decl/Container.m new file mode 100644 index 000000000000..4d2139ff5fc6 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/forward-decl/Container.m @@ -0,0 +1,27 @@ +#import "Container.h" + +@interface ForwardDeclaredClass : NSObject +{ + int a; + int b; +} +@end + +@implementation ForwardDeclaredClass + +@end + +@implementation Container + +-(id)init +{ + member = [ForwardDeclaredClass alloc]; + return [super init]; +} + +-(ForwardDeclaredClass *)getMember +{ + return member; +} + +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/forward-decl/Makefile b/packages/Python/lldbsuite/test/lang/objc/forward-decl/Makefile new file mode 100644 index 000000000000..b99925b50b38 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/forward-decl/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +DYLIB_NAME := Container +DYLIB_OBJC_SOURCES := Container.m +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/forward-decl/TestForwardDecl.py b/packages/Python/lldbsuite/test/lang/objc/forward-decl/TestForwardDecl.py new file mode 100644 index 000000000000..207518abc91b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/forward-decl/TestForwardDecl.py @@ -0,0 +1,54 @@ +"""Test that a forward-declared class works when its complete definition is in a library""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ForwardDeclTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.source = 'main.m' + self.line = line_number(self.source, '// Set breakpoint 0 here.') + self.shlib_names = ["Container"] + + @skipUnlessDarwin + def test_expr(self): + self.build() + + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Register our shared libraries for remote targets so they get automatically uploaded + environment = self.registerSharedLibrariesWithTarget(target, self.shlib_names) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, environment, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # This should display correctly. + self.expect("expression [j getMember]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 0x"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/forward-decl/main.m b/packages/Python/lldbsuite/test/lang/objc/forward-decl/main.m new file mode 100644 index 000000000000..8e5256e95230 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/forward-decl/main.m @@ -0,0 +1,14 @@ +#import +#import "Container.h" + +int main(int argc, const char * argv[]) +{ + + @autoreleasepool { + Container *j = [[Container alloc] init]; + + printf("member value = %p", [j getMember]); // Set breakpoint 0 here. + } + return 0; +} + diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/Makefile b/packages/Python/lldbsuite/test/lang/objc/foundation/Makefile new file mode 100644 index 000000000000..ce2ccd28321d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m my-base.m +#OBJC_SOURCES := const-strings.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/TestConstStrings.py b/packages/Python/lldbsuite/test/lang/objc/foundation/TestConstStrings.py new file mode 100644 index 000000000000..a9298dd81cf8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/TestConstStrings.py @@ -0,0 +1,53 @@ +""" +Test that objective-c constant strings are generated correctly by the expression +parser. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ConstStringTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + d = {'OBJC_SOURCES': 'const-strings.m'} + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.main_source = "const-strings.m" + self.line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + def test_break(self): + """Test constant string generation amd comparison by the expression parser.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(self.d) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, self.main_source, self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + self.expect("process status", STOPPED_DUE_TO_BREAKPOINT, + substrs = [" at %s:%d" % (self.main_source, self.line), + "stop reason = breakpoint"]) + + self.expect('expression (int)[str compare:@"hello"]', + startstr = "(int) $0 = 0") + self.expect('expression (int)[str compare:@"world"]', + startstr = "(int) $1 = -1") + + # Test empty strings, too. + self.expect('expression (int)[@"" length]', + startstr = "(int) $2 = 0") + + self.expect('expression (int)[@"123" length]', + startstr = "(int) $3 = 3") diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/TestFoundationDisassembly.py b/packages/Python/lldbsuite/test/lang/objc/foundation/TestFoundationDisassembly.py new file mode 100644 index 000000000000..88db12e95931 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/TestFoundationDisassembly.py @@ -0,0 +1,134 @@ +""" +Test the lldb disassemble command on foundation framework. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipUnlessDarwin +class FoundationDisassembleTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # rdar://problem/8504895 + # Crash while doing 'disassemble -n "-[NSNumber descriptionWithLocale:]" + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_foundation_disasm(self): + """Do 'disassemble -n func' on each and every 'Code' symbol entry from the Foundation.framework.""" + self.build() + + # Enable synchronous mode + self.dbg.SetAsync(False) + + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + foundation_framework = None + for module in target.modules: + print(module) + if module.file.basename == "Foundation": + foundation_framework = module.file.fullpath + break + + self.assertTrue(foundation_framework != None, "Foundation.framework path located") + self.runCmd("image dump symtab '%s'" % foundation_framework) + raw_output = self.res.GetOutput() + # Now, grab every 'Code' symbol and feed it into the command: + # 'disassemble -n func'. + # + # The symbol name is on the last column and trails the flag column which + # looks like '0xhhhhhhhh', i.e., 8 hexadecimal digits. + codeRE = re.compile(r""" + \ Code\ {9} # ' Code' followed by 9 SPCs, + .* # the wildcard chars, + 0x[0-9a-f]{8} # the flag column, and + \ (.+)$ # finally the function symbol. + """, re.VERBOSE) + for line in raw_output.split(os.linesep): + match = codeRE.search(line) + if match: + func = match.group(1) + #print("line:", line) + #print("func:", func) + self.runCmd('disassemble -n "%s"' % func) + + + def test_simple_disasm(self): + """Test the lldb 'disassemble' command""" + self.build() + + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + print(target) + for module in target.modules: + print(module) + + # Stop at +[NSString stringWithFormat:]. + symbol_name = "+[NSString stringWithFormat:]" + break_results = lldbutil.run_break_set_command (self, "_regexp-break %s"%(symbol_name)) + + lldbutil.check_breakpoint_result (self, break_results, symbol_name=symbol_name, num_locations=1) + + # Stop at -[MyString initWithNSString:]. + lldbutil.run_break_set_by_symbol (self, '-[MyString initWithNSString:]', num_expected_locations=1, sym_exact=True) + + # Stop at the "description" selector. + lldbutil.run_break_set_by_selector (self, 'description', num_expected_locations=1, module_name='a.out') + + # Stop at -[NSAutoreleasePool release]. + break_results = lldbutil.run_break_set_command (self, "_regexp-break -[NSAutoreleasePool release]") + lldbutil.check_breakpoint_result (self, break_results, symbol_name='-[NSAutoreleasePool release]', num_locations=1) + + self.runCmd("run", RUN_SUCCEEDED) + + # First stop is +[NSString stringWithFormat:]. + self.expect("thread backtrace", "Stop at +[NSString stringWithFormat:]", + substrs = ["Foundation`+[NSString stringWithFormat:]"]) + + # Do the disassemble for the currently stopped function. + self.runCmd("disassemble -f") + + self.runCmd("process continue") + # Skip another breakpoint for +[NSString stringWithFormat:]. + self.runCmd("process continue") + + # Followed by a.out`-[MyString initWithNSString:]. + self.expect("thread backtrace", "Stop at a.out`-[MyString initWithNSString:]", + substrs = ["a.out`-[MyString initWithNSString:]"]) + + # Do the disassemble for the currently stopped function. + self.runCmd("disassemble -f") + + self.runCmd("process continue") + + # Followed by -[MyString description]. + self.expect("thread backtrace", "Stop at -[MyString description]", + substrs = ["a.out`-[MyString description]"]) + + # Do the disassemble for the currently stopped function. + self.runCmd("disassemble -f") + + self.runCmd("process continue") + # Skip another breakpoint for -[MyString description]. + self.runCmd("process continue") + + # Followed by -[NSAutoreleasePool release]. + self.expect("thread backtrace", "Stop at -[NSAutoreleasePool release]", + substrs = ["Foundation`-[NSAutoreleasePool release]"]) + + # Do the disassemble for the currently stopped function. + self.runCmd("disassemble -f") diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py b/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py new file mode 100644 index 000000000000..8358bd5e923f --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py @@ -0,0 +1,262 @@ +""" +Set breakpoints on objective-c class and instance methods in foundation. +Also lookup objective-c data types and evaluate expressions. +""" + +from __future__ import print_function + + + +import os, os.path, time +import lldb +import string +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +file_index = 0 +@skipUnlessDarwin +class FoundationTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.main_source = "main.m" + self.line = line_number(self.main_source, '// Set break point at this line.') + + def test_break(self): + """Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Stop at +[NSString stringWithFormat:]. + break_results = lldbutil.run_break_set_command(self, "_regexp-break +[NSString stringWithFormat:]") + lldbutil.check_breakpoint_result (self, break_results, symbol_name='+[NSString stringWithFormat:]', num_locations=1) + + # Stop at -[MyString initWithNSString:]. + lldbutil.run_break_set_by_symbol (self, '-[MyString initWithNSString:]', num_expected_locations=1, sym_exact=True) + + # Stop at the "description" selector. + lldbutil.run_break_set_by_selector (self, 'description', num_expected_locations=1, module_name='a.out') + + # Stop at -[NSAutoreleasePool release]. + break_results = lldbutil.run_break_set_command(self, "_regexp-break -[NSAutoreleasePool release]") + lldbutil.check_breakpoint_result (self, break_results, symbol_name='-[NSAutoreleasePool release]', num_locations=1) + + self.runCmd("run", RUN_SUCCEEDED) + + # First stop is +[NSString stringWithFormat:]. + self.expect("thread backtrace", "Stop at +[NSString stringWithFormat:]", + substrs = ["Foundation`+[NSString stringWithFormat:]"]) + + self.runCmd("process continue") + + # Second stop is still +[NSString stringWithFormat:]. + self.expect("thread backtrace", "Stop at +[NSString stringWithFormat:]", + substrs = ["Foundation`+[NSString stringWithFormat:]"]) + + self.runCmd("process continue") + + # Followed by a.out`-[MyString initWithNSString:]. + self.expect("thread backtrace", "Stop at a.out`-[MyString initWithNSString:]", + substrs = ["a.out`-[MyString initWithNSString:]"]) + + self.runCmd("process continue") + + # Followed by -[MyString description]. + self.expect("thread backtrace", "Stop at -[MyString description]", + substrs = ["a.out`-[MyString description]"]) + + self.runCmd("process continue") + + # Followed by the same -[MyString description]. + self.expect("thread backtrace", "Stop at -[MyString description]", + substrs = ["a.out`-[MyString description]"]) + + self.runCmd("process continue") + + # Followed by -[NSAutoreleasePool release]. + self.expect("thread backtrace", "Stop at -[NSAutoreleasePool release]", + substrs = ["Foundation`-[NSAutoreleasePool release]"]) + + # rdar://problem/8542091 + # rdar://problem/8492646 + def test_data_type_and_expr(self): + """Lookup objective-c data types and evaluate expressions.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Stop at -[MyString description]. + lldbutil.run_break_set_by_symbol (self, '-[MyString description]', num_expected_locations=1, sym_exact=True) +# self.expect("breakpoint set -n '-[MyString description]", BREAKPOINT_CREATED, +# startstr = "Breakpoint created: 1: name = '-[MyString description]', locations = 1") + + self.runCmd("run", RUN_SUCCEEDED) + + # The backtrace should show we stop at -[MyString description]. + self.expect("thread backtrace", "Stop at -[MyString description]", + substrs = ["a.out`-[MyString description]"]) + + # Lookup objc data type MyString and evaluate some expressions. + + self.expect("image lookup -t NSString", DATA_TYPES_DISPLAYED_CORRECTLY, + substrs = ['name = "NSString"', + 'compiler_type = "@interface NSString']) + + self.expect("image lookup -t MyString", DATA_TYPES_DISPLAYED_CORRECTLY, + substrs = ['name = "MyString"', + 'compiler_type = "@interface MyString', + 'NSString * str;', + 'NSDate * date;']) + + self.expect("frame variable --show-types --scope", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["ARG: (MyString *) self"], + patterns = ["ARG: \(.*\) _cmd", + "(objc_selector *)|(SEL)"]) + + # rdar://problem/8651752 + # don't crash trying to ask clang how many children an empty record has + self.runCmd("frame variable *_cmd") + + # rdar://problem/8492646 + # test/foundation fails after updating to tot r115023 + # self->str displays nothing as output + self.expect("frame variable --show-types self->str", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(NSString *) self->str") + + # rdar://problem/8447030 + # 'frame variable self->date' displays the wrong data member + self.expect("frame variable --show-types self->date", VARIABLES_DISPLAYED_CORRECTLY, + startstr = "(NSDate *) self->date") + + # This should display the str and date member fields as well. + self.expect("frame variable --show-types *self", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(MyString) *self", + "(NSString *) str", + "(NSDate *) date"]) + + # isa should be accessible. + self.expect("expression self->isa", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(Class)"]) + + # This should fail expectedly. + self.expect("expression self->non_existent_member", + COMMAND_FAILED_AS_EXPECTED, error=True, + startstr = "error: 'MyString' does not have a member named 'non_existent_member'") + + # Use expression parser. + self.runCmd("expression self->str") + self.runCmd("expression self->date") + + # (lldb) expression self->str + # error: instance variable 'str' is protected + # error: 1 errors parsing expression + # + # (lldb) expression self->date + # error: instance variable 'date' is protected + # error: 1 errors parsing expression + # + + self.runCmd("breakpoint delete 1") + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("process continue") + + # rdar://problem/8542091 + # test/foundation: expr -o -- my not working? + # + # Test new feature with r115115: + # Add "-o" option to "expression" which prints the object description if available. + self.expect("expression --object-description -- my", "Object description displayed correctly", + patterns = ["Hello from.*a.out.*with timestamp: "]) + + @add_test_categories(['pyapi']) + def test_print_ivars_correctly (self): + self.build() + # See: lldb needs to use the ObjC runtime symbols for ivar offsets + # Only fails for the ObjC 2.0 runtime. + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + break1 = target.BreakpointCreateByLocation(self.main_source, self.line) + self.assertTrue(break1, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread = process.GetThreadAtIndex(0) + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + from lldbsuite.test.lldbutil import stop_reason_to_str + self.fail(STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS % + stop_reason_to_str(thread.GetStopReason())) + + # Make sure we stopped at the first breakpoint. + + cur_frame = thread.GetFrameAtIndex(0) + + line_number = cur_frame.GetLineEntry().GetLine() + self.assertTrue (line_number == self.line, "Hit the first breakpoint.") + + my_var = cur_frame.FindVariable("my") + self.assertTrue(my_var, "Made a variable object for my") + + str_var = cur_frame.FindVariable("str") + self.assertTrue(str_var, "Made a variable object for str") + + # Now make sure that the my->str == str: + + my_str_var = my_var.GetChildMemberWithName("str") + self.assertTrue(my_str_var, "Found a str ivar in my") + + str_value = int(str_var.GetValue(), 0) + + my_str_value = int(my_str_var.GetValue(), 0) + + self.assertTrue(str_value == my_str_value, "Got the correct value for my->str") + + def test_expression_lookups_objc(self): + """Test running an expression detect spurious debug info lookups (DWARF).""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Stop at -[MyString initWithNSString:]. + lldbutil.run_break_set_by_symbol (self, '-[MyString initWithNSString:]', num_expected_locations=1, sym_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + global file_index + # Log any DWARF lookups + ++file_index + logfile = os.path.join(os.getcwd(), "dwarf-lookups-" + self.getArchitecture() + "-" + str(file_index) + ".txt") + self.runCmd("log enable -f %s dwarf lookups" % (logfile)) + self.runCmd("expr self") + self.runCmd("log disable dwarf lookups") + + def cleanup(): + if os.path.exists (logfile): + os.unlink (logfile) + + self.addTearDownHook(cleanup) + + if os.path.exists (logfile): + f = open(logfile) + lines = f.readlines() + num_errors = 0 + for line in lines: + if string.find(line, "$__lldb") != -1: + if num_errors == 0: + print("error: found spurious name lookups when evaluating an expression:") + num_errors += 1 + print(line, end='') + self.assertTrue(num_errors == 0, "Spurious lookups detected") + f.close() diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods2.py b/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods2.py new file mode 100644 index 000000000000..b61a7702f1fe --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods2.py @@ -0,0 +1,169 @@ +""" +Test more expression command sequences with objective-c. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipUnlessDarwin +class FoundationTestCase2(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break at. + self.lines = [] + self.lines.append(line_number('main.m', '// Break here for selector: tests')) + self.lines.append(line_number('main.m', '// Break here for NSArray tests')) + self.lines.append(line_number('main.m', '// Break here for NSString tests')) + self.lines.append(line_number('main.m', '// Break here for description test')) + self.lines.append(line_number('main.m', '// Set break point at this line')) + + def test_more_expr_commands(self): + """More expression commands for objective-c.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Create a bunch of breakpoints. + for line in self.lines: + lldbutil.run_break_set_by_file_and_line (self, "main.m", line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Test_Selector: + self.runCmd("thread backtrace") + self.expect("expression (char *)sel_getName(sel)", + substrs = ["(char *)", + "length"]) + + self.runCmd("process continue") + + # Test_NSArray: + self.runCmd("thread backtrace") + self.runCmd("process continue") + + # Test_NSString: + self.runCmd("thread backtrace") + self.runCmd("process continue") + + # Test_MyString: + self.runCmd("thread backtrace") + self.expect("expression (char *)sel_getName(_cmd)", + substrs = ["(char *)", + "description"]) + + self.runCmd("process continue") + + def test_NSArray_expr_commands(self): + """Test expression commands for NSArray.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside Test_NSArray: + line = self.lines[1] + lldbutil.run_break_set_by_file_and_line (self, "main.m", line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Test_NSArray: + self.runCmd("thread backtrace") + self.expect("expression (int)[nil_mutable_array count]", + patterns = ["\(int\) \$.* = 0"]) + self.expect("expression (int)[array1 count]", + patterns = ["\(int\) \$.* = 3"]) + self.expect("expression (int)[array2 count]", + patterns = ["\(int\) \$.* = 3"]) + self.expect("expression (int)array1.count", + patterns = ["\(int\) \$.* = 3"]) + self.expect("expression (int)array2.count", + patterns = ["\(int\) \$.* = 3"]) + self.runCmd("process continue") + + def test_NSString_expr_commands(self): + """Test expression commands for NSString.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside Test_NSString: + line = self.lines[2] + lldbutil.run_break_set_by_file_and_line (self, "main.m", line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Test_NSString: + self.runCmd("thread backtrace") + self.expect("expression (int)[str length]", + patterns = ["\(int\) \$.* ="]) + self.expect("expression (int)[str_id length]", + patterns = ["\(int\) \$.* ="]) + self.expect("expression [str description]", + patterns = ["\(id\) \$.* = 0x"]) + self.expect("expression (id)[str_id description]", + patterns = ["\(id\) \$.* = 0x"]) + self.expect("expression str.length") + self.expect("expression str.description") + self.expect('expression str = @"new"') + self.runCmd("image lookup -t NSString") + self.expect('expression str = [NSString stringWithCString: "new"]') + self.runCmd("process continue") + + def test_MyString_dump(self): + """Test dump of a known Objective-C object by dereferencing it.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + line = self.lines[4] + + lldbutil.run_break_set_by_file_and_line (self, "main.m", line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("expression --show-types -- *my", + patterns = ["\(MyString\) \$.* = ", "\(MyBase\)", "\(NSObject\)", "\(Class\)"]) + self.runCmd("process continue") + + @expectedFailurei386 + def test_NSError_po(self): + """Test that po of the result of an unknown method doesn't require a cast.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + line = self.lines[4] + + lldbutil.run_break_set_by_file_and_line (self, "main.m", line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect('po [NSError errorWithDomain:@"Hello" code:35 userInfo:@{@"NSDescription" : @"be completed."}]', + substrs = ["Error Domain=Hello", "Code=35", "be completed."]) + self.runCmd("process continue") + + def test_NSError_p(self): + """Test that p of the result of an unknown method does require a cast.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + line = self.lines[4] + + lldbutil.run_break_set_by_file_and_line (self, "main.m", line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("p [NSError thisMethodIsntImplemented:0]", + error = True, + patterns = ["no known method", "cast the message send to the method's return type"]) + self.runCmd("process continue") diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjectDescriptionAPI.py b/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjectDescriptionAPI.py new file mode 100644 index 000000000000..a85f0fec9636 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjectDescriptionAPI.py @@ -0,0 +1,71 @@ +""" +Test SBValue.GetObjectDescription() with the value from SBTarget.FindGlobalVariables(). +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ObjectDescriptionAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.source = 'main.m' + self.line = line_number(self.source, '// Set break point at this line.') + + # rdar://problem/10857337 + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_find_global_variables_then_object_description(self): + """Exercise SBTarget.FindGlobalVariables() API.""" + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + exe = os.path.join(os.getcwd(), 'b.out') + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + # Make sure we hit our breakpoint: + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue (len(thread_list) == 1) + + thread = thread_list[0] + frame0 = thread.GetFrameAtIndex(0) + + # Note my_global_str's object description prints fine here. + value_list1 = frame0.GetVariables(True, True, True, True) + for v in value_list1: + self.DebugSBValue(v) + if self.TraceOn(): + print("val:", v) + print("object description:", v.GetObjectDescription()) + if v.GetName() == 'my_global_str': + self.assertTrue(v.GetObjectDescription() == 'This is a global string') + + # But not here! + value_list2 = target.FindGlobalVariables('my_global_str', 3) + for v in value_list2: + self.DebugSBValue(v) + if self.TraceOn(): + print("val:", v) + print("object description:", v.GetObjectDescription()) + if v.GetName() == 'my_global_str': + self.assertTrue(v.GetObjectDescription() == 'This is a global string') diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/TestRuntimeTypes.py b/packages/Python/lldbsuite/test/lang/objc/foundation/TestRuntimeTypes.py new file mode 100644 index 000000000000..8f191721d89b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/TestRuntimeTypes.py @@ -0,0 +1,48 @@ +""" +Test that Objective-C methods from the runtime work correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipUnlessDarwin +class RuntimeTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_break(self): + """Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'.""" + if self.getArchitecture() != 'x86_64': + self.skipTest("This only applies to the v2 runtime") + + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Stop at -[MyString description]. + lldbutil.run_break_set_by_symbol (self, '-[MyString description]', num_expected_locations=1, sym_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The backtrace should show we stop at -[MyString description]. + self.expect("thread backtrace", "Stop at -[MyString description]", + substrs = ["a.out`-[MyString description]"]) + + # Use runtime information about NSString. + + # The length property should be usable. + self.expect("expression str.length", VARIABLES_DISPLAYED_CORRECTLY, + patterns = [r"(\(unsigned long long\))|\(NSUInteger\)"]) + + # Static methods on NSString should work. + self.expect("expr [NSString stringWithCString:\"foo\" encoding:1]", VALID_TYPE, + substrs = ["(id)", "$1"]) + + self.expect("po $1", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/TestSymbolTable.py b/packages/Python/lldbsuite/test/lang/objc/foundation/TestSymbolTable.py new file mode 100644 index 000000000000..72952c1878b1 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/TestSymbolTable.py @@ -0,0 +1,68 @@ +""" +Test symbol table access for main.m. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +@skipUnlessDarwin +class FoundationSymtabTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + symbols_list = ['-[MyString initWithNSString:]', + '-[MyString dealloc]', + '-[MyString description]', + '-[MyString descriptionPauses]', # synthesized property + '-[MyString setDescriptionPauses:]', # synthesized property + 'Test_Selector', + 'Test_NSString', + 'Test_MyString', + 'Test_NSArray', + 'main' + ] + + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test symbol table access with Python APIs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # + # Exercise Python APIs to access the symbol table entries. + # + + # Create the filespec by which to locate our a.out module. + filespec = lldb.SBFileSpec(exe, False) + + module = target.FindModule(filespec) + self.assertTrue(module, VALID_MODULE) + + # Create the set of known symbols. As we iterate through the symbol + # table, remove the symbol from the set if it is a known symbol. + expected_symbols = set(self.symbols_list) + for symbol in module: + self.assertTrue(symbol, VALID_SYMBOL) + #print("symbol:", symbol) + name = symbol.GetName() + if name in expected_symbols: + #print("Removing %s from known_symbols %s" % (name, expected_symbols)) + expected_symbols.remove(name) + + # At this point, the known_symbols set should have become an empty set. + # If not, raise an error. + #print("symbols unaccounted for:", expected_symbols) + self.assertTrue(len(expected_symbols) == 0, + "All the known symbols are accounted for") diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/const-strings.m b/packages/Python/lldbsuite/test/lang/objc/foundation/const-strings.m new file mode 100644 index 000000000000..8a43abee7b84 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/const-strings.m @@ -0,0 +1,24 @@ +#import + +// Tests to run: + +// Breakpoint 1 +// -- +// (lldb) expr (int)[str compare:@"hello"] +// (int) $0 = 0 +// (lldb) expr (int)[str compare:@"world"] +// (int) $1 = -1 +// (lldb) expr (int)[@"" length] +// (int) $2 = 0 + +int main () +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *str = [NSString stringWithCString:"hello" encoding:NSASCIIStringEncoding]; + + NSLog(@"String \"%@\" has length %lu", str, [str length]); // Set breakpoint here. + + [pool drain]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/main.m b/packages/Python/lldbsuite/test/lang/objc/foundation/main.m new file mode 100644 index 000000000000..519bec5a3e6a --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/main.m @@ -0,0 +1,141 @@ +#import +#include +#import "my-base.h" + +@interface MyString : MyBase { + NSString *str; + NSDate *date; + BOOL _desc_pauses; +} + +@property(retain) NSString * str_property; +@property BOOL descriptionPauses; + +- (id)initWithNSString:(NSString *)string; +@end + +@implementation MyString +@synthesize descriptionPauses = _desc_pauses; +@synthesize str_property = str; + +- (id)initWithNSString:(NSString *)string +{ + if (self = [super init]) + { + str = [NSString stringWithString:string]; + date = [NSDate date]; + } + self.descriptionPauses = NO; + return self; +} + +- (void)dealloc +{ + [date release]; + [str release]; + [super dealloc]; +} + +- (NSString *)description +{ + // Set a breakpoint on '-[MyString description]' and test expressions: + // expression (char *)sel_getName(_cmd) + if (self.descriptionPauses) // Break here for description test + { + printf ("\nAbout to sleep.\n"); + usleep(100000); + } + + return [str stringByAppendingFormat:@" with timestamp: %@", date]; +} +@end + +int +Test_Selector () +{ + SEL sel = @selector(length); + printf("sel = %p\n", sel); + // Expressions to test here for selector: + // expression (char *)sel_getName(sel) + // The expression above should return "sel" as it should be just + // a uniqued C string pointer. We were seeing the result pointer being + // truncated with recent LLDBs. + return 0; // Break here for selector: tests +} + +int +Test_NSString (const char *program) +{ + NSString *str = [NSString stringWithFormat:@"Hello from '%s'", program]; + NSLog(@"NSString instance: %@", str); + printf("str = '%s'\n", [str cStringUsingEncoding: [NSString defaultCStringEncoding]]); + printf("[str length] = %zu\n", (size_t)[str length]); + printf("[str description] = %s\n", [[str description] UTF8String]); + id str_id = str; + // Expressions to test here for NSString: + // expression (char *)sel_getName(sel) + // expression [str length] + // expression [str_id length] + // expression [str description] + // expression [str_id description] + // expression str.length + // expression str.description + // expression str = @"new" + // expression str = [NSString stringWithFormat: @"%cew", 'N'] + return 0; // Break here for NSString tests +} + +NSString *my_global_str = NULL; + +void +Test_MyString (const char *program) +{ + my_global_str = @"This is a global string"; + NSString *str = [NSString stringWithFormat:@"Hello from '%s'", program]; + MyString *my = [[MyString alloc] initWithNSString:str]; + NSLog(@"MyString instance: %@", [my description]); + my.descriptionPauses = YES; // Set break point at this line. Test 'expression -o -- my'. + NSLog(@"MyString instance: %@", [my description]); +} + +int +Test_NSArray () +{ + NSMutableArray *nil_mutable_array = nil; + NSArray *array1 = [NSArray arrayWithObjects: @"array1 object1", @"array1 object2", @"array1 object3", nil]; + NSArray *array2 = [NSArray arrayWithObjects: array1, @"array2 object2", @"array2 object3", nil]; + // Expressions to test here for NSArray: + // expression [nil_mutable_array count] + // expression [array1 count] + // expression array1.count + // expression [array2 count] + // expression array2.count + id obj; + // After each object at index call, use expression and validate object + obj = [array1 objectAtIndex: 0]; // Break here for NSArray tests + obj = [array1 objectAtIndex: 1]; + obj = [array1 objectAtIndex: 2]; + + obj = [array2 objectAtIndex: 0]; + obj = [array2 objectAtIndex: 1]; + obj = [array2 objectAtIndex: 2]; + NSUInteger count = [nil_mutable_array count]; + return 0; +} + + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + Test_Selector(); + Test_NSArray (); + Test_NSString (argv[0]); + Test_MyString (argv[0]); + + printf("sizeof(id) = %zu\n", sizeof(id)); + printf("sizeof(Class) = %zu\n", sizeof(Class)); + printf("sizeof(SEL) = %zu\n", sizeof(SEL)); + + [pool release]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/my-base.h b/packages/Python/lldbsuite/test/lang/objc/foundation/my-base.h new file mode 100644 index 000000000000..53202aa0de37 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/my-base.h @@ -0,0 +1,8 @@ +@interface MyBase : NSObject +{ +#if !__OBJC2__ + int maybe_used; // The 1.0 runtime needs to have backed properties... +#endif +} +@property int propertyMovesThings; +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/foundation/my-base.m b/packages/Python/lldbsuite/test/lang/objc/foundation/my-base.m new file mode 100644 index 000000000000..0c316b244f26 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/foundation/my-base.m @@ -0,0 +1,10 @@ +#import +#import "my-base.h" +@implementation MyBase +#if __OBJC2__ +@synthesize propertyMovesThings; +#else +@synthesize propertyMovesThings = maybe_used; +#endif +@end + diff --git a/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/InternalDefiner.h b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/InternalDefiner.h new file mode 100644 index 000000000000..59652d4b09c0 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/InternalDefiner.h @@ -0,0 +1,11 @@ +#import +#import + +@interface InternalDefiner : NSObject { +@public + uintptr_t foo; +} + +-(id)initWithFoo:(uintptr_t)f andBar:(uintptr_t)b; + +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/InternalDefiner.m b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/InternalDefiner.m new file mode 100644 index 000000000000..1a10ce021ce3 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/InternalDefiner.m @@ -0,0 +1,31 @@ +#import "InternalDefiner.h" + +@interface InternalDefiner () { + uintptr_t bar; +} + +@end + +@implementation InternalDefiner + +-(id)init +{ + if (self = [super init]) + { + foo = 2; + bar = 3; + } + return self; +} + +-(id)initWithFoo:(uintptr_t)f andBar:(uintptr_t)b +{ + if (self = [super init]) + { + foo = f; + bar = b; + } + return self; +} + +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/Makefile b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/Makefile new file mode 100644 index 000000000000..1768d11ca279 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +DYLIB_NAME := InternalDefiner +DYLIB_OBJC_SOURCES := InternalDefiner.m +OBJC_SOURCES := main.m + +LD_EXTRAS = -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/TestHiddenIvars.py b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/TestHiddenIvars.py new file mode 100644 index 000000000000..e85dd8f48dec --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/TestHiddenIvars.py @@ -0,0 +1,174 @@ +"""Test that hidden ivars in a shared library are visible from the main executable.""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import subprocess + +class HiddenIvarsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.source = 'main.m' + self.line = line_number(self.source, '// breakpoint1') + # The makefile names of the shared libraries as they appear in DYLIB_NAME. + # The names should have no loading "lib" or extension as they will be localized + self.shlib_names = ["InternalDefiner"] + + @skipUnlessDarwin + @skipIfDwarf # This test requires a stripped binary and a dSYM + @skipIfDWO # This test requires a stripped binary and a dSYM + def test_expr_stripped(self): + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + else: + self.build() + self.expr(True) + + @skipUnlessDarwin + def test_expr(self): + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + else: + self.build() + self.expr(False) + + @skipUnlessDarwin + @skipIfDwarf # This test requires a stripped binary and a dSYM + @skipIfDWO # This test requires a stripped binary and a dSYM + def test_frame_variable_stripped(self): + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + else: + self.build() + self.frame_var(True) + + @skipUnlessDarwin + def test_frame_variable(self): + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + else: + self.build() + self.frame_var(False) + + @unittest2.expectedFailure("rdar://18683637") + @skipUnlessDarwin + def test_frame_variable_across_modules(self): + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + else: + self.build() + self.common_setup(False) + self.expect("frame variable k->bar", VARIABLES_DISPLAYED_CORRECTLY, substrs = ["= 3"]) + + def common_setup(self, strip): + + if strip: + self.assertTrue(subprocess.call(['/usr/bin/strip', '-Sx', 'libInternalDefiner.dylib']) == 0, 'stripping dylib succeeded') + self.assertTrue(subprocess.call(['/bin/rm', '-rf', 'libInternalDefiner.dylib.dSYM']) == 0, 'remove dylib dSYM file succeeded') + self.assertTrue(subprocess.call(['/usr/bin/strip', '-Sx', 'a.out']) == 0, 'stripping a.out succeeded') + # Create a target by the debugger. + target = self.dbg.CreateTarget("a.out") + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Register our shared libraries for remote targets so they get automatically uploaded + environment = self.registerSharedLibrariesWithTarget(target, self.shlib_names) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, environment, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + def expr(self, strip): + self.common_setup(strip) + + # This should display correctly. + self.expect("expression (j->_definer->foo)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 4"]) + + self.expect("expression (j->_definer->bar)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 5"]) + + if strip: + self.expect("expression *(j->_definer)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo = 4"]) + else: + self.expect("expression *(j->_definer)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo = 4", "bar = 5"]) + + self.expect("expression (k->foo)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 2"]) + + self.expect("expression (k->bar)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 3"]) + + self.expect("expression k.filteredDataSource", VARIABLES_DISPLAYED_CORRECTLY, + substrs = [' = 0x', '"2 elements"']) + + if strip: + self.expect("expression *(k)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo = 2", ' = 0x', '"2 elements"']) + else: + self.expect("expression *(k)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo = 2", "bar = 3", '_filteredDataSource = 0x', '"2 elements"']) + + def frame_var(self, strip): + self.common_setup(strip) + + # This should display correctly. + self.expect("frame variable j->_definer->foo", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 4"]) + + if not strip: + self.expect("frame variable j->_definer->bar", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 5"]) + + if strip: + self.expect("frame variable *j->_definer", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo = 4"]) + else: + self.expect("frame variable *j->_definer", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo = 4", "bar = 5"]) + + self.expect("frame variable k->foo", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["= 2"]) + + self.expect("frame variable k->_filteredDataSource", VARIABLES_DISPLAYED_CORRECTLY, + substrs = [' = 0x', '"2 elements"']) + + if strip: + self.expect("frame variable *k", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo = 2", '_filteredDataSource = 0x', '"2 elements"']) + else: + self.expect("frame variable *k", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo = 2", "bar = 3", '_filteredDataSource = 0x', '"2 elements"']) diff --git a/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/main.m b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/main.m new file mode 100644 index 000000000000..1795d56e7d8a --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/hidden-ivars/main.m @@ -0,0 +1,54 @@ +#import +#import "InternalDefiner.h" + +@interface Container : NSObject { +@public + InternalDefiner *_definer; +} + +-(id)init; +@end + +@implementation Container + +-(id)init +{ + if (self = [super init]) + { + _definer = [[InternalDefiner alloc] initWithFoo:4 andBar:5]; + } + return self; +} + +@end + +@interface InheritContainer : InternalDefiner +@property (nonatomic, strong) NSMutableArray *filteredDataSource; +-(id)init; +@end + +@implementation InheritContainer + +-(id)init +{ + if (self = [super initWithFoo:2 andBar:3]) + { + self.filteredDataSource = [NSMutableArray arrayWithObjects:@"hello", @"world", nil]; + } + return self; +} + +@end + +int main(int argc, const char * argv[]) +{ + @autoreleasepool { + Container *j = [[Container alloc] init]; + InheritContainer *k = [[InheritContainer alloc] init]; + + printf("ivar value = %u\n", (unsigned)j->_definer->foo); // breakpoint1 + printf("ivar value = %u\n", (unsigned)k->foo); + } + return 0; +} + diff --git a/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/Makefile b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/Makefile new file mode 100644 index 000000000000..329ceabeea97 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/Makefile @@ -0,0 +1,12 @@ +LEVEL = ../../../make + +myclass.o: myclass.h myclass.m + $(CC) myclass.m -c -o myclass.o + +repro: myclass.o repro.m + $(CC) -g -O0 myclass.o repro.m -framework Foundation + +cleanup: + rm -r myclass.o + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/TestObjCiVarIMP.py b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/TestObjCiVarIMP.py new file mode 100644 index 000000000000..9ed2bb98e6e1 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/TestObjCiVarIMP.py @@ -0,0 +1,63 @@ +""" +Test that dynamically discovered ivars of type IMP do not crash LLDB +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +import lldbsuite.support.seven as seven + +def execute_command (command): + # print('%% %s' % (command)) + (exit_status, output) = seven.get_command_status_output(command) + # if output: + # print(output) + # print('status = %u' % (exit_status)) + return exit_status + +class ObjCiVarIMPTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + @no_debug_info_test + def test_imp_ivar_type(self): + """Test that dynamically discovered ivars of type IMP do not crash LLDB""" + if self.getArchitecture() == 'i386': + # rdar://problem/9946499 + self.skipTest("Dynamic types for ObjC V1 runtime not implemented") + + execute_command("make repro") + def cleanup(): + execute_command("make cleanup") + self.addTearDownHook(cleanup) + + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoint + + bkpt = lldbutil.run_break_set_by_source_regexp (self, "break here") + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + self.expect('frame variable --ptr-depth=1 --show-types -d run -- object', substrs=[ + '(MyClass *) object = 0x', + '(void *) myImp = 0x' + ]) + self.expect('disassemble --start-address `((MyClass*)object)->myImp`', substrs=[ + '-[MyClass init]' + ]) diff --git a/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/myclass.h b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/myclass.h new file mode 100644 index 000000000000..da28d1e05181 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/myclass.h @@ -0,0 +1,6 @@ +#import + +@interface MyClass : NSObject +{} +- (id)init; +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/myclass.m b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/myclass.m new file mode 100644 index 000000000000..85b2fcfe9b3c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/myclass.m @@ -0,0 +1,16 @@ +#import +#import "myclass.h" + +@implementation MyClass +{ + IMP myImp; +} +- (id)init { + if (self = [super init]) + { + SEL theSelector = @selector(init); + self->myImp = [self methodForSelector:theSelector]; + } + return self; +} +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/repro.m b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/repro.m new file mode 100644 index 000000000000..14f911f07dd8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/ivar-IMP/repro.m @@ -0,0 +1,7 @@ +#import +#import "myclass.h" + +int main() { + id object = [MyClass new]; + return 0; // break here +} diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/Makefile b/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/Makefile new file mode 100644 index 000000000000..e42b59f9518c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make +OBJC_SOURCES := main.m + +CFLAGS += -fmodules -gmodules -g + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/TestModulesAutoImport.py b/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/TestModulesAutoImport.py new file mode 100644 index 000000000000..2c052aae3ba1 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/TestModulesAutoImport.py @@ -0,0 +1,53 @@ +"""Test that importing modules in Objective-C works as expected.""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +import platform +import lldbsuite.test.lldbutil as lldbutil + +from distutils.version import StrictVersion + +from lldbsuite.test.lldbtest import * + +class ObjCModulesAutoImportTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.m', '// Set breakpoint 0 here.') + + @skipUnlessDarwin + @unittest2.expectedFailure("rdar://problem/19991953") + @expectedFailureDarwin # clang: error: unknown argument: '-gmodules' + @unittest2.skipIf(platform.system() != "Darwin" or StrictVersion('12.0.0') > platform.release(), "Only supported on Darwin 12.0.0+") + def test_expr(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.runCmd("settings set target.auto-import-clang-modules true") + + self.expect("p getpid()", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["pid_t"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/main.m b/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/main.m new file mode 100644 index 000000000000..5452ffd9bd15 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-auto-import/main.m @@ -0,0 +1,7 @@ +@import Darwin; + +int main() +{ + size_t ret = printf("Stop here\n"); // Set breakpoint 0 here. + return ret; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/Makefile b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/Makefile new file mode 100644 index 000000000000..cbd95de007d8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m myModule.m + +include $(LEVEL)/Makefile.rules + +CFLAGS += -fmodules -I$(PWD) +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/TestIncompleteModules.py b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/TestIncompleteModules.py new file mode 100644 index 000000000000..8667e628d464 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/TestIncompleteModules.py @@ -0,0 +1,61 @@ +"""Test that DWARF types are trusted over module types""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +import platform +import lldbsuite.test.lldbutil as lldbutil + +from distutils.version import StrictVersion + +from lldbsuite.test.lldbtest import * + +class IncompleteModulesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.m', '// Set breakpoint 0 here.') + + @skipUnlessDarwin + @unittest2.expectedFailure("rdar://20416388") + @unittest2.skipIf(platform.system() != "Darwin" or StrictVersion('12.0.0') > platform.release(), "Only supported on Darwin 12.0.0+") + def test_expr(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.runCmd("settings set target.clang-module-search-paths \"" + os.getcwd() + "\"") + + self.expect("expr @import myModule; 3", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["int", "3"]) + + self.expect("expr [myObject privateMethod]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["int", "5"]) + + self.expect("expr MIN(2,3)", "#defined macro was found", + substrs = ["int", "2"]) + + self.expect("expr MAX(2,3)", "#undefd macro was correcltly not found", + error=True) diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/main.m b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/main.m new file mode 100644 index 000000000000..8ebfb0c1f11a --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/main.m @@ -0,0 +1,11 @@ +@import Foundation; +@import myModule; + +int main() +{ + @autoreleasepool + { + MyClass *myObject = [MyClass alloc]; + [myObject publicMethod]; // Set breakpoint 0 here. + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/module.map b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/module.map new file mode 100644 index 000000000000..2ef8064d15b4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/module.map @@ -0,0 +1,4 @@ +module myModule { + header "myModule.h" + export * +} diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/myModule.h b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/myModule.h new file mode 100644 index 000000000000..d03dde0d07aa --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/myModule.h @@ -0,0 +1,8 @@ +@import Foundation; + +#undef MAX + +@interface MyClass : NSObject { +}; +-(void)publicMethod; +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/myModule.m b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/myModule.m new file mode 100644 index 000000000000..d6a2619d8016 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-incomplete/myModule.m @@ -0,0 +1,14 @@ +#include "myModule.h" +#include "stdio.h" + +@implementation MyClass { +}; +-(void)publicMethod { + printf("Hello public!\n"); +} +-(int)privateMethod { + printf("Hello private!\n"); + return 5; +} +@end + diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/Makefile b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/Makefile new file mode 100644 index 000000000000..6ad9e0010bb0 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +C_SOURCES := myModule.c + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +CFLAGS += -fmodules -I$(PWD) diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/TestModulesInlineFunctions.py b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/TestModulesInlineFunctions.py new file mode 100644 index 000000000000..d9d94a8cf321 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/TestModulesInlineFunctions.py @@ -0,0 +1,55 @@ +"""Test that inline functions from modules are imported correctly""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +import platform +import lldbsuite.test.lldbutil as lldbutil + +from distutils.version import StrictVersion + +from lldbsuite.test.lldbtest import * + +class ModulesInlineFunctionsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.m', '// Set breakpoint here.') + + @skipUnlessDarwin + @unittest2.skipIf(platform.system() != "Darwin" or StrictVersion('12.0.0') > platform.release(), "Only supported on Darwin 12.0.0+") + @expectedFailureDarwin("llvm.org/pr25743") + def test_expr(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.runCmd("settings set target.clang-module-search-paths \"" + os.getcwd() + "\"") + + self.expect("expr @import myModule; 3", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["int", "3"]) + + self.expect("expr isInline(2)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["4"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/main.m b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/main.m new file mode 100644 index 000000000000..13a5bf316ee9 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/main.m @@ -0,0 +1,9 @@ +@import Darwin; +@import myModule; + +int main() +{ + int a = isInline(2); + int b = notInline(); + printf("%d %d\n", a, b); // Set breakpoint here. +} diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/module.map b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/module.map new file mode 100644 index 000000000000..2ef8064d15b4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/module.map @@ -0,0 +1,4 @@ +module myModule { + header "myModule.h" + export * +} diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/myModule.c b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/myModule.c new file mode 100644 index 000000000000..ad3c85d155e4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/myModule.c @@ -0,0 +1,7 @@ +#include "myModule.h" + +int notInline() +{ + return 3; +} + diff --git a/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/myModule.h b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/myModule.h new file mode 100644 index 000000000000..d50d0101f645 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules-inline-functions/myModule.h @@ -0,0 +1,7 @@ +int notInline(); + +static __inline__ __attribute__ ((always_inline)) int isInline(int a) +{ + int b = a + a; + return b; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/modules/Makefile b/packages/Python/lldbsuite/test/lang/objc/modules/Makefile new file mode 100644 index 000000000000..ad3cb3fadcde --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/modules/TestObjCModules.py b/packages/Python/lldbsuite/test/lang/objc/modules/TestObjCModules.py new file mode 100644 index 000000000000..04fc07281811 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules/TestObjCModules.py @@ -0,0 +1,71 @@ +"""Test that importing modules in Objective-C works as expected.""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +import platform +import lldbsuite.test.lldbutil as lldbutil + +from distutils.version import StrictVersion + +from lldbsuite.test.lldbtest import * + +class ObjCModulesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.m', '// Set breakpoint 0 here.') + + @skipUnlessDarwin + @unittest2.expectedFailure("rdar://20416388") + @unittest2.skipIf(platform.system() != "Darwin" or StrictVersion('12.0.0') > platform.release(), "Only supported on Darwin 12.0.0+") + def test_expr(self): + if not self.applies(): + return + + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.expect("expr @import Darwin; 3", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["int", "3"]) + + self.expect("expr getpid()", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["pid_t"]) + + self.expect("expr @import Foundation; 4", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["int", "4"]) + + self.expect("expr string.length", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["NSUInteger", "5"]) + + self.expect("expr array.count", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["NSUInteger", "3"]) + + self.expect("p *[NSURL URLWithString:@\"http://lldb.llvm.org\"]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["NSURL", "isa", "_urlString"]) + + self.expect("p [NSURL URLWithString:@\"http://lldb.llvm.org\"].scheme", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["http"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/modules/main.m b/packages/Python/lldbsuite/test/lang/objc/modules/main.m new file mode 100644 index 000000000000..99b50f9620d6 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/modules/main.m @@ -0,0 +1,12 @@ +#import + +int main() +{ + @autoreleasepool + { + NSString *string = @"Hello"; + NSArray *array = @[ @1, @2, @3 ]; + + NSLog(@"Stop here"); // Set breakpoint 0 here. + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc++/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc++/Makefile new file mode 100644 index 000000000000..910d4943ef05 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc++/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJCXX_SOURCES := main.mm +LD_EXTRAS = -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc++/TestObjCXX.py b/packages/Python/lldbsuite/test/lang/objc/objc++/TestObjCXX.py new file mode 100644 index 000000000000..38ef853ae112 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc++/TestObjCXX.py @@ -0,0 +1,33 @@ +""" +Make sure that ivars of Objective-C++ classes are visible in LLDB. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ObjCXXTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_break(self): + """Test ivars of Objective-C++ classes""" + if self.getArchitecture() == 'i386': + self.skipTest("requires Objective-C 2.0 runtime") + + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, 'breakpoint 1', num_expected_locations=1) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("expr f->f", "Found ivar in class", + substrs = ["= 3"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc++/main.mm b/packages/Python/lldbsuite/test/lang/objc/objc++/main.mm new file mode 100644 index 000000000000..50d2f0a8df37 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc++/main.mm @@ -0,0 +1,19 @@ +#include + +@interface F : NSObject +@end + +@implementation F +{ +@public + int f; +} + +@end + +int main(int argc, char* argv[]) +{ + F* f = [F new]; + f->f = 3; + return 0; // breakpoint 1 +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/Makefile new file mode 100644 index 000000000000..7f7baeb3cf1c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m +LD_EXTRAS = -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/TestObjCBaseClassSBType.py b/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/TestObjCBaseClassSBType.py new file mode 100644 index 000000000000..5d2414eed8f5 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/TestObjCBaseClassSBType.py @@ -0,0 +1,58 @@ +""" +Use lldb Python API to test base class resolution for ObjC classes +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ObjCDynamicValueTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + self.line = line_number('main.m', '// Set breakpoint here.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_get_baseclass(self): + """Test fetching ObjC dynamic values.""" + if self.getArchitecture() == 'i386': + # rdar://problem/9946499 + self.skipTest("Dynamic types for ObjC V1 runtime not implemented") + + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoints: + + target.BreakpointCreateByLocation('main.m', self.line) + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + var = self.frame().FindVariable("foo") + var_ptr_type = var.GetType() + var_pte_type = var_ptr_type.GetPointeeType() + self.assertTrue(var_ptr_type.GetNumberOfDirectBaseClasses() == 1, "Foo * has one base class") + self.assertTrue(var_pte_type.GetNumberOfDirectBaseClasses() == 1, "Foo has one base class") + + self.assertTrue(var_ptr_type.GetDirectBaseClassAtIndex(0).IsValid(), "Foo * has a valid base class") + self.assertTrue(var_pte_type.GetDirectBaseClassAtIndex(0).IsValid(), "Foo * has a valid base class") + + self.assertTrue(var_ptr_type.GetDirectBaseClassAtIndex(0).GetName() == var_pte_type.GetDirectBaseClassAtIndex(0).GetName(), "Foo and its pointer type don't agree on their base class") diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/main.m new file mode 100644 index 000000000000..3ec78fd0bd6b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-baseclass-sbtype/main.m @@ -0,0 +1,22 @@ +#import + +@interface Foo : NSObject {} + +-(id) init; + +@end + +@implementation Foo + +-(id) init +{ + return self = [super init]; +} +@end +int main () +{ + Foo *foo = [Foo new]; + NSLog(@"a"); // Set breakpoint here. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/TestObjCBuiltinTypes.py b/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/TestObjCBuiltinTypes.py new file mode 100644 index 000000000000..f7ce2809081a --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/TestObjCBuiltinTypes.py @@ -0,0 +1,55 @@ +"""Test that the expression parser doesn't get confused by 'id' and 'Class'""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCBuiltinTypes(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.cpp" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + # [regression] Can't print ivar value: error: reference to 'id' is ambiguous + def test_with_python_api(self): + """Test expression parser respect for ObjC built-in types.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + # Now make sure we can call a function in the class method we've stopped in. + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + self.expect("expr (foo)", patterns = ["\(ns::id\) \$.* = 0"]) + + self.expect("expr id my_id = 0; my_id", patterns = ["\(id\) \$.* = nil"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/main.cpp b/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/main.cpp new file mode 100644 index 000000000000..6dd8cbc6e9fe --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-builtin-types/main.cpp @@ -0,0 +1,9 @@ +namespace ns { + typedef int id; +}; + +int main() +{ + ns::id foo = 0; + return foo; // Set breakpoint here. +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-checker/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-checker/Makefile new file mode 100644 index 000000000000..a1608fe5a664 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-checker/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-checker/TestObjCCheckers.py b/packages/Python/lldbsuite/test/lang/objc/objc-checker/TestObjCCheckers.py new file mode 100644 index 000000000000..533ec2f6b7c4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-checker/TestObjCCheckers.py @@ -0,0 +1,75 @@ +""" +Use lldb Python API to make sure the dynamic checkers are doing their jobs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ObjCCheckerTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + self.source_name = 'main.m' + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_objc_checker(self): + """Test that checkers catch unrecognized selectors""" + if self.getArchitecture() == 'i386': + self.skipTest("requires Objective-C 2.0 runtime") + + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoints: + + + main_bkpt = target.BreakpointCreateBySourceRegex ("Set a breakpoint here.", lldb.SBFileSpec (self.source_name)) + self.assertTrue(main_bkpt and + main_bkpt.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, main_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + # + # The class Simple doesn't have a count method. Make sure that we don't + # actually try to send count but catch it as an unrecognized selector. + + frame = thread.GetFrameAtIndex(0) + expr_value = frame.EvaluateExpression("(int) [my_simple count]", False) + expr_error = expr_value.GetError() + + self.assertTrue (expr_error.Fail()) + + # Make sure the call produced no NSLog stdout. + stdout = process.GetSTDOUT(100) + self.assertTrue (stdout is None or (len(stdout) == 0)) + + # Make sure the error is helpful: + err_string = expr_error.GetCString() + self.assertTrue ("selector" in err_string) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-checker/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-checker/main.m new file mode 100644 index 000000000000..4a09a2826f70 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-checker/main.m @@ -0,0 +1,32 @@ +#import + +@interface Simple : NSObject +{ + int _value; +} +- (int) value; +- (void) setValue: (int) newValue; +@end + +@implementation Simple +- (int) value +{ + return _value; +} + +- (void) setValue: (int) newValue +{ + _value = newValue; +} +@end + +int main () +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Simple *my_simple = [[Simple alloc] init]; + my_simple.value = 20; + // Set a breakpoint here. + NSLog (@"Object has value: %d.", my_simple.value); + [pool drain]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-class-method/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-class-method/Makefile new file mode 100644 index 000000000000..c2d07910a6e8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-class-method/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := class.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-class-method/TestObjCClassMethod.py b/packages/Python/lldbsuite/test/lang/objc/objc-class-method/TestObjCClassMethod.py new file mode 100644 index 000000000000..2a978bc9c024 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-class-method/TestObjCClassMethod.py @@ -0,0 +1,56 @@ +"""Test calling functions in class methods.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCClassMethod(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "class.m" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @expectedFailurei386 + @add_test_categories(['pyapi']) + #rdar://problem/9745789 "expression" can't call functions in class methods + def test_with_python_api(self): + """Test calling functions in class methods.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + # Now make sure we can call a function in the class method we've stopped in. + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + cmd_value = frame.EvaluateExpression ("(int)[Foo doSomethingWithString:@\"Hello\"]") + self.assertTrue (cmd_value.IsValid()) + self.assertTrue (cmd_value.GetValueAsUnsigned() == 5) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-class-method/class.m b/packages/Python/lldbsuite/test/lang/objc/objc-class-method/class.m new file mode 100644 index 000000000000..18a2c2729bea --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-class-method/class.m @@ -0,0 +1,24 @@ +#import + +@interface Foo : NSObject ++(int) doSomethingWithString: (NSString *) string; +-(int) doSomethingInstance: (NSString *) string; +@end + +@implementation Foo ++(int) doSomethingWithString: (NSString *) string +{ + NSLog (@"String is: %@.", string); + return [string length]; +} + +-(int) doSomethingInstance: (NSString *)string +{ + return [Foo doSomethingWithString:string]; +} +@end + +int main() +{ + return 0; // Set breakpoint here. +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/.categories b/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/.categories new file mode 100644 index 000000000000..9526bab96fbb --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/.categories @@ -0,0 +1 @@ +dyntype diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/Makefile new file mode 100644 index 000000000000..dd909afba21b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LD_EXTRAS = -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/TestObjCDynamicSBType.py b/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/TestObjCDynamicSBType.py new file mode 100644 index 000000000000..b9e84541e2df --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/TestObjCDynamicSBType.py @@ -0,0 +1,62 @@ +""" +Test that we are able to properly report a usable dynamic type +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipUnlessDarwin +class ObjCDynamicSBTypeTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break inside main(). + self.main_source = "main.m" + self.line = line_number(self.main_source, '// Set breakpoint here.') + + @skipIfi386 + def test_dyn(self): + """Test that we are able to properly report a usable dynamic type.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, self.main_source, self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + v_object = self.frame().FindVariable("object").GetDynamicValue(lldb.eDynamicCanRunTarget) + v_base = self.frame().FindVariable("base").GetDynamicValue(lldb.eDynamicCanRunTarget) + self.assertTrue(v_object.GetTypeName() == "MyDerivedClass *", "The NSObject is properly type-named") + self.assertTrue(v_base.GetTypeName() == "MyDerivedClass *", "The Base is properly type-named") + object_type = v_object.GetType() + base_type = v_base.GetType() + self.assertTrue(object_type.GetName() == "MyDerivedClass *", "The dynamic SBType for NSObject is for the correct type") + self.assertTrue(base_type.GetName() == "MyDerivedClass *", "The dynamic SBType for Base is for the correct type") + object_pointee_type = object_type.GetPointeeType() + base_pointee_type = base_type.GetPointeeType() + self.assertTrue(object_pointee_type.GetName() == "MyDerivedClass", "The dynamic type for NSObject figures out its pointee type just fine") + self.assertTrue(base_pointee_type.GetName() == "MyDerivedClass", "The dynamic type for Base figures out its pointee type just fine") + + self.assertTrue(object_pointee_type.GetDirectBaseClassAtIndex(0).GetName() == "MyBaseClass", "The dynamic type for NSObject can go back to its base class") + self.assertTrue(base_pointee_type.GetDirectBaseClassAtIndex(0).GetName() == "MyBaseClass", "The dynamic type for Base can go back to its base class") + + self.assertTrue(object_pointee_type.GetDirectBaseClassAtIndex(0).GetType().GetDirectBaseClassAtIndex(0).GetName() == "NSObject", "The dynamic type for NSObject can go up the hierarchy") + self.assertTrue(base_pointee_type.GetDirectBaseClassAtIndex(0).GetType().GetDirectBaseClassAtIndex(0).GetName() == "NSObject", "The dynamic type for Base can go up the hierarchy") + + self.assertTrue(object_pointee_type.GetNumberOfFields() == 2, "The dynamic type for NSObject has 2 fields") + self.assertTrue(base_pointee_type.GetNumberOfFields() == 2, "The dynamic type for Base has 2 fields") diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/main.m new file mode 100644 index 000000000000..f3587b52cd5f --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-dyn-sbtype/main.m @@ -0,0 +1,53 @@ +#import + +@interface MyBaseClass : NSObject +{} +-(id) init; +-(int) getInt; +@end + +@implementation MyBaseClass +- (id) init { + return (self = [super init]); +} + +- (int) getInt { + return 1; +} +@end + +@interface MyDerivedClass : MyBaseClass +{ + int x; + int y; +} +-(id) init; +-(int) getInt; +@end + +@implementation MyDerivedClass +- (id) init { + self = [super init]; + if (self) { + self-> x = 0; + self->y = 1; + } + return self; +} + +- (int) getInt { + y = x++; + return x; +} +@end + + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSObject* object = [[MyDerivedClass alloc] init]; + MyBaseClass* base = [[MyDerivedClass alloc] init]; + [pool release]; // Set breakpoint here. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/Makefile new file mode 100644 index 000000000000..a981f4b9035e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := dynamic-value.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/TestObjCDynamicValue.py b/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/TestObjCDynamicValue.py new file mode 100644 index 000000000000..acddfb8bd647 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/TestObjCDynamicValue.py @@ -0,0 +1,177 @@ +""" +Use lldb Python API to test dynamic values in ObjC +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ObjCDynamicValueTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + + self.source_name = 'dynamic-value.m' + self.set_property_line = line_number(self.source_name, '// This is the line in setProperty, make sure we step to here.') + self.handle_SourceBase = line_number(self.source_name, + '// Break here to check dynamic values.') + self.main_before_setProperty_line = line_number(self.source_name, + '// Break here to see if we can step into real method.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + @expectedFailureDarwin("llvm.org/pr20271 rdar://18684107") + def test_get_objc_dynamic_vals(self): + """Test fetching ObjC dynamic values.""" + if self.getArchitecture() == 'i386': + # rdar://problem/9946499 + self.skipTest("Dynamic types for ObjC V1 runtime not implemented") + + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoints: + + handle_SourceBase_bkpt = target.BreakpointCreateByLocation(self.source_name, self.handle_SourceBase) + self.assertTrue(handle_SourceBase_bkpt and + handle_SourceBase_bkpt.GetNumLocations() == 1, + VALID_BREAKPOINT) + + main_before_setProperty_bkpt = target.BreakpointCreateByLocation(self.source_name, self.main_before_setProperty_line) + self.assertTrue(main_before_setProperty_bkpt and + main_before_setProperty_bkpt.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, main_before_setProperty_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + # + # At this point, myObserver has a Source pointer that is actually a KVO swizzled SourceDerived + # make sure we can get that properly: + + frame = thread.GetFrameAtIndex(0) + myObserver = frame.FindVariable('myObserver', lldb.eDynamicCanRunTarget) + self.assertTrue (myObserver) + myObserver_source = myObserver.GetChildMemberWithName ('_source', lldb.eDynamicCanRunTarget) + self.examine_SourceDerived_ptr (myObserver_source) + + # + # Make sure a static value can be correctly turned into a dynamic value. + + frame = thread.GetFrameAtIndex(0) + myObserver_static = frame.FindVariable('myObserver', lldb.eNoDynamicValues) + self.assertTrue (myObserver_static) + myObserver = myObserver_static.GetDynamicValue (lldb.eDynamicCanRunTarget) + myObserver_source = myObserver.GetChildMemberWithName ('_source', lldb.eDynamicCanRunTarget) + self.examine_SourceDerived_ptr (myObserver_source) + + # The "frame var" code uses another path to get into children, so let's + # make sure that works as well: + + result = lldb.SBCommandReturnObject() + + self.expect('frame var -d run-target myObserver->_source', 'frame var finds its way into a child member', + patterns = ['\(SourceDerived \*\)']) + + # check that our ObjC GetISA() does a good job at hiding KVO swizzled classes + + self.expect('frame var -d run-target myObserver->_source -T', 'the KVO-ed class is hidden', + substrs = ['SourceDerived']) + + self.expect('frame var -d run-target myObserver->_source -T', 'the KVO-ed class is hidden', matching = False, + substrs = ['NSKVONotify']) + + # This test is not entirely related to the main thrust of this test case, but since we're here, + # try stepping into setProperty, and make sure we get into the version in Source: + + thread.StepInto() + + threads = lldbutil.get_stopped_threads (process, lldb.eStopReasonPlanComplete) + self.assertTrue (len(threads) == 1) + line_entry = threads[0].GetFrameAtIndex(0).GetLineEntry() + + self.assertEqual (line_entry.GetLine(), self.set_property_line) + self.assertEqual (line_entry.GetFileSpec().GetFilename(), self.source_name) + + # Okay, back to the main business. Continue to the handle_SourceBase and make sure we get the correct dynamic value. + + threads = lldbutil.continue_to_breakpoint (process, handle_SourceBase_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + frame = thread.GetFrameAtIndex(0) + + # Get "object" using FindVariable: + + noDynamic = lldb.eNoDynamicValues + useDynamic = lldb.eDynamicCanRunTarget + + object_static = frame.FindVariable ('object', noDynamic) + object_dynamic = frame.FindVariable ('object', useDynamic) + + # Delete this object to make sure that this doesn't cause havoc with the dynamic object that depends on it. + del (object_static) + + self.examine_SourceDerived_ptr (object_dynamic) + + # Get "this" using FindValue, make sure that works too: + object_static = frame.FindValue ('object', lldb.eValueTypeVariableArgument, noDynamic) + object_dynamic = frame.FindValue ('object', lldb.eValueTypeVariableArgument, useDynamic) + del (object_static) + self.examine_SourceDerived_ptr (object_dynamic) + + # Get "this" using the EvaluateExpression: + object_static = frame.EvaluateExpression ('object', noDynamic) + object_dynamic = frame.EvaluateExpression ('object', useDynamic) + del (object_static) + self.examine_SourceDerived_ptr (object_dynamic) + + # Continue again to the handle_SourceBase and make sure we get the correct dynamic value. + # This one looks exactly the same, but in fact this is an "un-KVO'ed" version of SourceBase, so + # its isa pointer points to SourceBase not NSKVOSourceBase or whatever... + + threads = lldbutil.continue_to_breakpoint (process, handle_SourceBase_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + frame = thread.GetFrameAtIndex(0) + + # Get "object" using FindVariable: + + object_static = frame.FindVariable ('object', noDynamic) + object_dynamic = frame.FindVariable ('object', useDynamic) + + # Delete this object to make sure that this doesn't cause havoc with the dynamic object that depends on it. + del (object_static) + + self.examine_SourceDerived_ptr (object_dynamic) + + def examine_SourceDerived_ptr (self, object): + self.assertTrue (object) + self.assertTrue (object.GetTypeName().find ('SourceDerived') != -1) + derivedValue = object.GetChildMemberWithName ('_derivedValue') + self.assertTrue (derivedValue) + self.assertTrue (int (derivedValue.GetValue(), 0) == 30) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/dynamic-value.m b/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/dynamic-value.m new file mode 100644 index 000000000000..2bcb76b1d9dc --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-dynamic-value/dynamic-value.m @@ -0,0 +1,147 @@ +#import + +// SourceBase will be the base class of Source. We'll pass a Source object into a +// function as a SourceBase, and then see if the dynamic typing can get us through the KVO +// goo and all the way back to Source. + +@interface SourceBase: NSObject +{ + uint32_t _value; +} +- (SourceBase *) init; +- (uint32_t) getValue; +@end + +@implementation SourceBase +- (SourceBase *) init +{ + [super init]; + _value = 10; + return self; +} +- (uint32_t) getValue +{ + return _value; +} +@end + +// Source is a class that will be observed by the Observer class below. +// When Observer sets itself up to observe this property (in initWithASource) +// the KVO system will overwrite the "isa" pointer of the object with the "kvo'ed" +// one. + +@interface Source : SourceBase +{ + int _property; +} +- (Source *) init; +- (void) setProperty: (int) newValue; +@end + +@implementation Source +- (Source *) init +{ + [super init]; + _property = 20; + return self; +} +- (void) setProperty: (int) newValue +{ + _property = newValue; // This is the line in setProperty, make sure we step to here. +} +@end + +@interface SourceDerived : Source +{ + int _derivedValue; +} +- (SourceDerived *) init; +- (uint32_t) getValue; +@end + +@implementation SourceDerived +- (SourceDerived *) init +{ + [super init]; + _derivedValue = 30; + return self; +} +- (uint32_t) getValue +{ + return _derivedValue; +} +@end + +// Observer is the object that will watch Source and cause KVO to swizzle it... + +@interface Observer : NSObject +{ + Source *_source; +} ++ (Observer *) observerWithSource: (Source *) source; +- (Observer *) initWithASource: (Source *) source; +- (void) observeValueForKeyPath: (NSString *) path + ofObject: (id) object + change: (NSDictionary *) change + context: (void *) context; +@end + +@implementation Observer + ++ (Observer *) observerWithSource: (Source *) inSource; +{ + Observer *retval; + + retval = [[Observer alloc] initWithASource: inSource]; + return retval; +} + +- (Observer *) initWithASource: (Source *) source +{ + [super init]; + _source = source; + [_source addObserver: self + forKeyPath: @"property" + options: (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) + context: NULL]; + return self; +} + +- (void) observeValueForKeyPath: (NSString *) path + ofObject: (id) object + change: (NSDictionary *) change + context: (void *) context +{ + printf ("Observer function called.\n"); + return; +} +@end + +uint32_t +handle_SourceBase (SourceBase *object) +{ + return [object getValue]; // Break here to check dynamic values. +} + +int main () +{ + Source *mySource; + Observer *myObserver; + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + mySource = [[SourceDerived alloc] init]; + myObserver = [Observer observerWithSource: mySource]; + + [mySource setProperty: 5]; // Break here to see if we can step into real method. + + uint32_t return_value = handle_SourceBase (mySource); + + SourceDerived *unwatchedSource = [[SourceDerived alloc] init]; + + return_value = handle_SourceBase (unwatchedSource); + + [pool release]; + return 0; + +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/Makefile new file mode 100644 index 000000000000..fdd3b5ebfa9e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := objc-ivar-offsets.m main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/TestObjCIvarOffsets.py b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/TestObjCIvarOffsets.py new file mode 100644 index 000000000000..6be1d379c8eb --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/TestObjCIvarOffsets.py @@ -0,0 +1,74 @@ +"""Test printing ObjC objects that use unbacked properties - so that the static ivar offsets are incorrect.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestObjCIvarOffsets(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.m" + self.stop_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test printing ObjC objects that use unbacked properties""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation(self.main_source, self.stop_line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue (process, "Created a process.") + self.assertTrue (process.GetState() == lldb.eStateStopped, "Stopped it too.") + + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue (len(thread_list) == 1) + thread = thread_list[0] + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (frame, "frame 0 is valid") + + mine = thread.GetFrameAtIndex(0).FindVariable("mine") + self.assertTrue(mine, "Found local variable mine.") + + # Test the value object value for BaseClass->_backed_int + + error = lldb.SBError() + + mine_backed_int = mine.GetChildMemberWithName ("_backed_int") + self.assertTrue(mine_backed_int, "Found mine->backed_int local variable.") + backed_value = mine_backed_int.GetValueAsSigned (error) + self.assertTrue (error.Success()) + self.assertTrue (backed_value == 1111) + + # Test the value object value for DerivedClass->_derived_backed_int + + mine_derived_backed_int = mine.GetChildMemberWithName ("_derived_backed_int") + self.assertTrue(mine_derived_backed_int, "Found mine->derived_backed_int local variable.") + derived_backed_value = mine_derived_backed_int.GetValueAsSigned (error) + self.assertTrue (error.Success()) + self.assertTrue (derived_backed_value == 3333) + + # Make sure we also get bit-field offsets correct: + + mine_flag2 = mine.GetChildMemberWithName ("flag2") + self.assertTrue(mine_flag2, "Found mine->flag2 local variable.") + flag2_value = mine_flag2.GetValueAsUnsigned (error) + self.assertTrue (error.Success()) + self.assertTrue (flag2_value == 7) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/main.m new file mode 100644 index 000000000000..41943f48aef5 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/main.m @@ -0,0 +1,15 @@ +#include "objc-ivar-offsets.h" + +int +main () +{ + DerivedClass *mine = [[DerivedClass alloc] init]; + mine.backed_int = 1111; + mine.unbacked_int = 2222; + mine.derived_backed_int = 3333; + mine.derived_unbacked_int = 4444; + mine->flag1 = 1; + mine->flag2 = 7; + + return 0; // Set breakpoint here. +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/objc-ivar-offsets.h b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/objc-ivar-offsets.h new file mode 100644 index 000000000000..99bbd427b064 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/objc-ivar-offsets.h @@ -0,0 +1,27 @@ +#import + +@interface BaseClass : NSObject +{ + int _backed_int; +#if !__OBJC2__ + int _unbacked_int; +#endif +} +@property int backed_int; +@property int unbacked_int; +@end + +@interface DerivedClass : BaseClass +{ + int _derived_backed_int; +#if !__OBJC2__ + int _derived_unbacked_int; +#endif + @public + uint32_t flag1 : 1; + uint32_t flag2 : 3; +} + +@property int derived_backed_int; +@property int derived_unbacked_int; +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/objc-ivar-offsets.m b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/objc-ivar-offsets.m new file mode 100644 index 000000000000..db87adea3d13 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-offsets/objc-ivar-offsets.m @@ -0,0 +1,19 @@ +#import "objc-ivar-offsets.h" + +@implementation BaseClass +@synthesize backed_int = _backed_int; +#if __OBJC2__ +@synthesize unbacked_int; +#else +@synthesize unbacked_int = _unbacked_int; +#endif +@end + +@implementation DerivedClass +@synthesize derived_backed_int = _derived_backed_int; +#if __OBJC2__ +@synthesize derived_unbacked_int; +#else +@synthesize derived_unbacked_int = _derived_unbacked_int; +#endif +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-protocols/TestIvarProtocols.py b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-protocols/TestIvarProtocols.py new file mode 100644 index 000000000000..80305e303d03 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-protocols/TestIvarProtocols.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.skipIfFreeBSD,lldbtest.skipIfLinux,lldbtest.skipIfWindows]) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-protocols/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-protocols/main.m new file mode 100644 index 000000000000..aa6c4715c33b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-protocols/main.m @@ -0,0 +1,33 @@ +#import + +@protocol MyProtocol +-(void)aMethod; +@end + +@interface MyClass : NSObject { + id myId; + NSObject *myObject; +}; + +-(void)doSomething; + +@end + +@implementation MyClass + +-(void)doSomething +{ + NSLog(@"Hello"); //% self.expect("expression -- myId", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["id"]); + //% self.expect("expression -- myObject", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["NSObject"]); +} + +@end + +int main () +{ + @autoreleasepool + { + MyClass *c = [MyClass alloc]; + [c doSomething]; + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/Makefile new file mode 100644 index 000000000000..4365ed9ae936 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +default: a.out.stripped + +a.out.stripped: a.out.dSYM + strip -o a.out.stripped a.out + +clean:: + rm -f a.out.stripped + rm -rf a.out.stripped.dSYM + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py new file mode 100644 index 000000000000..78c7123eefca --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py @@ -0,0 +1,59 @@ +"""Test printing ObjC objects that use unbacked properties - so that the static ivar offsets are incorrect.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestObjCIvarStripped(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.m" + self.stop_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @skipIfDwarf # This test requires a stripped binary and a dSYM + @skipIfDWO # This test requires a stripped binary and a dSYM + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test that we can find stripped Objective-C ivars in the runtime""" + self.build() + exe = os.path.join(os.getcwd(), "a.out.stripped") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.dbg.HandleCommand("add-dsym a.out.dSYM") + + breakpoint = target.BreakpointCreateByLocation(self.main_source, self.stop_line) + self.assertTrue(breakpoint.IsValid() and breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT) + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue (process, "Created a process.") + self.assertTrue (process.GetState() == lldb.eStateStopped, "Stopped it too.") + + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue (len(thread_list) == 1) + thread = thread_list[0] + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (frame, "frame 0 is valid") + + # Test the expression for mc->_foo + + error = lldb.SBError() + + ivar = frame.EvaluateExpression ("(mc->_foo)") + self.assertTrue(ivar, "Got result for mc->_foo") + ivar_value = ivar.GetValueAsSigned (error) + self.assertTrue (error.Success()) + self.assertTrue (ivar_value == 3) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/main.m new file mode 100644 index 000000000000..ed9c1d9ec426 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-ivar-stripped/main.m @@ -0,0 +1,33 @@ +#import + +@interface MyClass : NSObject { +@public + int _foo; +}; + +-(id)init; +@end + +@implementation MyClass + +-(id)init +{ + if ([super init]) + { + _foo = 3; + } + + return self; +} + +@end + +int main () +{ + @autoreleasepool + { + MyClass *mc = [[MyClass alloc] init]; + + NSLog(@"%d", mc->_foo); // Set breakpoint here. + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/Makefile new file mode 100644 index 000000000000..ad3cb3fadcde --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py b/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py new file mode 100644 index 000000000000..ca77de261829 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py @@ -0,0 +1,103 @@ +"""Test that the Objective-C syntax for dictionary/array literals and indexing works""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +import platform +import lldbsuite.test.lldbutil as lldbutil + +from distutils.version import StrictVersion + +from lldbsuite.test.lldbtest import * + +class ObjCNewSyntaxTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.m', '// Set breakpoint 0 here.') + + @skipUnlessDarwin + @expectedFailureAll(oslist=['macosx'], compiler='clang', compiler_version=['<', '7.0.0']) + @unittest2.skipIf(platform.system() != "Darwin" or StrictVersion('12.0.0') > platform.release(), "Only supported on Darwin 12.0.0+") + def test_expr(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 1. + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.expect("expr --object-description -- immutable_array[0]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo"]) + + self.expect("expr --object-description -- mutable_array[0]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["foo"]) + + self.expect("expr --object-description -- mutable_array[0] = @\"bar\"", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["bar"]) + + self.expect("expr --object-description -- mutable_array[0]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["bar"]) + + self.expect("expr --object-description -- immutable_dictionary[@\"key\"]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["value"]) + + self.expect("expr --object-description -- mutable_dictionary[@\"key\"]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["value"]) + + self.expect("expr --object-description -- mutable_dictionary[@\"key\"] = @\"object\"", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["object"]) + + self.expect("expr --object-description -- mutable_dictionary[@\"key\"]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["object"]) + + self.expect("expr --object-description -- @[ @\"foo\", @\"bar\" ]", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["NSArray", "foo", "bar"]) + + self.expect("expr --object-description -- @{ @\"key\" : @\"object\" }", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["key", "object"]) + + self.expect("expr --object-description -- @'a'", VARIABLES_DISPLAYED_CORRECTLY, + substrs = [str(ord('a'))]) + + self.expect("expr --object-description -- @1", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["1"]) + + self.expect("expr --object-description -- @1l", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["1"]) + + self.expect("expr --object-description -- @1ul", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["1"]) + + self.expect("expr --object-description -- @1ll", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["1"]) + + self.expect("expr --object-description -- @1ull", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["1"]) + + self.expect("expr -- @123.45", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["NSNumber", "123.45"]) + + self.expect("expr --object-description -- @( 1 + 3 )", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["4"]) + self.expect("expr -- @((char*)\"Hello world\" + 6)", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["NSString", "world"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/main.m new file mode 100644 index 000000000000..d77ba5b10de9 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/main.m @@ -0,0 +1,21 @@ +#import + +int main() +{ + @autoreleasepool + { + // NSArrays + NSArray *immutable_array = @[ @"foo", @"bar" ]; + NSMutableArray *mutable_array = [NSMutableArray arrayWithCapacity:2]; + [mutable_array addObjectsFromArray:immutable_array]; + + // NSDictionaries + NSDictionary *immutable_dictionary = @{ @"key" : @"value" }; + NSMutableDictionary *mutable_dictionary = [NSMutableDictionary dictionaryWithCapacity:1]; + [mutable_dictionary addEntriesFromDictionary:immutable_dictionary]; + + NSNumber *one = @1; + + NSLog(@"Stop here"); // Set breakpoint 0 here. + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-optimized/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-optimized/Makefile new file mode 100644 index 000000000000..aa6a7520525e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-optimized/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +CFLAGS ?= -arch $(ARCH) -g -O2 +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-optimized/TestObjcOptimized.py b/packages/Python/lldbsuite/test/lang/objc/objc-optimized/TestObjcOptimized.py new file mode 100644 index 000000000000..a4a202e3f940 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-optimized/TestObjcOptimized.py @@ -0,0 +1,63 @@ +""" +Test that objective-c expression parser continues to work for optimized build. + +http://llvm.org/viewvc/llvm-project?rev=126973&view=rev +Fixed a bug in the expression parser where the 'this' +or 'self' variable was not properly read if the compiler +optimized it into a register. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import re + +# rdar://problem/9087739 +# test failure: objc_optimized does not work for "-C clang -A i386" +@skipUnlessDarwin +class ObjcOptimizedTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + myclass = "MyClass" + mymethod = "description" + method_spec = "-[%s %s]" % (myclass, mymethod) + + def test_break(self): + """Test 'expr member' continues to work for optimized build.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_symbol (self, self.method_spec, num_expected_locations=1, sym_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint"], + patterns = ["frame.*0:.*%s %s" % (self.myclass, self.mymethod)]) + + self.expect('expression member', + startstr = "(int) $0 = 5") + + # + interp = self.dbg.GetCommandInterpreter() + result = lldb.SBCommandReturnObject() + interp.HandleCommand('frame variable self', result) + output = result.GetOutput() + + desired_pointer = "0x0" + + mo = re.search("0x[0-9a-f]+", output) + + if mo: + desired_pointer = mo.group(0) + + self.expect('expression (self)', + substrs = [("(%s *) $1 = " % self.myclass), desired_pointer]) + + self.expect('expression self->non_member', error=True, + substrs = ["does not have a member named 'non_member'"]) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-optimized/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-optimized/main.m new file mode 100644 index 000000000000..df88eea0f86a --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-optimized/main.m @@ -0,0 +1,44 @@ +#import + +@interface MyClass : NSObject { + int member; +} + +- (id)initWithMember:(int)_member; +- (NSString*)description; +@end + +@implementation MyClass + +- (id)initWithMember:(int)_member +{ + if (self = [super init]) + { + member = _member; + } + return self; +} + +- (void)dealloc +{ + [super dealloc]; +} + +// Set a breakpoint on '-[MyClass description]' and test expressions: expr member +- (NSString *)description +{ + return [NSString stringWithFormat:@"%d", member]; +} +@end + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + MyClass *my_object = [[MyClass alloc] initWithMember:5]; + + NSLog(@"MyObject %@", [my_object description]); + + [pool release]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-property/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-property/Makefile new file mode 100644 index 000000000000..a1608fe5a664 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-property/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-property/TestObjCProperty.py b/packages/Python/lldbsuite/test/lang/objc/objc-property/TestObjCProperty.py new file mode 100644 index 000000000000..c22a1f1ad532 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-property/TestObjCProperty.py @@ -0,0 +1,114 @@ +""" +Use lldb Python API to verify that expression evaluation for property references uses the correct getters and setters +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ObjCPropertyTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + self.source_name = 'main.m' + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_objc_properties(self): + """Test that expr uses the correct property getters and setters""" + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target, VALID_TARGET) + + # Set up our breakpoints: + + main_bkpt = target.BreakpointCreateBySourceRegex ("Set a breakpoint here.", lldb.SBFileSpec (self.source_name)) + self.assertTrue(main_bkpt and + main_bkpt.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, main_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + frame = thread.GetFrameAtIndex(0) + + mine = frame.FindVariable ("mine") + self.assertTrue (mine.IsValid()) + access_count = mine.GetChildMemberWithName ("_access_count") + self.assertTrue (access_count.IsValid()) + start_access_count = access_count.GetValueAsUnsigned (123456) + self.assertTrue (start_access_count != 123456) + + # + # The first set of tests test calling the getter & setter of + # a property that actually only has a getter & setter and no + # @property. + # + nonexistant_value = frame.EvaluateExpression("mine.nonexistantInt", False) + nonexistant_error = nonexistant_value.GetError() + self.assertTrue (nonexistant_error.Success()) + nonexistant_int = nonexistant_value.GetValueAsUnsigned (123456) + self.assertTrue (nonexistant_int == 6) + + # Calling the getter function would up the access count, so make sure that happened. + + new_access_count = access_count.GetValueAsUnsigned (123456) + self.assertTrue (new_access_count - start_access_count == 1) + start_access_count = new_access_count + + # + # Now call the setter, then make sure that + nonexistant_change = frame.EvaluateExpression("mine.nonexistantInt = 10", False) + nonexistant_error = nonexistant_change.GetError() + self.assertTrue (nonexistant_error.Success()) + + # Calling the setter function would up the access count, so make sure that happened. + + new_access_count = access_count.GetValueAsUnsigned (123456) + self.assertTrue (new_access_count - start_access_count == 1) + start_access_count = new_access_count + + # + # Now we call the getter of a property that is backed by an ivar, + # make sure it works and that we actually update the backing ivar. + # + + backed_value = frame.EvaluateExpression("mine.backedInt", False) + backed_error = backed_value.GetError() + self.assertTrue (backed_error.Success()) + backing_value = mine.GetChildMemberWithName ("_backedInt") + self.assertTrue (backing_value.IsValid()) + self.assertTrue (backed_value.GetValueAsUnsigned (12345) == backing_value.GetValueAsUnsigned(23456)) + + unbacked_value = frame.EvaluateExpression("mine.unbackedInt", False) + unbacked_error = unbacked_value.GetError() + self.assertTrue (unbacked_error.Success()) + + idWithProtocol_value = frame.EvaluateExpression("mine.idWithProtocol", False) + idWithProtocol_error = idWithProtocol_value.GetError() + self.assertTrue (idWithProtocol_error.Success()) + self.assertTrue (idWithProtocol_value.GetTypeName() == "id") diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-property/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-property/main.m new file mode 100644 index 000000000000..2ef142be9b00 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-property/main.m @@ -0,0 +1,100 @@ +#import + +@protocol MyProtocol + +-(const char *)hello; + +@end + +@interface BaseClass : NSObject +{ + int _backedInt; + int _access_count; +} + +- (int) nonexistantInt; +- (void) setNonexistantInt: (int) in_int; + +- (int) myGetUnbackedInt; +- (void) mySetUnbackedInt: (int) in_int; + +- (int) getAccessCount; + ++(BaseClass *) baseClassWithBackedInt: (int) inInt andUnbackedInt: (int) inOtherInt; + +@property(getter=myGetUnbackedInt,setter=mySetUnbackedInt:) int unbackedInt; +@property int backedInt; +@property (nonatomic, assign) id idWithProtocol; +@end + +@implementation BaseClass +@synthesize unbackedInt; +@synthesize backedInt = _backedInt; + ++ (BaseClass *) baseClassWithBackedInt: (int) inInt andUnbackedInt: (int) inOtherInt +{ + BaseClass *new = [[BaseClass alloc] init]; + + new->_backedInt = inInt; + new->unbackedInt = inOtherInt; + + return new; +} + +- (int) myGetUnbackedInt +{ + // NSLog (@"Getting BaseClass::unbackedInt - %d.\n", unbackedInt); + _access_count++; + return unbackedInt; +} + +- (void) mySetUnbackedInt: (int) in_int +{ + // NSLog (@"Setting BaseClass::unbackedInt from %d to %d.", unbackedInt, in_int); + _access_count++; + unbackedInt = in_int; +} + +- (int) nonexistantInt +{ + // NSLog (@"Getting BaseClass::nonexistantInt - %d.\n", 5); + _access_count++; + return 6; +} + +- (void) setNonexistantInt: (int) in_int +{ + // NSLog (@"Setting BaseClass::nonexistantInt from 7 to %d.", in_int); + _access_count++; +} + +- (int) getAccessCount +{ + return _access_count; +} +@end + +int +main () +{ + BaseClass *mine = [BaseClass baseClassWithBackedInt: 10 andUnbackedInt: 20]; + + // Set a breakpoint here. + int nonexistant = mine.nonexistantInt; + + int backedInt = mine.backedInt; + + int unbackedInt = mine.unbackedInt; + + id idWithProtocol = mine.idWithProtocol; + + NSLog (@"Results for %p: nonexistant: %d backed: %d unbacked: %d accessCount: %d.", + mine, + nonexistant, + backedInt, + unbackedInt, + [mine getAccessCount]); + return 0; + +} + diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/Makefile new file mode 100644 index 000000000000..a1608fe5a664 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/TestRuntimeIvars.py b/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/TestRuntimeIvars.py new file mode 100644 index 000000000000..80305e303d03 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/TestRuntimeIvars.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.skipIfFreeBSD,lldbtest.skipIfLinux,lldbtest.skipIfWindows]) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/main.m b/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/main.m new file mode 100644 index 000000000000..1f5a9b077e27 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-runtime-ivars/main.m @@ -0,0 +1,10 @@ +#import + +int main () +{ + @autoreleasepool + { + NSLog(@"Hello"); //% self.expect("expression -- *((NSConcretePointerArray*)[NSPointerArray strongObjectsPointerArray])", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["count", "capacity", "options", "mutations"]); + //% self.expect("expression -- ((NSConcretePointerArray*)[NSPointerArray strongObjectsPointerArray])->count", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["unsigned"]); + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/Makefile new file mode 100644 index 000000000000..81e7f12dea2b --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../../make + +OBJC_SOURCES := static.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +default: a.out.stripped + +a.out.stripped: a.out.dSYM + strip -o a.out.stripped a.out + +clean:: + rm -f a.out.stripped + rm -rf a.out.stripped.dSYM + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/TestObjCStaticMethodStripped.py b/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/TestObjCStaticMethodStripped.py new file mode 100644 index 000000000000..4bcc10b8b882 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/TestObjCStaticMethodStripped.py @@ -0,0 +1,65 @@ +"""Test calling functions in static methods with a stripped binary.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCStaticMethodStripped(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "static.m" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + @skipIfDwarf # This test requires a stripped binary and a dSYM + @skipIfDWO # This test requires a stripped binary and a dSYM + # + def test_with_python_api(self): + """Test calling functions in static methods with a stripped binary.""" + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + self.build() + exe = os.path.join(os.getcwd(), "a.out.stripped") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + # Now make sure we can call a function in the static method we've stopped in. + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + cmd_value = frame.EvaluateExpression ("(char *) sel_getName (_cmd)") + self.assertTrue (cmd_value.IsValid()) + sel_name = cmd_value.GetSummary() + self.assertTrue (sel_name == "\"doSomethingWithString:\"", "Got the right value for the selector as string.") + + cmd_value = frame.EvaluateExpression ("[Foo doSomethingElseWithString:string]") + self.assertTrue (cmd_value.IsValid()) + string_length = cmd_value.GetValueAsUnsigned() + self.assertTrue (string_length == 27, "Got the right value from another class method on the same class.") diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/static.m b/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/static.m new file mode 100644 index 000000000000..ec7b2ef67195 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-static-method-stripped/static.m @@ -0,0 +1,29 @@ +#import + +@interface Foo : NSObject ++(void) doSomethingWithString: (NSString *) string; +-(void) doSomethingWithNothing; +@end + +@implementation Foo ++(void) doSomethingWithString: (NSString *) string +{ + NSLog (@"String is: %@.", string); // Set breakpoint here. +} + ++(int) doSomethingElseWithString: (NSString *) string +{ + NSLog (@"String is still: %@.", string); + return [string length]; +} + +-(void) doSomethingWithNothing +{ +} +@end + +int main() +{ + [Foo doSomethingWithString: @"Some string I have in mind."]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-static-method/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-static-method/Makefile new file mode 100644 index 000000000000..a8e973f6d3b1 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-static-method/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := static.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-static-method/TestObjCStaticMethod.py b/packages/Python/lldbsuite/test/lang/objc/objc-static-method/TestObjCStaticMethod.py new file mode 100644 index 000000000000..89ef1e7b6898 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-static-method/TestObjCStaticMethod.py @@ -0,0 +1,61 @@ +"""Test calling functions in static methods.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCStaticMethod(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "static.m" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + # "expression" can't call functions in class methods + def test_with_python_api(self): + """Test calling functions in static methods.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + # Now make sure we can call a function in the static method we've stopped in. + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + cmd_value = frame.EvaluateExpression ("(char *) sel_getName (_cmd)") + self.assertTrue (cmd_value.IsValid()) + sel_name = cmd_value.GetSummary() + self.assertTrue (sel_name == "\"doSomethingWithString:\"", "Got the right value for the selector as string.") + + cmd_value = frame.EvaluateExpression ("[self doSomethingElseWithString:string]") + self.assertTrue (cmd_value.IsValid()) + string_length = cmd_value.GetValueAsUnsigned() + self.assertTrue (string_length == 27, "Got the right value from another class method on the same class.") diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-static-method/static.m b/packages/Python/lldbsuite/test/lang/objc/objc-static-method/static.m new file mode 100644 index 000000000000..ec7b2ef67195 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-static-method/static.m @@ -0,0 +1,29 @@ +#import + +@interface Foo : NSObject ++(void) doSomethingWithString: (NSString *) string; +-(void) doSomethingWithNothing; +@end + +@implementation Foo ++(void) doSomethingWithString: (NSString *) string +{ + NSLog (@"String is: %@.", string); // Set breakpoint here. +} + ++(int) doSomethingElseWithString: (NSString *) string +{ + NSLog (@"String is still: %@.", string); + return [string length]; +} + +-(void) doSomethingWithNothing +{ +} +@end + +int main() +{ + [Foo doSomethingWithString: @"Some string I have in mind."]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-stepping/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-stepping/Makefile new file mode 100644 index 000000000000..b097fe65fce7 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-stepping/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := stepping-tests.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-stepping/TestObjCStepping.py b/packages/Python/lldbsuite/test/lang/objc/objc-stepping/TestObjCStepping.py new file mode 100644 index 000000000000..1df416d45133 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-stepping/TestObjCStepping.py @@ -0,0 +1,172 @@ +"""Test stepping through ObjC method dispatch in various forms.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCStepping(TestBase): + + def getCategories (self): + return ['basic_process'] + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers that we will step to in main: + self.main_source = "stepping-tests.m" + self.source_randomMethod_line = line_number (self.main_source, '// Source randomMethod start line.') + self.sourceBase_randomMethod_line = line_number (self.main_source, '// SourceBase randomMethod start line.') + self.source_returnsStruct_start_line = line_number (self.main_source, '// Source returnsStruct start line.') + self.sourceBase_returnsStruct_start_line = line_number (self.main_source, '// SourceBase returnsStruct start line.') + self.stepped_past_nil_line = line_number (self.main_source, '// Step over nil should stop here.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test stepping through ObjC method dispatch in various forms.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + breakpoints_to_disable = [] + + break1 = target.BreakpointCreateBySourceRegex ("// Set first breakpoint here.", self.main_source_spec) + self.assertTrue(break1, VALID_BREAKPOINT) + breakpoints_to_disable.append (break1) + + break2 = target.BreakpointCreateBySourceRegex ("// Set second breakpoint here.", self.main_source_spec) + self.assertTrue(break2, VALID_BREAKPOINT) + breakpoints_to_disable.append (break2) + + break3 = target.BreakpointCreateBySourceRegex ('// Set third breakpoint here.', self.main_source_spec) + self.assertTrue(break3, VALID_BREAKPOINT) + breakpoints_to_disable.append (break3) + + break4 = target.BreakpointCreateBySourceRegex ('// Set fourth breakpoint here.', self.main_source_spec) + self.assertTrue(break4, VALID_BREAKPOINT) + breakpoints_to_disable.append (break4) + + break5 = target.BreakpointCreateBySourceRegex ('// Set fifth breakpoint here.', self.main_source_spec) + self.assertTrue(break5, VALID_BREAKPOINT) + breakpoints_to_disable.append (break5) + + break_returnStruct_call_super = target.BreakpointCreateBySourceRegex ('// Source returnsStruct call line.', self.main_source_spec) + self.assertTrue(break_returnStruct_call_super, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_returnStruct_call_super) + + break_step_nil = target.BreakpointCreateBySourceRegex ('// Set nil step breakpoint here.', self.main_source_spec) + self.assertTrue(break_step_nil, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break1) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint 1.") + + thread = threads[0] + + mySource = thread.GetFrameAtIndex(0).FindVariable("mySource") + self.assertTrue(mySource, "Found mySource local variable.") + mySource_isa = mySource.GetChildMemberWithName ("isa") + self.assertTrue(mySource_isa, "Found mySource->isa local variable.") + className = mySource_isa.GetSummary () + + if self.TraceOn(): + print(mySource_isa) + + # Lets delete mySource so we can check that after stepping a child variable + # with no parent persists and is useful. + del (mySource) + + # Now step in, that should leave us in the Source randomMethod: + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.source_randomMethod_line, "Stepped into Source randomMethod.") + + # Now step in again, through the super call, and that should leave us in the SourceBase randomMethod: + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.sourceBase_randomMethod_line, "Stepped through super into SourceBase randomMethod.") + + threads = lldbutil.continue_to_breakpoint (process, break2) + self.assertTrue (len(threads) == 1, "Continued to second breakpoint in main.") + + # Again, step in twice gets us to a stret method and a stret super call: + thread = threads[0] + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.source_returnsStruct_start_line, "Stepped into Source returnsStruct.") + + threads = lldbutil.continue_to_breakpoint (process, break_returnStruct_call_super) + self.assertTrue (len(threads) == 1, "Stepped to the call super line in Source returnsStruct.") + thread = threads[0] + + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.sourceBase_returnsStruct_start_line, "Stepped through super into SourceBase returnsStruct.") + + # Cool now continue to get past the call that initializes the Observer, and then do our steps in again to see that + # we can find our way when we're stepping through a KVO swizzled object. + + threads = lldbutil.continue_to_breakpoint (process, break3) + self.assertTrue (len(threads) == 1, "Continued to third breakpoint in main, our object should now be swizzled.") + + newClassName = mySource_isa.GetSummary () + + if self.TraceOn(): + print(mySource_isa) + + self.assertTrue (newClassName != className, "The isa did indeed change, swizzled!") + + # Now step in, that should leave us in the Source randomMethod: + thread = threads[0] + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.source_randomMethod_line, "Stepped into Source randomMethod in swizzled object.") + + # Now step in again, through the super call, and that should leave us in the SourceBase randomMethod: + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.sourceBase_randomMethod_line, "Stepped through super into SourceBase randomMethod in swizzled object.") + + threads = lldbutil.continue_to_breakpoint (process, break4) + self.assertTrue (len(threads) == 1, "Continued to fourth breakpoint in main.") + thread = threads[0] + + # Again, step in twice gets us to a stret method and a stret super call: + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.source_returnsStruct_start_line, "Stepped into Source returnsStruct in swizzled object.") + + threads = lldbutil.continue_to_breakpoint(process, break_returnStruct_call_super) + self.assertTrue (len(threads) == 1, "Stepped to the call super line in Source returnsStruct - second time.") + thread = threads[0] + + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.sourceBase_returnsStruct_start_line, "Stepped through super into SourceBase returnsStruct in swizzled object.") + + for bkpt in breakpoints_to_disable: + bkpt.SetEnabled(False) + + threads = lldbutil.continue_to_breakpoint (process, break_step_nil) + self.assertTrue (len(threads) == 1, "Continued to step nil breakpoint.") + + thread.StepInto() + line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine() + self.assertTrue (line_number == self.stepped_past_nil_line, "Step in over dispatch to nil stepped over.") diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-stepping/stepping-tests.m b/packages/Python/lldbsuite/test/lang/objc/objc-stepping/stepping-tests.m new file mode 100644 index 000000000000..63db536dee49 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-stepping/stepping-tests.m @@ -0,0 +1,138 @@ +#import +#include + +struct return_me +{ + int first; + int second; +}; + +@interface SourceBase: NSObject +{ + struct return_me my_return; +} +- (SourceBase *) initWithFirst: (int) first andSecond: (int) second; +- (void) randomMethod; +- (struct return_me) returnsStruct; +@end + +@implementation SourceBase +- (void) randomMethod +{ + printf ("Called in SourceBase version of randomMethod.\n"); // SourceBase randomMethod start line. +} + +- (struct return_me) returnsStruct +{ + return my_return; // SourceBase returnsStruct start line. +} + +- (SourceBase *) initWithFirst: (int) first andSecond: (int) second +{ + my_return.first = first; + my_return.second = second; + + return self; +} +@end + +@interface Source : SourceBase +{ + int _property; +} +- (void) setProperty: (int) newValue; +- (void) randomMethod; +- (struct return_me) returnsStruct; +@end + +@implementation Source +- (void) setProperty: (int) newValue +{ + _property = newValue; +} + +- (void) randomMethod +{ + [super randomMethod]; // Source randomMethod start line. + printf ("Called in Source version of random method."); +} + +- (struct return_me) returnsStruct +{ + printf ("Called in Source version of returnsStruct.\n"); // Source returnsStruct start line. + return [super returnsStruct]; // Source returnsStruct call line. +} + +@end + +@interface Observer : NSObject +{ + Source *_source; +} ++ (Observer *) observerWithSource: (Source *) source; +- (Observer *) initWithASource: (Source *) source; +- (void) observeValueForKeyPath: (NSString *) path + ofObject: (id) object + change: (NSDictionary *) change + context: (void *) context; +@end + +@implementation Observer + ++ (Observer *) observerWithSource: (Source *) inSource; +{ + Observer *retval; + + retval = [[Observer alloc] initWithASource: inSource]; + return retval; +} + +- (Observer *) initWithASource: (Source *) source +{ + [super init]; + _source = source; + [_source addObserver: self + forKeyPath: @"property" + options: (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) + context: NULL]; + return self; +} + +- (void) observeValueForKeyPath: (NSString *) path + ofObject: (id) object + change: (NSDictionary *) change + context: (void *) context +{ + printf ("Observer function called.\n"); + return; +} +@end + +int main () +{ + Source *mySource; + Observer *myObserver; + struct return_me ret_val; + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + mySource = [[Source alloc] init]; + + [mySource randomMethod]; // Set first breakpoint here. + ret_val = [mySource returnsStruct]; // Set second breakpoint here. + + myObserver = [Observer observerWithSource: mySource]; + + [mySource randomMethod]; // Set third breakpoint here. + ret_val = [mySource returnsStruct]; // Set fourth breakpoint here. + [mySource setProperty: 5]; // Set fifth breakpoint here. + + // We also had a bug where stepping into a method dispatch to nil turned + // into continue. So make sure that works here: + + mySource = nil; + [mySource randomMethod]; // Set nil step breakpoint here. + [pool release]; // Step over nil should stop here. + return 0; + +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/Makefile new file mode 100644 index 000000000000..c16802966cff --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := test.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/TestObjCStructArgument.py b/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/TestObjCStructArgument.py new file mode 100644 index 000000000000..36cde21c9d66 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/TestObjCStructArgument.py @@ -0,0 +1,57 @@ +"""Test passing structs to Objective-C methods.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCStructArgument(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "test.m" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test passing structs to Objective-C methods.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + self.expect("p [summer sumThings:tts]", substrs = ['9']) + + self.expect("po [NSValue valueWithRect:rect]", substrs = ['NSRect: {{0, 0}, {10, 20}}']) + + # Now make sure we can call a method that returns a struct without crashing. + cmd_value = frame.EvaluateExpression ("[provider getRange]") + self.assertTrue (cmd_value.IsValid()) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/test.m b/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/test.m new file mode 100644 index 000000000000..8bf0ec07b710 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-struct-argument/test.m @@ -0,0 +1,34 @@ +#import + +struct things_to_sum { + int a; + int b; + int c; +}; + +@interface ThingSummer : NSObject { +}; +-(int)sumThings:(struct things_to_sum)tts; +@end + +@implementation ThingSummer +-(int)sumThings:(struct things_to_sum)tts +{ + return tts.a + tts.b + tts.c; +} +@end + +int main() +{ + @autoreleasepool + { + ThingSummer *summer = [ThingSummer alloc]; + struct things_to_sum tts = { 2, 3, 4 }; + int ret = [summer sumThings:tts]; + + NSRect rect = {{0, 0}, {10, 20}}; + + // Set breakpoint here. + return rect.origin.x; + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/Makefile new file mode 100644 index 000000000000..c16802966cff --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := test.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/TestObjCStructReturn.py b/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/TestObjCStructReturn.py new file mode 100644 index 000000000000..010de2180825 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/TestObjCStructReturn.py @@ -0,0 +1,53 @@ +"""Test calling functions in class methods.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCClassMethod(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "test.m" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test calling functions in class methods.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + # Now make sure we can call a method that returns a struct without crashing. + cmd_value = frame.EvaluateExpression ("[provider getRange]") + self.assertTrue (cmd_value.IsValid()) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/test.m b/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/test.m new file mode 100644 index 000000000000..aafe231ea817 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-struct-return/test.m @@ -0,0 +1,23 @@ +#import + +@interface RangeProvider : NSObject { +}; +-(NSRange)getRange; +@end + +@implementation RangeProvider +-(NSRange)getRange +{ + return NSMakeRange(0, 3); +} +@end + +int main() +{ + @autoreleasepool + { + RangeProvider *provider = [RangeProvider alloc]; + NSRange range = [provider getRange]; // Set breakpoint here. + return 0; + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-super/Makefile b/packages/Python/lldbsuite/test/lang/objc/objc-super/Makefile new file mode 100644 index 000000000000..c2d07910a6e8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-super/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := class.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-super/TestObjCSuper.py b/packages/Python/lldbsuite/test/lang/objc/objc-super/TestObjCSuper.py new file mode 100644 index 000000000000..84d147f632da --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-super/TestObjCSuper.py @@ -0,0 +1,59 @@ +"""Test calling methods on super.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCSuperMethod(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "class.m" + self.break_line = line_number(self.main_source, '// Set breakpoint here.') + + @skipUnlessDarwin + @expectedFailurei386 + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test calling methods on super.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + # Now make sure we can call a function in the class method we've stopped in. + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + cmd_value = frame.EvaluateExpression ("[self get]") + self.assertTrue (cmd_value.IsValid()) + self.assertTrue (cmd_value.GetValueAsUnsigned() == 2) + + cmd_value = frame.EvaluateExpression ("[super get]") + self.assertTrue (cmd_value.IsValid()) + self.assertTrue (cmd_value.GetValueAsUnsigned() == 1) diff --git a/packages/Python/lldbsuite/test/lang/objc/objc-super/class.m b/packages/Python/lldbsuite/test/lang/objc/objc-super/class.m new file mode 100644 index 000000000000..b55b649aaaec --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/objc-super/class.m @@ -0,0 +1,39 @@ +#import + +@interface Foo : NSObject { +} +-(int)get; +@end + +@implementation Foo +-(int)get +{ + return 1; +} +@end + +@interface Bar : Foo { +} +-(int)get; +@end + +@implementation Bar +-(int)get +{ + return 2; +} + +-(int)callme +{ + return [self get]; // Set breakpoint here. +} +@end + +int main() +{ + @autoreleasepool + { + Bar *bar = [Bar alloc]; + return [bar callme]; + } +} diff --git a/packages/Python/lldbsuite/test/lang/objc/print-obj/Makefile b/packages/Python/lldbsuite/test/lang/objc/print-obj/Makefile new file mode 100644 index 000000000000..dba1065cd714 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/print-obj/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := blocked.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/print-obj/TestPrintObj.py b/packages/Python/lldbsuite/test/lang/objc/print-obj/TestPrintObj.py new file mode 100644 index 000000000000..5c52cc0e0697 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/print-obj/TestPrintObj.py @@ -0,0 +1,87 @@ +""" +Test "print object" where another thread blocks the print object from making progress. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +@skipUnlessDarwin +class PrintObjTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # My source program. + self.source = "blocked.m" + # Find the line numbers to break at. + self.line = line_number(self.source, '// Set a breakpoint here.') + + def test_print_obj(self): + """ + Test "print object" where another thread blocks the print object from making progress. + + Set a breakpoint on the line in my_pthread_routine. 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. + """ + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + exe = os.path.join(os.getcwd(), 'b.out') + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + self.runCmd("breakpoint list") + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.runCmd("thread backtrace all") + + # Let's get the current stopped thread. We'd like to switch to the + # other thread to issue our 'po lock_me' command. + import lldbsuite.test.lldbutil as lldbutil + this_thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(this_thread) + + # Find the other thread. The iteration protocol of SBProcess and the + # rich comparison methods (__eq__/__ne__) of SBThread come in handy. + other_thread = None + for t in process: + if t != this_thread: + other_thread = t + break + + # Set the other thread as the selected thread to issue our 'po' command.other + self.assertTrue(other_thread) + process.SetSelectedThread(other_thread) + if self.TraceOn(): + print("selected thread:" + lldbutil.get_description(other_thread)) + self.runCmd("thread backtrace") + + # We want to traverse the frame to the one corresponding to blocked.m to + # issue our 'po lock_me' command. + + depth = other_thread.GetNumFrames() + for i in range(depth): + frame = other_thread.GetFrameAtIndex(i) + name = frame.GetFunctionName() + if name == 'main': + other_thread.SetSelectedFrame(i) + if self.TraceOn(): + print("selected frame:" + lldbutil.get_description(frame)) + break + + self.expect("po lock_me", OBJECT_PRINTED_CORRECTLY, + substrs = ['I am pretty special.']) diff --git a/packages/Python/lldbsuite/test/lang/objc/print-obj/blocked.m b/packages/Python/lldbsuite/test/lang/objc/print-obj/blocked.m new file mode 100644 index 000000000000..ba2497554444 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/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/packages/Python/lldbsuite/test/lang/objc/radar-9691614/Makefile b/packages/Python/lldbsuite/test/lang/objc/radar-9691614/Makefile new file mode 100644 index 000000000000..ad3cb3fadcde --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/radar-9691614/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/radar-9691614/TestObjCMethodReturningBOOL.py b/packages/Python/lldbsuite/test/lang/objc/radar-9691614/TestObjCMethodReturningBOOL.py new file mode 100644 index 000000000000..38551f671a92 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/radar-9691614/TestObjCMethodReturningBOOL.py @@ -0,0 +1,45 @@ +""" +Test that objective-c method returning BOOL works correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipUnlessDarwin +class MethodReturningBOOLTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break inside main(). + self.main_source = "main.m" + self.line = line_number(self.main_source, '// Set breakpoint here.') + + def test_method_ret_BOOL(self): + """Test that objective-c method returning BOOL works correctly.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + self.expect("process status", STOPPED_DUE_TO_BREAKPOINT, + substrs = [" at %s:%d" % (self.main_source, self.line), + "stop reason = breakpoint"]) + + # rdar://problem/9691614 + self.runCmd('p (int)[my isValid]') diff --git a/packages/Python/lldbsuite/test/lang/objc/radar-9691614/main.m b/packages/Python/lldbsuite/test/lang/objc/radar-9691614/main.m new file mode 100644 index 000000000000..bb87d673452c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/radar-9691614/main.m @@ -0,0 +1,67 @@ +#import +#include + +@interface MyString : NSObject { + NSString *str; + NSDate *date; + BOOL _is_valid; +} + +- (id)initWithNSString:(NSString *)string; +- (BOOL)isValid; +@end + +@implementation MyString +- (id)initWithNSString:(NSString *)string +{ + if (self = [super init]) + { + str = [NSString stringWithString:string]; + date = [NSDate date]; + } + _is_valid = YES; + return self; +} + +- (BOOL)isValid +{ + return _is_valid; +} + +- (void)dealloc +{ + [date release]; + [str release]; + [super dealloc]; +} + +- (NSString *)description +{ + return [str stringByAppendingFormat:@" with timestamp: %@", date]; +} +@end + +void +Test_MyString (const char *program) +{ + NSString *str = [NSString stringWithFormat:@"Hello from '%s'", program]; + MyString *my = [[MyString alloc] initWithNSString:str]; + if ([my isValid]) + printf("my is valid!\n"); + + NSLog(@"NSString instance: %@", [str description]); // Set breakpoint here. + // Test 'p (int)[my isValid]'. + // The expression parser should not crash -- rdar://problem/9691614. + + NSLog(@"MyString instance: %@", [my description]); +} + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + Test_MyString (argv[0]); + + [pool release]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/Makefile b/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/Makefile new file mode 100644 index 000000000000..ad3cb3fadcde --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/TestRdar10967107.py b/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/TestRdar10967107.py new file mode 100644 index 000000000000..0902527d424c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/TestRdar10967107.py @@ -0,0 +1,44 @@ +""" +Test that CoreFoundation classes CFGregorianDate and CFRange are not improperly uniqued +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipUnlessDarwin +class Rdar10967107TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break inside main(). + self.main_source = "main.m" + self.line = line_number(self.main_source, '// Set breakpoint here.') + + def test_cfrange_diff_cfgregoriandate(self): + """Test that CoreFoundation classes CFGregorianDate and CFRange are not improperly uniqued.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, self.main_source, self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + # check that each type is correctly bound to its list of children + self.expect("frame variable cf_greg_date --raw", substrs = ['year','month','day','hour','minute','second']) + self.expect("frame variable cf_range --raw", substrs = ['location','length']) + # check that printing both does not somehow confuse LLDB + self.expect("frame variable --raw", substrs = ['year','month','day','hour','minute','second','location','length']) diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/main.m b/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/main.m new file mode 100644 index 000000000000..386a458950b6 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-10967107/main.m @@ -0,0 +1,13 @@ +#import + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + NSDate *date1 = [NSDate date]; + CFGregorianDate cf_greg_date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef)date1), NULL); + CFRange cf_range = {4,4}; + + [pool release]; // Set breakpoint here. + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/Makefile b/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/Makefile new file mode 100644 index 000000000000..ad3cb3fadcde --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/TestRdar11355592.py b/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/TestRdar11355592.py new file mode 100644 index 000000000000..42120a57834c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/TestRdar11355592.py @@ -0,0 +1,65 @@ +""" +Test that we do not attempt to make a dynamic type for a 'const char*' +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipUnlessDarwin +class Rdar10967107TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break inside main(). + self.main_source = "main.m" + self.line = line_number(self.main_source, '// Set breakpoint here.') + + def test_charstar_dyntype(self): + """Test that we do not attempt to make a dynamic type for a 'const char*'""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, self.main_source, self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + # check that we correctly see the const char*, even with dynamic types on + self.expect("frame variable my_string", substrs = ['const char *']) + self.expect("frame variable my_string --dynamic-type run-target", substrs = ['const char *']) + # check that expr also gets it right + self.expect("expr my_string", substrs = ['const char *']) + self.expect("expr -d run -- my_string", substrs = ['const char *']) + # but check that we get the real Foolie as such + self.expect("frame variable my_foolie", substrs = ['FoolMeOnce *']) + self.expect("frame variable my_foolie --dynamic-type run-target", substrs = ['FoolMeOnce *']) + # check that expr also gets it right + self.expect("expr my_foolie", substrs = ['FoolMeOnce *']) + self.expect("expr -d run -- my_foolie", substrs = ['FoolMeOnce *']) + # now check that assigning a true string does not break anything + self.runCmd("next") + # check that we correctly see the const char*, even with dynamic types on + self.expect("frame variable my_string", substrs = ['const char *']) + self.expect("frame variable my_string --dynamic-type run-target", substrs = ['const char *']) + # check that expr also gets it right + self.expect("expr my_string", substrs = ['const char *']) + self.expect("expr -d run -- my_string", substrs = ['const char *']) + # but check that we get the real Foolie as such + self.expect("frame variable my_foolie", substrs = ['FoolMeOnce *']) + self.expect("frame variable my_foolie --dynamic-type run-target", substrs = ['FoolMeOnce *']) + # check that expr also gets it right + self.expect("expr my_foolie", substrs = ['FoolMeOnce *']) + self.expect("expr -d run -- my_foolie", substrs = ['FoolMeOnce *']) diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/main.m b/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/main.m new file mode 100644 index 000000000000..09b3b18a7878 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-11355592/main.m @@ -0,0 +1,37 @@ +#import + +@interface FoolMeOnce : NSObject +{ + int32_t value_one; // ivars needed to make 32-bit happy + int32_t value_two; +} +- (FoolMeOnce *) initWithFirst: (int32_t) first andSecond: (int32_t) second; + +@property int32_t value_one; +@property int32_t value_two; + +@end + +@implementation FoolMeOnce +@synthesize value_one; +@synthesize value_two; +- (FoolMeOnce *) initWithFirst: (int32_t) first andSecond: (int32_t) second +{ + value_one = first; + value_two = second; + return self; +} +@end + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + FoolMeOnce *my_foolie = [[FoolMeOnce alloc] initWithFirst: 20 andSecond: 55]; + const char *my_string = (char *) my_foolie; + + my_string = "Now this is a REAL string..."; // Set breakpoint here. + + [pool release]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/Makefile b/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/Makefile new file mode 100644 index 000000000000..385b557c9af0 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/Makefile @@ -0,0 +1,11 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules + +ifneq (,$(findstring arm,$(ARCH))) + LD_EXTRAS = -framework Foundation -framework UIKit +else + LD_EXTRAS = -framework Foundation -framework Cocoa +endif diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/TestRdar12408181.py b/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/TestRdar12408181.py new file mode 100644 index 000000000000..b95d95106032 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/TestRdar12408181.py @@ -0,0 +1,48 @@ +""" +Test that we are able to find out how many children NSWindow has +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipUnlessDarwin +class Rdar12408181TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break inside main(). + self.main_source = "main.m" + self.line = line_number(self.main_source, '// Set breakpoint here.') + + def test_nswindow_count(self): + """Test that we are able to find out how many children NSWindow has.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, self.main_source, self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + if self.frame().EvaluateExpression('(void*)_CGSDefaultConnection()').GetValueAsUnsigned() != 0: + window = self.frame().FindVariable("window") + window_dynamic = window.GetDynamicValue(lldb.eDynamicCanRunTarget) + self.assertTrue(window.GetNumChildren() > 1, "NSWindow (static) only has 1 child!") + self.assertTrue(window_dynamic.GetNumChildren() > 1, "NSWindow (dynamic) only has 1 child!") + self.assertTrue(window.GetChildAtIndex(0).IsValid(), "NSWindow (static) has an invalid child") + self.assertTrue(window_dynamic.GetChildAtIndex(0).IsValid(), "NSWindow (dynamic) has an invalid child") + else: + self.skipTest('no WindowServer connection') diff --git a/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/main.m b/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/main.m new file mode 100644 index 000000000000..858ba2a4a22f --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/rdar-12408181/main.m @@ -0,0 +1,24 @@ +#import +#if defined (__i386__) || defined (__x86_64__) +#import +#else +#import +#endif + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#if defined (__i386__) || defined (__x86_64__) + + [NSApplication sharedApplication]; + NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,100,100) styleMask:NSBorderlessWindowMask backing:NSBackingStoreRetained defer:NO]; + [window setCanHide:YES]; +#else + [UIApplication sharedApplication]; + CGRect rect = { 0, 0, 100, 100}; + UIWindow* window = [[UIWindow alloc] initWithFrame:rect]; +#endif + [pool release]; // Set breakpoint here. + return 0; +} + diff --git a/packages/Python/lldbsuite/test/lang/objc/real-definition/Bar.h b/packages/Python/lldbsuite/test/lang/objc/real-definition/Bar.h new file mode 100644 index 000000000000..20a81faa1017 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/real-definition/Bar.h @@ -0,0 +1,12 @@ +#import + +@class InternalClass; + +@interface Bar : NSObject { + @private + InternalClass *storage; +} + +- (NSString *)description; + +@end \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/objc/real-definition/Bar.m b/packages/Python/lldbsuite/test/lang/objc/real-definition/Bar.m new file mode 100644 index 000000000000..46d7e3888751 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/real-definition/Bar.m @@ -0,0 +1,43 @@ +#import "Bar.h" + +@interface InternalClass : NSObject { + @public + NSString *foo; + NSString *bar; +} +@end + +@implementation InternalClass +@end + +@interface Bar () +{ + NSString *_hidden_ivar; +} + +@end + +@implementation Bar + +- (id)init +{ + self = [super init]; + if (self) { + _hidden_ivar = [NSString stringWithFormat:@"%p: @Bar", self]; + } + return self; // Set breakpoint where Bar is an implementation +} + +- (void)dealloc +{ + [_hidden_ivar release]; + [super dealloc]; +} + +- (NSString *)description +{ + return [_hidden_ivar copyWithZone:NULL]; +} + +@end + \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/objc/real-definition/Foo.h b/packages/Python/lldbsuite/test/lang/objc/real-definition/Foo.h new file mode 100644 index 000000000000..14ff9eed50fd --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/real-definition/Foo.h @@ -0,0 +1,11 @@ +#import + +#import "Bar.h" + +@interface Foo : NSObject { + Bar *_bar; +} + +- (NSString *)description; + +@end \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/objc/real-definition/Foo.m b/packages/Python/lldbsuite/test/lang/objc/real-definition/Foo.m new file mode 100644 index 000000000000..bcdeaeffc29a --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/real-definition/Foo.m @@ -0,0 +1,25 @@ +#import "Foo.h" + +@implementation Foo + +- (id)init +{ + self = [super init]; + if (self) { + _bar = [[Bar alloc] init]; + } + return self; // Set breakpoint where Bar is an interface +} + +- (void)dealloc +{ + [_bar release]; + [super dealloc]; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"%p: @Foo { _bar = %@ }", self, _bar]; +} + +@end diff --git a/packages/Python/lldbsuite/test/lang/objc/real-definition/Makefile b/packages/Python/lldbsuite/test/lang/objc/real-definition/Makefile new file mode 100644 index 000000000000..61cc3b3b6db8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/real-definition/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := Bar.m Foo.m main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/real-definition/TestRealDefinition.py b/packages/Python/lldbsuite/test/lang/objc/real-definition/TestRealDefinition.py new file mode 100644 index 000000000000..30fd2a5b0f6d --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/real-definition/TestRealDefinition.py @@ -0,0 +1,86 @@ +"""Test that types defined in shared libraries work correctly.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestRealDefinition(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_frame_var_after_stop_at_interface(self): + """Test that we can find the implementation for an objective C type""" + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + self.build() + self.common_setup() + + line = line_number('Foo.m', '// Set breakpoint where Bar is an interface') + lldbutil.run_break_set_by_file_and_line (self, 'Foo.m', line, num_expected_locations=1, loc_exact=True); + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Run and stop at Foo + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.runCmd("continue", RUN_SUCCEEDED) + + # Run at stop at main + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # This should display correctly. + self.expect("frame variable foo->_bar->_hidden_ivar", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(NSString *)", "foo->_bar->_hidden_ivar = 0x"]) + + @skipUnlessDarwin + def test_frame_var_after_stop_at_implementation(self): + """Test that we can find the implementation for an objective C type""" + if self.getArchitecture() == 'i386': + self.skipTest("requires modern objc runtime") + self.build() + self.common_setup() + + line = line_number('Bar.m', '// Set breakpoint where Bar is an implementation') + lldbutil.run_break_set_by_file_and_line (self, 'Bar.m', line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Run and stop at Foo + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + self.runCmd("continue", RUN_SUCCEEDED) + + # Run at stop at main + self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, + substrs = [' resolved, hit count = 1']) + + # This should display correctly. + self.expect("frame variable foo->_bar->_hidden_ivar", VARIABLES_DISPLAYED_CORRECTLY, + substrs = ["(NSString *)", "foo->_bar->_hidden_ivar = 0x"]) + + def common_setup(self): + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Break inside the foo function which takes a bar_ptr argument. + line = line_number('main.m', '// Set breakpoint in main') + lldbutil.run_break_set_by_file_and_line (self, "main.m", line, num_expected_locations=1, loc_exact=True) diff --git a/packages/Python/lldbsuite/test/lang/objc/real-definition/main.m b/packages/Python/lldbsuite/test/lang/objc/real-definition/main.m new file mode 100644 index 000000000000..8c31dc9abb31 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/real-definition/main.m @@ -0,0 +1,13 @@ +#include +#include +#import +#import "Foo.h" + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Foo *foo = [[Foo alloc] init]; + NSLog (@"foo is %@", foo); // Set breakpoint in main + [pool release]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/sample/Makefile b/packages/Python/lldbsuite/test/lang/objc/sample/Makefile new file mode 100644 index 000000000000..a1608fe5a664 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/sample/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/sample/main.m b/packages/Python/lldbsuite/test/lang/objc/sample/main.m new file mode 100644 index 000000000000..9dffc71aaac6 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/sample/main.m @@ -0,0 +1,70 @@ +#import + + +@interface MyString : NSObject { + NSString *_string; + NSDate *_date; +} +- (id)initWithNSString:(NSString *)string; + +@property (copy) NSString *string; +@property (readonly,getter=getTheDate) NSDate *date; + +- (NSDate *) getTheDate; +@end + +@implementation MyString + +@synthesize string = _string; +@synthesize date = _date; + +- (id)initWithNSString:(NSString *)string +{ + if (self = [super init]) + { + _string = [NSString stringWithString:string]; + _date = [NSDate date]; + } + return self; +} + +- (void) dealloc +{ + [_date release]; + [_string release]; + [super dealloc]; +} + +- (NSDate *) getTheDate +{ + return _date; +} + +- (NSString *)description +{ + return [_string stringByAppendingFormat:@" with timestamp: %@", _date]; +} +@end + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + static NSString *g_global_nsstr = @"Howdy"; + + MyString *myStr = [[MyString alloc] initWithNSString: [NSString stringWithFormat:@"string %i", 1]]; + NSString *str1 = myStr.string; + NSString *str2 = [NSString stringWithFormat:@"string %i", 2]; + NSString *str3 = [NSString stringWithFormat:@"string %i", 3]; + NSArray *array = [NSArray arrayWithObjects: str1, str2, str3, nil]; + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: + str1, @"1", + str2, @"2", + str3, @"3", + myStr.date, @"date", + nil]; + + id str_id = str1; + SEL sel = @selector(length); + [pool release]; + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/objc/self/Makefile b/packages/Python/lldbsuite/test/lang/objc/self/Makefile new file mode 100644 index 000000000000..bdae30428be4 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/self/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m +LD_EXTRAS ?= -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objc/self/TestObjCSelf.py b/packages/Python/lldbsuite/test/lang/objc/self/TestObjCSelf.py new file mode 100644 index 000000000000..37db151f9ae3 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/self/TestObjCSelf.py @@ -0,0 +1,36 @@ +""" +Tests that ObjC member variables are available where they should be. +""" +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ObjCSelfTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_with_run_command(self): + """Test that the appropriate member variables are available when stopped in Objective-C class and instance methods""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.set_breakpoint(line_number('main.m', '// breakpoint 1')) + self.set_breakpoint(line_number('main.m', '// breakpoint 2')) + + self.runCmd("process launch", RUN_SUCCEEDED) + + self.expect("expression -- m_a = 2", + startstr = "(int) $0 = 2") + + self.runCmd("process continue") + + # This would be disallowed if we enforced const. But we don't. + self.expect("expression -- m_a = 2", + error=True) + + self.expect("expression -- s_a", + startstr = "(int) $1 = 5") + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.m", line, num_expected_locations=1, loc_exact=True) diff --git a/packages/Python/lldbsuite/test/lang/objc/self/main.m b/packages/Python/lldbsuite/test/lang/objc/self/main.m new file mode 100644 index 000000000000..928aaf2ff024 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objc/self/main.m @@ -0,0 +1,54 @@ +//===-- main.m ------------------------------------------*- Objective-C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +@interface A : NSObject +{ + int m_a; +} +-(id)init; +-(void)accessMember:(int)a; ++(void)accessStaticMember:(int)a; +@end + +static int s_a = 5; + +@implementation A +-(id)init +{ + self = [super init]; + + if (self) + m_a = 2; + + return self; +} + +-(void)accessMember:(int)a +{ + m_a = a; // breakpoint 1 +} + ++(void)accessStaticMember:(int)a +{ + s_a = a; // breakpoint 2 +} +@end + +int main() +{ + NSAutoreleasePool *pool = [NSAutoreleasePool alloc]; + A *my_a = [[A alloc] init]; + + [my_a accessMember:3]; + [A accessStaticMember:5]; + + [pool release]; +} diff --git a/packages/Python/lldbsuite/test/lang/objcxx/objcxx-ivar-vector/TestIvarVector.py b/packages/Python/lldbsuite/test/lang/objcxx/objcxx-ivar-vector/TestIvarVector.py new file mode 100644 index 000000000000..80305e303d03 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objcxx/objcxx-ivar-vector/TestIvarVector.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.skipIfFreeBSD,lldbtest.skipIfLinux,lldbtest.skipIfWindows]) diff --git a/packages/Python/lldbsuite/test/lang/objcxx/objcxx-ivar-vector/main.mm b/packages/Python/lldbsuite/test/lang/objcxx/objcxx-ivar-vector/main.mm new file mode 100644 index 000000000000..36eda1da2acb --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objcxx/objcxx-ivar-vector/main.mm @@ -0,0 +1,33 @@ +#import + +#include + +@interface MyElement : NSObject { +} +@end + +@interface MyClass : NSObject { + std::vector elements; +}; + +-(void)doSomething; + +@end + +@implementation MyClass + +-(void)doSomething +{ + NSLog(@"Hello"); //% self.expect("expression -- elements", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["vector", "MyElement"]); +} + +@end + +int main () +{ + @autoreleasepool + { + MyClass *c = [MyClass alloc]; + [c doSomething]; + } +} diff --git a/packages/Python/lldbsuite/test/lang/objcxx/sample/Makefile b/packages/Python/lldbsuite/test/lang/objcxx/sample/Makefile new file mode 100644 index 000000000000..edd3430de7c8 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objcxx/sample/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJCXX_SOURCES := main.mm +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/lang/objcxx/sample/main.mm b/packages/Python/lldbsuite/test/lang/objcxx/sample/main.mm new file mode 100644 index 000000000000..c9a2172c368c --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/objcxx/sample/main.mm @@ -0,0 +1,71 @@ +#import +#include + +@interface MyString : NSObject { + NSString *_string; + NSDate *_date; +} +- (id)initWithNSString:(NSString *)string; + +@property (copy) NSString *string; +@property (readonly,getter=getTheDate) NSDate *date; + +- (NSDate *) getTheDate; +@end + +@implementation MyString + +@synthesize string = _string; +@synthesize date = _date; + +- (id)initWithNSString:(NSString *)string +{ + if (self = [super init]) + { + _string = [NSString stringWithString:string]; + _date = [NSDate date]; + } + return self; +} + +- (void) dealloc +{ + [_date release]; + [_string release]; + [super dealloc]; +} + +- (NSDate *) getTheDate +{ + return _date; +} + +- (NSString *)description +{ + return [_string stringByAppendingFormat:@" with timestamp: %@", _date]; +} +@end + +int main (int argc, char const *argv[]) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + static NSString *g_global_nsstr = @"Howdy"; + + MyString *myStr = [[MyString alloc] initWithNSString: [NSString stringWithFormat:@"string %i", 1]]; + NSString *str1 = myStr.string; + NSString *str2 = [NSString stringWithFormat:@"string %i", 2]; + NSString *str3 = [NSString stringWithFormat:@"string %i", 3]; + NSArray *array = [NSArray arrayWithObjects: str1, str2, str3, nil]; + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: + str1, @"1", + str2, @"2", + str3, @"3", + myStr.date, @"date", + nil]; + + id str_id = str1; + SEL sel = @selector(length); + [pool release]; + std::cout << "Hello, objc++!\n"; + return 0; +} diff --git a/packages/Python/lldbsuite/test/linux/builtin_trap/Makefile b/packages/Python/lldbsuite/test/linux/builtin_trap/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/linux/builtin_trap/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/linux/builtin_trap/TestBuiltinTrap.py b/packages/Python/lldbsuite/test/linux/builtin_trap/TestBuiltinTrap.py new file mode 100644 index 000000000000..ea742341512c --- /dev/null +++ b/packages/Python/lldbsuite/test/linux/builtin_trap/TestBuiltinTrap.py @@ -0,0 +1,51 @@ +""" +Test lldb ability to unwind a stack with a function containing a call to the +'__builtin_trap' intrinsic, which GCC (4.6) encodes to an illegal opcode. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BuiltinTrapTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + @expectedFailureAll("llvm.org/pr15936", compiler="gcc", compiler_version=["<=","4.6"]) + @expectedFailureAll(archs="arm", compiler="gcc", triple=".*-android") # gcc generates incorrect linetable + @skipIfWindows + def test_with_run_command(self): + """Test that LLDB handles a function with __builtin_trap correctly.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, + num_expected_locations=1, + loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # print backtrace, expect both 'bar' and 'main' functions to be listed + self.expect('bt', substrs = ['bar', 'main']) + + # go up one frame + self.runCmd("up", RUN_SUCCEEDED) + + # evaluate a local + self.expect('p foo', substrs = ['= 5']) diff --git a/packages/Python/lldbsuite/test/linux/builtin_trap/main.cpp b/packages/Python/lldbsuite/test/linux/builtin_trap/main.cpp new file mode 100644 index 000000000000..84156ef2ac4f --- /dev/null +++ b/packages/Python/lldbsuite/test/linux/builtin_trap/main.cpp @@ -0,0 +1,17 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +void bar(int const *foo) { + __builtin_trap(); // Set break point at this line. +} + +int main() { + int foo = 5; + bar(&foo); +} diff --git a/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/Makefile b/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/Makefile new file mode 100644 index 000000000000..67aa16625bff --- /dev/null +++ b/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py b/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py new file mode 100644 index 000000000000..c6d9afb02003 --- /dev/null +++ b/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py @@ -0,0 +1,66 @@ +""" +This tests that we do not lose control of the inferior, while doing an instruction-level step +over a thread creation instruction. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CreateDuringInstructionStepTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + @skipUnlessPlatform(['linux']) + @expectedFailureAndroid('llvm.org/pr24737', archs=['arm']) + def test_step_inst(self): + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target and target.IsValid(), "Target is valid") + + # This should create a breakpoint in the stepping thread. + breakpoint = target.BreakpointCreateByName("main") + self.assertTrue(breakpoint and breakpoint.IsValid(), "Breakpoint is valid") + + # Run the program. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint) + self.assertEqual(len(threads), 1, STOPPED_DUE_TO_BREAKPOINT) + + thread = threads[0] + self.assertTrue(thread and thread.IsValid(), "Thread is valid") + + # Make sure we see only one threads + self.assertEqual(process.GetNumThreads(), 1, 'Number of expected threads and actual threads do not match.') + + # Keep stepping until we see the thread creation + while process.GetNumThreads() < 2: + thread.StepInstruction(False) + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonPlanComplete, "Step operation succeeded") + if self.TraceOn(): + self.runCmd("disassemble --pc") + + if self.TraceOn(): + self.runCmd("thread list") + + # We have successfully caught thread creation. Now just run to completion + process.Continue() + + # At this point, the inferior process should have exited. + self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/main.cpp b/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/main.cpp new file mode 100644 index 000000000000..079e8b13798c --- /dev/null +++ b/packages/Python/lldbsuite/test/linux/thread/create_during_instruction_step/main.cpp @@ -0,0 +1,55 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file deliberately uses low level linux-specific API for thread creation because: +// - instruction-stepping over thread creation using higher-level functions was very slow +// - it was also unreliable due to single-stepping bugs unrelated to this test +// - some threading libraries do not create or destroy threads when we would expect them to + +#include + +#include +#include + +enum { STACK_SIZE = 0x2000 }; + +static uint8_t child_stack[STACK_SIZE]; + +pid_t child_tid; + +std::atomic flag(false); + +int thread_main(void *) +{ + while (! flag) // Make sure the thread does not exit prematurely + ; + + return 0; +} + +int main () +{ + int ret = clone(thread_main, + child_stack + STACK_SIZE/2, // Don't care whether the stack grows up or down, + // just point to the middle + CLONE_CHILD_CLEARTID | CLONE_FILES | CLONE_FS | CLONE_PARENT_SETTID | + CLONE_SIGHAND | CLONE_SYSVSEM | CLONE_THREAD | CLONE_VM, + nullptr, // thread_main argument + &child_tid); + + if (ret == -1) + { + perror("clone"); + return 1; + } + + flag = true; + + return 0; +} diff --git a/packages/Python/lldbsuite/test/lldb_pylint_helper.py b/packages/Python/lldbsuite/test/lldb_pylint_helper.py new file mode 100644 index 000000000000..1cc09441a2d6 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldb_pylint_helper.py @@ -0,0 +1,181 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Sync lldb and related source from a local machine to a remote machine. + +This facilitates working on the lldb sourcecode on multiple machines +and multiple OS types, verifying changes across all. + +Provides helper support for adding lldb test paths to the python path. +""" + +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import os +import platform +import subprocess +import sys + +# Third-party modules + +# LLDB modules + + +def add_lldb_test_paths(check_dir): + # pylint: disable=line-too-long + """Adds lldb test-related paths to the python path. + + Starting with the given directory and working upward through + each parent directory up to the root, it looks for the lldb + test directory. When found, the lldb test directory and its + child test_runner/lib directory will be added to the python + system path. + + Instructions for use: + + This method supports a simple way of getting pylint to be able + to reliably lint lldb python test scripts (including the test + infrastructure itself). To do so, add the following to a + .pylintrc file in your home directory: + + [Master] + init-hook='import os; import sys; sys.path.append(os.path.expanduser("~/path/to/lldb/packages/Python/lldbsuite/test")); import lldb_pylint_helper; lldb_pylint_helper.add_lldb_test_paths(os.getcwd()); print("sys.path={}\n".format(sys.path))' + + Replace ~/path/to/lldb with a valid path to your local lldb source + tree. Note you can have multiple lldb source trees on your system, and + this will work just fine. The path in your .pylintrc is just needed to + find the paths needed for pylint in whatever lldb source tree you're in. + pylint will use the python files in whichever tree it is run from. + + Note it is critical that the init-hook line be contained on a single line. + You can remove the print line at the end once you know the pythonpath is + getting set up the way you expect. + + With these changes, you will be able to run the following, for example. + + cd lldb/sourcetree/1-of-many/test/lang/c/anonymous + pylint TestAnonymous.py + + This will work, and include all the lldb/sourcetree/1-of-many lldb-specific + python directories to your path. + + You can then run it in another lldb source tree on the same machine like + so: + + cd lldb/sourcetree/2-of-many/test/functionalities/inferior-assert + pyline TestInferiorAssert.py + + and this will properly lint that file, using the lldb-specific python + directories from the 2-of-many source tree. + + Note at the time I'm writing this, our tests are in pretty sad shape + as far as a stock pylint setup goes. But we need to start somewhere :-) + + @param check_dir specifies a directory that will be used to start + looking for the lldb test infrastructure python library paths. + """ + # Add the test-related packages themselves. + add_lldb_test_package_paths(check_dir) + + # Add the lldb directory itself + add_lldb_module_directory() + + +def add_lldb_module_directory(): + """ + Desired Approach: + + Part A: find an lldb + + 1. Walk up the parent chain from the current directory, looking for + a directory matching *build*. If we find that, use it as the + root of a directory search for an lldb[.exe] executable. + + 2. If 1 fails, use the path and look for an lldb[.exe] in there. + + If Part A ends up with an lldb, go to part B. Otherwise, give up + on the lldb python module path. + + Part B: use the output from 'lldb[.exe] -P' to find the lldb dir. + + Current approach: + If Darwin, use 'xcrun lldb -P'; others: find lldb on path. + + Drawback to current approach: + If the tester is changing the SB API (adding new methods), pylint + will not know about them as it is using the wrong lldb python module. + In practice, this should be minor. + """ + try: + lldb_module_path = None + + if platform.system() == 'Darwin': + # Use xcrun to find the selected lldb. + lldb_module_path = subprocess.check_output(["xcrun", "lldb", "-P"]) + elif platform.system() == 'Windows': + lldb_module_path = subprocess.check_output( + ["lldb.exe", "-P"], shell=True) + else: + # Use the shell to run lldb from the path. + lldb_module_path = subprocess.check_output( + ["lldb", "-P"], shell=True) + + # Trim the result. + if lldb_module_path is not None: + lldb_module_path = lldb_module_path.strip() + + # If we have a result, add it to the path + if lldb_module_path is not None and len(lldb_module_path) > 0: + sys.path.insert(0, lldb_module_path) + # pylint: disable=broad-except + except Exception as exception: + print("failed to find python path: {}".format(exception)) + + +def add_lldb_test_package_paths(check_dir): + """Adds the lldb test infrastructure modules to the python path. + + See add_lldb_test_paths for more details. + + @param check_dir the directory of the test. + """ + + def child_dirs(parent_dir): + return [os.path.join(parent_dir, child) + for child in os.listdir(parent_dir) + if os.path.isdir(os.path.join(parent_dir, child))] + + check_dir = os.path.realpath(check_dir) + while check_dir and len(check_dir) > 0: + # If the current directory contains a packages/Python + # directory, add that directory to the path. + packages_python_child_dir = os.path.join( + check_dir, "packages", "Python") + if os.path.exists(packages_python_child_dir): + sys.path.insert(0, packages_python_child_dir) + sys.path.insert(0, os.path.join( + packages_python_child_dir, "test_runner", "lib")) + + # Handle third_party module/package directory. + third_party_module_dir = os.path.join( + check_dir, "third_party", "Python", "module") + for child_dir in child_dirs(third_party_module_dir): + # Yes, we embed the module in the module parent dir + sys.path.insert(0, child_dir) + + # We're done. + break + + # Continue looking up the parent chain until we have no more + # directories to check. + new_check_dir = os.path.dirname(check_dir) + # We're done when the new check dir is not different + # than the current one. + if new_check_dir == check_dir: + break + check_dir = new_check_dir diff --git a/packages/Python/lldbsuite/test/lldbbench.py b/packages/Python/lldbsuite/test/lldbbench.py new file mode 100644 index 000000000000..6b568f874661 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldbbench.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import + +# System modules +import time + +# Third-party modules + +# LLDB modules +from .lldbtest import * + +class Stopwatch(object): + """Stopwatch provides a simple utility to start/stop your stopwatch multiple + times. Each start/stop is equal to a lap, with its elapsed time accumulated + while measurment is in progress. + + When you're ready to start from scratch for another round of measurements, + be sure to call the reset() method. + + For example, + + sw = Stopwatch() + for i in range(1000): + with sw: + # Do some length operations... + ... + # Get the average time. + avg_time = sw.avg() + + # Reset the stopwatch as we are about to perform other kind of operations. + sw.reset() + ... + """ + + ############################################################# + # + # Context manager interfaces to support the 'with' statement. + # + ############################################################# + + def __enter__(self): + """ + Context management protocol on entry to the body of the with statement. + """ + return self.start() + + def __exit__(self, type, value, tb): + """ + Context management protocol on exit from the body of the with statement. + """ + self.stop() + + def reset(self): + self.__laps__ = 0 + self.__total_elapsed__ = 0.0 + self.__start__ = None + self.__stop__ = None + self.__elapsed__ = 0.0 + self.__nums__ = [] + + def __init__(self): + self.reset() + + def start(self): + if self.__start__ is None: + self.__start__ = time.time() + else: + raise Exception("start() already called, did you forget to stop() first?") + # Return self to facilitate the context manager __enter__ protocol. + return self + + def stop(self): + if self.__start__ is not None: + self.__stop__ = time.time() + elapsed = self.__stop__ - self.__start__ + self.__total_elapsed__ += elapsed + self.__laps__ += 1 + self.__nums__.append(elapsed) + self.__start__ = None # Reset __start__ to be None again. + else: + raise Exception("stop() called without first start()?") + + def laps(self): + """Gets the number of laps. One lap is equal to a start/stop action.""" + return self.__laps__ + + def avg(self): + """Equal to total elapsed time divided by the number of laps.""" + return self.__total_elapsed__ / self.__laps__ + + #def sigma(self): + # """Return the standard deviation of the available samples.""" + # if self.__laps__ <= 0: + # return None + # return numpy.std(self.__nums__) + + def __str__(self): + return "Avg: %f (Laps: %d, Total Elapsed Time: %f, min=%f, max=%f)" % (self.avg(), + self.__laps__, + self.__total_elapsed__, + min(self.__nums__), + max(self.__nums__)) + +class BenchBase(TestBase): + """ + Abstract base class for benchmark tests. + """ + def setUp(self): + """Fixture for unittest test case setup.""" + super(BenchBase, self).setUp() + #TestBase.setUp(self) + self.stopwatch = Stopwatch() + + def tearDown(self): + """Fixture for unittest test case teardown.""" + super(BenchBase, self).tearDown() + #TestBase.tearDown(self) + del self.stopwatch + diff --git a/packages/Python/lldbsuite/test/lldbcurses.py b/packages/Python/lldbsuite/test/lldbcurses.py new file mode 100644 index 000000000000..bc1fbf00d372 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldbcurses.py @@ -0,0 +1,1143 @@ +from __future__ import absolute_import + +# System modules +import curses +import curses.panel +import sys +import time + +# Third-party modules +import six + +# LLDB modules + +class Point(object): + def __init__(self, x, y): + self.x = x + self.y = y + + def __repr__(self): + return str(self) + + def __str__(self): + return "(x=%u, y=%u)" % (self.x, self.y) + + def __eq__(self, rhs): + return self.x == rhs.x and self.y == rhs.y + + def __ne__(self, rhs): + return self.x != rhs.x or self.y != rhs.y + + def is_valid_coordinate(self): + return self.x >= 0 and self.y >= 0 + +class Size(object): + def __init__(self, w, h): + self.w = w + self.h = h + + def __repr__(self): + return str(self) + + def __str__(self): + return "(w=%u, h=%u)" % (self.w, self.h) + + def __eq__(self, rhs): + return self.w == rhs.w and self.h == rhs.h + + def __ne__(self, rhs): + return self.w != rhs.w or self.h != rhs.h + +class Rect(object): + def __init__(self, x=0, y=0, w=0, h=0): + self.origin = Point(x, y) + self.size = Size(w, h) + + def __repr__(self): + return str(self) + + def __str__(self): + return "{ %s, %s }" % (str(self.origin), str(self.size)) + + def get_min_x(self): + return self.origin.x + + def get_max_x(self): + return self.origin.x + self.size.w + + def get_min_y(self): + return self.origin.y + + def get_max_y(self): + return self.origin.y + self.size.h + + def contains_point(self, pt): + if pt.x < self.get_max_x(): + if pt.y < self.get_max_y(): + if pt.x >= self.get_min_y(): + return pt.y >= self.get_min_y() + return False + + def __eq__(self, rhs): + return self.origin == rhs.origin and self.size == rhs.size + + def __ne__(self, rhs): + return self.origin != rhs.origin or self.size != rhs.size + +class QuitException(Exception): + def __init__(self): + super(QuitException, self).__init__('QuitException') + +class Window(object): + def __init__(self, window, delegate = None, can_become_first_responder = True): + self.window = window + self.parent = None + self.delegate = delegate + self.children = list() + self.first_responders = list() + self.can_become_first_responder = can_become_first_responder + self.key_actions = dict() + + def add_child(self, window): + self.children.append(window) + window.parent = self + + def resize(self, size): + self.window.resize(size.h, size.w) + + def resize_child(self, child, delta_size, adjust_neighbors): + if child in self.children: + frame = self.get_frame() + orig_frame = child.get_frame() + new_frame = Rect(x=orig_frame.origin.x, y=orig_frame.origin.y, w=orig_frame.size.w + delta_size.w, h=orig_frame.size.h + delta_size.h) + old_child_max_x = orig_frame.get_max_x() + new_child_max_x = new_frame.get_max_x() + window_max_x = frame.get_max_x() + if new_child_max_x < window_max_x: + child.resize(new_frame.size) + if old_child_max_x == window_max_x: + new_frame.origin.x += window_max_x - new_child_max_x + child.set_position(new_frame.origin) + elif new_child_max_x > window_max_x: + new_frame.origin.x -= new_child_max_x - window_max_x + child.set_position(new_frame.origin) + child.resize(new_frame.size) + + if adjust_neighbors: + #print('orig_frame = %s\r\n' % (str(orig_frame)), end='') + for curr_child in self.children: + if curr_child is child: + continue + curr_child_frame = curr_child.get_frame() + if delta_size.w != 0: + #print('curr_child_frame = %s\r\n' % (str(curr_child_frame)), end='') + if curr_child_frame.get_min_x() == orig_frame.get_max_x(): + curr_child_frame.origin.x += delta_size.w + curr_child_frame.size.w -= delta_size.w + #print('adjusted curr_child_frame = %s\r\n' % (str(curr_child_frame)), end='') + curr_child.resize (curr_child_frame.size) + curr_child.slide_position (Size(w=delta_size.w, h=0)) + elif curr_child_frame.get_max_x() == orig_frame.get_min_x(): + curr_child_frame.size.w -= delta_size.w + #print('adjusted curr_child_frame = %s\r\n' % (str(curr_child_frame)), end='') + curr_child.resize (curr_child_frame.size) + + def add_key_action(self, arg, callback, decription): + if isinstance(arg, list): + for key in arg: + self.add_key_action(key, callback, description) + else: + if isinstance(arg, six.integer_types): + key_action_dict = { 'key' : arg, + 'callback' : callback, + 'description' : decription } + self.key_actions[arg] = key_action_dict + elif isinstance(arg, basestring): + key_integer = ord(arg) + key_action_dict = { 'key' : key_integer, + 'callback' : callback, + 'description' : decription } + self.key_actions[key_integer] = key_action_dict + else: + raise ValueError + + def draw_title_box(self, title): + is_in_first_responder_chain = self.is_in_first_responder_chain() + if is_in_first_responder_chain: + self.attron (curses.A_REVERSE) + self.box() + if is_in_first_responder_chain: + self.attroff (curses.A_REVERSE) + if title: + self.addstr(Point(x=2, y=0), ' ' + title + ' ') + + def remove_child(self, window): + self.children.remove(window) + + def get_first_responder(self): + if len(self.first_responders): + return self.first_responders[-1] + else: + return None + + def set_first_responder(self, window): + if window.can_become_first_responder: + if six.callable(getattr(window, "hidden", None)) and window.hidden(): + return False + if not window in self.children: + self.add_child(window) + # See if we have a current first responder, and if we do, let it know that + # it will be resigning as first responder + first_responder = self.get_first_responder() + if first_responder: + first_responder.relinquish_first_responder() + # Now set the first responder to "window" + if len(self.first_responders) == 0: + self.first_responders.append(window) + else: + self.first_responders[-1] = window + return True + else: + return False + + def push_first_responder(self, window): + # Only push the window as the new first responder if the window isn't already the first responder + if window != self.get_first_responder(): + self.first_responders.append(window) + + def pop_first_responder(self, window): + # Only pop the window from the first responder list if it is the first responder + if window == self.get_first_responder(): + old_first_responder = self.first_responders.pop() + old_first_responder.relinquish_first_responder() + return True + else: + return False + + def relinquish_first_responder(self): + '''Override if there is something that you need to do when you lose first responder status.''' + pass + + # def resign_first_responder(self, remove_from_parent, new_first_responder): + # success = False + # if self.parent: + # if self.is_first_responder(): + # self.relinquish_first_responder() + # if len(self.parent.first_responder): + # self.parent.first_responder = None + # success = True + # if remove_from_parent: + # self.parent.remove_child(self) + # if new_first_responder: + # self.parent.set_first_responder(new_first_responder) + # else: + # self.parent.select_next_first_responder() + # return success + + def is_first_responder(self): + if self.parent: + return self.parent.get_first_responder() == self + else: + return False + + def is_in_first_responder_chain(self): + if self.parent: + return self in self.parent.first_responders + else: + return False + + def select_next_first_responder(self): + if len(self.first_responders) > 1: + self.pop_first_responder(self.first_responders[-1]) + else: + num_children = len(self.children) + if num_children == 1: + return self.set_first_responder(self.children[0]) + for (i,window) in enumerate(self.children): + if window.is_first_responder(): + break + if i < num_children: + for i in range(i+1,num_children): + if self.set_first_responder(self.children[i]): + return True + for i in range(0, i): + if self.set_first_responder(self.children[i]): + return True + + def point_in_window(self, pt): + size = self.get_size() + return pt.x >= 0 and pt.x < size.w and pt.y >= 0 and pt.y < size.h + + def addch(self, c): + try: + self.window.addch(c) + except: + pass + + def addch_at_point(self, pt, c): + try: + self.window.addch(pt.y, pt.x, c) + except: + pass + + def addstr(self, pt, str): + try: + self.window.addstr(pt.y, pt.x, str) + except: + pass + + def addnstr_at_point(self, pt, str, n): + try: + self.window.addnstr(pt.y, pt.x, str, n) + except: + pass + def addnstr(self, str, n): + try: + self.window.addnstr(str, n) + except: + pass + + def attron(self, attr): + return self.window.attron (attr) + + def attroff(self, attr): + return self.window.attroff (attr) + + def box(self, vertch=0, horch=0): + if vertch == 0: + vertch = curses.ACS_VLINE + if horch == 0: + horch = curses.ACS_HLINE + self.window.box(vertch, horch) + + def get_contained_rect(self, top_inset=0, bottom_inset=0, left_inset=0, right_inset=0, height=-1, width=-1): + '''Get a rectangle based on the top "height" lines of this window''' + rect = self.get_frame() + x = rect.origin.x + left_inset + y = rect.origin.y + top_inset + if height == -1: + h = rect.size.h - (top_inset + bottom_inset) + else: + h = height + if width == -1: + w = rect.size.w - (left_inset + right_inset) + else: + w = width + return Rect (x = x, y = y, w = w, h = h) + + def erase(self): + self.window.erase() + + def get_cursor(self): + (y, x) = self.window.getyx() + return Point(x=x, y=y) + + def get_frame(self): + position = self.get_position() + size = self.get_size() + return Rect(x=position.x, y=position.y, w=size.w, h=size.h) + + def get_frame_in_parent(self): + position = self.get_position_in_parent() + size = self.get_size() + return Rect(x=position.x, y=position.y, w=size.w, h=size.h) + + def get_position_in_parent(self): + (y, x) = self.window.getparyx() + return Point(x, y) + + def get_position(self): + (y, x) = self.window.getbegyx() + return Point(x, y) + + def get_size(self): + (y, x) = self.window.getmaxyx() + return Size(w=x, h=y) + + def move(self, pt): + self.window.move(pt.y, pt.x) + + def refresh(self): + self.update() + curses.panel.update_panels() + self.move(Point(x=0, y=0)) + return self.window.refresh() + + def resize(self, size): + return self.window.resize(size.h, size.w) + + def timeout(self, timeout_msec): + return self.window.timeout(timeout_msec) + + def handle_key(self, key, check_parent=True): + '''Handle a key press in this window.''' + + # First try the first responder if this window has one, but don't allow + # it to check with its parent (False second parameter) so we don't recurse + # and get a stack overflow + for first_responder in reversed(self.first_responders): + if first_responder.handle_key(key, False): + return True + + # Check our key map to see if we have any actions. Actions don't take + # any arguments, they must be callable + if key in self.key_actions: + key_action = self.key_actions[key] + key_action['callback']() + return True + # Check if there is a wildcard key for any key + if -1 in self.key_actions: + key_action = self.key_actions[-1] + key_action['callback']() + return True + # Check if the window delegate wants to handle this key press + if self.delegate: + if six.callable(getattr(self.delegate, "handle_key", None)): + if self.delegate.handle_key(self, key): + return True + if self.delegate(self, key): + return True + # Check if we have a parent window and if so, let the parent + # window handle the key press + if check_parent and self.parent: + return self.parent.handle_key(key, True) + else: + return False # Key not handled + + def update(self): + for child in self.children: + child.update() + + def quit_action(self): + raise QuitException + + def get_key(self, timeout_msec=-1): + self.timeout(timeout_msec) + done = False + c = self.window.getch() + if c == 27: + self.timeout(0) + escape_key = 0 + while True: + escape_key = self.window.getch() + if escape_key == -1: + break + else: + c = c << 8 | escape_key + self.timeout(timeout_msec) + return c + + def key_event_loop(self, timeout_msec=-1, n=sys.maxsize): + '''Run an event loop to receive key presses and pass them along to the + responder chain. + + timeout_msec is the timeout it milliseconds. If the value is -1, an + infinite wait will be used. It the value is zero, a non-blocking mode + will be used, and if greater than zero it will wait for a key press + for timeout_msec milliseconds. + + n is the number of times to go through the event loop before exiting''' + done = False + while not done and n > 0: + c = self.get_key(timeout_msec) + if c != -1: + try: + self.handle_key(c) + except QuitException: + done = True + n -= 1 + +class Panel(Window): + def __init__(self, frame, delegate = None, can_become_first_responder = True): + window = curses.newwin(frame.size.h,frame.size.w, frame.origin.y, frame.origin.x) + super(Panel, self).__init__(window, delegate, can_become_first_responder) + self.panel = curses.panel.new_panel(window) + + def hide(self): + return self.panel.hide() + + def hidden(self): + return self.panel.hidden() + + def show(self): + return self.panel.show() + + def top(self): + return self.panel.top() + + def set_position(self, pt): + self.panel.move(pt.y, pt.x) + + def slide_position(self, size): + new_position = self.get_position() + new_position.x = new_position.x + size.w + new_position.y = new_position.y + size.h + self.set_position(new_position) + +class BoxedPanel(Panel): + def __init__(self, frame, title, delegate = None, can_become_first_responder = True): + super(BoxedPanel, self).__init__(frame, delegate, can_become_first_responder) + self.title = title + self.lines = list() + self.first_visible_idx = 0 + self.selected_idx = -1 + self.add_key_action(curses.KEY_UP, self.select_prev, "Select the previous item") + self.add_key_action(curses.KEY_DOWN, self.select_next, "Select the next item") + self.add_key_action(curses.KEY_HOME, self.scroll_begin, "Go to the beginning of the list") + self.add_key_action(curses.KEY_END, self.scroll_end, "Go to the end of the list") + self.add_key_action(0x1b4f48, self.scroll_begin, "Go to the beginning of the list") + self.add_key_action(0x1b4f46, self.scroll_end, "Go to the end of the list") + self.add_key_action(curses.KEY_PPAGE, self.scroll_page_backward, "Scroll to previous page") + self.add_key_action(curses.KEY_NPAGE, self.scroll_page_forward, "Scroll to next forward") + self.update() + + def clear(self, update=True): + self.lines = list() + self.first_visible_idx = 0 + self.selected_idx = -1 + if update: + self.update() + + def get_usable_width(self): + '''Valid usable width is 0 to (width - 3) since the left and right lines display the box around + this frame and we skip a leading space''' + w = self.get_size().w + if w > 3: + return w-3 + else: + return 0 + + def get_usable_height(self): + '''Valid line indexes are 0 to (height - 2) since the top and bottom lines display the box around this frame.''' + h = self.get_size().h + if h > 2: + return h-2 + else: + return 0 + + def get_point_for_line(self, global_line_idx): + '''Returns the point to use when displaying a line whose index is "line_idx"''' + line_idx = global_line_idx - self.first_visible_idx + num_lines = self.get_usable_height() + if line_idx < num_lines: + return Point(x=2, y=1+line_idx) + else: + return Point(x=-1, y=-1) # return an invalid coordinate if the line index isn't valid + + def set_title (self, title, update=True): + self.title = title + if update: + self.update() + + def scroll_to_line (self, idx): + if idx < len(self.lines): + self.selected_idx = idx + max_visible_lines = self.get_usable_height() + if idx < self.first_visible_idx or idx >= self.first_visible_idx + max_visible_lines: + self.first_visible_idx = idx + self.refresh() + + def scroll_begin (self): + self.first_visible_idx = 0 + if len(self.lines) > 0: + self.selected_idx = 0 + else: + self.selected_idx = -1 + self.update() + + def scroll_end (self): + max_visible_lines = self.get_usable_height() + num_lines = len(self.lines) + if num_lines > max_visible_lines: + self.first_visible_idx = num_lines - max_visible_lines + else: + self.first_visible_idx = 0 + self.selected_idx = num_lines-1 + self.update() + + def scroll_page_backward(self): + num_lines = len(self.lines) + max_visible_lines = self.get_usable_height() + new_index = self.first_visible_idx - max_visible_lines + if new_index < 0: + self.first_visible_idx = 0 + else: + self.first_visible_idx = new_index + self.refresh() + + def scroll_page_forward(self): + max_visible_lines = self.get_usable_height() + self.first_visible_idx += max_visible_lines + self._adjust_first_visible_line() + self.refresh() + + def select_next (self): + self.selected_idx += 1 + if self.selected_idx >= len(self.lines): + self.selected_idx = len(self.lines) - 1 + self.refresh() + + def select_prev (self): + self.selected_idx -= 1 + if self.selected_idx < 0: + if len(self.lines) > 0: + self.selected_idx = 0 + else: + self.selected_idx = -1 + self.refresh() + + def get_selected_idx(self): + return self.selected_idx + + def _adjust_first_visible_line(self): + num_lines = len(self.lines) + max_visible_lines = self.get_usable_height() + if (self.first_visible_idx >= num_lines) or (num_lines - self.first_visible_idx) > max_visible_lines: + self.first_visible_idx = num_lines - max_visible_lines + + def append_line(self, s, update=True): + self.lines.append(s) + self._adjust_first_visible_line() + if update: + self.update() + + def set_line(self, line_idx, s, update=True): + '''Sets a line "line_idx" within the boxed panel to be "s"''' + if line_idx < 0: + return + while line_idx >= len(self.lines): + self.lines.append('') + self.lines[line_idx] = s + self._adjust_first_visible_line() + if update: + self.update() + + def update(self): + self.erase() + self.draw_title_box(self.title) + max_width = self.get_usable_width() + for line_idx in range(self.first_visible_idx, len(self.lines)): + pt = self.get_point_for_line(line_idx) + if pt.is_valid_coordinate(): + is_selected = line_idx == self.selected_idx + if is_selected: + self.attron (curses.A_REVERSE) + self.move(pt) + self.addnstr(self.lines[line_idx], max_width) + if is_selected: + self.attroff (curses.A_REVERSE) + else: + return + + def load_file(self, path): + f = open(path) + if f: + self.lines = f.read().splitlines() + for (idx, line) in enumerate(self.lines): + # Remove any tabs from lines since they hose up the display + if "\t" in line: + self.lines[idx] = (8*' ').join(line.split('\t')) + self.selected_idx = 0 + self.first_visible_idx = 0 + self.refresh() + +class Item(object): + def __init__(self, title, action): + self.title = title + self.action = action + +class TreeItemDelegate(object): + + def might_have_children(self): + return False + + def update_children(self, item): + '''Return a list of child Item objects''' + return None + + def draw_item_string(self, tree_window, item, s): + pt = tree_window.get_cursor() + width = tree_window.get_size().w - 1 + if width > pt.x: + tree_window.addnstr(s, width - pt.x) + + def draw_item(self, tree_window, item): + self.draw_item_string(tree_window, item, item.title) + + def do_action(self): + pass + +class TreeItem(object): + def __init__(self, delegate, parent = None, title = None, action = None, is_expanded = False): + self.parent = parent + self.title = title + self.action = action + self.delegate = delegate + self.is_expanded = not parent or is_expanded == True + self._might_have_children = None + self.children = None + self._children_might_have_children = False + + def get_children(self): + if self.is_expanded and self.might_have_children(): + if self.children is None: + self._children_might_have_children = False + self.children = self.update_children() + for child in self.children: + if child.might_have_children(): + self._children_might_have_children = True + break + else: + self._children_might_have_children = False + self.children = None + return self.children + + def append_visible_items(self, items): + items.append(self) + children = self.get_children() + if children: + for child in children: + child.append_visible_items(items) + + def might_have_children(self): + if self._might_have_children is None: + if not self.parent: + # Root item always might have children + self._might_have_children = True + else: + # Check with the delegate to see if the item might have children + self._might_have_children = self.delegate.might_have_children() + return self._might_have_children + + def children_might_have_children(self): + return self._children_might_have_children + + def update_children(self): + if self.is_expanded and self.might_have_children(): + self.children = self.delegate.update_children(self) + for child in self.children: + child.update_children() + else: + self.children = None + return self.children + + def get_num_visible_rows(self): + rows = 1 + if self.is_expanded: + children = self.get_children() + if children: + for child in children: + rows += child.get_num_visible_rows() + return rows + def draw(self, tree_window, row): + display_row = tree_window.get_display_row(row) + if display_row >= 0: + tree_window.move(tree_window.get_item_draw_point(row)) + if self.parent: + self.parent.draw_tree_for_child(tree_window, self, 0) + if self.might_have_children(): + tree_window.addch (curses.ACS_DIAMOND) + tree_window.addch (curses.ACS_HLINE) + elif self.parent and self.parent.children_might_have_children(): + if self.parent.parent: + tree_window.addch (curses.ACS_HLINE) + tree_window.addch (curses.ACS_HLINE) + else: + tree_window.addch (' ') + tree_window.addch (' ') + is_selected = tree_window.is_selected(row) + if is_selected: + tree_window.attron (curses.A_REVERSE) + self.delegate.draw_item(tree_window, self) + if is_selected: + tree_window.attroff (curses.A_REVERSE) + + def draw_tree_for_child (self, tree_window, child, reverse_depth): + if self.parent: + self.parent.draw_tree_for_child (tree_window, self, reverse_depth + 1) + if self.children[-1] == child: + # Last child + if reverse_depth == 0: + tree_window.addch (curses.ACS_LLCORNER) + tree_window.addch (curses.ACS_HLINE) + else: + tree_window.addch (' ') + tree_window.addch (' ') + else: + # Middle child + if reverse_depth == 0: + tree_window.addch (curses.ACS_LTEE) + tree_window.addch (curses.ACS_HLINE) + else: + tree_window.addch (curses.ACS_VLINE) + tree_window.addch (' ') + + def was_selected(self): + self.delegate.do_action() + +class TreePanel(Panel): + def __init__(self, frame, title, root_item): + self.root_item = root_item + self.title = title + self.first_visible_idx = 0 + self.selected_idx = 0 + self.items = None + super(TreePanel, self).__init__(frame) + self.add_key_action(curses.KEY_UP, self.select_prev, "Select the previous item") + self.add_key_action(curses.KEY_DOWN, self.select_next, "Select the next item") + self.add_key_action(curses.KEY_RIGHT,self.right_arrow, "Expand an item") + self.add_key_action(curses.KEY_LEFT, self.left_arrow, "Unexpand an item or navigate to parent") + self.add_key_action(curses.KEY_HOME, self.scroll_begin, "Go to the beginning of the tree") + self.add_key_action(curses.KEY_END, self.scroll_end, "Go to the end of the tree") + self.add_key_action(0x1b4f48, self.scroll_begin, "Go to the beginning of the tree") + self.add_key_action(0x1b4f46, self.scroll_end, "Go to the end of the tree") + self.add_key_action(curses.KEY_PPAGE, self.scroll_page_backward, "Scroll to previous page") + self.add_key_action(curses.KEY_NPAGE, self.scroll_page_forward, "Scroll to next forward") + + def get_selected_item(self): + if self.selected_idx < len(self.items): + return self.items[self.selected_idx] + else: + return None + + def select_item(self, item): + if self.items and item in self.items: + self.selected_idx = self.items.index(item) + return True + else: + return False + + def get_visible_items(self): + # Clear self.items when you want to update all chidren + if self.items is None: + self.items = list() + children = self.root_item.get_children() + if children: + for child in children: + child.append_visible_items(self.items) + return self.items + + def update(self): + self.erase() + self.draw_title_box(self.title) + visible_items = self.get_visible_items() + for (row, child) in enumerate(visible_items): + child.draw(self, row) + + def get_item_draw_point(self, row): + display_row = self.get_display_row(row) + if display_row >= 0: + return Point(2, display_row + 1) + else: + return Point(-1, -1) + + def get_display_row(self, row): + if row >= self.first_visible_idx: + display_row = row - self.first_visible_idx + if display_row < self.get_size().h-2: + return display_row + return -1 + + def is_selected(self, row): + return row == self.selected_idx + + def get_num_lines(self): + self.get_visible_items() + return len(self.items) + + def get_num_visible_lines(self): + return self.get_size().h-2 + def select_next (self): + self.selected_idx += 1 + num_lines = self.get_num_lines() + if self.selected_idx >= num_lines: + self.selected_idx = num_lines - 1 + self._selection_changed() + self.refresh() + + def select_prev (self): + self.selected_idx -= 1 + if self.selected_idx < 0: + num_lines = self.get_num_lines() + if num_lines > 0: + self.selected_idx = 0 + else: + self.selected_idx = -1 + self._selection_changed() + self.refresh() + + def scroll_begin (self): + self.first_visible_idx = 0 + num_lines = self.get_num_lines() + if num_lines > 0: + self.selected_idx = 0 + else: + self.selected_idx = -1 + self.refresh() + + def redisplay_tree(self): + self.items = None + self.refresh() + + def right_arrow(self): + selected_item = self.get_selected_item() + if selected_item and selected_item.is_expanded == False: + selected_item.is_expanded = True + self.redisplay_tree() + + def left_arrow(self): + selected_item = self.get_selected_item() + if selected_item: + if selected_item.is_expanded == True: + selected_item.is_expanded = False + self.redisplay_tree() + elif selected_item.parent: + if self.select_item(selected_item.parent): + self.refresh() + + + def scroll_end (self): + num_visible_lines = self.get_num_visible_lines() + num_lines = self.get_num_lines() + if num_lines > num_visible_lines: + self.first_visible_idx = num_lines - num_visible_lines + else: + self.first_visible_idx = 0 + self.selected_idx = num_lines-1 + self.refresh() + + def scroll_page_backward(self): + num_visible_lines = self.get_num_visible_lines() + new_index = self.selected_idx - num_visible_lines + if new_index < 0: + self.selected_idx = 0 + else: + self.selected_idx = new_index + self._selection_changed() + self.refresh() + + def scroll_page_forward(self): + num_lines = self.get_num_lines() + num_visible_lines = self.get_num_visible_lines() + new_index = self.selected_idx + num_visible_lines + if new_index >= num_lines: + new_index = num_lines - 1 + self.selected_idx = new_index + self._selection_changed() + self.refresh() + + def _selection_changed(self): + num_lines = self.get_num_lines() + num_visible_lines = self.get_num_visible_lines() + last_visible_index = self.first_visible_idx + num_visible_lines + if self.selected_idx >= last_visible_index: + self.first_visible_idx += (self.selected_idx - last_visible_index + 1) + if self.selected_idx < self.first_visible_idx: + self.first_visible_idx = self.selected_idx + if self.selected_idx >= 0 and self.selected_idx < len(self.items): + item = self.items[self.selected_idx] + item.was_selected() + + +class Menu(BoxedPanel): + def __init__(self, title, items): + max_title_width = 0 + for item in items: + if max_title_width < len(item.title): + max_title_width = len(item.title) + frame = Rect(x=0, y=0, w=max_title_width+4, h=len(items)+2) + super(Menu, self).__init__(frame, title=None, delegate=None, can_become_first_responder=True) + self.selected_idx = 0 + self.title = title + self.items = items + for (item_idx, item) in enumerate(items): + self.set_line(item_idx, item.title) + self.hide() + + def update(self): + super(Menu, self).update() + + def relinquish_first_responder(self): + if not self.hidden(): + self.hide() + + def perform_action(self): + selected_idx = self.get_selected_idx() + if selected_idx < len(self.items): + action = self.items[selected_idx].action + if action: + action() + +class MenuBar(Panel): + def __init__(self, frame): + super(MenuBar, self).__init__(frame, can_become_first_responder=True) + self.menus = list() + self.selected_menu_idx = -1 + self.add_key_action(curses.KEY_LEFT, self.select_prev, "Select the previous menu") + self.add_key_action(curses.KEY_RIGHT, self.select_next, "Select the next menu") + self.add_key_action(curses.KEY_DOWN, lambda: self.select(0), "Select the first menu") + self.add_key_action(27, self.relinquish_first_responder, "Hide current menu") + self.add_key_action(curses.KEY_ENTER, self.perform_action, "Select the next menu item") + self.add_key_action(10, self.perform_action, "Select the next menu item") + + def insert_menu(self, menu, index=sys.maxsize): + if index >= len(self.menus): + self.menus.append(menu) + else: + self.menus.insert(index, menu) + pt = self.get_position() + for menu in self.menus: + menu.set_position(pt) + pt.x += len(menu.title) + 5 + + def perform_action(self): + '''If no menu is visible, show the first menu. If a menu is visible, perform the action + associated with the selected menu item in the menu''' + menu_visible = False + for menu in self.menus: + if not menu.hidden(): + menu_visible = True + break + if menu_visible: + menu.perform_action() + self.selected_menu_idx = -1 + self._selected_menu_changed() + else: + self.select(0) + + def relinquish_first_responder(self): + if self.selected_menu_idx >= 0: + self.selected_menu_idx = -1 + self._selected_menu_changed() + + def _selected_menu_changed(self): + for (menu_idx, menu) in enumerate(self.menus): + is_hidden = menu.hidden() + if menu_idx != self.selected_menu_idx: + if not is_hidden: + if self.parent.pop_first_responder(menu) == False: + menu.hide() + for (menu_idx, menu) in enumerate(self.menus): + is_hidden = menu.hidden() + if menu_idx == self.selected_menu_idx: + if is_hidden: + menu.show() + self.parent.push_first_responder(menu) + menu.top() + self.parent.refresh() + + def select(self, index): + if index < len(self.menus): + self.selected_menu_idx = index + self._selected_menu_changed() + + def select_next (self): + num_menus = len(self.menus) + if self.selected_menu_idx == -1: + if num_menus > 0: + self.selected_menu_idx = 0 + self._selected_menu_changed() + else: + if self.selected_menu_idx + 1 < num_menus: + self.selected_menu_idx += 1 + else: + self.selected_menu_idx = -1 + self._selected_menu_changed() + + def select_prev (self): + num_menus = len(self.menus) + if self.selected_menu_idx == -1: + if num_menus > 0: + self.selected_menu_idx = num_menus - 1 + self._selected_menu_changed() + else: + if self.selected_menu_idx - 1 >= 0: + self.selected_menu_idx -= 1 + else: + self.selected_menu_idx = -1 + self._selected_menu_changed() + + def update(self): + self.erase() + is_in_first_responder_chain = self.is_in_first_responder_chain() + if is_in_first_responder_chain: + self.attron (curses.A_REVERSE) + pt = Point(x=0, y=0) + for menu in self.menus: + self.addstr(pt, '| ' + menu.title + ' ') + pt.x += len(menu.title) + 5 + self.addstr(pt, '|') + width = self.get_size().w + while pt.x < width: + self.addch_at_point(pt, ' ') + pt.x += 1 + if is_in_first_responder_chain: + self.attroff (curses.A_REVERSE) + + for menu in self.menus: + menu.update() + + +class StatusPanel(Panel): + def __init__(self, frame): + super(StatusPanel, self).__init__(frame, delegate=None, can_become_first_responder=False) + self.status_items = list() + self.status_dicts = dict() + self.next_status_x = 1 + + def add_status_item(self, name, title, format, width, value, update=True): + status_item_dict = { 'name': name, + 'title' : title, + 'width' : width, + 'format' : format, + 'value' : value, + 'x' : self.next_status_x } + index = len(self.status_items) + self.status_items.append(status_item_dict) + self.status_dicts[name] = index + self.next_status_x += width + 2; + if update: + self.update() + + def increment_status(self, name, update=True): + if name in self.status_dicts: + status_item_idx = self.status_dicts[name] + status_item_dict = self.status_items[status_item_idx] + status_item_dict['value'] = status_item_dict['value'] + 1 + if update: + self.update() + + def update_status(self, name, value, update=True): + if name in self.status_dicts: + status_item_idx = self.status_dicts[name] + status_item_dict = self.status_items[status_item_idx] + status_item_dict['value'] = status_item_dict['format'] % (value) + if update: + self.update() + def update(self): + self.erase(); + for status_item_dict in self.status_items: + self.addnstr_at_point(Point(x=status_item_dict['x'], y=0), '%s: %s' % (status_item_dict['title'], status_item_dict['value']), status_item_dict['width']) + +stdscr = None + +def intialize_curses(): + global stdscr + stdscr = curses.initscr() + curses.noecho() + curses.cbreak() + stdscr.keypad(1) + try: + curses.start_color() + except: + pass + return Window(stdscr) + +def terminate_curses(): + global stdscr + if stdscr: + stdscr.keypad(0) + curses.echo() + curses.nocbreak() + curses.endwin() + diff --git a/packages/Python/lldbsuite/test/lldbinline.py b/packages/Python/lldbsuite/test/lldbinline.py new file mode 100644 index 000000000000..691a3fba6dc2 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldbinline.py @@ -0,0 +1,210 @@ +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import os +import sys + +# Third-party modules + +# LLDB modules +import lldb +from .lldbtest import * +from . import lldbutil + +def source_type(filename): + _, extension = os.path.splitext(filename) + return { + '.c' : 'C_SOURCES', + '.cpp' : 'CXX_SOURCES', + '.cxx' : 'CXX_SOURCES', + '.cc' : 'CXX_SOURCES', + '.m' : 'OBJC_SOURCES', + '.mm' : 'OBJCXX_SOURCES' + }.get(extension, None) + +class CommandParser: + def __init__(self): + self.breakpoints = [] + + def parse_one_command(self, line): + parts = line.split('//%') + + command = None + new_breakpoint = True + + if len(parts) == 2: + command = parts[1].strip() # take off whitespace + new_breakpoint = parts[0].strip() != "" + + return (command, new_breakpoint) + + def parse_source_files(self, source_files): + for source_file in source_files: + file_handle = open(source_file) + lines = file_handle.readlines() + line_number = 0 + current_breakpoint = None # non-NULL means we're looking through whitespace to find additional commands + for line in lines: + line_number = line_number + 1 # 1-based, so we do this first + (command, new_breakpoint) = self.parse_one_command(line) + + if new_breakpoint: + current_breakpoint = None + + if command != None: + if current_breakpoint == None: + current_breakpoint = {} + current_breakpoint['file_name'] = source_file + current_breakpoint['line_number'] = line_number + current_breakpoint['command'] = command + self.breakpoints.append(current_breakpoint) + else: + current_breakpoint['command'] = current_breakpoint['command'] + "\n" + command + + def set_breakpoints(self, target): + for breakpoint in self.breakpoints: + breakpoint['breakpoint'] = target.BreakpointCreateByLocation(breakpoint['file_name'], breakpoint['line_number']) + + def handle_breakpoint(self, test, breakpoint_id): + for breakpoint in self.breakpoints: + if breakpoint['breakpoint'].GetID() == breakpoint_id: + test.execute_user_command(breakpoint['command']) + return + +class InlineTest(TestBase): + # Internal implementation + + def getRerunArgs(self): + # The -N option says to NOT run a if it matches the option argument, so + # if we are using dSYM we say to NOT run dwarf (-N dwarf) and vice versa. + if self.using_dsym is None: + # The test was skipped altogether. + return "" + elif self.using_dsym: + return "-N dwarf %s" % (self.mydir) + else: + return "-N dsym %s" % (self.mydir) + + def BuildMakefile(self): + if os.path.exists("Makefile"): + return + + categories = {} + + for f in os.listdir(os.getcwd()): + t = source_type(f) + if t: + if t in list(categories.keys()): + categories[t].append(f) + else: + categories[t] = [f] + + makefile = open("Makefile", 'w+') + + level = os.sep.join([".."] * len(self.mydir.split(os.sep))) + os.sep + "make" + + makefile.write("LEVEL = " + level + "\n") + + for t in list(categories.keys()): + line = t + " := " + " ".join(categories[t]) + makefile.write(line + "\n") + + if ('OBJCXX_SOURCES' in list(categories.keys())) or ('OBJC_SOURCES' in list(categories.keys())): + makefile.write("LDFLAGS = $(CFLAGS) -lobjc -framework Foundation\n") + + if ('CXX_SOURCES' in list(categories.keys())): + makefile.write("CXXFLAGS += -std=c++11\n") + + makefile.write("include $(LEVEL)/Makefile.rules\n") + makefile.flush() + makefile.close() + + + @skipUnlessDarwin + def __test_with_dsym(self): + self.using_dsym = True + self.BuildMakefile() + self.buildDsym() + self.do_test() + + def __test_with_dwarf(self): + self.using_dsym = False + self.BuildMakefile() + self.buildDwarf() + self.do_test() + + def __test_with_dwo(self): + self.using_dsym = False + self.BuildMakefile() + self.buildDwo() + self.do_test() + + def execute_user_command(self, __command): + exec(__command, globals(), locals()) + + def do_test(self): + exe_name = "a.out" + exe = os.path.join(os.getcwd(), exe_name) + source_files = [ f for f in os.listdir(os.getcwd()) if source_type(f) ] + target = self.dbg.CreateTarget(exe) + + parser = CommandParser() + parser.parse_source_files(source_files) + parser.set_breakpoints(target) + + process = target.LaunchSimple(None, None, os.getcwd()) + + while lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint): + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + breakpoint_id = thread.GetStopReasonDataAtIndex (0) + parser.handle_breakpoint(self, breakpoint_id) + process.Continue() + + + # Utilities for testcases + + def check_expression (self, expression, expected_result, use_summary = True): + value = self.frame().EvaluateExpression (expression) + self.assertTrue(value.IsValid(), expression+"returned a valid value") + if self.TraceOn(): + print(value.GetSummary()) + print(value.GetValue()) + if use_summary: + answer = value.GetSummary() + else: + answer = value.GetValue() + report_str = "%s expected: %s got: %s"%(expression, expected_result, answer) + self.assertTrue(answer == expected_result, report_str) + +def ApplyDecoratorsToFunction(func, decorators): + tmp = func + if type(decorators) == list: + for decorator in decorators: + tmp = decorator(tmp) + elif hasattr(decorators, '__call__'): + tmp = decorators(tmp) + return tmp + + +def MakeInlineTest(__file, __globals, decorators=None): + # Derive the test name from the current file name + file_basename = os.path.basename(__file) + InlineTest.mydir = TestBase.compute_mydir(__file) + + test_name, _ = os.path.splitext(file_basename) + # Build the test case + test = type(test_name, (InlineTest,), {'using_dsym': None}) + test.name = test_name + + test.test_with_dsym = ApplyDecoratorsToFunction(test._InlineTest__test_with_dsym, decorators) + test.test_with_dwarf = ApplyDecoratorsToFunction(test._InlineTest__test_with_dwarf, decorators) + test.test_with_dwo = ApplyDecoratorsToFunction(test._InlineTest__test_with_dwo, decorators) + + # Add the test case to the globals, and hide InlineTest + __globals.update({test_name : test}) + + # Store the name of the originating file.o + test.test_filename = __file + return test + diff --git a/packages/Python/lldbsuite/test/lldbpexpect.py b/packages/Python/lldbsuite/test/lldbpexpect.py new file mode 100644 index 000000000000..55b958a55972 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldbpexpect.py @@ -0,0 +1,63 @@ +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import os +import sys + +# Third-party modules +import pexpect + +# LLDB Modules +import lldb +from .lldbtest import * +from . import lldbutil + +class PExpectTest(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + + def launchArgs(self): + pass + + def launch(self, timeout=None): + if timeout is None: timeout = 30 + logfile = sys.stdout if self.TraceOn() else None + self.child = pexpect.spawn('%s %s' % (lldbtest_config.lldbExec, self.launchArgs()), logfile=logfile) + self.child.timeout = timeout + self.timeout = timeout + + def expect(self, patterns=None, timeout=None, exact=None): + if patterns is None: return None + if timeout is None: timeout = self.timeout + if exact is None: exact = False + if exact: + return self.child.expect_exact(patterns, timeout=timeout) + else: + return self.child.expect(patterns, timeout=timeout) + + def expectall(self, patterns=None, timeout=None, exact=None): + if patterns is None: return None + if timeout is None: timeout = self.timeout + if exact is None: exact = False + for pattern in patterns: + self.expect(pattern, timeout=timeout, exact=exact) + + def sendimpl(self, sender, command, patterns=None, timeout=None, exact=None): + sender(command) + return self.expect(patterns=patterns, timeout=timeout, exact=exact) + + def send(self, command, patterns=None, timeout=None, exact=None): + return self.sendimpl(self.child.send, command, patterns, timeout, exact) + + def sendline(self, command, patterns=None, timeout=None, exact=None): + return self.sendimpl(self.child.sendline, command, patterns, timeout, exact) + + def quit(self, gracefully=None): + if gracefully is None: gracefully = True + self.child.sendeof() + self.child.close(force=not gracefully) + self.child = None diff --git a/packages/Python/lldbsuite/test/lldbplatformutil.py b/packages/Python/lldbsuite/test/lldbplatformutil.py new file mode 100644 index 000000000000..f51c016c13c2 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldbplatformutil.py @@ -0,0 +1,27 @@ +""" This module contains functions used by the test cases to hide the +architecture and/or the platform dependent nature of the tests. """ + +from __future__ import absolute_import + +# System modules + +# Third-party modules + +# LLDB modules + +import re + +def check_first_register_readable(test_case): + arch = test_case.getArchitecture() + + if arch in ['x86_64', 'i386']: + test_case.expect("register read eax", substrs = ['eax = 0x']) + elif arch in ['arm']: + test_case.expect("register read r0", substrs = ['r0 = 0x']) + elif arch in ['aarch64']: + test_case.expect("register read x0", substrs = ['x0 = 0x']) + elif re.match("mips",arch): + test_case.expect("register read zero", substrs = ['zero = 0x']) + else: + # TODO: Add check for other architectures + test_case.fail("Unsupported architecture for test case (arch: %s)" % test_case.getArchitecture()) diff --git a/packages/Python/lldbsuite/test/lldbtest.py b/packages/Python/lldbsuite/test/lldbtest.py new file mode 100644 index 000000000000..43d02c521d18 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldbtest.py @@ -0,0 +1,2775 @@ +""" +LLDB module which provides the abstract base class of lldb test case. + +The concrete subclass can override lldbtest.TesBase in order to inherit the +common behavior for unitest.TestCase.setUp/tearDown implemented in this file. + +The subclass should override the attribute mydir in order for the python runtime +to locate the individual test cases when running as part of a large test suite +or when running each test case as a separate python invocation. + +./dotest.py provides a test driver which sets up the environment to run the +entire of part of the test suite . Example: + +# Exercises the test suite in the types directory.... +/Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 types +... + +Session logs for test failures/errors/unexpected successes will go into directory '2012-05-16-13_35_42' +Command invoked: python ./dotest.py -A x86_64 types +compilers=['clang'] + +Configuration: arch=x86_64 compiler=clang +---------------------------------------------------------------------- +Collected 72 tests + +........................................................................ +---------------------------------------------------------------------- +Ran 72 tests in 135.468s + +OK +$ +""" + +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import abc +import collections +from distutils.version import LooseVersion +import gc +import glob +import inspect +import os, sys, traceback +import os.path +import re +import signal +from subprocess import * +import time +import types + +# Third-party modules +import unittest2 +from six import add_metaclass +from six import StringIO as SixStringIO +from six.moves.urllib import parse as urlparse +import six + +# LLDB modules +import lldb +from . import configuration +from . import lldbtest_config +from . import lldbutil +from . import test_categories + +from .result_formatter import EventBuilder + +# dosep.py starts lots and lots of dotest instances +# This option helps you find if two (or more) dotest instances are using the same +# directory at the same time +# Enable it to cause test failures and stderr messages if dotest instances try to run in +# the same directory simultaneously +# it is disabled by default because it litters the test directories with ".dirlock" files +debug_confirm_directory_exclusivity = False + +# See also dotest.parseOptionsAndInitTestdirs(), where the environment variables +# LLDB_COMMAND_TRACE and LLDB_DO_CLEANUP are set from '-t' and '-r dir' options. + +# By default, traceAlways is False. +if "LLDB_COMMAND_TRACE" in os.environ and os.environ["LLDB_COMMAND_TRACE"]=="YES": + traceAlways = True +else: + traceAlways = False + +# By default, doCleanup is True. +if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"]=="NO": + doCleanup = False +else: + doCleanup = True + + +# +# Some commonly used assert messages. +# + +COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" + +CURRENT_EXECUTABLE_SET = "Current executable set successfully" + +PROCESS_IS_VALID = "Process is valid" + +PROCESS_KILLED = "Process is killed successfully" + +PROCESS_EXITED = "Process exited successfully" + +PROCESS_STOPPED = "Process status should be stopped" + +RUN_SUCCEEDED = "Process is launched successfully" + +RUN_COMPLETED = "Process exited successfully" + +BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" + +BREAKPOINT_CREATED = "Breakpoint created successfully" + +BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" + +BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" + +BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit cout = 1" + +BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit cout = 2" + +BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit cout = 3" + +MISSING_EXPECTED_REGISTERS = "At least one expected register is unavailable." + +OBJECT_PRINTED_CORRECTLY = "Object printed correctly" + +SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" + +STEP_OUT_SUCCEEDED = "Thread step-out succeeded" + +STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" + +STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion" + +STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" + +STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( + STOPPED_DUE_TO_BREAKPOINT, "instead, the actual stop reason is: '%s'") + +STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" + +STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count" + +STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" + +STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" + +STOPPED_DUE_TO_WATCHPOINT = "Process should be stopped due to watchpoint" + +DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" + +VALID_BREAKPOINT = "Got a valid breakpoint" + +VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" + +VALID_COMMAND_INTERPRETER = "Got a valid command interpreter" + +VALID_FILESPEC = "Got a valid filespec" + +VALID_MODULE = "Got a valid module" + +VALID_PROCESS = "Got a valid process" + +VALID_SYMBOL = "Got a valid symbol" + +VALID_TARGET = "Got a valid target" + +VALID_PLATFORM = "Got a valid platform" + +VALID_TYPE = "Got a valid type" + +VALID_VARIABLE = "Got a valid variable" + +VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" + +WATCHPOINT_CREATED = "Watchpoint created successfully" + +def CMD_MSG(str): + '''A generic "Command '%s' returns successfully" message generator.''' + return "Command '%s' returns successfully" % str + +def COMPLETION_MSG(str_before, str_after): + '''A generic message generator for the completion mechanism.''' + return "'%s' successfully completes to '%s'" % (str_before, str_after) + +def EXP_MSG(str, exe): + '''A generic "'%s' returns expected result" message generator if exe. + Otherwise, it generates "'%s' matches expected result" message.''' + return "'%s' %s expected result" % (str, 'returns' if exe else 'matches') + +def SETTING_MSG(setting): + '''A generic "Value of setting '%s' is correct" message generator.''' + return "Value of setting '%s' is correct" % setting + +def EnvArray(): + """Returns an env variable array from the os.environ map object.""" + return list(map(lambda k,v: k+"="+v, list(os.environ.keys()), list(os.environ.values()))) + +def line_number(filename, string_to_match): + """Helper function to return the line number of the first matched string.""" + with open(filename, 'r') as f: + for i, line in enumerate(f): + if line.find(string_to_match) != -1: + # Found our match. + return i+1 + raise Exception("Unable to find '%s' within file %s" % (string_to_match, filename)) + +def pointer_size(): + """Return the pointer size of the host system.""" + import ctypes + a_pointer = ctypes.c_void_p(0xffff) + return 8 * ctypes.sizeof(a_pointer) + +def is_exe(fpath): + """Returns true if fpath is an executable.""" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + """Returns the full path to a program; None otherwise.""" + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +class recording(SixStringIO): + """ + A nice little context manager for recording the debugger interactions into + our session object. If trace flag is ON, it also emits the interactions + into the stderr. + """ + def __init__(self, test, trace): + """Create a SixStringIO instance; record the session obj and trace flag.""" + SixStringIO.__init__(self) + # The test might not have undergone the 'setUp(self)' phase yet, so that + # the attribute 'session' might not even exist yet. + self.session = getattr(test, "session", None) if test else None + self.trace = trace + + def __enter__(self): + """ + Context management protocol on entry to the body of the with statement. + Just return the SixStringIO object. + """ + return self + + def __exit__(self, type, value, tb): + """ + Context management protocol on exit from the body of the with statement. + If trace is ON, it emits the recordings into stderr. Always add the + recordings to our session object. And close the SixStringIO object, too. + """ + if self.trace: + print(self.getvalue(), file=sys.stderr) + if self.session: + print(self.getvalue(), file=self.session) + self.close() + +@add_metaclass(abc.ABCMeta) +class _BaseProcess(object): + + @abc.abstractproperty + def pid(self): + """Returns process PID if has been launched already.""" + + @abc.abstractmethod + def launch(self, executable, args): + """Launches new process with given executable and args.""" + + @abc.abstractmethod + def terminate(self): + """Terminates previously launched process..""" + +class _LocalProcess(_BaseProcess): + + def __init__(self, trace_on): + self._proc = None + self._trace_on = trace_on + self._delayafterterminate = 0.1 + + @property + def pid(self): + return self._proc.pid + + def launch(self, executable, args): + self._proc = Popen([executable] + args, + stdout = open(os.devnull) if not self._trace_on else None, + stdin = PIPE) + + def terminate(self): + if self._proc.poll() == None: + # Terminate _proc like it does the pexpect + signals_to_try = [sig for sig in ['SIGHUP', 'SIGCONT', 'SIGINT'] if sig in dir(signal)] + for sig in signals_to_try: + try: + self._proc.send_signal(getattr(signal, sig)) + time.sleep(self._delayafterterminate) + if self._proc.poll() != None: + return + except ValueError: + pass # Windows says SIGINT is not a valid signal to send + self._proc.terminate() + time.sleep(self._delayafterterminate) + if self._proc.poll() != None: + return + self._proc.kill() + time.sleep(self._delayafterterminate) + + def poll(self): + return self._proc.poll() + +class _RemoteProcess(_BaseProcess): + + def __init__(self, install_remote): + self._pid = None + self._install_remote = install_remote + + @property + def pid(self): + return self._pid + + def launch(self, executable, args): + if self._install_remote: + src_path = executable + dst_path = lldbutil.append_to_process_working_directory(os.path.basename(executable)) + + dst_file_spec = lldb.SBFileSpec(dst_path, False) + err = lldb.remote_platform.Install(lldb.SBFileSpec(src_path, True), dst_file_spec) + if err.Fail(): + raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (src_path, dst_path, err)) + else: + dst_path = executable + dst_file_spec = lldb.SBFileSpec(executable, False) + + launch_info = lldb.SBLaunchInfo(args) + launch_info.SetExecutableFile(dst_file_spec, True) + launch_info.SetWorkingDirectory(lldb.remote_platform.GetWorkingDirectory()) + + # Redirect stdout and stderr to /dev/null + launch_info.AddSuppressFileAction(1, False, True) + launch_info.AddSuppressFileAction(2, False, True) + + err = lldb.remote_platform.Launch(launch_info) + if err.Fail(): + raise Exception("remote_platform.Launch('%s', '%s') failed: %s" % (dst_path, args, err)) + self._pid = launch_info.GetProcessID() + + def terminate(self): + lldb.remote_platform.Kill(self._pid) + +# From 2.7's subprocess.check_output() convenience function. +# Return a tuple (stdoutdata, stderrdata). +def system(commands, **kwargs): + r"""Run an os command with arguments and return its output as a byte string. + + If the exit code was non-zero it raises a CalledProcessError. The + CalledProcessError object will have the return code in the returncode + attribute and output in the output attribute. + + The arguments are the same as for the Popen constructor. Example: + + >>> check_output(["ls", "-l", "/dev/null"]) + 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' + + The stdout argument is not allowed as it is used internally. + To capture standard error in the result, use stderr=STDOUT. + + >>> check_output(["/bin/sh", "-c", + ... "ls -l non_existent_file ; exit 0"], + ... stderr=STDOUT) + 'ls: non_existent_file: No such file or directory\n' + """ + + # Assign the sender object to variable 'test' and remove it from kwargs. + test = kwargs.pop('sender', None) + + # [['make', 'clean', 'foo'], ['make', 'foo']] -> ['make clean foo', 'make foo'] + commandList = [' '.join(x) for x in commands] + output = "" + error = "" + for shellCommand in commandList: + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden.') + if 'shell' in kwargs and kwargs['shell']==False: + raise ValueError('shell=False not allowed') + process = Popen(shellCommand, stdout=PIPE, stderr=PIPE, shell=True, universal_newlines=True, **kwargs) + pid = process.pid + this_output, this_error = process.communicate() + retcode = process.poll() + + # Enable trace on failure return while tracking down FreeBSD buildbot issues + trace = traceAlways + if not trace and retcode and sys.platform.startswith("freebsd"): + trace = True + + with recording(test, trace) as sbuf: + print(file=sbuf) + print("os command:", shellCommand, file=sbuf) + print("with pid:", pid, file=sbuf) + print("stdout:", this_output, file=sbuf) + print("stderr:", this_error, file=sbuf) + print("retcode:", retcode, file=sbuf) + print(file=sbuf) + + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = shellCommand + raise CalledProcessError(retcode, cmd) + output = output + this_output + error = error + this_error + return (output, error) + +def getsource_if_available(obj): + """ + Return the text of the source code for an object if available. Otherwise, + a print representation is returned. + """ + import inspect + try: + return inspect.getsource(obj) + except: + return repr(obj) + +def builder_module(): + if sys.platform.startswith("freebsd"): + return __import__("builder_freebsd") + if sys.platform.startswith("netbsd"): + return __import__("builder_netbsd") + return __import__("builder_" + sys.platform) + +def run_adb_command(cmd, device_id): + device_id_args = [] + if device_id: + device_id_args = ["-s", device_id] + full_cmd = ["adb"] + device_id_args + cmd + p = Popen(full_cmd, stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + return p.returncode, stdout, stderr + +def append_android_envs(dictionary): + if dictionary is None: + dictionary = {} + dictionary["OS"] = "Android" + if android_device_api() >= 16: + dictionary["PIE"] = 1 + return dictionary + +def target_is_android(): + if not hasattr(target_is_android, 'result'): + triple = lldb.DBG.GetSelectedPlatform().GetTriple() + match = re.match(".*-.*-.*-android", triple) + target_is_android.result = match is not None + return target_is_android.result + +def android_device_api(): + if not hasattr(android_device_api, 'result'): + assert configuration.lldb_platform_url is not None + device_id = None + parsed_url = urlparse.urlparse(configuration.lldb_platform_url) + host_name = parsed_url.netloc.split(":")[0] + if host_name != 'localhost': + device_id = host_name + if device_id.startswith('[') and device_id.endswith(']'): + device_id = device_id[1:-1] + retcode, stdout, stderr = run_adb_command( + ["shell", "getprop", "ro.build.version.sdk"], device_id) + if retcode == 0: + android_device_api.result = int(stdout) + else: + raise LookupError( + ">>> Unable to determine the API level of the Android device.\n" + ">>> stdout:\n%s\n" + ">>> stderr:\n%s\n" % (stdout, stderr)) + return android_device_api.result + +def check_expected_version(comparison, expected, actual): + def fn_leq(x,y): return x <= y + def fn_less(x,y): return x < y + def fn_geq(x,y): return x >= y + def fn_greater(x,y): return x > y + def fn_eq(x,y): return x == y + def fn_neq(x,y): return x != y + + op_lookup = { + "==": fn_eq, + "=": fn_eq, + "!=": fn_neq, + "<>": fn_neq, + ">": fn_greater, + "<": fn_less, + ">=": fn_geq, + "<=": fn_leq + } + expected_str = '.'.join([str(x) for x in expected]) + actual_str = '.'.join([str(x) for x in actual]) + + return op_lookup[comparison](LooseVersion(actual_str), LooseVersion(expected_str)) + +# +# Decorators for categorizing test cases. +# +from functools import wraps + +def add_test_categories(cat): + """Add test categories to a TestCase method""" + cat = test_categories.validate(cat, True) + def impl(func): + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@add_test_categories can only be used to decorate a test method") + if hasattr(func, "categories"): + cat.extend(func.categories) + func.categories = cat + return func + + return impl + +def benchmarks_test(func): + """Decorate the item as a benchmarks test.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@benchmarks_test can only be used to decorate a test method") + @wraps(func) + def wrapper(self, *args, **kwargs): + self.skipTest("benchmarks test") + return func(self, *args, **kwargs) + + # Mark this function as such to separate them from the regular tests. + wrapper.__benchmarks_test__ = True + return wrapper + +def no_debug_info_test(func): + """Decorate the item as a test what don't use any debug info. If this annotation is specified + then the test runner won't generate a separate test for each debug info format. """ + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@no_debug_info_test can only be used to decorate a test method") + @wraps(func) + def wrapper(self, *args, **kwargs): + return func(self, *args, **kwargs) + + # Mark this function as such to separate them from the regular tests. + wrapper.__no_debug_info_test__ = True + return wrapper + +def debugserver_test(func): + """Decorate the item as a debugserver test.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@debugserver_test can only be used to decorate a test method") + @wraps(func) + def wrapper(self, *args, **kwargs): + if configuration.dont_do_debugserver_test: + self.skipTest("debugserver tests") + return func(self, *args, **kwargs) + + # Mark this function as such to separate them from the regular tests. + wrapper.__debugserver_test__ = True + return wrapper + +def llgs_test(func): + """Decorate the item as a lldb-server test.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@llgs_test can only be used to decorate a test method") + @wraps(func) + def wrapper(self, *args, **kwargs): + if configuration.dont_do_llgs_test: + self.skipTest("llgs tests") + return func(self, *args, **kwargs) + + # Mark this function as such to separate them from the regular tests. + wrapper.__llgs_test__ = True + return wrapper + +def not_remote_testsuite_ready(func): + """Decorate the item as a test which is not ready yet for remote testsuite.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@not_remote_testsuite_ready can only be used to decorate a test method") + @wraps(func) + def wrapper(self, *args, **kwargs): + if lldb.remote_platform: + self.skipTest("not ready for remote testsuite") + return func(self, *args, **kwargs) + + # Mark this function as such to separate them from the regular tests. + wrapper.__not_ready_for_remote_testsuite_test__ = True + return wrapper + +def expectedFailure(expected_fn, bugnumber=None): + def expectedFailure_impl(func): + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + if expected_fn(self): + if configuration.results_formatter_object is not None: + # Mark this test as expected to fail. + configuration.results_formatter_object.handle_event( + EventBuilder.event_for_mark_test_expected_failure(self)) + xfail_func = unittest2.expectedFailure(func) + xfail_func(*args, **kwargs) + else: + func(*args, **kwargs) + return wrapper + # if bugnumber is not-callable(incluing None), that means decorator function is called with optional arguments + # return decorator in this case, so it will be used to decorating original method + if six.callable(bugnumber): + return expectedFailure_impl(bugnumber) + else: + return expectedFailure_impl + +# You can also pass not_in(list) to reverse the sense of the test for the arguments that +# are simple lists, namely oslist, compiler, and debug_info. + +def not_in(iterable): + return lambda x : x not in iterable + +def check_list_or_lambda(list_or_lambda, value): + if six.callable(list_or_lambda): + return list_or_lambda(value) + elif isinstance(list_or_lambda, list): + for item in list_or_lambda: + if value in item: + return True + return False + elif isinstance(list_or_lambda, str): + return value is None or value in list_or_lambda + else: + return list_or_lambda is None or value is None or list_or_lambda == value + +# provide a function to xfail on defined oslist, compiler version, and archs +# if none is specified for any argument, that argument won't be checked and thus means for all +# for example, +# @expectedFailureAll, xfail for all platform/compiler/arch, +# @expectedFailureAll(compiler='gcc'), xfail for gcc on all platform/architecture +# @expectedFailureAll(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), xfail for gcc>=4.9 on linux with i386 +def expectedFailureAll(bugnumber=None, oslist=None, hostoslist=None, compiler=None, compiler_version=None, archs=None, triple=None, debug_info=None, swig_version=None, py_version=None): + def fn(self): + oslist_passes = check_list_or_lambda(oslist, self.getPlatform()) + hostoslist_passes = check_list_or_lambda(hostoslist, getHostPlatform()) + compiler_passes = check_list_or_lambda(self.getCompiler(), compiler) and self.expectedCompilerVersion(compiler_version) + arch_passes = check_list_or_lambda(archs, self.getArchitecture()) + triple_passes = triple is None or re.match(triple, lldb.DBG.GetSelectedPlatform().GetTriple()) + debug_info_passes = check_list_or_lambda(debug_info, self.debug_info) + swig_version_passes = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version)) + py_version_passes = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info) + + return (oslist_passes and + hostoslist_passes and + compiler_passes and + arch_passes and + triple_passes and + debug_info_passes and + swig_version_passes and + py_version_passes) + return expectedFailure(fn, bugnumber) + +def expectedFailureDwarf(bugnumber=None): + return expectedFailureAll(bugnumber=bugnumber, debug_info="dwarf") + +def expectedFailureDwo(bugnumber=None): + return expectedFailureAll(bugnumber=bugnumber, debug_info="dwo") + +def expectedFailureDsym(bugnumber=None): + return expectedFailureAll(bugnumber=bugnumber, debug_info="dsym") + +def expectedFailureCompiler(compiler, compiler_version=None, bugnumber=None): + if compiler_version is None: + compiler_version=['=', None] + return expectedFailureAll(bugnumber=bugnumber, compiler=compiler, compiler_version=compiler_version) + +# to XFAIL a specific clang versions, try this +# @expectedFailureClang('bugnumber', ['<=', '3.4']) +def expectedFailureClang(bugnumber=None, compiler_version=None): + return expectedFailureCompiler('clang', compiler_version, bugnumber) + +def expectedFailureGcc(bugnumber=None, compiler_version=None): + return expectedFailureCompiler('gcc', compiler_version, bugnumber) + +def expectedFailureIcc(bugnumber=None): + return expectedFailureCompiler('icc', None, bugnumber) + +def expectedFailureArch(arch, bugnumber=None): + def fn(self): + return arch in self.getArchitecture() + return expectedFailure(fn, bugnumber) + +def expectedFailurei386(bugnumber=None): + return expectedFailureArch('i386', bugnumber) + +def expectedFailurex86_64(bugnumber=None): + return expectedFailureArch('x86_64', bugnumber) + +def expectedFailureOS(oslist, bugnumber=None, compilers=None, debug_info=None): + def fn(self): + return (self.getPlatform() in oslist and + self.expectedCompiler(compilers) and + (debug_info is None or self.debug_info in debug_info)) + return expectedFailure(fn, bugnumber) + +def expectedFailureHostOS(oslist, bugnumber=None, compilers=None): + def fn(self): + return (getHostPlatform() in oslist and + self.expectedCompiler(compilers)) + return expectedFailure(fn, bugnumber) + +def expectedFailureDarwin(bugnumber=None, compilers=None, debug_info=None): + # For legacy reasons, we support both "darwin" and "macosx" as OS X triples. + return expectedFailureOS(getDarwinOSTriples(), bugnumber, compilers, debug_info=debug_info) + +def expectedFailureFreeBSD(bugnumber=None, compilers=None, debug_info=None): + return expectedFailureOS(['freebsd'], bugnumber, compilers, debug_info=debug_info) + +def expectedFailureLinux(bugnumber=None, compilers=None, debug_info=None): + return expectedFailureOS(['linux'], bugnumber, compilers, debug_info=debug_info) + +def expectedFailureNetBSD(bugnumber=None, compilers=None, debug_info=None): + return expectedFailureOS(['netbsd'], bugnumber, compilers, debug_info=debug_info) + +def expectedFailureWindows(bugnumber=None, compilers=None, debug_info=None): + return expectedFailureOS(['windows'], bugnumber, compilers, debug_info=debug_info) + +def expectedFailureHostWindows(bugnumber=None, compilers=None): + return expectedFailureHostOS(['windows'], bugnumber, compilers) + +def matchAndroid(api_levels=None, archs=None): + def match(self): + if not target_is_android(): + return False + if archs is not None and self.getArchitecture() not in archs: + return False + if api_levels is not None and android_device_api() not in api_levels: + return False + return True + return match + + +def expectedFailureAndroid(bugnumber=None, api_levels=None, archs=None): + """ Mark a test as xfail for Android. + + Arguments: + bugnumber - The LLVM pr associated with the problem. + api_levels - A sequence of numbers specifying the Android API levels + for which a test is expected to fail. None means all API level. + arch - A sequence of architecture names specifying the architectures + for which a test is expected to fail. None means all architectures. + """ + return expectedFailure(matchAndroid(api_levels, archs), bugnumber) + +# Flakey tests get two chances to run. If they fail the first time round, the result formatter +# makes sure it is run one more time. +def expectedFlakey(expected_fn, bugnumber=None): + def expectedFailure_impl(func): + @wraps(func) + def wrapper(*args, **kwargs): + self = args[0] + if expected_fn(self): + # Send event marking test as explicitly eligible for rerunning. + if configuration.results_formatter_object is not None: + # Mark this test as rerunnable. + configuration.results_formatter_object.handle_event( + EventBuilder.event_for_mark_test_rerun_eligible(self)) + func(*args, **kwargs) + return wrapper + # if bugnumber is not-callable(incluing None), that means decorator function is called with optional arguments + # return decorator in this case, so it will be used to decorating original method + if six.callable(bugnumber): + return expectedFailure_impl(bugnumber) + else: + return expectedFailure_impl + +def expectedFlakeyDwarf(bugnumber=None): + def fn(self): + return self.debug_info == "dwarf" + return expectedFlakey(fn, bugnumber) + +def expectedFlakeyDsym(bugnumber=None): + def fn(self): + return self.debug_info == "dwarf" + return expectedFlakey(fn, bugnumber) + +def expectedFlakeyOS(oslist, bugnumber=None, compilers=None): + def fn(self): + return (self.getPlatform() in oslist and + self.expectedCompiler(compilers)) + return expectedFlakey(fn, bugnumber) + +def expectedFlakeyDarwin(bugnumber=None, compilers=None): + # For legacy reasons, we support both "darwin" and "macosx" as OS X triples. + return expectedFlakeyOS(getDarwinOSTriples(), bugnumber, compilers) + +def expectedFlakeyFreeBSD(bugnumber=None, compilers=None): + return expectedFlakeyOS(['freebsd'], bugnumber, compilers) + +def expectedFlakeyLinux(bugnumber=None, compilers=None): + return expectedFlakeyOS(['linux'], bugnumber, compilers) + +def expectedFlakeyNetBSD(bugnumber=None, compilers=None): + return expectedFlakeyOS(['netbsd'], bugnumber, compilers) + +def expectedFlakeyCompiler(compiler, compiler_version=None, bugnumber=None): + if compiler_version is None: + compiler_version=['=', None] + def fn(self): + return compiler in self.getCompiler() and self.expectedCompilerVersion(compiler_version) + return expectedFlakey(fn, bugnumber) + +# @expectedFlakeyClang('bugnumber', ['<=', '3.4']) +def expectedFlakeyClang(bugnumber=None, compiler_version=None): + return expectedFlakeyCompiler('clang', compiler_version, bugnumber) + +# @expectedFlakeyGcc('bugnumber', ['<=', '3.4']) +def expectedFlakeyGcc(bugnumber=None, compiler_version=None): + return expectedFlakeyCompiler('gcc', compiler_version, bugnumber) + +def expectedFlakeyAndroid(bugnumber=None, api_levels=None, archs=None): + return expectedFlakey(matchAndroid(api_levels, archs), bugnumber) + +def skipIfRemote(func): + """Decorate the item to skip tests if testing remotely.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfRemote can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + if lldb.remote_platform: + self = args[0] + self.skipTest("skip on remote platform") + else: + func(*args, **kwargs) + return wrapper + +def skipUnlessListedRemote(remote_list=None): + def myImpl(func): + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfRemote can only be used to decorate a " + "test method") + + @wraps(func) + def wrapper(*args, **kwargs): + if remote_list and lldb.remote_platform: + self = args[0] + triple = self.dbg.GetSelectedPlatform().GetTriple() + for r in remote_list: + if r in triple: + func(*args, **kwargs) + return + self.skipTest("skip on remote platform %s" % str(triple)) + else: + func(*args, **kwargs) + return wrapper + + return myImpl + +def skipIfRemoteDueToDeadlock(func): + """Decorate the item to skip tests if testing remotely due to the test deadlocking.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfRemote can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + if lldb.remote_platform: + self = args[0] + self.skipTest("skip on remote platform (deadlocks)") + else: + func(*args, **kwargs) + return wrapper + +def skipIfNoSBHeaders(func): + """Decorate the item to mark tests that should be skipped when LLDB is built with no SB API headers.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfNoSBHeaders can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + if sys.platform.startswith("darwin"): + header = os.path.join(os.environ["LLDB_LIB_DIR"], 'LLDB.framework', 'Versions','Current','Headers','LLDB.h') + else: + header = os.path.join(os.environ["LLDB_SRC"], "include", "lldb", "API", "LLDB.h") + platform = sys.platform + if not os.path.exists(header): + self.skipTest("skip because LLDB.h header not found") + else: + func(*args, **kwargs) + return wrapper + +def skipIfiOSSimulator(func): + """Decorate the item to skip tests that should be skipped on the iOS Simulator.""" + return unittest2.skipIf(configuration.lldb_platform_name == 'ios-simulator', 'skip on the iOS Simulator')(func) + +def skipIfFreeBSD(func): + """Decorate the item to skip tests that should be skipped on FreeBSD.""" + return skipIfPlatform(["freebsd"])(func) + +def skipIfNetBSD(func): + """Decorate the item to skip tests that should be skipped on NetBSD.""" + return skipIfPlatform(["netbsd"])(func) + +def getDarwinOSTriples(): + return ['darwin', 'macosx', 'ios'] + +def skipIfDarwin(func): + """Decorate the item to skip tests that should be skipped on Darwin.""" + return skipIfPlatform(getDarwinOSTriples())(func) + +def skipIfLinux(func): + """Decorate the item to skip tests that should be skipped on Linux.""" + return skipIfPlatform(["linux"])(func) + +def skipUnlessHostLinux(func): + """Decorate the item to skip tests that should be skipped on any non Linux host.""" + return skipUnlessHostPlatform(["linux"])(func) + +def skipIfWindows(func): + """Decorate the item to skip tests that should be skipped on Windows.""" + return skipIfPlatform(["windows"])(func) + +def skipIfHostWindows(func): + """Decorate the item to skip tests that should be skipped on Windows.""" + return skipIfHostPlatform(["windows"])(func) + +def skipUnlessWindows(func): + """Decorate the item to skip tests that should be skipped on any non-Windows platform.""" + return skipUnlessPlatform(["windows"])(func) + +def skipUnlessDarwin(func): + """Decorate the item to skip tests that should be skipped on any non Darwin platform.""" + return skipUnlessPlatform(getDarwinOSTriples())(func) + +def skipUnlessGoInstalled(func): + """Decorate the item to skip tests when no Go compiler is available.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfGcc can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + compiler = self.getGoCompilerVersion() + if not compiler: + self.skipTest("skipping because go compiler not found") + else: + # Ensure the version is the minimum version supported by + # the LLDB go support. + match_version = re.search(r"(\d+\.\d+(\.\d+)?)", compiler) + if not match_version: + # Couldn't determine version. + self.skipTest( + "skipping because go version could not be parsed " + "out of {}".format(compiler)) + else: + from distutils.version import StrictVersion + min_strict_version = StrictVersion("1.4.0") + compiler_strict_version = StrictVersion(match_version.group(1)) + if compiler_strict_version < min_strict_version: + self.skipTest( + "skipping because available go version ({}) does " + "not meet minimum required go version ({})".format( + compiler_strict_version, + min_strict_version)) + func(*args, **kwargs) + return wrapper + +def getPlatform(): + """Returns the target platform which the tests are running on.""" + platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] + if platform.startswith('freebsd'): + platform = 'freebsd' + elif platform.startswith('netbsd'): + platform = 'netbsd' + return platform + +def getHostPlatform(): + """Returns the host platform running the test suite.""" + # Attempts to return a platform name matching a target Triple platform. + if sys.platform.startswith('linux'): + return 'linux' + elif sys.platform.startswith('win32'): + return 'windows' + elif sys.platform.startswith('darwin'): + return 'darwin' + elif sys.platform.startswith('freebsd'): + return 'freebsd' + elif sys.platform.startswith('netbsd'): + return 'netbsd' + else: + return sys.platform + +def platformIsDarwin(): + """Returns true if the OS triple for the selected platform is any valid apple OS""" + return getPlatform() in getDarwinOSTriples() + +def skipIfHostIncompatibleWithRemote(func): + """Decorate the item to skip tests if binaries built on this host are incompatible.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfHostIncompatibleWithRemote can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + host_arch = self.getLldbArchitecture() + host_platform = getHostPlatform() + target_arch = self.getArchitecture() + target_platform = 'darwin' if self.platformIsDarwin() else self.getPlatform() + if not (target_arch == 'x86_64' and host_arch == 'i386') and host_arch != target_arch: + self.skipTest("skipping because target %s is not compatible with host architecture %s" % (target_arch, host_arch)) + elif target_platform != host_platform: + self.skipTest("skipping because target is %s but host is %s" % (target_platform, host_platform)) + else: + func(*args, **kwargs) + return wrapper + +def skipIfHostPlatform(oslist): + """Decorate the item to skip tests if running on one of the listed host platforms.""" + return unittest2.skipIf(getHostPlatform() in oslist, + "skip on %s" % (", ".join(oslist))) + +def skipUnlessHostPlatform(oslist): + """Decorate the item to skip tests unless running on one of the listed host platforms.""" + return unittest2.skipUnless(getHostPlatform() in oslist, + "requires on of %s" % (", ".join(oslist))) + +def skipUnlessArch(archlist): + """Decorate the item to skip tests unless running on one of the listed architectures.""" + def myImpl(func): + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipUnlessArch can only be used to decorate a test method") + + @wraps(func) + def wrapper(*args, **kwargs): + self = args[0] + if self.getArchitecture() not in archlist: + self.skipTest("skipping for architecture %s (requires one of %s)" % + (self.getArchitecture(), ", ".join(archlist))) + else: + func(*args, **kwargs) + return wrapper + + return myImpl + +def skipIfPlatform(oslist): + """Decorate the item to skip tests if running on one of the listed platforms.""" + return unittest2.skipIf(getPlatform() in oslist, + "skip on %s" % (", ".join(oslist))) + +def skipUnlessPlatform(oslist): + """Decorate the item to skip tests unless running on one of the listed platforms.""" + return unittest2.skipUnless(getPlatform() in oslist, + "requires on of %s" % (", ".join(oslist))) + +def skipIfLinuxClang(func): + """Decorate the item to skip tests that should be skipped if building on + Linux with clang. + """ + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfLinuxClang can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + compiler = self.getCompiler() + platform = self.getPlatform() + if "clang" in compiler and platform == "linux": + self.skipTest("skipping because Clang is used on Linux") + else: + func(*args, **kwargs) + return wrapper + +# provide a function to skip on defined oslist, compiler version, and archs +# if none is specified for any argument, that argument won't be checked and thus means for all +# for example, +# @skipIf, skip for all platform/compiler/arch, +# @skipIf(compiler='gcc'), skip for gcc on all platform/architecture +# @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386 + +# TODO: refactor current code, to make skipIfxxx functions to call this function +def skipIf(bugnumber=None, oslist=None, compiler=None, compiler_version=None, archs=None, debug_info=None, swig_version=None, py_version=None, remote=None): + def fn(self): + oslist_passes = check_list_or_lambda(oslist, self.getPlatform()) + compiler_passes = check_list_or_lambda(self.getCompiler(), compiler) and self.expectedCompilerVersion(compiler_version) + arch_passes = check_list_or_lambda(archs, self.getArchitecture()) + debug_info_passes = check_list_or_lambda(debug_info, self.debug_info) + swig_version_passes = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version)) + py_version_passes = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info) + remote_passes = (remote is None) or (remote == (lldb.remote_platform is not None)) + + return (oslist_passes and + compiler_passes and + arch_passes and + debug_info_passes and + swig_version_passes and + py_version_passes and + remote_passes) + + local_vars = locals() + args = [x for x in inspect.getargspec(skipIf).args] + arg_vals = [eval(x, globals(), local_vars) for x in args] + args = [x for x in zip(args, arg_vals) if x[1] is not None] + reasons = ['%s=%s' % (x, str(y)) for (x,y) in args] + return skipTestIfFn(fn, bugnumber, skipReason='skipping because ' + ' && '.join(reasons)) + +def skipIfDebugInfo(bugnumber=None, debug_info=None): + return skipIf(bugnumber=bugnumber, debug_info=debug_info) + +def skipIfDWO(bugnumber=None): + return skipIfDebugInfo(bugnumber, ["dwo"]) + +def skipIfDwarf(bugnumber=None): + return skipIfDebugInfo(bugnumber, ["dwarf"]) + +def skipIfDsym(bugnumber=None): + return skipIfDebugInfo(bugnumber, ["dsym"]) + +def skipTestIfFn(expected_fn, bugnumber=None, skipReason=None): + def skipTestIfFn_impl(func): + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + if expected_fn(self): + self.skipTest(skipReason) + else: + func(*args, **kwargs) + return wrapper + if six.callable(bugnumber): + return skipTestIfFn_impl(bugnumber) + else: + return skipTestIfFn_impl + +def skipIfGcc(func): + """Decorate the item to skip tests that should be skipped if building with gcc .""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfGcc can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + compiler = self.getCompiler() + if "gcc" in compiler: + self.skipTest("skipping because gcc is the test compiler") + else: + func(*args, **kwargs) + return wrapper + +def skipIfIcc(func): + """Decorate the item to skip tests that should be skipped if building with icc .""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfIcc can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + compiler = self.getCompiler() + if "icc" in compiler: + self.skipTest("skipping because icc is the test compiler") + else: + func(*args, **kwargs) + return wrapper + +def skipIfi386(func): + """Decorate the item to skip tests that should be skipped if building 32-bit.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfi386 can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + if "i386" == self.getArchitecture(): + self.skipTest("skipping because i386 is not a supported architecture") + else: + func(*args, **kwargs) + return wrapper + +def skipIfTargetAndroid(api_levels=None, archs=None): + """Decorator to skip tests when the target is Android. + + Arguments: + api_levels - The API levels for which the test should be skipped. If + it is None, then the test will be skipped for all API levels. + arch - A sequence of architecture names specifying the architectures + for which a test is skipped. None means all architectures. + """ + def myImpl(func): + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipIfTargetAndroid can only be used to " + "decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + self = args[0] + if matchAndroid(api_levels, archs)(self): + self.skipTest("skiped on Android target with API %d and architecture %s" % + (android_device_api(), self.getArchitecture())) + func(*args, **kwargs) + return wrapper + return myImpl + +def skipUnlessCompilerRt(func): + """Decorate the item to skip tests if testing remotely.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@skipUnless can only be used to decorate a test method") + @wraps(func) + def wrapper(*args, **kwargs): + from unittest2 import case + import os.path + compilerRtPath = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "llvm","projects","compiler-rt") + print(compilerRtPath) + if not os.path.exists(compilerRtPath): + self = args[0] + self.skipTest("skip if compiler-rt not found") + else: + func(*args, **kwargs) + return wrapper + +class _PlatformContext(object): + """Value object class which contains platform-specific options.""" + + def __init__(self, shlib_environment_var, shlib_prefix, shlib_extension): + self.shlib_environment_var = shlib_environment_var + self.shlib_prefix = shlib_prefix + self.shlib_extension = shlib_extension + + +class Base(unittest2.TestCase): + """ + Abstract base for performing lldb (see TestBase) or other generic tests (see + BenchBase for one example). lldbtest.Base works with the test driver to + accomplish things. + + """ + + # The concrete subclass should override this attribute. + mydir = None + + # Keep track of the old current working directory. + oldcwd = None + + @staticmethod + def compute_mydir(test_file): + '''Subclasses should call this function to correctly calculate the required "mydir" attribute as follows: + + mydir = TestBase.compute_mydir(__file__)''' + test_dir = os.path.dirname(test_file) + return test_dir[len(os.environ["LLDB_TEST"])+1:] + + def TraceOn(self): + """Returns True if we are in trace mode (tracing detailed test execution).""" + return traceAlways + + @classmethod + def setUpClass(cls): + """ + Python unittest framework class setup fixture. + Do current directory manipulation. + """ + # Fail fast if 'mydir' attribute is not overridden. + if not cls.mydir or len(cls.mydir) == 0: + raise Exception("Subclasses must override the 'mydir' attribute.") + + # Save old working directory. + cls.oldcwd = os.getcwd() + + # Change current working directory if ${LLDB_TEST} is defined. + # See also dotest.py which sets up ${LLDB_TEST}. + if ("LLDB_TEST" in os.environ): + full_dir = os.path.join(os.environ["LLDB_TEST"], cls.mydir) + if traceAlways: + print("Change dir to:", full_dir, file=sys.stderr) + os.chdir(os.path.join(os.environ["LLDB_TEST"], cls.mydir)) + + if debug_confirm_directory_exclusivity: + import lock + cls.dir_lock = lock.Lock(os.path.join(full_dir, ".dirlock")) + try: + cls.dir_lock.try_acquire() + # write the class that owns the lock into the lock file + cls.dir_lock.handle.write(cls.__name__) + except IOError as ioerror: + # nothing else should have this directory lock + # wait here until we get a lock + cls.dir_lock.acquire() + # read the previous owner from the lock file + lock_id = cls.dir_lock.handle.read() + print("LOCK ERROR: {} wants to lock '{}' but it is already locked by '{}'".format(cls.__name__, full_dir, lock_id), file=sys.stderr) + raise ioerror + + # Set platform context. + if platformIsDarwin(): + cls.platformContext = _PlatformContext('DYLD_LIBRARY_PATH', 'lib', 'dylib') + elif getPlatform() in ("freebsd", "linux", "netbsd"): + cls.platformContext = _PlatformContext('LD_LIBRARY_PATH', 'lib', 'so') + else: + cls.platformContext = None + + @classmethod + def tearDownClass(cls): + """ + Python unittest framework class teardown fixture. + Do class-wide cleanup. + """ + + if doCleanup: + # First, let's do the platform-specific cleanup. + module = builder_module() + module.cleanup() + + # Subclass might have specific cleanup function defined. + if getattr(cls, "classCleanup", None): + if traceAlways: + print("Call class-specific cleanup function for class:", cls, file=sys.stderr) + try: + cls.classCleanup() + except: + exc_type, exc_value, exc_tb = sys.exc_info() + traceback.print_exception(exc_type, exc_value, exc_tb) + + if debug_confirm_directory_exclusivity: + cls.dir_lock.release() + del cls.dir_lock + + # Restore old working directory. + if traceAlways: + print("Restore dir to:", cls.oldcwd, file=sys.stderr) + os.chdir(cls.oldcwd) + + @classmethod + def skipLongRunningTest(cls): + """ + By default, we skip long running test case. + This can be overridden by passing '-l' to the test driver (dotest.py). + """ + if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ["LLDB_SKIP_LONG_RUNNING_TEST"]: + return False + else: + return True + + def enableLogChannelsForCurrentTest(self): + if len(lldbtest_config.channels) == 0: + return + + # if debug channels are specified in lldbtest_config.channels, + # create a new set of log files for every test + log_basename = self.getLogBasenameForCurrentTest() + + # confirm that the file is writeable + host_log_path = "{}-host.log".format(log_basename) + open(host_log_path, 'w').close() + + log_enable = "log enable -Tpn -f {} ".format(host_log_path) + for channel_with_categories in lldbtest_config.channels: + channel_then_categories = channel_with_categories.split(' ', 1) + channel = channel_then_categories[0] + if len(channel_then_categories) > 1: + categories = channel_then_categories[1] + else: + categories = "default" + + if channel == "gdb-remote": + # communicate gdb-remote categories to debugserver + os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories + + self.ci.HandleCommand(log_enable + channel_with_categories, self.res) + if not self.res.Succeeded(): + raise Exception('log enable failed (check LLDB_LOG_OPTION env variable)') + + # Communicate log path name to debugserver & lldb-server + server_log_path = "{}-server.log".format(log_basename) + open(server_log_path, 'w').close() + os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path + + # Communicate channels to lldb-server + os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join(lldbtest_config.channels) + + if len(lldbtest_config.channels) == 0: + return + + def disableLogChannelsForCurrentTest(self): + # close all log files that we opened + for channel_and_categories in lldbtest_config.channels: + # channel format - [ [ ...]] + channel = channel_and_categories.split(' ', 1)[0] + self.ci.HandleCommand("log disable " + channel, self.res) + if not self.res.Succeeded(): + raise Exception('log disable failed (check LLDB_LOG_OPTION env variable)') + + def setUp(self): + """Fixture for unittest test case setup. + + It works with the test driver to conditionally skip tests and does other + initializations.""" + #import traceback + #traceback.print_stack() + + if "LIBCXX_PATH" in os.environ: + self.libcxxPath = os.environ["LIBCXX_PATH"] + else: + self.libcxxPath = None + + if "LLDBMI_EXEC" in os.environ: + self.lldbMiExec = os.environ["LLDBMI_EXEC"] + else: + self.lldbMiExec = None + + # If we spawn an lldb process for test (via pexpect), do not load the + # init file unless told otherwise. + if "NO_LLDBINIT" in os.environ and "NO" == os.environ["NO_LLDBINIT"]: + self.lldbOption = "" + else: + self.lldbOption = "--no-lldbinit" + + # Assign the test method name to self.testMethodName. + # + # For an example of the use of this attribute, look at test/types dir. + # There are a bunch of test cases under test/types and we don't want the + # module cacheing subsystem to be confused with executable name "a.out" + # used for all the test cases. + self.testMethodName = self._testMethodName + + # This is for the case of directly spawning 'lldb'/'gdb' and interacting + # with it using pexpect. + self.child = None + self.child_prompt = "(lldb) " + # If the child is interacting with the embedded script interpreter, + # there are two exits required during tear down, first to quit the + # embedded script interpreter and second to quit the lldb command + # interpreter. + self.child_in_script_interpreter = False + + # These are for customized teardown cleanup. + self.dict = None + self.doTearDownCleanup = False + # And in rare cases where there are multiple teardown cleanups. + self.dicts = [] + self.doTearDownCleanups = False + + # List of spawned subproces.Popen objects + self.subprocesses = [] + + # List of forked process PIDs + self.forkedProcessPids = [] + + # Create a string buffer to record the session info, to be dumped into a + # test case specific file if test failure is encountered. + self.log_basename = self.getLogBasenameForCurrentTest() + + session_file = "{}.log".format(self.log_basename) + # Python 3 doesn't support unbuffered I/O in text mode. Open buffered. + self.session = open(session_file, "w") + + # Optimistically set __errored__, __failed__, __expected__ to False + # initially. If the test errored/failed, the session info + # (self.session) is then dumped into a session specific file for + # diagnosis. + self.__cleanup_errored__ = False + self.__errored__ = False + self.__failed__ = False + self.__expected__ = False + # We are also interested in unexpected success. + self.__unexpected__ = False + # And skipped tests. + self.__skipped__ = False + + # See addTearDownHook(self, hook) which allows the client to add a hook + # function to be run during tearDown() time. + self.hooks = [] + + # See HideStdout(self). + self.sys_stdout_hidden = False + + if self.platformContext: + # set environment variable names for finding shared libraries + self.dylibPath = self.platformContext.shlib_environment_var + + # Create the debugger instance if necessary. + try: + self.dbg = lldb.DBG + except AttributeError: + self.dbg = lldb.SBDebugger.Create() + + if not self.dbg: + raise Exception('Invalid debugger instance') + + # Retrieve the associated command interpreter instance. + self.ci = self.dbg.GetCommandInterpreter() + if not self.ci: + raise Exception('Could not get the command interpreter') + + # And the result object. + self.res = lldb.SBCommandReturnObject() + + self.enableLogChannelsForCurrentTest() + + #Initialize debug_info + self.debug_info = None + + def setAsync(self, value): + """ Sets async mode to True/False and ensures it is reset after the testcase completes.""" + old_async = self.dbg.GetAsync() + self.dbg.SetAsync(value) + self.addTearDownHook(lambda: self.dbg.SetAsync(old_async)) + + def cleanupSubprocesses(self): + # Ensure any subprocesses are cleaned up + for p in self.subprocesses: + p.terminate() + del p + del self.subprocesses[:] + # Ensure any forked processes are cleaned up + for pid in self.forkedProcessPids: + if os.path.exists("/proc/" + str(pid)): + os.kill(pid, signal.SIGTERM) + + def spawnSubprocess(self, executable, args=[], install_remote=True): + """ Creates a subprocess.Popen object with the specified executable and arguments, + saves it in self.subprocesses, and returns the object. + NOTE: if using this function, ensure you also call: + + self.addTearDownHook(self.cleanupSubprocesses) + + otherwise the test suite will leak processes. + """ + proc = _RemoteProcess(install_remote) if lldb.remote_platform else _LocalProcess(self.TraceOn()) + proc.launch(executable, args) + self.subprocesses.append(proc) + return proc + + def forkSubprocess(self, executable, args=[]): + """ Fork a subprocess with its own group ID. + NOTE: if using this function, ensure you also call: + + self.addTearDownHook(self.cleanupSubprocesses) + + otherwise the test suite will leak processes. + """ + child_pid = os.fork() + if child_pid == 0: + # If more I/O support is required, this can be beefed up. + fd = os.open(os.devnull, os.O_RDWR) + os.dup2(fd, 1) + os.dup2(fd, 2) + # This call causes the child to have its of group ID + os.setpgid(0,0) + os.execvp(executable, [executable] + args) + # Give the child time to get through the execvp() call + time.sleep(0.1) + self.forkedProcessPids.append(child_pid) + return child_pid + + def HideStdout(self): + """Hide output to stdout from the user. + + During test execution, there might be cases where we don't want to show the + standard output to the user. For example, + + self.runCmd(r'''sc print("\n\n\tHello!\n")''') + + tests whether command abbreviation for 'script' works or not. There is no + need to show the 'Hello' output to the user as long as the 'script' command + succeeds and we are not in TraceOn() mode (see the '-t' option). + + In this case, the test method calls self.HideStdout(self) to redirect the + sys.stdout to a null device, and restores the sys.stdout upon teardown. + + Note that you should only call this method at most once during a test case + execution. Any subsequent call has no effect at all.""" + if self.sys_stdout_hidden: + return + + self.sys_stdout_hidden = True + old_stdout = sys.stdout + sys.stdout = open(os.devnull, 'w') + def restore_stdout(): + sys.stdout = old_stdout + self.addTearDownHook(restore_stdout) + + # ======================================================================= + # Methods for customized teardown cleanups as well as execution of hooks. + # ======================================================================= + + def setTearDownCleanup(self, dictionary=None): + """Register a cleanup action at tearDown() time with a dictinary""" + self.dict = dictionary + self.doTearDownCleanup = True + + def addTearDownCleanup(self, dictionary): + """Add a cleanup action at tearDown() time with a dictinary""" + self.dicts.append(dictionary) + self.doTearDownCleanups = True + + def addTearDownHook(self, hook): + """ + Add a function to be run during tearDown() time. + + Hooks are executed in a first come first serve manner. + """ + if six.callable(hook): + with recording(self, traceAlways) as sbuf: + print("Adding tearDown hook:", getsource_if_available(hook), file=sbuf) + self.hooks.append(hook) + + return self + + def deletePexpectChild(self): + # This is for the case of directly spawning 'lldb' and interacting with it + # using pexpect. + if self.child and self.child.isalive(): + import pexpect + with recording(self, traceAlways) as sbuf: + print("tearing down the child process....", file=sbuf) + try: + if self.child_in_script_interpreter: + self.child.sendline('quit()') + self.child.expect_exact(self.child_prompt) + self.child.sendline('settings set interpreter.prompt-on-quit false') + self.child.sendline('quit') + self.child.expect(pexpect.EOF) + except (ValueError, pexpect.ExceptionPexpect): + # child is already terminated + pass + except OSError as exception: + import errno + if exception.errno != errno.EIO: + # unexpected error + raise + # child is already terminated + pass + finally: + # Give it one final blow to make sure the child is terminated. + self.child.close() + + def tearDown(self): + """Fixture for unittest test case teardown.""" + #import traceback + #traceback.print_stack() + + self.deletePexpectChild() + + # Check and run any hook functions. + for hook in reversed(self.hooks): + with recording(self, traceAlways) as sbuf: + print("Executing tearDown hook:", getsource_if_available(hook), file=sbuf) + import inspect + hook_argc = len(inspect.getargspec(hook).args) + if hook_argc == 0 or getattr(hook,'im_self',None): + hook() + elif hook_argc == 1: + hook(self) + else: + hook() # try the plain call and hope it works + + del self.hooks + + # Perform registered teardown cleanup. + if doCleanup and self.doTearDownCleanup: + self.cleanup(dictionary=self.dict) + + # In rare cases where there are multiple teardown cleanups added. + if doCleanup and self.doTearDownCleanups: + if self.dicts: + for dict in reversed(self.dicts): + self.cleanup(dictionary=dict) + + self.disableLogChannelsForCurrentTest() + + # ========================================================= + # Various callbacks to allow introspection of test progress + # ========================================================= + + def markError(self): + """Callback invoked when an error (unexpected exception) errored.""" + self.__errored__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "ERROR" to the stderr twice. + # Once by the Python unittest framework, and a second time by us. + print("ERROR", file=sbuf) + + def markCleanupError(self): + """Callback invoked when an error occurs while a test is cleaning up.""" + self.__cleanup_errored__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "CLEANUP_ERROR" to the stderr twice. + # Once by the Python unittest framework, and a second time by us. + print("CLEANUP_ERROR", file=sbuf) + + def markFailure(self): + """Callback invoked when a failure (test assertion failure) occurred.""" + self.__failed__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "FAIL" to the stderr twice. + # Once by the Python unittest framework, and a second time by us. + print("FAIL", file=sbuf) + + def markExpectedFailure(self,err,bugnumber): + """Callback invoked when an expected failure/error occurred.""" + self.__expected__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "expected failure" to the + # stderr twice. + # Once by the Python unittest framework, and a second time by us. + if bugnumber == None: + print("expected failure", file=sbuf) + else: + print("expected failure (problem id:" + str(bugnumber) + ")", file=sbuf) + + def markSkippedTest(self): + """Callback invoked when a test is skipped.""" + self.__skipped__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "skipped test" to the + # stderr twice. + # Once by the Python unittest framework, and a second time by us. + print("skipped test", file=sbuf) + + def markUnexpectedSuccess(self, bugnumber): + """Callback invoked when an unexpected success occurred.""" + self.__unexpected__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "unexpected success" to the + # stderr twice. + # Once by the Python unittest framework, and a second time by us. + if bugnumber == None: + print("unexpected success", file=sbuf) + else: + print("unexpected success (problem id:" + str(bugnumber) + ")", file=sbuf) + + def getRerunArgs(self): + return " -f %s.%s" % (self.__class__.__name__, self._testMethodName) + + def getLogBasenameForCurrentTest(self, prefix=None): + """ + returns a partial path that can be used as the beginning of the name of multiple + log files pertaining to this test + + /--.. + """ + dname = os.path.join(os.environ["LLDB_TEST"], + os.environ["LLDB_SESSION_DIRNAME"]) + if not os.path.isdir(dname): + os.mkdir(dname) + + compiler = self.getCompiler() + + if compiler[1] == ':': + compiler = compiler[2:] + if os.path.altsep is not None: + compiler = compiler.replace(os.path.altsep, os.path.sep) + + fname = "{}-{}-{}".format(self.id(), self.getArchitecture(), "_".join(compiler.split(os.path.sep))) + if len(fname) > 200: + fname = "{}-{}-{}".format(self.id(), self.getArchitecture(), compiler.split(os.path.sep)[-1]) + + if prefix is not None: + fname = "{}-{}".format(prefix, fname) + + return os.path.join(dname, fname) + + def dumpSessionInfo(self): + """ + Dump the debugger interactions leading to a test error/failure. This + allows for more convenient postmortem analysis. + + See also LLDBTestResult (dotest.py) which is a singlton class derived + from TextTestResult and overwrites addError, addFailure, and + addExpectedFailure methods to allow us to to mark the test instance as + such. + """ + + # We are here because self.tearDown() detected that this test instance + # either errored or failed. The lldb.test_result singleton contains + # two lists (erros and failures) which get populated by the unittest + # framework. Look over there for stack trace information. + # + # The lists contain 2-tuples of TestCase instances and strings holding + # formatted tracebacks. + # + # See http://docs.python.org/library/unittest.html#unittest.TestResult. + + # output tracebacks into session + pairs = [] + if self.__errored__: + pairs = configuration.test_result.errors + prefix = 'Error' + elif self.__cleanup_errored__: + pairs = configuration.test_result.cleanup_errors + prefix = 'CleanupError' + elif self.__failed__: + pairs = configuration.test_result.failures + prefix = 'Failure' + elif self.__expected__: + pairs = configuration.test_result.expectedFailures + prefix = 'ExpectedFailure' + elif self.__skipped__: + prefix = 'SkippedTest' + elif self.__unexpected__: + prefix = 'UnexpectedSuccess' + else: + prefix = 'Success' + + if not self.__unexpected__ and not self.__skipped__: + for test, traceback in pairs: + if test is self: + print(traceback, file=self.session) + + # put footer (timestamp/rerun instructions) into session + testMethod = getattr(self, self._testMethodName) + if getattr(testMethod, "__benchmarks_test__", False): + benchmarks = True + else: + benchmarks = False + + import datetime + print("Session info generated @", datetime.datetime.now().ctime(), file=self.session) + print("To rerun this test, issue the following command from the 'test' directory:\n", file=self.session) + print("./dotest.py %s -v %s %s" % (self.getRunOptions(), + ('+b' if benchmarks else '-t'), + self.getRerunArgs()), file=self.session) + self.session.close() + del self.session + + # process the log files + log_files_for_this_test = glob.glob(self.log_basename + "*") + + if prefix != 'Success' or lldbtest_config.log_success: + # keep all log files, rename them to include prefix + dst_log_basename = self.getLogBasenameForCurrentTest(prefix) + for src in log_files_for_this_test: + if os.path.isfile(src): + dst = src.replace(self.log_basename, dst_log_basename) + if os.name == "nt" and os.path.isfile(dst): + # On Windows, renaming a -> b will throw an exception if b exists. On non-Windows platforms + # it silently replaces the destination. Ultimately this means that atomic renames are not + # guaranteed to be possible on Windows, but we need this to work anyway, so just remove the + # destination first if it already exists. + os.remove(dst) + + os.rename(src, dst) + else: + # success! (and we don't want log files) delete log files + for log_file in log_files_for_this_test: + try: + os.unlink(log_file) + except: + # We've seen consistent unlink failures on Windows, perhaps because the + # just-created log file is being scanned by anti-virus. Empirically, this + # sleep-and-retry approach allows tests to succeed much more reliably. + # Attempts to figure out exactly what process was still holding a file handle + # have failed because running instrumentation like Process Monitor seems to + # slow things down enough that the problem becomes much less consistent. + time.sleep(0.5) + os.unlink(log_file) + + # ==================================================== + # Config. methods supported through a plugin interface + # (enables reading of the current test configuration) + # ==================================================== + + def getArchitecture(self): + """Returns the architecture in effect the test suite is running with.""" + module = builder_module() + arch = module.getArchitecture() + if arch == 'amd64': + arch = 'x86_64' + return arch + + def getLldbArchitecture(self): + """Returns the architecture of the lldb binary.""" + if not hasattr(self, 'lldbArchitecture'): + + # spawn local process + command = [ + lldbtest_config.lldbExec, + "-o", + "file " + lldbtest_config.lldbExec, + "-o", + "quit" + ] + + output = check_output(command) + str = output.decode("utf-8"); + + for line in str.splitlines(): + m = re.search("Current executable set to '.*' \\((.*)\\)\\.", line) + if m: + self.lldbArchitecture = m.group(1) + break + + return self.lldbArchitecture + + def getCompiler(self): + """Returns the compiler in effect the test suite is running with.""" + module = builder_module() + return module.getCompiler() + + def getCompilerBinary(self): + """Returns the compiler binary the test suite is running with.""" + return self.getCompiler().split()[0] + + def getCompilerVersion(self): + """ Returns a string that represents the compiler version. + Supports: llvm, clang. + """ + from .lldbutil import which + version = 'unknown' + + compiler = self.getCompilerBinary() + version_output = system([[which(compiler), "-v"]])[1] + for line in version_output.split(os.linesep): + m = re.search('version ([0-9\.]+)', line) + if m: + version = m.group(1) + return version + + def getGoCompilerVersion(self): + """ Returns a string that represents the go compiler version, or None if go is not found. + """ + compiler = which("go") + if compiler: + version_output = system([[compiler, "version"]])[0] + for line in version_output.split(os.linesep): + m = re.search('go version (devel|go\\S+)', line) + if m: + return m.group(1) + return None + + def platformIsDarwin(self): + """Returns true if the OS triple for the selected platform is any valid apple OS""" + return platformIsDarwin() + + def getPlatform(self): + """Returns the target platform the test suite is running on.""" + return getPlatform() + + def isIntelCompiler(self): + """ Returns true if using an Intel (ICC) compiler, false otherwise. """ + return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]]) + + def expectedCompilerVersion(self, compiler_version): + """Returns True iff compiler_version[1] matches the current compiler version. + Use compiler_version[0] to specify the operator used to determine if a match has occurred. + Any operator other than the following defaults to an equality test: + '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' + """ + if (compiler_version == None): + return True + operator = str(compiler_version[0]) + version = compiler_version[1] + + if (version == None): + return True + if (operator == '>'): + return self.getCompilerVersion() > version + if (operator == '>=' or operator == '=>'): + return self.getCompilerVersion() >= version + if (operator == '<'): + return self.getCompilerVersion() < version + if (operator == '<=' or operator == '=<'): + return self.getCompilerVersion() <= version + if (operator == '!=' or operator == '!' or operator == 'not'): + return str(version) not in str(self.getCompilerVersion()) + return str(version) in str(self.getCompilerVersion()) + + def expectedCompiler(self, compilers): + """Returns True iff any element of compilers is a sub-string of the current compiler.""" + if (compilers == None): + return True + + for compiler in compilers: + if compiler in self.getCompiler(): + return True + + return False + + def expectedArch(self, archs): + """Returns True iff any element of archs is a sub-string of the current architecture.""" + if (archs == None): + return True + + for arch in archs: + if arch in self.getArchitecture(): + return True + + return False + + def getRunOptions(self): + """Command line option for -A and -C to run this test again, called from + self.dumpSessionInfo().""" + arch = self.getArchitecture() + comp = self.getCompiler() + if arch: + option_str = "-A " + arch + else: + option_str = "" + if comp: + option_str += " -C " + comp + return option_str + + # ================================================== + # Build methods supported through a plugin interface + # ================================================== + + def getstdlibFlag(self): + """ Returns the proper -stdlib flag, or empty if not required.""" + if self.platformIsDarwin() or self.getPlatform() == "freebsd": + stdlibflag = "-stdlib=libc++" + else: # this includes NetBSD + stdlibflag = "" + return stdlibflag + + def getstdFlag(self): + """ Returns the proper stdflag. """ + if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): + stdflag = "-std=c++0x" + else: + stdflag = "-std=c++11" + return stdflag + + def buildDriver(self, sources, exe_name): + """ Platform-specific way to build a program that links with LLDB (via the liblldb.so + or LLDB.framework). + """ + + stdflag = self.getstdFlag() + stdlibflag = self.getstdlibFlag() + + lib_dir = os.environ["LLDB_LIB_DIR"] + if sys.platform.startswith("darwin"): + dsym = os.path.join(lib_dir, 'LLDB.framework', 'LLDB') + d = {'CXX_SOURCES' : sources, + 'EXE' : exe_name, + 'CFLAGS_EXTRAS' : "%s %s" % (stdflag, stdlibflag), + 'FRAMEWORK_INCLUDES' : "-F%s" % lib_dir, + 'LD_EXTRAS' : "%s -Wl,-rpath,%s" % (dsym, lib_dir), + } + elif sys.platform.rstrip('0123456789') in ('freebsd', 'linux', 'netbsd') or os.environ.get('LLDB_BUILD_TYPE') == 'Makefile': + d = {'CXX_SOURCES' : sources, + 'EXE' : exe_name, + 'CFLAGS_EXTRAS' : "%s %s -I%s" % (stdflag, stdlibflag, os.path.join(os.environ["LLDB_SRC"], "include")), + 'LD_EXTRAS' : "-L%s -llldb" % lib_dir} + elif sys.platform.startswith('win'): + d = {'CXX_SOURCES' : sources, + 'EXE' : exe_name, + 'CFLAGS_EXTRAS' : "%s %s -I%s" % (stdflag, stdlibflag, os.path.join(os.environ["LLDB_SRC"], "include")), + 'LD_EXTRAS' : "-L%s -lliblldb" % os.environ["LLDB_IMPLIB_DIR"]} + if self.TraceOn(): + print("Building LLDB Driver (%s) from sources %s" % (exe_name, sources)) + + self.buildDefault(dictionary=d) + + def buildLibrary(self, sources, lib_name): + """Platform specific way to build a default library. """ + + stdflag = self.getstdFlag() + + lib_dir = os.environ["LLDB_LIB_DIR"] + if self.platformIsDarwin(): + dsym = os.path.join(lib_dir, 'LLDB.framework', 'LLDB') + d = {'DYLIB_CXX_SOURCES' : sources, + 'DYLIB_NAME' : lib_name, + 'CFLAGS_EXTRAS' : "%s -stdlib=libc++" % stdflag, + 'FRAMEWORK_INCLUDES' : "-F%s" % lib_dir, + 'LD_EXTRAS' : "%s -Wl,-rpath,%s -dynamiclib" % (dsym, lib_dir), + } + elif self.getPlatform() in ('freebsd', 'linux', 'netbsd') or os.environ.get('LLDB_BUILD_TYPE') == 'Makefile': + d = {'DYLIB_CXX_SOURCES' : sources, + 'DYLIB_NAME' : lib_name, + 'CFLAGS_EXTRAS' : "%s -I%s -fPIC" % (stdflag, os.path.join(os.environ["LLDB_SRC"], "include")), + 'LD_EXTRAS' : "-shared -L%s -llldb" % lib_dir} + elif self.getPlatform() == 'windows': + d = {'DYLIB_CXX_SOURCES' : sources, + 'DYLIB_NAME' : lib_name, + 'CFLAGS_EXTRAS' : "%s -I%s -fPIC" % (stdflag, os.path.join(os.environ["LLDB_SRC"], "include")), + 'LD_EXTRAS' : "-shared -l%s\liblldb.lib" % self.os.environ["LLDB_IMPLIB_DIR"]} + if self.TraceOn(): + print("Building LLDB Library (%s) from sources %s" % (lib_name, sources)) + + self.buildDefault(dictionary=d) + + def buildProgram(self, sources, exe_name): + """ Platform specific way to build an executable from C/C++ sources. """ + d = {'CXX_SOURCES' : sources, + 'EXE' : exe_name} + self.buildDefault(dictionary=d) + + def buildDefault(self, architecture=None, compiler=None, dictionary=None, clean=True): + """Platform specific way to build the default binaries.""" + module = builder_module() + if target_is_android(): + dictionary = append_android_envs(dictionary) + if not module.buildDefault(self, architecture, compiler, dictionary, clean): + raise Exception("Don't know how to build default binary") + + def buildDsym(self, architecture=None, compiler=None, dictionary=None, clean=True): + """Platform specific way to build binaries with dsym info.""" + module = builder_module() + if not module.buildDsym(self, architecture, compiler, dictionary, clean): + raise Exception("Don't know how to build binary with dsym") + + def buildDwarf(self, architecture=None, compiler=None, dictionary=None, clean=True): + """Platform specific way to build binaries with dwarf maps.""" + module = builder_module() + if target_is_android(): + dictionary = append_android_envs(dictionary) + if not module.buildDwarf(self, architecture, compiler, dictionary, clean): + raise Exception("Don't know how to build binary with dwarf") + + def buildDwo(self, architecture=None, compiler=None, dictionary=None, clean=True): + """Platform specific way to build binaries with dwarf maps.""" + module = builder_module() + if target_is_android(): + dictionary = append_android_envs(dictionary) + if not module.buildDwo(self, architecture, compiler, dictionary, clean): + raise Exception("Don't know how to build binary with dwo") + + def buildGo(self): + """Build the default go binary. + """ + system([[which('go'), 'build -gcflags "-N -l" -o a.out main.go']]) + + def signBinary(self, binary_path): + if sys.platform.startswith("darwin"): + codesign_cmd = "codesign --force --sign lldb_codesign %s" % (binary_path) + call(codesign_cmd, shell=True) + + def findBuiltClang(self): + """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler).""" + paths_to_try = [ + "llvm-build/Release+Asserts/x86_64/Release+Asserts/bin/clang", + "llvm-build/Debug+Asserts/x86_64/Debug+Asserts/bin/clang", + "llvm-build/Release/x86_64/Release/bin/clang", + "llvm-build/Debug/x86_64/Debug/bin/clang", + ] + lldb_root_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..") + for p in paths_to_try: + path = os.path.join(lldb_root_path, p) + if os.path.exists(path): + return path + + # Tries to find clang at the same folder as the lldb + path = os.path.join(os.path.dirname(lldbtest_config.lldbExec), "clang") + if os.path.exists(path): + return path + + return os.environ["CC"] + + def getBuildFlags(self, use_cpp11=True, use_libcxx=False, use_libstdcxx=False): + """ Returns a dictionary (which can be provided to build* functions above) which + contains OS-specific build flags. + """ + cflags = "" + ldflags = "" + + # On Mac OS X, unless specifically requested to use libstdc++, use libc++ + if not use_libstdcxx and self.platformIsDarwin(): + use_libcxx = True + + if use_libcxx and self.libcxxPath: + cflags += "-stdlib=libc++ " + if self.libcxxPath: + libcxxInclude = os.path.join(self.libcxxPath, "include") + libcxxLib = os.path.join(self.libcxxPath, "lib") + if os.path.isdir(libcxxInclude) and os.path.isdir(libcxxLib): + cflags += "-nostdinc++ -I%s -L%s -Wl,-rpath,%s " % (libcxxInclude, libcxxLib, libcxxLib) + + if use_cpp11: + cflags += "-std=" + if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): + cflags += "c++0x" + else: + cflags += "c++11" + if self.platformIsDarwin() or self.getPlatform() == "freebsd": + cflags += " -stdlib=libc++" + elif self.getPlatform() == "netbsd": + cflags += " -stdlib=libstdc++" + elif "clang" in self.getCompiler(): + cflags += " -stdlib=libstdc++" + + return {'CFLAGS_EXTRAS' : cflags, + 'LD_EXTRAS' : ldflags, + } + + def cleanup(self, dictionary=None): + """Platform specific way to do cleanup after build.""" + module = builder_module() + if not module.cleanup(self, dictionary): + raise Exception("Don't know how to do cleanup with dictionary: "+dictionary) + + def getLLDBLibraryEnvVal(self): + """ Returns the path that the OS-specific library search environment variable + (self.dylibPath) should be set to in order for a program to find the LLDB + library. If an environment variable named self.dylibPath is already set, + the new path is appended to it and returned. + """ + existing_library_path = os.environ[self.dylibPath] if self.dylibPath in os.environ else None + lib_dir = os.environ["LLDB_LIB_DIR"] + if existing_library_path: + return "%s:%s" % (existing_library_path, lib_dir) + elif sys.platform.startswith("darwin"): + return os.path.join(lib_dir, 'LLDB.framework') + else: + return lib_dir + + def getLibcPlusPlusLibs(self): + if self.getPlatform() in ('freebsd', 'linux', 'netbsd'): + return ['libc++.so.1'] + else: + return ['libc++.1.dylib','libc++abi.dylib'] + +# Metaclass for TestBase to change the list of test metods when a new TestCase is loaded. +# We change the test methods to create a new test method for each test for each debug info we are +# testing. The name of the new test method will be '_' and with adding +# the new test method we remove the old method at the same time. +class LLDBTestCaseFactory(type): + def __new__(cls, name, bases, attrs): + newattrs = {} + for attrname, attrvalue in attrs.items(): + if attrname.startswith("test") and not getattr(attrvalue, "__no_debug_info_test__", False): + target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] + + # If any debug info categories were explicitly tagged, assume that list to be + # authoritative. If none were specified, try with all debug info formats. + all_dbginfo_categories = set(test_categories.debug_info_categories) + categories = set(getattr(attrvalue, "categories", [])) & all_dbginfo_categories + if not categories: + categories = all_dbginfo_categories + + supported_categories = [x for x in categories + if test_categories.is_supported_on_platform(x, target_platform)] + if "dsym" in supported_categories: + @add_test_categories(["dsym"]) + @wraps(attrvalue) + def dsym_test_method(self, attrvalue=attrvalue): + self.debug_info = "dsym" + return attrvalue(self) + dsym_method_name = attrname + "_dsym" + dsym_test_method.__name__ = dsym_method_name + newattrs[dsym_method_name] = dsym_test_method + + if "dwarf" in supported_categories: + @add_test_categories(["dwarf"]) + @wraps(attrvalue) + def dwarf_test_method(self, attrvalue=attrvalue): + self.debug_info = "dwarf" + return attrvalue(self) + dwarf_method_name = attrname + "_dwarf" + dwarf_test_method.__name__ = dwarf_method_name + newattrs[dwarf_method_name] = dwarf_test_method + + if "dwo" in supported_categories: + @add_test_categories(["dwo"]) + @wraps(attrvalue) + def dwo_test_method(self, attrvalue=attrvalue): + self.debug_info = "dwo" + return attrvalue(self) + dwo_method_name = attrname + "_dwo" + dwo_test_method.__name__ = dwo_method_name + newattrs[dwo_method_name] = dwo_test_method + else: + newattrs[attrname] = attrvalue + return super(LLDBTestCaseFactory, cls).__new__(cls, name, bases, newattrs) + +# Setup the metaclass for this class to change the list of the test methods when a new class is loaded +@add_metaclass(LLDBTestCaseFactory) +class TestBase(Base): + """ + This abstract base class is meant to be subclassed. It provides default + implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), + among other things. + + Important things for test class writers: + + - Overwrite the mydir class attribute, otherwise your test class won't + run. It specifies the relative directory to the top level 'test' so + the test harness can change to the correct working directory before + running your test. + + - The setUp method sets up things to facilitate subsequent interactions + with the debugger as part of the test. These include: + - populate the test method name + - create/get a debugger set with synchronous mode (self.dbg) + - get the command interpreter from with the debugger (self.ci) + - create a result object for use with the command interpreter + (self.res) + - plus other stuffs + + - The tearDown method tries to perform some necessary cleanup on behalf + of the test to return the debugger to a good state for the next test. + These include: + - execute any tearDown hooks registered by the test method with + TestBase.addTearDownHook(); examples can be found in + settings/TestSettings.py + - kill the inferior process associated with each target, if any, + and, then delete the target from the debugger's target list + - perform build cleanup before running the next test method in the + same test class; examples of registering for this service can be + found in types/TestIntegerTypes.py with the call: + - self.setTearDownCleanup(dictionary=d) + + - Similarly setUpClass and tearDownClass perform classwise setup and + teardown fixtures. The tearDownClass method invokes a default build + cleanup for the entire test class; also, subclasses can implement the + classmethod classCleanup(cls) to perform special class cleanup action. + + - The instance methods runCmd and expect are used heavily by existing + test cases to send a command to the command interpreter and to perform + string/pattern matching on the output of such command execution. The + expect method also provides a mode to peform string/pattern matching + without running a command. + + - The build methods buildDefault, buildDsym, and buildDwarf are used to + build the binaries used during a particular test scenario. A plugin + should be provided for the sys.platform running the test suite. The + Mac OS X implementation is located in plugins/darwin.py. + """ + + # Maximum allowed attempts when launching the inferior process. + # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. + maxLaunchCount = 3; + + # Time to wait before the next launching attempt in second(s). + # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. + timeWaitNextLaunch = 1.0; + + # Returns the list of categories to which this test case belongs + # by default, look for a ".categories" file, and read its contents + # if no such file exists, traverse the hierarchy - we guarantee + # a .categories to exist at the top level directory so we do not end up + # looping endlessly - subclasses are free to define their own categories + # in whatever way makes sense to them + def getCategories(self): + import inspect + import os.path + folder = inspect.getfile(self.__class__) + folder = os.path.dirname(folder) + while folder != '/': + categories_file_name = os.path.join(folder,".categories") + if os.path.exists(categories_file_name): + categories_file = open(categories_file_name,'r') + categories = categories_file.readline() + categories_file.close() + categories = str.replace(categories,'\n','') + categories = str.replace(categories,'\r','') + return categories.split(',') + else: + folder = os.path.dirname(folder) + continue + + def setUp(self): + #import traceback + #traceback.print_stack() + + # Works with the test driver to conditionally skip tests via decorators. + Base.setUp(self) + + if "LLDB_MAX_LAUNCH_COUNT" in os.environ: + self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) + + if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: + self.timeWaitNextLaunch = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) + + # We want our debugger to be synchronous. + self.dbg.SetAsync(False) + + # Retrieve the associated command interpreter instance. + self.ci = self.dbg.GetCommandInterpreter() + if not self.ci: + raise Exception('Could not get the command interpreter') + + # And the result object. + self.res = lldb.SBCommandReturnObject() + + if lldb.remote_platform and configuration.lldb_platform_working_dir: + remote_test_dir = lldbutil.join_remote_paths( + configuration.lldb_platform_working_dir, + self.getArchitecture(), + str(self.test_number), + self.mydir) + error = lldb.remote_platform.MakeDirectory(remote_test_dir, 448) # 448 = 0o700 + if error.Success(): + lldb.remote_platform.SetWorkingDirectory(remote_test_dir) + + # This function removes all files from the current working directory while leaving + # the directories in place. The cleaup is required to reduce the disk space required + # by the test suit while leaving the directories untached is neccessary because + # sub-directories might belong to an other test + def clean_working_directory(): + # TODO: Make it working on Windows when we need it for remote debugging support + # TODO: Replace the heuristic to remove the files with a logic what collects the + # list of files we have to remove during test runs. + shell_cmd = lldb.SBPlatformShellCommand("rm %s/*" % remote_test_dir) + lldb.remote_platform.Run(shell_cmd) + self.addTearDownHook(clean_working_directory) + else: + print("error: making remote directory '%s': %s" % (remote_test_dir, error)) + + def registerSharedLibrariesWithTarget(self, target, shlibs): + '''If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing + + Any modules in the target that have their remote install file specification set will + get uploaded to the remote host. This function registers the local copies of the + shared libraries with the target and sets their remote install locations so they will + be uploaded when the target is run. + ''' + if not shlibs or not self.platformContext: + return None + + shlib_environment_var = self.platformContext.shlib_environment_var + shlib_prefix = self.platformContext.shlib_prefix + shlib_extension = '.' + self.platformContext.shlib_extension + + working_dir = self.get_process_working_directory() + environment = ['%s=%s' % (shlib_environment_var, working_dir)] + # Add any shared libraries to our target if remote so they get + # uploaded into the working directory on the remote side + for name in shlibs: + # The path can be a full path to a shared library, or a make file name like "Foo" for + # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a + # basename like "libFoo.so". So figure out which one it is and resolve the local copy + # of the shared library accordingly + if os.path.exists(name): + local_shlib_path = name # name is the full path to the local shared library + else: + # Check relative names + local_shlib_path = os.path.join(os.getcwd(), shlib_prefix + name + shlib_extension) + if not os.path.exists(local_shlib_path): + local_shlib_path = os.path.join(os.getcwd(), name + shlib_extension) + if not os.path.exists(local_shlib_path): + local_shlib_path = os.path.join(os.getcwd(), name) + + # Make sure we found the local shared library in the above code + self.assertTrue(os.path.exists(local_shlib_path)) + + # Add the shared library to our target + shlib_module = target.AddModule(local_shlib_path, None, None, None) + if lldb.remote_platform: + # We must set the remote install location if we want the shared library + # to get uploaded to the remote target + remote_shlib_path = lldbutil.append_to_process_working_directory(os.path.basename(local_shlib_path)) + shlib_module.SetRemoteInstallFileSpec(lldb.SBFileSpec(remote_shlib_path, False)) + + return environment + + # utility methods that tests can use to access the current objects + def target(self): + if not self.dbg: + raise Exception('Invalid debugger instance') + return self.dbg.GetSelectedTarget() + + def process(self): + if not self.dbg: + raise Exception('Invalid debugger instance') + return self.dbg.GetSelectedTarget().GetProcess() + + def thread(self): + if not self.dbg: + raise Exception('Invalid debugger instance') + return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread() + + def frame(self): + if not self.dbg: + raise Exception('Invalid debugger instance') + return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + + def get_process_working_directory(self): + '''Get the working directory that should be used when launching processes for local or remote processes.''' + if lldb.remote_platform: + # Remote tests set the platform working directory up in TestBase.setUp() + return lldb.remote_platform.GetWorkingDirectory() + else: + # local tests change directory into each test subdirectory + return os.getcwd() + + def tearDown(self): + #import traceback + #traceback.print_stack() + + # Ensure all the references to SB objects have gone away so that we can + # be sure that all test-specific resources have been freed before we + # attempt to delete the targets. + gc.collect() + + # Delete the target(s) from the debugger as a general cleanup step. + # This includes terminating the process for each target, if any. + # We'd like to reuse the debugger for our next test without incurring + # the initialization overhead. + targets = [] + for target in self.dbg: + if target: + targets.append(target) + process = target.GetProcess() + if process: + rc = self.invoke(process, "Kill") + self.assertTrue(rc.Success(), PROCESS_KILLED) + for target in targets: + self.dbg.DeleteTarget(target) + + # Do this last, to make sure it's in reverse order from how we setup. + Base.tearDown(self) + + # This must be the last statement, otherwise teardown hooks or other + # lines might depend on this still being active. + del self.dbg + + def switch_to_thread_with_stop_reason(self, stop_reason): + """ + Run the 'thread list' command, and select the thread with stop reason as + 'stop_reason'. If no such thread exists, no select action is done. + """ + from .lldbutil import stop_reason_to_str + self.runCmd('thread list') + output = self.res.GetOutput() + thread_line_pattern = re.compile("^[ *] thread #([0-9]+):.*stop reason = %s" % + stop_reason_to_str(stop_reason)) + for line in output.splitlines(): + matched = thread_line_pattern.match(line) + if matched: + self.runCmd('thread select %s' % matched.group(1)) + + def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False): + """ + Ask the command interpreter to handle the command and then check its + return status. + """ + # Fail fast if 'cmd' is not meaningful. + if not cmd or len(cmd) == 0: + raise Exception("Bad 'cmd' parameter encountered") + + trace = (True if traceAlways else trace) + + if cmd.startswith("target create "): + cmd = cmd.replace("target create ", "file ") + + running = (cmd.startswith("run") or cmd.startswith("process launch")) + + for i in range(self.maxLaunchCount if running else 1): + self.ci.HandleCommand(cmd, self.res, inHistory) + + with recording(self, trace) as sbuf: + print("runCmd:", cmd, file=sbuf) + if not check: + print("check of return status not required", file=sbuf) + if self.res.Succeeded(): + print("output:", self.res.GetOutput(), file=sbuf) + else: + print("runCmd failed!", file=sbuf) + print(self.res.GetError(), file=sbuf) + + if self.res.Succeeded(): + break + elif running: + # For process launch, wait some time before possible next try. + time.sleep(self.timeWaitNextLaunch) + with recording(self, trace) as sbuf: + print("Command '" + cmd + "' failed!", file=sbuf) + + if check: + self.assertTrue(self.res.Succeeded(), + msg if msg else CMD_MSG(cmd)) + + def match (self, str, patterns, msg=None, trace=False, error=False, matching=True, exe=True): + """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern + + Otherwise, all the arguments have the same meanings as for the expect function""" + + trace = (True if traceAlways else trace) + + if exe: + # First run the command. If we are expecting error, set check=False. + # Pass the assert message along since it provides more semantic info. + self.runCmd(str, msg=msg, trace = (True if trace else False), check = not error) + + # Then compare the output against expected strings. + output = self.res.GetError() if error else self.res.GetOutput() + + # If error is True, the API client expects the command to fail! + if error: + self.assertFalse(self.res.Succeeded(), + "Command '" + str + "' is expected to fail!") + else: + # No execution required, just compare str against the golden input. + output = str + with recording(self, trace) as sbuf: + print("looking at:", output, file=sbuf) + + # The heading says either "Expecting" or "Not expecting". + heading = "Expecting" if matching else "Not expecting" + + for pattern in patterns: + # Match Objects always have a boolean value of True. + match_object = re.search(pattern, output) + matched = bool(match_object) + with recording(self, trace) as sbuf: + print("%s pattern: %s" % (heading, pattern), file=sbuf) + print("Matched" if matched else "Not matched", file=sbuf) + if matched: + break + + self.assertTrue(matched if matching else not matched, + msg if msg else EXP_MSG(str, exe)) + + return match_object + + def expect(self, str, msg=None, patterns=None, startstr=None, endstr=None, substrs=None, trace=False, error=False, matching=True, exe=True, inHistory=False): + """ + Similar to runCmd; with additional expect style output matching ability. + + Ask the command interpreter to handle the command and then check its + return status. The 'msg' parameter specifies an informational assert + message. We expect the output from running the command to start with + 'startstr', matches the substrings contained in 'substrs', and regexp + matches the patterns contained in 'patterns'. + + If the keyword argument error is set to True, it signifies that the API + client is expecting the command to fail. In this case, the error stream + from running the command is retrieved and compared against the golden + input, instead. + + If the keyword argument matching is set to False, it signifies that the API + client is expecting the output of the command not to match the golden + input. + + Finally, the required argument 'str' represents the lldb command to be + sent to the command interpreter. In case the keyword argument 'exe' is + set to False, the 'str' is treated as a string to be matched/not-matched + against the golden input. + """ + trace = (True if traceAlways else trace) + + if exe: + # First run the command. If we are expecting error, set check=False. + # Pass the assert message along since it provides more semantic info. + self.runCmd(str, msg=msg, trace = (True if trace else False), check = not error, inHistory=inHistory) + + # Then compare the output against expected strings. + output = self.res.GetError() if error else self.res.GetOutput() + + # If error is True, the API client expects the command to fail! + if error: + self.assertFalse(self.res.Succeeded(), + "Command '" + str + "' is expected to fail!") + else: + # No execution required, just compare str against the golden input. + if isinstance(str,lldb.SBCommandReturnObject): + output = str.GetOutput() + else: + output = str + with recording(self, trace) as sbuf: + print("looking at:", output, file=sbuf) + + # The heading says either "Expecting" or "Not expecting". + heading = "Expecting" if matching else "Not expecting" + + # Start from the startstr, if specified. + # If there's no startstr, set the initial state appropriately. + matched = output.startswith(startstr) if startstr else (True if matching else False) + + if startstr: + with recording(self, trace) as sbuf: + print("%s start string: %s" % (heading, startstr), file=sbuf) + print("Matched" if matched else "Not matched", file=sbuf) + + # Look for endstr, if specified. + keepgoing = matched if matching else not matched + if endstr: + matched = output.endswith(endstr) + with recording(self, trace) as sbuf: + print("%s end string: %s" % (heading, endstr), file=sbuf) + print("Matched" if matched else "Not matched", file=sbuf) + + # Look for sub strings, if specified. + keepgoing = matched if matching else not matched + if substrs and keepgoing: + for str in substrs: + matched = output.find(str) != -1 + with recording(self, trace) as sbuf: + print("%s sub string: %s" % (heading, str), file=sbuf) + print("Matched" if matched else "Not matched", file=sbuf) + keepgoing = matched if matching else not matched + if not keepgoing: + break + + # Search for regular expression patterns, if specified. + keepgoing = matched if matching else not matched + if patterns and keepgoing: + for pattern in patterns: + # Match Objects always have a boolean value of True. + matched = bool(re.search(pattern, output)) + with recording(self, trace) as sbuf: + print("%s pattern: %s" % (heading, pattern), file=sbuf) + print("Matched" if matched else "Not matched", file=sbuf) + keepgoing = matched if matching else not matched + if not keepgoing: + break + + self.assertTrue(matched if matching else not matched, + msg if msg else EXP_MSG(str, exe)) + + def invoke(self, obj, name, trace=False): + """Use reflection to call a method dynamically with no argument.""" + trace = (True if traceAlways else trace) + + method = getattr(obj, name) + import inspect + self.assertTrue(inspect.ismethod(method), + name + "is a method name of object: " + str(obj)) + result = method() + with recording(self, trace) as sbuf: + print(str(method) + ":", result, file=sbuf) + return result + + def build(self, architecture=None, compiler=None, dictionary=None, clean=True): + """Platform specific way to build the default binaries.""" + module = builder_module() + if target_is_android(): + dictionary = append_android_envs(dictionary) + if self.debug_info is None: + return self.buildDefault(architecture, compiler, dictionary, clean) + elif self.debug_info == "dsym": + return self.buildDsym(architecture, compiler, dictionary, clean) + elif self.debug_info == "dwarf": + return self.buildDwarf(architecture, compiler, dictionary, clean) + elif self.debug_info == "dwo": + return self.buildDwo(architecture, compiler, dictionary, clean) + else: + self.fail("Can't build for debug info: %s" % self.debug_info) + + # ================================================= + # Misc. helper methods for debugging test execution + # ================================================= + + def DebugSBValue(self, val): + """Debug print a SBValue object, if traceAlways is True.""" + from .lldbutil import value_type_to_str + + if not traceAlways: + return + + err = sys.stderr + err.write(val.GetName() + ":\n") + err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') + err.write('\t' + "ByteSize -> " + str(val.GetByteSize()) + '\n') + err.write('\t' + "NumChildren -> " + str(val.GetNumChildren()) + '\n') + err.write('\t' + "Value -> " + str(val.GetValue()) + '\n') + err.write('\t' + "ValueAsUnsigned -> " + str(val.GetValueAsUnsigned())+ '\n') + err.write('\t' + "ValueType -> " + value_type_to_str(val.GetValueType()) + '\n') + err.write('\t' + "Summary -> " + str(val.GetSummary()) + '\n') + err.write('\t' + "IsPointerType -> " + str(val.TypeIsPointerType()) + '\n') + err.write('\t' + "Location -> " + val.GetLocation() + '\n') + + def DebugSBType(self, type): + """Debug print a SBType object, if traceAlways is True.""" + if not traceAlways: + return + + err = sys.stderr + err.write(type.GetName() + ":\n") + err.write('\t' + "ByteSize -> " + str(type.GetByteSize()) + '\n') + err.write('\t' + "IsPointerType -> " + str(type.IsPointerType()) + '\n') + err.write('\t' + "IsReferenceType -> " + str(type.IsReferenceType()) + '\n') + + def DebugPExpect(self, child): + """Debug the spwaned pexpect object.""" + if not traceAlways: + return + + print(child) + + @classmethod + def RemoveTempFile(cls, file): + if os.path.exists(file): + os.remove(file) diff --git a/packages/Python/lldbsuite/test/lldbtest_config.py b/packages/Python/lldbsuite/test/lldbtest_config.py new file mode 100644 index 000000000000..c2d4549437b9 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldbtest_config.py @@ -0,0 +1,21 @@ +""" + The LLVM Compiler Infrastructure + + This file is distributed under the University of Illinois Open Source + License. See LICENSE.TXT for details. + +Configuration options for lldbtest.py set by dotest.py during initialization +""" + +# array of strings +# each string has the name of an lldb channel followed by +# zero or more categories in that channel +# ex. "gdb-remote packets" +channels = [] + +# leave logs/traces even for successful test runs +log_success = False + +# path to the lldb command line executable tool +lldbExec = None + diff --git a/packages/Python/lldbsuite/test/lldbutil.py b/packages/Python/lldbsuite/test/lldbutil.py new file mode 100644 index 000000000000..339619dc4f74 --- /dev/null +++ b/packages/Python/lldbsuite/test/lldbutil.py @@ -0,0 +1,1016 @@ +""" +This LLDB module contains miscellaneous utilities. +Some of the test suite takes advantage of the utility functions defined here. +They can also be useful for general purpose lldb scripting. +""" + +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import collections +import os +import re +import sys + +# Third-party modules +from six import StringIO as SixStringIO +import six + +# LLDB modules +import lldb + + +# =================================================== +# Utilities for locating/checking executable programs +# =================================================== + +def is_exe(fpath): + """Returns True if fpath is an executable.""" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + """Returns the full path to a program; None otherwise.""" + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +# =================================================== +# Disassembly for an SBFunction or an SBSymbol object +# =================================================== + +def disassemble(target, function_or_symbol): + """Disassemble the function or symbol given a target. + + It returns the disassembly content in a string object. + """ + buf = SixStringIO() + insts = function_or_symbol.GetInstructions(target) + for i in insts: + print(i, file=buf) + return buf.getvalue() + +# ========================================================== +# Integer (byte size 1, 2, 4, and 8) to bytearray conversion +# ========================================================== + +def int_to_bytearray(val, bytesize): + """Utility function to convert an integer into a bytearray. + + It returns the bytearray in the little endian format. It is easy to get the + big endian format, just do ba.reverse() on the returned object. + """ + import struct + + if bytesize == 1: + return bytearray([val]) + + # Little endian followed by a format character. + template = "<%c" + if bytesize == 2: + fmt = template % 'h' + elif bytesize == 4: + fmt = template % 'i' + elif bytesize == 4: + fmt = template % 'q' + else: + return None + + packed = struct.pack(fmt, val) + return bytearray(list(map(ord, packed))) + +def bytearray_to_int(bytes, bytesize): + """Utility function to convert a bytearray into an integer. + + It interprets the bytearray in the little endian format. For a big endian + bytearray, just do ba.reverse() on the object before passing it in. + """ + import struct + + if bytesize == 1: + return bytes[0] + + # Little endian followed by a format character. + template = "<%c" + if bytesize == 2: + fmt = template % 'h' + elif bytesize == 4: + fmt = template % 'i' + elif bytesize == 4: + fmt = template % 'q' + else: + return None + + unpacked = struct.unpack(fmt, str(bytes)) + return unpacked[0] + + +# ============================================================== +# Get the description of an lldb object or None if not available +# ============================================================== +def get_description(obj, option=None): + """Calls lldb_obj.GetDescription() and returns a string, or None. + + For SBTarget, SBBreakpointLocation, and SBWatchpoint lldb objects, an extra + option can be passed in to describe the detailed level of description + desired: + o lldb.eDescriptionLevelBrief + o lldb.eDescriptionLevelFull + o lldb.eDescriptionLevelVerbose + """ + method = getattr(obj, 'GetDescription') + if not method: + return None + tuple = (lldb.SBTarget, lldb.SBBreakpointLocation, lldb.SBWatchpoint) + if isinstance(obj, tuple): + if option is None: + option = lldb.eDescriptionLevelBrief + + stream = lldb.SBStream() + if option is None: + success = method(stream) + else: + success = method(stream, option) + if not success: + return None + return stream.GetData() + + +# ================================================= +# Convert some enum value to its string counterpart +# ================================================= + +def state_type_to_str(enum): + """Returns the stateType string given an enum.""" + if enum == lldb.eStateInvalid: + return "invalid" + elif enum == lldb.eStateUnloaded: + return "unloaded" + elif enum == lldb.eStateConnected: + return "connected" + elif enum == lldb.eStateAttaching: + return "attaching" + elif enum == lldb.eStateLaunching: + return "launching" + elif enum == lldb.eStateStopped: + return "stopped" + elif enum == lldb.eStateRunning: + return "running" + elif enum == lldb.eStateStepping: + return "stepping" + elif enum == lldb.eStateCrashed: + return "crashed" + elif enum == lldb.eStateDetached: + return "detached" + elif enum == lldb.eStateExited: + return "exited" + elif enum == lldb.eStateSuspended: + return "suspended" + else: + raise Exception("Unknown StateType enum") + +def stop_reason_to_str(enum): + """Returns the stopReason string given an enum.""" + if enum == lldb.eStopReasonInvalid: + return "invalid" + elif enum == lldb.eStopReasonNone: + return "none" + elif enum == lldb.eStopReasonTrace: + return "trace" + elif enum == lldb.eStopReasonBreakpoint: + return "breakpoint" + elif enum == lldb.eStopReasonWatchpoint: + return "watchpoint" + elif enum == lldb.eStopReasonExec: + return "exec" + elif enum == lldb.eStopReasonSignal: + return "signal" + elif enum == lldb.eStopReasonException: + return "exception" + elif enum == lldb.eStopReasonPlanComplete: + return "plancomplete" + elif enum == lldb.eStopReasonThreadExiting: + return "threadexiting" + else: + raise Exception("Unknown StopReason enum") + +def symbol_type_to_str(enum): + """Returns the symbolType string given an enum.""" + if enum == lldb.eSymbolTypeInvalid: + return "invalid" + elif enum == lldb.eSymbolTypeAbsolute: + return "absolute" + elif enum == lldb.eSymbolTypeCode: + return "code" + elif enum == lldb.eSymbolTypeData: + return "data" + elif enum == lldb.eSymbolTypeTrampoline: + return "trampoline" + elif enum == lldb.eSymbolTypeRuntime: + return "runtime" + elif enum == lldb.eSymbolTypeException: + return "exception" + elif enum == lldb.eSymbolTypeSourceFile: + return "sourcefile" + elif enum == lldb.eSymbolTypeHeaderFile: + return "headerfile" + elif enum == lldb.eSymbolTypeObjectFile: + return "objectfile" + elif enum == lldb.eSymbolTypeCommonBlock: + return "commonblock" + elif enum == lldb.eSymbolTypeBlock: + return "block" + elif enum == lldb.eSymbolTypeLocal: + return "local" + elif enum == lldb.eSymbolTypeParam: + return "param" + elif enum == lldb.eSymbolTypeVariable: + return "variable" + elif enum == lldb.eSymbolTypeVariableType: + return "variabletype" + elif enum == lldb.eSymbolTypeLineEntry: + return "lineentry" + elif enum == lldb.eSymbolTypeLineHeader: + return "lineheader" + elif enum == lldb.eSymbolTypeScopeBegin: + return "scopebegin" + elif enum == lldb.eSymbolTypeScopeEnd: + return "scopeend" + elif enum == lldb.eSymbolTypeAdditional: + return "additional" + elif enum == lldb.eSymbolTypeCompiler: + return "compiler" + elif enum == lldb.eSymbolTypeInstrumentation: + return "instrumentation" + elif enum == lldb.eSymbolTypeUndefined: + return "undefined" + +def value_type_to_str(enum): + """Returns the valueType string given an enum.""" + if enum == lldb.eValueTypeInvalid: + return "invalid" + elif enum == lldb.eValueTypeVariableGlobal: + return "global_variable" + elif enum == lldb.eValueTypeVariableStatic: + return "static_variable" + elif enum == lldb.eValueTypeVariableArgument: + return "argument_variable" + elif enum == lldb.eValueTypeVariableLocal: + return "local_variable" + elif enum == lldb.eValueTypeRegister: + return "register" + elif enum == lldb.eValueTypeRegisterSet: + return "register_set" + elif enum == lldb.eValueTypeConstResult: + return "constant_result" + else: + raise Exception("Unknown ValueType enum") + + +# ================================================== +# Get stopped threads due to each stop reason. +# ================================================== + +def sort_stopped_threads(process, + breakpoint_threads = None, + crashed_threads = None, + watchpoint_threads = None, + signal_threads = None, + exiting_threads = None, + other_threads = None): + """ Fills array *_threads with threads stopped for the corresponding stop + reason. + """ + for lst in [breakpoint_threads, + watchpoint_threads, + signal_threads, + exiting_threads, + other_threads]: + if lst is not None: + lst[:] = [] + + for thread in process: + dispatched = False + for (reason, list) in [(lldb.eStopReasonBreakpoint, breakpoint_threads), + (lldb.eStopReasonException, crashed_threads), + (lldb.eStopReasonWatchpoint, watchpoint_threads), + (lldb.eStopReasonSignal, signal_threads), + (lldb.eStopReasonThreadExiting, exiting_threads), + (None, other_threads)]: + if not dispatched and list is not None: + if thread.GetStopReason() == reason or reason is None: + list.append(thread) + dispatched = True + +# ================================================== +# Utility functions for setting breakpoints +# ================================================== + +def run_break_set_by_file_and_line (test, file_name, line_number, extra_options = None, num_expected_locations = 1, loc_exact=False, module_name=None): + """Set a breakpoint by file and line, returning the breakpoint number. + + If extra_options is not None, then we append it to the breakpoint set command. + + If num_expected_locations is -1 we check that we got AT LEAST one location, otherwise we check that num_expected_locations equals the number of locations. + + If loc_exact is true, we check that there is one location, and that location must be at the input file and line number.""" + + if file_name == None: + command = 'breakpoint set -l %d'%(line_number) + else: + command = 'breakpoint set -f "%s" -l %d'%(file_name, line_number) + + if module_name: + command += " --shlib '%s'" % (module_name) + + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + if num_expected_locations == 1 and loc_exact: + check_breakpoint_result (test, break_results, num_locations=num_expected_locations, file_name = file_name, line_number = line_number, module_name=module_name) + else: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_by_symbol (test, symbol, extra_options = None, num_expected_locations = -1, sym_exact = False, module_name=None): + """Set a breakpoint by symbol name. Common options are the same as run_break_set_by_file_and_line. + + If sym_exact is true, then the output symbol must match the input exactly, otherwise we do a substring match.""" + command = 'breakpoint set -n "%s"'%(symbol) + + if module_name: + command += " --shlib '%s'" % (module_name) + + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + if num_expected_locations == 1 and sym_exact: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations, symbol_name = symbol, module_name=module_name) + else: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_by_selector (test, selector, extra_options = None, num_expected_locations = -1, module_name=None): + """Set a breakpoint by selector. Common options are the same as run_break_set_by_file_and_line.""" + + command = 'breakpoint set -S "%s"' % (selector) + + if module_name: + command += ' --shlib "%s"' % (module_name) + + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + if num_expected_locations == 1: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations, symbol_name = selector, symbol_match_exact=False, module_name=module_name) + else: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_by_regexp (test, regexp, extra_options=None, num_expected_locations=-1): + """Set a breakpoint by regular expression match on symbol name. Common options are the same as run_break_set_by_file_and_line.""" + + command = 'breakpoint set -r "%s"'%(regexp) + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + check_breakpoint_result (test, break_results, num_locations=num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_by_source_regexp (test, regexp, extra_options=None, num_expected_locations=-1): + """Set a breakpoint by source regular expression. Common options are the same as run_break_set_by_file_and_line.""" + command = 'breakpoint set -p "%s"'%(regexp) + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + check_breakpoint_result (test, break_results, num_locations=num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_command (test, command): + """Run the command passed in - it must be some break set variant - and analyze the result. + Returns a dictionary of information gleaned from the command-line results. + Will assert if the breakpoint setting fails altogether. + + Dictionary will contain: + bpno - breakpoint of the newly created breakpoint, -1 on error. + num_locations - number of locations set for the breakpoint. + + If there is only one location, the dictionary MAY contain: + file - source file name + line_no - source line number + symbol - symbol name + inline_symbol - inlined symbol name + offset - offset from the original symbol + module - module + address - address at which the breakpoint was set.""" + + patterns = [r"^Breakpoint (?P[0-9]+): (?P[0-9]+) locations\.$", + r"^Breakpoint (?P[0-9]+): (?Pno) locations \(pending\)\.", + r"^Breakpoint (?P[0-9]+): where = (?P.*)`(?P[+\-]{0,1}[^+]+)( \+ (?P[0-9]+)){0,1}( \[inlined\] (?P.*)){0,1} at (?P[^:]+):(?P[0-9]+), address = (?P

0x[0-9a-fA-F]+)$", + r"^Breakpoint (?P[0-9]+): where = (?P.*)`(?P.*)( \+ (?P[0-9]+)){0,1}, address = (?P
0x[0-9a-fA-F]+)$"] + match_object = test.match (command, patterns) + break_results = match_object.groupdict() + + # We always insert the breakpoint number, setting it to -1 if we couldn't find it + # Also, make sure it gets stored as an integer. + if not 'bpno' in break_results: + break_results['bpno'] = -1 + else: + break_results['bpno'] = int(break_results['bpno']) + + # We always insert the number of locations + # If ONE location is set for the breakpoint, then the output doesn't mention locations, but it has to be 1... + # We also make sure it is an integer. + + if not 'num_locations' in break_results: + num_locations = 1 + else: + num_locations = break_results['num_locations'] + if num_locations == 'no': + num_locations = 0 + else: + num_locations = int(break_results['num_locations']) + + break_results['num_locations'] = num_locations + + if 'line_no' in break_results: + break_results['line_no'] = int(break_results['line_no']) + + return break_results + +def get_bpno_from_match (break_results): + return int (break_results['bpno']) + +def check_breakpoint_result (test, break_results, file_name=None, line_number=-1, symbol_name=None, symbol_match_exact=True, module_name=None, offset=-1, num_locations=-1): + + out_num_locations = break_results['num_locations'] + + if num_locations == -1: + test.assertTrue (out_num_locations > 0, "Expecting one or more locations, got none.") + else: + test.assertTrue (num_locations == out_num_locations, "Expecting %d locations, got %d."%(num_locations, out_num_locations)) + + if file_name: + out_file_name = "" + if 'file' in break_results: + out_file_name = break_results['file'] + test.assertTrue (file_name == out_file_name, "Breakpoint file name '%s' doesn't match resultant name '%s'."%(file_name, out_file_name)) + + if line_number != -1: + out_line_number = -1 + if 'line_no' in break_results: + out_line_number = break_results['line_no'] + + test.assertTrue (line_number == out_line_number, "Breakpoint line number %s doesn't match resultant line %s."%(line_number, out_line_number)) + + if symbol_name: + out_symbol_name = "" + # Look first for the inlined symbol name, otherwise use the symbol name: + if 'inline_symbol' in break_results and break_results['inline_symbol']: + out_symbol_name = break_results['inline_symbol'] + elif 'symbol' in break_results: + out_symbol_name = break_results['symbol'] + + if symbol_match_exact: + test.assertTrue(symbol_name == out_symbol_name, "Symbol name '%s' doesn't match resultant symbol '%s'."%(symbol_name, out_symbol_name)) + else: + test.assertTrue(out_symbol_name.find(symbol_name) != -1, "Symbol name '%s' isn't in resultant symbol '%s'."%(symbol_name, out_symbol_name)) + + if module_name: + out_module_name = None + if 'module' in break_results: + out_module_name = break_results['module'] + + test.assertTrue (module_name.find(out_module_name) != -1, "Symbol module name '%s' isn't in expected module name '%s'."%(out_module_name, module_name)) + +# ================================================== +# Utility functions related to Threads and Processes +# ================================================== + +def get_stopped_threads(process, reason): + """Returns the thread(s) with the specified stop reason in a list. + + The list can be empty if no such thread exists. + """ + threads = [] + for t in process: + if t.GetStopReason() == reason: + threads.append(t) + return threads + +def get_stopped_thread(process, reason): + """A convenience function which returns the first thread with the given stop + reason or None. + + Example usages: + + 1. Get the stopped thread due to a breakpoint condition + + ... + from lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonPlanComplete) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + ... + + 2. Get the thread stopped due to a breakpoint + + ... + from lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + ... + + """ + threads = get_stopped_threads(process, reason) + if len(threads) == 0: + return None + return threads[0] + +def get_threads_stopped_at_breakpoint (process, bkpt): + """ For a stopped process returns the thread stopped at the breakpoint passed in bkpt""" + stopped_threads = [] + threads = [] + + stopped_threads = get_stopped_threads (process, lldb.eStopReasonBreakpoint) + + if len(stopped_threads) == 0: + return threads + + for thread in stopped_threads: + # Make sure we've hit our breakpoint... + break_id = thread.GetStopReasonDataAtIndex (0) + if break_id == bkpt.GetID(): + threads.append(thread) + + return threads + +def is_thread_crashed (test, thread): + """In the test suite we dereference a null pointer to simulate a crash. The way this is + reported depends on the platform.""" + if test.platformIsDarwin(): + return thread.GetStopReason() == lldb.eStopReasonException and "EXC_BAD_ACCESS" in thread.GetStopDescription(100) + elif test.getPlatform() == "linux": + return thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex(0) == thread.GetProcess().GetUnixSignals().GetSignalNumberFromName("SIGSEGV") + else: + return "invalid address" in thread.GetStopDescription(100) + +def get_crashed_threads (test, process): + threads = [] + if process.GetState() != lldb.eStateStopped: + return threads + for thread in process: + if is_thread_crashed(test, thread): + threads.append(thread) + return threads + +def continue_to_breakpoint (process, bkpt): + """ Continues the process, if it stops, returns the threads stopped at bkpt; otherwise, returns None""" + process.Continue() + if process.GetState() != lldb.eStateStopped: + return None + else: + return get_threads_stopped_at_breakpoint (process, bkpt) + +def get_caller_symbol(thread): + """ + Returns the symbol name for the call site of the leaf function. + """ + depth = thread.GetNumFrames() + if depth <= 1: + return None + caller = thread.GetFrameAtIndex(1).GetSymbol() + if caller: + return caller.GetName() + else: + return None + + +def get_function_names(thread): + """ + Returns a sequence of function names from the stack frames of this thread. + """ + def GetFuncName(i): + return thread.GetFrameAtIndex(i).GetFunctionName() + + return list(map(GetFuncName, list(range(thread.GetNumFrames())))) + + +def get_symbol_names(thread): + """ + Returns a sequence of symbols for this thread. + """ + def GetSymbol(i): + return thread.GetFrameAtIndex(i).GetSymbol().GetName() + + return list(map(GetSymbol, list(range(thread.GetNumFrames())))) + + +def get_pc_addresses(thread): + """ + Returns a sequence of pc addresses for this thread. + """ + def GetPCAddress(i): + return thread.GetFrameAtIndex(i).GetPCAddress() + + return list(map(GetPCAddress, list(range(thread.GetNumFrames())))) + + +def get_filenames(thread): + """ + Returns a sequence of file names from the stack frames of this thread. + """ + def GetFilename(i): + return thread.GetFrameAtIndex(i).GetLineEntry().GetFileSpec().GetFilename() + + return list(map(GetFilename, list(range(thread.GetNumFrames())))) + + +def get_line_numbers(thread): + """ + Returns a sequence of line numbers from the stack frames of this thread. + """ + def GetLineNumber(i): + return thread.GetFrameAtIndex(i).GetLineEntry().GetLine() + + return list(map(GetLineNumber, list(range(thread.GetNumFrames())))) + + +def get_module_names(thread): + """ + Returns a sequence of module names from the stack frames of this thread. + """ + def GetModuleName(i): + return thread.GetFrameAtIndex(i).GetModule().GetFileSpec().GetFilename() + + return list(map(GetModuleName, list(range(thread.GetNumFrames())))) + + +def get_stack_frames(thread): + """ + Returns a sequence of stack frames for this thread. + """ + def GetStackFrame(i): + return thread.GetFrameAtIndex(i) + + return list(map(GetStackFrame, list(range(thread.GetNumFrames())))) + + +def print_stacktrace(thread, string_buffer = False): + """Prints a simple stack trace of this thread.""" + + output = SixStringIO() if string_buffer else sys.stdout + target = thread.GetProcess().GetTarget() + + depth = thread.GetNumFrames() + + mods = get_module_names(thread) + funcs = get_function_names(thread) + symbols = get_symbol_names(thread) + files = get_filenames(thread) + lines = get_line_numbers(thread) + addrs = get_pc_addresses(thread) + + if thread.GetStopReason() != lldb.eStopReasonInvalid: + desc = "stop reason=" + stop_reason_to_str(thread.GetStopReason()) + else: + desc = "" + print("Stack trace for thread id={0:#x} name={1} queue={2} ".format( + thread.GetThreadID(), thread.GetName(), thread.GetQueueName()) + desc, file=output) + + for i in range(depth): + frame = thread.GetFrameAtIndex(i) + function = frame.GetFunction() + + load_addr = addrs[i].GetLoadAddress(target) + if not function: + file_addr = addrs[i].GetFileAddress() + start_addr = frame.GetSymbol().GetStartAddress().GetFileAddress() + symbol_offset = file_addr - start_addr + print(" frame #{num}: {addr:#016x} {mod}`{symbol} + {offset}".format( + num=i, addr=load_addr, mod=mods[i], symbol=symbols[i], offset=symbol_offset), file=output) + else: + print(" frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line} {args}".format( + num=i, addr=load_addr, mod=mods[i], + func='%s [inlined]' % funcs[i] if frame.IsInlined() else funcs[i], + file=files[i], line=lines[i], + args=get_args_as_string(frame, showFuncName=False) if not frame.IsInlined() else '()'), file=output) + + if string_buffer: + return output.getvalue() + + +def print_stacktraces(process, string_buffer = False): + """Prints the stack traces of all the threads.""" + + output = SixStringIO() if string_buffer else sys.stdout + + print("Stack traces for " + str(process), file=output) + + for thread in process: + print(print_stacktrace(thread, string_buffer=True), file=output) + + if string_buffer: + return output.getvalue() + +def expect_state_changes(test, listener, states, timeout = 5): + """Listens for state changed events on the listener and makes sure they match what we + expect. Stop-and-restart events (where GetRestartedFromEvent() returns true) are ignored.""" + + for expected_state in states: + def get_next_event(): + event = lldb.SBEvent() + if not listener.WaitForEvent(timeout, event): + test.fail("Timed out while waiting for a transition to state %s" % + lldb.SBDebugger.StateAsCString(expected_state)) + return event + + event = get_next_event() + while (lldb.SBProcess.GetStateFromEvent(event) == lldb.eStateStopped and + lldb.SBProcess.GetRestartedFromEvent(event)): + # Ignore restarted event and the subsequent running event. + event = get_next_event() + test.assertEqual(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning, + "Restarted event followed by a running event") + event = get_next_event() + + test.assertEqual(lldb.SBProcess.GetStateFromEvent(event), expected_state) + +# =================================== +# Utility functions related to Frames +# =================================== + +def get_parent_frame(frame): + """ + Returns the parent frame of the input frame object; None if not available. + """ + thread = frame.GetThread() + parent_found = False + for f in thread: + if parent_found: + return f + if f.GetFrameID() == frame.GetFrameID(): + parent_found = True + + # If we reach here, no parent has been found, return None. + return None + +def get_args_as_string(frame, showFuncName=True): + """ + Returns the args of the input frame object as a string. + """ + # arguments => True + # locals => False + # statics => False + # in_scope_only => True + vars = frame.GetVariables(True, False, False, True) # type of SBValueList + args = [] # list of strings + for var in vars: + args.append("(%s)%s=%s" % (var.GetTypeName(), + var.GetName(), + var.GetValue())) + if frame.GetFunction(): + name = frame.GetFunction().GetName() + elif frame.GetSymbol(): + name = frame.GetSymbol().GetName() + else: + name = "" + if showFuncName: + return "%s(%s)" % (name, ", ".join(args)) + else: + return "(%s)" % (", ".join(args)) + +def print_registers(frame, string_buffer = False): + """Prints all the register sets of the frame.""" + + output = SixStringIO() if string_buffer else sys.stdout + + print("Register sets for " + str(frame), file=output) + + registerSet = frame.GetRegisters() # Return type of SBValueList. + print("Frame registers (size of register set = %d):" % registerSet.GetSize(), file=output) + for value in registerSet: + #print(value, file=output) + print("%s (number of children = %d):" % (value.GetName(), value.GetNumChildren()), file=output) + for child in value: + print("Name: %s, Value: %s" % (child.GetName(), child.GetValue()), file=output) + + if string_buffer: + return output.getvalue() + +def get_registers(frame, kind): + """Returns the registers given the frame and the kind of registers desired. + + Returns None if there's no such kind. + """ + registerSet = frame.GetRegisters() # Return type of SBValueList. + for value in registerSet: + if kind.lower() in value.GetName().lower(): + return value + + return None + +def get_GPRs(frame): + """Returns the general purpose registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_GPRs + regs = get_GPRs(frame) + for reg in regs: + print("%s => %s" % (reg.GetName(), reg.GetValue())) + ... + """ + return get_registers(frame, "general purpose") + +def get_FPRs(frame): + """Returns the floating point registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_FPRs + regs = get_FPRs(frame) + for reg in regs: + print("%s => %s" % (reg.GetName(), reg.GetValue())) + ... + """ + return get_registers(frame, "floating point") + +def get_ESRs(frame): + """Returns the exception state registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_ESRs + regs = get_ESRs(frame) + for reg in regs: + print("%s => %s" % (reg.GetName(), reg.GetValue())) + ... + """ + return get_registers(frame, "exception state") + +# ====================================== +# Utility classes/functions for SBValues +# ====================================== + +class BasicFormatter(object): + """The basic formatter inspects the value object and prints the value.""" + def format(self, value, buffer=None, indent=0): + if not buffer: + output = SixStringIO() + else: + output = buffer + # If there is a summary, it suffices. + val = value.GetSummary() + # Otherwise, get the value. + if val == None: + val = value.GetValue() + if val == None and value.GetNumChildren() > 0: + val = "%s (location)" % value.GetLocation() + print("{indentation}({type}) {name} = {value}".format( + indentation = ' ' * indent, + type = value.GetTypeName(), + name = value.GetName(), + value = val), file=output) + return output.getvalue() + +class ChildVisitingFormatter(BasicFormatter): + """The child visiting formatter prints the value and its immediate children. + + The constructor takes a keyword arg: indent_child, which defaults to 2. + """ + def __init__(self, indent_child=2): + """Default indentation of 2 SPC's for the children.""" + self.cindent = indent_child + def format(self, value, buffer=None): + if not buffer: + output = SixStringIO() + else: + output = buffer + + BasicFormatter.format(self, value, buffer=output) + for child in value: + BasicFormatter.format(self, child, buffer=output, indent=self.cindent) + + return output.getvalue() + +class RecursiveDecentFormatter(BasicFormatter): + """The recursive decent formatter prints the value and the decendents. + + The constructor takes two keyword args: indent_level, which defaults to 0, + and indent_child, which defaults to 2. The current indentation level is + determined by indent_level, while the immediate children has an additional + indentation by inden_child. + """ + def __init__(self, indent_level=0, indent_child=2): + self.lindent = indent_level + self.cindent = indent_child + def format(self, value, buffer=None): + if not buffer: + output = SixStringIO() + else: + output = buffer + + BasicFormatter.format(self, value, buffer=output, indent=self.lindent) + new_indent = self.lindent + self.cindent + for child in value: + if child.GetSummary() != None: + BasicFormatter.format(self, child, buffer=output, indent=new_indent) + else: + if child.GetNumChildren() > 0: + rdf = RecursiveDecentFormatter(indent_level=new_indent) + rdf.format(child, buffer=output) + else: + BasicFormatter.format(self, child, buffer=output, indent=new_indent) + + return output.getvalue() + +# =========================================================== +# Utility functions for path manipulation on remote platforms +# =========================================================== + +def join_remote_paths(*paths): + # TODO: update with actual platform name for remote windows once it exists + if lldb.remote_platform.GetName() == 'remote-windows': + return os.path.join(*paths).replace(os.path.sep, '\\') + return os.path.join(*paths).replace(os.path.sep, '/') + +def append_to_process_working_directory(*paths): + remote = lldb.remote_platform + if remote: + return join_remote_paths(remote.GetWorkingDirectory(), *paths) + return os.path.join(os.getcwd(), *paths) + +# ================================================== +# Utility functions to get the correct signal number +# ================================================== + +import signal + +def get_signal_number(signal_name): + platform = lldb.remote_platform + if platform and platform.IsValid(): + signals = platform.GetUnixSignals() + if signals.IsValid(): + signal_number = signals.GetSignalNumberFromName(signal_name) + if signal_number > 0: + return signal_number + # No remote platform; fall back to using local python signals. + return getattr(signal, signal_name) + +class PrintableRegex(object): + def __init__(self, text): + self.regex = re.compile(text) + self.text = text + + def match(self, str): + return self.regex.match(str) + + def __str__(self): + return "%s" % (self.text) + + def __repr__(self): + return "re.compile(%s) -> %s" % (self.text, self.regex) + +def skip_if_callable(test, mycallable, reason): + if six.callable(mycallable): + if mycallable(test): + test.skipTest(reason) + return True + return False + +def skip_if_library_missing(test, target, library): + def find_library(target, library): + for module in target.modules: + filename = module.file.GetFilename() + if isinstance(library, str): + if library == filename: + return False + elif hasattr(library, 'match'): + if library.match(filename): + return False + return True + def find_library_callable(test): + return find_library(target, library) + return skip_if_callable(test, find_library_callable, "could not find library matching '%s' in target %s" % (library, target)) diff --git a/packages/Python/lldbsuite/test/lock.py b/packages/Python/lldbsuite/test/lock.py new file mode 100644 index 000000000000..89823b88abc5 --- /dev/null +++ b/packages/Python/lldbsuite/test/lock.py @@ -0,0 +1,27 @@ +""" +Interprocess mutex based on file locks +""" + +import fcntl +import os + +class Lock: + + def __init__(self, filename): + self.filename = filename + # This will create it if it does not exist already + unbuffered = 0 + self.handle = open(filename, 'a+', unbuffered) + + def acquire(self): + fcntl.flock(self.handle, fcntl.LOCK_EX) + + # will throw IOError if unavailable + def try_acquire(self): + fcntl.flock(self.handle, fcntl.LOCK_NB | fcntl.LOCK_EX) + + def release(self): + fcntl.flock(self.handle, fcntl.LOCK_UN) + + def __del__(self): + self.handle.close() diff --git a/packages/Python/lldbsuite/test/logging/Makefile b/packages/Python/lldbsuite/test/logging/Makefile new file mode 100644 index 000000000000..d4bc9c689043 --- /dev/null +++ b/packages/Python/lldbsuite/test/logging/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/logging/TestLogging.py b/packages/Python/lldbsuite/test/logging/TestLogging.py new file mode 100644 index 000000000000..f8558a758ee9 --- /dev/null +++ b/packages/Python/lldbsuite/test/logging/TestLogging.py @@ -0,0 +1,107 @@ +""" +Test lldb logging. This test just makes sure logging doesn't crash, and produces some output. +""" + +from __future__ import print_function + + + +import os, time, string +import lldb +from lldbsuite.test.lldbtest import * + +class LogTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + append_log_file = "lldb-commands-log-append.txt" + truncate_log_file = "lldb-commands-log-truncate.txt" + + @classmethod + def classCleanup(cls): + """Cleanup the test byproducts.""" + cls.RemoveTempFile(cls.truncate_log_file) + cls.RemoveTempFile(cls.append_log_file) + + def test (self): + self.build() + if self.debug_info == "dsym": + self.command_log_tests ("dsym") + else: + self.command_log_tests ("dwarf") + + def command_log_tests (self, type): + exe = os.path.join (os.getcwd(), "a.out") + self.expect("file " + exe, + patterns = [ "Current executable set to .*a.out" ]) + + log_file = os.path.join (os.getcwd(), "lldb-commands-log-%s-%s-%s.txt" % (type, + os.path.basename(self.getCompiler()), + self.getArchitecture())) + + if (os.path.exists (log_file)): + os.remove (log_file) + + # By default, Debugger::EnableLog() will set log options to + # PREPEND_THREAD_NAME + OPTION_THREADSAFE. We don't want the + # threadnames here, so we enable just threadsafe (-t). + self.runCmd ("log enable -t -f '%s' lldb commands" % (log_file)) + + self.runCmd ("command alias bp breakpoint") + + self.runCmd ("bp set -n main") + + self.runCmd ("bp l") + + self.runCmd("log disable lldb") + + self.assertTrue (os.path.isfile (log_file)) + + f = open (log_file) + log_lines = f.readlines() + f.close () + os.remove (log_file) + + self.assertTrue(log_lines > 0, "Something was written to the log file.") + + # Check that lldb truncates its log files + @no_debug_info_test + def test_log_truncate (self): + if (os.path.exists (self.truncate_log_file)): + os.remove (self.truncate_log_file) + + # put something in our log file + with open(self.truncate_log_file, "w") as f: + for i in range(1, 1000): + f.write("bacon\n") + + self.runCmd ("log enable -t -f '%s' lldb commands" % (self.truncate_log_file)) + self.runCmd ("help log") + self.runCmd ("log disable lldb") + + self.assertTrue (os.path.isfile (self.truncate_log_file)) + with open(self.truncate_log_file, "r") as f: + contents = f.read () + + # check that it got removed + self.assertTrue(string.find(contents, "bacon") == -1) + + # Check that lldb can append to a log file + @no_debug_info_test + def test_log_append (self): + if (os.path.exists (self.append_log_file)): + os.remove (self.append_log_file) + + # put something in our log file + with open(self.append_log_file, "w") as f: + f.write("bacon\n") + + self.runCmd ("log enable -t -a -f '%s' lldb commands" % (self.append_log_file)) + self.runCmd ("help log") + self.runCmd ("log disable lldb") + + self.assertTrue (os.path.isfile (self.append_log_file)) + with open(self.append_log_file, "r") as f: + contents = f.read () + + # check that it is still there + self.assertTrue(string.find(contents, "bacon") == 0) diff --git a/packages/Python/lldbsuite/test/logging/main.cpp b/packages/Python/lldbsuite/test/logging/main.cpp new file mode 100644 index 000000000000..7af4e3de2d0b --- /dev/null +++ b/packages/Python/lldbsuite/test/logging/main.cpp @@ -0,0 +1,62 @@ +//===-- 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 +#include +#include + +int +product (int x, int y) +{ + int result = x * y; + return result; +} + +int +sum (int a, int b) +{ + int result = a + b; + return result; +} + +int +strange_max (int m, int n) +{ + if (m > n) + return m; + else if (n > m) + return n; + else + return 0; +} + +int +foo (int i, int j) +{ + if (strange_max (i, j) == i) + return product (i, j); + else if (strange_max (i, j) == j) + return sum (i, j); + else + return product (sum (i, i), sum (j, j)); +} + +int +main(int argc, char const *argv[]) +{ + + int array[3]; + + array[0] = foo (1238, 78392); + array[1] = foo (379265, 23674); + array[2] = foo (872934, 234); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/macosx/add-dsym/Makefile b/packages/Python/lldbsuite/test/macosx/add-dsym/Makefile new file mode 100644 index 000000000000..4e4aa71de2a2 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/add-dsym/Makefile @@ -0,0 +1,11 @@ +CC ?= clang + +all: clean + mkdir hide.app + mkdir hide.app/Contents + $(CC) -g main.c + mv a.out.dSYM hide.app/Contents + strip -x a.out + +clean: + rm -rf a.out a.out.dSYM hide.app diff --git a/packages/Python/lldbsuite/test/macosx/add-dsym/TestAddDsymMidExecutionCommand.py b/packages/Python/lldbsuite/test/macosx/add-dsym/TestAddDsymMidExecutionCommand.py new file mode 100644 index 000000000000..497f695d7cc7 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/add-dsym/TestAddDsymMidExecutionCommand.py @@ -0,0 +1,45 @@ +"""Test that the 'add-dsym', aka 'target symbols add', succeeds in the middle of debug session.""" + +from __future__ import print_function + + + +import os, time +import lldb +import sys +from lldbsuite.test.lldbtest import * + +@skipUnlessDarwin +class AddDsymMidExecutionCommandCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + self.source = 'main.c' + + @no_debug_info_test # Prevent the genaration of the dwarf version of this test + def test_add_dsym_mid_execution(self): + """Test that add-dsym mid-execution loads the symbols at the right place for a slid binary.""" + self.buildDsym(clean=True) + exe = os.path.join(os.getcwd(), "a.out") + + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + main_bp = self.target.BreakpointCreateByName ("main", "a.out") + self.assertTrue(main_bp, VALID_BREAKPOINT) + + self.runCmd("settings set target.disable-aslr false") + self.process = self.target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(self.process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.assertTrue(self.process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + self.runCmd("add-dsym hide.app/Contents/a.out.dSYM") + + self.expect("frame select", + substrs = ['a.out`main at main.c']) diff --git a/packages/Python/lldbsuite/test/macosx/add-dsym/main.c b/packages/Python/lldbsuite/test/macosx/add-dsym/main.c new file mode 100644 index 000000000000..da9e09f07389 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/add-dsym/main.c @@ -0,0 +1,7 @@ +#include +static int var = 5; +int main () +{ + printf ("%p is %d\n", &var, var); // break on this line + return ++var; +} diff --git a/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/Makefile b/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/Makefile new file mode 100644 index 000000000000..aa3a0fcdcea4 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +MAKE_DSYM := NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/TestAppleTypesIsProduced.py b/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/TestAppleTypesIsProduced.py new file mode 100644 index 000000000000..fad14db41003 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/TestAppleTypesIsProduced.py @@ -0,0 +1,67 @@ +""" +Test that clang produces the __apple accelerator tables, for example, __apple_types, correctly. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.lldbutil import symbol_type_to_str + +class AppleTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + #rdar://problem/11166975 + @skipUnlessDarwin + def test_debug_info_for_apple_types(self): + """Test that __apple_types section does get produced by clang.""" + + if not self.getCompiler().endswith('clang'): + self.skipTest("clang compiler only test") + + self.build() + if self.debug_info == "dsym": + exe = os.path.join(os.getcwd(), "a.out.dSYM/Contents/Resources/DWARF/a.out") + else: + exe = os.path.join(os.getcwd(), "main.o") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + self.assertTrue(target.GetNumModules() > 0) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + print("Number of modules for the target: %d" % target.GetNumModules()) + for module in target.module_iter(): + print(module) + + # Get the executable module at index 0. + exe_module = target.GetModuleAtIndex(0) + + dwarf_section = exe_module.FindSection("__DWARF") + self.assertTrue(dwarf_section) + print("__DWARF section:", dwarf_section) + print("Number of sub-sections: %d" % dwarf_section.GetNumSubSections()) + INDENT = ' ' * 4 + for subsec in dwarf_section: + print(INDENT + str(subsec)) + + debug_str_sub_section = dwarf_section.FindSubSection("__debug_str") + self.assertTrue(debug_str_sub_section) + print("__debug_str sub-section:", debug_str_sub_section) + + # Find our __apple_types section by name. + apple_types_sub_section = dwarf_section.FindSubSection("__apple_types") + self.assertTrue(apple_types_sub_section) + print("__apple_types sub-section:", apple_types_sub_section) + + # These other three all important subsections should also be present. + self.assertTrue(dwarf_section.FindSubSection("__apple_names") and + dwarf_section.FindSubSection("__apple_namespac") and + dwarf_section.FindSubSection("__apple_objc")) diff --git a/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/main.c b/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/main.c new file mode 100644 index 000000000000..cb4bdb9c16b4 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/debug-info/apple_types/main.c @@ -0,0 +1,27 @@ +//===-- 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; + }; // Set break point at this line. + + struct rect_tag { + struct point_tag bottom_left; + struct point_tag top_right; + }; + struct point_tag pt = { 2, 3 }; // This is the first executable statement. + struct rect_tag rect = {{1,2}, {3,4}}; + pt.x = argc; + pt.y = argc * argc; + rect.top_right.x = rect.top_right.x + argc; + rect.top_right.y = rect.top_right.y + argc; + return 0; +} diff --git a/packages/Python/lldbsuite/test/macosx/indirect_symbol/Makefile b/packages/Python/lldbsuite/test/macosx/indirect_symbol/Makefile new file mode 100644 index 000000000000..07aa39eac3be --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/indirect_symbol/Makefile @@ -0,0 +1,48 @@ +CC ?= clang +ifeq "$(ARCH)" "" + ARCH = x86_64 +endif + +ifeq "$(OS)" "" + OS = $(shell uname -s) +endif + +CFLAGS ?= -g -O0 +CWD := $(shell pwd) + +LIB_PREFIX := lib + +ifeq "$(OS)" "Darwin" + CFLAGS += -arch $(ARCH) + DS := dsymutil + LD_FLAGS := -dynamiclib + LIB_INDIRECT := $(LIB_PREFIX)indirect.dylib + LIB_REEXPORT := $(LIB_PREFIX)reexport.dylib + EXEC_PATH := "@executable_path" + EXEC_PATH_INDIRECT := -install_name $(EXEC_PATH)/$(LIB_INDIRECT) + EXEC_PATH_REEXPORT := -install_name $(EXEC_PATH)/$(LIB_REEXPORT) +endif + +all: a.out $(LIB_INDIRECT) $(LIB_REEXPORT) + +a.out: main.o $(LIB_INDIRECT) $(LIB_REEXPORT) + $(CC) $(CFLAGS) -o a.out main.o -L. $(LIB_INDIRECT) $(LIB_REEXPORT) + +main.o: main.c + $(CC) $(CFLAGS) -c main.c + +$(LIB_INDIRECT): indirect.o + $(CC) $(CFLAGS) $(LD_FLAGS) $(EXEC_PATH_INDIRECT) -o $(LIB_INDIRECT) indirect.o + if [ "$(OS)" = "Darwin" ]; then dsymutil $(LIB_INDIRECT); fi + +indirect.o: indirect.c + $(CC) $(CFLAGS) -c indirect.c + +$(LIB_REEXPORT): reexport.o $(LIB_INDIRECT) + $(CC) $(CFLAGS) $(LD_FLAGS) $(EXEC_PATH_REEXPORT) -o $(LIB_REEXPORT) reexport.o -L. -lindirect -Wl,-alias_list,$(CWD)/alias.list + if [ "$(OS)" = "Darwin" ]; then dsymutil $(LIB_REEXPORT); fi + +reexport.o: reexport.c + $(CC) $(CFLAGS) -c reexport.c +clean: + rm -rf $(wildcard *.o *~ *.dylib *.so a.out *.dSYM) diff --git a/packages/Python/lldbsuite/test/macosx/indirect_symbol/TestIndirectSymbols.py b/packages/Python/lldbsuite/test/macosx/indirect_symbol/TestIndirectSymbols.py new file mode 100644 index 000000000000..4f98865ca5a2 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/indirect_symbol/TestIndirectSymbols.py @@ -0,0 +1,88 @@ +"""Test stepping and setting breakpoints in indirect and re-exported symbols.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestIndirectFunctions(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers that we will step to in main: + self.main_source = "main.c" + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test stepping and setting breakpoints in indirect and re-exported symbols.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + break1 = target.BreakpointCreateBySourceRegex ("Set breakpoint here to step in indirect.", self.main_source_spec) + self.assertTrue(break1, VALID_BREAKPOINT) + + break2 = target.BreakpointCreateBySourceRegex ("Set breakpoint here to step in reexported.", self.main_source_spec) + self.assertTrue(break2, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break1) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint 1.") + + thread = threads[0] + + # Now do a step-into, and we should end up in the hidden target of this indirect function. + thread.StepInto() + curr_function = thread.GetFrameAtIndex(0).GetFunctionName() + self.assertTrue (curr_function == "call_through_indirect_hidden", "Stepped into indirect symbols.") + + # Now set a breakpoint using the indirect symbol name, and make sure we get to that: + break_indirect = target.BreakpointCreateByName ("call_through_indirect"); + self.assertTrue (break_indirect, VALID_BREAKPOINT) + + # Now continue should take us to the second call through the indirect symbol: + + threads = lldbutil.continue_to_breakpoint (process, break_indirect) + self.assertTrue (len(threads) == 1, "Stopped at breakpoint in indirect function.") + curr_function = thread.GetFrameAtIndex(0).GetFunctionName() + self.assertTrue (curr_function == "call_through_indirect_hidden", "Stepped into indirect symbols.") + + # Delete this breakpoint so it won't get in the way: + target.BreakpointDelete (break_indirect.GetID()) + + # Now continue to the site of the first re-exported function call in main: + threads = lldbutil.continue_to_breakpoint (process, break2) + + # This is stepping Into through a re-exported symbol to an indirect symbol: + thread.StepInto() + curr_function = thread.GetFrameAtIndex(0).GetFunctionName() + self.assertTrue (curr_function == "call_through_indirect_hidden", "Stepped into indirect symbols.") + + # And the last bit is to set a breakpoint on the re-exported symbol and make sure we are again in out target function. + break_reexported = target.BreakpointCreateByName ("reexport_to_indirect"); + self.assertTrue (break_reexported, VALID_BREAKPOINT) + + # Now continue should take us to the second call through the indirect symbol: + + threads = lldbutil.continue_to_breakpoint (process, break_reexported) + self.assertTrue (len(threads) == 1, "Stopped at breakpoint in reexported function target.") + curr_function = thread.GetFrameAtIndex(0).GetFunctionName() + self.assertTrue (curr_function == "call_through_indirect_hidden", "Stepped into indirect symbols.") diff --git a/packages/Python/lldbsuite/test/macosx/indirect_symbol/alias.list b/packages/Python/lldbsuite/test/macosx/indirect_symbol/alias.list new file mode 100644 index 000000000000..3232c5888379 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/indirect_symbol/alias.list @@ -0,0 +1 @@ +_call_through_indirect _reexport_to_indirect \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/macosx/indirect_symbol/indirect.c b/packages/Python/lldbsuite/test/macosx/indirect_symbol/indirect.c new file mode 100644 index 000000000000..48e1459bb59b --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/indirect_symbol/indirect.c @@ -0,0 +1,14 @@ +#define MakeResolver(name) \ + void * name ## Resolver(void) __asm__("_" #name); \ + void * name ## Resolver(void) { \ + __asm__(".symbol_resolver _" #name); \ + return name ## _hidden; \ + } + +int +call_through_indirect_hidden(int arg) +{ + return arg + 5; +} + +MakeResolver(call_through_indirect) diff --git a/packages/Python/lldbsuite/test/macosx/indirect_symbol/main.c b/packages/Python/lldbsuite/test/macosx/indirect_symbol/main.c new file mode 100644 index 000000000000..b5af058d00b1 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/indirect_symbol/main.c @@ -0,0 +1,14 @@ +extern int call_through_indirect(int); +extern int reexport_to_indirect(int); + +int +main () +{ + int indirect_result = call_through_indirect(20); // Set breakpoint here to step in indirect. + indirect_result = call_through_indirect(30); + + int reexport_result = reexport_to_indirect (20); // Set breakpoint here to step in reexported. + reexport_result = reexport_to_indirect (30); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/macosx/indirect_symbol/reexport.c b/packages/Python/lldbsuite/test/macosx/indirect_symbol/reexport.c new file mode 100644 index 000000000000..096a463b3a47 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/indirect_symbol/reexport.c @@ -0,0 +1,7 @@ +extern int call_through_indirect(int); + +int +fake_call_through_reexport(int value) +{ + return value + 10; +} diff --git a/packages/Python/lldbsuite/test/macosx/order/Makefile b/packages/Python/lldbsuite/test/macosx/order/Makefile new file mode 100644 index 000000000000..52fae2d2ca34 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/order/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +C_SOURCES := main.c +LDFLAGS = $(CFLAGS) -Xlinker -order_file -Xlinker ./order-file +MAKE_DSYM := NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/macosx/order/TestOrderFile.py b/packages/Python/lldbsuite/test/macosx/order/TestOrderFile.py new file mode 100644 index 000000000000..6541169798ae --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/order/TestOrderFile.py @@ -0,0 +1,36 @@ +""" +Test that debug symbols have the correct order as specified by the order file. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * + +class OrderFileTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test(self): + """Test debug symbols follow the correct order by the order file.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Test that the debug symbols have Function f3 before Function f1. + # Use "-s address" option to sort by address. + self.runCmd("image dump symtab -s address a.out") + output = self.res.GetOutput() + mo_f3 = re.search("Code +.+f3", output) + mo_f1 = re.search("Code +.+f1", output) + + # Match objects for f3 and f1 must exist and f3 must come before f1. + self.assertTrue(mo_f3 and mo_f1 and mo_f3.start() < mo_f1.start(), + "Symbols have correct order by the order file") + + self.runCmd("run", RUN_COMPLETED) diff --git a/packages/Python/lldbsuite/test/macosx/order/cmds.txt b/packages/Python/lldbsuite/test/macosx/order/cmds.txt new file mode 100644 index 000000000000..8c51dd763bf4 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/order/cmds.txt @@ -0,0 +1,3 @@ +b main.c:41 +c +lines -shlib a.out main.c diff --git a/packages/Python/lldbsuite/test/macosx/order/main.c b/packages/Python/lldbsuite/test/macosx/order/main.c new file mode 100644 index 000000000000..cd1ea581876f --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/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 20 +int f1 (char *s) +{ + return printf("f1: %s\n", s); +} + + + + + +// We want f2 to start on line 30 +int f2 (char *s) +{ + return printf("f2: %s\n", s); +} + + + + + +// We want f3 to start on line 40 +int f3 (char *s) +{ + return printf("f3: %s\n", s); +} + + + + + +// We want main to start on line 50 +int main (int argc, const char * argv[]) +{ + f1("carp"); + f2("ding"); + f3("dong"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/macosx/order/order-file b/packages/Python/lldbsuite/test/macosx/order/order-file new file mode 100644 index 000000000000..0cf8ecd2a639 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/order/order-file @@ -0,0 +1,4 @@ +main.o:_f3 +main.o:_main +main.o:_f2 +main.o:_f1 diff --git a/packages/Python/lldbsuite/test/macosx/queues/Makefile b/packages/Python/lldbsuite/test/macosx/queues/Makefile new file mode 100644 index 000000000000..93f2f7b2f340 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/queues/Makefile @@ -0,0 +1,28 @@ +CC ?= clang +ifeq "$(ARCH)" "" + ARCH = x86_64 +endif + +ifeq "$(OS)" "" + OS = $(shell uname -s) +endif + +CFLAGS ?= -g -O0 +CWD := $(shell pwd) + +LIB_PREFIX := lib + +ifeq "$(OS)" "Darwin" + CFLAGS += -arch $(ARCH) +endif + +all: a.out + +a.out: main.o + $(CC) $(CFLAGS) -o a.out main.o + +main.o: main.c + $(CC) $(CFLAGS) -c main.c + +clean: + rm -rf $(wildcard *.o *~ *.dylib *.so a.out *.dSYM) diff --git a/packages/Python/lldbsuite/test/macosx/queues/TestQueues.py b/packages/Python/lldbsuite/test/macosx/queues/TestQueues.py new file mode 100644 index 000000000000..492d1d3bda3a --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/queues/TestQueues.py @@ -0,0 +1,243 @@ +"""Test queues inspection SB APIs.""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestQueues(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + @unittest2.expectedFailure("rdar://22531180") + def test_with_python_api(self): + """Test queues inspection SB APIs.""" + self.build() + self.queues() + self.queues_with_libBacktraceRecording() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers that we will step to in main: + self.main_source = "main.c" + + def check_queue_for_valid_queue_id(self, queue): + self.assertTrue(queue.GetQueueID() != 0, "Check queue %s for valid QueueID (got 0x%x)" % (queue.GetName(), queue.GetQueueID())) + + def check_running_and_pending_items_on_queue(self, queue, expected_running, expected_pending): + self.assertTrue(queue.GetNumPendingItems() == expected_pending, "queue %s should have %d pending items, instead has %d pending items" % (queue.GetName(), expected_pending, (queue.GetNumPendingItems()))) + self.assertTrue(queue.GetNumRunningItems() == expected_running, "queue %s should have %d running items, instead has %d running items" % (queue.GetName(), expected_running, (queue.GetNumRunningItems()))) + + def check_number_of_threads_owned_by_queue(self, queue, number_threads): + self.assertTrue(queue.GetNumThreads() == number_threads, "queue %s should have %d thread executing, but has %d" % (queue.GetName(), number_threads, queue.GetNumThreads())) + + def check_queue_kind (self, queue, kind): + expected_kind_string = "Unknown" + if kind == lldb.eQueueKindSerial: + expected_kind_string = "Serial queue" + if kind == lldb.eQueueKindConcurrent: + expected_kind_string = "Concurrent queue" + actual_kind_string = "Unknown" + if queue.GetKind() == lldb.eQueueKindSerial: + actual_kind_string = "Serial queue" + if queue.GetKind() == lldb.eQueueKindConcurrent: + actual_kind_string = "Concurrent queue" + self.assertTrue(queue.GetKind() == kind, "queue %s is expected to be a %s but it is actually a %s" % (queue.GetName(), expected_kind_string, actual_kind_string)) + + def check_queues_threads_match_queue(self, queue): + for idx in range(0, queue.GetNumThreads()): + t = queue.GetThreadAtIndex(idx) + self.assertTrue(t.IsValid(), "Queue %s's thread #%d must be valid" % (queue.GetName(), idx)) + self.assertTrue(t.GetQueueID() == queue.GetQueueID(), "Queue %s has a QueueID of %d but its thread #%d has a QueueID of %d" % (queue.GetName(), queue.GetQueueID(), idx, t.GetQueueID())) + self.assertTrue(t.GetQueueName() == queue.GetName(), "Queue %s has a QueueName of %s but its thread #%d has a QueueName of %s" % (queue.GetName(), queue.GetName(), idx, t.GetQueueName())) + self.assertTrue(t.GetQueue().GetQueueID() == queue.GetQueueID(), "Thread #%d's Queue's QueueID of %d is not the same as the QueueID of its owning queue %d" % (idx, t.GetQueue().GetQueueID(), queue.GetQueueID())) + + def queues(self): + """Test queues inspection SB APIs without libBacktraceRecording.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + self.main_source_spec = lldb.SBFileSpec (self.main_source) + break1 = target.BreakpointCreateByName ("stopper", 'a.out') + self.assertTrue(break1, VALID_BREAKPOINT) + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break1) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint 1.") + + queue_submittor_1 = lldb.SBQueue() + queue_performer_1 = lldb.SBQueue() + queue_performer_2 = lldb.SBQueue() + queue_performer_3 = lldb.SBQueue() + for idx in range (0, process.GetNumQueues()): + q = process.GetQueueAtIndex(idx) + if q.GetName() == "com.apple.work_submittor_1": + queue_submittor_1 = q + if q.GetName() == "com.apple.work_performer_1": + queue_performer_1 = q + if q.GetName() == "com.apple.work_performer_2": + queue_performer_2 = q + if q.GetName() == "com.apple.work_performer_3": + queue_performer_3 = q + + self.assertTrue(queue_submittor_1.IsValid() and queue_performer_1.IsValid() and queue_performer_2.IsValid() and queue_performer_3.IsValid(), "Got all four expected queues: %s %s %s %s" % (queue_submittor_1.IsValid(), queue_performer_1.IsValid(), queue_performer_2.IsValid(), queue_performer_3.IsValid())) + + self.check_queue_for_valid_queue_id (queue_submittor_1) + self.check_queue_for_valid_queue_id (queue_performer_1) + self.check_queue_for_valid_queue_id (queue_performer_2) + self.check_queue_for_valid_queue_id (queue_performer_3) + + self.check_number_of_threads_owned_by_queue (queue_submittor_1, 1) + self.check_number_of_threads_owned_by_queue (queue_performer_1, 1) + self.check_number_of_threads_owned_by_queue (queue_performer_2, 1) + self.check_number_of_threads_owned_by_queue (queue_performer_3, 4) + + self.check_queue_kind (queue_submittor_1, lldb.eQueueKindSerial) + self.check_queue_kind (queue_performer_1, lldb.eQueueKindSerial) + self.check_queue_kind (queue_performer_2, lldb.eQueueKindSerial) + self.check_queue_kind (queue_performer_3, lldb.eQueueKindConcurrent) + + self.check_queues_threads_match_queue (queue_submittor_1) + self.check_queues_threads_match_queue (queue_performer_1) + self.check_queues_threads_match_queue (queue_performer_2) + self.check_queues_threads_match_queue (queue_performer_3) + + + + # We have threads running with all the different dispatch QoS service + # levels - find those threads and check that we can get the correct + # QoS name for each of them. + + user_initiated_thread = lldb.SBThread() + user_interactive_thread = lldb.SBThread() + utility_thread = lldb.SBThread() + unspecified_thread = lldb.SBThread() + background_thread = lldb.SBThread() + for th in process.threads: + if th.GetName() == "user initiated QoS": + user_initiated_thread = th + if th.GetName() == "user interactive QoS": + user_interactive_thread = th + if th.GetName() == "utility QoS": + utility_thread = th + if th.GetName() == "unspecified QoS": + unspecified_thread = th + if th.GetName() == "background QoS": + background_thread = th + + self.assertTrue(user_initiated_thread.IsValid(), "Found user initiated QoS thread") + self.assertTrue(user_interactive_thread.IsValid(), "Found user interactive QoS thread") + self.assertTrue(utility_thread.IsValid(), "Found utility QoS thread") + self.assertTrue(unspecified_thread.IsValid(), "Found unspecified QoS thread") + self.assertTrue(background_thread.IsValid(), "Found background QoS thread") + + stream = lldb.SBStream() + self.assertTrue(user_initiated_thread.GetInfoItemByPathAsString("requested_qos.printable_name", stream), "Get QoS printable string for user initiated QoS thread") + self.assertTrue(stream.GetData() == "User Initiated", "user initiated QoS thread name is valid") + stream.Clear() + self.assertTrue(user_interactive_thread.GetInfoItemByPathAsString("requested_qos.printable_name", stream), "Get QoS printable string for user interactive QoS thread") + self.assertTrue(stream.GetData() == "User Interactive", "user interactive QoS thread name is valid") + stream.Clear() + self.assertTrue(utility_thread.GetInfoItemByPathAsString("requested_qos.printable_name", stream), "Get QoS printable string for utility QoS thread") + self.assertTrue(stream.GetData() == "Utility", "utility QoS thread name is valid") + stream.Clear() + self.assertTrue(unspecified_thread.GetInfoItemByPathAsString("requested_qos.printable_name", stream), "Get QoS printable string for unspecified QoS thread") + self.assertTrue(stream.GetData() == "User Initiated", "unspecified QoS thread name is valid") + stream.Clear() + self.assertTrue(background_thread.GetInfoItemByPathAsString("requested_qos.printable_name", stream), "Get QoS printable string for background QoS thread") + self.assertTrue(stream.GetData() == "Background", "background QoS thread name is valid") + + def queues_with_libBacktraceRecording(self): + """Test queues inspection SB APIs with libBacktraceRecording present.""" + exe = os.path.join(os.getcwd(), "a.out") + + if not os.path.isfile('/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib'): + self.skipTest ("Skipped because libBacktraceRecording.dylib was present on the system.") + + if not os.path.isfile('/usr/lib/system/introspection/libdispatch.dylib'): + self.skipTest ("Skipped because introspection libdispatch dylib is not present.") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + break1 = target.BreakpointCreateByName ("stopper", 'a.out') + self.assertTrue(break1, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, ['DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib', 'DYLD_LIBRARY_PATH=/usr/lib/system/introspection'], self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break1) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint 1.") + + libbtr_module_filespec = lldb.SBFileSpec("libBacktraceRecording.dylib") + libbtr_module = target.FindModule (libbtr_module_filespec) + if not libbtr_module.IsValid(): + self.skipTest ("Skipped because libBacktraceRecording.dylib was not loaded into the process.") + + self.assertTrue(process.GetNumQueues() >= 4, "Found the correct number of queues.") + + queue_submittor_1 = lldb.SBQueue() + queue_performer_1 = lldb.SBQueue() + queue_performer_2 = lldb.SBQueue() + queue_performer_3 = lldb.SBQueue() + for idx in range (0, process.GetNumQueues()): + q = process.GetQueueAtIndex(idx) + if q.GetName() == "com.apple.work_submittor_1": + queue_submittor_1 = q + if q.GetName() == "com.apple.work_performer_1": + queue_performer_1 = q + if q.GetName() == "com.apple.work_performer_2": + queue_performer_2 = q + if q.GetName() == "com.apple.work_performer_3": + queue_performer_3 = q + + self.assertTrue(queue_submittor_1.IsValid() and queue_performer_1.IsValid() and queue_performer_2.IsValid() and queue_performer_3.IsValid(), "Got all four expected queues: %s %s %s %s" % (queue_submittor_1.IsValid(), queue_performer_1.IsValid(), queue_performer_2.IsValid(), queue_performer_3.IsValid())) + + self.check_queue_for_valid_queue_id (queue_submittor_1) + self.check_queue_for_valid_queue_id (queue_performer_1) + self.check_queue_for_valid_queue_id (queue_performer_2) + self.check_queue_for_valid_queue_id (queue_performer_3) + + self.check_running_and_pending_items_on_queue (queue_submittor_1, 1, 0) + self.check_running_and_pending_items_on_queue (queue_performer_1, 1, 3) + self.check_running_and_pending_items_on_queue (queue_performer_2, 1, 9999) + self.check_running_and_pending_items_on_queue (queue_performer_3, 4, 0) + + self.check_number_of_threads_owned_by_queue (queue_submittor_1, 1) + self.check_number_of_threads_owned_by_queue (queue_performer_1, 1) + self.check_number_of_threads_owned_by_queue (queue_performer_2, 1) + self.check_number_of_threads_owned_by_queue (queue_performer_3, 4) + + self.check_queue_kind (queue_submittor_1, lldb.eQueueKindSerial) + self.check_queue_kind (queue_performer_1, lldb.eQueueKindSerial) + self.check_queue_kind (queue_performer_2, lldb.eQueueKindSerial) + self.check_queue_kind (queue_performer_3, lldb.eQueueKindConcurrent) + + + self.check_queues_threads_match_queue (queue_submittor_1) + self.check_queues_threads_match_queue (queue_performer_1) + self.check_queues_threads_match_queue (queue_performer_2) + self.check_queues_threads_match_queue (queue_performer_3) + + self.assertTrue(queue_performer_2.GetPendingItemAtIndex(0).IsValid(), "queue 2's pending item #0 is valid") + self.assertTrue(queue_performer_2.GetPendingItemAtIndex(0).GetAddress().GetSymbol().GetName() == "doing_the_work_2", "queue 2's pending item #0 should be doing_the_work_2") + self.assertTrue(queue_performer_2.GetNumPendingItems() == 9999, "verify that queue 2 still has 9999 pending items") + self.assertTrue(queue_performer_2.GetPendingItemAtIndex(9998).IsValid(), "queue 2's pending item #9998 is valid") + self.assertTrue(queue_performer_2.GetPendingItemAtIndex(9998).GetAddress().GetSymbol().GetName() == "doing_the_work_2", "queue 2's pending item #0 should be doing_the_work_2") + self.assertTrue(queue_performer_2.GetPendingItemAtIndex(9999).IsValid() == False, "queue 2's pending item #9999 is invalid") diff --git a/packages/Python/lldbsuite/test/macosx/queues/main.c b/packages/Python/lldbsuite/test/macosx/queues/main.c new file mode 100644 index 000000000000..fa6a3e570ae3 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/queues/main.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include + +void +doing_the_work_1(void *in) +{ + while (1) + sleep (1); +} + +void +submit_work_1a(void *in) +{ + dispatch_queue_t *work_performer_1 = (dispatch_queue_t*) in; + dispatch_async_f (*work_performer_1, NULL, doing_the_work_1); + dispatch_async_f (*work_performer_1, NULL, doing_the_work_1); +} + +void +submit_work_1b(void *in) +{ + dispatch_queue_t *work_performer_1 = (dispatch_queue_t*) in; + dispatch_async_f (*work_performer_1, NULL, doing_the_work_1); + dispatch_async_f (*work_performer_1, NULL, doing_the_work_1); + while (1) + sleep (1); +} + +void +doing_the_work_2(void *in) +{ + while (1) + sleep (1); +} + +void +submit_work_2(void *in) +{ + dispatch_queue_t *work_performer_2 = (dispatch_queue_t*) in; + int i = 0; + while (i++ < 5000) + { + dispatch_async_f (*work_performer_2, NULL, doing_the_work_2); + dispatch_async_f (*work_performer_2, NULL, doing_the_work_2); + } +} + + +void +doing_the_work_3(void *in) +{ + while (1) + sleep(1); +} + +void +submit_work_3(void *in) +{ + dispatch_queue_t *work_performer_3 = (dispatch_queue_t*) in; + dispatch_async_f (*work_performer_3, NULL, doing_the_work_3); + dispatch_async_f (*work_performer_3, NULL, doing_the_work_3); + dispatch_async_f (*work_performer_3, NULL, doing_the_work_3); + dispatch_async_f (*work_performer_3, NULL, doing_the_work_3); +} + + +void +stopper () +{ + while (1) + sleep (1); +} + +int main () +{ + dispatch_queue_t work_submittor_1 = dispatch_queue_create ("com.apple.work_submittor_1", DISPATCH_QUEUE_SERIAL); + dispatch_queue_t work_submittor_2 = dispatch_queue_create ("com.apple.work_submittor_and_quit_2", DISPATCH_QUEUE_SERIAL); + dispatch_queue_t work_submittor_3 = dispatch_queue_create ("com.apple.work_submittor_3", DISPATCH_QUEUE_SERIAL); + + dispatch_queue_t work_performer_1 = dispatch_queue_create ("com.apple.work_performer_1", DISPATCH_QUEUE_SERIAL); + dispatch_queue_t work_performer_2 = dispatch_queue_create ("com.apple.work_performer_2", DISPATCH_QUEUE_SERIAL); + + dispatch_queue_t work_performer_3 = dispatch_queue_create ("com.apple.work_performer_3", DISPATCH_QUEUE_CONCURRENT); + + dispatch_async_f (work_submittor_1, (void*) &work_performer_1, submit_work_1a); + dispatch_async_f (work_submittor_1, (void*) &work_performer_1, submit_work_1b); + + dispatch_async_f (work_submittor_2, (void*) &work_performer_2, submit_work_2); + + dispatch_async_f (work_submittor_3, (void*) &work_performer_3, submit_work_3); + + + // Spin up threads with each of the different libdispatch QoS values. + + dispatch_async (dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + pthread_setname_np ("user initiated QoS"); + while (1) + sleep (10); + }); + dispatch_async (dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ + pthread_setname_np ("user interactive QoS"); + while (1) + sleep (10); + }); + dispatch_async (dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ + pthread_setname_np ("default QoS"); + while (1) + sleep (10); + }); + dispatch_async (dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{ + pthread_setname_np ("utility QoS"); + while (1) + sleep (10); + }); + dispatch_async (dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ + pthread_setname_np ("background QoS"); + while (1) + sleep (10); + }); + dispatch_async (dispatch_get_global_queue(QOS_CLASS_UNSPECIFIED, 0), ^{ + pthread_setname_np ("unspecified QoS"); + while (1) + sleep (10); + }); + + + sleep (1); + stopper (); + +} + diff --git a/packages/Python/lldbsuite/test/macosx/safe-to-func-call/Makefile b/packages/Python/lldbsuite/test/macosx/safe-to-func-call/Makefile new file mode 100644 index 000000000000..93f2f7b2f340 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/safe-to-func-call/Makefile @@ -0,0 +1,28 @@ +CC ?= clang +ifeq "$(ARCH)" "" + ARCH = x86_64 +endif + +ifeq "$(OS)" "" + OS = $(shell uname -s) +endif + +CFLAGS ?= -g -O0 +CWD := $(shell pwd) + +LIB_PREFIX := lib + +ifeq "$(OS)" "Darwin" + CFLAGS += -arch $(ARCH) +endif + +all: a.out + +a.out: main.o + $(CC) $(CFLAGS) -o a.out main.o + +main.o: main.c + $(CC) $(CFLAGS) -c main.c + +clean: + rm -rf $(wildcard *.o *~ *.dylib *.so a.out *.dSYM) diff --git a/packages/Python/lldbsuite/test/macosx/safe-to-func-call/TestSafeFuncCalls.py b/packages/Python/lldbsuite/test/macosx/safe-to-func-call/TestSafeFuncCalls.py new file mode 100644 index 000000000000..297223c3e84e --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/safe-to-func-call/TestSafeFuncCalls.py @@ -0,0 +1,63 @@ +"""Test function call thread safety.""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestSafeFuncCalls(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers that we will step to in main: + self.main_source = "main.c" + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test_with_python_api(self): + """Test function call thread safety.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + self.main_source_spec = lldb.SBFileSpec (self.main_source) + break1 = target.BreakpointCreateByName ("stopper", 'a.out') + self.assertTrue(break1, VALID_BREAKPOINT) + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break1) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint 1.") + + self.check_number_of_threads(process) + + main_thread = lldb.SBThread() + select_thread = lldb.SBThread() + for idx in range (0, process.GetNumThreads()): + t = process.GetThreadAtIndex (idx) + if t.GetName() == "main thread": + main_thread = t + if t.GetName() == "select thread": + select_thread = t + + self.assertTrue(main_thread.IsValid() and select_thread.IsValid(), "Got both expected threads") + + self.safe_to_call_func_on_main_thread (main_thread) + self.safe_to_call_func_on_select_thread (select_thread) + + def check_number_of_threads(self, process): + self.assertTrue(process.GetNumThreads() == 2, "Check that the process has two threads when sitting at the stopper() breakpoint") + + def safe_to_call_func_on_main_thread (self, main_thread): + self.assertTrue(main_thread.SafeToCallFunctions() == True, "It is safe to call functions on the main thread") + + def safe_to_call_func_on_select_thread (self, select_thread): + self.assertTrue(select_thread.SafeToCallFunctions() == False, "It is not safe to call functions on the select thread") diff --git a/packages/Python/lldbsuite/test/macosx/safe-to-func-call/main.c b/packages/Python/lldbsuite/test/macosx/safe-to-func-call/main.c new file mode 100644 index 000000000000..613384ff73b3 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/safe-to-func-call/main.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +void * +select_thread (void *in) +{ + pthread_setname_np ("select thread"); + fd_set fdset; + FD_SET (STDIN_FILENO, &fdset); + while (1) + select (2, &fdset, NULL, NULL, NULL); + return NULL; +} + +void stopper () +{ + while (1) + sleep(1); // break here +} + +int main () +{ + pthread_setname_np ("main thread"); + pthread_t other_thread; + pthread_create (&other_thread, NULL, select_thread, NULL); + sleep (1); + stopper(); +} diff --git a/packages/Python/lldbsuite/test/macosx/universal/Makefile b/packages/Python/lldbsuite/test/macosx/universal/Makefile new file mode 100644 index 000000000000..854c78ed8c29 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/universal/Makefile @@ -0,0 +1,19 @@ +CC ?= clang + +testit: testit.i386 testit.x86_64 + lipo -create -o testit testit.i386 testit.x86_64 + +testit.i386: testit.i386.o + $(CC) -arch i386 -o testit.i386 testit.i386.o + +testit.x86_64: testit.x86_64.o + $(CC) -arch x86_64 -o testit.x86_64 testit.x86_64.o + +testit.i386.o: main.c + $(CC) -g -O0 -arch i386 -c -o testit.i386.o main.c + +testit.x86_64.o: main.c + $(CC) -g -O0 -arch x86_64 -c -o testit.x86_64.o main.c + +clean: + rm -rf $(wildcard testit* *~) diff --git a/packages/Python/lldbsuite/test/macosx/universal/TestUniversal.py b/packages/Python/lldbsuite/test/macosx/universal/TestUniversal.py new file mode 100644 index 000000000000..4b722b0c1d8e --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/universal/TestUniversal.py @@ -0,0 +1,107 @@ +"""Test aspects of lldb commands on universal binaries.""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class UniversalTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set break point at this line.') + + @add_test_categories(['pyapi']) + @skipUnlessDarwin + @unittest2.skipUnless(hasattr(os, "uname") and os.uname()[4] in ['i386', 'x86_64'], + "requires i386 or x86_64") + def test_sbdebugger_create_target_with_file_and_target_triple(self): + """Test the SBDebugger.CreateTargetWithFileAndTargetTriple() API.""" + # Invoke the default build rule. + self.build() + + # Note that "testit" is a universal binary. + exe = os.path.join(os.getcwd(), "testit") + + # Create a target by the debugger. + target = self.dbg.CreateTargetWithFileAndTargetTriple(exe, "i386-apple-macosx") + self.assertTrue(target, VALID_TARGET) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + @skipUnlessDarwin + @unittest2.skipUnless(hasattr(os, "uname") and os.uname()[4] in ['i386', 'x86_64'], + "requires i386 or x86_64") + def test_process_launch_for_universal(self): + """Test process launch of a universal binary.""" + from lldbsuite.test.lldbutil import print_registers + + # Invoke the default build rule. + self.build() + + # Note that "testit" is a universal binary. + exe = os.path.join(os.getcwd(), "testit") + + # By default, x86_64 is assumed if no architecture is specified. + self.expect("file " + exe, CURRENT_EXECUTABLE_SET, + startstr = "Current executable set to ", + substrs = ["testit' (x86_64)."]) + + # Break inside the main. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + + # We should be able to launch the x86_64 executable. + self.runCmd("run", RUN_SUCCEEDED) + + # Check whether we have a 64-bit process launched. + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + self.assertTrue(target and process and + self.invoke(process, 'GetAddressByteSize') == 8, + "64-bit process launched") + + frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0) + registers = print_registers(frame, string_buffer=True) + self.expect(registers, exe=False, + substrs = ['Name: rax']) + + self.runCmd("continue") + + # Now specify i386 as the architecture for "testit". + self.expect("file -a i386 " + exe, CURRENT_EXECUTABLE_SET, + startstr = "Current executable set to ", + substrs = ["testit' (i386)."]) + + # Break inside the main. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + + # We should be able to launch the i386 executable as well. + self.runCmd("run", RUN_SUCCEEDED) + + # Check whether we have a 32-bit process launched. + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + self.assertTrue(target and process, + "32-bit process launched") + + pointerSize = self.invoke(process, 'GetAddressByteSize') + self.assertTrue(pointerSize == 4, + "AddressByteSize of 32-bit process should be 4, got %d instead." % pointerSize) + + frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0) + registers = print_registers(frame, string_buffer=True) + self.expect(registers, exe=False, + substrs = ['Name: eax']) + + self.runCmd("continue") diff --git a/packages/Python/lldbsuite/test/macosx/universal/main.c b/packages/Python/lldbsuite/test/macosx/universal/main.c new file mode 100644 index 000000000000..9351c77f7146 --- /dev/null +++ b/packages/Python/lldbsuite/test/macosx/universal/main.c @@ -0,0 +1,7 @@ +#include +int +main (int argc, char **argv) +{ + printf ("Hello there!\n"); // Set break point at this line. + return 0; +} diff --git a/packages/Python/lldbsuite/test/make/Makefile.rules b/packages/Python/lldbsuite/test/make/Makefile.rules new file mode 100644 index 000000000000..e753317e9398 --- /dev/null +++ b/packages/Python/lldbsuite/test/make/Makefile.rules @@ -0,0 +1,600 @@ +#---------------------------------------------------------------------- +# Clients fill in the source files to build +#---------------------------------------------------------------------- +# C_SOURCES := main.c +# CXX_SOURCES := +# OBJC_SOURCES := +# OBJCXX_SOURCES := +# DYLIB_C_SOURCES := +# DYLIB_CXX_SOURCES := +# +# Specifying DYLIB_ONLY has the effect of building dylib only, skipping +# the building of the a.out executable program. For example, +# DYLIB_ONLY := YES +# +# Also might be of interest: +# FRAMEWORK_INCLUDES (Darwin only) := +# CFLAGS_EXTRAS := +# LD_EXTRAS := +# SPLIT_DEBUG_SYMBOLS := YES +# CROSS_COMPILE := +# +# And test/functionalities/archives/Makefile: +# MAKE_DSYM := NO +# ARCHIVE_NAME := libfoo.a +# ARCHIVE_C_SOURCES := a.c b.c + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +THIS_FILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))/ +LLDB_BASE_DIR := $(THIS_FILE_DIR)../../../../../ + + +#---------------------------------------------------------------------- +# If TRIPLE is not defined try to set the ARCH, CC, CFLAGS, and more +# from the triple alone +#---------------------------------------------------------------------- +TRIPLE_CFLAGS := +ifneq "$(TRIPLE)" "" + triple_space = $(subst -, ,$(TRIPLE)) + ARCH =$(word 1, $(triple_space)) + TRIPLE_VENDOR =$(word 2, $(triple_space)) + triple_os_and_version =$(shell echo $(word 3, $(triple_space)) | sed 's/\([a-z]*\)\(.*\)/\1 \2/') + TRIPLE_OS =$(word 1, $(triple_os_and_version)) + TRIPLE_VERSION =$(word 2, $(triple_os_and_version)) + ifeq "$(TRIPLE_VENDOR)" "apple" + ifeq "$(TRIPLE_OS)" "ios" + ifeq "$(SDKROOT)" "" + # Set SDKROOT if it wasn't set + ifneq (,$(findstring arm,$(ARCH))) + SDKROOT = $(shell xcrun --sdk iphoneos --show-sdk-path) + ifeq "$(TRIPLE_VERSION)" "" + TRIPLE_VERSION =$(shell echo $(notdir $(SDKROOT)) | sed -e 's/.*\([0-9]\.[0-9]\).*/\1/') + endif + TRIPLE_CFLAGS :=-mios-version-min=$(TRIPLE_VERSION) -isysroot "$(SDKROOT)" + else + SDKROOT = $(shell xcrun --sdk iphonesimulator --show-sdk-path) + ifeq "$(TRIPLE_VERSION)" "" + TRIPLE_VERSION =$(shell echo $(notdir $(SDKROOT)) | sed -e 's/.*\([0-9]\.[0-9]\).*/\1/') + endif + TRIPLE_CFLAGS :=-mios-simulator-version-min=$(TRIPLE_VERSION) -isysroot "$(SDKROOT)" + endif + endif + endif + endif +endif + +#---------------------------------------------------------------------- +# If OS is not defined, use 'uname -s' to determine the OS name. +# +# uname on Windows gives "windows32", but most environments standardize +# on "Windows_NT", so we'll make it consistent here. When running +# tests from Visual Studio, the environment variable isn't inherited +# all the way down to the process spawned for make. +#---------------------------------------------------------------------- +HOST_OS = $(shell uname -s) +ifeq "$(HOST_OS)" "windows32" + HOST_OS = Windows_NT +endif +ifeq "$(OS)" "" + OS = $(HOST_OS) +endif + +#---------------------------------------------------------------------- +# If ARCH is not defined, default to x86_64. +#---------------------------------------------------------------------- +ifeq "$(ARCH)" "" +ifeq "$(OS)" "Windows_NT" + ARCH = x86 +else + ARCH = x86_64 +endif +endif + +#---------------------------------------------------------------------- +# CC defaults to clang. +# +# If you change the defaults of CC, be sure to also change it in the file +# test/plugins/builder_base.py, which provides a Python way to return the +# value of the make variable CC -- getCompiler(). +# +# See also these functions: +# o cxx_compiler +# o cxx_linker +#---------------------------------------------------------------------- +CC ?= clang +ifeq "$(CC)" "cc" + ifneq "$(shell which clang)" "" + CC = clang + else ifneq "$(shell which clang-3.5)" "" + CC = clang-3.5 + else ifneq "$(shell which gcc)" "" + CC = gcc + endif +endif + +#---------------------------------------------------------------------- +# ARCHFLAG is the flag used to tell the compiler which architecture +# to compile for. The default is the flag that clang accepts. +#---------------------------------------------------------------------- +ARCHFLAG ?= -arch + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +ifeq "$(OS)" "Darwin" + DS := $(shell xcrun -find -toolchain default dsymutil) + DSFLAGS = + DSYM = $(EXE).dSYM + AR := $(CROSS_COMPILE)libtool + ARFLAGS := -static -o +else + AR := $(CROSS_COMPILE)ar + # On non-Apple platforms, -arch becomes -m + ARCHFLAG := -m + + # i386, i686, x86 -> 32 + # amd64, x86_64, x64 -> 64 + ifeq "$(ARCH)" "amd64" + override ARCH := $(subst amd64,64,$(ARCH)) + endif + ifeq "$(ARCH)" "x86_64" + override ARCH := $(subst x86_64,64,$(ARCH)) + endif + ifeq "$(ARCH)" "x64" + override ARCH := $(subst x64,64,$(ARCH)) + endif + ifeq "$(ARCH)" "x86" + override ARCH := $(subst x86,32,$(ARCH)) + endif + ifeq "$(ARCH)" "i386" + override ARCH := $(subst i386,32,$(ARCH)) + endif + ifeq "$(ARCH)" "i686" + override ARCH := $(subst i686,32,$(ARCH)) + endif + ifeq "$(ARCH)" "powerpc" + override ARCH := $(subst powerpc,32,$(ARCH)) + endif + ifeq "$(ARCH)" "powerpc64" + override ARCH := $(subst powerpc64,64,$(ARCH)) + endif + ifeq "$(ARCH)" "aarch64" + override ARCH := + override ARCHFLAG := + endif + ifeq "$(ARCH)" "arm" + override ARCH := + override ARCHFLAG := + endif + ifeq "$(findstring mips,$(ARCH))" "mips" + override ARCHFLAG := - + endif + + ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES" + DSYM = $(EXE).debug + endif +endif + +LIMIT_DEBUG_INFO_FLAGS = +NO_LIMIT_DEBUG_INFO_FLAGS = +ifneq (,$(findstring clang,$(CC))) + LIMIT_DEBUG_INFO_FLAGS += -flimit-debug-info + NO_LIMIT_DEBUG_INFO_FLAGS += -fno-limit-debug-info +endif + +DEBUG_INFO_FLAG ?= -g + +CFLAGS ?= $(DEBUG_INFO_FLAG) -O0 -fno-builtin +ifeq "$(OS)" "Darwin" + CFLAGS += $(ARCHFLAG) $(ARCH) $(FRAMEWORK_INCLUDES) $(CFLAGS_EXTRAS) -I$(LLDB_BASE_DIR)include +else + CFLAGS += $(ARCHFLAG)$(ARCH) $(FRAMEWORK_INCLUDES) $(CFLAGS_EXTRAS) -I$(LLDB_BASE_DIR)include +endif + +CFLAGS += -include $(THIS_FILE_DIR)test_common.h $(TRIPLE_CFLAGS) + +# Use this one if you want to build one part of the result without debug information: +ifeq "$(OS)" "Darwin" + CFLAGS_NO_DEBUG = -O0 $(ARCHFLAG) $(ARCH) $(FRAMEWORK_INCLUDES) $(CFLAGS_EXTRAS) $(TRIPLE_CFLAGS) +else + CFLAGS_NO_DEBUG = -O0 $(ARCHFLAG)$(ARCH) $(FRAMEWORK_INCLUDES) $(CFLAGS_EXTRAS) $(TRIPLE_CFLAGS) +endif + +ifeq "$(MAKE_DWO)" "YES" + CFLAGS += -gsplit-dwarf +endif + +CXXFLAGS += -std=c++11 +CXXFLAGS += $(CFLAGS) +LD = $(CC) +LDFLAGS ?= $(CFLAGS) +LDFLAGS += $(LD_EXTRAS) +ifeq (,$(filter $(OS), Windows_NT Android)) + ifneq (,$(filter YES,$(ENABLE_THREADS))) + LDFLAGS += -pthread + endif +endif +OBJECTS = +EXE ?= a.out + +ifneq "$(DYLIB_NAME)" "" + ifeq "$(OS)" "Darwin" + DYLIB_FILENAME = lib$(DYLIB_NAME).dylib + DYLIB_EXECUTABLE_PATH ?= @executable_path + else ifeq "$(OS)" "Windows_NT" + DYLIB_FILENAME = $(DYLIB_NAME).dll + else + DYLIB_FILENAME = lib$(DYLIB_NAME).so + endif +endif + +# Function that returns the counterpart C++ compiler, given $(CC) as arg. +cxx_compiler_notdir = $(if $(findstring clang,$(1)), \ + $(subst clang,clang++,$(1)), \ + $(if $(findstring icc,$(1)), \ + $(subst icc,icpc,$(1)), \ + $(if $(findstring llvm-gcc,$(1)), \ + $(subst llvm-gcc,llvm-g++,$(1)), \ + $(if $(findstring gcc,$(1)), \ + $(subst gcc,g++,$(1)), \ + $(subst cc,c++,$(1)))))) +cxx_compiler = $(if $(findstring /,$(1)),$(join $(dir $(1)), $(call cxx_compiler_notdir,$(notdir $(1)))),$(call cxx_compiler_notdir,$(1))) + +# Function that returns the C++ linker, given $(CC) as arg. +cxx_linker_notdir = $(if $(findstring clang,$(1)), \ + $(subst clang,clang++,$(1)), \ + $(if $(findstring icc,$(1)), \ + $(subst icc,icpc,$(1)), \ + $(if $(findstring llvm-gcc,$(1)), \ + $(subst llvm-gcc,llvm-g++,$(1)), \ + $(if $(findstring gcc,$(1)), \ + $(subst gcc,g++,$(1)), \ + $(subst cc,c++,$(1)))))) +cxx_linker = $(if $(findstring /,$(1)),$(join $(dir $(1)), $(call cxx_linker_notdir,$(notdir $(1)))),$(call cxx_linker_notdir,$(1))) + +OBJCOPY := $(CROSS_COMPILE)objcopy + +#---------------------------------------------------------------------- +# Windows specific options +#---------------------------------------------------------------------- +ifeq "$(OS)" "Windows_NT" + ifneq (,$(findstring clang,$(CC))) + # Clang for Windows doesn't support C++ Exceptions + CXXFLAGS += -fno-exceptions + CXXFLAGS += -D_HAS_EXCEPTIONS=0 + ifeq "$(VisualStudioVersion)" "14.0" + CXXFLAGS += -fms-compatibility-version=19.0 + endif + # The MSVC linker doesn't understand long section names + # generated by the clang compiler. + LDFLAGS += -fuse-ld=lld + endif +endif + +#---------------------------------------------------------------------- +# Android specific options +#---------------------------------------------------------------------- +ifeq "$(OS)" "Android" + ifdef PIE + LDFLAGS += -pie + endif + replace_with = $(if $(findstring clang,$(1)), \ + $(subst clang,$(2),$(1)), \ + $(if $(findstring gcc,$(1)), \ + $(subst gcc,$(2),$(1)), \ + $(subst cc,$(2),$(1)))) + ifeq "$(notdir $(CC))" "$(CC)" + replace_cc_with = $(call replace_with,$(CC),$(1)) + else + replace_cc_with = $(join $(dir $(CC)),$(call replace_with,$(notdir $(CC)),$(1))) + endif + OBJCOPY = $(call replace_cc_with,objcopy) + AR = $(call replace_cc_with,ar) +endif + +#---------------------------------------------------------------------- +# C++ standard library options +#---------------------------------------------------------------------- +ifeq (1,$(USE_LIBSTDCPP)) + # Clang requires an extra flag: -stdlib=libstdc++ + ifneq (,$(findstring clang,$(CC))) + CXXFLAGS += -stdlib=libstdc++ + LDFLAGS += -stdlib=libstdc++ + endif +endif + +ifeq (1,$(USE_LIBCPP)) + # Clang requires an extra flag: -stdlib=libstdc++ + ifneq (,$(findstring clang,$(CC))) + ifeq "$(OS)" "Linux" + # This is the default install location on Ubuntu 14.04 + ifneq ($(wildcard /usr/include/c++/v1/.),) + CXXFLAGS += -stdlib=libc++ -DLLDB_USING_LIBCPP + LDFLAGS += -stdlib=libc++ + CXXFLAGS += -I/usr/include/c++/v1 + endif + else + CXXFLAGS += -stdlib=libc++ -DLLDB_USING_LIBCPP + LDFLAGS += -stdlib=libc++ + endif + endif +endif + +#---------------------------------------------------------------------- +# dylib settings +#---------------------------------------------------------------------- +ifneq "$(strip $(DYLIB_C_SOURCES))" "" + DYLIB_OBJECTS +=$(strip $(DYLIB_C_SOURCES:.c=.o)) +endif + +ifneq "$(strip $(DYLIB_OBJC_SOURCES))" "" + DYLIB_OBJECTS +=$(strip $(DYLIB_OBJC_SOURCES:.m=.o)) +endif + +ifneq "$(strip $(DYLIB_CXX_SOURCES))" "" + DYLIB_OBJECTS +=$(strip $(DYLIB_CXX_SOURCES:.cpp=.o)) + CXX = $(call cxx_compiler,$(CC)) + LD = $(call cxx_linker,$(CC)) +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)) + CXX = $(call cxx_compiler,$(CC)) + LD = $(call cxx_linker,$(CC)) +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)) + CXX = $(call cxx_compiler,$(CC)) + LD = $(call cxx_linker,$(CC)) + ifeq "$(findstring lobjc,$(LDFLAGS))" "" + LDFLAGS +=-lobjc + endif +endif + +#---------------------------------------------------------------------- +# Check if we have any C source files for archive +#---------------------------------------------------------------------- +ifneq "$(strip $(ARCHIVE_C_SOURCES))" "" + ARCHIVE_OBJECTS +=$(strip $(ARCHIVE_C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files for archive +#---------------------------------------------------------------------- +ifneq "$(strip $(ARCHIVE_CXX_SOURCES))" "" + ARCHIVE_OBJECTS +=$(strip $(ARCHIVE_CXX_SOURCES:.cpp=.o)) + CXX = $(call cxx_compiler,$(CC)) + LD = $(call cxx_linker,$(CC)) +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files for archive +#---------------------------------------------------------------------- +ifneq "$(strip $(ARCHIVE_OBJC_SOURCES))" "" + ARCHIVE_OBJECTS +=$(strip $(ARCHIVE_OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files for archive +#---------------------------------------------------------------------- +ifneq "$(strip $(ARCHIVE_OBJCXX_SOURCES))" "" + ARCHIVE_OBJECTS +=$(strip $(ARCHIVE_OBJCXX_SOURCES:.mm=.o)) + CXX = $(call cxx_compiler,$(CC)) + LD = $(call cxx_linker,$(CC)) + ifeq "$(findstring lobjc,$(LDFLAGS))" "" + LDFLAGS +=-lobjc + endif +endif + +#---------------------------------------------------------------------- +# Check if we are compiling with gcc 4.6 +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES) $(OBJCXX_SOURCES))" "" +ifneq "$(filter g++,$(CXX))" "" + CXXVERSION = $(shell $(CXX) -dumpversion | cut -b 1-3) + ifeq "$(CXXVERSION)" "4.6" + # GCC 4.6 cannot handle -std=c++11, so replace it with -std=c++0x + # instead. FIXME: remove once GCC version is upgraded. + override CXXFLAGS := $(subst -std=c++11,-std=c++0x,$(CXXFLAGS)) + endif +endif +endif + +#---------------------------------------------------------------------- +# DYLIB_ONLY variable can be used to skip the building of a.out. +# See the sections below regarding dSYM file as well as the building of +# EXE from all the objects. +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable if $(MAKE_DSYM) != "NO" +#---------------------------------------------------------------------- +ifneq "$(DYLIB_ONLY)" "YES" +$(DSYM) : $(EXE) +ifeq "$(OS)" "Darwin" +ifneq "$(MAKE_DSYM)" "NO" + "$(DS)" $(DSFLAGS) -o "$(DSYM)" "$(EXE)" +endif +else +ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES" + $(OBJCOPY) --only-keep-debug "$(EXE)" "$(DSYM)" + $(OBJCOPY) --strip-debug --add-gnu-debuglink="$(DSYM)" "$(EXE)" "$(EXE)" +endif +endif +endif + +#---------------------------------------------------------------------- +# Compile the executable from all the objects. +#---------------------------------------------------------------------- +ifneq "$(DYLIB_NAME)" "" +ifeq "$(DYLIB_ONLY)" "" +$(EXE) : $(OBJECTS) $(ARCHIVE_NAME) $(DYLIB_FILENAME) + $(LD) $(OBJECTS) $(ARCHIVE_NAME) -L. -l$(DYLIB_NAME) $(LDFLAGS) -o "$(EXE)" +else +EXE = $(DYLIB_FILENAME) +endif +else +$(EXE) : $(OBJECTS) $(ARCHIVE_NAME) + $(LD) $(OBJECTS) $(LDFLAGS) $(ARCHIVE_NAME) -o "$(EXE)" +endif + +#---------------------------------------------------------------------- +# Make the archive +#---------------------------------------------------------------------- +ifneq "$(ARCHIVE_NAME)" "" +ifeq "$(OS)" "Darwin" +$(ARCHIVE_NAME) : $(ARCHIVE_OBJECTS) + $(AR) $(ARFLAGS) $(ARCHIVE_NAME) $(ARCHIVE_OBJECTS) + $(RM) $(ARCHIVE_OBJECTS) +else +$(ARCHIVE_NAME) : $(foreach ar_obj,$(ARCHIVE_OBJECTS),$(ARCHIVE_NAME)($(ar_obj))) +endif +endif + +#---------------------------------------------------------------------- +# Make the dylib +#---------------------------------------------------------------------- +$(DYLIB_OBJECTS) : CFLAGS += -DCOMPILING_LLDB_TEST_DLL + +$(DYLIB_FILENAME) : $(DYLIB_OBJECTS) +ifeq "$(OS)" "Darwin" + $(LD) $(DYLIB_OBJECTS) $(LDFLAGS) -install_name "$(DYLIB_EXECUTABLE_PATH)/$(DYLIB_FILENAME)" -dynamiclib -o "$(DYLIB_FILENAME)" +ifneq "$(MAKE_DSYM)" "NO" +ifneq "$(DS)" "" + "$(DS)" $(DSFLAGS) "$(DYLIB_FILENAME)" +endif +endif +else + $(LD) $(DYLIB_OBJECTS) $(LDFLAGS) -shared -o "$(DYLIB_FILENAME)" +ifeq "$(SPLIT_DEBUG_SYMBOLS)" "YES" + $(OBJCOPY) --only-keep-debug "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME).debug" + $(OBJCOPY) --strip-debug --add-gnu-debuglink="$(DYLIB_FILENAME).debug" "$(DYLIB_FILENAME)" "$(DYLIB_FILENAME)" +endif +endif + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an object's 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) +DWOS := $(OBJECTS:.o=.dwo) $(ARCHIVE_OBJECTS:.o=.dwo) +ifneq "$(DYLIB_NAME)" "" + DYLIB_PREREQS := $(DYLIB_OBJECTS:.o=.d) + DYLIB_DWOS := $(DYLIB_OBJECTS:.o=.dwo) +endif + +#---------------------------------------------------------------------- +# 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. +#---------------------------------------------------------------------- +ifeq "$(HOST_OS)" "Windows_NT" + JOIN_CMD = & + QUOTE = " +else + JOIN_CMD = ; + QUOTE = ' +endif + +%.d: %.c + @rm -f $@ $(JOIN_CMD) \ + $(CC) -M $(CFLAGS) $< > $@.tmp && \ + sed $(QUOTE)s,\($*\)\.o[ :]*,\1.o $@ : ,g$(QUOTE) < $@.tmp > $@ $(JOIN_CMD) \ + rm -f $@.tmp + +%.d: %.cpp + @rm -f $@ $(JOIN_CMD) \ + $(CXX) -M $(CXXFLAGS) $< > $@.tmp && \ + sed $(QUOTE)s,\($*\)\.o[ :]*,\1.o $@ : ,g$(QUOTE) < $@.tmp > $@ $(JOIN_CMD) \ + rm -f $@.tmp + +%.d: %.m + @rm -f $@ $(JOIN_CMD) \ + $(CC) -M $(CFLAGS) $< > $@.tmp && \ + sed $(QUOTE)s,\($*\)\.o[ :]*,\1.o $@ : ,g$(QUOTE) < $@.tmp > $@ $(JOIN_CMD) \ + rm -f $@.tmp + +%.d: %.mm + @rm -f $@ $(JOIN_CMD) \ + $(CXX) -M $(CXXFLAGS) $< > $@.tmp && \ + sed $(QUOTE)s,\($*\)\.o[ :]*,\1.o $@ : ,g$(QUOTE) < $@.tmp > $@ $(JOIN_CMD) \ + rm -f $@.tmp + +#---------------------------------------------------------------------- +# 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) +ifneq "$(DYLIB_NAME)" "" + sinclude $(DYLIB_PREREQS) +endif + +# Define a suffix rule for .mm -> .o +.SUFFIXES: .mm .o +.mm.o: + $(CXX) $(CXXFLAGS) -c $< + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean:: + $(RM) $(OBJECTS) $(PREREQS) $(PREREQS:.d=.d.tmp) $(DWOS) $(ARCHIVE_NAME) $(ARCHIVE_OBJECTS) +ifneq "$(DYLIB_NAME)" "" + $(RM) -r $(DYLIB_FILENAME).dSYM + $(RM) $(DYLIB_OBJECTS) $(DYLIB_PREREQS) $(DYLIB_PREREQS:.d=.d.tmp) $(DYLIB_DWOS) $(DYLIB_FILENAME) $(DYLIB_FILENAME).debug +endif +ifneq "$(DSYM)" "" + $(RM) -r "$(DSYM)" +endif +ifeq "$(OS)" "Windows_NT" +# http://llvm.org/pr24589 + IF EXIST "$(EXE)" del "$(EXE)" + $(RM) $(wildcard *.manifest *.pdb *.ilk) +ifneq "$(DYLIB_NAME)" "" + $(RM) $(DYLIB_NAME).lib $(DYLIB_NAME).exp +endif +else + $(RM) "$(EXE)" +endif + +#---------------------------------------------------------------------- +# From http://blog.melski.net/tag/debugging-makefiles/ +# +# Usage: make print-CC print-CXX print-LD +#---------------------------------------------------------------------- +print-%: + @echo '$*=$($*)' + @echo ' origin = $(origin $*)' + @echo ' flavor = $(flavor $*)' + @echo ' value = $(value $*)' + +### Local Variables: ### +### mode:makefile ### +### End: ### diff --git a/packages/Python/lldbsuite/test/make/test_common.h b/packages/Python/lldbsuite/test/make/test_common.h new file mode 100644 index 000000000000..6f819706366e --- /dev/null +++ b/packages/Python/lldbsuite/test/make/test_common.h @@ -0,0 +1,19 @@ +// This header is included in all the test programs (C and C++) and provides a +// hook for dealing with platform-specifics. +#if defined(_WIN32) || defined(_WIN64) +#ifdef COMPILING_LLDB_TEST_DLL +#define LLDB_TEST_API __declspec(dllexport) +#else +#define LLDB_TEST_API __declspec(dllimport) +#endif +#else +#define LLDB_TEST_API +#endif + +#if defined(__cplusplus) && defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Compiling MSVC libraries with _HAS_EXCEPTIONS=0, eliminates most but not all +// calls to __uncaught_exception. Unfortunately, it does seem to eliminate +// the delcaration of __uncaught_excpeiton. Including ensures that it is +// declared. This may not be necessary after MSVC 12. +#include +#endif diff --git a/packages/Python/lldbsuite/test/plugins/builder_base.py b/packages/Python/lldbsuite/test/plugins/builder_base.py new file mode 100644 index 000000000000..c4e3dff5301c --- /dev/null +++ b/packages/Python/lldbsuite/test/plugins/builder_base.py @@ -0,0 +1,137 @@ +""" +If the build* function is passed the compiler argument, for example, 'llvm-gcc', +it is passed as a make variable to the make command. Otherwise, we check the +LLDB_CC environment variable; if it is defined, it is passed as a make variable +to the make command. + +If neither the compiler keyword argument nor the LLDB_CC environment variable is +specified, no CC make variable is passed to the make command. The Makefile gets +to define the default CC being used. + +Same idea holds for LLDB_ARCH environment variable, which maps to the ARCH make +variable. +""" + +import os, sys +import platform +import lldbsuite.test.lldbtest as lldbtest + +def getArchitecture(): + """Returns the architecture in effect the test suite is running with.""" + return os.environ["ARCH"] if "ARCH" in os.environ else "" + +def getCompiler(): + """Returns the compiler in effect the test suite is running with.""" + return os.environ["CC"] if "CC" in os.environ else "clang" + +def getArchFlag(): + """Returns the flag required to specify the arch""" + compiler = getCompiler() + if compiler is None: + return "" + elif "gcc" in compiler: + archflag = "-m" + elif "clang" in compiler: + archflag = "-arch" + else: + archflag = None + + return ("ARCHFLAG=" + archflag) if archflag else "" + +def getMake(): + """Returns the name for GNU make""" + if platform.system() == "FreeBSD" or platform.system() == "NetBSD": + return "gmake" + else: + return "make" + +def getArchSpec(architecture): + """ + Helper function to return the key-value string to specify the architecture + used for the make system. + """ + arch = architecture if architecture else None + if not arch and "ARCH" in os.environ: + arch = os.environ["ARCH"] + + return ("ARCH=" + arch) if arch else "" + +def getCCSpec(compiler): + """ + Helper function to return the key-value string to specify the compiler + used for the make system. + """ + cc = compiler if compiler else None + if not cc and "CC" in os.environ: + cc = os.environ["CC"] + if cc: + return "CC=\"%s\"" % cc + else: + return "" + +def getCmdLine(d): + """ + Helper function to return a properly formatted command line argument(s) + string used for the make system. + """ + + # If d is None or an empty mapping, just return an empty string. + if not d: + return "" + pattern = '%s="%s"' if "win32" in sys.platform else "%s='%s'" + + def setOrAppendVariable(k, v): + append_vars = ["CFLAGS_EXTRAS", "LD_EXTRAS"] + if k in append_vars and k in os.environ: + v = os.environ[k] + " " + v + return pattern % (k, v) + cmdline = " ".join([setOrAppendVariable(k, v) for k, v in list(d.items())]) + + return cmdline + + +def buildDefault(sender=None, architecture=None, compiler=None, dictionary=None, clean=True): + """Build the binaries the default way.""" + commands = [] + if clean: + commands.append([getMake(), "clean", getCmdLine(dictionary)]) + commands.append([getMake(), getArchSpec(architecture), getCCSpec(compiler), getCmdLine(dictionary)]) + + lldbtest.system(commands, sender=sender) + + # True signifies that we can handle building default. + return True + +def buildDwarf(sender=None, architecture=None, compiler=None, dictionary=None, clean=True): + """Build the binaries with dwarf debug info.""" + commands = [] + if clean: + commands.append([getMake(), "clean", getCmdLine(dictionary)]) + commands.append([getMake(), "MAKE_DSYM=NO", getArchSpec(architecture), getCCSpec(compiler), getCmdLine(dictionary)]) + + lldbtest.system(commands, sender=sender) + # True signifies that we can handle building dwarf. + return True + +def buildDwo(sender=None, architecture=None, compiler=None, dictionary=None, clean=True): + """Build the binaries with dwarf debug info.""" + commands = [] + if clean: + commands.append([getMake(), "clean", getCmdLine(dictionary)]) + commands.append([getMake(), "MAKE_DSYM=NO", "MAKE_DWO=YES", getArchSpec(architecture), getCCSpec(compiler), getCmdLine(dictionary)]) + + lldbtest.system(commands, sender=sender) + # True signifies that we can handle building dwo. + return True + +def cleanup(sender=None, dictionary=None): + """Perform a platform-specific cleanup after the test.""" + #import traceback + #traceback.print_stack() + commands = [] + if os.path.isfile("Makefile"): + commands.append([getMake(), "clean", getCmdLine(dictionary)]) + + lldbtest.system(commands, sender=sender) + # True signifies that we can handle cleanup. + return True diff --git a/packages/Python/lldbsuite/test/plugins/builder_darwin.py b/packages/Python/lldbsuite/test/plugins/builder_darwin.py new file mode 100644 index 000000000000..dd07206e323b --- /dev/null +++ b/packages/Python/lldbsuite/test/plugins/builder_darwin.py @@ -0,0 +1,21 @@ + +from __future__ import print_function +import os +import lldbsuite.test.lldbtest as lldbtest + +from builder_base import * + +#print("Hello, darwin plugin!") + +def buildDsym(sender=None, architecture=None, compiler=None, dictionary=None, clean=True): + """Build the binaries with dsym debug info.""" + commands = [] + + if clean: + commands.append(["make", "clean", getCmdLine(dictionary)]) + commands.append(["make", "MAKE_DSYM=YES", getArchSpec(architecture), getCCSpec(compiler), getCmdLine(dictionary)]) + + lldbtest.system(commands, sender=sender) + + # True signifies that we can handle building dsym. + return True diff --git a/packages/Python/lldbsuite/test/plugins/builder_freebsd.py b/packages/Python/lldbsuite/test/plugins/builder_freebsd.py new file mode 100644 index 000000000000..e56be429823e --- /dev/null +++ b/packages/Python/lldbsuite/test/plugins/builder_freebsd.py @@ -0,0 +1,4 @@ +from builder_base import * + +def buildDsym(sender=None, architecture=None, compiler=None, dictionary=None, clean=True): + return False diff --git a/packages/Python/lldbsuite/test/plugins/builder_linux2.py b/packages/Python/lldbsuite/test/plugins/builder_linux2.py new file mode 100644 index 000000000000..e56be429823e --- /dev/null +++ b/packages/Python/lldbsuite/test/plugins/builder_linux2.py @@ -0,0 +1,4 @@ +from builder_base import * + +def buildDsym(sender=None, architecture=None, compiler=None, dictionary=None, clean=True): + return False diff --git a/packages/Python/lldbsuite/test/plugins/builder_netbsd.py b/packages/Python/lldbsuite/test/plugins/builder_netbsd.py new file mode 100644 index 000000000000..e56be429823e --- /dev/null +++ b/packages/Python/lldbsuite/test/plugins/builder_netbsd.py @@ -0,0 +1,4 @@ +from builder_base import * + +def buildDsym(sender=None, architecture=None, compiler=None, dictionary=None, clean=True): + return False diff --git a/packages/Python/lldbsuite/test/plugins/builder_win32.py b/packages/Python/lldbsuite/test/plugins/builder_win32.py new file mode 100644 index 000000000000..e56be429823e --- /dev/null +++ b/packages/Python/lldbsuite/test/plugins/builder_win32.py @@ -0,0 +1,4 @@ +from builder_base import * + +def buildDsym(sender=None, architecture=None, compiler=None, dictionary=None, clean=True): + return False diff --git a/packages/Python/lldbsuite/test/python_api/.categories b/packages/Python/lldbsuite/test/python_api/.categories new file mode 100644 index 000000000000..db8069c3b9ff --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/.categories @@ -0,0 +1 @@ +pyapi diff --git a/packages/Python/lldbsuite/test/python_api/breakpoint/Makefile b/packages/Python/lldbsuite/test/python_api/breakpoint/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/breakpoint/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/breakpoint/TestBreakpointAPI.py b/packages/Python/lldbsuite/test/python_api/breakpoint/TestBreakpointAPI.py new file mode 100644 index 000000000000..1f4fa20b8304 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/breakpoint/TestBreakpointAPI.py @@ -0,0 +1,45 @@ +""" +Test SBBreakpoint APIs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class BreakpointAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + def test_breakpoint_is_valid(self): + """Make sure that if an SBBreakpoint gets deleted its IsValid returns false.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'AFunction'. + breakpoint = target.BreakpointCreateByName('AFunction', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now delete it: + did_delete = target.BreakpointDelete(breakpoint.GetID()) + self.assertTrue (did_delete, "Did delete the breakpoint we just created.") + + # Make sure we can't find it: + del_bkpt = target.FindBreakpointByID (breakpoint.GetID()) + self.assertTrue (not del_bkpt, "We did delete the breakpoint.") + + # Finally make sure the original breakpoint is no longer valid. + self.assertTrue (not breakpoint, "Breakpoint we deleted is no longer valid.") diff --git a/packages/Python/lldbsuite/test/python_api/breakpoint/main.c b/packages/Python/lldbsuite/test/python_api/breakpoint/main.c new file mode 100644 index 000000000000..2677594e622e --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/breakpoint/main.c @@ -0,0 +1,14 @@ +#include + +void +AFunction() +{ + printf ("I am a function.\n"); +} + +int +main () +{ + AFunction(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/class_members/Makefile b/packages/Python/lldbsuite/test/python_api/class_members/Makefile new file mode 100644 index 000000000000..0d7550f9f282 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/class_members/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +OBJCXX_SOURCES := main.mm + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/class_members/TestSBTypeClassMembers.py b/packages/Python/lldbsuite/test/python_api/class_members/TestSBTypeClassMembers.py new file mode 100644 index 000000000000..e1d5520bcc3d --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/class_members/TestSBTypeClassMembers.py @@ -0,0 +1,77 @@ +""" +Test SBType APIs to fetch member function types. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class SBTypeMemberFunctionsTest(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break at. + self.source = 'main.mm' + self.line = line_number(self.source, '// set breakpoint here') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test(self): + """Test SBType APIs to fetch member function types.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + exe = os.path.join(os.getcwd(), self.exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Get Frame #0. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + + variable = frame0.FindVariable("d") + Derived = variable.GetType() + Base = Derived.GetDirectBaseClassAtIndex(0).GetType() + + self.assertTrue(Derived.GetNumberOfMemberFunctions() == 2, "Derived declares two methods") + self.assertTrue(Derived.GetMemberFunctionAtIndex(0).GetType().GetFunctionReturnType().GetName() == "int", "Derived::dImpl returns int") + + self.assertTrue(Base.GetNumberOfMemberFunctions() == 4, "Base declares three methods") + self.assertTrue(Base.GetMemberFunctionAtIndex(3).GetType().GetFunctionArgumentTypes().GetSize() == 3, "Base::sfunc takes three arguments") + self.assertTrue(Base.GetMemberFunctionAtIndex(3).GetName() == "sfunc", "Base::sfunc not found") + self.assertTrue(Base.GetMemberFunctionAtIndex(3).GetKind() == lldb.eMemberFunctionKindStaticMethod, "Base::sfunc is a static") + self.assertTrue(Base.GetMemberFunctionAtIndex(2).GetType().GetFunctionArgumentTypes().GetSize() == 0, "Base::dat takes no arguments") + self.assertTrue(Base.GetMemberFunctionAtIndex(1).GetType().GetFunctionArgumentTypes().GetTypeAtIndex(1).GetName() == "char", "Base::bar takes a second 'char' argument") + self.assertTrue(Base.GetMemberFunctionAtIndex(1).GetName() == "bar", "Base::bar not found") + + variable = frame0.FindVariable("thingy") + Thingy = variable.GetType() + + self.assertTrue(Thingy.GetNumberOfMemberFunctions() == 2, "Thingy declares two methods") + + self.assertTrue(Thingy.GetMemberFunctionAtIndex(0).GetReturnType().GetName() == "id", "Thingy::init returns an id") + self.assertTrue(Thingy.GetMemberFunctionAtIndex(1).GetNumberOfArguments() == 2, "Thingy::foo takes two arguments") + self.assertTrue(Thingy.GetMemberFunctionAtIndex(1).GetArgumentTypeAtIndex(0).GetName() == "int", "Thingy::foo takes an int") diff --git a/packages/Python/lldbsuite/test/python_api/class_members/main.mm b/packages/Python/lldbsuite/test/python_api/class_members/main.mm new file mode 100644 index 000000000000..ff61b369ee1f --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/class_members/main.mm @@ -0,0 +1,47 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +class Base { +public: + int foo(int x, int y) { return 1; } + char bar(int x, char y) { return 2; } + void dat() {} + static int sfunc(char, int, float) { return 3; } +}; + +class Derived: public Base { +protected: + int dImpl() { return 1; } +public: + float baz(float b) { return b + 1.0; } +}; + +@interface Thingy: NSObject { +} +- (id)init; +- (id)fooWithBar: (int)bar andBaz:(id)baz; +@end + +@implementation Thingy { +} +- (id)init { + return (self = [super init]); +} +- (id)fooWithBar: (int)bar andBaz:(id)baz { + return nil; +} +@end + +int main() { + Derived d; + Thingy *thingy = [[Thingy alloc] init]; + return 0; // set breakpoint here +} diff --git a/packages/Python/lldbsuite/test/python_api/debugger/TestDebuggerAPI.py b/packages/Python/lldbsuite/test/python_api/debugger/TestDebuggerAPI.py new file mode 100644 index 000000000000..98feda859ae9 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/debugger/TestDebuggerAPI.py @@ -0,0 +1,40 @@ +""" +Test Debugger APIs. +""" + +import os +import lldb +from lldbsuite.test.lldbtest import * + + +class DebuggerAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_debugger_api_boundary_condition(self): + """Exercise SBDebugger APIs with boundary conditions.""" + self.dbg.HandleCommand(None) + self.dbg.SetDefaultArchitecture(None) + self.dbg.GetScriptingLanguage(None) + self.dbg.CreateTarget(None) + self.dbg.CreateTarget(None, None, None, True, lldb.SBError()) + self.dbg.CreateTargetWithFileAndTargetTriple(None, None) + self.dbg.CreateTargetWithFileAndArch(None, None) + self.dbg.FindTargetWithFileAndArch(None, None) + self.dbg.SetInternalVariable(None, None, None) + self.dbg.GetInternalVariableValue(None, None) + # FIXME (filcab): We must first allow for the swig bindings to know if + # a Python callback is set. (Check python-typemaps.swig) + #self.dbg.SetLoggingCallback(None) + self.dbg.SetPrompt(None) + self.dbg.SetCurrentPlatform(None) + self.dbg.SetCurrentPlatformSDKRoot(None) + + @add_test_categories(['pyapi']) + def test_debugger_delete_invalid_target(self): + """SBDebugger.DeleteTarget() should not crash LLDB given and invalid target.""" + target = lldb.SBTarget() + self.assertFalse(target.IsValid()) + self.dbg.DeleteTarget(target) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/TestDefaultConstructorForAPIObjects.py b/packages/Python/lldbsuite/test/python_api/default-constructor/TestDefaultConstructorForAPIObjects.py new file mode 100644 index 000000000000..aa3a6141326a --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/TestDefaultConstructorForAPIObjects.py @@ -0,0 +1,395 @@ +""" +Test lldb Python API object's default constructor and make sure it is invalid +after initial construction. + +There are also some cases of boundary condition testings sprinkled throughout +the tests where None is passed to SB API which expects (const char *) in the +C++ API counterpart. Passing None should not crash lldb! + +There are three exceptions to the above general rules, though; API objects +SBCommadnReturnObject, SBStream, and SBSymbolContextList, are all valid objects +after default construction. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class APIDefaultConstructorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBAddress(self): + obj = lldb.SBAddress() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_address + sb_address.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBBlock(self): + obj = lldb.SBBlock() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_block + sb_block.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBBreakpoint(self): + obj = lldb.SBBreakpoint() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_breakpoint + sb_breakpoint.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBBreakpointLocation(self): + obj = lldb.SBBreakpointLocation() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_breakpointlocation + sb_breakpointlocation.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBBroadcaster(self): + obj = lldb.SBBroadcaster() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_broadcaster + sb_broadcaster.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBCommandReturnObject(self): + """SBCommandReturnObject object is valid after default construction.""" + obj = lldb.SBCommandReturnObject() + if self.TraceOn(): + print(obj) + self.assertTrue(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBCommunication(self): + obj = lldb.SBCommunication() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_communication + sb_communication.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBCompileUnit(self): + obj = lldb.SBCompileUnit() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_compileunit + sb_compileunit.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBDebugger(self): + obj = lldb.SBDebugger() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_debugger + sb_debugger.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + # darwin: This test passes with swig 3.0.2, fails w/3.0.5 other tests fail with 2.0.12 http://llvm.org/pr23488 + def test_SBError(self): + obj = lldb.SBError() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_error + sb_error.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBEvent(self): + obj = lldb.SBEvent() + # This is just to test that typemap, as defined in lldb.swig, works. + obj2 = lldb.SBEvent(0, "abc") + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_event + sb_event.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + def test_SBFileSpec(self): + obj = lldb.SBFileSpec() + # This is just to test that FileSpec(None) does not crash. + obj2 = lldb.SBFileSpec(None, True) + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_filespec + sb_filespec.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBFrame(self): + obj = lldb.SBFrame() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_frame + sb_frame.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBFunction(self): + obj = lldb.SBFunction() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_function + sb_function.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBInstruction(self): + obj = lldb.SBInstruction() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_instruction + sb_instruction.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBInstructionList(self): + obj = lldb.SBInstructionList() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_instructionlist + sb_instructionlist.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBLineEntry(self): + obj = lldb.SBLineEntry() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_lineentry + sb_lineentry.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBListener(self): + obj = lldb.SBListener() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_listener + sb_listener.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + # Py3 asserts due to a bug in SWIG. Trying to upstream a patch to fix this in 3.0.8 + @skipIf(py_version=['>=', (3,0)], swig_version=['<', (3,0,8)]) + def test_SBModule(self): + obj = lldb.SBModule() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_module + sb_module.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBProcess(self): + obj = lldb.SBProcess() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_process + sb_process.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBSection(self): + obj = lldb.SBSection() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_section + sb_section.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBStream(self): + """SBStream object is valid after default construction.""" + obj = lldb.SBStream() + if self.TraceOn(): + print(obj) + self.assertTrue(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBStringList(self): + obj = lldb.SBStringList() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_stringlist + sb_stringlist.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBSymbol(self): + obj = lldb.SBSymbol() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_symbol + sb_symbol.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBSymbolContext(self): + obj = lldb.SBSymbolContext() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_symbolcontext + sb_symbolcontext.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBSymbolContextList(self): + """SBSymbolContextList object is valid after default construction.""" + obj = lldb.SBSymbolContextList() + if self.TraceOn(): + print(obj) + self.assertTrue(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBTarget(self): + obj = lldb.SBTarget() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_target + sb_target.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBThread(self): + obj = lldb.SBThread() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_thread + sb_thread.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBType(self): + try: + obj = lldb.SBType() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # If we reach here, the test fails. + self.fail("lldb.SBType() should fail, not succeed!") + except: + # Exception is expected. + return + + # Unreachable code because lldb.SBType() should fail. + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_type + sb_type.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBTypeList(self): + """SBTypeList object is valid after default construction.""" + obj = lldb.SBTypeList() + if self.TraceOn(): + print(obj) + self.assertTrue(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBValue(self): + obj = lldb.SBValue() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_value + sb_value.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBValueList(self): + obj = lldb.SBValueList() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_valuelist + sb_valuelist.fuzz_obj(obj) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_SBWatchpoint(self): + obj = lldb.SBWatchpoint() + if self.TraceOn(): + print(obj) + self.assertFalse(obj) + # Do fuzz testing on the invalid obj, it should not crash lldb. + import sb_watchpoint + sb_watchpoint.fuzz_obj(obj) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_address.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_address.py new file mode 100644 index 000000000000..f0e979543a4c --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_address.py @@ -0,0 +1,22 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetFileAddress() + obj.GetLoadAddress(lldb.SBTarget()) + obj.SetLoadAddress(0xffff, lldb.SBTarget()) + obj.OffsetAddress(sys.maxsize) + obj.GetDescription(lldb.SBStream()) + obj.GetSection() + obj.GetSymbolContext(lldb.eSymbolContextEverything) + obj.GetModule() + obj.GetCompileUnit() + obj.GetFunction() + obj.GetBlock() + obj.GetSymbol() + obj.GetLineEntry() + obj.Clear() diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_block.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_block.py new file mode 100644 index 000000000000..3eeb24b42734 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_block.py @@ -0,0 +1,17 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.IsInlined() + obj.GetInlinedName() + obj.GetInlinedCallSiteFile() + obj.GetInlinedCallSiteLine() + obj.GetInlinedCallSiteColumn() + obj.GetParent() + obj.GetSibling() + obj.GetFirstChild() + obj.GetDescription(lldb.SBStream()) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_breakpoint.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_breakpoint.py new file mode 100644 index 000000000000..2c05990edae0 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_breakpoint.py @@ -0,0 +1,36 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetID() + obj.ClearAllBreakpointSites() + obj.FindLocationByAddress(sys.maxsize) + obj.FindLocationIDByAddress(sys.maxsize) + obj.FindLocationByID(0) + obj.GetLocationAtIndex(0) + obj.SetEnabled(True) + obj.IsEnabled() + obj.GetHitCount() + obj.SetIgnoreCount(1) + obj.GetIgnoreCount() + obj.SetCondition("i >= 10") + obj.GetCondition() + obj.SetThreadID(0) + obj.GetThreadID() + obj.SetThreadIndex(0) + obj.GetThreadIndex() + obj.SetThreadName("worker thread") + obj.GetThreadName() + obj.SetQueueName("my queue") + obj.GetQueueName() + obj.SetScriptCallbackFunction(None) + obj.SetScriptCallbackBody (None) + obj.GetNumResolvedLocations() + obj.GetNumLocations() + obj.GetDescription(lldb.SBStream()) + for bp_loc in obj: + s = str(bp_loc) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_breakpointlocation.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_breakpointlocation.py new file mode 100644 index 000000000000..2251892643b3 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_breakpointlocation.py @@ -0,0 +1,28 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetAddress() + obj.GetLoadAddress() + obj.SetEnabled(True) + obj.IsEnabled() + obj.SetCondition("i >= 10") + obj.GetCondition() + obj.SetThreadID(0) + obj.GetThreadID() + obj.SetThreadIndex(0) + obj.GetThreadIndex() + obj.SetThreadName("worker thread") + obj.GetThreadName() + obj.SetQueueName("my queue") + obj.GetQueueName() + obj.IsResolved() + obj.GetDescription(lldb.SBStream(), lldb.eDescriptionLevelVerbose) + breakpoint = obj.GetBreakpoint() + # Do fuzz testing on the breakpoint obj, it should not crash lldb. + import sb_breakpoint + sb_breakpoint.fuzz_obj(breakpoint) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_broadcaster.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_broadcaster.py new file mode 100644 index 000000000000..27539e855cdd --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_broadcaster.py @@ -0,0 +1,20 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.BroadcastEventByType(lldb.eBreakpointEventTypeInvalidType, True) + obj.BroadcastEvent(lldb.SBEvent(), False) + listener = lldb.SBListener("fuzz_testing") + obj.AddInitialEventsToListener(listener, 0xffffffff) + obj.AddInitialEventsToListener(listener, 0) + obj.AddListener(listener, 0xffffffff) + obj.AddListener(listener, 0) + obj.GetName() + obj.EventTypeHasListeners(0) + obj.RemoveListener(listener, 0xffffffff) + obj.RemoveListener(listener, 0) + obj.Clear() diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_communication.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_communication.py new file mode 100644 index 000000000000..d4b90840657b --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_communication.py @@ -0,0 +1,28 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + broadcaster = obj.GetBroadcaster() + # Do fuzz testing on the broadcaster obj, it should not crash lldb. + import sb_broadcaster + sb_broadcaster.fuzz_obj(broadcaster) + obj.AdoptFileDesriptor(0, False) + obj.AdoptFileDesriptor(1, False) + obj.AdoptFileDesriptor(2, False) + obj.Connect("file:/tmp/myfile") + obj.Connect(None) + obj.Disconnect() + obj.IsConnected() + obj.GetCloseOnEOF() + obj.SetCloseOnEOF(True) + obj.SetCloseOnEOF(False) + #obj.Write(None, sys.maxint, None) + #obj.Read(None, sys.maxint, 0xffffffff, None) + obj.ReadThreadStart() + obj.ReadThreadStop() + obj.ReadThreadIsRunning() + obj.SetReadThreadBytesReceivedCallback(None, None) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_compileunit.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_compileunit.py new file mode 100644 index 000000000000..92755ffa395d --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_compileunit.py @@ -0,0 +1,15 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetFileSpec() + obj.GetNumLineEntries() + obj.GetLineEntryAtIndex(0xffffffff) + obj.FindLineEntryIndex(0, 0xffffffff, None) + obj.GetDescription(lldb.SBStream()) + for line_entry in obj: + s = str(line_entry) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_debugger.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_debugger.py new file mode 100644 index 000000000000..e7c188f09bad --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_debugger.py @@ -0,0 +1,56 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.SetAsync(True) + obj.SetAsync(False) + obj.GetAsync() + obj.SkipLLDBInitFiles(True) + obj.SetInputFileHandle(None, True) + obj.SetOutputFileHandle(None, True) + obj.SetErrorFileHandle(None, True) + obj.GetInputFileHandle() + obj.GetOutputFileHandle() + obj.GetErrorFileHandle() + obj.GetCommandInterpreter() + obj.HandleCommand("nothing here") + listener = obj.GetListener() + obj.HandleProcessEvent(lldb.SBProcess(), lldb.SBEvent(), None, None) + obj.CreateTargetWithFileAndTargetTriple("a.out", "A-B-C") + obj.CreateTargetWithFileAndArch("b.out", "arm") + obj.CreateTarget("c.out") + obj.DeleteTarget(lldb.SBTarget()) + obj.GetTargetAtIndex(0xffffffff) + obj.FindTargetWithProcessID(0) + obj.FindTargetWithFileAndArch("a.out", "arm") + obj.GetNumTargets() + obj.GetSelectedTarget() + obj.GetSourceManager() + obj.SetSelectedTarget(lldb.SBTarget()) + obj.SetCurrentPlatformSDKRoot("tmp/sdk-root") + try: + obj.DispatchInput(None) + except Exception: + pass + obj.DispatchInputInterrupt() + obj.DispatchInputEndOfFile() + obj.GetInstanceName() + obj.GetDescription(lldb.SBStream()) + obj.GetTerminalWidth() + obj.SetTerminalWidth(0xffffffff) + obj.GetID() + obj.GetPrompt() + obj.SetPrompt("Hi, Mom!") + obj.GetScriptLanguage() + obj.SetScriptLanguage(lldb.eScriptLanguageNone) + obj.SetScriptLanguage(lldb.eScriptLanguagePython) + obj.GetCloseInputOnEOF() + obj.SetCloseInputOnEOF(True) + obj.SetCloseInputOnEOF(False) + obj.Clear() + for target in obj: + s = str(target) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_error.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_error.py new file mode 100644 index 000000000000..7e069323bf3f --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_error.py @@ -0,0 +1,25 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetCString() + obj.Fail() + obj.Success() + obj.GetError() + obj.GetType() + obj.SetError(5, lldb.eErrorTypeGeneric) + obj.SetErrorToErrno() + obj.SetErrorToGenericError() + obj.SetErrorString("xyz") + obj.SetErrorString(None) + obj.SetErrorStringWithFormat("%s!", "error") + obj.SetErrorStringWithFormat(None) + obj.SetErrorStringWithFormat("error") + obj.SetErrorStringWithFormat("%s %s", "warning", "danger") + obj.SetErrorStringWithFormat("%s %s %s", "danger", "will", "robinson") + obj.GetDescription(lldb.SBStream()) + obj.Clear() diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_event.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_event.py new file mode 100644 index 000000000000..c64f9ba927c2 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_event.py @@ -0,0 +1,17 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetDataFlavor() + obj.GetType() + broadcaster = obj.GetBroadcaster() + # Do fuzz testing on the broadcaster obj, it should not crash lldb. + import sb_broadcaster + sb_broadcaster.fuzz_obj(broadcaster) + obj.BroadcasterMatchesRef(broadcaster) + obj.GetDescription(lldb.SBStream()) + obj.Clear() diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_filespec.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_filespec.py new file mode 100644 index 000000000000..3aa9235b8a16 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_filespec.py @@ -0,0 +1,14 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.Exists() + obj.ResolveExecutableLocation() + obj.GetFilename() + obj.GetDirectory() + obj.GetPath(None, 0) + obj.GetDescription(lldb.SBStream()) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_frame.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_frame.py new file mode 100644 index 000000000000..41edaff02ed7 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_frame.py @@ -0,0 +1,37 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetFrameID() + obj.GetPC() + obj.SetPC(0xffffffff) + obj.GetSP() + obj.GetFP() + obj.GetPCAddress() + obj.GetSymbolContext(0) + obj.GetModule() + obj.GetCompileUnit() + obj.GetFunction() + obj.GetSymbol() + obj.GetBlock() + obj.GetFunctionName() + obj.IsInlined() + obj.EvaluateExpression("x + y") + obj.EvaluateExpression("x + y", lldb.eDynamicCanRunTarget) + obj.GetFrameBlock() + obj.GetLineEntry() + obj.GetThread() + obj.Disassemble() + obj.GetVariables(True, True, True, True) + obj.GetVariables(True, True, True, False, lldb.eDynamicCanRunTarget) + obj.GetRegisters() + obj.FindVariable("my_var") + obj.FindVariable("my_var", lldb.eDynamicCanRunTarget) + obj.FindValue("your_var", lldb.eValueTypeVariableGlobal) + obj.FindValue("your_var", lldb.eValueTypeVariableStatic, lldb.eDynamicCanRunTarget) + obj.GetDescription(lldb.SBStream()) + obj.Clear() diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_function.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_function.py new file mode 100644 index 000000000000..fb88d37ac19c --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_function.py @@ -0,0 +1,19 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetName() + obj.GetMangledName() + obj.GetInstructions(lldb.SBTarget()) + sa = obj.GetStartAddress() + ea = obj.GetEndAddress() + # Do fuzz testing on the address obj, it should not crash lldb. + import sb_address + sb_address.fuzz_obj(sa) + sb_address.fuzz_obj(ea) + obj.GetPrologueByteSize + obj.GetDescription(lldb.SBStream()) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_instruction.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_instruction.py new file mode 100644 index 000000000000..b961bc389e31 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_instruction.py @@ -0,0 +1,16 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetAddress() + obj.GetByteSize() + obj.DoesBranch() + obj.Print(None) + obj.GetDescription(lldb.SBStream()) + obj.EmulateWithFrame(lldb.SBFrame(), 0) + obj.DumpEmulation("armv7") + obj.TestEmulation(lldb.SBStream(), "my-file") diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_instructionlist.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_instructionlist.py new file mode 100644 index 000000000000..09d62f970480 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_instructionlist.py @@ -0,0 +1,17 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetSize() + obj.GetInstructionAtIndex(0xffffffff) + obj.AppendInstruction(lldb.SBInstruction()) + obj.Print(None) + obj.GetDescription(lldb.SBStream()) + obj.DumpEmulationForAllInstructions("armv7") + obj.Clear() + for inst in obj: + s = str(inst) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_lineentry.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_lineentry.py new file mode 100644 index 000000000000..d97f2517f4bc --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_lineentry.py @@ -0,0 +1,14 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetStartAddress() + obj.GetEndAddress() + obj.GetFileSpec() + obj.GetLine() + obj.GetColumn() + obj.GetDescription(lldb.SBStream()) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_listener.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_listener.py new file mode 100644 index 000000000000..0747547b9cfd --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_listener.py @@ -0,0 +1,23 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.AddEvent(lldb.SBEvent()) + obj.StartListeningForEvents(lldb.SBBroadcaster(), 0xffffffff) + obj.StopListeningForEvents(lldb.SBBroadcaster(), 0xffffffff) + event = lldb.SBEvent() + broadcaster = lldb.SBBroadcaster() + obj.WaitForEvent(5, event) + obj.WaitForEventForBroadcaster(5, broadcaster, event) + obj.WaitForEventForBroadcasterWithType(5, broadcaster, 0xffffffff, event) + obj.PeekAtNextEvent(event) + obj.PeekAtNextEventForBroadcaster(broadcaster, event) + obj.PeekAtNextEventForBroadcasterWithType(broadcaster, 0xffffffff, event) + obj.GetNextEvent(event) + obj.GetNextEventForBroadcaster(broadcaster, event) + obj.GetNextEventForBroadcasterWithType(broadcaster, 0xffffffff, event) + obj.HandleBroadcastEvent(event) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_module.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_module.py new file mode 100644 index 000000000000..0b9aa99167af --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_module.py @@ -0,0 +1,29 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetFileSpec() + obj.GetPlatformFileSpec() + obj.SetPlatformFileSpec(lldb.SBFileSpec()) + obj.GetUUIDString() + obj.ResolveFileAddress(sys.maxsize) + obj.ResolveSymbolContextForAddress(lldb.SBAddress(), 0) + obj.GetDescription(lldb.SBStream()) + obj.GetNumSymbols() + obj.GetSymbolAtIndex(sys.maxsize) + sc_list = obj.FindFunctions("my_func") + sc_list = obj.FindFunctions("my_func", lldb.eFunctionNameTypeAny) + obj.FindGlobalVariables(lldb.SBTarget(), "my_global_var", 1) + for section in obj.section_iter(): + s = str(section) + for symbol in obj.symbol_in_section_iter(lldb.SBSection()): + s = str(symbol) + for symbol in obj: + s = str(symbol) + obj.GetAddressByteSize() + obj.GetByteOrder() + obj.GetTriple() diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_process.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_process.py new file mode 100644 index 000000000000..40132b199580 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_process.py @@ -0,0 +1,49 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetTarget() + obj.GetByteOrder() + obj.PutSTDIN("my data") + obj.GetSTDOUT(6) + obj.GetSTDERR(6) + event = lldb.SBEvent() + obj.ReportEventState(event, None) + obj.AppendEventStateReport(event, lldb.SBCommandReturnObject()) + error = lldb.SBError() + obj.RemoteAttachToProcessWithID(123, error) + obj.RemoteLaunch(None, None, None, None, None, None, 0, False, error) + obj.GetNumThreads() + obj.GetThreadAtIndex(0) + obj.GetThreadByID(0) + obj.GetSelectedThread() + obj.SetSelectedThread(lldb.SBThread()) + obj.SetSelectedThreadByID(0) + obj.GetState() + obj.GetExitStatus() + obj.GetExitDescription() + obj.GetProcessID() + obj.GetAddressByteSize() + obj.Destroy() + obj.Continue() + obj.Stop() + obj.Kill() + obj.Detach() + obj.Signal(7) + obj.ReadMemory(0x0000ffff, 10, error) + obj.WriteMemory(0x0000ffff, "hi data", error) + obj.ReadCStringFromMemory(0x0, 128, error) + obj.ReadUnsignedFromMemory(0xff, 4, error) + obj.ReadPointerFromMemory(0xff, error) + obj.GetBroadcaster() + obj.GetDescription(lldb.SBStream()) + obj.LoadImage(lldb.SBFileSpec(), error) + obj.UnloadImage(0) + obj.Clear() + obj.GetNumSupportedHardwareWatchpoints(error) + for thread in obj: + s = str(thread) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_section.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_section.py new file mode 100644 index 000000000000..899130abe27d --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_section.py @@ -0,0 +1,22 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.IsValid() + obj.GetName() + obj.FindSubSection("hello_section_name") + obj.GetNumSubSections() + obj.GetSubSectionAtIndex(600) + obj.GetFileAddress() + obj.GetByteSize() + obj.GetFileOffset() + obj.GetFileByteSize() + obj.GetSectionData(1000, 100) + obj.GetSectionType() + obj.GetDescription(lldb.SBStream()) + for subsec in obj: + s = str(subsec) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_stringlist.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_stringlist.py new file mode 100644 index 000000000000..9d8242c9b27d --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_stringlist.py @@ -0,0 +1,17 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.AppendString("another string") + obj.AppendString(None) + obj.AppendList(None, 0) + obj.AppendList(lldb.SBStringList()) + obj.GetSize() + obj.GetStringAtIndex(0xffffffff) + obj.Clear() + for n in obj: + s = str(n) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_symbol.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_symbol.py new file mode 100644 index 000000000000..4a4c67a08804 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_symbol.py @@ -0,0 +1,16 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetName() + obj.GetMangledName() + obj.GetInstructions(lldb.SBTarget()) + obj.GetStartAddress() + obj.GetEndAddress() + obj.GetPrologueByteSize() + obj.GetType() + obj.GetDescription(lldb.SBStream()) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_symbolcontext.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_symbolcontext.py new file mode 100644 index 000000000000..01b5b7b793ea --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_symbolcontext.py @@ -0,0 +1,15 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetModule() + obj.GetCompileUnit() + obj.GetFunction() + obj.GetBlock() + obj.GetLineEntry() + obj.GetSymbol() + obj.GetDescription(lldb.SBStream()) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_target.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_target.py new file mode 100644 index 000000000000..3b01be3e3440 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_target.py @@ -0,0 +1,65 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetProcess() + listener = lldb.SBListener() + error = lldb.SBError() + obj.Launch(listener, None, None, None, None, None, None, 0, True, error) + obj.LaunchSimple(None, None, None) + obj.AttachToProcessWithID(listener, 123, error) + obj.AttachToProcessWithName(listener, 'lldb', False, error) + obj.ConnectRemote(listener, "connect://to/here", None, error) + obj.GetExecutable() + obj.GetNumModules() + obj.GetModuleAtIndex(0xffffffff) + obj.GetDebugger() + filespec = lldb.SBFileSpec() + obj.FindModule(filespec) + sc_list = obj.FindFunctions("the_func") + sc_list = obj.FindFunctions("the_func", lldb.eFunctionNameTypeAny) + obj.FindFirstType("dont_care") + obj.FindTypes("dont_care") + obj.FindFirstType(None) + obj.GetInstructions(lldb.SBAddress(), bytearray()) + obj.GetSourceManager() + obj.FindGlobalVariables("my_global_var", 1) + address = obj.ResolveLoadAddress(0xffff) + obj.ResolveSymbolContextForAddress(address, 0) + obj.BreakpointCreateByLocation("filename", 20) + obj.BreakpointCreateByLocation(filespec, 20) + obj.BreakpointCreateByName("func", None) + obj.BreakpointCreateByRegex("func.", None) + obj.BreakpointCreateByAddress(0xf0f0) + obj.GetNumBreakpoints() + obj.GetBreakpointAtIndex(0) + obj.BreakpointDelete(0) + obj.FindBreakpointByID(0) + obj.EnableAllBreakpoints() + obj.DisableAllBreakpoints() + obj.DeleteAllBreakpoints() + obj.GetNumWatchpoints() + obj.GetWatchpointAtIndex(0) + obj.DeleteWatchpoint(0) + obj.FindWatchpointByID(0) + obj.EnableAllWatchpoints() + obj.DisableAllWatchpoints() + obj.DeleteAllWatchpoints() + obj.GetAddressByteSize() + obj.GetByteOrder() + obj.GetTriple() + error = lldb.SBError() + obj.WatchAddress(123, 8, True, True, error) + obj.GetBroadcaster() + obj.GetDescription(lldb.SBStream(), lldb.eDescriptionLevelBrief) + obj.Clear() + for module in obj.module_iter(): + s = str(module) + for bp in obj.breakpoint_iter(): + s = str(bp) + for wp in obj.watchpoint_iter(): + s = str(wp) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_thread.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_thread.py new file mode 100644 index 000000000000..b69b22ea6a9f --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_thread.py @@ -0,0 +1,37 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetStopReason() + obj.GetStopReasonDataCount() + obj.GetStopReasonDataAtIndex(100) + obj.GetStopDescription(256) + obj.GetThreadID() + obj.GetIndexID() + obj.GetName() + obj.GetQueueName() + obj.StepOver(lldb.eOnlyDuringStepping) + obj.StepInto(lldb.eOnlyDuringStepping) + obj.StepOut() + frame = lldb.SBFrame() + obj.StepOutOfFrame(frame) + obj.StepInstruction(True) + filespec = lldb.SBFileSpec() + obj.StepOverUntil(frame, filespec, 1234) + obj.RunToAddress(0xabcd) + obj.Suspend() + obj.Resume() + obj.IsSuspended() + obj.GetNumFrames() + obj.GetFrameAtIndex(200) + obj.GetSelectedFrame() + obj.SetSelectedFrame(999) + obj.GetProcess() + obj.GetDescription(lldb.SBStream()) + obj.Clear() + for frame in obj: + s = str(frame) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_type.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_type.py new file mode 100644 index 000000000000..5c801c1fbf2e --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_type.py @@ -0,0 +1,22 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetName() + obj.GetByteSize() + #obj.GetEncoding(5) + obj.GetNumberChildren(True) + member = lldb.SBTypeMember() + obj.GetChildAtIndex(True, 0, member) + obj.GetChildIndexForName(True, "_member_field") + obj.IsAPointerType() + obj.GetPointeeType() + obj.GetDescription(lldb.SBStream()) + obj.IsPointerType(None) + lldb.SBType.IsPointerType(None) + for child_type in obj: + s = str(child_type) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_value.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_value.py new file mode 100644 index 000000000000..2bb8c58e2546 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_value.py @@ -0,0 +1,67 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetError() + obj.GetID() + obj.GetName() + obj.GetTypeName() + obj.GetByteSize() + obj.IsInScope() + obj.GetFormat() + obj.SetFormat(lldb.eFormatBoolean) + obj.GetValue() + obj.GetValueType() + obj.GetValueDidChange() + obj.GetSummary() + obj.GetObjectDescription() + obj.GetLocation() + obj.SetValueFromCString("my_new_value") + obj.GetChildAtIndex(1) + obj.GetChildAtIndex(2, lldb.eNoDynamicValues, False) + obj.GetIndexOfChildWithName("my_first_child") + obj.GetChildMemberWithName("my_first_child") + obj.GetChildMemberWithName("my_first_child", lldb.eNoDynamicValues) + obj.GetNumChildren() + obj.GetOpaqueType() + obj.Dereference() + obj.TypeIsPointerType() + stream = lldb.SBStream() + obj.GetDescription(stream) + obj.GetExpressionPath(stream) + obj.GetExpressionPath(stream, True) + error = lldb.SBError() + obj.Watch(True, True, False, error) + obj.WatchPointee(True, False, True, error) + for child_val in obj: + s = str(child_val) + error = lldb.SBError() + obj.GetValueAsSigned (error, 0) + obj.GetValueAsUnsigned (error, 0) + obj.GetValueAsSigned(0) + obj.GetValueAsUnsigned(0) + obj.GetDynamicValue (lldb.eNoDynamicValues) + obj.GetStaticValue () + obj.IsDynamic() + invalid_type = lldb.SBType() + obj.CreateChildAtOffset ("a", 12, invalid_type) + obj.Cast (invalid_type) + obj.CreateValueFromExpression ("pt->x", "pt->x") + obj.CreateValueFromAddress ("x", 0x123, invalid_type) + invalid_data = lldb.SBData() + obj.CreateValueFromData ("x", invalid_data, invalid_type) + obj.GetValueForExpressionPath("[0]") + obj.AddressOf() + obj.GetLoadAddress() + obj.GetAddress() + obj.GetPointeeData (0, 1) + obj.GetData () + obj.GetTarget() + obj.GetProcess() + obj.GetThread() + obj.GetFrame() + obj.GetType() diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_valuelist.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_valuelist.py new file mode 100644 index 000000000000..32f12f93dd81 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_valuelist.py @@ -0,0 +1,14 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.Append(lldb.SBValue()) + obj.GetSize() + obj.GetValueAtIndex(100) + obj.FindValueObjectByUID(200) + for val in obj: + s = str(val) diff --git a/packages/Python/lldbsuite/test/python_api/default-constructor/sb_watchpoint.py b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_watchpoint.py new file mode 100644 index 000000000000..f462e62ff164 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/default-constructor/sb_watchpoint.py @@ -0,0 +1,21 @@ +""" +Fuzz tests an object after the default construction to make sure it does not crash lldb. +""" + +import sys +import lldb + +def fuzz_obj(obj): + obj.GetID() + obj.IsValid() + obj.GetHardwareIndex() + obj.GetWatchAddress() + obj.GetWatchSize() + obj.SetEnabled(True) + obj.IsEnabled() + obj.GetHitCount() + obj.GetIgnoreCount() + obj.SetIgnoreCount(5) + obj.GetDescription(lldb.SBStream(), lldb.eDescriptionLevelVerbose) + obj.SetCondition("shouldWeStop()") + obj.GetCondition() diff --git a/packages/Python/lldbsuite/test/python_api/disassemble-raw-data/TestDisassembleRawData.py b/packages/Python/lldbsuite/test/python_api/disassemble-raw-data/TestDisassembleRawData.py new file mode 100644 index 000000000000..20333abc57af --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/disassemble-raw-data/TestDisassembleRawData.py @@ -0,0 +1,39 @@ +""" +Use lldb Python API to disassemble raw machine code bytes +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class DisassembleRawDataTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_disassemble_raw_data(self): + """Test disassembling raw bytes with the API.""" + # Create a target from the debugger. + target = self.dbg.CreateTargetWithFileAndTargetTriple ("", "x86_64") + self.assertTrue(target, VALID_TARGET) + + raw_bytes = bytearray([0x48, 0x89, 0xe5]) + + insts = target.GetInstructions(lldb.SBAddress(0, target), raw_bytes) + + inst = insts.GetInstructionAtIndex(0) + + if self.TraceOn(): + print() + print("Raw bytes: ", [hex(x) for x in raw_bytes]) + print("Disassembled%s" % str(inst)) + + self.assertTrue (inst.GetMnemonic(target) == "movq") + self.assertTrue (inst.GetOperands(target) == '%' + "rsp, " + '%' + "rbp") diff --git a/packages/Python/lldbsuite/test/python_api/disassemble-raw-data/TestDisassemble_VST1_64.py b/packages/Python/lldbsuite/test/python_api/disassemble-raw-data/TestDisassemble_VST1_64.py new file mode 100644 index 000000000000..e24b2ee5613a --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/disassemble-raw-data/TestDisassemble_VST1_64.py @@ -0,0 +1,58 @@ +""" +Use lldb Python API to disassemble raw machine code bytes +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class Disassemble_VST1_64(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIf(True) # llvm.org/pr24575: all tests get ERRORs in dotest.py after this + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_disassemble_invalid_vst_1_64_raw_data(self): + """Test disassembling invalid vst1.64 raw bytes with the API.""" + # Create a target from the debugger. + target = self.dbg.CreateTargetWithFileAndTargetTriple ("", "thumbv7") + self.assertTrue(target, VALID_TARGET) + + raw_bytes = bytearray([0xf0, 0xb5, 0x03, 0xaf, + 0x2d, 0xe9, 0x00, 0x0d, + 0xad, 0xf1, 0x40, 0x04, + 0x24, 0xf0, 0x0f, 0x04, + 0xa5, 0x46]) + + insts = target.GetInstructions(lldb.SBAddress(), raw_bytes) + + if self.TraceOn(): + print() + for i in insts: + print("Disassembled%s" % str(i)) + + # Remove the following return statement when the radar is fixed. + return + + # rdar://problem/11034702 + # VST1 (multiple single elements) encoding? + # The disassembler should not crash! + raw_bytes = bytearray([0x04, 0xf9, 0xed, 0x82]) + + insts = target.GetInstructions(lldb.SBAddress(), raw_bytes) + + inst = insts.GetInstructionAtIndex(0) + + if self.TraceOn(): + print() + print("Raw bytes: ", [hex(x) for x in raw_bytes]) + print("Disassembled%s" % str(inst)) + + self.assertTrue (inst.GetMnemonic(target) == "vst1.64") diff --git a/packages/Python/lldbsuite/test/python_api/event/Makefile b/packages/Python/lldbsuite/test/python_api/event/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/event/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/event/TestEvents.py b/packages/Python/lldbsuite/test/python_api/event/TestEvents.py new file mode 100644 index 000000000000..0a1a0dfda7d2 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/event/TestEvents.py @@ -0,0 +1,288 @@ +""" +Test lldb Python event APIs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +@skipIfDarwin # llvm.org/pr25924, sometimes generating SIGSEGV +class EventAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.line = line_number('main.c', '// Find the line number of function "c" here.') + + @add_test_categories(['pyapi']) + @expectedFailureLinux("llvm.org/pr23730") # Flaky, fails ~1/10 cases + @skipIfLinux # skip to avoid crashes + def test_listen_for_and_print_event(self): + """Exercise SBEvent API.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + self.dbg.SetAsync(True) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + + listener = lldb.SBListener("my listener") + + # Now launch the process, and do not stop at the entry point. + error = lldb.SBError() + process = target.Launch (listener, + None, # argv + None, # envp + None, # stdin_path + None, # stdout_path + None, # stderr_path + None, # working directory + 0, # launch flags + False, # Stop at entry + error) # error + + self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) + + # Create an empty event object. + event = lldb.SBEvent() + + traceOn = self.TraceOn() + if traceOn: + lldbutil.print_stacktraces(process) + + # Create MyListeningThread class to wait for any kind of event. + import threading + class MyListeningThread(threading.Thread): + def run(self): + count = 0 + # Let's only try at most 4 times to retrieve any kind of event. + # After that, the thread exits. + while not count > 3: + if traceOn: + print("Try wait for event...") + if listener.WaitForEvent(5, event): + if traceOn: + desc = lldbutil.get_description(event) + print("Event description:", desc) + print("Event data flavor:", event.GetDataFlavor()) + print("Process state:", lldbutil.state_type_to_str(process.GetState())) + print() + else: + if traceOn: + print("timeout occurred waiting for event...") + count = count + 1 + return + + # Let's start the listening thread to retrieve the events. + my_thread = MyListeningThread() + my_thread.start() + + # Use Python API to continue the process. The listening thread should be + # able to receive the state changed events. + process.Continue() + + # Use Python API to kill the process. The listening thread should be + # able to receive the state changed event, too. + process.Kill() + + # Wait until the 'MyListeningThread' terminates. + my_thread.join() + + @add_test_categories(['pyapi']) + @expectedFlakeyLinux("llvm.org/pr23730") # Flaky, fails ~1/100 cases + def test_wait_for_event(self): + """Exercise SBListener.WaitForEvent() API.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + self.dbg.SetAsync(True) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Get the debugger listener. + listener = self.dbg.GetListener() + + # Now launch the process, and do not stop at entry point. + error = lldb.SBError() + process = target.Launch (listener, + None, # argv + None, # envp + None, # stdin_path + None, # stdout_path + None, # stderr_path + None, # working directory + 0, # launch flags + False, # Stop at entry + error) # error + self.assertTrue(error.Success() and process, PROCESS_IS_VALID) + + # Create an empty event object. + event = lldb.SBEvent() + self.assertFalse(event, "Event should not be valid initially") + + # Create MyListeningThread to wait for any kind of event. + import threading + class MyListeningThread(threading.Thread): + def run(self): + count = 0 + # Let's only try at most 3 times to retrieve any kind of event. + while not count > 3: + if listener.WaitForEvent(5, event): + #print("Got a valid event:", event) + #print("Event data flavor:", event.GetDataFlavor()) + #print("Event type:", lldbutil.state_type_to_str(event.GetType())) + return + count = count + 1 + print("Timeout: listener.WaitForEvent") + + return + + # Use Python API to kill the process. The listening thread should be + # able to receive a state changed event. + process.Kill() + + # Let's start the listening thread to retrieve the event. + my_thread = MyListeningThread() + my_thread.start() + + # Wait until the 'MyListeningThread' terminates. + my_thread.join() + + self.assertTrue(event, + "My listening thread successfully received an event") + + @skipIfFreeBSD # llvm.org/pr21325 + @add_test_categories(['pyapi']) + @expectedFlakeyLinux("llvm.org/pr23617") # Flaky, fails ~1/10 cases + @expectedFailureWindows("llvm.org/pr24778") + def test_add_listener_to_broadcaster(self): + """Exercise some SBBroadcaster APIs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + self.dbg.SetAsync(True) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + listener = lldb.SBListener("my listener") + + # Now launch the process, and do not stop at the entry point. + error = lldb.SBError() + process = target.Launch (listener, + None, # argv + None, # envp + None, # stdin_path + None, # stdout_path + None, # stderr_path + None, # working directory + 0, # launch flags + False, # Stop at entry + error) # error + + # Create an empty event object. + event = lldb.SBEvent() + self.assertFalse(event, "Event should not be valid initially") + + + # The finite state machine for our custom listening thread, with an + # initial state of None, which means no event has been received. + # It changes to 'connected' after 'connected' event is received (for remote platforms) + # It changes to 'running' after 'running' event is received (should happen only if the + # currentstate is either 'None' or 'connected') + # It changes to 'stopped' if a 'stopped' event is received (should happen only if the + # current state is 'running'.) + self.state = None + + # Create MyListeningThread to wait for state changed events. + # By design, a "running" event is expected following by a "stopped" event. + import threading + class MyListeningThread(threading.Thread): + def run(self): + #print("Running MyListeningThread:", self) + + # Regular expression pattern for the event description. + pattern = re.compile("data = {.*, state = (.*)}$") + + # Let's only try at most 6 times to retrieve our events. + count = 0 + while True: + if listener.WaitForEvent(5, event): + desc = lldbutil.get_description(event) + #print("Event description:", desc) + match = pattern.search(desc) + if not match: + break; + if match.group(1) == 'connected': + # When debugging remote targets with lldb-server, we + # first get the 'connected' event. + self.context.assertTrue(self.context.state == None) + self.context.state = 'connected' + continue + elif match.group(1) == 'running': + self.context.assertTrue(self.context.state == None or self.context.state == 'connected') + self.context.state = 'running' + continue + elif match.group(1) == 'stopped': + self.context.assertTrue(self.context.state == 'running') + # Whoopee, both events have been received! + self.context.state = 'stopped' + break + else: + break + print("Timeout: listener.WaitForEvent") + count = count + 1 + if count > 6: + break + + return + + # Use Python API to continue the process. The listening thread should be + # able to receive the state changed events. + process.Continue() + + # Start the listening thread to receive the "running" followed by the + # "stopped" events. + my_thread = MyListeningThread() + # Supply the enclosing context so that our listening thread can access + # the 'state' variable. + my_thread.context = self + my_thread.start() + + # Wait until the 'MyListeningThread' terminates. + my_thread.join() + + # The final judgement. :-) + self.assertTrue(self.state == 'stopped', + "Both expected state changed events received") diff --git a/packages/Python/lldbsuite/test/python_api/event/main.c b/packages/Python/lldbsuite/test/python_api/event/main.c new file mode 100644 index 000000000000..343526542d7e --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/event/main.c @@ -0,0 +1,49 @@ +//===-- 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 + +// This simple program is to test the lldb Python API related to events. + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); + + return val; +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; // Find the line number of function "c" here. +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/exprpath_synthetic/TestExprPathSynthetic.py b/packages/Python/lldbsuite/test/python_api/exprpath_synthetic/TestExprPathSynthetic.py new file mode 100644 index 000000000000..80305e303d03 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/exprpath_synthetic/TestExprPathSynthetic.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.skipIfFreeBSD,lldbtest.skipIfLinux,lldbtest.skipIfWindows]) diff --git a/packages/Python/lldbsuite/test/python_api/exprpath_synthetic/main.mm b/packages/Python/lldbsuite/test/python_api/exprpath_synthetic/main.mm new file mode 100644 index 000000000000..f7383a5a14de --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/exprpath_synthetic/main.mm @@ -0,0 +1,20 @@ +//===-- main.mm --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#import +#include + +int main (int argc, char const *argv[]) +{ + std::vector v{1,2,3,4,5}; + NSArray *a = @[@"Hello",@"World",@"From Me"]; + return 0; //% v = self.frame().FindVariable("v"); v0 = v.GetChildAtIndex(0); s = lldb.SBStream(); v0.GetExpressionPath(s); + //% self.runCmd("expr %s = 12" % s.GetData()); self.assertTrue(v0.GetValueAsUnsigned() == 12, "value change via expr failed") + //% a = self.frame().FindVariable("a"); a1 = a.GetChildAtIndex(1); s = lldb.SBStream(); a1.GetExpressionPath(s); + //% self.expect("po %s" % s.GetData(), substrs = ["World"]) +} diff --git a/packages/Python/lldbsuite/test/python_api/findvalue_duplist/Makefile b/packages/Python/lldbsuite/test/python_api/findvalue_duplist/Makefile new file mode 100644 index 000000000000..ddffdcfb62d6 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/findvalue_duplist/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +# Clean renamed executable on 'make clean' +clean: OBJECTS+=no_synth + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/findvalue_duplist/TestSBFrameFindValue.py b/packages/Python/lldbsuite/test/python_api/findvalue_duplist/TestSBFrameFindValue.py new file mode 100644 index 000000000000..96d4f51fd097 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/findvalue_duplist/TestSBFrameFindValue.py @@ -0,0 +1,50 @@ +"""Test that SBFrame::FindValue finds things but does not duplicate the entire variables list""" + +from __future__ import print_function + + + +import os, sys, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SBFrameFindValueTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + def test_formatters_api(self): + """Test that SBFrame::FindValue finds things but does not duplicate the entire variables list""" + self.build() + self.setTearDownCleanup() + + exe_name = "a.out" + exe = os.path.join(os.getcwd(), exe_name) + + # Create the target + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Set the breakpoints + breakpoint = target.BreakpointCreateBySourceRegex('Set breakpoint here', lldb.SBFileSpec("main.cpp")) + self.assertTrue(breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be at our breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + + self.assertTrue(len(threads) == 1) + self.thread = threads[0] + self.frame = self.thread.frames[0] + self.assertTrue(self.frame, "Frame 0 is valid.") + + self.assertTrue(self.frame.GetVariables(True,True,False,True).GetSize() == 2, "variable count is off") + self.assertFalse(self.frame.FindValue("NoSuchThing",lldb.eValueTypeVariableArgument,lldb.eDynamicCanRunTarget).IsValid(), "found something that should not be here") + self.assertTrue(self.frame.GetVariables(True,True,False,True).GetSize() == 2, "variable count is off after failed FindValue()") + self.assertTrue(self.frame.FindValue("a",lldb.eValueTypeVariableArgument,lldb.eDynamicCanRunTarget).IsValid(), "FindValue() didn't find an argument") + self.assertTrue(self.frame.GetVariables(True,True,False,True).GetSize() == 2, "variable count is off after successful FindValue()") diff --git a/packages/Python/lldbsuite/test/python_api/findvalue_duplist/main.cpp b/packages/Python/lldbsuite/test/python_api/findvalue_duplist/main.cpp new file mode 100644 index 000000000000..7058d46b04a5 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/findvalue_duplist/main.cpp @@ -0,0 +1,7 @@ +int foo(int a, int b) { + return a + b; // Set breakpoint here +} + +int main() { + return foo(1,3); +} diff --git a/packages/Python/lldbsuite/test/python_api/formatters/Makefile b/packages/Python/lldbsuite/test/python_api/formatters/Makefile new file mode 100644 index 000000000000..ddffdcfb62d6 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/formatters/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +# Clean renamed executable on 'make clean' +clean: OBJECTS+=no_synth + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/formatters/TestFormattersSBAPI.py b/packages/Python/lldbsuite/test/python_api/formatters/TestFormattersSBAPI.py new file mode 100644 index 000000000000..de7f15f76faf --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/formatters/TestFormattersSBAPI.py @@ -0,0 +1,346 @@ +"""Test Python APIs for working with formatters""" + +from __future__ import print_function + + + +import os, sys, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SBFormattersAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + self.line = line_number('main.cpp', '// Set break point at this line.') + + @add_test_categories(['pyapi']) + def test_formatters_api(self): + """Test Python APIs for working with formatters""" + self.build() + self.setTearDownCleanup() + + """Test Python APIs for working with formatters""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synthetic clear', check=False) + self.runCmd('type category delete foobar', check=False) + self.runCmd('type category delete JASSynth', check=False) + self.runCmd('type category delete newbar', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + + format = lldb.SBTypeFormat(lldb.eFormatHex) + category = self.dbg.GetDefaultCategory() + category.AddTypeFormat(lldb.SBTypeNameSpecifier("int"),format) + + self.expect("frame variable foo.A", + substrs = ['0x00000001']) + self.expect("frame variable foo.E", matching=False, + substrs = ['b8cca70a']) + + category.AddTypeFormat(lldb.SBTypeNameSpecifier("long"),format) + self.expect("frame variable foo.A", + substrs = ['0x00000001']) + self.expect("frame variable foo.E", + substrs = ['b8cca70a']) + + format.format = lldb.eFormatOctal + category.AddTypeFormat(lldb.SBTypeNameSpecifier("int"),format) + self.expect("frame variable foo.A", + substrs = ['01']) + self.expect("frame variable foo.E", + substrs = ['b8cca70a']) + + category.DeleteTypeFormat(lldb.SBTypeNameSpecifier("int")) + category.DeleteTypeFormat(lldb.SBTypeNameSpecifier("long")) + self.expect("frame variable foo.A", matching=False, + substrs = ['01']) + self.expect("frame variable foo.E", matching=False, + substrs = ['b8cca70a']) + + summary = lldb.SBTypeSummary.CreateWithSummaryString("the hello world you'll never see") + summary.SetSummaryString('hello world') + new_category = self.dbg.GetCategory("foobar") + self.assertFalse(new_category.IsValid(), "getting a non-existing category worked") + new_category = self.dbg.CreateCategory("foobar") + new_category.enabled = True + new_category.AddTypeSummary(lldb.SBTypeNameSpecifier("^.*t$",True),summary) + self.expect("frame variable foo.A", + substrs = ['hello world']) + self.expect("frame variable foo.E", matching=False, + substrs = ['hello world']) + self.expect("frame variable foo.B", + substrs = ['hello world']) + self.expect("frame variable foo.F", + substrs = ['hello world']) + new_category.enabled = False + self.expect("frame variable foo.A", matching=False, + substrs = ['hello world']) + self.expect("frame variable foo.E", matching=False, + substrs = ['hello world']) + self.expect("frame variable foo.B", matching=False, + substrs = ['hello world']) + self.expect("frame variable foo.F", matching=False, + substrs = ['hello world']) + self.dbg.DeleteCategory(new_category.GetName()) + self.expect("frame variable foo.A", matching=False, + substrs = ['hello world']) + self.expect("frame variable foo.E", matching=False, + substrs = ['hello world']) + self.expect("frame variable foo.B", matching=False, + substrs = ['hello world']) + self.expect("frame variable foo.F", matching=False, + substrs = ['hello world']) + + filter = lldb.SBTypeFilter(0) + filter.AppendExpressionPath("A") + filter.AppendExpressionPath("D") + self.assertTrue(filter.GetNumberOfExpressionPaths() == 2, "filter with two items does not have two items") + + category.AddTypeFilter(lldb.SBTypeNameSpecifier("JustAStruct"),filter) + self.expect("frame variable foo", + substrs = ['A = 1', 'D = 6.28']) + self.expect("frame variable foo", matching=False, + substrs = ['B = ', 'C = ', 'E = ', 'F = ']) + + category.DeleteTypeFilter(lldb.SBTypeNameSpecifier("JustAStruct",True)) + self.expect("frame variable foo", + substrs = ['A = 1', 'D = 6.28']) + self.expect("frame variable foo", matching=False, + substrs = ['B = ', 'C = ', 'E = ', 'F = ']) + + category.DeleteTypeFilter(lldb.SBTypeNameSpecifier("JustAStruct",False)) + self.expect("frame variable foo", + substrs = ['A = 1', 'D = 6.28']) + self.expect("frame variable foo", matching=True, + substrs = ['B = ', 'C = ', 'E = ', 'F = ']) + + self.runCmd("command script import --allow-reload ./synth.py") + + self.expect("frame variable foo", matching=False, + substrs = ['X = 1']) + + self.dbg.GetCategory("JASSynth").SetEnabled(True) + self.expect("frame variable foo", matching=True, + substrs = ['X = 1']) + + self.dbg.GetCategory("CCCSynth").SetEnabled(True) + self.expect("frame variable ccc", matching=True, + substrs = ['CCC object with leading value (int) a = 111', 'a = 111', 'b = 222', 'c = 333']) + + foo_var = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('foo') + self.assertTrue(foo_var.IsValid(), 'could not find foo') + self.assertTrue(foo_var.GetDeclaration().IsValid(), 'foo declaration is invalid') + + self.assertTrue(foo_var.GetNumChildren() == 2, 'synthetic value has wrong number of child items (synth)') + self.assertTrue(foo_var.GetChildMemberWithName('X').GetValueAsUnsigned() == 1, 'foo_synth.X has wrong value (synth)') + self.assertFalse(foo_var.GetChildMemberWithName('B').IsValid(), 'foo_synth.B is valid but should not (synth)') + + self.dbg.GetCategory("JASSynth").SetEnabled(False) + foo_var = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('foo') + self.assertTrue(foo_var.IsValid(), 'could not find foo') + + self.assertFalse(foo_var.GetNumChildren() == 2, 'still seeing synthetic value') + + filter = lldb.SBTypeFilter(0) + filter.AppendExpressionPath("A") + filter.AppendExpressionPath("D") + category.AddTypeFilter(lldb.SBTypeNameSpecifier("JustAStruct"),filter) + self.expect("frame variable foo", + substrs = ['A = 1', 'D = 6.28']) + + foo_var = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('foo') + self.assertTrue(foo_var.IsValid(), 'could not find foo') + + self.assertTrue(foo_var.GetNumChildren() == 2, 'synthetic value has wrong number of child items (filter)') + self.assertTrue(foo_var.GetChildMemberWithName('X').GetValueAsUnsigned() == 0, 'foo_synth.X has wrong value (filter)') + self.assertTrue(foo_var.GetChildMemberWithName('A').GetValueAsUnsigned() == 1, 'foo_synth.A has wrong value (filter)') + + self.assertTrue(filter.ReplaceExpressionPathAtIndex(0,"C"), "failed to replace an expression path in filter") + self.expect("frame variable foo", + substrs = ['A = 1', 'D = 6.28']) + category.AddTypeFilter(lldb.SBTypeNameSpecifier("JustAStruct"),filter) + self.expect("frame variable foo", + substrs = ["C = 'e'", 'D = 6.28']) + category.AddTypeFilter(lldb.SBTypeNameSpecifier("FooType"),filter) + filter.ReplaceExpressionPathAtIndex(1,"F") + self.expect("frame variable foo", + substrs = ["C = 'e'", 'D = 6.28']) + category.AddTypeFilter(lldb.SBTypeNameSpecifier("JustAStruct"),filter) + self.expect("frame variable foo", + substrs = ["C = 'e'", 'F = 0']) + self.expect("frame variable bar", + substrs = ["C = 'e'", 'D = 6.28']) + + foo_var = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().FindVariable('foo') + self.assertTrue(foo_var.IsValid(), 'could not find foo') + self.assertTrue(foo_var.GetChildMemberWithName('C').GetValueAsUnsigned() == ord('e'), 'foo_synth.C has wrong value (filter)') + + chosen = self.dbg.GetFilterForType(lldb.SBTypeNameSpecifier("JustAStruct")) + self.assertTrue(chosen.count == 2, "wrong filter found for JustAStruct") + self.assertTrue(chosen.GetExpressionPathAtIndex(0) == 'C', "wrong item at index 0 for JustAStruct") + self.assertTrue(chosen.GetExpressionPathAtIndex(1) == 'F', "wrong item at index 1 for JustAStruct") + + self.assertFalse(category.DeleteTypeFilter(lldb.SBTypeNameSpecifier("NoSuchType")),"deleting a non-existing filter worked") + self.assertFalse(category.DeleteTypeSummary(lldb.SBTypeNameSpecifier("NoSuchType")),"deleting a non-existing summary worked") + self.assertFalse(category.DeleteTypeFormat(lldb.SBTypeNameSpecifier("NoSuchType")),"deleting a non-existing format worked") + self.assertFalse(category.DeleteTypeSynthetic(lldb.SBTypeNameSpecifier("NoSuchType")),"deleting a non-existing synthetic worked") + + self.assertFalse(category.DeleteTypeFilter(lldb.SBTypeNameSpecifier("")),"deleting a filter for '' worked") + self.assertFalse(category.DeleteTypeSummary(lldb.SBTypeNameSpecifier("")),"deleting a summary for '' worked") + self.assertFalse(category.DeleteTypeFormat(lldb.SBTypeNameSpecifier("")),"deleting a format for '' worked") + self.assertFalse(category.DeleteTypeSynthetic(lldb.SBTypeNameSpecifier("")),"deleting a synthetic for '' worked") + + try: + self.assertFalse(category.AddTypeSummary(lldb.SBTypeNameSpecifier("NoneSuchType"), None), "adding a summary valued None worked") + except: + pass + else: + self.assertFalse(True, "adding a summary valued None worked") + + try: + self.assertFalse(category.AddTypeFilter(lldb.SBTypeNameSpecifier("NoneSuchType"), None), "adding a filter valued None worked") + except: + pass + else: + self.assertFalse(True, "adding a filter valued None worked") + + try: + self.assertFalse(category.AddTypeSynthetic(lldb.SBTypeNameSpecifier("NoneSuchType"), None), "adding a synthetic valued None worked") + except: + pass + else: + self.assertFalse(True, "adding a synthetic valued None worked") + + try: + self.assertFalse(category.AddTypeFormat(lldb.SBTypeNameSpecifier("NoneSuchType"), None), "adding a format valued None worked") + except: + pass + else: + self.assertFalse(True, "adding a format valued None worked") + + + self.assertFalse(category.AddTypeSummary(lldb.SBTypeNameSpecifier("EmptySuchType"), lldb.SBTypeSummary()), "adding a summary without value worked") + self.assertFalse(category.AddTypeFilter(lldb.SBTypeNameSpecifier("EmptySuchType"), lldb.SBTypeFilter()), "adding a filter without value worked") + self.assertFalse(category.AddTypeSynthetic(lldb.SBTypeNameSpecifier("EmptySuchType"), lldb.SBTypeSynthetic()), "adding a synthetic without value worked") + self.assertFalse(category.AddTypeFormat(lldb.SBTypeNameSpecifier("EmptySuchType"), lldb.SBTypeFormat()), "adding a format without value worked") + + self.assertFalse(category.AddTypeSummary(lldb.SBTypeNameSpecifier(""), lldb.SBTypeSummary.CreateWithSummaryString("")), "adding a summary for an invalid type worked") + self.assertFalse(category.AddTypeFilter(lldb.SBTypeNameSpecifier(""), lldb.SBTypeFilter(0)), "adding a filter for an invalid type worked") + self.assertFalse(category.AddTypeSynthetic(lldb.SBTypeNameSpecifier(""), lldb.SBTypeSynthetic.CreateWithClassName("")), "adding a synthetic for an invalid type worked") + self.assertFalse(category.AddTypeFormat(lldb.SBTypeNameSpecifier(""), lldb.SBTypeFormat(lldb.eFormatHex)), "adding a format for an invalid type worked") + + new_category = self.dbg.CreateCategory("newbar") + new_category.AddTypeSummary(lldb.SBTypeNameSpecifier("JustAStruct"), + lldb.SBTypeSummary.CreateWithScriptCode("return 'hello scripted world';")) + self.expect("frame variable foo", matching=False, + substrs = ['hello scripted world']) + new_category.enabled = True + self.expect("frame variable foo", matching=True, + substrs = ['hello scripted world']) + + self.expect("frame variable foo_ptr", matching=True, + substrs = ['hello scripted world']) + new_category.AddTypeSummary(lldb.SBTypeNameSpecifier("JustAStruct"), + lldb.SBTypeSummary.CreateWithScriptCode("return 'hello scripted world';", + lldb.eTypeOptionSkipPointers)) + self.expect("frame variable foo", matching=True, + substrs = ['hello scripted world']) + + frame = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + foo_ptr = frame.FindVariable("foo_ptr") + summary = foo_ptr.GetTypeSummary() + + self.assertFalse(summary.IsValid(), "summary found for foo* when none was planned") + + self.expect("frame variable foo_ptr", matching=False, + substrs = ['hello scripted world']) + + new_category.AddTypeSummary(lldb.SBTypeNameSpecifier("JustAStruct"), + lldb.SBTypeSummary.CreateWithSummaryString("hello static world", + lldb.eTypeOptionNone)) + + summary = foo_ptr.GetTypeSummary() + + self.assertTrue(summary.IsValid(), "no summary found for foo* when one was in place") + self.assertTrue(summary.GetData() == "hello static world", "wrong summary found for foo*") + + self.expect("frame variable e1", substrs=["I am an empty Empty1 {}"]) + self.expect("frame variable e2", substrs=["I am an empty Empty2"]) + self.expect("frame variable e2", substrs=["I am an empty Empty2 {}"], matching=False) + + self.assertTrue(self.dbg.GetCategory(lldb.eLanguageTypeObjC) is not None, "ObjC category is None") + + @add_test_categories(['pyapi']) + def test_force_synth_off(self): + """Test that one can have the public API return non-synthetic SBValues if desired""" + self.build(dictionary={'EXE':'no_synth'}) + self.setTearDownCleanup() + + self.runCmd("file no_synth", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synthetic clear', check=False) + self.runCmd('type category delete foobar', check=False) + self.runCmd('type category delete JASSynth', check=False) + self.runCmd('type category delete newbar', check=False) + self.runCmd('settings set target.enable-synthetic-value true') + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + frame = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + int_vector = frame.FindVariable("int_vector") + if self.TraceOn(): + print(int_vector) + self.assertTrue(int_vector.GetNumChildren() == 0, 'synthetic vector is empty') + + self.runCmd('settings set target.enable-synthetic-value false') + frame = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + int_vector = frame.FindVariable("int_vector") + if self.TraceOn(): + print(int_vector) + self.assertFalse(int_vector.GetNumChildren() == 0, '"physical" vector is not empty') + + self.runCmd('settings set target.enable-synthetic-value true') + frame = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + int_vector = frame.FindVariable("int_vector") + if self.TraceOn(): + print(int_vector) + self.assertTrue(int_vector.GetNumChildren() == 0, 'synthetic vector is still empty') diff --git a/packages/Python/lldbsuite/test/python_api/formatters/main.cpp b/packages/Python/lldbsuite/test/python_api/formatters/main.cpp new file mode 100644 index 000000000000..f21c956144c2 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/formatters/main.cpp @@ -0,0 +1,59 @@ +#include +#include + +struct JustAStruct +{ + int A; + float B; + char C; + double D; + long E; + short F; +}; + +struct FooType +{ + int A; + float B; + char C; + double D; + long E; + short F; +}; + +struct CCC +{ + int a, b, c; +}; + +struct Empty1 { void *data; }; +struct Empty2 { void *data; }; + + +int main(int argc, char const *argv[]) { + JustAStruct foo; + foo.A = 1; + foo.B = 3.14; + foo.C = 'e'; + foo.D = 6.28; + foo.E = 3100419850; + foo.F = 0; + + FooType bar; + bar.A = 1; + bar.B = 3.14; + bar.C = 'e'; + bar.D = 6.28; + bar.E = 3100419850; + bar.F = 0; + JustAStruct* foo_ptr = &foo; + + std::vector int_vector; + + CCC ccc = {111, 222, 333}; + + Empty1 e1; + Empty2 e2; + + return 0; // Set break point at this line. +} diff --git a/packages/Python/lldbsuite/test/python_api/formatters/synth.py b/packages/Python/lldbsuite/test/python_api/formatters/synth.py new file mode 100644 index 000000000000..5a30c9a94bb5 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/formatters/synth.py @@ -0,0 +1,105 @@ +import lldb + +class jasSynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj; + def num_children(self): + return 2; + def get_child_at_index(self, index): + child = None + if index == 0: + child = self.valobj.GetChildMemberWithName('A'); + if index == 1: + child = self.valobj.CreateValueFromExpression('X', '(int)1') + return child; + def get_child_index(self, name): + if name == 'A': + return 0; + if name == 'X': + return 1; + return None; + + +def ccc_summary(sbvalue, internal_dict): + sbvalue = sbvalue.GetNonSyntheticValue() + # This tests that the SBValue.GetNonSyntheticValue() actually returns a + # non-synthetic value. If it does not, then sbvalue.GetChildMemberWithName("a") + # in the following statement will call the 'get_child_index' method of the + # synthetic child provider CCCSynthProvider below (which raises an exception). + return "CCC object with leading value " + str(sbvalue.GetChildMemberWithName("a")) + + +class CCCSynthProvider(object): + def __init__(self, sbvalue, internal_dict): + self._sbvalue = sbvalue + + def num_children(self): + return 3 + + def get_child_index(self, name): + raise RuntimeError("I don't want to be called!") + + def get_child_at_index(self, index): + if index == 0: + return self._sbvalue.GetChildMemberWithName("a") + if index == 1: + return self._sbvalue.GetChildMemberWithName("b") + if index == 2: + return self._sbvalue.GetChildMemberWithName("c") + + +def empty1_summary(sbvalue, internal_dict): + return "I am an empty Empty1" + + +class Empty1SynthProvider(object): + def __init__(self, sbvalue, internal_dict): + self._sbvalue = sbvalue + + def num_children(self): + return 0 + + def get_child_at_index(self, index): + return None + + +def empty2_summary(sbvalue, internal_dict): + return "I am an empty Empty2" + + +class Empty2SynthProvider(object): + def __init__(self, sbvalue, internal_dict): + self._sbvalue = sbvalue + + def num_children(self): + return 0 + + def get_child_at_index(self, index): + return None + + +def __lldb_init_module(debugger,dict): + debugger.CreateCategory("JASSynth").AddTypeSynthetic(lldb.SBTypeNameSpecifier("JustAStruct"), + lldb.SBTypeSynthetic.CreateWithClassName("synth.jasSynthProvider")) + cat = lldb.debugger.CreateCategory("CCCSynth") + cat.AddTypeSynthetic( + lldb.SBTypeNameSpecifier("CCC"), + lldb.SBTypeSynthetic.CreateWithClassName("synth.CCCSynthProvider", + lldb.eTypeOptionCascade)) + cat.AddTypeSummary( + lldb.SBTypeNameSpecifier("CCC"), + lldb.SBTypeSummary.CreateWithFunctionName("synth.ccc_summary", + lldb.eTypeOptionCascade)) + cat.AddTypeSynthetic( + lldb.SBTypeNameSpecifier("Empty1"), + lldb.SBTypeSynthetic.CreateWithClassName("synth.Empty1SynthProvider")) + cat.AddTypeSummary( + lldb.SBTypeNameSpecifier("Empty1"), + lldb.SBTypeSummary.CreateWithFunctionName("synth.empty1_summary")) + cat.AddTypeSynthetic( + lldb.SBTypeNameSpecifier("Empty2"), + lldb.SBTypeSynthetic.CreateWithClassName("synth.Empty2SynthProvider")) + cat.AddTypeSummary( + lldb.SBTypeNameSpecifier("Empty2"), + lldb.SBTypeSummary.CreateWithFunctionName("synth.empty2_summary", + lldb.eTypeOptionHideEmptyAggregates)) diff --git a/packages/Python/lldbsuite/test/python_api/frame/Makefile b/packages/Python/lldbsuite/test/python_api/frame/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/frame/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/frame/TestFrames.py b/packages/Python/lldbsuite/test/python_api/frame/TestFrames.py new file mode 100644 index 000000000000..7cc976fc6aa3 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/frame/TestFrames.py @@ -0,0 +1,207 @@ +""" +Use lldb Python SBFrame API to get the argument values of the call stacks. +And other SBFrame API tests. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class FrameAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24778") + def test_get_arg_vals_for_call_stack(self): + """Exercise SBFrame.GetVariables() API to get argument vals.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + # Keeps track of the number of times 'a' is called where it is within a + # depth of 3 of the 'c' leaf function. + callsOfA = 0 + + from six import StringIO as SixStringIO + session = SixStringIO() + while process.GetState() == lldb.eStateStopped: + thread = process.GetThreadAtIndex(0) + # Inspect at most 3 frames. + numFrames = min(3, thread.GetNumFrames()) + for i in range(numFrames): + frame = thread.GetFrameAtIndex(i) + if self.TraceOn(): + print("frame:", frame) + + name = frame.GetFunction().GetName() + if name == 'a': + callsOfA = callsOfA + 1 + + # We'll inspect only the arguments for the current frame: + # + # arguments => True + # locals => False + # statics => False + # in_scope_only => True + valList = frame.GetVariables(True, False, False, True) + argList = [] + for val in valList: + argList.append("(%s)%s=%s" % (val.GetTypeName(), + val.GetName(), + val.GetValue())) + print("%s(%s)" % (name, ", ".join(argList)), file=session) + + # Also check the generic pc & stack pointer. We can't test their absolute values, + # but they should be valid. Uses get_GPRs() from the lldbutil module. + gpr_reg_set = lldbutil.get_GPRs(frame) + pc_value = gpr_reg_set.GetChildMemberWithName("pc") + self.assertTrue (pc_value, "We should have a valid PC.") + pc_value_int = int(pc_value.GetValue(), 0) + # Make sure on arm targets we dont mismatch PC value on the basis of thumb bit. + # Frame PC will not have thumb bit set in case of a thumb instruction as PC. + if self.getArchitecture() in ['arm']: + pc_value_int &= ~1 + self.assertTrue (pc_value_int == frame.GetPC(), "PC gotten as a value should equal frame's GetPC") + sp_value = gpr_reg_set.GetChildMemberWithName("sp") + self.assertTrue (sp_value, "We should have a valid Stack Pointer.") + self.assertTrue (int(sp_value.GetValue(), 0) == frame.GetSP(), "SP gotten as a value should equal frame's GetSP") + + print("---", file=session) + process.Continue() + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + + # Expect to find 'a' on the call stacks two times. + self.assertTrue(callsOfA == 2, + "Expect to find 'a' on the call stacks two times") + # By design, the 'a' call frame has the following arg vals: + # o a((int)val=1, (char)ch='A') + # o a((int)val=3, (char)ch='A') + if self.TraceOn(): + print("Full stack traces when stopped on the breakpoint 'c':") + print(session.getvalue()) + self.expect(session.getvalue(), "Argugment values displayed correctly", + exe=False, + substrs = ["a((int)val=1, (char)ch='A')", + "a((int)val=3, (char)ch='A')"]) + + @add_test_categories(['pyapi']) + def test_frame_api_boundary_condition(self): + """Exercise SBFrame APIs with boundary condition inputs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + thread = process.GetThreadAtIndex(0) + frame = thread.GetFrameAtIndex(0) + if self.TraceOn(): + print("frame:", frame) + + # Boundary condition testings. + val1 = frame.FindVariable(None, True) + val2 = frame.FindVariable(None, False) + val3 = frame.FindValue(None, lldb.eValueTypeVariableGlobal) + if self.TraceOn(): + print("val1:", val1) + print("val2:", val2) + + frame.EvaluateExpression(None) + + @add_test_categories(['pyapi']) + def test_frame_api_IsEqual(self): + """Exercise SBFrame API IsEqual.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread) + + frameEntered = thread.GetFrameAtIndex(0) + if self.TraceOn(): + print(frameEntered) + lldbutil.print_stacktrace(thread) + self.assertTrue(frameEntered) + + # Doing two step overs while still inside c(). + thread.StepOver() + thread.StepOver() + self.assertTrue(thread) + frameNow = thread.GetFrameAtIndex(0) + if self.TraceOn(): + print(frameNow) + lldbutil.print_stacktrace(thread) + self.assertTrue(frameNow) + + # The latest two frames are considered equal. + self.assertTrue(frameEntered.IsEqual(frameNow)) + + # Now let's step out of frame c(). + thread.StepOutOfFrame(frameNow) + frameOutOfC = thread.GetFrameAtIndex(0) + if self.TraceOn(): + print(frameOutOfC) + lldbutil.print_stacktrace(thread) + self.assertTrue(frameOutOfC) + + # The latest two frames should not be equal. + self.assertFalse(frameOutOfC.IsEqual(frameNow)) diff --git a/packages/Python/lldbsuite/test/python_api/frame/inlines/Makefile b/packages/Python/lldbsuite/test/python_api/frame/inlines/Makefile new file mode 100644 index 000000000000..641ee5d96ca6 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/frame/inlines/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +C_SOURCES := inlines.c + +ifneq (,$(findstring icc,$(CC))) + CFLAGS += -debug inline-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/frame/inlines/TestInlinedFrame.py b/packages/Python/lldbsuite/test/python_api/frame/inlines/TestInlinedFrame.py new file mode 100644 index 000000000000..d4cf8fe30ccd --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/frame/inlines/TestInlinedFrame.py @@ -0,0 +1,79 @@ +""" +Testlldb Python SBFrame APIs IsInlined() and GetFunctionName(). +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class InlinedFrameAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.source = 'inlines.c' + self.first_stop = line_number(self.source, '// This should correspond to the first break stop.') + self.second_stop = line_number(self.source, '// This should correspond to the second break stop.') + + @add_test_categories(['pyapi']) + def test_stop_at_outer_inline(self): + """Exercise SBFrame.IsInlined() and SBFrame.GetFunctionName().""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by the name of 'inner_inline'. + breakpoint = target.BreakpointCreateByName('inner_inline', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() > 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + import lldbsuite.test.lldbutil as lldbutil + stack_traces1 = lldbutil.print_stacktraces(process, string_buffer=True) + if self.TraceOn(): + print("Full stack traces when first stopped on the breakpoint 'inner_inline':") + print(stack_traces1) + + # The first breakpoint should correspond to an inlined call frame. + # If it's an inlined call frame, expect to find, in the stack trace, + # that there is a frame which corresponds to the following call site: + # + # outer_inline (argc); + # + frame0 = process.GetThreadAtIndex(0).GetFrameAtIndex(0) + if frame0.IsInlined(): + filename = frame0.GetLineEntry().GetFileSpec().GetFilename() + self.assertTrue(filename == self.source) + self.expect(stack_traces1, "First stop at %s:%d" % (self.source, self.first_stop), exe=False, + substrs = ['%s:%d' % (self.source, self.first_stop)]) + + # Expect to break again for the second time. + process.Continue() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + stack_traces2 = lldbutil.print_stacktraces(process, string_buffer=True) + if self.TraceOn(): + print("Full stack traces when stopped on the breakpoint 'inner_inline' for the second time:") + print(stack_traces2) + self.expect(stack_traces2, "Second stop at %s:%d" % (self.source, self.second_stop), exe=False, + substrs = ['%s:%d' % (self.source, self.second_stop)]) diff --git a/packages/Python/lldbsuite/test/python_api/frame/inlines/inlines.c b/packages/Python/lldbsuite/test/python_api/frame/inlines/inlines.c new file mode 100644 index 000000000000..a2a8212278d0 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/frame/inlines/inlines.c @@ -0,0 +1,53 @@ +#include +#include "inlines.h" + +#define INLINE_ME __inline__ __attribute__((always_inline)) + +int +not_inlined_2 (int input) +{ + printf ("Called in not_inlined_2 with : %d.\n", input); + return input; +} + +int +not_inlined_1 (int input) +{ + printf ("Called in not_inlined_1 with %d.\n", input); + return not_inlined_2(input); +} + +INLINE_ME int +inner_inline (int inner_input, int mod_value) +{ + int inner_result; + inner_result = inner_input % mod_value; + printf ("Returning: %d.\n", inner_result); + return not_inlined_1 (inner_result); +} + +INLINE_ME int +outer_inline (int outer_input) +{ + int outer_result; + + outer_result = inner_inline (outer_input, outer_input % 3); + return outer_result; +} + +int +main (int argc, char **argv) +{ + printf ("Starting...\n"); + + int (*func_ptr) (int); + func_ptr = outer_inline; + + outer_inline (argc); // This should correspond to the first break stop. + + func_ptr (argc); // This should correspond to the second break stop. + + return 0; +} + + diff --git a/packages/Python/lldbsuite/test/python_api/frame/inlines/inlines.h b/packages/Python/lldbsuite/test/python_api/frame/inlines/inlines.h new file mode 100644 index 000000000000..265d7b4966ed --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/frame/inlines/inlines.h @@ -0,0 +1,4 @@ +int inner_inline (int inner_input, int mod_value); +int outer_inline (int outer_input); +int not_inlined_2 (int input); +int not_inlined_1 (int input); diff --git a/packages/Python/lldbsuite/test/python_api/frame/main.c b/packages/Python/lldbsuite/test/python_api/frame/main.c new file mode 100644 index 000000000000..35209db1812e --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/frame/main.c @@ -0,0 +1,58 @@ +//===-- 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 + +// This simple program is to test the lldb Python API related to frames. + +int a(int, char); +int b(int, char); +int c(int, char); + +int a(int val, char ch) +{ + int my_val = val; + char my_ch = ch; + printf("a(val=%d, ch='%c')\n", val, ch); + if (val <= 1) + return b(val+1, ch+1); + else if (val >= 3) + return c(val+1, ch+1); + + return val; +} + +int b(int val, char ch) +{ + int my_val = val; + char my_ch = ch; + printf("b(val=%d, ch='%c')\n", val, ch); + return c(val+1, ch+1); +} + +int c(int val, char ch) +{ + int my_val = val; + char my_ch = ch; + printf("c(val=%d, ch='%c')\n", val, ch); + return val + 3 + ch; +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1, 'A'); // a(1, 'A') -> b(2, 'B') -> c(3, 'C') + printf("a(1, 'A') returns %d\n", A1); + + int B2 = b(2, 'B'); // b(2, 'B') -> c(3, 'C') + printf("b(2, 'B') returns %d\n", B2); + + int A3 = a(3, 'A'); // a(3, 'A') -> c(4, 'B') + printf("a(3, 'A') returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/function_symbol/Makefile b/packages/Python/lldbsuite/test/python_api/function_symbol/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/function_symbol/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/function_symbol/TestDisasmAPI.py b/packages/Python/lldbsuite/test/python_api/function_symbol/TestDisasmAPI.py new file mode 100644 index 000000000000..c9876a8c8954 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/function_symbol/TestDisasmAPI.py @@ -0,0 +1,111 @@ +""" +Test retrieval of SBAddress from function/symbol, disassembly, and SBAddress APIs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class DisasmAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.line1 = line_number('main.c', '// Find the line number for breakpoint 1 here.') + self.line2 = line_number('main.c', '// Find the line number for breakpoint 2 here.') + + @add_test_categories(['pyapi']) + def test(self): + """Exercise getting SBAddress objects, disassembly, and SBAddress APIs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create the two breakpoints inside function 'a'. + breakpoint1 = target.BreakpointCreateByLocation('main.c', self.line1) + breakpoint2 = target.BreakpointCreateByLocation('main.c', self.line2) + #print("breakpoint1:", breakpoint1) + #print("breakpoint2:", breakpoint2) + self.assertTrue(breakpoint1 and + breakpoint1.GetNumLocations() == 1, + VALID_BREAKPOINT) + self.assertTrue(breakpoint2 and + breakpoint2.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.line1. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + lineEntry = frame0.GetLineEntry() + self.assertTrue(lineEntry.GetLine() == self.line1) + + address1 = lineEntry.GetStartAddress() + #print("address1:", address1) + + # Now call SBTarget.ResolveSymbolContextForAddress() with address1. + context1 = target.ResolveSymbolContextForAddress(address1, lldb.eSymbolContextEverything) + + self.assertTrue(context1) + if self.TraceOn(): + print("context1:", context1) + + # Continue the inferior, the breakpoint 2 should be hit. + process.Continue() + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + lineEntry = frame0.GetLineEntry() + self.assertTrue(lineEntry.GetLine() == self.line2) + + # Verify that the symbol and the function has the same address range per function 'a'. + symbol = context1.GetSymbol() + function = frame0.GetFunction() + self.assertTrue(symbol and function) + + disasm_output = lldbutil.disassemble(target, symbol) + if self.TraceOn(): + print("symbol:", symbol) + print("disassembly=>\n", disasm_output) + + disasm_output = lldbutil.disassemble(target, function) + if self.TraceOn(): + print("function:", function) + print("disassembly=>\n", disasm_output) + + sa1 = symbol.GetStartAddress() + #print("sa1:", sa1) + #print("sa1.GetFileAddress():", hex(sa1.GetFileAddress())) + #ea1 = symbol.GetEndAddress() + #print("ea1:", ea1) + sa2 = function.GetStartAddress() + #print("sa2:", sa2) + #print("sa2.GetFileAddress():", hex(sa2.GetFileAddress())) + #ea2 = function.GetEndAddress() + #print("ea2:", ea2) + self.assertTrue(sa1 and sa2 and sa1 == sa2, + "The two starting addresses should be the same") + + from lldbsuite.test.lldbutil import get_description + desc1 = get_description(sa1) + desc2 = get_description(sa2) + self.assertTrue(desc1 and desc2 and desc1 == desc2, + "SBAddress.GetDescription() API of sa1 and sa2 should return the same string") diff --git a/packages/Python/lldbsuite/test/python_api/function_symbol/TestSymbolAPI.py b/packages/Python/lldbsuite/test/python_api/function_symbol/TestSymbolAPI.py new file mode 100644 index 000000000000..d45f5724f49c --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/function_symbol/TestSymbolAPI.py @@ -0,0 +1,81 @@ +""" +Test newly added SBSymbol and SBAddress APIs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class SymbolAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.line1 = line_number('main.c', '// Find the line number for breakpoint 1 here.') + self.line2 = line_number('main.c', '// Find the line number for breakpoint 2 here.') + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24778") + def test(self): + """Exercise some SBSymbol and SBAddress APIs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create the two breakpoints inside function 'a'. + breakpoint1 = target.BreakpointCreateByLocation('main.c', self.line1) + breakpoint2 = target.BreakpointCreateByLocation('main.c', self.line2) + #print("breakpoint1:", breakpoint1) + #print("breakpoint2:", breakpoint2) + self.assertTrue(breakpoint1 and + breakpoint1.GetNumLocations() == 1, + VALID_BREAKPOINT) + self.assertTrue(breakpoint2 and + breakpoint2.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.line1. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + symbol_line1 = frame0.GetSymbol() + # We should have a symbol type of code. + self.assertTrue(symbol_line1.GetType() == lldb.eSymbolTypeCode) + addr_line1 = symbol_line1.GetStartAddress() + # And a section type of code, too. + self.assertTrue(addr_line1.GetSection().GetSectionType() == lldb.eSectionTypeCode) + + # Continue the inferior, the breakpoint 2 should be hit. + process.Continue() + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + symbol_line2 = frame0.GetSymbol() + # We should have a symbol type of code. + self.assertTrue(symbol_line2.GetType() == lldb.eSymbolTypeCode) + addr_line2 = symbol_line2.GetStartAddress() + # And a section type of code, too. + self.assertTrue(addr_line2.GetSection().GetSectionType() == lldb.eSectionTypeCode) + + # Now verify that both addresses point to the same module. + if self.TraceOn(): + print("UUID:", addr_line1.GetModule().GetUUIDString()) + self.assertTrue(addr_line1.GetModule().GetUUIDString() == addr_line2.GetModule().GetUUIDString()) diff --git a/packages/Python/lldbsuite/test/python_api/function_symbol/main.c b/packages/Python/lldbsuite/test/python_api/function_symbol/main.c new file mode 100644 index 000000000000..b60b2faf18dc --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/function_symbol/main.c @@ -0,0 +1,60 @@ +//===-- 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 + +// This simple program is to test the lldb Python APIs SBTarget, SBFrame, +// SBFunction, SBSymbol, and SBAddress. +// +// When stopped on breakppint 1, we can get the line entry using SBFrame API +// SBFrame.GetLineEntry(). We'll get the start address for the line entry +// with the SBAddress type, resolve the symbol context using the SBTarget API +// SBTarget.ResolveSymbolContextForAddress() in order to get the SBSymbol. +// +// We then stop at breakpoint 2, get the SBFrame, and the SBFunction object. +// +// The address from calling GetStartAddress() on the symbol and the function +// should point to the same address, and we also verify that. + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) // Find the line number for breakpoint 1 here. + val = b(val); + else if (val >= 3) + val = c(val); + + return val; // Find the line number for breakpoint 2 here. +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/hello_world/Makefile b/packages/Python/lldbsuite/test/python_api/hello_world/Makefile new file mode 100644 index 000000000000..9976203b63bb --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/hello_world/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../make + +C_SOURCES := main.c +# See TestHelloWorld.py, which specifies the executable name with a dictionary. +EXE := hello_world + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/hello_world/TestHelloWorld.py b/packages/Python/lldbsuite/test/python_api/hello_world/TestHelloWorld.py new file mode 100644 index 000000000000..47c3ba146cef --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/hello_world/TestHelloWorld.py @@ -0,0 +1,147 @@ +"""Test Python APIs for target (launch and attach), breakpoint, and process.""" + +from __future__ import print_function + + + +import os, sys, time +import lldb +import time +from lldbsuite.test.lldbtest import * + +class HelloWorldTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Get the full path to our executable to be attached/debugged. + self.exe = os.path.join(os.getcwd(), self.testMethodName) + self.d = {'EXE': self.testMethodName} + # Find a couple of the line numbers within main.c. + self.line1 = line_number('main.c', '// Set break point at this line.') + self.line2 = line_number('main.c', '// Waiting to be attached...') + + def tearDown(self): + # Destroy process before TestBase.tearDown() + self.dbg.GetSelectedTarget().GetProcess().Destroy() + # Call super's tearDown(). + TestBase.tearDown(self) + + @add_test_categories(['pyapi']) + @skipIfiOSSimulator + def test_with_process_launch_api(self): + """Create target, breakpoint, launch a process, and then kill it.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + target = self.dbg.CreateTarget(self.exe) + + breakpoint = target.BreakpointCreateByLocation("main.c", self.line1) + + # The default state after breakpoint creation should be enabled. + self.assertTrue(breakpoint.IsEnabled(), + "Breakpoint should be enabled after creation") + + breakpoint.SetEnabled(False) + self.assertTrue(not breakpoint.IsEnabled(), + "Breakpoint.SetEnabled(False) works") + + breakpoint.SetEnabled(True) + self.assertTrue(breakpoint.IsEnabled(), + "Breakpoint.SetEnabled(True) works") + + # rdar://problem/8364687 + # SBTarget.Launch() issue (or is there some race condition)? + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + # The following isn't needed anymore, rdar://8364687 is fixed. + # + # Apply some dances after LaunchProcess() in order to break at "main". + # It only works sometimes. + #self.breakAfterLaunch(process, "main") + + process = target.GetProcess() + self.assertTrue(process, PROCESS_IS_VALID) + + thread = process.GetThreadAtIndex(0) + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + from lldbsuite.test.lldbutil import stop_reason_to_str + self.fail(STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS % + stop_reason_to_str(thread.GetStopReason())) + + # The breakpoint should have a hit count of 1. + self.assertTrue(breakpoint.GetHitCount() == 1, BREAKPOINT_HIT_ONCE) + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24600") + @expectedFailurei386("llvm.org/pr25338") + @skipIfiOSSimulator + def test_with_attach_to_process_with_id_api(self): + """Create target, spawn a process, and attach to it with process id.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + target = self.dbg.CreateTarget(self.exe) + + # Spawn a new process + popen = self.spawnSubprocess(self.exe, ["abc", "xyz"]) + self.addTearDownHook(self.cleanupSubprocesses) + + # Give the subprocess time to start and wait for user input + time.sleep(0.25) + + listener = lldb.SBListener("my.attach.listener") + error = lldb.SBError() + process = target.AttachToProcessWithID(listener, popen.pid, error) + + self.assertTrue(error.Success() and process, PROCESS_IS_VALID) + + # Let's check the stack traces of the attached process. + import lldbsuite.test.lldbutil as lldbutil + stacktraces = lldbutil.print_stacktraces(process, string_buffer=True) + self.expect(stacktraces, exe=False, + substrs = ['main.c:%d' % self.line2, + '(int)argc=3']) + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24600") + @expectedFailurei386("llvm.org/pr25338") + @skipIfiOSSimulator + def test_with_attach_to_process_with_name_api(self): + """Create target, spawn a process, and attach to it with process name.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + target = self.dbg.CreateTarget(self.exe) + + # Spawn a new process + popen = self.spawnSubprocess(self.exe, ["abc", "xyz"]) + self.addTearDownHook(self.cleanupSubprocesses) + + # Give the subprocess time to start and wait for user input + time.sleep(0.25) + + listener = lldb.SBListener("my.attach.listener") + error = lldb.SBError() + # Pass 'False' since we don't want to wait for new instance of "hello_world" to be launched. + name = os.path.basename(self.exe) + + # While we're at it, make sure that passing a None as the process name + # does not hang LLDB. + target.AttachToProcessWithName(listener, None, False, error) + # Also boundary condition test ConnectRemote(), too. + target.ConnectRemote(listener, None, None, error) + + process = target.AttachToProcessWithName(listener, name, False, error) + + self.assertTrue(error.Success() and process, PROCESS_IS_VALID) + + # Verify that after attach, our selected target indeed matches name. + self.expect(self.dbg.GetSelectedTarget().GetExecutable().GetFilename(), exe=False, + startstr = name) + + # Let's check the stack traces of the attached process. + import lldbsuite.test.lldbutil as lldbutil + stacktraces = lldbutil.print_stacktraces(process, string_buffer=True) + self.expect(stacktraces, exe=False, + substrs = ['main.c:%d' % self.line2, + '(int)argc=3']) diff --git a/packages/Python/lldbsuite/test/python_api/hello_world/main.c b/packages/Python/lldbsuite/test/python_api/hello_world/main.c new file mode 100644 index 000000000000..31a041ede741 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/hello_world/main.c @@ -0,0 +1,35 @@ +#include + +#if defined(__linux__) +#include +#endif + +int main(int argc, char const *argv[]) { + +#if defined(__linux__) + // Immediately enable any ptracer so that we can allow the stub attach + // operation to succeed. Some Linux kernels are locked down so that + // only an ancestor process can be a ptracer of a process. This disables that + // restriction. Without it, attach-related stub tests will fail. +#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY) + int prctl_result; + + // For now we execute on best effort basis. If this fails for + // some reason, so be it. + prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); + (void) prctl_result; +#endif +#endif + + printf("Hello world.\n"); // Set break point at this line. + if (argc == 1) + return 0; + + // Waiting to be attached by the debugger, otherwise. + char line[100]; + while (fgets(line, sizeof(line), stdin)) { // Waiting to be attached... + printf("input line=>%s\n", line); + } + + printf("Exiting now\n"); +} diff --git a/packages/Python/lldbsuite/test/python_api/interpreter/Makefile b/packages/Python/lldbsuite/test/python_api/interpreter/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/interpreter/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/interpreter/TestCommandInterpreterAPI.py b/packages/Python/lldbsuite/test/python_api/interpreter/TestCommandInterpreterAPI.py new file mode 100644 index 000000000000..faff11818da7 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/interpreter/TestCommandInterpreterAPI.py @@ -0,0 +1,73 @@ +"""Test the SBCommandInterpreter APIs.""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class CommandInterpreterAPICase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break on inside main.cpp. + self.line = line_number('main.c', 'Hello world.') + + @add_test_categories(['pyapi']) + def test_with_process_launch_api(self): + """Test the SBCommandInterpreter APIs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Retrieve the associated command interpreter from our debugger. + ci = self.dbg.GetCommandInterpreter() + self.assertTrue(ci, VALID_COMMAND_INTERPRETER) + + # Exercise some APIs.... + + self.assertTrue(ci.HasCommands()) + self.assertTrue(ci.HasAliases()) + self.assertTrue(ci.HasAliasOptions()) + self.assertTrue(ci.CommandExists("breakpoint")) + self.assertTrue(ci.CommandExists("target")) + self.assertTrue(ci.CommandExists("platform")) + self.assertTrue(ci.AliasExists("file")) + self.assertTrue(ci.AliasExists("run")) + self.assertTrue(ci.AliasExists("bt")) + + res = lldb.SBCommandReturnObject() + ci.HandleCommand("breakpoint set -f main.c -l %d" % self.line, res) + self.assertTrue(res.Succeeded()) + ci.HandleCommand("process launch", res) + self.assertTrue(res.Succeeded()) + + # Boundary conditions should not crash lldb! + self.assertFalse(ci.CommandExists(None)) + self.assertFalse(ci.AliasExists(None)) + ci.HandleCommand(None, res) + self.assertFalse(res.Succeeded()) + res.AppendMessage("Just appended a message.") + res.AppendMessage(None) + if self.TraceOn(): + print(res) + + process = ci.GetProcess() + self.assertTrue(process) + + import lldbsuite.test.lldbutil as lldbutil + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + if self.TraceOn(): + lldbutil.print_stacktraces(process) diff --git a/packages/Python/lldbsuite/test/python_api/interpreter/main.c b/packages/Python/lldbsuite/test/python_api/interpreter/main.c new file mode 100644 index 000000000000..277aa54a4eea --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/interpreter/main.c @@ -0,0 +1,6 @@ +#include + +int main(int argc, char const *argv[]) { + printf("Hello world.\n"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/frame/Makefile b/packages/Python/lldbsuite/test/python_api/lldbutil/frame/Makefile new file mode 100644 index 000000000000..69b74b5d7531 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/frame/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +MAKE_DSYM :=NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/frame/TestFrameUtils.py b/packages/Python/lldbsuite/test/python_api/lldbutil/frame/TestFrameUtils.py new file mode 100644 index 000000000000..2cde05af0c39 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/frame/TestFrameUtils.py @@ -0,0 +1,59 @@ +""" +Test utility functions for the frame object. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class FrameUtilsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', + "// Find the line number here.") + + @add_test_categories(['pyapi']) + def test_frame_utils(self): + """Test utility functions for the frame object.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.c", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.LaunchProcess() failed") + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + import lldbsuite.test.lldbutil as lldbutil + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue (thread) + frame0 = thread.GetFrameAtIndex(0) + self.assertTrue (frame0) + frame1 = thread.GetFrameAtIndex(1) + self.assertTrue (frame1) + parent = lldbutil.get_parent_frame(frame0) + self.assertTrue(parent and parent.GetFrameID() == frame1.GetFrameID()) + frame0_args = lldbutil.get_args_as_string(frame0) + parent_args = lldbutil.get_args_as_string(parent) + self.assertTrue(frame0_args and parent_args and "(int)val=1" in frame0_args) + if self.TraceOn(): + lldbutil.print_stacktrace(thread) + print("Current frame: %s" % frame0_args) + print("Parent frame: %s" % parent_args) diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/frame/main.c b/packages/Python/lldbsuite/test/python_api/lldbutil/frame/main.c new file mode 100644 index 000000000000..e6eeef5b46d9 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/frame/main.c @@ -0,0 +1,47 @@ +//===-- 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 a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); + + return val; +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; // Find the line number here. +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/iter/Makefile b/packages/Python/lldbsuite/test/python_api/lldbutil/iter/Makefile new file mode 100644 index 000000000000..051354123495 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/iter/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp +MAKE_DSYM := NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestLLDBIterator.py b/packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestLLDBIterator.py new file mode 100644 index 000000000000..07177c1fae4a --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestLLDBIterator.py @@ -0,0 +1,122 @@ +""" +Test the iteration protocol for some lldb container objects. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * + +class LLDBIteratorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.line1 = line_number('main.cpp', '// Set break point at this line.') + self.line2 = line_number('main.cpp', '// And that line.') + + @add_test_categories(['pyapi']) + def test_lldb_iter_module(self): + """Test module_iter works correctly for SBTarget -> SBModule.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line1) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.LaunchProcess() failed") + + from lldbsuite.test.lldbutil import get_description + yours = [] + for i in range(target.GetNumModules()): + yours.append(target.GetModuleAtIndex(i)) + mine = [] + for m in target.module_iter(): + mine.append(m) + + self.assertTrue(len(yours) == len(mine)) + for i in range(len(yours)): + if self.TraceOn(): + print("yours[%d]='%s'" % (i, get_description(yours[i]))) + print("mine[%d]='%s'" % (i, get_description(mine[i]))) + self.assertTrue(yours[i] == mine[i], + "UUID+FileSpec of yours[{0}] and mine[{0}] matches".format(i)) + + @add_test_categories(['pyapi']) + def test_lldb_iter_breakpoint(self): + """Test breakpoint_iter works correctly for SBTarget -> SBBreakpoint.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line1) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line2) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + self.assertTrue(target.GetNumBreakpoints() == 2) + + from lldbsuite.test.lldbutil import get_description + yours = [] + for i in range(target.GetNumBreakpoints()): + yours.append(target.GetBreakpointAtIndex(i)) + mine = [] + for b in target.breakpoint_iter(): + mine.append(b) + + self.assertTrue(len(yours) == len(mine)) + for i in range(len(yours)): + if self.TraceOn(): + print("yours[%d]='%s'" % (i, get_description(yours[i]))) + print("mine[%d]='%s'" % (i, get_description(mine[i]))) + self.assertTrue(yours[i] == mine[i], + "ID of yours[{0}] and mine[{0}] matches".format(i)) + + @add_test_categories(['pyapi']) + def test_lldb_iter_frame(self): + """Test iterator works correctly for SBProcess->SBThread->SBFrame.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line1) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.LaunchProcess() failed") + + from lldbsuite.test.lldbutil import print_stacktrace + stopped_due_to_breakpoint = False + for thread in process: + if self.TraceOn(): + print_stacktrace(thread) + ID = thread.GetThreadID() + if thread.GetStopReason() == lldb.eStopReasonBreakpoint: + stopped_due_to_breakpoint = True + for frame in thread: + self.assertTrue(frame.GetThread().GetThreadID() == ID) + if self.TraceOn(): + print(frame) + + self.assertTrue(stopped_due_to_breakpoint) diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestRegistersIterator.py b/packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestRegistersIterator.py new file mode 100644 index 000000000000..1645ae1f2a5d --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/iter/TestRegistersIterator.py @@ -0,0 +1,94 @@ +""" +Test the iteration protocol for frame registers. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * + +class RegistersIteratorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line1 = line_number('main.cpp', '// Set break point at this line.') + + @add_test_categories(['pyapi']) + @expectedFailureWindows # Test crashes + def test_iter_registers(self): + """Test iterator works correctly for lldbutil.iter_registers().""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line1) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.LaunchProcess() failed") + + import lldbsuite.test.lldbutil as lldbutil + for thread in process: + if thread.GetStopReason() == lldb.eStopReasonBreakpoint: + for frame in thread: + # Dump the registers of this frame using lldbutil.get_GPRs() and friends. + if self.TraceOn(): + print(frame) + + REGs = lldbutil.get_GPRs(frame) + num = len(REGs) + if self.TraceOn(): + print("\nNumber of general purpose registers: %d" % num) + for reg in REGs: + self.assertTrue(reg) + if self.TraceOn(): + print("%s => %s" % (reg.GetName(), reg.GetValue())) + + REGs = lldbutil.get_FPRs(frame) + num = len(REGs) + if self.TraceOn(): + print("\nNumber of floating point registers: %d" % num) + for reg in REGs: + self.assertTrue(reg) + if self.TraceOn(): + print("%s => %s" % (reg.GetName(), reg.GetValue())) + + REGs = lldbutil.get_ESRs(frame) + if self.platformIsDarwin(): + num = len(REGs) + if self.TraceOn(): + print("\nNumber of exception state registers: %d" % num) + for reg in REGs: + self.assertTrue(reg) + if self.TraceOn(): + print("%s => %s" % (reg.GetName(), reg.GetValue())) + else: + self.assertIsNone(REGs) + + # And these should also work. + for kind in ["General Purpose Registers", + "Floating Point Registers"]: + REGs = lldbutil.get_registers(frame, kind) + self.assertTrue(REGs) + + REGs = lldbutil.get_registers(frame, "Exception State Registers") + if self.platformIsDarwin(): + self.assertIsNotNone(REGs) + else: + self.assertIsNone(REGs) + + # We've finished dumping the registers for frame #0. + break diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/iter/main.cpp b/packages/Python/lldbsuite/test/python_api/lldbutil/iter/main.cpp new file mode 100644 index 000000000000..8fb45f94b1ef --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/iter/main.cpp @@ -0,0 +1,134 @@ +//===-- 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 + +// C++ includes +#include +#include +#include +#include + +std::thread g_thread_1; +std::thread g_thread_2; +std::thread g_thread_3; +std::mutex g_mask_mutex; + +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 uint32_t g_mask = 0; + + std::lock_guard lock(g_mask_mutex); + switch (action) + { + case eGet: + break; + + case eAssign: + g_mask |= mask; + break; + + case eClearBits: + g_mask &= ~mask; + break; + } + return g_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); + + std::default_random_engine generator; + std::uniform_int_distribution distribution(0, 3000000); + + while (mask_access(eGet) & thread_mask) + { + // random micro second sleep from zero to 3 seconds + int usec = distribution(generator); + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + + std::chrono::microseconds duration(usec); + std::this_thread::sleep_for(duration); + printf ("%s (thread = %u) after usleep ...\n", __FUNCTION__, thread_index); // Set break point at this line. + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); + return NULL; +} + + +int main (int argc, char const *argv[]) +{ + 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); // And that line. + + // Create 3 threads + g_thread_1 = std::thread(thread_func, (void*)&thread_index_1); + g_thread_2 = std::thread(thread_func, (void*)&thread_index_2); + g_thread_3 = std::thread(thread_func, (void*)&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 + g_thread_1.join(); + g_thread_2.join(); + g_thread_3.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/process/Makefile b/packages/Python/lldbsuite/test/python_api/lldbutil/process/Makefile new file mode 100644 index 000000000000..93fc28b4ee07 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/process/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp +MAKE_DSYM :=NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/process/TestPrintStackTraces.py b/packages/Python/lldbsuite/test/python_api/lldbutil/process/TestPrintStackTraces.py new file mode 100644 index 000000000000..b48ded4dd365 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/process/TestPrintStackTraces.py @@ -0,0 +1,52 @@ +""" +Test SBprocess and SBThread APIs with printing of the stack traces using lldbutil. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * + +class ThreadsStackTracesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + @expectedFailureAll("llvm.org/pr23043", ["linux"], archs=["i386"]) # We are unable to produce a backtrace of the main thread when the thread is blocked in fgets + @expectedFailureWindows("llvm.org/pr24778") + @add_test_categories(['pyapi']) + def test_stack_traces(self): + """Test SBprocess and SBThread APIs with printing of the stack traces.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (["abc", "xyz"], None, self.get_process_working_directory()) + + if not process: + self.fail("SBTarget.LaunchProcess() failed") + + import lldbsuite.test.lldbutil as lldbutil + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + stacktraces = lldbutil.print_stacktraces(process, string_buffer=True) + self.expect(stacktraces, exe=False, + substrs = ['(int)argc=3']) diff --git a/packages/Python/lldbsuite/test/python_api/lldbutil/process/main.cpp b/packages/Python/lldbsuite/test/python_api/lldbutil/process/main.cpp new file mode 100644 index 000000000000..6b87c3d649e6 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/lldbutil/process/main.cpp @@ -0,0 +1,136 @@ +//===-- 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 + +// C++ includes +#include +#include +#include +#include + +std::thread g_thread_1; +std::thread g_thread_2; +std::thread g_thread_3; +std::mutex g_mask_mutex; + +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 uint32_t g_mask = 0; + + std::lock_guard lock(g_mask_mutex); + switch (action) + { + case eGet: + break; + + case eAssign: + g_mask |= mask; + break; + + case eClearBits: + g_mask &= ~mask; + break; + } + return g_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); + + std::default_random_engine generator; + std::uniform_int_distribution distribution(0, 3000000); + + while (mask_access(eGet) & thread_mask) + { + // random micro second sleep from zero to 3 seconds + int usec = distribution(generator); + + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + std::chrono::microseconds duration(usec); + std::this_thread::sleep_for(duration); + printf ("%s (thread = %u) after usleep ...\n", __FUNCTION__, thread_index); // Set break point at this line. + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); + return NULL; +} + + +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); // And that line. + + // Create 3 threads + g_thread_1 = std::thread(thread_func, (void*)&thread_index_1); + g_thread_2 = std::thread(thread_func, (void*)&thread_index_2); + g_thread_3 = std::thread(thread_func, (void*)&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 + g_thread_1.join(); + g_thread_2.join(); + g_thread_3.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/module_section/Makefile b/packages/Python/lldbsuite/test/python_api/module_section/Makefile new file mode 100644 index 000000000000..ee74ebae1f42 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/module_section/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp b.cpp c.cpp +MAKE_DSYM :=NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/module_section/TestModuleAndSection.py b/packages/Python/lldbsuite/test/python_api/module_section/TestModuleAndSection.py new file mode 100644 index 000000000000..bc97d432406f --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/module_section/TestModuleAndSection.py @@ -0,0 +1,127 @@ +""" +Test some SBModule and SBSection APIs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.lldbutil import symbol_type_to_str + +class ModuleAndSectionAPIsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + def test_module_and_section(self): + """Test module and section APIs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + self.assertTrue(target.GetNumModules() > 0) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + print("Number of modules for the target: %d" % target.GetNumModules()) + for module in target.module_iter(): + print(module) + + # Get the executable module at index 0. + exe_module = target.GetModuleAtIndex(0) + + print("Exe module: %s" % str(exe_module)) + print("Number of sections: %d" % exe_module.GetNumSections()) + INDENT = ' ' * 4 + INDENT2 = INDENT * 2 + for sec in exe_module.section_iter(): + print(sec) + print(INDENT + "Number of subsections: %d" % sec.GetNumSubSections()) + if sec.GetNumSubSections() == 0: + for sym in exe_module.symbol_in_section_iter(sec): + print(INDENT + str(sym)) + print(INDENT + "symbol type: %s" % symbol_type_to_str(sym.GetType())) + else: + for subsec in sec: + print(INDENT + str(subsec)) + # Now print the symbols belonging to the subsection.... + for sym in exe_module.symbol_in_section_iter(subsec): + print(INDENT2 + str(sym)) + print(INDENT2 + "symbol type: %s" % symbol_type_to_str(sym.GetType())) + + @add_test_categories(['pyapi']) + def test_module_and_section_boundary_condition(self): + """Test module and section APIs by passing None when it expects a Python string.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + self.assertTrue(target.GetNumModules() > 0) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + print("Number of modules for the target: %d" % target.GetNumModules()) + for module in target.module_iter(): + print(module) + + # Get the executable module at index 0. + exe_module = target.GetModuleAtIndex(0) + + print("Exe module: %s" % str(exe_module)) + print("Number of sections: %d" % exe_module.GetNumSections()) + + # Boundary condition testings. Should not crash lldb! + exe_module.FindFirstType(None) + exe_module.FindTypes(None) + exe_module.FindGlobalVariables(target, None, 1) + exe_module.FindFunctions(None, 0) + exe_module.FindSection(None) + + # Get the section at index 1. + if exe_module.GetNumSections() > 1: + sec1 = exe_module.GetSectionAtIndex(1) + print(sec1) + else: + sec1 = None + + if sec1: + sec1.FindSubSection(None) + + @add_test_categories(['pyapi']) + def test_module_compile_unit_iter(self): + """Test module's compile unit iterator APIs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + self.assertTrue(target.GetNumModules() > 0) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + print("Number of modules for the target: %d" % target.GetNumModules()) + for module in target.module_iter(): + print(module) + + # Get the executable module at index 0. + exe_module = target.GetModuleAtIndex(0) + + print("Exe module: %s" % str(exe_module)) + print("Number of compile units: %d" % exe_module.GetNumCompileUnits()) + INDENT = ' ' * 4 + INDENT2 = INDENT * 2 + for cu in exe_module.compile_unit_iter(): + print(cu) + diff --git a/packages/Python/lldbsuite/test/python_api/module_section/b.cpp b/packages/Python/lldbsuite/test/python_api/module_section/b.cpp new file mode 100644 index 000000000000..4e3e54138e5b --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/module_section/b.cpp @@ -0,0 +1,3 @@ +int b_function(int input) { + return input * 2; +} diff --git a/packages/Python/lldbsuite/test/python_api/module_section/c.cpp b/packages/Python/lldbsuite/test/python_api/module_section/c.cpp new file mode 100644 index 000000000000..3c87bfe30c6c --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/module_section/c.cpp @@ -0,0 +1,3 @@ +int c_function(int input) { + return input * 3; +} diff --git a/packages/Python/lldbsuite/test/python_api/module_section/main.cpp b/packages/Python/lldbsuite/test/python_api/module_section/main.cpp new file mode 100644 index 000000000000..6b87c3d649e6 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/module_section/main.cpp @@ -0,0 +1,136 @@ +//===-- 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 + +// C++ includes +#include +#include +#include +#include + +std::thread g_thread_1; +std::thread g_thread_2; +std::thread g_thread_3; +std::mutex g_mask_mutex; + +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 uint32_t g_mask = 0; + + std::lock_guard lock(g_mask_mutex); + switch (action) + { + case eGet: + break; + + case eAssign: + g_mask |= mask; + break; + + case eClearBits: + g_mask &= ~mask; + break; + } + return g_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); + + std::default_random_engine generator; + std::uniform_int_distribution distribution(0, 3000000); + + while (mask_access(eGet) & thread_mask) + { + // random micro second sleep from zero to 3 seconds + int usec = distribution(generator); + + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + std::chrono::microseconds duration(usec); + std::this_thread::sleep_for(duration); + printf ("%s (thread = %u) after usleep ...\n", __FUNCTION__, thread_index); // Set break point at this line. + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); + return NULL; +} + + +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); // And that line. + + // Create 3 threads + g_thread_1 = std::thread(thread_func, (void*)&thread_index_1); + g_thread_2 = std::thread(thread_func, (void*)&thread_index_2); + g_thread_3 = std::thread(thread_func, (void*)&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 + g_thread_1.join(); + g_thread_2.join(); + g_thread_3.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/objc_type/Makefile b/packages/Python/lldbsuite/test/python_api/objc_type/Makefile new file mode 100644 index 000000000000..31e57fe28a58 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/objc_type/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation diff --git a/packages/Python/lldbsuite/test/python_api/objc_type/TestObjCType.py b/packages/Python/lldbsuite/test/python_api/objc_type/TestObjCType.py new file mode 100644 index 000000000000..e9b29b90e1a6 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/objc_type/TestObjCType.py @@ -0,0 +1,64 @@ +""" +Test SBType for ObjC classes. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ObjCSBTypeTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + self.line = line_number("main.m", '// Break at this line') + + @skipUnlessDarwin + @add_test_categories(['pyapi']) + def test(self): + """Test SBType for ObjC classes.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation("main.m", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + + + # Get Frame #0. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + + aBar = self.frame().FindVariable("aBar") + aBarType = aBar.GetType() + self.assertTrue(aBarType.IsValid(), "Bar should be a valid data type") + self.assertTrue(aBarType.GetName() == "Bar *", "Bar has the right name") + + self.assertTrue(aBarType.GetNumberOfDirectBaseClasses() == 1, "Bar has a superclass") + aFooType = aBarType.GetDirectBaseClassAtIndex(0) + + self.assertTrue(aFooType.IsValid(), "Foo should be a valid data type") + self.assertTrue(aFooType.GetName() == "Foo", "Foo has the right name") + + self.assertTrue(aBarType.GetNumberOfFields() == 1, "Bar has a field") + aBarField = aBarType.GetFieldAtIndex(0) + + self.assertTrue(aBarField.GetName() == "_iVar", "The field has the right name") diff --git a/packages/Python/lldbsuite/test/python_api/objc_type/main.m b/packages/Python/lldbsuite/test/python_api/objc_type/main.m new file mode 100644 index 000000000000..6ae54ade09ac --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/objc_type/main.m @@ -0,0 +1,52 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +@interface Foo: NSObject +{} +- (id) init; +@end + +@interface Bar: Foo +{ + int _iVar; +} +- (id) init; +@end + +@implementation Foo + +- (id) init +{ + self = [super init]; + return self; +} + +@end + +@implementation Bar + +- (id) init +{ + self = [super init]; + if (self) + self->_iVar = 5; + return self; +} + +@end + +int main() +{ + Bar* aBar = [Bar new]; + id nothing = [aBar noSuchSelector]; // Break at this line + return 0; +} + diff --git a/packages/Python/lldbsuite/test/python_api/process/Makefile b/packages/Python/lldbsuite/test/python_api/process/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/process/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/process/TestProcessAPI.py b/packages/Python/lldbsuite/test/python_api/process/TestProcessAPI.py new file mode 100644 index 000000000000..f312bc8a9247 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/process/TestProcessAPI.py @@ -0,0 +1,289 @@ +""" +Test SBProcess APIs, including ReadMemory(), WriteMemory(), and others. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbutil import get_stopped_thread, state_type_to_str +from lldbsuite.test.lldbtest import * + +class ProcessAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number("main.cpp", "// Set break point at this line and check variable 'my_char'.") + + @add_test_categories(['pyapi']) + def test_read_memory(self): + """Test Python SBProcess.ReadMemory() API.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + frame = thread.GetFrameAtIndex(0) + + # Get the SBValue for the global variable 'my_char'. + val = frame.FindValue("my_char", lldb.eValueTypeVariableGlobal) + self.DebugSBValue(val) + + # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and + # expect to get a Python string as the result object! + error = lldb.SBError() + self.assertFalse(val.TypeIsPointerType()) + content = process.ReadMemory(val.AddressOf().GetValueAsUnsigned(), 1, error) + if not error.Success(): + self.fail("SBProcess.ReadMemory() failed") + if self.TraceOn(): + print("memory content:", content) + + self.expect(content, "Result from SBProcess.ReadMemory() matches our expected output: 'x'", + exe=False, + startstr = 'x') + + # Read (char *)my_char_ptr. + val = frame.FindValue("my_char_ptr", lldb.eValueTypeVariableGlobal) + self.DebugSBValue(val) + cstring = process.ReadCStringFromMemory(val.GetValueAsUnsigned(), 256, error) + if not error.Success(): + self.fail("SBProcess.ReadCStringFromMemory() failed") + if self.TraceOn(): + print("cstring read is:", cstring) + + self.expect(cstring, "Result from SBProcess.ReadCStringFromMemory() matches our expected output", + exe=False, + startstr = 'Does it work?') + + # Get the SBValue for the global variable 'my_cstring'. + val = frame.FindValue("my_cstring", lldb.eValueTypeVariableGlobal) + self.DebugSBValue(val) + + # Due to the typemap magic (see lldb.swig), we pass in 256 to read at most 256 bytes + # from the address, and expect to get a Python string as the result object! + self.assertFalse(val.TypeIsPointerType()) + cstring = process.ReadCStringFromMemory(val.AddressOf().GetValueAsUnsigned(), 256, error) + if not error.Success(): + self.fail("SBProcess.ReadCStringFromMemory() failed") + if self.TraceOn(): + print("cstring read is:", cstring) + + self.expect(cstring, "Result from SBProcess.ReadCStringFromMemory() matches our expected output", + exe=False, + startstr = 'lldb.SBProcess.ReadCStringFromMemory() works!') + + # Get the SBValue for the global variable 'my_uint32'. + val = frame.FindValue("my_uint32", lldb.eValueTypeVariableGlobal) + self.DebugSBValue(val) + + # Due to the typemap magic (see lldb.swig), we pass in 4 to read 4 bytes + # from the address, and expect to get an int as the result! + self.assertFalse(val.TypeIsPointerType()) + my_uint32 = process.ReadUnsignedFromMemory(val.AddressOf().GetValueAsUnsigned(), 4, error) + if not error.Success(): + self.fail("SBProcess.ReadCStringFromMemory() failed") + if self.TraceOn(): + print("uint32 read is:", my_uint32) + + if my_uint32 != 12345: + self.fail("Result from SBProcess.ReadUnsignedFromMemory() does not match our expected output") + + @add_test_categories(['pyapi']) + def test_write_memory(self): + """Test Python SBProcess.WriteMemory() API.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + frame = thread.GetFrameAtIndex(0) + + # Get the SBValue for the global variable 'my_char'. + val = frame.FindValue("my_char", lldb.eValueTypeVariableGlobal) + self.DebugSBValue(val) + + # If the variable does not have a load address, there's no sense continuing. + if not val.GetLocation().startswith("0x"): + return + + # OK, let's get the hex location of the variable. + location = int(val.GetLocation(), 16) + + # The program logic makes the 'my_char' variable to have memory content as 'x'. + # But we want to use the WriteMemory() API to assign 'a' to the variable. + + # Now use WriteMemory() API to write 'a' into the global variable. + error = lldb.SBError() + result = process.WriteMemory(location, 'a', error) + if not error.Success() or result != 1: + self.fail("SBProcess.WriteMemory() failed") + + # Read from the memory location. This time it should be 'a'. + # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and + # expect to get a Python string as the result object! + content = process.ReadMemory(location, 1, error) + if not error.Success(): + self.fail("SBProcess.ReadMemory() failed") + if self.TraceOn(): + print("memory content:", content) + + self.expect(content, "Result from SBProcess.ReadMemory() matches our expected output: 'a'", + exe=False, + startstr = 'a') + + @add_test_categories(['pyapi']) + def test_access_my_int(self): + """Test access 'my_int' using Python SBProcess.GetByteOrder() and other APIs.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + frame = thread.GetFrameAtIndex(0) + + # Get the SBValue for the global variable 'my_int'. + val = frame.FindValue("my_int", lldb.eValueTypeVariableGlobal) + self.DebugSBValue(val) + + # If the variable does not have a load address, there's no sense continuing. + if not val.GetLocation().startswith("0x"): + return + + # OK, let's get the hex location of the variable. + location = int(val.GetLocation(), 16) + + # Note that the canonical from of the bytearray is little endian. + from lldbsuite.test.lldbutil import int_to_bytearray, bytearray_to_int + + byteSize = val.GetByteSize() + bytes = int_to_bytearray(256, byteSize) + + byteOrder = process.GetByteOrder() + if byteOrder == lldb.eByteOrderBig: + bytes.reverse() + elif byteOrder == lldb.eByteOrderLittle: + pass + else: + # Neither big endian nor little endian? Return for now. + # Add more logic here if we want to handle other types. + return + + # The program logic makes the 'my_int' variable to have int type and value of 0. + # But we want to use the WriteMemory() API to assign 256 to the variable. + + # Now use WriteMemory() API to write 256 into the global variable. + new_value = str(bytes) + error = lldb.SBError() + result = process.WriteMemory(location, new_value, error) + if not error.Success() or result != byteSize: + self.fail("SBProcess.WriteMemory() failed") + + # Make sure that the val we got originally updates itself to notice the change: + self.expect(val.GetValue(), + "SBProcess.ReadMemory() successfully writes (int)256 to the memory location for 'my_int'", + exe=False, + startstr = '256') + + # And for grins, get the SBValue for the global variable 'my_int' again, to make sure that also tracks the new value: + val = frame.FindValue("my_int", lldb.eValueTypeVariableGlobal) + self.expect(val.GetValue(), + "SBProcess.ReadMemory() successfully writes (int)256 to the memory location for 'my_int'", + exe=False, + startstr = '256') + + # Now read the memory content. The bytearray should have (byte)1 as the second element. + content = process.ReadMemory(location, byteSize, error) + if not error.Success(): + self.fail("SBProcess.ReadMemory() failed") + + # Use "ascii" as the encoding because each element of 'content' is in the range [0..255]. + new_bytes = bytearray(content, "ascii") + + # The bytearray_to_int utility function expects a little endian bytearray. + if byteOrder == lldb.eByteOrderBig: + new_bytes.reverse() + + new_value = bytearray_to_int(new_bytes, byteSize) + if new_value != 256: + self.fail("Memory content read from 'my_int' does not match (int)256") + + # Dump the memory content.... + if self.TraceOn(): + for i in new_bytes: + print("byte:", i) + + @add_test_categories(['pyapi']) + def test_remote_launch(self): + """Test SBProcess.RemoteLaunch() API with a process not in eStateConnected, and it should fail.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + if self.TraceOn(): + print("process state:", state_type_to_str(process.GetState())) + self.assertTrue(process.GetState() != lldb.eStateConnected) + + error = lldb.SBError() + success = process.RemoteLaunch(None, None, None, None, None, None, 0, False, error) + self.assertTrue(not success, "RemoteLaunch() should fail for process state != eStateConnected") + + @add_test_categories(['pyapi']) + def test_get_num_supported_hardware_watchpoints(self): + """Test SBProcess.GetNumSupportedHardwareWatchpoints() API with a process.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + error = lldb.SBError(); + num = process.GetNumSupportedHardwareWatchpoints(error) + if self.TraceOn() and error.Success(): + print("Number of supported hardware watchpoints: %d" % num) + diff --git a/packages/Python/lldbsuite/test/python_api/process/io/Makefile b/packages/Python/lldbsuite/test/python_api/process/io/Makefile new file mode 100644 index 000000000000..5361f2a5bbea --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/process/io/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +EXE := process_io + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/process/io/TestProcessIO.py b/packages/Python/lldbsuite/test/python_api/process/io/TestProcessIO.py new file mode 100644 index 000000000000..2944ee78acf1 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/process/io/TestProcessIO.py @@ -0,0 +1,206 @@ +"""Test Python APIs for process IO.""" + +from __future__ import print_function + + + +import os, sys, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ProcessIOTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Get the full path to our executable to be debugged. + self.exe = os.path.join(os.getcwd(), "process_io") + self.local_input_file = os.path.join(os.getcwd(), "input.txt") + self.local_output_file = os.path.join(os.getcwd(), "output.txt") + self.local_error_file = os.path.join(os.getcwd(), "error.txt") + + self.input_file = os.path.join(self.get_process_working_directory(), "input.txt") + self.output_file = os.path.join(self.get_process_working_directory(), "output.txt") + self.error_file = os.path.join(self.get_process_working_directory(), "error.txt") + self.lines = ["Line 1", "Line 2", "Line 3"] + + @skipIfWindows # stdio manipulation unsupported on Windows + @add_test_categories(['pyapi']) + def test_stdin_by_api(self): + """Exercise SBProcess.PutSTDIN().""" + self.build() + self.create_target() + self.run_process(True) + output = self.process.GetSTDOUT(1000) + self.check_process_output(output, output) + + @skipIfWindows # stdio manipulation unsupported on Windows + @add_test_categories(['pyapi']) + def test_stdin_redirection(self): + """Exercise SBLaunchInfo::AddOpenFileAction() for STDIN without specifying STDOUT or STDERR.""" + self.build() + self.create_target() + self.redirect_stdin() + self.run_process(False) + output = self.process.GetSTDOUT(1000) + self.check_process_output(output, output) + + @skipIfWindows # stdio manipulation unsupported on Windows + @add_test_categories(['pyapi']) + def test_stdout_redirection(self): + """Exercise SBLaunchInfo::AddOpenFileAction() for STDOUT without specifying STDIN or STDERR.""" + self.build() + self.create_target() + self.redirect_stdout() + self.run_process(True) + output = self.read_output_file_and_delete() + error = self.process.GetSTDOUT(1000) + self.check_process_output(output, error) + + @skipIfWindows # stdio manipulation unsupported on Windows + @add_test_categories(['pyapi']) + def test_stderr_redirection(self): + """Exercise SBLaunchInfo::AddOpenFileAction() for STDERR without specifying STDIN or STDOUT.""" + self.build() + self.create_target() + self.redirect_stderr() + self.run_process(True) + output = self.process.GetSTDOUT(1000) + error = self.read_error_file_and_delete() + self.check_process_output(output, error) + + @skipIfWindows # stdio manipulation unsupported on Windows + @add_test_categories(['pyapi']) + def test_stdout_stderr_redirection(self): + """Exercise SBLaunchInfo::AddOpenFileAction() for STDOUT and STDERR without redirecting STDIN.""" + self.build() + self.create_target() + self.redirect_stdout() + self.redirect_stderr() + self.run_process(True) + output = self.read_output_file_and_delete() + error = self.read_error_file_and_delete() + self.check_process_output(output, error) + + # target_file - path on local file system or remote file system if running remote + # local_file - path on local system + def read_file_and_delete(self, target_file, local_file): + if lldb.remote_platform: + self.runCmd('platform get-file "{remote}" "{local}"'.format( + remote=target_file, local=local_file)) + + self.assertTrue(os.path.exists(local_file), 'Make sure "{local}" file exists'.format(local=local_file)) + f = open(local_file, 'r') + contents = f.read() + f.close() + + #TODO: add 'platform delete-file' file command + #if lldb.remote_platform: + # self.runCmd('platform delete-file "{remote}"'.format(remote=target_file)) + os.unlink(local_file) + return contents + + def read_output_file_and_delete(self): + return self.read_file_and_delete(self.output_file, self.local_output_file) + + def read_error_file_and_delete(self): + return self.read_file_and_delete(self.error_file, self.local_error_file) + + def create_target(self): + '''Create the target and launch info that will be used by all tests''' + self.target = self.dbg.CreateTarget(self.exe) + self.launch_info = lldb.SBLaunchInfo([self.exe]) + self.launch_info.SetWorkingDirectory(self.get_process_working_directory()) + + def redirect_stdin(self): + '''Redirect STDIN (file descriptor 0) to use our input.txt file + + Make the input.txt file to use when redirecting STDIN, setup a cleanup action + to delete the input.txt at the end of the test in case exceptions are thrown, + and redirect STDIN in the launch info.''' + f = open(self.local_input_file, 'w') + for line in self.lines: + f.write(line + "\n") + f.close() + + if lldb.remote_platform: + self.runCmd('platform put-file "{local}" "{remote}"'.format( + local=self.local_input_file, remote=self.input_file)) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + os.unlink(self.local_input_file) + #TODO: add 'platform delete-file' file command + #if lldb.remote_platform: + # self.runCmd('platform delete-file "{remote}"'.format(remote=self.input_file)) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + self.launch_info.AddOpenFileAction(0, self.input_file, True, False); + + def redirect_stdout(self): + '''Redirect STDOUT (file descriptor 1) to use our output.txt file''' + self.launch_info.AddOpenFileAction(1, self.output_file, False, True); + + def redirect_stderr(self): + '''Redirect STDERR (file descriptor 2) to use our error.txt file''' + self.launch_info.AddOpenFileAction(2, self.error_file, False, True); + + def run_process(self, put_stdin): + '''Run the process to completion and optionally put lines to STDIN via the API if "put_stdin" is True''' + # Set the breakpoints + self.breakpoint = self.target.BreakpointCreateBySourceRegex('Set breakpoint here', lldb.SBFileSpec("main.c")) + self.assertTrue(self.breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + error = lldb.SBError() + # This should launch the process and it should exit by the time we get back + # because we have synchronous mode enabled + self.process = self.target.Launch (self.launch_info, error) + + self.assertTrue(error.Success(), "Make sure process launched successfully") + self.assertTrue(self.process, PROCESS_IS_VALID) + + if self.TraceOn(): + print("process launched.") + + # Frame #0 should be at our breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, self.breakpoint) + + self.assertTrue(len(threads) == 1) + self.thread = threads[0] + self.frame = self.thread.frames[0] + self.assertTrue(self.frame, "Frame 0 is valid.") + + if self.TraceOn(): + print("process stopped at breakpoint, sending STDIN via LLDB API.") + + # Write data to stdin via the public API if we were asked to + if put_stdin: + for line in self.lines: + self.process.PutSTDIN(line + "\n") + + # Let process continue so it will exit + self.process.Continue() + state = self.process.GetState() + self.assertTrue(state == lldb.eStateExited, PROCESS_IS_VALID) + + def check_process_output (self, output, error): + # Since we launched the process without specifying stdin/out/err, + # a pseudo terminal is used for stdout/err, and we are satisfied + # once "input line=>1" appears in stdout. + # See also main.c. + if self.TraceOn(): + print("output = '%s'" % output) + print("error = '%s'" % error) + + for line in self.lines: + check_line = 'input line to stdout: %s' % (line) + self.assertTrue(check_line in output, "verify stdout line shows up in STDOUT") + for line in self.lines: + check_line = 'input line to stderr: %s' % (line) + self.assertTrue(check_line in error, "verify stderr line shows up in STDERR") diff --git a/packages/Python/lldbsuite/test/python_api/process/io/main.c b/packages/Python/lldbsuite/test/python_api/process/io/main.c new file mode 100644 index 000000000000..c9a5707f0e17 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/process/io/main.c @@ -0,0 +1,19 @@ +#include + +int main(int argc, char const *argv[]) { + printf("Hello world.\n"); // Set breakpoint here + char line[100]; + if (fgets(line, sizeof(line), stdin)) { + fprintf(stdout, "input line to stdout: %s", line); + fprintf(stderr, "input line to stderr: %s", line); + } + if (fgets(line, sizeof(line), stdin)) { + fprintf(stdout, "input line to stdout: %s", line); + fprintf(stderr, "input line to stderr: %s", line); + } + if (fgets(line, sizeof(line), stdin)) { + fprintf(stdout, "input line to stdout: %s", line); + fprintf(stderr, "input line to stderr: %s", line); + } + printf("Exiting now\n"); +} diff --git a/packages/Python/lldbsuite/test/python_api/process/main.cpp b/packages/Python/lldbsuite/test/python_api/process/main.cpp new file mode 100644 index 000000000000..9610936e0534 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/process/main.cpp @@ -0,0 +1,31 @@ +//===-- 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 + +// This simple program is to test the lldb Python API related to process. + +char my_char = 'u'; +char my_cstring[] = "lldb.SBProcess.ReadCStringFromMemory() works!"; +char *my_char_ptr = (char *)"Does it work?"; +uint32_t my_uint32 = 12345; +int my_int = 0; + +int main (int argc, char const *argv[]) +{ + for (int i = 0; i < 3; ++i) { + printf("my_char='%c'\n", my_char); + ++my_char; + } + + printf("after the loop: my_char='%c'\n", my_char); // 'my_char' should print out as 'x'. + + return 0; // Set break point at this line and check variable 'my_char'. + // Use lldb Python API to set memory content for my_int and check the result. +} diff --git a/packages/Python/lldbsuite/test/python_api/rdar-12481949/Makefile b/packages/Python/lldbsuite/test/python_api/rdar-12481949/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/rdar-12481949/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/rdar-12481949/Test-rdar-12481949.py b/packages/Python/lldbsuite/test/python_api/rdar-12481949/Test-rdar-12481949.py new file mode 100644 index 000000000000..f56fd96e4665 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/rdar-12481949/Test-rdar-12481949.py @@ -0,0 +1,54 @@ +""" +Check that SBValue.GetValueAsSigned() does the right thing for a 32-bit -1. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class Radar12481949DataFormatterTestCase(TestBase): + + # test for rdar://problem/12481949 + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test_with_run_command(self): + """Check that SBValue.GetValueAsSigned() does the right thing for a 32-bit -1.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format delete hex', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.assertTrue(self.frame().FindVariable("myvar").GetValueAsSigned() == -1, "GetValueAsSigned() says -1") + self.assertTrue(self.frame().FindVariable("myvar").GetValueAsSigned() != 0xFFFFFFFF, "GetValueAsSigned() does not say 0xFFFFFFFF") + self.assertTrue(self.frame().FindVariable("myvar").GetValueAsSigned() != 0xFFFFFFFFFFFFFFFF, "GetValueAsSigned() does not say 0xFFFFFFFFFFFFFFFF") + + self.assertTrue(self.frame().FindVariable("myvar").GetValueAsUnsigned() != -1, "GetValueAsUnsigned() does not say -1") + self.assertTrue(self.frame().FindVariable("myvar").GetValueAsUnsigned() == 0xFFFFFFFF, "GetValueAsUnsigned() says 0xFFFFFFFF") + self.assertTrue(self.frame().FindVariable("myvar").GetValueAsUnsigned() != 0xFFFFFFFFFFFFFFFF, "GetValueAsUnsigned() does not says 0xFFFFFFFFFFFFFFFF") diff --git a/packages/Python/lldbsuite/test/python_api/rdar-12481949/main.cpp b/packages/Python/lldbsuite/test/python_api/rdar-12481949/main.cpp new file mode 100644 index 000000000000..e8ebf36f6190 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/rdar-12481949/main.cpp @@ -0,0 +1,17 @@ +//===-- 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 () +{ + int32_t myvar = -1; + printf ("%d\n", myvar); // Set break point at this line. + return myvar+1; +} diff --git a/packages/Python/lldbsuite/test/python_api/sbdata/Makefile b/packages/Python/lldbsuite/test/python_api/sbdata/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbdata/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/sbdata/TestSBData.py b/packages/Python/lldbsuite/test/python_api/sbdata/TestSBData.py new file mode 100644 index 000000000000..2ff516e10d8e --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbdata/TestSBData.py @@ -0,0 +1,348 @@ +"""Test the SBData APIs.""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +from math import fabs +import lldbsuite.test.lldbutil as lldbutil + +class SBDataAPICase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break on inside main.cpp. + self.line = line_number('main.cpp', '// set breakpoint here') + + @add_test_categories(['pyapi']) + def test_with_run_command(self): + """Test the SBData APIs.""" + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + target = self.dbg.GetSelectedTarget() + + process = target.GetProcess() + + thread = process.GetThreadAtIndex(0) + + frame = thread.GetSelectedFrame() + if self.TraceOn(): + print(frame) + foobar = frame.FindVariable('foobar') + self.assertTrue(foobar.IsValid()) + if self.TraceOn(): + print(foobar) + + data = foobar.GetPointeeData(0, 2) + + if self.TraceOn(): + print(data) + + offset = 0 + error = lldb.SBError() + + self.assert_data(data.GetUnsignedInt32, offset, 1) + offset += 4 + low = data.GetSignedInt16(error, offset) + self.assertTrue(error.Success()) + offset += 2 + high = data.GetSignedInt16(error, offset) + self.assertTrue(error.Success()) + offset += 2 + self.assertTrue ((low == 9 and high == 0) or (low == 0 and high == 9), 'foo[0].b == 9') + self.assertTrue( fabs(data.GetFloat(error, offset) - 3.14) < 1, 'foo[0].c == 3.14') + self.assertTrue(error.Success()) + offset += 4 + self.assert_data(data.GetUnsignedInt32, offset, 8) + offset += 4 + self.assert_data(data.GetUnsignedInt32, offset, 5) + offset += 4 + + self.runCmd("n") + + offset = 16 + + self.assert_data(data.GetUnsignedInt32, offset, 5) + + data = foobar.GetPointeeData(1, 1) + + offset = 0 + + self.assert_data(data.GetSignedInt32, offset, 8) + offset += 4 + self.assert_data(data.GetSignedInt32, offset, 7) + offset += 8 + self.assertTrue(data.GetUnsignedInt32(error, offset) == 0, 'do not read beyond end') + self.assertTrue(not error.Success()) + error.Clear() # clear the error for the next test + + star_foobar = foobar.Dereference() + self.assertTrue(star_foobar.IsValid()) + + data = star_foobar.GetData() + + if self.TraceOn(): + print(data) + + offset = 0 + self.assert_data(data.GetUnsignedInt32, offset, 1) + offset += 4 + self.assert_data(data.GetUnsignedInt32, offset, 9) + + foobar_addr = star_foobar.GetLoadAddress() + foobar_addr += 12 + + # http://llvm.org/bugs/show_bug.cgi?id=11579 + # lldb::SBValue::CreateValueFromAddress does not verify SBType::GetPointerType succeeds + # This should not crash LLDB. + nothing = foobar.CreateValueFromAddress("nothing", foobar_addr, star_foobar.GetType().GetBasicType(lldb.eBasicTypeInvalid)) + + new_foobar = foobar.CreateValueFromAddress("f00", foobar_addr, star_foobar.GetType()) + self.assertTrue(new_foobar.IsValid()) + if self.TraceOn(): + print(new_foobar) + + data = new_foobar.GetData() + + if self.TraceOn(): + print(data) + + self.assertTrue(data.uint32[0] == 8, 'then foo[1].a == 8') + self.assertTrue(data.uint32[1] == 7, 'then foo[1].b == 7') + self.assertTrue(fabs(data.float[2] - 3.14) < 1, 'foo[1].c == 3.14') # exploiting that sizeof(uint32) == sizeof(float) + + self.runCmd("n") + + offset = 0 + self.assert_data(data.GetUnsignedInt32, offset, 8) + offset += 4 + self.assert_data(data.GetUnsignedInt32, offset, 7) + offset += 4 + self.assertTrue(fabs(data.GetFloat(error, offset) - 3.14) < 1, 'foo[1].c == 3.14') + self.assertTrue(error.Success()) + + data = new_foobar.GetData() + + if self.TraceOn(): + print(data) + + offset = 0 + self.assert_data(data.GetUnsignedInt32, offset, 8) + offset += 4 + self.assert_data(data.GetUnsignedInt32, offset, 7) + offset += 4 + self.assertTrue(fabs(data.GetFloat(error, offset) - 6.28) < 1, 'foo[1].c == 6.28') + self.assertTrue(error.Success()) + + self.runCmd("n") + + barfoo = frame.FindVariable('barfoo') + + data = barfoo.GetData() + + if self.TraceOn(): + print(barfoo) + + if self.TraceOn(): + print(data) + + offset = 0 + self.assert_data(data.GetUnsignedInt32, offset, 1) + offset += 4 + self.assert_data(data.GetUnsignedInt32, offset, 2) + offset += 4 + self.assertTrue(fabs(data.GetFloat(error, offset) - 3) < 1, 'barfoo[0].c == 3') + self.assertTrue(error.Success()) + offset += 4 + self.assert_data(data.GetUnsignedInt32, offset, 4) + offset += 4 + self.assert_data(data.GetUnsignedInt32, offset, 5) + offset += 4 + self.assertTrue(fabs(data.GetFloat(error, offset) - 6) < 1, 'barfoo[1].c == 6') + self.assertTrue(error.Success()) + + new_object = barfoo.CreateValueFromData("new_object",data,barfoo.GetType().GetBasicType(lldb.eBasicTypeInt)) + + if self.TraceOn(): + print(new_object) + + self.assertTrue(new_object.GetValue() == "1", 'new_object == 1') + + data.SetData(error, 'A\0\0\0', data.GetByteOrder(), data.GetAddressByteSize()) + self.assertTrue(error.Success()) + + data2 = lldb.SBData() + data2.SetData(error, 'BCD', data.GetByteOrder(), data.GetAddressByteSize()) + self.assertTrue(error.Success()) + + data.Append(data2) + + if self.TraceOn(): + print(data) + + # this breaks on EBCDIC + offset = 0 + self.assert_data(data.GetUnsignedInt32, offset, 65) + offset += 4 + self.assert_data(data.GetUnsignedInt8, offset, 66) + offset += 1 + self.assert_data(data.GetUnsignedInt8, offset, 67) + offset += 1 + self.assert_data(data.GetUnsignedInt8, offset, 68) + offset += 1 + + # check the new API calls introduced per LLVM llvm.org/prenhancement request + # 11619 (Allow creating SBData values from arrays or primitives in Python) + + hello_str = "hello!" + data2 = lldb.SBData.CreateDataFromCString(process.GetByteOrder(),process.GetAddressByteSize(),hello_str) + self.assertTrue(len(data2.uint8) == len(hello_str)) + self.assertTrue(data2.uint8[0] == 104, 'h == 104') + self.assertTrue(data2.uint8[1] == 101, 'e == 101') + self.assertTrue(data2.uint8[2] == 108, 'l == 108') + self.assert_data(data2.GetUnsignedInt8, 3, 108) # l + self.assertTrue(data2.uint8[4] == 111, 'o == 111') + self.assert_data(data2.GetUnsignedInt8, 5, 33) # ! + + uint_lists = [ [1,2,3,4,5], [int(i) for i in [1, 2, 3, 4, 5]] ] + int_lists = [ [2, -2], [int(i) for i in [2, -2]] ] + + for l in uint_lists: + data2 = lldb.SBData.CreateDataFromUInt64Array(process.GetByteOrder(), process.GetAddressByteSize(), l) + self.assert_data(data2.GetUnsignedInt64, 0, 1) + self.assert_data(data2.GetUnsignedInt64, 8, 2) + self.assert_data(data2.GetUnsignedInt64, 16, 3) + self.assert_data(data2.GetUnsignedInt64, 24, 4) + self.assert_data(data2.GetUnsignedInt64, 32, 5) + + self.assertTrue(data2.uint64s == [1,2,3,4,5], 'read_data_helper failure: data2 == [1,2,3,4,5]') + + for l in int_lists: + data2 = lldb.SBData.CreateDataFromSInt32Array(process.GetByteOrder(), process.GetAddressByteSize(), l) + self.assertTrue(data2.sint32[0:2] == [2,-2], 'signed32 data2 = [2,-2]') + + data2.Append(lldb.SBData.CreateDataFromSInt64Array(process.GetByteOrder(), process.GetAddressByteSize(), int_lists[0])) + self.assert_data(data2.GetSignedInt32, 0, 2) + self.assert_data(data2.GetSignedInt32, 4, -2) + self.assertTrue(data2.sint64[1:3] == [2,-2], 'signed64 data2 = [2,-2]') + + for l in int_lists: + data2 = lldb.SBData.CreateDataFromSInt64Array(process.GetByteOrder(), process.GetAddressByteSize(), l) + self.assert_data(data2.GetSignedInt64, 0, 2) + self.assert_data(data2.GetSignedInt64, 8, -2) + self.assertTrue(data2.sint64[0:2] == [2,-2], 'signed64 data2 = [2,-2]') + + for l in uint_lists: + data2 = lldb.SBData.CreateDataFromUInt32Array(process.GetByteOrder(), process.GetAddressByteSize(), l) + self.assert_data(data2.GetUnsignedInt32,0, 1) + self.assert_data(data2.GetUnsignedInt32,4, 2) + self.assert_data(data2.GetUnsignedInt32,8, 3) + self.assert_data(data2.GetUnsignedInt32,12, 4) + self.assert_data(data2.GetUnsignedInt32,16, 5) + + bool_list = [True, True, False, False, True, False] + + data2 = lldb.SBData.CreateDataFromSInt32Array(process.GetByteOrder(), process.GetAddressByteSize(), bool_list) + self.assertTrue(data2.sint32[0:6] == [1, 1, 0, 0, 1, 0], 'signed32 data2 = [1, 1, 0, 0, 1, 0]') + + data2 = lldb.SBData.CreateDataFromUInt32Array(process.GetByteOrder(), process.GetAddressByteSize(), bool_list) + self.assertTrue(data2.uint32[0:6] == [1, 1, 0, 0, 1, 0], 'unsigned32 data2 = [1, 1, 0, 0, 1, 0]') + + data2 = lldb.SBData.CreateDataFromSInt64Array(process.GetByteOrder(), process.GetAddressByteSize(), bool_list) + self.assertTrue(data2.sint64[0:6] == [1, 1, 0, 0, 1, 0], 'signed64 data2 = [1, 1, 0, 0, 1, 0]') + + data2 = lldb.SBData.CreateDataFromUInt64Array(process.GetByteOrder(), process.GetAddressByteSize(), bool_list) + self.assertTrue(data2.uint64[0:6] == [1, 1, 0, 0, 1, 0], 'signed64 data2 = [1, 1, 0, 0, 1, 0]') + + data2 = lldb.SBData.CreateDataFromDoubleArray(process.GetByteOrder(),process.GetAddressByteSize(),[3.14,6.28,2.71]) + self.assertTrue( fabs(data2.GetDouble(error,0) - 3.14) < 0.5, 'double data2[0] = 3.14') + self.assertTrue(error.Success()) + self.assertTrue( fabs(data2.GetDouble(error,8) - 6.28) < 0.5, 'double data2[1] = 6.28') + self.assertTrue(error.Success()) + self.assertTrue( fabs(data2.GetDouble(error,16) - 2.71) < 0.5, 'double data2[2] = 2.71') + self.assertTrue(error.Success()) + + data2 = lldb.SBData() + + data2.SetDataFromCString(hello_str) + self.assertTrue(len(data2.uint8) == len(hello_str)) + self.assert_data(data2.GetUnsignedInt8, 0, 104) + self.assert_data(data2.GetUnsignedInt8, 1, 101) + self.assert_data(data2.GetUnsignedInt8, 2, 108) + self.assert_data(data2.GetUnsignedInt8, 3, 108) + self.assert_data(data2.GetUnsignedInt8, 4, 111) + self.assert_data(data2.GetUnsignedInt8, 5, 33) + + data2.SetDataFromUInt64Array([1,2,3,4,5]) + self.assert_data(data2.GetUnsignedInt64, 0, 1) + self.assert_data(data2.GetUnsignedInt64, 8, 2) + self.assert_data(data2.GetUnsignedInt64, 16, 3) + self.assert_data(data2.GetUnsignedInt64, 24, 4) + self.assert_data(data2.GetUnsignedInt64, 32, 5) + + self.assertTrue(data2.uint64[0] == 1, 'read_data_helper failure: set data2[0] = 1') + self.assertTrue(data2.uint64[1] == 2, 'read_data_helper failure: set data2[1] = 2') + self.assertTrue(data2.uint64[2] == 3, 'read_data_helper failure: set data2[2] = 3') + self.assertTrue(data2.uint64[3] == 4, 'read_data_helper failure: set data2[3] = 4') + self.assertTrue(data2.uint64[4] == 5, 'read_data_helper failure: set data2[4] = 5') + + self.assertTrue(data2.uint64[0:2] == [1,2], 'read_data_helper failure: set data2[0:2] = [1,2]') + + data2.SetDataFromSInt32Array([2, -2]) + self.assert_data(data2.GetSignedInt32, 0, 2) + self.assert_data(data2.GetSignedInt32, 4, -2) + + data2.SetDataFromSInt64Array([2, -2]) + self.assert_data(data2.GetSignedInt32, 0, 2) + self.assert_data(data2.GetSignedInt32, 8, -2) + + data2.SetDataFromUInt32Array([1,2,3,4,5]) + self.assert_data(data2.GetUnsignedInt32, 0, 1) + self.assert_data(data2.GetUnsignedInt32, 4, 2) + self.assert_data(data2.GetUnsignedInt32, 8, 3) + self.assert_data(data2.GetUnsignedInt32, 12, 4) + self.assert_data(data2.GetUnsignedInt32, 16, 5) + + self.assertTrue(data2.uint32[0] == 1, 'read_data_helper failure: set 32-bit data2[0] = 1') + self.assertTrue(data2.uint32[1] == 2, 'read_data_helper failure: set 32-bit data2[1] = 2') + self.assertTrue(data2.uint32[2] == 3, 'read_data_helper failure: set 32-bit data2[2] = 3') + self.assertTrue(data2.uint32[3] == 4, 'read_data_helper failure: set 32-bit data2[3] = 4') + self.assertTrue(data2.uint32[4] == 5, 'read_data_helper failure: set 32-bit data2[4] = 5') + + data2.SetDataFromDoubleArray([3.14,6.28,2.71]) + self.assertTrue( fabs(data2.GetDouble(error,0) - 3.14) < 0.5, 'set double data2[0] = 3.14') + self.assertTrue( fabs(data2.GetDouble(error,8) - 6.28) < 0.5, 'set double data2[1] = 6.28') + self.assertTrue( fabs(data2.GetDouble(error,16) - 2.71) < 0.5, 'set double data2[2] = 2.71') + + self.assertTrue( fabs(data2.double[0] - 3.14) < 0.5, 'read_data_helper failure: set double data2[0] = 3.14') + self.assertTrue( fabs(data2.double[1] - 6.28) < 0.5, 'read_data_helper failure: set double data2[1] = 6.28') + self.assertTrue( fabs(data2.double[2] - 2.71) < 0.5, 'read_data_helper failure: set double data2[2] = 2.71') + + def assert_data(self, func, arg, expected): + """ Asserts func(SBError error, arg) == expected. """ + error = lldb.SBError() + result = func(error, arg) + if not error.Success(): + stream = lldb.SBStream() + error.GetDescription(stream) + self.assertTrue(error.Success(), + "%s(error, %s) did not succeed: %s" % (func.__name__, + arg, + stream.GetData())) + self.assertTrue(expected == result, "%s(error, %s) == %s != %s" % (func.__name__, arg, result, expected)) diff --git a/packages/Python/lldbsuite/test/python_api/sbdata/main.cpp b/packages/Python/lldbsuite/test/python_api/sbdata/main.cpp new file mode 100644 index 000000000000..6018475d83c0 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbdata/main.cpp @@ -0,0 +1,43 @@ +//===-- 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 + +struct foo +{ + uint32_t a; + uint32_t b; + float c; + foo() : a(0), b(1), c(3.14) {} + foo(uint32_t A, uint32_t B, float C) : + a(A), + b(B), + c(C) + {} +}; + +int main (int argc, char const *argv[]) +{ + foo* foobar = new foo[2]; + + foobar[0].a = 1; + foobar[0].b = 9; + + foobar[1].a = 8; + foobar[1].b = 5; + + foobar[1].b = 7; // set breakpoint here + + foobar[1].c = 6.28; + + foo barfoo[] = {foo(1,2,3), foo(4,5,6)}; + + delete[] foobar; + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/sbtype_typeclass/TestSBTypeTypeClass.py b/packages/Python/lldbsuite/test/python_api/sbtype_typeclass/TestSBTypeTypeClass.py new file mode 100644 index 000000000000..80305e303d03 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbtype_typeclass/TestSBTypeTypeClass.py @@ -0,0 +1,4 @@ +import lldbsuite.test.lldbinline as lldbinline +import lldbsuite.test.lldbtest as lldbtest + +lldbinline.MakeInlineTest(__file__, globals(), [lldbtest.skipIfFreeBSD,lldbtest.skipIfLinux,lldbtest.skipIfWindows]) diff --git a/packages/Python/lldbsuite/test/python_api/sbtype_typeclass/main.m b/packages/Python/lldbsuite/test/python_api/sbtype_typeclass/main.m new file mode 100644 index 000000000000..599d36107f41 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbtype_typeclass/main.m @@ -0,0 +1,34 @@ +//===-- main.m --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#import + +@interface ThisClassTestsThings : NSObject +@end + +@implementation ThisClassTestsThings +- (int)doSomething { + + id s = self; + NSLog(@"%@",s); //% s = self.frame().FindVariable("s"); s.SetPreferDynamicValue(lldb.eDynamicCanRunTarget) + //% s_type = s.GetType() + //% typeClass = s_type.GetTypeClass() + //% condition = (typeClass == lldb.eTypeClassClass) or (typeClass ==lldb.eTypeClassObjCObject) or (typeClass == lldb.eTypeClassObjCInterface) or (typeClass == lldb.eTypeClassObjCObjectPointer) or (typeClass == lldb.eTypeClassPointer) + //% self.assertTrue(condition, "s has the wrong TypeClass") + return 0; +} +- (id)init { + return (self = [super init]); +} +@end + + +int main (int argc, char const *argv[]) +{ + return [[[ThisClassTestsThings alloc] init] doSomething]; +} diff --git a/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/Makefile b/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/Makefile new file mode 100644 index 000000000000..a9c1edd1bdcf --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../make +CXX_SOURCES := main.cpp +CXXFLAGS += -std=c++11 +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/TestSBValueConstAddrOf.py b/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/TestSBValueConstAddrOf.py new file mode 100644 index 000000000000..a3d43c1bdeeb --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/TestSBValueConstAddrOf.py @@ -0,0 +1,3 @@ +import lldbsuite.test.lldbinline as lldbinline + +lldbinline.MakeInlineTest(__file__, globals()) diff --git a/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/main.cpp b/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/main.cpp new file mode 100644 index 000000000000..eb9c8e0c142a --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbvalue_const_addrof/main.cpp @@ -0,0 +1,40 @@ +#include +#include + +struct RegisterContext +{ + uintptr_t r0; + uintptr_t r1; + uintptr_t r2; + uintptr_t r3; + uintptr_t r4; + uintptr_t pc; + uintptr_t fp; + uintptr_t sp; +}; + +struct ThreadInfo { + uint32_t tid; + const char *name; + RegisterContext regs; + ThreadInfo *next; +}; +int main (int argc, char const *argv[], char const *envp[]); + +ThreadInfo g_thread2 = { 0x2222, "thread2", { 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, (uintptr_t)&main, 0x2006, 0x2007 }, NULL }; +ThreadInfo g_thread1 = { 0x1111, "thread1", { 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, (uintptr_t)&main, 0x1006, 0x1007 }, &g_thread2 }; +ThreadInfo *g_thread_list_ptr = &g_thread1; + +int main (int argc, char const *argv[], char const *envp[]) +{ + printf ("g_thread_list is %p\n", g_thread_list_ptr); + return 0; //% v = lldb.target.FindFirstGlobalVariable('g_thread_list_ptr') + //% v_gla = v.GetChildMemberWithName('regs').GetLoadAddress() + //% v_aof = v.GetChildMemberWithName('regs').AddressOf().GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS) + //% expr = '(%s)0x%x' % (v.GetType().GetName(), v.GetValueAsUnsigned(0)) + //% e = v.CreateValueFromExpression('e', expr) + //% e_gla = e.GetChildMemberWithName('regs').GetLoadAddress() + //% e_aof = e.GetChildMemberWithName('regs').AddressOf().GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS) + //% self.assertTrue(v_gla == e_gla, "GetLoadAddress() differs") + //% self.assertTrue(v_aof == e_aof, "AddressOf() differs") +} diff --git a/packages/Python/lldbsuite/test/python_api/sbvalue_persist/Makefile b/packages/Python/lldbsuite/test/python_api/sbvalue_persist/Makefile new file mode 100644 index 000000000000..e5c7b9150738 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbvalue_persist/Makefile @@ -0,0 +1,15 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +# Clean renamed executable on 'make clean' +clean: OBJECTS+=no_synth + +# clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD +# targets. Other targets do not, which causes this test to fail. +# This flag enables FullDebugInfo for all targets. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS += -fno-limit-debug-info +endif + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/sbvalue_persist/TestSBValuePersist.py b/packages/Python/lldbsuite/test/python_api/sbvalue_persist/TestSBValuePersist.py new file mode 100644 index 000000000000..6edbbda4c112 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbvalue_persist/TestSBValuePersist.py @@ -0,0 +1,74 @@ +"""Test SBValue::Persist""" + +from __future__ import print_function + + + +import os, sys, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SBValuePersistTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24772") + def test(self): + """Test SBValue::Persist""" + self.build() + self.setTearDownCleanup() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, "break here") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synthetic clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + foo = self.frame().FindVariable("foo") + bar = self.frame().FindVariable("bar") + baz = self.frame().FindVariable("baz") + + self.assertTrue(foo.IsValid(), "foo is not valid") + self.assertTrue(bar.IsValid(), "bar is not valid") + self.assertTrue(baz.IsValid(), "baz is not valid") + + fooPersist = foo.Persist() + barPersist = bar.Persist() + bazPersist = baz.Persist() + + self.assertTrue(fooPersist.IsValid(), "fooPersist is not valid") + self.assertTrue(barPersist.IsValid(), "barPersist is not valid") + self.assertTrue(bazPersist.IsValid(), "bazPersist is not valid") + + self.assertTrue(fooPersist.GetValueAsUnsigned(0) == 10, "fooPersist != 10") + self.assertTrue(barPersist.GetPointeeData().sint32[0] == 4, "barPersist != 4") + self.assertTrue(bazPersist.GetSummary() == '"85"', "bazPersist != 85") + + self.runCmd("continue") + + self.assertTrue(fooPersist.IsValid(), "fooPersist is not valid") + self.assertTrue(barPersist.IsValid(), "barPersist is not valid") + self.assertTrue(bazPersist.IsValid(), "bazPersist is not valid") + + self.assertTrue(fooPersist.GetValueAsUnsigned(0) == 10, "fooPersist != 10") + self.assertTrue(barPersist.GetPointeeData().sint32[0] == 4, "barPersist != 4") + self.assertTrue(bazPersist.GetSummary() == '"85"', "bazPersist != 85") + + self.expect("expr *(%s)" % (barPersist.GetName()), substrs = ['= 4']) diff --git a/packages/Python/lldbsuite/test/python_api/sbvalue_persist/main.cpp b/packages/Python/lldbsuite/test/python_api/sbvalue_persist/main.cpp new file mode 100644 index 000000000000..650d87acd6e6 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/sbvalue_persist/main.cpp @@ -0,0 +1,14 @@ +#include +#include + +void f() {} + +int main() { + int foo = 10; + int *bar = new int(4); + std::string baz = "85"; + + f(); // break here + f(); // break here + return 0; +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/python_api/section/Makefile b/packages/Python/lldbsuite/test/python_api/section/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/section/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/section/TestSectionAPI.py b/packages/Python/lldbsuite/test/python_api/section/TestSectionAPI.py new file mode 100644 index 000000000000..587216468f06 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/section/TestSectionAPI.py @@ -0,0 +1,41 @@ +""" +Test SBSection APIs. +""" + +from __future__ import print_function + + + +from lldbsuite.test.lldbtest import * + +class SectionAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + def test_get_target_byte_size(self): + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + exe = os.path.join(os.getcwd(), 'b.out') + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # find the .data section of the main module + mod = target.GetModuleAtIndex(0) + data_section = None + for s in mod.sections: + sect_type = s.GetSectionType() + if sect_type == lldb.eSectionTypeData: + data_section = s + break + elif sect_type == lldb.eSectionTypeContainer: + for i in range(s.GetNumSubSections()): + ss = s.GetSubSectionAtIndex(i) + sect_type = ss.GetSectionType() + if sect_type == lldb.eSectionTypeData: + data_section = ss + break + + self.assertIsNotNone(data_section) + self.assertEqual(data_section.target_byte_size, 1) diff --git a/packages/Python/lldbsuite/test/python_api/section/main.c b/packages/Python/lldbsuite/test/python_api/section/main.c new file mode 100644 index 000000000000..746681d721a9 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/section/main.c @@ -0,0 +1,28 @@ +//===-- 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 + +// This simple program is to test the lldb Python API SBSection. It includes +// somes global data, and so the build process produces a DATA section, which +// the test code can use to query for the target byte size + +char my_global_var_of_char_type = 'X'; + +int main (int argc, char const *argv[]) +{ + // this code just "does something" with the global so that it is not + // optimised away + if (argc > 1 && strlen(argv[1])) + { + my_global_var_of_char_type += argv[1][0]; + } + + return my_global_var_of_char_type; +} diff --git a/packages/Python/lldbsuite/test/python_api/signals/Makefile b/packages/Python/lldbsuite/test/python_api/signals/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/signals/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/signals/TestSignalsAPI.py b/packages/Python/lldbsuite/test/python_api/signals/TestSignalsAPI.py new file mode 100644 index 000000000000..9825c553cce9 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/signals/TestSignalsAPI.py @@ -0,0 +1,47 @@ +""" +Test SBProcess APIs, including ReadMemory(), WriteMemory(), and others. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbutil import get_stopped_thread, state_type_to_str +from lldbsuite.test.lldbtest import * + +class SignalsAPITestCase(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @expectedFlakeyLinux # this test fails 1/100 dosep runs + @skipIfWindows # Windows doesn't have signals + def test_ignore_signal(self): + """Test Python SBUnixSignals.Suppress/Stop/Notify() API.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + line = line_number("main.cpp", "// Set break point at this line and setup signal ignores.") + breakpoint = target.BreakpointCreateByLocation("main.cpp", line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + + unix_signals = process.GetUnixSignals() + sigint = unix_signals.GetSignalNumberFromName("SIGINT") + unix_signals.SetShouldSuppress(sigint, True) + unix_signals.SetShouldStop(sigint, False) + unix_signals.SetShouldNotify(sigint, False) + + process.Continue() + self.assertTrue(process.state == lldb.eStateExited, "The process should have exited") + self.assertTrue(process.GetExitStatus() == 0, "The process should have returned 0") diff --git a/packages/Python/lldbsuite/test/python_api/signals/main.cpp b/packages/Python/lldbsuite/test/python_api/signals/main.cpp new file mode 100644 index 000000000000..81924f435a1a --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/signals/main.cpp @@ -0,0 +1,28 @@ +//===-- 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 +#if defined(_WIN32) +#include +#else +#include +#include +#endif + +// This simple program is to test the lldb Python API related to process. + +int main (int argc, char const *argv[]) +{ +#if defined(_WIN32) + ::ExitProcess(1); +#else + kill(getpid(), SIGINT); // Set break point at this line and setup signal ignores. +#endif + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/symbol-context/Makefile b/packages/Python/lldbsuite/test/python_api/symbol-context/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/symbol-context/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/symbol-context/TestSymbolContext.py b/packages/Python/lldbsuite/test/python_api/symbol-context/TestSymbolContext.py new file mode 100644 index 000000000000..2fec8dba0365 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/symbol-context/TestSymbolContext.py @@ -0,0 +1,89 @@ +""" +Test SBSymbolContext APIs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class SymbolContextAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.line = line_number('main.c', '// Find the line number of function "c" here.') + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24778") + def test(self): + """Exercise SBSymbolContext API extensively.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + #print("breakpoint:", breakpoint) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.line. + from lldbsuite.test.lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + frame0 = thread.GetFrameAtIndex(0) + self.assertTrue(frame0.GetLineEntry().GetLine() == self.line) + + # Now get the SBSymbolContext from this frame. We want everything. :-) + context = frame0.GetSymbolContext(lldb.eSymbolContextEverything) + self.assertTrue(context) + + # Get the description of this module. + module = context.GetModule() + desc = lldbutil.get_description(module) + self.expect(desc, "The module should match", exe=False, + substrs = [os.path.join(self.mydir, 'a.out')]) + + compileUnit = context.GetCompileUnit() + self.expect(str(compileUnit), "The compile unit should match", exe=False, + substrs = [os.path.join(self.mydir, 'main.c')]) + + function = context.GetFunction() + self.assertTrue(function) + #print("function:", function) + + block = context.GetBlock() + self.assertTrue(block) + #print("block:", block) + + lineEntry = context.GetLineEntry() + #print("line entry:", lineEntry) + self.expect(lineEntry.GetFileSpec().GetDirectory(), "The line entry should have the correct directory", + exe=False, + substrs = [self.mydir]) + self.expect(lineEntry.GetFileSpec().GetFilename(), "The line entry should have the correct filename", + exe=False, + substrs = ['main.c']) + self.assertTrue(lineEntry.GetLine() == self.line, + "The line entry's line number should match ") + + symbol = context.GetSymbol() + self.assertTrue(function.GetName() == symbol.GetName() and symbol.GetName() == 'c', + "The symbol name should be 'c'") diff --git a/packages/Python/lldbsuite/test/python_api/symbol-context/main.c b/packages/Python/lldbsuite/test/python_api/symbol-context/main.c new file mode 100644 index 000000000000..3ac1825d8d21 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/symbol-context/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. +// +//===----------------------------------------------------------------------===// +#include + +// This simple program is to test the lldb Python API SBSymbolContext. +// When stopped on a frame, we can get the symbol context using the SBFrame API +// SBFrame.GetSymbolContext(). + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); + + return val; +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; // Find the line number of function "c" here. +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/target/Makefile b/packages/Python/lldbsuite/test/python_api/target/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/target/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/target/TestTargetAPI.py b/packages/Python/lldbsuite/test/python_api/target/TestTargetAPI.py new file mode 100644 index 000000000000..0231d285ecf9 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/target/TestTargetAPI.py @@ -0,0 +1,372 @@ +""" +Test SBTarget APIs. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TargetAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to of function 'c'. + self.line1 = line_number('main.c', '// Find the line number for breakpoint 1 here.') + self.line2 = line_number('main.c', '// Find the line number for breakpoint 2 here.') + self.line_main = line_number("main.c", "// Set a break at entry to main.") + + #rdar://problem/9700873 + # Find global variable value fails for dwarf if inferior not started + # (Was CrashTracer: [USER] 1 crash in Python at _lldb.so: lldb_private::MemoryCache::Read + 94) + # + # It does not segfaults now. But for dwarf, the variable value is None if + # the inferior process does not exist yet. The radar has been updated. + #@unittest232.skip("segmentation fault -- skipping") + @add_test_categories(['pyapi']) + def test_find_global_variables(self): + """Exercise SBTarget.FindGlobalVariables() API.""" + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.find_global_variables('b.out') + + @add_test_categories(['pyapi']) + @expectedFailureWindows("llvm.org/pr24778") + def test_find_functions(self): + """Exercise SBTarget.FindFunctions() API.""" + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.find_functions('b.out') + + @add_test_categories(['pyapi']) + def test_get_description(self): + """Exercise SBTarget.GetDescription() API.""" + self.build() + self.get_description() + + @add_test_categories(['pyapi']) + def test_launch_new_process_and_redirect_stdout(self): + """Exercise SBTarget.Launch() API.""" + self.build() + self.launch_new_process_and_redirect_stdout() + + @add_test_categories(['pyapi']) + def test_resolve_symbol_context_with_address(self): + """Exercise SBTarget.ResolveSymbolContextForAddress() API.""" + self.build() + self.resolve_symbol_context_with_address() + + @add_test_categories(['pyapi']) + def test_get_platform(self): + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + target = self.create_simple_target('b.out') + platform = target.platform + self.assertTrue(platform, VALID_PLATFORM) + + @add_test_categories(['pyapi']) + def test_get_data_byte_size(self): + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + target = self.create_simple_target('b.out') + self.assertEqual(target.data_byte_size, 1) + + @add_test_categories(['pyapi']) + def test_get_code_byte_size(self): + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + target = self.create_simple_target('b.out') + self.assertEqual(target.code_byte_size, 1) + + @add_test_categories(['pyapi']) + def test_resolve_file_address(self): + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + target = self.create_simple_target('b.out') + + # find the file address in the .data section of the main + # module + data_section = self.find_data_section(target) + data_section_addr = data_section.file_addr + + # resolve the above address, and compare the address produced + # by the resolution against the original address/section + res_file_addr = target.ResolveFileAddress(data_section_addr) + self.assertTrue(res_file_addr.IsValid()) + + self.assertEqual(data_section_addr, res_file_addr.file_addr) + + data_section2 = res_file_addr.section + self.assertIsNotNone(data_section2) + self.assertEqual(data_section.name, data_section2.name) + + @add_test_categories(['pyapi']) + def test_read_memory(self): + d = {'EXE': 'b.out'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + target = self.create_simple_target('b.out') + + breakpoint = target.BreakpointCreateByLocation("main.c", self.line_main) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Put debugger into synchronous mode so when we target.LaunchSimple returns + # it will guaranteed to be at the breakpoint + self.dbg.SetAsync(False) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # find the file address in the .data section of the main + # module + data_section = self.find_data_section(target) + sb_addr = lldb.SBAddress(data_section, 0) + error = lldb.SBError() + content = target.ReadMemory(sb_addr, 1, error) + self.assertTrue(error.Success(), "Make sure memory read succeeded") + self.assertEqual(len(content), 1) + + def create_simple_target(self, fn): + exe = os.path.join(os.getcwd(), fn) + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + return target + + def find_data_section(self, target): + mod = target.GetModuleAtIndex(0) + data_section = None + for s in mod.sections: + sect_type = s.GetSectionType() + if sect_type == lldb.eSectionTypeData: + data_section = s + break + elif sect_type == lldb.eSectionTypeContainer: + for i in range(s.GetNumSubSections()): + ss = s.GetSubSectionAtIndex(i) + sect_type = ss.GetSectionType() + if sect_type == lldb.eSectionTypeData: + data_section = ss + break + + self.assertIsNotNone(data_section) + return data_section + + def find_global_variables(self, exe_name): + """Exercise SBTaget.FindGlobalVariables() API.""" + exe = os.path.join(os.getcwd(), exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + #rdar://problem/9700873 + # Find global variable value fails for dwarf if inferior not started + # (Was CrashTracer: [USER] 1 crash in Python at _lldb.so: lldb_private::MemoryCache::Read + 94) + # + # Remove the lines to create a breakpoint and to start the inferior + # which are workarounds for the dwarf case. + + breakpoint = target.BreakpointCreateByLocation('main.c', self.line1) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + # Make sure we hit our breakpoint: + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue (len(thread_list) == 1) + + value_list = target.FindGlobalVariables('my_global_var_of_char_type', 3) + self.assertTrue(value_list.GetSize() == 1) + my_global_var = value_list.GetValueAtIndex(0) + self.DebugSBValue(my_global_var) + self.assertTrue(my_global_var) + self.expect(my_global_var.GetName(), exe=False, + startstr = "my_global_var_of_char_type") + self.expect(my_global_var.GetTypeName(), exe=False, + startstr = "char") + self.expect(my_global_var.GetValue(), exe=False, + startstr = "'X'") + + # While we are at it, let's also exercise the similar SBModule.FindGlobalVariables() API. + for m in target.module_iter(): + if os.path.normpath(m.GetFileSpec().GetDirectory()) == os.getcwd() and m.GetFileSpec().GetFilename() == exe_name: + value_list = m.FindGlobalVariables(target, 'my_global_var_of_char_type', 3) + self.assertTrue(value_list.GetSize() == 1) + self.assertTrue(value_list.GetValueAtIndex(0).GetValue() == "'X'") + break + + def find_functions(self, exe_name): + """Exercise SBTaget.FindFunctions() API.""" + exe = os.path.join(os.getcwd(), exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + list = target.FindFunctions('c', lldb.eFunctionNameTypeAuto) + self.assertTrue(list.GetSize() == 1) + + for sc in list: + self.assertTrue(sc.GetModule().GetFileSpec().GetFilename() == exe_name) + self.assertTrue(sc.GetSymbol().GetName() == 'c') + + def get_description(self): + """Exercise SBTaget.GetDescription() API.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + from lldbsuite.test.lldbutil import get_description + + # get_description() allows no option to mean lldb.eDescriptionLevelBrief. + desc = get_description(target) + #desc = get_description(target, option=lldb.eDescriptionLevelBrief) + if not desc: + self.fail("SBTarget.GetDescription() failed") + self.expect(desc, exe=False, + substrs = ['a.out']) + self.expect(desc, exe=False, matching=False, + substrs = ['Target', 'Module', 'Breakpoint']) + + desc = get_description(target, option=lldb.eDescriptionLevelFull) + if not desc: + self.fail("SBTarget.GetDescription() failed") + self.expect(desc, exe=False, + substrs = ['a.out', 'Target', 'Module', 'Breakpoint']) + + @not_remote_testsuite_ready + def launch_new_process_and_redirect_stdout(self): + """Exercise SBTaget.Launch() API with redirected stdout.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Add an extra twist of stopping the inferior in a breakpoint, and then continue till it's done. + # We should still see the entire stdout redirected once the process is finished. + line = line_number('main.c', '// a(3) -> c(3)') + breakpoint = target.BreakpointCreateByLocation('main.c', line) + + # Now launch the process, do not stop at entry point, and redirect stdout to "stdout.txt" file. + # The inferior should run to completion after "process.Continue()" call. + local_path = "stdout.txt"; + if lldb.remote_platform: + stdout_path = lldbutil.append_to_process_working_directory("lldb-stdout-redirect.txt") + else: + stdout_path = local_path + error = lldb.SBError() + process = target.Launch (self.dbg.GetListener(), None, None, None, stdout_path, None, None, 0, False, error) + process.Continue() + #self.runCmd("process status") + if lldb.remote_platform: + # copy output file to host + lldb.remote_platform.Get(lldb.SBFileSpec(stdout_path), lldb.SBFileSpec(local_path)) + + # The 'stdout.txt' file should now exist. + self.assertTrue(os.path.isfile("stdout.txt"), + "'stdout.txt' exists due to redirected stdout via SBTarget.Launch() API.") + + # Read the output file produced by running the program. + with open('stdout.txt', 'r') as f: + output = f.read() + + # Let's delete the 'stdout.txt' file as a cleanup step. + try: + os.remove("stdout.txt") + pass + except OSError: + pass + + self.expect(output, exe=False, + substrs = ["a(1)", "b(2)", "a(3)"]) + + + def resolve_symbol_context_with_address(self): + """Exercise SBTaget.ResolveSymbolContextForAddress() API.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create the two breakpoints inside function 'a'. + breakpoint1 = target.BreakpointCreateByLocation('main.c', self.line1) + breakpoint2 = target.BreakpointCreateByLocation('main.c', self.line2) + #print("breakpoint1:", breakpoint1) + #print("breakpoint2:", breakpoint2) + self.assertTrue(breakpoint1 and + breakpoint1.GetNumLocations() == 1, + VALID_BREAKPOINT) + self.assertTrue(breakpoint2 and + breakpoint2.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.line1. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + #self.runCmd("process status") + frame0 = thread.GetFrameAtIndex(0) + lineEntry = frame0.GetLineEntry() + self.assertTrue(lineEntry.GetLine() == self.line1) + + address1 = lineEntry.GetStartAddress() + + # Continue the inferior, the breakpoint 2 should be hit. + process.Continue() + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + #self.runCmd("process status") + frame0 = thread.GetFrameAtIndex(0) + lineEntry = frame0.GetLineEntry() + self.assertTrue(lineEntry.GetLine() == self.line2) + + address2 = lineEntry.GetStartAddress() + + #print("address1:", address1) + #print("address2:", address2) + + # Now call SBTarget.ResolveSymbolContextForAddress() with the addresses from our line entry. + context1 = target.ResolveSymbolContextForAddress(address1, lldb.eSymbolContextEverything) + context2 = target.ResolveSymbolContextForAddress(address2, lldb.eSymbolContextEverything) + + self.assertTrue(context1 and context2) + #print("context1:", context1) + #print("context2:", context2) + + # Verify that the context point to the same function 'a'. + symbol1 = context1.GetSymbol() + symbol2 = context2.GetSymbol() + self.assertTrue(symbol1 and symbol2) + #print("symbol1:", symbol1) + #print("symbol2:", symbol2) + + from lldbsuite.test.lldbutil import get_description + desc1 = get_description(symbol1) + desc2 = get_description(symbol2) + self.assertTrue(desc1 and desc2 and desc1 == desc2, + "The two addresses should resolve to the same symbol") diff --git a/packages/Python/lldbsuite/test/python_api/target/main.c b/packages/Python/lldbsuite/test/python_api/target/main.c new file mode 100644 index 000000000000..ba82a5437ae3 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/target/main.c @@ -0,0 +1,60 @@ +//===-- 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 + +// This simple program is to test the lldb Python API SBTarget. +// +// When stopped on breakppint 1, and then 2, we can get the line entries using +// SBFrame API SBFrame.GetLineEntry(). We'll get the start addresses for the +// two line entries; with the start address (of SBAddress type), we can then +// resolve the symbol context using the SBTarget API +// SBTarget.ResolveSymbolContextForAddress(). +// +// The two symbol context should point to the same symbol, i.e., 'a' function. + +char my_global_var_of_char_type = 'X'; // Test SBTarget.FindGlobalVariables(...). + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) // Find the line number for breakpoint 1 here. + val = b(val); + else if (val >= 3) + val = c(val); + + return val; // Find the line number for breakpoint 2 here. +} + +int b(int val) +{ + return c(val); +} + +int c(int val) +{ + return val + 3; +} + +int main (int argc, char const *argv[]) +{ + // Set a break at entry to main. + int A1 = a(1); // a(1) -> b(1) -> c(1) + printf("a(1) returns %d\n", A1); + + int B2 = b(2); // b(2) -> c(2) + printf("b(2) returns %d\n", B2); + + int A3 = a(3); // a(3) -> c(3) + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/thread/Makefile b/packages/Python/lldbsuite/test/python_api/thread/Makefile new file mode 100644 index 000000000000..aa257ae2bd2a --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/thread/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES ?= main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/thread/TestThreadAPI.py b/packages/Python/lldbsuite/test/python_api/thread/TestThreadAPI.py new file mode 100644 index 000000000000..dad829a48f2f --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/thread/TestThreadAPI.py @@ -0,0 +1,245 @@ +""" +Test SBThread APIs. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbutil import get_stopped_thread, get_caller_symbol +from lldbsuite.test.lldbtest import * + +class ThreadAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + def test_get_process(self): + """Test Python SBThread.GetProcess() API.""" + self.build() + self.get_process() + + @add_test_categories(['pyapi']) + def test_get_stop_description(self): + """Test Python SBThread.GetStopDescription() API.""" + self.build() + self.get_stop_description() + + @add_test_categories(['pyapi']) + def test_run_to_address(self): + """Test Python SBThread.RunToAddress() API.""" + # We build a different executable than the default build() does. + d = {'CXX_SOURCES': 'main2.cpp', 'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.run_to_address(self.exe_name) + + @add_test_categories(['pyapi']) + @expectedFailureFreeBSD # llvm.org/pr20476 + @expectedFailureWindows # Test crashes + def test_step_out_of_malloc_into_function_b(self): + """Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b().""" + # We build a different executable than the default build() does. + d = {'CXX_SOURCES': 'main2.cpp', 'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.step_out_of_malloc_into_function_b(self.exe_name) + + @add_test_categories(['pyapi']) + def test_step_over_3_times(self): + """Test Python SBThread.StepOver() API.""" + # We build a different executable than the default build() does. + d = {'CXX_SOURCES': 'main2.cpp', 'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.step_over_3_times(self.exe_name) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number within main.cpp to break inside main(). + self.break_line = line_number("main.cpp", "// Set break point at this line and check variable 'my_char'.") + # Find the line numbers within main2.cpp for step_out_of_malloc_into_function_b() and step_over_3_times(). + self.step_out_of_malloc = line_number("main2.cpp", "// thread step-out of malloc into function b.") + self.after_3_step_overs = line_number("main2.cpp", "// we should reach here after 3 step-over's.") + + # We'll use the test method name as the exe_name for executable comppiled from main2.cpp. + self.exe_name = self.testMethodName + + def get_process(self): + """Test Python SBThread.GetProcess() API.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.break_line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + self.runCmd("breakpoint list") + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + self.runCmd("process status") + + proc_of_thread = thread.GetProcess() + #print("proc_of_thread:", proc_of_thread) + self.assertTrue(proc_of_thread.GetProcessID() == process.GetProcessID()) + + def get_stop_description(self): + """Test Python SBThread.GetStopDescription() API.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.break_line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + #self.runCmd("breakpoint list") + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + #self.runCmd("process status") + + # Due to the typemap magic (see lldb.swig), we pass in an (int)length to GetStopDescription + # and expect to get a Python string as the return object! + # The 100 is just an arbitrary number specifying the buffer size. + stop_description = thread.GetStopDescription(100) + self.expect(stop_description, exe=False, + startstr = 'breakpoint') + + def step_out_of_malloc_into_function_b(self, exe_name): + """Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b().""" + exe = os.path.join(os.getcwd(), exe_name) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByName('malloc') + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + while True: + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + caller_symbol = get_caller_symbol(thread) + if not caller_symbol: + self.fail("Test failed: could not locate the caller symbol of malloc") + + # Our top frame may be an inlined function in malloc() (e.g., on + # FreeBSD). Apply a simple heuristic of stepping out until we find + # a non-malloc caller + while caller_symbol.startswith("malloc"): + thread.StepOut() + self.assertTrue(thread.IsValid(), "Thread valid after stepping to outer malloc") + caller_symbol = get_caller_symbol(thread) + + if caller_symbol == "b(int)": + break + process.Continue() + + # On Linux malloc calls itself in some case. Remove the breakpoint because we don't want + # to hit it during step-out. + target.BreakpointDelete(breakpoint.GetID()) + + thread.StepOut() + self.runCmd("thread backtrace") + self.assertTrue(thread.GetFrameAtIndex(0).GetLineEntry().GetLine() == self.step_out_of_malloc, + "step out of malloc into function b is successful") + + def step_over_3_times(self, exe_name): + """Test Python SBThread.StepOver() API.""" + exe = os.path.join(os.getcwd(), exe_name) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation('main2.cpp', self.step_out_of_malloc) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + self.runCmd("breakpoint list") + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.step_out_of_malloc. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + self.runCmd("thread backtrace") + frame0 = thread.GetFrameAtIndex(0) + lineEntry = frame0.GetLineEntry() + self.assertTrue(lineEntry.GetLine() == self.step_out_of_malloc) + + thread.StepOver() + thread.StepOver() + thread.StepOver() + self.runCmd("thread backtrace") + + # Verify that we are stopped at the correct source line number in main2.cpp. + frame0 = thread.GetFrameAtIndex(0) + lineEntry = frame0.GetLineEntry() + self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) + # Expected failure with clang as the compiler. + # rdar://problem/9223880 + # + # Which has been fixed on the lldb by compensating for inaccurate line + # table information with r140416. + self.assertTrue(lineEntry.GetLine() == self.after_3_step_overs) + + def run_to_address(self, exe_name): + """Test Python SBThread.RunToAddress() API.""" + exe = os.path.join(os.getcwd(), exe_name) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation('main2.cpp', self.step_out_of_malloc) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + self.runCmd("breakpoint list") + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame #0 should be on self.step_out_of_malloc. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + self.runCmd("thread backtrace") + frame0 = thread.GetFrameAtIndex(0) + lineEntry = frame0.GetLineEntry() + self.assertTrue(lineEntry.GetLine() == self.step_out_of_malloc) + + # Get the start/end addresses for this line entry. + start_addr = lineEntry.GetStartAddress().GetLoadAddress(target) + end_addr = lineEntry.GetEndAddress().GetLoadAddress(target) + if self.TraceOn(): + print("start addr:", hex(start_addr)) + print("end addr:", hex(end_addr)) + + # Disable the breakpoint. + self.assertTrue(target.DisableAllBreakpoints()) + self.runCmd("breakpoint list") + + thread.StepOver() + thread.StepOver() + thread.StepOver() + self.runCmd("thread backtrace") + + # Now ask SBThread to run to the address 'start_addr' we got earlier, which + # corresponds to self.step_out_of_malloc line entry's start address. + thread.RunToAddress(start_addr) + self.runCmd("process status") + #self.runCmd("thread backtrace") diff --git a/packages/Python/lldbsuite/test/python_api/thread/main.cpp b/packages/Python/lldbsuite/test/python_api/thread/main.cpp new file mode 100644 index 000000000000..8d8069036898 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/thread/main.cpp @@ -0,0 +1,26 @@ +//===-- 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 + +// This simple program is to test the lldb Python API related to thread. + +char my_char = 'u'; +int my_int = 0; + +int main (int argc, char const *argv[]) +{ + for (int i = 0; i < 3; ++i) { + printf("my_char='%c'\n", my_char); + ++my_char; + } + + printf("after the loop: my_char='%c'\n", my_char); // 'my_char' should print out as 'x'. + + return 0; // Set break point at this line and check variable 'my_char'. +} diff --git a/packages/Python/lldbsuite/test/python_api/thread/main2.cpp b/packages/Python/lldbsuite/test/python_api/thread/main2.cpp new file mode 100644 index 000000000000..a95d7d7aec1a --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/thread/main2.cpp @@ -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 +#include + +int a(int); +int b(int); +int c(int); + +int a(int val) +{ + if (val <= 1) + return b(val); + else if (val >= 3) + return c(val); + + return val; +} + +int b(int val) +{ + int rc = c(val); + void *ptr = malloc(1024); // thread step-out of malloc into function b. + if (!ptr) + return -1; + else + printf("ptr=%p\n", ptr); + return rc; // we should reach here after 3 step-over's. +} + +int c(int val) +{ + return val + 3; +} + +int main (int argc, char const *argv[]) +{ + int A1 = a(1); + printf("a(1) returns %d\n", A1); + + int B2 = b(2); + printf("b(2) returns %d\n", B2); + + int A3 = a(3); + printf("a(3) returns %d\n", A3); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/type/Makefile b/packages/Python/lldbsuite/test/python_api/type/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/type/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/type/TestTypeList.py b/packages/Python/lldbsuite/test/python_api/type/TestTypeList.py new file mode 100644 index 000000000000..d9e5719844e2 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/type/TestTypeList.py @@ -0,0 +1,116 @@ +""" +Test SBType and SBTypeList API. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TypeAndTypeListTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break at. + self.source = 'main.cpp' + self.line = line_number(self.source, '// Break at this line') + + @add_test_categories(['pyapi']) + def test(self): + """Exercise SBType and SBTypeList API.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + exe = os.path.join(os.getcwd(), self.exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Get Frame #0. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + + # Get the type 'Task'. + type_list = target.FindTypes('Task') + if self.TraceOn(): + print("Size of type_list from target.FindTypes('Task') query: %d" % type_list.GetSize()) + self.assertTrue(len(type_list) >= 1) # a second Task make be scared up by the Objective-C runtime + for type in type_list: + self.assertTrue(type) + self.DebugSBType(type) + self.assertFalse(type.IsAnonymousType(), "Task is not anonymous") + for field in type.fields: + if field.name == "type": + for enum_member in field.type.enum_members: + self.assertTrue(enum_member) + self.DebugSBType(enum_member.type) + elif field.name == "my_type_is_nameless": + self.assertTrue(field.type.IsAnonymousType(), "my_type_is_nameless has an anonymous type") + elif field.name == "my_type_is_named": + self.assertFalse(field.type.IsAnonymousType(), "my_type_is_named has a named type") + + # Pass an empty string. LLDB should not crash. :-) + fuzz_types = target.FindTypes(None) + fuzz_type = target.FindFirstType(None) + + # Now use the SBTarget.FindFirstType() API to find 'Task'. + task_type = target.FindFirstType('Task') + self.assertTrue(task_type) + self.DebugSBType(task_type) + + # Get the reference type of 'Task', just for fun. + task_ref_type = task_type.GetReferenceType() + self.assertTrue(task_ref_type) + self.DebugSBType(task_ref_type) + + # Get the pointer type of 'Task', which is the same as task_head's type. + task_pointer_type = task_type.GetPointerType() + self.assertTrue(task_pointer_type) + self.DebugSBType(task_pointer_type) + + # Get variable 'task_head'. + task_head = frame0.FindVariable('task_head') + self.assertTrue(task_head, VALID_VARIABLE) + self.DebugSBValue(task_head) + task_head_type = task_head.GetType() + self.DebugSBType(task_head_type) + self.assertTrue(task_head_type.IsPointerType()) + + self.assertTrue(task_head_type == task_pointer_type) + + # Get the pointee type of 'task_head'. + task_head_pointee_type = task_head_type.GetPointeeType() + self.DebugSBType(task_head_pointee_type) + + self.assertTrue(task_type == task_head_pointee_type) + + # We'll now get the child member 'id' from 'task_head'. + id = task_head.GetChildMemberWithName('id') + self.DebugSBValue(id) + id_type = id.GetType() + self.DebugSBType(id_type) + + # SBType.GetBasicType() takes an enum 'BasicType' (lldb-enumerations.h). + int_type = id_type.GetBasicType(lldb.eBasicTypeInt) + self.assertTrue(id_type == int_type) diff --git a/packages/Python/lldbsuite/test/python_api/type/main.cpp b/packages/Python/lldbsuite/test/python_api/type/main.cpp new file mode 100644 index 000000000000..b7f3dcc7fbea --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/type/main.cpp @@ -0,0 +1,60 @@ +//===-- 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 + +class Task { +public: + int id; + Task *next; + enum { + TASK_TYPE_1, + TASK_TYPE_2 + } type; + struct { + int x; + } my_type_is_nameless; + struct name { + int x; + } my_type_is_named; + Task(int i, Task *n): + id(i), + next(n), + type(TASK_TYPE_1) + {} +}; + + +int main (int argc, char const *argv[]) +{ + Task *task_head = new Task(-1, NULL); + Task *task1 = new Task(1, NULL); + Task *task2 = new Task(2, NULL); + Task *task3 = new Task(3, NULL); // Orphaned. + Task *task4 = new Task(4, NULL); + Task *task5 = new Task(5, NULL); + + task_head->next = task1; + task1->next = task2; + task2->next = task4; + task4->next = task5; + + int total = 0; + Task *t = task_head; + while (t != NULL) { + if (t->id >= 0) + ++total; + t = t->next; + } + printf("We have a total number of %d tasks\n", total); + + // This corresponds to an empty task list. + Task *empty_task_head = new Task(-1, NULL); + + return 0; // Break at this line +} diff --git a/packages/Python/lldbsuite/test/python_api/value/Makefile b/packages/Python/lldbsuite/test/python_api/value/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/value/TestValueAPI.py b/packages/Python/lldbsuite/test/python_api/value/TestValueAPI.py new file mode 100644 index 000000000000..77ff07ea6488 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/TestValueAPI.py @@ -0,0 +1,135 @@ +""" +Test some SBValue APIs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ValueAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to of function 'c'. + self.line = line_number('main.c', '// Break at this line') + + @expectedFailureWindows("llvm.org/pr24772") + @add_test_categories(['pyapi']) + def test(self): + """Exercise some SBValue APIs.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + exe = os.path.join(os.getcwd(), self.exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation('main.c', self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Get Frame #0. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + + # Get global variable 'days_of_week'. + list = target.FindGlobalVariables('days_of_week', 1) + days_of_week = list.GetValueAtIndex(0) + self.assertTrue(days_of_week, VALID_VARIABLE) + self.assertTrue(days_of_week.GetNumChildren() == 7, VALID_VARIABLE) + self.DebugSBValue(days_of_week) + + # Get global variable 'weekdays'. + list = target.FindGlobalVariables('weekdays', 1) + weekdays = list.GetValueAtIndex(0) + self.assertTrue(weekdays, VALID_VARIABLE) + self.assertTrue(weekdays.GetNumChildren() == 5, VALID_VARIABLE) + self.DebugSBValue(weekdays) + + # Get global variable 'g_table'. + list = target.FindGlobalVariables('g_table', 1) + g_table = list.GetValueAtIndex(0) + self.assertTrue(g_table, VALID_VARIABLE) + self.assertTrue(g_table.GetNumChildren() == 2, VALID_VARIABLE) + self.DebugSBValue(g_table) + + fmt = lldbutil.BasicFormatter() + cvf = lldbutil.ChildVisitingFormatter(indent_child=2) + rdf = lldbutil.RecursiveDecentFormatter(indent_child=2) + if self.TraceOn(): + print(fmt.format(days_of_week)) + print(cvf.format(days_of_week)) + print(cvf.format(weekdays)) + print(rdf.format(g_table)) + + # Get variable 'my_int_ptr'. + value = frame0.FindVariable('my_int_ptr') + self.assertTrue(value, VALID_VARIABLE) + self.DebugSBValue(value) + + # Get what 'my_int_ptr' points to. + pointed = value.GetChildAtIndex(0) + self.assertTrue(pointed, VALID_VARIABLE) + self.DebugSBValue(pointed) + + # While we are at it, verify that 'my_int_ptr' points to 'g_my_int'. + symbol = target.ResolveLoadAddress(int(pointed.GetLocation(), 0)).GetSymbol() + self.assertTrue(symbol) + self.expect(symbol.GetName(), exe=False, + startstr = 'g_my_int') + + # Get variable 'str_ptr'. + value = frame0.FindVariable('str_ptr') + self.assertTrue(value, VALID_VARIABLE) + self.DebugSBValue(value) + + # SBValue::TypeIsPointerType() should return true. + self.assertTrue(value.TypeIsPointerType()) + + # Verify the SBValue::GetByteSize() API is working correctly. + arch = self.getArchitecture() + if arch == 'i386': + self.assertTrue(value.GetByteSize() == 4) + elif arch == 'x86_64': + self.assertTrue(value.GetByteSize() == 8) + + # Get child at index 5 => 'Friday'. + child = value.GetChildAtIndex(5, lldb.eNoDynamicValues, True) + self.assertTrue(child, VALID_VARIABLE) + self.DebugSBValue(child) + + self.expect(child.GetSummary(), exe=False, + substrs = ['Friday']) + + # Now try to get at the same variable using GetValueForExpressionPath(). + # These two SBValue objects should have the same value. + val2 = value.GetValueForExpressionPath('[5]') + self.assertTrue(val2, VALID_VARIABLE) + self.DebugSBValue(val2) + self.assertTrue(child.GetValue() == val2.GetValue() and + child.GetSummary() == val2.GetSummary()) + + val_i = target.EvaluateExpression('i') + val_s = target.EvaluateExpression('s') + val_a = target.EvaluateExpression('a') + self.assertTrue(val_s.GetChildMemberWithName('a').AddressOf(), VALID_VARIABLE) + self.assertTrue(val_a.Cast(val_i.GetType()).AddressOf(), VALID_VARIABLE) diff --git a/packages/Python/lldbsuite/test/python_api/value/change_values/Makefile b/packages/Python/lldbsuite/test/python_api/value/change_values/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/change_values/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/value/change_values/TestChangeValueAPI.py b/packages/Python/lldbsuite/test/python_api/value/change_values/TestChangeValueAPI.py new file mode 100644 index 000000000000..77aefe05be46 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/change_values/TestChangeValueAPI.py @@ -0,0 +1,148 @@ +""" +Test some SBValue APIs. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ChangeValueAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to of function 'c'. + self.line = line_number('main.c', '// Stop here and set values') + self.check_line = line_number('main.c', '// Stop here and check values') + self.end_line = line_number ('main.c', '// Set a breakpoint here at the end') + + @expectedFailureWindows("llvm.org/pr24772") + @add_test_categories(['pyapi']) + def test_change_value(self): + """Exercise the SBValue::SetValueFromCString API.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + exe = os.path.join(os.getcwd(), self.exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation('main.c', self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Create the breakpoint inside the function 'main' + check_breakpoint = target.BreakpointCreateByLocation('main.c', self.check_line) + self.assertTrue(check_breakpoint, VALID_BREAKPOINT) + + # Create the breakpoint inside function 'main'. + end_breakpoint = target.BreakpointCreateByLocation('main.c', self.end_line) + self.assertTrue(end_breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Get Frame #0. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + self.assertTrue (frame0.IsValid(), "Got a valid frame.") + + # Get the val variable and change it: + error = lldb.SBError() + + val_value = frame0.FindVariable ("val") + self.assertTrue (val_value.IsValid(), "Got the SBValue for val") + actual_value = val_value.GetValueAsSigned (error, 0); + self.assertTrue (error.Success(), "Got a value from val") + self.assertTrue (actual_value == 100, "Got the right value from val") + + result = val_value.SetValueFromCString ("12345") + self.assertTrue (result, "Setting val returned True.") + actual_value = val_value.GetValueAsSigned (error, 0); + self.assertTrue (error.Success(), "Got a changed value from val") + self.assertTrue (actual_value == 12345, "Got the right changed value from val") + + # Now check that we can set a structure element: + + mine_value = frame0.FindVariable ("mine") + self.assertTrue (mine_value.IsValid(), "Got the SBValue for mine") + + mine_second_value = mine_value.GetChildMemberWithName ("second_val") + self.assertTrue (mine_second_value.IsValid(), "Got second_val from mine") + actual_value = mine_second_value.GetValueAsUnsigned (error, 0) + self.assertTrue (error.Success(), "Got an unsigned value for second_val") + self.assertTrue (actual_value == 5555) + + result = mine_second_value.SetValueFromCString ("98765") + self.assertTrue (result, "Success setting mine.second_value.") + actual_value = mine_second_value.GetValueAsSigned (error, 0); + self.assertTrue (error.Success(), "Got a changed value from mine.second_val") + self.assertTrue (actual_value == 98765, "Got the right changed value from mine.second_val") + + # Next do the same thing with the pointer version. + ptr_value = frame0.FindVariable ("ptr") + self.assertTrue (ptr_value.IsValid(), "Got the SBValue for ptr") + + ptr_second_value = ptr_value.GetChildMemberWithName ("second_val") + self.assertTrue (ptr_second_value.IsValid(), "Got second_val from ptr") + actual_value = ptr_second_value.GetValueAsUnsigned (error, 0) + self.assertTrue (error.Success(), "Got an unsigned value for ptr->second_val") + self.assertTrue (actual_value == 6666) + + result = ptr_second_value.SetValueFromCString ("98765") + self.assertTrue (result, "Success setting ptr->second_value.") + actual_value = ptr_second_value.GetValueAsSigned (error, 0); + self.assertTrue (error.Success(), "Got a changed value from ptr->second_val") + self.assertTrue (actual_value == 98765, "Got the right changed value from ptr->second_val") + + # gcc may set multiple locations for breakpoint + breakpoint.SetEnabled(False) + + # Now continue, grab the stdout and make sure we changed the real values as well... + process.Continue(); + + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + + expected_value = "Val - 12345 Mine - 55, 98765, 55555555. Ptr - 66, 98765, 66666666" + stdout = process.GetSTDOUT(1000) + self.assertTrue (expected_value in stdout, "STDOUT showed changed values.") + + # Finally, change the stack pointer to 0, and we should not make it to our end breakpoint. + frame0 = thread.GetFrameAtIndex(0) + self.assertTrue (frame0.IsValid(), "Second time: got a valid frame.") + sp_value = frame0.FindValue ("sp", lldb.eValueTypeRegister); + self.assertTrue (sp_value.IsValid(), "Got a stack pointer value") + result = sp_value.SetValueFromCString("1") + self.assertTrue (result, "Setting sp returned true.") + actual_value = sp_value.GetValueAsUnsigned (error, 0) + self.assertTrue (error.Success(), "Got a changed value for sp") + self.assertTrue (actual_value == 1, "Got the right changed value for sp.") + + # Boundary condition test the SBValue.CreateValueFromExpression() API. + # LLDB should not crash! + nosuchval = mine_value.CreateValueFromExpression(None, None) + + process.Continue() + + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread == None, "We should not have managed to hit our second breakpoint with sp == 1") + + process.Kill() diff --git a/packages/Python/lldbsuite/test/python_api/value/change_values/main.c b/packages/Python/lldbsuite/test/python_api/value/change_values/main.c new file mode 100644 index 000000000000..01455c019647 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/change_values/main.c @@ -0,0 +1,29 @@ +#include +#include +#include + +struct foo +{ + uint8_t first_val; + uint32_t second_val; + uint64_t third_val; +}; + +int main () +{ + int val = 100; + struct foo mine = {55, 5555, 55555555}; + struct foo *ptr = (struct foo *) malloc (sizeof (struct foo)); + ptr->first_val = 66; + ptr->second_val = 6666; + ptr->third_val = 66666666; + + // Stop here and set values + printf ("Val - %d Mine - %d, %d, %llu. Ptr - %d, %d, %llu\n", val, + mine.first_val, mine.second_val, mine.third_val, + ptr->first_val, ptr->second_val, ptr->third_val); + + // Stop here and check values + printf ("This is just another call which we won't make it over %d.", val); + return 0; // Set a breakpoint here at the end +} diff --git a/packages/Python/lldbsuite/test/python_api/value/linked_list/Makefile b/packages/Python/lldbsuite/test/python_api/value/linked_list/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/linked_list/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/value/linked_list/TestValueAPILinkedList.py b/packages/Python/lldbsuite/test/python_api/value/linked_list/TestValueAPILinkedList.py new file mode 100644 index 000000000000..22d7e7ebf7e8 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/linked_list/TestValueAPILinkedList.py @@ -0,0 +1,133 @@ +""" +Test SBValue API linked_list_iter which treats the SBValue as a linked list and +supports iteration till the end of list is reached. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ValueAsLinkedListTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + self.exe_name = self.testMethodName + # Find the line number to break at. + self.line = line_number('main.cpp', '// Break at this line') + + @add_test_categories(['pyapi']) + def test(self): + """Exercise SBValue API linked_list_iter.""" + d = {'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + exe = os.path.join(os.getcwd(), self.exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create the breakpoint inside function 'main'. + breakpoint = target.BreakpointCreateByLocation('main.cpp', self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Get Frame #0. + self.assertTrue(process.GetState() == lldb.eStateStopped) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + + # Get variable 'task_head'. + task_head = frame0.FindVariable('task_head') + self.assertTrue(task_head, VALID_VARIABLE) + self.DebugSBValue(task_head) + + # By design (see main.cpp), the visited id's are: [1, 2, 4, 5]. + visitedIDs = [1, 2, 4, 5] + list = [] + + cvf = lldbutil.ChildVisitingFormatter(indent_child=2) + for t in task_head.linked_list_iter('next'): + self.assertTrue(t, VALID_VARIABLE) + # Make sure that 'next' corresponds to an SBValue with pointer type. + self.assertTrue(t.TypeIsPointerType()) + if self.TraceOn(): + print(cvf.format(t)) + list.append(int(t.GetChildMemberWithName("id").GetValue())) + + # Sanity checks that the we visited all the items (no more, no less). + if self.TraceOn(): + print("visited IDs:", list) + self.assertTrue(visitedIDs == list) + + # Let's exercise the linked_list_iter() API again, this time supplying + # our end of list test function. + def eol(val): + """Test function to determine end of list.""" + # End of list is reached if either the value object is invalid + # or it corresponds to a null pointer. + if not val or int(val.GetValue(), 16) == 0: + return True + # Also check the "id" for correct semantics. If id <= 0, the item + # is corrupted, let's return True to signify end of list. + if int(val.GetChildMemberWithName("id").GetValue(), 0) <= 0: + return True + + # Otherwise, return False. + return False + + list = [] + for t in task_head.linked_list_iter('next', eol): + self.assertTrue(t, VALID_VARIABLE) + # Make sure that 'next' corresponds to an SBValue with pointer type. + self.assertTrue(t.TypeIsPointerType()) + if self.TraceOn(): + print(cvf.format(t)) + list.append(int(t.GetChildMemberWithName("id").GetValue())) + + # Sanity checks that the we visited all the items (no more, no less). + if self.TraceOn(): + print("visited IDs:", list) + self.assertTrue(visitedIDs == list) + + # Get variable 'empty_task_head'. + empty_task_head = frame0.FindVariable('empty_task_head') + self.assertTrue(empty_task_head, VALID_VARIABLE) + self.DebugSBValue(empty_task_head) + + list = [] + # There is no iterable item from empty_task_head.linked_list_iter(). + for t in empty_task_head.linked_list_iter('next', eol): + if self.TraceOn(): + print(cvf.format(t)) + list.append(int(t.GetChildMemberWithName("id").GetValue())) + + self.assertTrue(len(list) == 0) + + # Get variable 'task_evil'. + task_evil = frame0.FindVariable('task_evil') + self.assertTrue(task_evil, VALID_VARIABLE) + self.DebugSBValue(task_evil) + + list = [] + # There 3 iterable items from task_evil.linked_list_iter(). :-) + for t in task_evil.linked_list_iter('next'): + if self.TraceOn(): + print(cvf.format(t)) + list.append(int(t.GetChildMemberWithName("id").GetValue())) + + self.assertTrue(len(list) == 3) diff --git a/packages/Python/lldbsuite/test/python_api/value/linked_list/main.cpp b/packages/Python/lldbsuite/test/python_api/value/linked_list/main.cpp new file mode 100644 index 000000000000..50517f487749 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/linked_list/main.cpp @@ -0,0 +1,56 @@ +//===-- 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 + +class Task { +public: + int id; + Task *next; + Task(int i, Task *n): + id(i), + next(n) + {} +}; + + +int main (int argc, char const *argv[]) +{ + Task *task_head = NULL; + Task *task1 = new Task(1, NULL); + Task *task2 = new Task(2, NULL); + Task *task3 = new Task(3, NULL); // Orphaned. + Task *task4 = new Task(4, NULL); + Task *task5 = new Task(5, NULL); + + task_head = task1; + task1->next = task2; + task2->next = task4; + task4->next = task5; + + int total = 0; + Task *t = task_head; + while (t != NULL) { + if (t->id >= 0) + ++total; + t = t->next; + } + printf("We have a total number of %d tasks\n", total); + + // This corresponds to an empty task list. + Task *empty_task_head = NULL; + + Task *task_evil = new Task(1, NULL); + Task *task_2 = new Task(2, NULL); + Task *task_3 = new Task(3, NULL); + task_evil->next = task_2; + task_2->next = task_3; + task_3->next = task_evil; // In order to cause inifinite loop. :-) + + return 0; // Break at this line +} diff --git a/packages/Python/lldbsuite/test/python_api/value/main.c b/packages/Python/lldbsuite/test/python_api/value/main.c new file mode 100644 index 000000000000..a00795750de0 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value/main.c @@ -0,0 +1,52 @@ +//===-- 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 + +// This simple program is to test the lldb Python API SBValue.GetChildAtIndex(). + +int g_my_int = 100; + +const char *days_of_week[7] = { "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" }; + +const char *weekdays[5] = { "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday" }; + +const char **g_table[2] = { days_of_week, weekdays }; + +typedef int MyInt; + +struct MyStruct +{ + int a; + int b; +}; + +int main (int argc, char const *argv[]) +{ + int i; + MyInt a = 12345; + struct MyStruct s = { 11, 22 }; + int *my_int_ptr = &g_my_int; + printf("my_int_ptr points to location %p\n", my_int_ptr); + const char **str_ptr = days_of_week; + for (i = 0; i < 7; ++i) + printf("%s\n", str_ptr[i]); // Break at this line + // and do str_ptr_val.GetChildAtIndex(5, lldb.eNoDynamicValues, True). + + return 0; +} diff --git a/packages/Python/lldbsuite/test/python_api/value_var_update/Makefile b/packages/Python/lldbsuite/test/python_api/value_var_update/Makefile new file mode 100644 index 000000000000..4b0e5814e3e3 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value_var_update/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +C_SOURCES := main.c +CFLAGS_EXTRAS += -std=c99 +# See TestHelloWorld.py, which specifies the executable name with a dictionary. +EXE := hello_world + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/value_var_update/TestValueVarUpdate.py b/packages/Python/lldbsuite/test/python_api/value_var_update/TestValueVarUpdate.py new file mode 100644 index 000000000000..251bbcf4b24d --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value_var_update/TestValueVarUpdate.py @@ -0,0 +1,59 @@ +"""Test SBValue::GetValueDidChange""" + +from __future__ import print_function + + + +import os, sys, time +import lldb +import time +from lldbsuite.test.lldbtest import * + +class HelloWorldTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Get the full path to our executable to be attached/debugged. + self.exe = os.path.join(os.getcwd(), self.testMethodName) + self.d = {'EXE': self.testMethodName} + + @add_test_categories(['pyapi']) + def test_with_process_launch_api(self): + """Test SBValue::GetValueDidChange""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + target = self.dbg.CreateTarget(self.exe) + + breakpoint = target.BreakpointCreateBySourceRegex("break here", lldb.SBFileSpec("main.c")) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + i = self.frame().FindVariable("i") + i_val = i.GetValueAsUnsigned(0) + c = self.frame().FindVariable("c") + + # Update any values from the SBValue objects so we can ask them if they changed after a continue + i.GetValueDidChange() + c.GetChildAtIndex(1).GetValueDidChange() + c.GetChildAtIndex(0).GetChildAtIndex(0).GetValueDidChange() + + if self.TraceOn(): self.runCmd("frame variable") + + self.runCmd("continue") + + if self.TraceOn(): self.runCmd("frame variable") + + self.assertTrue(i_val != i.GetValueAsUnsigned(0), "GetValue() is saying a lie") + self.assertTrue(i.GetValueDidChange(), "GetValueDidChange() is saying a lie") + + # Check complex type + self.assertTrue(c.GetChildAtIndex(0).GetChildAtIndex(0).GetValueDidChange() and + not c.GetChildAtIndex(1).GetValueDidChange(), "GetValueDidChange() is saying a lie") diff --git a/packages/Python/lldbsuite/test/python_api/value_var_update/main.c b/packages/Python/lldbsuite/test/python_api/value_var_update/main.c new file mode 100644 index 000000000000..9ffca5cbb9f8 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/value_var_update/main.c @@ -0,0 +1,15 @@ +struct complex_type { + struct { long l; } inner; + struct complex_type *complex_ptr; +}; + +int main() { + int i = 0; + struct complex_type c = { { 1L }, &c }; + for (int j = 3; j < 20; j++) + { + c.inner.l += (i += j); + i = i - 1; // break here + } + return i; +} diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/Makefile b/packages/Python/lldbsuite/test/python_api/watchpoint/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/TestSetWatchpoint.py b/packages/Python/lldbsuite/test/python_api/watchpoint/TestSetWatchpoint.py new file mode 100644 index 000000000000..264e21240dd9 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/TestSetWatchpoint.py @@ -0,0 +1,93 @@ +""" +Use lldb Python SBValue API to create a watchpoint for read_write of 'globl' var. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class SetWatchpointAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + + @add_test_categories(['pyapi']) + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watch_val(self): + """Exercise SBValue.Watch() API to set a watchpoint.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # We should be stopped due to the breakpoint. Get frame #0. + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + # Watch 'global' for read and write. + value = frame0.FindValue('global', lldb.eValueTypeVariableGlobal) + error = lldb.SBError(); + watchpoint = value.Watch(True, True, True, error) + self.assertTrue(value and watchpoint, + "Successfully found the variable and set a watchpoint") + self.DebugSBValue(value) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + print(watchpoint) + + # Continue. Expect the program to stop due to the variable being written to. + process.Continue() + + if (self.TraceOn()): + lldbutil.print_stacktraces(process) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint) + self.assertTrue(thread, "The thread stopped due to watchpoint") + self.DebugSBValue(value) + + # Continue. Expect the program to stop due to the variable being read from. + process.Continue() + + if (self.TraceOn()): + lldbutil.print_stacktraces(process) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint) + self.assertTrue(thread, "The thread stopped due to watchpoint") + self.DebugSBValue(value) + + # Continue the process. We don't expect the program to be stopped again. + process.Continue() + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/TestWatchpointIgnoreCount.py b/packages/Python/lldbsuite/test/python_api/watchpoint/TestWatchpointIgnoreCount.py new file mode 100644 index 000000000000..a15e73347030 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/TestWatchpointIgnoreCount.py @@ -0,0 +1,89 @@ +""" +Use lldb Python SBWatchpoint API to set the ignore count. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class WatchpointIgnoreCountTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + + @add_test_categories(['pyapi']) + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_set_watch_ignore_count(self): + """Test SBWatchpoint.SetIgnoreCount() API.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create a breakpoint on main.c in order to set our watchpoint later. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # We should be stopped due to the breakpoint. Get frame #0. + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + # Watch 'global' for read and write. + value = frame0.FindValue('global', lldb.eValueTypeVariableGlobal) + error = lldb.SBError(); + watchpoint = value.Watch(True, True, True, error) + self.assertTrue(value and watchpoint, + "Successfully found the variable and set a watchpoint") + self.DebugSBValue(value) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + # There should be only 1 watchpoint location under the target. + self.assertTrue(target.GetNumWatchpoints() == 1) + watchpoint = target.GetWatchpointAtIndex(0) + self.assertTrue(watchpoint.IsEnabled()) + self.assertTrue(watchpoint.GetIgnoreCount() == 0) + watch_id = watchpoint.GetID() + self.assertTrue(watch_id != 0) + print(watchpoint) + + # Now immediately set the ignore count to 2. When we continue, expect the + # inferior to run to its completion without stopping due to watchpoint. + watchpoint.SetIgnoreCount(2) + print(watchpoint) + process.Continue() + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + + # Verify some vital statistics. + self.assertTrue(watchpoint) + self.assertTrue(watchpoint.GetWatchSize() == 4) + self.assertTrue(watchpoint.GetHitCount() == 2) + print(watchpoint) diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/TestWatchpointIter.py b/packages/Python/lldbsuite/test/python_api/watchpoint/TestWatchpointIter.py new file mode 100644 index 000000000000..315450280675 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/TestWatchpointIter.py @@ -0,0 +1,115 @@ +""" +Use lldb Python SBTarget API to iterate on the watchpoint(s) for the target. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class WatchpointIteratorTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + + @add_test_categories(['pyapi']) + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watch_iter(self): + """Exercise SBTarget.watchpoint_iter() API to iterate on the available watchpoints.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Create a breakpoint on main.c in order to set our watchpoint later. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # We should be stopped due to the breakpoint. Get frame #0. + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + # Watch 'global' for read and write. + value = frame0.FindValue('global', lldb.eValueTypeVariableGlobal) + error = lldb.SBError(); + watchpoint = value.Watch(True, True, True, error) + self.assertTrue(value and watchpoint, + "Successfully found the variable and set a watchpoint") + self.DebugSBValue(value) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + # There should be only 1 watchpoint location under the target. + self.assertTrue(target.GetNumWatchpoints() == 1) + self.assertTrue(watchpoint.IsEnabled()) + watch_id = watchpoint.GetID() + self.assertTrue(watch_id != 0) + + # Continue. Expect the program to stop due to the variable being written to. + process.Continue() + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + # Print the stack traces. + lldbutil.print_stacktraces(process) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint) + self.assertTrue(thread, "The thread stopped due to watchpoint") + self.DebugSBValue(value) + + # We currently only support hardware watchpoint. Verify that we have a + # meaningful hardware index at this point. Exercise the printed repr of + # SBWatchpointLocation. + print(watchpoint) + self.assertTrue(watchpoint.GetHardwareIndex() != -1) + + # SBWatchpoint.GetDescription() takes a description level arg. + print(lldbutil.get_description(watchpoint, lldb.eDescriptionLevelFull)) + + # Now disable the 'rw' watchpoint. The program won't stop when it reads + # 'global' next. + watchpoint.SetEnabled(False) + self.assertTrue(watchpoint.GetHardwareIndex() == -1) + self.assertFalse(watchpoint.IsEnabled()) + + # Continue. The program does not stop again when the variable is being + # read from because the watchpoint location has been disabled. + process.Continue() + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + + # Verify some vital statistics and exercise the iterator API. + for watchpoint in target.watchpoint_iter(): + self.assertTrue(watchpoint) + self.assertTrue(watchpoint.GetWatchSize() == 4) + self.assertTrue(watchpoint.GetHitCount() == 1) + print(watchpoint) + diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/condition/Makefile b/packages/Python/lldbsuite/test/python_api/watchpoint/condition/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/condition/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/condition/TestWatchpointConditionAPI.py b/packages/Python/lldbsuite/test/python_api/watchpoint/condition/TestWatchpointConditionAPI.py new file mode 100644 index 000000000000..f30bf856aa07 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/condition/TestWatchpointConditionAPI.py @@ -0,0 +1,89 @@ +""" +Test watchpoint condition API. +""" + +from __future__ import print_function + + + +import os, time +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class WatchpointConditionAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # And the watchpoint variable declaration line number. + self.decl = line_number(self.source, '// Watchpoint variable declaration.') + # Build dictionary to have unique executable names for each test method. + self.exe_name = self.testMethodName + self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name} + + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @skipIfWindows # Watchpoints not supported on Windows, and this test hangs + def test_watchpoint_cond_api(self): + """Test watchpoint condition API.""" + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + exe = os.path.join(os.getcwd(), self.exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # We should be stopped due to the breakpoint. Get frame #0. + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + # Watch 'global' for write. + value = frame0.FindValue('global', lldb.eValueTypeVariableGlobal) + error = lldb.SBError(); + watchpoint = value.Watch(True, False, True, error) + self.assertTrue(value and watchpoint, + "Successfully found the variable and set a watchpoint") + self.DebugSBValue(value) + + # Now set the condition as "global==5". + watchpoint.SetCondition('global==5') + self.expect(watchpoint.GetCondition(), exe=False, + startstr = 'global==5') + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + print(watchpoint) + + # Continue. Expect the program to stop due to the variable being written to. + process.Continue() + + if (self.TraceOn()): + lldbutil.print_stacktraces(process) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint) + self.assertTrue(thread, "The thread stopped due to watchpoint") + self.DebugSBValue(value) + + # Verify that the condition is met. + self.assertTrue(value.GetValueAsUnsigned() == 5) diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/condition/main.cpp b/packages/Python/lldbsuite/test/python_api/watchpoint/condition/main.cpp new file mode 100644 index 000000000000..f4c3527f8af2 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/condition/main.cpp @@ -0,0 +1,28 @@ +//===-- 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 + +int32_t global = 0; // Watchpoint variable declaration. + +static void modify(int32_t &var) { + ++var; +} + +int main(int argc, char** argv) { + int local = 0; + printf("&global=%p\n", &global); + printf("about to write to 'global'...\n"); // Set break point at this line. + // When stopped, watch 'global', + // for the condition "global == 5". + for (int i = 0; i < 10; ++i) + modify(global); + + printf("global=%d\n", global); +} diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/main.c b/packages/Python/lldbsuite/test/python_api/watchpoint/main.c new file mode 100644 index 000000000000..4753edfba991 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/main.c @@ -0,0 +1,24 @@ +//===-- 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 + +int32_t global = 10; // Watchpoint variable declaration. + +int main(int argc, char** argv) { + int local = 0; + printf("&global=%p\n", &global); + printf("about to write to 'global'...\n"); // Set break point at this line. + // When stopped, watch 'global' for read&write. + global = 20; + local += argc; + ++local; + printf("local: %d\n", local); + printf("global=%d\n", global); +} diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/Makefile b/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/Makefile new file mode 100644 index 000000000000..8817fff55e8c --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestSetWatchlocation.py b/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestSetWatchlocation.py new file mode 100644 index 000000000000..5a4a464657d6 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestSetWatchlocation.py @@ -0,0 +1,90 @@ +""" +Use lldb Python SBValue.WatchPointee() API to create a watchpoint for write of '*g_char_ptr'. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class SetWatchlocationAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # This is for verifying that watch location works. + self.violating_func = "do_bad_thing_with_location"; + + @add_test_categories(['pyapi']) + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") # WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows + def test_watch_location(self): + """Exercise SBValue.WatchPointee() API to set a watchpoint.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # We should be stopped due to the breakpoint. Get frame #0. + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + value = frame0.FindValue('g_char_ptr', + lldb.eValueTypeVariableGlobal) + pointee = value.CreateValueFromAddress("pointee", + value.GetValueAsUnsigned(0), + value.GetType().GetPointeeType()) + # Watch for write to *g_char_ptr. + error = lldb.SBError(); + watchpoint = value.WatchPointee(True, False, True, error) + self.assertTrue(value and watchpoint, + "Successfully found the pointer and set a watchpoint") + self.DebugSBValue(value) + self.DebugSBValue(pointee) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + print(watchpoint) + + # Continue. Expect the program to stop due to the variable being written to. + process.Continue() + + if (self.TraceOn()): + lldbutil.print_stacktraces(process) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint) + self.assertTrue(thread, "The thread stopped due to watchpoint") + self.DebugSBValue(value) + self.DebugSBValue(pointee) + + self.expect(lldbutil.print_stacktrace(thread, string_buffer=True), exe=False, + substrs = [self.violating_func]) + + # This finishes our test. diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py b/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py new file mode 100644 index 000000000000..6facbaa8f2e9 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py @@ -0,0 +1,130 @@ +""" +Use lldb Python SBtarget.WatchAddress() API to create a watchpoint for write of '*g_char_ptr'. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TargetWatchAddressAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # This is for verifying that watch location works. + self.violating_func = "do_bad_thing_with_location"; + + @add_test_categories(['pyapi']) + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @expectedFailureWindows("llvm.org/pr24446") + def test_watch_address(self): + """Exercise SBTarget.WatchAddress() API to set a watchpoint.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # We should be stopped due to the breakpoint. Get frame #0. + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + value = frame0.FindValue('g_char_ptr', + lldb.eValueTypeVariableGlobal) + pointee = value.CreateValueFromAddress("pointee", + value.GetValueAsUnsigned(0), + value.GetType().GetPointeeType()) + # Watch for write to *g_char_ptr. + error = lldb.SBError(); + watchpoint = target.WatchAddress(value.GetValueAsUnsigned(), 1, False, True, error) + self.assertTrue(value and watchpoint, + "Successfully found the pointer and set a watchpoint") + self.DebugSBValue(value) + self.DebugSBValue(pointee) + + # Hide stdout if not running with '-t' option. + if not self.TraceOn(): + self.HideStdout() + + print(watchpoint) + + # Continue. Expect the program to stop due to the variable being written to. + process.Continue() + + if (self.TraceOn()): + lldbutil.print_stacktraces(process) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint) + self.assertTrue(thread, "The thread stopped due to watchpoint") + self.DebugSBValue(value) + self.DebugSBValue(pointee) + + self.expect(lldbutil.print_stacktrace(thread, string_buffer=True), exe=False, + substrs = [self.violating_func]) + + # This finishes our test. + + @add_test_categories(['pyapi']) + @expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported + @skipIf(archs=['mips', 'mipsel', 'mips64', 'mips64el']) # No size constraint on MIPS for watches + def test_watch_address_with_invalid_watch_size(self): + """Exercise SBTarget.WatchAddress() API but pass an invalid watch_size.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # We should be stopped due to the breakpoint. Get frame #0. + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + value = frame0.FindValue('g_char_ptr', + lldb.eValueTypeVariableGlobal) + pointee = value.CreateValueFromAddress("pointee", + value.GetValueAsUnsigned(0), + value.GetType().GetPointeeType()) + # Watch for write to *g_char_ptr. + error = lldb.SBError(); + watchpoint = target.WatchAddress(value.GetValueAsUnsigned(), 365, False, True, error) + self.assertFalse(watchpoint) + self.expect(error.GetCString(), exe=False, + substrs = ['watch size of %d is not supported' % 365]) diff --git a/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/main.cpp b/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/main.cpp new file mode 100644 index 000000000000..a197a92a4814 --- /dev/null +++ b/packages/Python/lldbsuite/test/python_api/watchpoint/watchlocation/main.cpp @@ -0,0 +1,104 @@ +//===-- 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 +#include +#include +#include + +std::default_random_engine g_random_engine{std::random_device{}()}; +std::uniform_int_distribution<> g_distribution{0, 3000000}; +std::condition_variable g_condition_variable; +std::mutex g_mutex; +int g_count; + +char *g_char_ptr = nullptr; + +void +barrier_wait() +{ + std::unique_lock lock{g_mutex}; + if (--g_count > 0) + g_condition_variable.wait(lock); + else + g_condition_variable.notify_all(); +} + +void +do_bad_thing_with_location(char *char_ptr, char new_val) +{ + *char_ptr = new_val; +} + +uint32_t +access_pool (bool flag = false) +{ + static std::mutex g_access_mutex; + if (!flag) + g_access_mutex.lock(); + + char old_val = *g_char_ptr; + if (flag) + do_bad_thing_with_location(g_char_ptr, old_val + 1); + + if (!flag) + g_access_mutex.unlock(); + return *g_char_ptr; +} + +void +thread_func (uint32_t thread_index) +{ + printf ("%s (thread index = %u) startng...\n", __FUNCTION__, thread_index); + + barrier_wait(); + + uint32_t count = 0; + uint32_t val; + while (count++ < 15) + { + // random micro second sleep from zero to 3 seconds + int usec = g_distribution(g_random_engine); + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + std::this_thread::sleep_for(std::chrono::microseconds{usec}); + + if (count < 7) + val = access_pool (); + else + val = access_pool (true); + + printf ("%s (thread = %u) after usleep access_pool returns %d (count=%d)...\n", __FUNCTION__, thread_index, val, count); + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); +} + + +int main (int argc, char const *argv[]) +{ + g_count = 4; + std::thread threads[3]; + + g_char_ptr = new char{}; + + // Create 3 threads + for (auto &thread : threads) + thread = std::thread{thread_func, std::distance(threads, &thread)}; + + printf ("Before turning all three threads loose...\n"); // Set break point at this line. + barrier_wait(); + + // Join all of our threads + for (auto &thread : threads) + thread.join(); + + delete g_char_ptr; + + return 0; +} diff --git a/packages/Python/lldbsuite/test/redo.py b/packages/Python/lldbsuite/test/redo.py new file mode 100644 index 000000000000..e4eba8b27860 --- /dev/null +++ b/packages/Python/lldbsuite/test/redo.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python + +""" +A simple utility to redo the failed/errored tests. + +You need to specify the session directory in order for this script to locate the +tests which need to be re-run. + +See also dotest.py, the test driver running the test suite. + +Type: + +./dotest.py -h + +for help. +""" + +from __future__ import print_function + +import os, sys, datetime +import re + +# If True, redo with no '-t' option for the test driver. +no_trace = False + +# To be filled with the filterspecs found in the session logs. +redo_specs = [] + +# The filename components to match for. Only files with the contained component names +# will be considered for re-run. Examples: ['X86_64', 'clang']. +filename_components = [] + +do_delay = False + +# There is a known bug with respect to comp_specs and arch_specs, in that if we +# encountered "-C clang" and "-C gcc" when visiting the session files, both +# compilers will end up in the invocation of the test driver when rerunning. +# That is: ./dotest -v -C clang^gcc ... -f ...". Ditto for "-A" flags. + +# The "-C compiler" for comp_specs. +comp_specs = set() +# The "-A arch" for arch_specs. +arch_specs = set() + +def usage(): + print("""\ +Usage: redo.py [-F filename_component] [-n] [session_dir] [-d] +where options: +-F : only consider the test for re-run if the session filename contains the filename component + for example: -F x86_64 +-n : when running the tests, do not turn on trace mode, i.e, no '-t' option + is passed to the test driver (this will run the tests faster) +-d : pass -d down to the test driver (introduces a delay so you can attach with a debugger) + +and session_dir specifies the session directory which contains previously +recorded session infos for all the test cases which either failed or errored. + +If sessin_dir is left unspecified, this script uses the heuristic to find the +possible session directories with names starting with %Y-%m-%d- (for example, +2012-01-23-) and employs the one with the latest timestamp.""") + sys.exit(0) + +def where(session_dir, test_dir): + """Returns the full path to the session directory; None if non-existent.""" + abspath = os.path.abspath(session_dir) + if os.path.isdir(abspath): + return abspath + + session_dir_path = os.path.join(test_dir, session_dir) + if os.path.isdir(session_dir_path): + return session_dir_path + + return None + +# This is the pattern for the line from the log file to redo a test. +# We want the filter spec. +filter_pattern = re.compile("^\./dotest\.py.*-f (.*)$") +comp_pattern = re.compile(" -C ([^ ]+) ") +arch_pattern = re.compile(" -A ([^ ]+) ") +def redo(suffix, dir, names): + """Visitor function for os.path.walk(path, visit, arg).""" + global redo_specs + global comp_specs + global arch_specs + global filter_pattern + global comp_pattern + global arch_pattern + global filename_components + global do_delay + + for name in names: + if name.endswith(suffix): + #print("Find a log file:", name) + if name.startswith("Error") or name.startswith("Failure"): + if filename_components: + if not all([comp in name for comp in filename_components]): + continue + with open(os.path.join(dir, name), 'r') as log: + content = log.read() + for line in content.splitlines(): + match = filter_pattern.match(line) + if match: + filterspec = match.group(1) + print("adding filterspec:", filterspec) + redo_specs.append(filterspec) + comp = comp_pattern.search(line) + if comp: + comp_specs.add(comp.group(1)) + arch = arch_pattern.search(line) + if arch: + arch_specs.add(arch.group(1)) + else: + continue + +def main(): + """Read the session directory and run the failed test cases one by one.""" + global no_trace + global redo_specs + global filename_components + global do_delay + + test_dir = sys.path[0] + if not test_dir: + test_dir = os.getcwd() + if not test_dir.endswith('test'): + print("This script expects to reside in lldb's test directory.") + sys.exit(-1) + + index = 1 + while index < len(sys.argv): + if sys.argv[index].startswith('-h') or sys.argv[index].startswith('--help'): + usage() + + if sys.argv[index].startswith('-'): + # We should continue processing... + pass + else: + # End of option processing. + break + + if sys.argv[index] == '-F': + # Increment by 1 to fetch the filename component spec. + index += 1 + if index >= len(sys.argv) or sys.argv[index].startswith('-'): + usage() + filename_components.append(sys.argv[index]) + elif sys.argv[index] == '-n': + no_trace = True + elif sys.argv[index] == '-d': + do_delay = True + + index += 1 + + if index < len(sys.argv): + # Get the specified session directory. + session_dir = sys.argv[index] + else: + # Use heuristic to find the latest session directory. + name = datetime.datetime.now().strftime("%Y-%m-%d-") + dirs = [d for d in os.listdir(os.getcwd()) if d.startswith(name)] + if len(dirs) == 0: + print("No default session directory found, please specify it explicitly.") + usage() + session_dir = max(dirs, key=os.path.getmtime) + if not session_dir or not os.path.exists(session_dir): + print("No default session directory found, please specify it explicitly.") + usage() + + #print("The test directory:", test_dir) + session_dir_path = where(session_dir, test_dir) + + print("Using session dir path:", session_dir_path) + os.chdir(test_dir) + os.path.walk(session_dir_path, redo, ".log") + + if not redo_specs: + print("No failures/errors recorded within the session directory, please specify a different session directory.\n") + usage() + + filters = " -f ".join(redo_specs) + compilers = '' + for comp in comp_specs: + compilers += " -C %s" % (comp) + archs = '' + for arch in arch_specs: + archs += "--arch %s " % (arch) + + command = "./dotest.py %s %s -v %s %s -f " % (compilers, archs, "" if no_trace else "-t", "-d" if do_delay else "") + + + print("Running %s" % (command + filters)) + os.system(command + filters) + +if __name__ == '__main__': + main() diff --git a/packages/Python/lldbsuite/test/result_formatter.py b/packages/Python/lldbsuite/test/result_formatter.py new file mode 100644 index 000000000000..44474918895e --- /dev/null +++ b/packages/Python/lldbsuite/test/result_formatter.py @@ -0,0 +1,1304 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Provides classes used by the test results reporting infrastructure +within the LLDB test suite. +""" + +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import argparse +import importlib +import inspect +import os +import pprint +import socket +import sys +import threading +import time +import traceback + +# Third-party modules +import six +from six.moves import cPickle + +# LLDB modules +from . import configuration + +import lldbsuite + + +# Ignore method count on DTOs. +# pylint: disable=too-few-public-methods +class FormatterConfig(object): + """Provides formatter configuration info to create_results_formatter().""" + def __init__(self): + self.filename = None + self.port = None + self.formatter_name = None + self.formatter_options = None + + +# Ignore method count on DTOs. +# pylint: disable=too-few-public-methods +class CreatedFormatter(object): + """Provides transfer object for returns from create_results_formatter().""" + def __init__(self, formatter, cleanup_func): + self.formatter = formatter + self.cleanup_func = cleanup_func + + +def create_results_formatter(config): + """Sets up a test results formatter. + + @param config an instance of FormatterConfig + that indicates how to setup the ResultsFormatter. + + @return an instance of CreatedFormatter. + """ + def create_socket(port): + """Creates a socket to the localhost on the given port. + + @param port the port number of the listenering port on + the localhost. + + @return (socket object, socket closing function) + """ + def socket_closer(open_sock): + """Close down an opened socket properly.""" + open_sock.shutdown(socket.SHUT_RDWR) + open_sock.close() + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("localhost", port)) + return (sock, lambda: socket_closer(sock)) + + default_formatter_name = None + results_file_object = None + cleanup_func = None + + if config.filename: + # Open the results file for writing. + if config.filename == 'stdout': + results_file_object = sys.stdout + cleanup_func = None + elif config.filename == 'stderr': + results_file_object = sys.stderr + cleanup_func = None + else: + results_file_object = open(config.filename, "w") + cleanup_func = results_file_object.close + default_formatter_name = ( + "lldbsuite.test.xunit_formatter.XunitFormatter") + elif config.port: + # Connect to the specified localhost port. + results_file_object, cleanup_func = create_socket(config.port) + default_formatter_name = ( + "lldbsuite.test.result_formatter.RawPickledFormatter") + + # If we have a results formatter name specified and we didn't specify + # a results file, we should use stdout. + if config.formatter_name is not None and results_file_object is None: + # Use stdout. + results_file_object = sys.stdout + cleanup_func = None + + if results_file_object: + # We care about the formatter. Choose user-specified or, if + # none specified, use the default for the output type. + if config.formatter_name: + formatter_name = config.formatter_name + else: + formatter_name = default_formatter_name + + # Create an instance of the class. + # First figure out the package/module. + components = formatter_name.split(".") + module = importlib.import_module(".".join(components[:-1])) + + # Create the class name we need to load. + cls = getattr(module, components[-1]) + + # Handle formatter options for the results formatter class. + formatter_arg_parser = cls.arg_parser() + if config.formatter_options and len(config.formatter_options) > 0: + command_line_options = config.formatter_options + else: + command_line_options = [] + + formatter_options = formatter_arg_parser.parse_args( + command_line_options) + + # Create the TestResultsFormatter given the processed options. + results_formatter_object = cls(results_file_object, formatter_options) + + def shutdown_formatter(): + """Shuts down the formatter when it is no longer needed.""" + # Tell the formatter to write out anything it may have + # been saving until the very end (e.g. xUnit results + # can't complete its output until this point). + results_formatter_object.send_terminate_as_needed() + + # And now close out the output file-like object. + if cleanup_func is not None: + cleanup_func() + + return CreatedFormatter( + results_formatter_object, + shutdown_formatter) + else: + return None + + +class EventBuilder(object): + """Helper class to build test result event dictionaries.""" + + BASE_DICTIONARY = None + + # Test Event Types + TYPE_JOB_RESULT = "job_result" + TYPE_TEST_RESULT = "test_result" + TYPE_TEST_START = "test_start" + TYPE_MARK_TEST_RERUN_ELIGIBLE = "test_eligible_for_rerun" + TYPE_MARK_TEST_EXPECTED_FAILURE = "test_expected_failure" + TYPE_SESSION_TERMINATE = "terminate" + + RESULT_TYPES = set([ + TYPE_JOB_RESULT, + TYPE_TEST_RESULT + ]) + + # Test/Job Status Tags + STATUS_EXCEPTIONAL_EXIT = "exceptional_exit" + STATUS_SUCCESS = "success" + STATUS_FAILURE = "failure" + STATUS_EXPECTED_FAILURE = "expected_failure" + STATUS_EXPECTED_TIMEOUT = "expected_timeout" + STATUS_UNEXPECTED_SUCCESS = "unexpected_success" + STATUS_SKIP = "skip" + STATUS_ERROR = "error" + STATUS_TIMEOUT = "timeout" + + """Test methods or jobs with a status matching any of these + status values will cause a testrun failure, unless + the test methods rerun and do not trigger an issue when rerun.""" + TESTRUN_ERROR_STATUS_VALUES = set([ + STATUS_ERROR, + STATUS_EXCEPTIONAL_EXIT, + STATUS_FAILURE, + STATUS_TIMEOUT + ]) + + @staticmethod + def _get_test_name_info(test): + """Returns (test-class-name, test-method-name) from a test case instance. + + @param test a unittest.TestCase instance. + + @return tuple containing (test class name, test method name) + """ + test_class_components = test.id().split(".") + test_class_name = ".".join(test_class_components[:-1]) + test_name = test_class_components[-1] + return (test_class_name, test_name) + + @staticmethod + def bare_event(event_type): + """Creates an event with default additions, event type and timestamp. + + @param event_type the value set for the "event" key, used + to distinguish events. + + @returns an event dictionary with all default additions, the "event" + key set to the passed in event_type, and the event_time value set to + time.time(). + """ + if EventBuilder.BASE_DICTIONARY is not None: + # Start with a copy of the "always include" entries. + event = dict(EventBuilder.BASE_DICTIONARY) + else: + event = {} + + event.update({ + "event": event_type, + "event_time": time.time() + }) + return event + + @staticmethod + def _event_dictionary_common(test, event_type): + """Returns an event dictionary setup with values for the given event type. + + @param test the unittest.TestCase instance + + @param event_type the name of the event type (string). + + @return event dictionary with common event fields set. + """ + test_class_name, test_name = EventBuilder._get_test_name_info(test) + + # Determine the filename for the test case. If there is an attribute + # for it, use it. Otherwise, determine from the TestCase class path. + if hasattr(test, "test_filename"): + test_filename = test.test_filename + else: + test_filename = inspect.getfile(test.__class__) + + event = EventBuilder.bare_event(event_type) + event.update({ + "test_class": test_class_name, + "test_name": test_name, + "test_filename": test_filename + }) + + return event + + @staticmethod + def _error_tuple_class(error_tuple): + """Returns the unittest error tuple's error class as a string. + + @param error_tuple the error tuple provided by the test framework. + + @return the error type (typically an exception) raised by the + test framework. + """ + type_var = error_tuple[0] + module = inspect.getmodule(type_var) + if module: + return "{}.{}".format(module.__name__, type_var.__name__) + else: + return type_var.__name__ + + @staticmethod + def _error_tuple_message(error_tuple): + """Returns the unittest error tuple's error message. + + @param error_tuple the error tuple provided by the test framework. + + @return the error message provided by the test framework. + """ + return str(error_tuple[1]) + + @staticmethod + def _error_tuple_traceback(error_tuple): + """Returns the unittest error tuple's error message. + + @param error_tuple the error tuple provided by the test framework. + + @return the error message provided by the test framework. + """ + return error_tuple[2] + + @staticmethod + def _event_dictionary_test_result(test, status): + """Returns an event dictionary with common test result fields set. + + @param test a unittest.TestCase instance. + + @param status the status/result of the test + (e.g. "success", "failure", etc.) + + @return the event dictionary + """ + event = EventBuilder._event_dictionary_common( + test, EventBuilder.TYPE_TEST_RESULT) + event["status"] = status + return event + + @staticmethod + def _event_dictionary_issue(test, status, error_tuple): + """Returns an event dictionary with common issue-containing test result + fields set. + + @param test a unittest.TestCase instance. + + @param status the status/result of the test + (e.g. "success", "failure", etc.) + + @param error_tuple the error tuple as reported by the test runner. + This is of the form (type, error). + + @return the event dictionary + """ + event = EventBuilder._event_dictionary_test_result(test, status) + event["issue_class"] = EventBuilder._error_tuple_class(error_tuple) + event["issue_message"] = EventBuilder._error_tuple_message(error_tuple) + backtrace = EventBuilder._error_tuple_traceback(error_tuple) + if backtrace is not None: + event["issue_backtrace"] = traceback.format_tb(backtrace) + return event + + @staticmethod + def event_for_start(test): + """Returns an event dictionary for the test start event. + + @param test a unittest.TestCase instance. + + @return the event dictionary + """ + return EventBuilder._event_dictionary_common( + test, EventBuilder.TYPE_TEST_START) + + @staticmethod + def event_for_success(test): + """Returns an event dictionary for a successful test. + + @param test a unittest.TestCase instance. + + @return the event dictionary + """ + return EventBuilder._event_dictionary_test_result( + test, EventBuilder.STATUS_SUCCESS) + + @staticmethod + def event_for_unexpected_success(test, bugnumber): + """Returns an event dictionary for a test that succeeded but was + expected to fail. + + @param test a unittest.TestCase instance. + + @param bugnumber the issue identifier for the bug tracking the + fix request for the test expected to fail (but is in fact + passing here). + + @return the event dictionary + + """ + event = EventBuilder._event_dictionary_test_result( + test, EventBuilder.STATUS_UNEXPECTED_SUCCESS) + if bugnumber: + event["bugnumber"] = str(bugnumber) + return event + + @staticmethod + def event_for_failure(test, error_tuple): + """Returns an event dictionary for a test that failed. + + @param test a unittest.TestCase instance. + + @param error_tuple the error tuple as reported by the test runner. + This is of the form (type, error). + + @return the event dictionary + """ + return EventBuilder._event_dictionary_issue( + test, EventBuilder.STATUS_FAILURE, error_tuple) + + @staticmethod + def event_for_expected_failure(test, error_tuple, bugnumber): + """Returns an event dictionary for a test that failed as expected. + + @param test a unittest.TestCase instance. + + @param error_tuple the error tuple as reported by the test runner. + This is of the form (type, error). + + @param bugnumber the issue identifier for the bug tracking the + fix request for the test expected to fail. + + @return the event dictionary + + """ + event = EventBuilder._event_dictionary_issue( + test, EventBuilder.STATUS_EXPECTED_FAILURE, error_tuple) + if bugnumber: + event["bugnumber"] = str(bugnumber) + return event + + @staticmethod + def event_for_skip(test, reason): + """Returns an event dictionary for a test that was skipped. + + @param test a unittest.TestCase instance. + + @param reason the reason why the test is being skipped. + + @return the event dictionary + """ + event = EventBuilder._event_dictionary_test_result( + test, EventBuilder.STATUS_SKIP) + event["skip_reason"] = reason + return event + + @staticmethod + def event_for_error(test, error_tuple): + """Returns an event dictionary for a test that hit a test execution error. + + @param test a unittest.TestCase instance. + + @param error_tuple the error tuple as reported by the test runner. + This is of the form (type, error). + + @return the event dictionary + """ + return EventBuilder._event_dictionary_issue( + test, EventBuilder.STATUS_ERROR, error_tuple) + + @staticmethod + def event_for_cleanup_error(test, error_tuple): + """Returns an event dictionary for a test that hit a test execution error + during the test cleanup phase. + + @param test a unittest.TestCase instance. + + @param error_tuple the error tuple as reported by the test runner. + This is of the form (type, error). + + @return the event dictionary + """ + event = EventBuilder._event_dictionary_issue( + test, EventBuilder.STATUS_ERROR, error_tuple) + event["issue_phase"] = "cleanup" + return event + + @staticmethod + def event_for_job_exceptional_exit( + pid, worker_index, exception_code, exception_description, + test_filename, command_line): + """Creates an event for a job (i.e. process) exit due to signal. + + @param pid the process id for the job that failed + @param worker_index optional id for the job queue running the process + @param exception_code optional code + (e.g. SIGTERM integer signal number) + @param exception_description optional string containing symbolic + representation of the issue (e.g. "SIGTERM") + @param test_filename the path to the test filename that exited + in some exceptional way. + @param command_line the Popen-style list provided as the command line + for the process that timed out. + + @return an event dictionary coding the job completion description. + """ + event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT) + event["status"] = EventBuilder.STATUS_EXCEPTIONAL_EXIT + if pid is not None: + event["pid"] = pid + if worker_index is not None: + event["worker_index"] = int(worker_index) + if exception_code is not None: + event["exception_code"] = exception_code + if exception_description is not None: + event["exception_description"] = exception_description + if test_filename is not None: + event["test_filename"] = test_filename + if command_line is not None: + event["command_line"] = command_line + return event + + @staticmethod + def event_for_job_timeout(pid, worker_index, test_filename, command_line): + """Creates an event for a job (i.e. process) timeout. + + @param pid the process id for the job that timed out + @param worker_index optional id for the job queue running the process + @param test_filename the path to the test filename that timed out. + @param command_line the Popen-style list provided as the command line + for the process that timed out. + + @return an event dictionary coding the job completion description. + """ + event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT) + event["status"] = "timeout" + if pid is not None: + event["pid"] = pid + if worker_index is not None: + event["worker_index"] = int(worker_index) + if test_filename is not None: + event["test_filename"] = test_filename + if command_line is not None: + event["command_line"] = command_line + return event + + @staticmethod + def event_for_mark_test_rerun_eligible(test): + """Creates an event that indicates the specified test is explicitly + eligible for rerun. + + Note there is a mode that will enable test rerun eligibility at the + global level. These markings for explicit rerun eligibility are + intended for the mode of running where only explicitly rerunnable + tests are rerun upon hitting an issue. + + @param test the TestCase instance to which this pertains. + + @return an event that specifies the given test as being eligible to + be rerun. + """ + event = EventBuilder._event_dictionary_common( + test, + EventBuilder.TYPE_MARK_TEST_RERUN_ELIGIBLE) + return event + + @staticmethod + def event_for_mark_test_expected_failure(test): + """Creates an event that indicates the specified test is expected + to fail. + + @param test the TestCase instance to which this pertains. + + @return an event that specifies the given test is expected to fail. + """ + event = EventBuilder._event_dictionary_common( + test, + EventBuilder.TYPE_MARK_TEST_EXPECTED_FAILURE) + return event + + @staticmethod + def add_entries_to_all_events(entries_dict): + """Specifies a dictionary of entries to add to all test events. + + This provides a mechanism for, say, a parallel test runner to + indicate to each inferior dotest.py that it should add a + worker index to each. + + Calling this method replaces all previous entries added + by a prior call to this. + + Event build methods will overwrite any entries that collide. + Thus, the passed in dictionary is the base, which gets merged + over by event building when keys collide. + + @param entries_dict a dictionary containing key and value + pairs that should be merged into all events created by the + event generator. May be None to clear out any extra entries. + """ + EventBuilder.BASE_DICTIONARY = dict(entries_dict) + + +class ResultsFormatter(object): + """Provides interface to formatting test results out to a file-like object. + + This class allows the LLDB test framework's raw test-realted + events to be processed and formatted in any manner desired. + Test events are represented by python dictionaries, formatted + as in the EventBuilder class above. + + ResultFormatter instances are given a file-like object in which + to write their results. + + ResultFormatter lifetime looks like the following: + + # The result formatter is created. + # The argparse options dictionary is generated from calling + # the SomeResultFormatter.arg_parser() with the options data + # passed to dotest.py via the "--results-formatter-options" + # argument. See the help on that for syntactic requirements + # on getting that parsed correctly. + formatter = SomeResultFormatter(file_like_object, argpared_options_dict) + + # Single call to session start, before parsing any events. + formatter.begin_session() + + formatter.handle_event({"event":"initialize",...}) + + # Zero or more calls specified for events recorded during the test session. + # The parallel test runner manages getting results from all the inferior + # dotest processes, so from a new format perspective, don't worry about + # that. The formatter will be presented with a single stream of events + # sandwiched between a single begin_session()/end_session() pair in the + # parallel test runner process/thread. + for event in zero_or_more_test_events(): + formatter.handle_event(event) + + # Single call to terminate/wrap-up. Formatters that need all the + # data before they can print a correct result (e.g. xUnit/JUnit), + # this is where the final report can be generated. + formatter.handle_event({"event":"terminate",...}) + + It is not the formatter's responsibility to close the file_like_object. + (i.e. do not close it). + + The lldb test framework passes these test events in real time, so they + arrive as they come in. + + In the case of the parallel test runner, the dotest inferiors + add a 'pid' field to the dictionary that indicates which inferior + pid generated the event. + + Note more events may be added in the future to support richer test + reporting functionality. One example: creating a true flaky test + result category so that unexpected successes really mean the test + is marked incorrectly (either should be marked flaky, or is indeed + passing consistently now and should have the xfail marker + removed). In this case, a flaky_success and flaky_fail event + likely will be added to capture these and support reporting things + like percentages of flaky test passing so we can see if we're + making some things worse/better with regards to failure rates. + + Another example: announcing all the test methods that are planned + to be run, so we can better support redo operations of various kinds + (redo all non-run tests, redo non-run tests except the one that + was running [perhaps crashed], etc.) + + Implementers are expected to override all the public methods + provided in this class. See each method's docstring to see + expectations about when the call should be chained. + + """ + @classmethod + def arg_parser(cls): + """@return arg parser used to parse formatter-specific options.""" + parser = argparse.ArgumentParser( + description='{} options'.format(cls.__name__), + usage=('dotest.py --results-formatter-options=' + '"--option1 value1 [--option2 value2 [...]]"')) + parser.add_argument( + "--dump-results", + action="store_true", + help=('dump the raw results data after printing ' + 'the summary output.')) + return parser + + def __init__(self, out_file, options): + super(ResultsFormatter, self).__init__() + self.out_file = out_file + self.options = options + self.using_terminal = False + if not self.out_file: + raise Exception("ResultsFormatter created with no file object") + self.start_time_by_test = {} + self.terminate_called = False + + # Store counts of test_result events by status. + self.result_status_counts = { + EventBuilder.STATUS_SUCCESS: 0, + EventBuilder.STATUS_EXPECTED_FAILURE: 0, + EventBuilder.STATUS_EXPECTED_TIMEOUT: 0, + EventBuilder.STATUS_SKIP: 0, + EventBuilder.STATUS_UNEXPECTED_SUCCESS: 0, + EventBuilder.STATUS_FAILURE: 0, + EventBuilder.STATUS_ERROR: 0, + EventBuilder.STATUS_TIMEOUT: 0, + EventBuilder.STATUS_EXCEPTIONAL_EXIT: 0 + } + + # Track the most recent test start event by worker index. + # We'll use this to assign TIMEOUT and exceptional + # exits to the most recent test started on a given + # worker index. + self.started_tests_by_worker = {} + + # Store the most recent test_method/job status. + self.result_events = {} + + # Track the number of test method reruns. + self.test_method_rerun_count = 0 + + # Lock that we use while mutating inner state, like the + # total test count and the elements. We minimize how + # long we hold the lock just to keep inner state safe, not + # entirely consistent from the outside. + self.lock = threading.RLock() + + # Keeps track of the test base filenames for tests that + # are expected to timeout. If a timeout occurs in any test + # basename that matches this list, that result should be + # converted into a non-issue. We'll create an expected + # timeout test status for this. + self.expected_timeouts_by_basename = set() + + # Tests which have reported that they are expecting to fail. These will + # be marked as expected failures even if they return a failing status, + # probably because they crashed or deadlocked. + self.expected_failures = set() + + # Keep track of rerun-eligible tests. + # This is a set that contains tests saved as: + # {test_filename}:{test_class}:{test_name} + self.rerun_eligible_tests = set() + + # A dictionary of test files that had a failing + # test, in the format of: + # key = test path, value = array of test methods that need rerun + self.tests_for_rerun = {} + + @classmethod + def _make_key(cls, result_event): + """Creates a key from a test or job result event. + + This key attempts to be as unique as possible. For + test result events, it will be unique per test method. + For job events (ones not promoted to a test result event), + it will be unique per test case file. + + @return a string-based key of the form + {test_filename}:{test_class}.{test_name} + """ + if result_event is None: + return None + component_count = 0 + if "test_filename" in result_event: + key = result_event["test_filename"] + component_count += 1 + if "test_class" in result_event: + if component_count > 0: + key += ":" + key += result_event["test_class"] + component_count += 1 + if "test_name" in result_event: + if component_count > 0: + key += "." + key += result_event["test_name"] + component_count += 1 + return key + + def _mark_test_as_expected_failure(self, test_result_event): + key = self._make_key(test_result_event) + if key is not None: + self.expected_failures.add(key) + else: + sys.stderr.write( + "\nerror: test marked as expected failure but " + "failed to create key.\n") + + def _mark_test_for_rerun_eligibility(self, test_result_event): + key = self._make_key(test_result_event) + if key is not None: + self.rerun_eligible_tests.add(key) + else: + sys.stderr.write( + "\nerror: test marked for re-run eligibility but " + "failed to create key.\n") + + def _maybe_add_test_to_rerun_list(self, result_event): + key = self._make_key(result_event) + if key is not None: + if (key in self.rerun_eligible_tests or + configuration.rerun_all_issues): + test_filename = result_event.get("test_filename", None) + if test_filename is not None: + test_name = result_event.get("test_name", None) + if test_filename not in self.tests_for_rerun: + self.tests_for_rerun[test_filename] = [] + if test_name is not None: + self.tests_for_rerun[test_filename].append(test_name) + else: + sys.stderr.write( + "\nerror: couldn't add testrun-failing test to rerun " + "list because no eligibility key could be created.\n") + + def _maybe_remap_job_result_event(self, test_event): + """Remaps timeout/exceptional exit job results to last test method running. + + @param test_event the job_result test event. This is an in/out + parameter. It will be modified if it can be mapped to a test_result + of the same status, using details from the last-running test method + known to be most recently started on the same worker index. + """ + test_start = None + + job_status = test_event["status"] + if job_status in [ + EventBuilder.STATUS_TIMEOUT, + EventBuilder.STATUS_EXCEPTIONAL_EXIT]: + worker_index = test_event.get("worker_index", None) + if worker_index is not None: + test_start = self.started_tests_by_worker.get( + worker_index, None) + + # If we have a test start to remap, do it here. + if test_start is not None: + test_event["event"] = EventBuilder.TYPE_TEST_RESULT + + # Fill in all fields from test start not present in + # job status message. + for (start_key, start_value) in test_start.items(): + if start_key not in test_event: + test_event[start_key] = start_value + + def _maybe_remap_expected_timeout(self, event): + if event is None: + return + + status = event.get("status", None) + if status is None or status != EventBuilder.STATUS_TIMEOUT: + return + + # Check if the timeout test's basename is in the expected timeout + # list. If so, convert to an expected timeout. + basename = os.path.basename(event.get("test_filename", "")) + if basename in self.expected_timeouts_by_basename: + # Convert to an expected timeout. + event["status"] = EventBuilder.STATUS_EXPECTED_TIMEOUT + + def _maybe_remap_expected_failure(self, event): + if event is None: + return + + key = self._make_key(event) + if key not in self.expected_failures: + return + + status = event.get("status", None) + if status in EventBuilder.TESTRUN_ERROR_STATUS_VALUES: + event["status"] = EventBuilder.STATUS_EXPECTED_FAILURE + elif status == EventBuilder.STATUS_SUCCESS: + event["status"] = EventBuilder.STATUS_UNEXPECTED_SUCCESS + + def handle_event(self, test_event): + """Handles the test event for collection into the formatter output. + + Derived classes may override this but should call down to this + implementation first. + + @param test_event the test event as formatted by one of the + event_for_* calls. + """ + with self.lock: + # Keep track of whether terminate was received. We do this so + # that a process can call the 'terminate' event on its own, to + # close down a formatter at the appropriate time. Then the + # atexit() cleanup can call the "terminate if it hasn't been + # called yet". + if test_event is not None: + event_type = test_event.get("event", "") + # We intentionally allow event_type to be checked anew + # after this check below since this check may rewrite + # the event type + if event_type == EventBuilder.TYPE_JOB_RESULT: + # Possibly convert the job status (timeout, exceptional exit) + # to an appropriate test_result event. + self._maybe_remap_job_result_event(test_event) + event_type = test_event.get("event", "") + + # Remap timeouts to expected timeouts. + if event_type in EventBuilder.RESULT_TYPES: + self._maybe_remap_expected_timeout(test_event) + self._maybe_remap_expected_failure(test_event) + event_type = test_event.get("event", "") + + if event_type == "terminate": + self.terminate_called = True + elif event_type in EventBuilder.RESULT_TYPES: + # Keep track of event counts per test/job result status type. + # The only job (i.e. inferior process) results that make it + # here are ones that cannot be remapped to the most recently + # started test for the given worker index. + status = test_event["status"] + self.result_status_counts[status] += 1 + # Clear the most recently started test for the related worker. + worker_index = test_event.get("worker_index", None) + if worker_index is not None: + self.started_tests_by_worker.pop(worker_index, None) + + if status in EventBuilder.TESTRUN_ERROR_STATUS_VALUES: + # A test/job status value in any of those status values + # causes a testrun failure. If such a test fails, check + # whether it can be rerun. If it can be rerun, add it + # to the rerun job. + self._maybe_add_test_to_rerun_list(test_event) + + # Build the test key. + test_key = self._make_key(test_event) + if test_key is None: + raise Exception( + "failed to find test filename for " + "test event {}".format(test_event)) + + # Save the most recent test event for the test key. + # This allows a second test phase to overwrite the most + # recent result for the test key (unique per method). + # We do final reporting at the end, so we'll report based + # on final results. + # We do this so that a re-run caused by, perhaps, the need + # to run a low-load, single-worker test run can have the final + # run's results to always be used. + if test_key in self.result_events: + # We are replacing the result of something that was + # already counted by the base class. Remove the double + # counting by reducing by one the count for the test + # result status. + old_status = self.result_events[test_key]["status"] + self.result_status_counts[old_status] -= 1 + self.test_method_rerun_count += 1 + self.result_events[test_key] = test_event + elif event_type == EventBuilder.TYPE_TEST_START: + # Track the start time for the test method. + self.track_start_time( + test_event["test_class"], + test_event["test_name"], + test_event["event_time"]) + # Track of the most recent test method start event + # for the related worker. This allows us to figure + # out whether a process timeout or exceptional exit + # can be charged (i.e. assigned) to a test method. + worker_index = test_event.get("worker_index", None) + if worker_index is not None: + self.started_tests_by_worker[worker_index] = test_event + + elif event_type == EventBuilder.TYPE_MARK_TEST_RERUN_ELIGIBLE: + self._mark_test_for_rerun_eligibility(test_event) + elif event_type == EventBuilder.TYPE_MARK_TEST_EXPECTED_FAILURE: + self._mark_test_as_expected_failure(test_event) + + def set_expected_timeouts_by_basename(self, basenames): + """Specifies a list of test file basenames that are allowed to timeout + without being called out as a timeout issue. + + These fall into a new status category called STATUS_EXPECTED_TIMEOUT. + """ + if basenames is not None: + for basename in basenames: + self.expected_timeouts_by_basename.add(basename) + + def track_start_time(self, test_class, test_name, start_time): + """tracks the start time of a test so elapsed time can be computed. + + this alleviates the need for test results to be processed serially + by test. it will save the start time for the test so that + elapsed_time_for_test() can compute the elapsed time properly. + """ + if test_class is None or test_name is None: + return + + test_key = "{}.{}".format(test_class, test_name) + self.start_time_by_test[test_key] = start_time + + def elapsed_time_for_test(self, test_class, test_name, end_time): + """returns the elapsed time for a test. + + this function can only be called once per test and requires that + the track_start_time() method be called sometime prior to calling + this method. + """ + if test_class is None or test_name is None: + return -2.0 + + test_key = "{}.{}".format(test_class, test_name) + if test_key not in self.start_time_by_test: + return -1.0 + else: + start_time = self.start_time_by_test[test_key] + del self.start_time_by_test[test_key] + return end_time - start_time + + def is_using_terminal(self): + """returns true if this results formatter is using the terminal and + output should be avoided.""" + return self.using_terminal + + def send_terminate_as_needed(self): + """sends the terminate event if it hasn't been received yet.""" + if not self.terminate_called: + terminate_event = EventBuilder.bare_event("terminate") + self.handle_event(terminate_event) + + # Derived classes may require self access + # pylint: disable=no-self-use + def replaces_summary(self): + """Returns whether the results formatter includes a summary + suitable to replace the old lldb test run results. + + @return True if the lldb test runner can skip its summary + generation when using this results formatter; False otherwise. + """ + return False + + def counts_by_test_result_status(self, status): + """Returns number of test method results for the given status. + + @status_result a test result status (e.g. success, fail, skip) + as defined by the EventBuilder.STATUS_* class members. + + @return an integer returning the number of test methods matching + the given test result status. + """ + return self.result_status_counts[status] + + @classmethod + def _event_sort_key(cls, event): + """Returns the sort key to be used for a test event. + + This method papers over the differences in a test method result vs. a + job (i.e. inferior process) result. + + @param event a test result or job result event. + @return a key useful for sorting events by name (test name preferably, + then by test filename). + """ + if "test_name" in event: + return event["test_name"] + else: + return event.get("test_filename", None) + + def _partition_results_by_status(self, categories): + """Partitions the captured test results by event status. + + This permits processing test results by the category ids. + + @param categories the list of categories on which to partition. + Follows the format described in _report_category_details(). + + @return a dictionary where each key is the test result status, + and each entry is a list containing all the test result events + that matched that test result status. Result status IDs with + no matching entries will have a zero-length list. + """ + partitioned_events = {} + for category in categories: + result_status_id = category[0] + matching_events = [ + [key, event] for (key, event) in self.result_events.items() + if event.get("status", "") == result_status_id] + partitioned_events[result_status_id] = sorted( + matching_events, + key=lambda x: self._event_sort_key(x[1])) + return partitioned_events + + def _print_banner(self, out_file, banner_text): + """Prints an ASCII banner around given text. + + Output goes to the out file for the results formatter. + + @param out_file a file-like object where output will be written. + @param banner_text the text to display, with a banner + of '=' around the line above and line below. + """ + banner_separator = "".ljust(len(banner_text), "=") + + out_file.write("\n{}\n{}\n{}\n".format( + banner_separator, + banner_text, + banner_separator)) + + def _print_summary_counts( + self, out_file, categories, result_events_by_status, extra_rows): + """Prints summary counts for all categories. + + @param out_file a file-like object used to print output. + + @param categories the list of categories on which to partition. + Follows the format described in _report_category_details(). + + @param result_events_by_status the partitioned list of test + result events in a dictionary, with the key set to the test + result status id and the value set to the list of test method + results that match the status id. + """ + + # Get max length for category printed name + category_with_max_printed_name = max( + categories, key=lambda x: len(x[1])) + max_category_name_length = len(category_with_max_printed_name[1]) + + # If we are provided with extra rows, consider these row name lengths. + if extra_rows is not None: + for row in extra_rows: + name_length = len(row[0]) + if name_length > max_category_name_length: + max_category_name_length = name_length + + self._print_banner(out_file, "Test Result Summary") + + # Prepend extra rows + if extra_rows is not None: + for row in extra_rows: + extra_label = "{}:".format(row[0]).ljust( + max_category_name_length + 1) + out_file.write("{} {:4}\n".format(extra_label, row[1])) + + for category in categories: + result_status_id = category[0] + result_label = "{}:".format(category[1]).ljust( + max_category_name_length + 1) + count = len(result_events_by_status[result_status_id]) + out_file.write("{} {:4}\n".format( + result_label, + count)) + + @classmethod + def _has_printable_details(cls, categories, result_events_by_status): + """Returns whether there are any test result details that need to be printed. + + This will spin through the results and see if any result in a category + that is printable has any results to print. + + @param categories the list of categories on which to partition. + Follows the format described in _report_category_details(). + + @param result_events_by_status the partitioned list of test + result events in a dictionary, with the key set to the test + result status id and the value set to the list of test method + results that match the status id. + + @return True if there are any details (i.e. test results + for failures, errors, unexpected successes); False otherwise. + """ + for category in categories: + result_status_id = category[0] + print_matching_tests = category[2] + if print_matching_tests: + if len(result_events_by_status[result_status_id]) > 0: + # We found a printable details test result status + # that has details to print. + return True + # We didn't find any test result category with printable + # details. + return False + + def _report_category_details(self, out_file, category, result_events_by_status): + """Reports all test results matching the given category spec. + + @param out_file a file-like object used to print output. + + @param category a category spec of the format [test_event_name, + printed_category_name, print_matching_entries?] + + @param result_events_by_status the partitioned list of test + result events in a dictionary, with the key set to the test + result status id and the value set to the list of test method + results that match the status id. + """ + result_status_id = category[0] + print_matching_tests = category[2] + detail_label = category[3] + + if print_matching_tests: + # Sort by test name + for (_, event) in result_events_by_status[result_status_id]: + # Convert full test path into test-root-relative. + test_relative_path = os.path.relpath( + os.path.realpath(event["test_filename"]), + lldbsuite.lldb_test_root) + + # Create extra info component (used for exceptional exit info) + if result_status_id == EventBuilder.STATUS_EXCEPTIONAL_EXIT: + extra_info = "[EXCEPTIONAL EXIT {} ({})] ".format( + event["exception_code"], + event["exception_description"]) + else: + extra_info = "" + + # Figure out the identity we will use for this test. + if configuration.verbose and ("test_class" in event): + test_id = "{}.{}".format( + event["test_class"], event["test_name"]) + elif "test_name" in event: + test_id = event["test_name"] + else: + test_id = "" + + # Display the info. + out_file.write("{}: {}{} ({})\n".format( + detail_label, + extra_info, + test_id, + test_relative_path)) + + def print_results(self, out_file): + """Writes the test result report to the output file. + + @param out_file a file-like object used for printing summary + results. This is different than self.out_file, which might + be something else for non-summary data. + """ + extra_results = [ + # Total test methods processed, excluding reruns. + ["Test Methods", len(self.result_events)], + ["Reruns", self.test_method_rerun_count]] + + # Output each of the test result entries. + categories = [ + # result id, printed name, print matching tests?, detail label + [EventBuilder.STATUS_SUCCESS, + "Success", False, None], + [EventBuilder.STATUS_EXPECTED_FAILURE, + "Expected Failure", False, None], + [EventBuilder.STATUS_FAILURE, + "Failure", True, "FAIL"], + [EventBuilder.STATUS_ERROR, + "Error", True, "ERROR"], + [EventBuilder.STATUS_EXCEPTIONAL_EXIT, + "Exceptional Exit", True, "ERROR"], + [EventBuilder.STATUS_UNEXPECTED_SUCCESS, + "Unexpected Success", True, "UNEXPECTED SUCCESS"], + [EventBuilder.STATUS_SKIP, "Skip", False, None], + [EventBuilder.STATUS_TIMEOUT, + "Timeout", True, "TIMEOUT"], + [EventBuilder.STATUS_EXPECTED_TIMEOUT, + # Intentionally using the unusual hyphenation in TIME-OUT to + # prevent buildbots from thinking it is an issue when scanning + # for TIMEOUT. + "Expected Timeout", True, "EXPECTED TIME-OUT"] + ] + + # Partition all the events by test result status + result_events_by_status = self._partition_results_by_status( + categories) + + # Print the details + have_details = self._has_printable_details( + categories, result_events_by_status) + if have_details: + self._print_banner(out_file, "Issue Details") + for category in categories: + self._report_category_details( + out_file, category, result_events_by_status) + + # Print the summary + self._print_summary_counts( + out_file, categories, result_events_by_status, extra_results) + + if self.options.dump_results: + # Debug dump of the key/result info for all categories. + self._print_banner("Results Dump") + for status, events_by_key in result_events_by_status.items(): + out_file.write("\nSTATUS: {}\n".format(status)) + for key, event in events_by_key: + out_file.write("key: {}\n".format(key)) + out_file.write("event: {}\n".format(event)) + + +class RawPickledFormatter(ResultsFormatter): + """Formats events as a pickled stream. + + The parallel test runner has inferiors pickle their results and send them + over a socket back to the parallel test. The parallel test runner then + aggregates them into the final results formatter (e.g. xUnit). + """ + + @classmethod + def arg_parser(cls): + """@return arg parser used to parse formatter-specific options.""" + parser = super(RawPickledFormatter, cls).arg_parser() + return parser + + def __init__(self, out_file, options): + super(RawPickledFormatter, self).__init__(out_file, options) + self.pid = os.getpid() + + def handle_event(self, test_event): + super(RawPickledFormatter, self).handle_event(test_event) + + # Convert initialize/terminate events into job_begin/job_end events. + event_type = test_event["event"] + if event_type is None: + return + + if event_type == "initialize": + test_event["event"] = "job_begin" + elif event_type == "terminate": + test_event["event"] = "job_end" + + # Tack on the pid. + test_event["pid"] = self.pid + + # Send it as {serialized_length_of_serialized_bytes}{serialized_bytes} + import struct + msg = cPickle.dumps(test_event) + packet = struct.pack("!I%ds" % len(msg), len(msg), msg) + self.out_file.send(packet) + + +class DumpFormatter(ResultsFormatter): + """Formats events to the file as their raw python dictionary format.""" + + def handle_event(self, test_event): + super(DumpFormatter, self).handle_event(test_event) + self.out_file.write("\n" + pprint.pformat(test_event) + "\n") diff --git a/packages/Python/lldbsuite/test/settings/Makefile b/packages/Python/lldbsuite/test/settings/Makefile new file mode 100644 index 000000000000..d4bc9c689043 --- /dev/null +++ b/packages/Python/lldbsuite/test/settings/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/settings/TestSettings.py b/packages/Python/lldbsuite/test/settings/TestSettings.py new file mode 100644 index 000000000000..1d1912495551 --- /dev/null +++ b/packages/Python/lldbsuite/test/settings/TestSettings.py @@ -0,0 +1,495 @@ +""" +Test lldb settings command. +""" + +from __future__ import print_function + + + +import os, time, re +import lldb +from lldbsuite.test.lldbtest import * + +class SettingsCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @classmethod + def classCleanup(cls): + """Cleanup the test byproducts.""" + cls.RemoveTempFile("output1.txt") + cls.RemoveTempFile("output2.txt") + cls.RemoveTempFile("stderr.txt") + cls.RemoveTempFile("stdout.txt") + + @no_debug_info_test + def test_apropos_should_also_search_settings_description(self): + """Test that 'apropos' command should also search descriptions for the settings variables.""" + + self.expect("apropos 'environment variable'", + substrs = ["target.env-vars", + "environment variables", + "executable's environment"]) + + @no_debug_info_test + def test_append_target_env_vars(self): + """Test that 'append target.run-args' works.""" + # Append the env-vars. + self.runCmd('settings append target.env-vars MY_ENV_VAR=YES') + # And add hooks to restore the settings during tearDown(). + self.addTearDownHook( + lambda: self.runCmd("settings clear target.env-vars")) + + # Check it immediately! + self.expect('settings show target.env-vars', + substrs = ['MY_ENV_VAR=YES']) + + @no_debug_info_test + def test_insert_before_and_after_target_run_args(self): + """Test that 'insert-before/after target.run-args' works.""" + # Set the run-args first. + self.runCmd('settings set target.run-args a b c') + # And add hooks to restore the settings during tearDown(). + self.addTearDownHook( + lambda: self.runCmd("settings clear target.run-args")) + + # Now insert-before the index-0 element with '__a__'. + self.runCmd('settings insert-before target.run-args 0 __a__') + # And insert-after the index-1 element with '__A__'. + self.runCmd('settings insert-after target.run-args 1 __A__') + # Check it immediately! + self.expect('settings show target.run-args', + substrs = ['target.run-args', + '[0]: "__a__"', + '[1]: "a"', + '[2]: "__A__"', + '[3]: "b"', + '[4]: "c"']) + + @no_debug_info_test + def test_replace_target_run_args(self): + """Test that 'replace target.run-args' works.""" + # Set the run-args and then replace the index-0 element. + self.runCmd('settings set target.run-args a b c') + # And add hooks to restore the settings during tearDown(). + self.addTearDownHook( + lambda: self.runCmd("settings clear target.run-args")) + + # Now replace the index-0 element with 'A', instead. + self.runCmd('settings replace target.run-args 0 A') + # Check it immediately! + self.expect('settings show target.run-args', + substrs = ['target.run-args (arguments) =', + '[0]: "A"', + '[1]: "b"', + '[2]: "c"']) + + @no_debug_info_test + def test_set_prompt(self): + """Test that 'set prompt' actually changes the prompt.""" + + # Set prompt to 'lldb2'. + self.runCmd("settings set prompt 'lldb2 '") + + # Immediately test the setting. + self.expect("settings show prompt", SETTING_MSG("prompt"), + startstr = 'prompt (string) = "lldb2 "') + + # The overall display should also reflect the new setting. + self.expect("settings show", SETTING_MSG("prompt"), + substrs = ['prompt (string) = "lldb2 "']) + + # Use '-r' option to reset to the original default prompt. + self.runCmd("settings clear prompt") + + @no_debug_info_test + def test_set_term_width(self): + """Test that 'set term-width' actually changes the term-width.""" + + self.runCmd("settings set term-width 70") + + # Immediately test the setting. + self.expect("settings show term-width", SETTING_MSG("term-width"), + startstr = "term-width (int) = 70") + + # The overall display should also reflect the new setting. + self.expect("settings show", SETTING_MSG("term-width"), + substrs = ["term-width (int) = 70"]) + + #rdar://problem/10712130 + def test_set_frame_format(self): + """Test that 'set frame-format' with a backtick char in the format string works as well as fullpath.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + def cleanup(): + self.runCmd("settings set frame-format %s" % self.format_string, check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("settings show frame-format") + m = re.match( + '^frame-format \(format-string\) = "(.*)\"$', + self.res.GetOutput()) + self.assertTrue(m, "Bad settings string") + self.format_string = m.group(1) + + # Change the default format to print function.name rather than function.name-with-args + format_string = "frame #${frame.index}: ${frame.pc}{ ${module.file.basename}`${function.name}{${function.pc-offset}}}{ at ${line.file.fullpath}:${line.number}}{, lang=${language}}\n" + self.runCmd("settings set frame-format %s" % format_string) + + # Immediately test the setting. + self.expect("settings show frame-format", SETTING_MSG("frame-format"), + substrs = [format_string]) + + self.runCmd("breakpoint set -n main") + self.runCmd("run") + self.expect("thread backtrace", + substrs = ["`main", os.getcwd()]) + + def test_set_auto_confirm(self): + """Test that after 'set auto-confirm true', manual confirmation should not kick in.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("settings set auto-confirm true") + + # Immediately test the setting. + self.expect("settings show auto-confirm", SETTING_MSG("auto-confirm"), + startstr = "auto-confirm (boolean) = true") + + # Now 'breakpoint delete' should just work fine without confirmation + # prompt from the command interpreter. + self.runCmd("breakpoint set -n main") + self.expect("breakpoint delete", + startstr = "All breakpoints removed") + + # Restore the original setting of auto-confirm. + self.runCmd("settings clear auto-confirm") + self.expect("settings show auto-confirm", SETTING_MSG("auto-confirm"), + startstr = "auto-confirm (boolean) = false") + + @skipUnlessArch(['x86_64', 'i386', 'i686']) + def test_disassembler_settings(self): + """Test that user options for the disassembler take effect.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # AT&T syntax + self.runCmd("settings set target.x86-disassembly-flavor att") + self.runCmd("settings set target.use-hex-immediates false") + self.expect("disassemble -n numberfn", + substrs = ["$90"]) + self.runCmd("settings set target.use-hex-immediates true") + self.runCmd("settings set target.hex-immediate-style c") + self.expect("disassemble -n numberfn", + substrs = ["$0x5a"]) + self.runCmd("settings set target.hex-immediate-style asm") + self.expect("disassemble -n numberfn", + substrs = ["$5ah"]) + + # Intel syntax + self.runCmd("settings set target.x86-disassembly-flavor intel") + self.runCmd("settings set target.use-hex-immediates false") + self.expect("disassemble -n numberfn", + substrs = ["90"]) + self.runCmd("settings set target.use-hex-immediates true") + self.runCmd("settings set target.hex-immediate-style c") + self.expect("disassemble -n numberfn", + substrs = ["0x5a"]) + self.runCmd("settings set target.hex-immediate-style asm") + self.expect("disassemble -n numberfn", + substrs = ["5ah"]) + + @expectedFailureWindows("llvm.org/pr24579") + def test_run_args_and_env_vars(self): + """Test that run-args and env-vars are passed to the launched process.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Set the run-args and the env-vars. + # And add hooks to restore the settings during tearDown(). + self.runCmd('settings set target.run-args A B C') + self.addTearDownHook( + lambda: self.runCmd("settings clear target.run-args")) + self.runCmd('settings set target.env-vars ["MY_ENV_VAR"]=YES') + self.addTearDownHook( + lambda: self.runCmd("settings clear target.env-vars")) + + self.runCmd("run", RUN_SUCCEEDED) + + # Read the output file produced by running the program. + if lldb.remote_platform: + self.runCmd('platform get-file "output2.txt" "output2.txt"') + with open('output2.txt', 'r') as f: + output = f.read() + + self.expect(output, exe=False, + substrs = ["argv[1] matches", + "argv[2] matches", + "argv[3] matches", + "Environment variable 'MY_ENV_VAR' successfully passed."]) + + @skipIfRemote # it doesn't make sense to send host env to remote target + def test_pass_host_env_vars(self): + """Test that the host env vars are passed to the launched process.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # By default, inherit-env is 'true'. + self.expect('settings show target.inherit-env', "Default inherit-env is 'true'", + startstr = "target.inherit-env (boolean) = true") + + # Set some host environment variables now. + os.environ["MY_HOST_ENV_VAR1"] = "VAR1" + os.environ["MY_HOST_ENV_VAR2"] = "VAR2" + + # This is the function to unset the two env variables set above. + def unset_env_variables(): + os.environ.pop("MY_HOST_ENV_VAR1") + os.environ.pop("MY_HOST_ENV_VAR2") + + self.addTearDownHook(unset_env_variables) + self.runCmd("run", RUN_SUCCEEDED) + + # Read the output file produced by running the program. + if lldb.remote_platform: + self.runCmd('platform get-file "output1.txt" "output1.txt"') + with open('output1.txt', 'r') as f: + output = f.read() + + self.expect(output, exe=False, + substrs = ["The host environment variable 'MY_HOST_ENV_VAR1' successfully passed.", + "The host environment variable 'MY_HOST_ENV_VAR2' successfully passed."]) + + def test_set_error_output_path(self): + """Test that setting target.error/output-path for the launched process works.""" + self.build() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Set the error-path and output-path and verify both are set. + self.runCmd("settings set target.error-path stderr.txt") + self.runCmd("settings set target.output-path stdout.txt") + # And add hooks to restore the original settings during tearDown(). + self.addTearDownHook( + lambda: self.runCmd("settings clear target.output-path")) + self.addTearDownHook( + lambda: self.runCmd("settings clear target.error-path")) + + self.expect("settings show target.error-path", + SETTING_MSG("target.error-path"), + substrs = ['target.error-path (file) = "stderr.txt"']) + + self.expect("settings show target.output-path", + SETTING_MSG("target.output-path"), + substrs = ['target.output-path (file) = "stdout.txt"']) + + self.runCmd("run", RUN_SUCCEEDED) + + if lldb.remote_platform: + self.runCmd('platform get-file "stderr.txt" "stderr.txt"') + self.runCmd('platform get-file "stdout.txt" "stdout.txt"') + + + # The 'stderr.txt' file should now exist. + self.assertTrue(os.path.isfile("stderr.txt"), + "'stderr.txt' exists due to target.error-path.") + + # Read the output file produced by running the program. + with open('stderr.txt', 'r') as f: + output = f.read() + + self.expect(output, exe=False, + startstr = "This message should go to standard error.") + + # The 'stdout.txt' file should now exist. + self.assertTrue(os.path.isfile("stdout.txt"), + "'stdout.txt' exists due to target.output-path.") + + # Read the output file produced by running the program. + with open('stdout.txt', 'r') as f: + output = f.read() + + self.expect(output, exe=False, + startstr = "This message should go to standard out.") + + @no_debug_info_test + def test_print_dictionary_setting(self): + self.runCmd ("settings clear target.env-vars") + self.runCmd ("settings set target.env-vars [\"MY_VAR\"]=some-value") + self.expect ("settings show target.env-vars", + substrs = [ "MY_VAR=some-value" ]) + self.runCmd ("settings clear target.env-vars") + + @no_debug_info_test + def test_print_array_setting(self): + self.runCmd ("settings clear target.run-args") + self.runCmd ("settings set target.run-args gobbledy-gook") + self.expect ("settings show target.run-args", + substrs = [ '[0]: "gobbledy-gook"' ]) + self.runCmd ("settings clear target.run-args") + + @no_debug_info_test + def test_settings_with_quotes (self): + self.runCmd ("settings clear target.run-args") + self.runCmd ("settings set target.run-args a b c") + self.expect ("settings show target.run-args", + substrs = [ '[0]: "a"', + '[1]: "b"', + '[2]: "c"' ]) + self.runCmd ("settings set target.run-args 'a b c'") + self.expect ("settings show target.run-args", + substrs = [ '[0]: "a b c"' ]) + self.runCmd ("settings clear target.run-args") + self.runCmd ("settings clear target.env-vars") + self.runCmd ('settings set target.env-vars ["MY_FILE"]="this is a file name with spaces.txt"') + self.expect ("settings show target.env-vars", + substrs = [ 'MY_FILE=this is a file name with spaces.txt' ]) + self.runCmd ("settings clear target.env-vars") + # Test and make sure that setting "format-string" settings obeys quotes if they are provided + self.runCmd ("settings set thread-format 'abc def' ") + self.expect ("settings show thread-format", 'thread-format (format-string) = "abc def"') + self.runCmd ('settings set thread-format "abc def" ') + self.expect ("settings show thread-format", 'thread-format (format-string) = "abc def"') + # Make sure when no quotes are provided that we maintain any trailing spaces + self.runCmd ('settings set thread-format abc def ') + self.expect ("settings show thread-format", 'thread-format (format-string) = "abc def "') + self.runCmd ('settings clear thread-format') + + @no_debug_info_test + def test_settings_with_trailing_whitespace (self): + + # boolean + self.runCmd ("settings set target.skip-prologue true") # Set to known value + self.runCmd ("settings set target.skip-prologue false ") # Set to new value with trailing whitespace + # Make sure the setting was correctly set to "false" + self.expect ("settings show target.skip-prologue", SETTING_MSG("target.skip-prologue"), + startstr = "target.skip-prologue (boolean) = false") + self.runCmd("settings clear target.skip-prologue", check=False) + # integer + self.runCmd ("settings set term-width 70") # Set to known value + self.runCmd ("settings set term-width 60 \t") # Set to new value with trailing whitespaces + self.expect ("settings show term-width", SETTING_MSG("term-width"), + startstr = "term-width (int) = 60") + self.runCmd("settings clear term-width", check=False) + # string + self.runCmd ("settings set target.arg0 abc") # Set to known value + self.runCmd ("settings set target.arg0 cde\t ") # Set to new value with trailing whitespaces + self.expect ("settings show target.arg0", SETTING_MSG("target.arg0"), + startstr = 'target.arg0 (string) = "cde"') + self.runCmd("settings clear target.arg0", check=False) + # file + path1 = os.path.join(os.getcwd(), "path1.txt") + path2 = os.path.join(os.getcwd(), "path2.txt") + self.runCmd ("settings set target.output-path %s" % path1) # Set to known value + self.expect ("settings show target.output-path", SETTING_MSG("target.output-path"), + startstr = 'target.output-path (file) = ', substrs=[path1]) + self.runCmd ("settings set target.output-path %s " % path2) # Set to new value with trailing whitespaces + self.expect ("settings show target.output-path", SETTING_MSG("target.output-path"), + startstr = 'target.output-path (file) = ', substrs=[path2]) + self.runCmd("settings clear target.output-path", check=False) + # enum + self.runCmd ("settings set stop-disassembly-display never") # Set to known value + self.runCmd ("settings set stop-disassembly-display always ") # Set to new value with trailing whitespaces + self.expect ("settings show stop-disassembly-display", SETTING_MSG("stop-disassembly-display"), + startstr = 'stop-disassembly-display (enum) = always') + self.runCmd("settings clear stop-disassembly-display", check=False) + # language + self.runCmd ("settings set target.language c89") # Set to known value + self.runCmd ("settings set target.language go ") # Set to new value with trailing whitespace + self.expect ("settings show target.language", SETTING_MSG("target.language"), + startstr = "target.language (language) = go") + self.runCmd("settings clear target.language", check=False) + # arguments + self.runCmd ("settings set target.run-args 1 2 3") # Set to known value + self.runCmd ("settings set target.run-args 3 4 5 ") # Set to new value with trailing whitespaces + self.expect ("settings show target.run-args", SETTING_MSG("target.run-args"), + substrs = [ 'target.run-args (arguments) =', + '[0]: "3"', + '[1]: "4"', + '[2]: "5"' ]) + self.runCmd ("settings set target.run-args 1 2 3") # Set to known value + self.runCmd ("settings set target.run-args 3 \ \ ") # Set to new value with trailing whitespaces + self.expect ("settings show target.run-args", SETTING_MSG("target.run-args"), + substrs = [ 'target.run-args (arguments) =', + '[0]: "3"', + '[1]: " "', + '[2]: " "' ]) + self.runCmd("settings clear target.run-args", check=False) + # dictionaries + self.runCmd ("settings clear target.env-vars") # Set to known value + self.runCmd ("settings set target.env-vars A=B C=D\t ") # Set to new value with trailing whitespaces + self.expect ("settings show target.env-vars", SETTING_MSG("target.env-vars"), + substrs = [ 'target.env-vars (dictionary of strings) =', + 'A=B', + 'C=D']) + self.runCmd("settings clear target.env-vars", check=False) + # regex + self.runCmd ("settings clear target.process.thread.step-avoid-regexp") # Set to known value + self.runCmd ("settings set target.process.thread.step-avoid-regexp foo\\ ") # Set to new value with trailing whitespaces + self.expect ("settings show target.process.thread.step-avoid-regexp", + SETTING_MSG("target.process.thread.step-avoid-regexp"), + substrs = [ 'target.process.thread.step-avoid-regexp (regex) = foo\\ ']) + self.runCmd("settings clear target.process.thread.step-avoid-regexp", check=False) + # format-string + self.runCmd ("settings clear disassembly-format") # Set to known value + self.runCmd ("settings set disassembly-format foo ") # Set to new value with trailing whitespaces + self.expect ("settings show disassembly-format", + SETTING_MSG("disassembly-format"), + substrs = [ 'disassembly-format (format-string) = "foo "']) + self.runCmd("settings clear disassembly-format", check=False) + + @no_debug_info_test + def test_all_settings_exist (self): + self.expect ("settings show", + substrs = [ "auto-confirm", + "frame-format", + "notify-void", + "prompt", + "script-lang", + "stop-disassembly-count", + "stop-disassembly-display", + "stop-line-count-after", + "stop-line-count-before", + "term-width", + "thread-format", + "use-external-editor", + "target.default-arch", + "target.move-to-nearest-code", + "target.expr-prefix", + "target.language", + "target.prefer-dynamic-value", + "target.enable-synthetic-value", + "target.skip-prologue", + "target.source-map", + "target.exec-search-paths", + "target.max-children-count", + "target.max-string-summary-length", + "target.breakpoints-use-platform-avoid-list", + "target.run-args", + "target.env-vars", + "target.inherit-env", + "target.input-path", + "target.output-path", + "target.error-path", + "target.disable-aslr", + "target.disable-stdio", + "target.x86-disassembly-flavor", + "target.use-hex-immediates", + "target.hex-immediate-style", + "target.process.disable-memory-cache", + "target.process.extra-startup-command", + "target.process.thread.step-avoid-regexp", + "target.process.thread.trace-thread"]) diff --git a/packages/Python/lldbsuite/test/settings/main.cpp b/packages/Python/lldbsuite/test/settings/main.cpp new file mode 100644 index 000000000000..cf2b16b0089b --- /dev/null +++ b/packages/Python/lldbsuite/test/settings/main.cpp @@ -0,0 +1,75 @@ +//===-- 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 +#include +#include + +int numberfn() +{ + return 0x5a; +} + +int +main(int argc, char const *argv[]) +{ + // The program writes its output to the following file: + // + // o "output1.txt" for test_pass_host_env_vars() test case + // o "output2.txt" for test_run_args_and_env_vars_with_dsym() test case + // o "output2.txt" for test_run_args_and_env_vars_with_dwarf() test case + std::ofstream outfile; + if (argc == 1) + outfile.open("output1.txt"); + else + outfile.open("output2.txt"); + + for (unsigned i = 0; i < argc; ++i) { + std::string theArg(argv[i]); + if (i == 1 && "A" == theArg) + outfile << "argv[1] matches\n"; + + if (i == 2 && "B" == theArg) + outfile << "argv[2] matches\n"; + + if (i == 3 && "C" == theArg) + outfile << "argv[3] matches\n"; + } + + // For passing environment vars from the debugger to the launched process. + if (::getenv("MY_ENV_VAR")) { + std::string MY_ENV_VAR(getenv("MY_ENV_VAR")); + if ("YES" == MY_ENV_VAR) { + outfile << "Environment variable 'MY_ENV_VAR' successfully passed.\n"; + } + } + + + // For passing host environment vars to the launched process. + if (::getenv("MY_HOST_ENV_VAR1")) { + std::string MY_HOST_ENV_VAR1(getenv("MY_HOST_ENV_VAR1")); + if ("VAR1" == MY_HOST_ENV_VAR1) { + outfile << "The host environment variable 'MY_HOST_ENV_VAR1' successfully passed.\n"; + } + } + + if (::getenv("MY_HOST_ENV_VAR2")) { + std::string MY_HOST_ENV_VAR2(getenv("MY_HOST_ENV_VAR2")); + if ("VAR2" == MY_HOST_ENV_VAR2) { + outfile << "The host environment variable 'MY_HOST_ENV_VAR2' successfully passed.\n"; + } + } + + std::cerr << "This message should go to standard error.\n"; + std::cout << "This message should go to standard out.\n"; + + outfile.close(); + return numberfn(); +} diff --git a/packages/Python/lldbsuite/test/settings/quoting/Makefile b/packages/Python/lldbsuite/test/settings/quoting/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/packages/Python/lldbsuite/test/settings/quoting/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/settings/quoting/TestQuoting.py b/packages/Python/lldbsuite/test/settings/quoting/TestQuoting.py new file mode 100644 index 000000000000..878fc8a50675 --- /dev/null +++ b/packages/Python/lldbsuite/test/settings/quoting/TestQuoting.py @@ -0,0 +1,92 @@ +""" +Test quoting of arguments to lldb commands +""" + +from __future__ import print_function + + + +import os, time, re +import lldb +from lldbsuite.test.lldbtest import * + +class SettingsCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @classmethod + def classCleanup(cls): + """Cleanup the test byproducts.""" + cls.RemoveTempFile("stdout.txt") + + @no_debug_info_test + def test_no_quote(self): + self.do_test_args("a b c", "a\0b\0c\0") + + @expectedFailureWindows("http://llvm.org/pr24557") + @no_debug_info_test + def test_single_quote(self): + self.do_test_args("'a b c'", "a b c\0") + + @no_debug_info_test + def test_double_quote(self): + self.do_test_args('"a b c"', "a b c\0") + + @expectedFailureWindows("http://llvm.org/pr24557") + @no_debug_info_test + def test_single_quote_escape(self): + self.do_test_args("'a b\\' c", "a b\\\0c\0") + + @expectedFailureWindows("http://llvm.org/pr24557") + @no_debug_info_test + def test_double_quote_escape(self): + self.do_test_args('"a b\\" c"', 'a b" c\0') + + @expectedFailureWindows("http://llvm.org/pr24557") + @no_debug_info_test + def test_double_quote_escape2(self): + self.do_test_args('"a b\\\\" c', 'a b\\\0c\0') + + @no_debug_info_test + def test_single_in_double(self): + self.do_test_args('"a\'b"', "a'b\0") + + @expectedFailureWindows("http://llvm.org/pr24557") + @no_debug_info_test + def test_double_in_single(self): + self.do_test_args("'a\"b'", 'a"b\0') + + @no_debug_info_test + def test_combined(self): + self.do_test_args('"a b"c\'d e\'', 'a bcd e\0') + + @no_debug_info_test + def test_bare_single(self): + self.do_test_args("a\\'b", "a'b\0") + + @expectedFailureWindows("http://llvm.org/pr24557") + @no_debug_info_test + def test_bare_double(self): + self.do_test_args('a\\"b', 'a"b\0') + + def do_test_args(self, args_in, args_out): + """Test argument parsing. Run the program with args_in. The program dumps its arguments + to stdout. Compare the stdout with args_out.""" + self.buildDefault() + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("process launch -o stdout.txt -- " + args_in) + + if lldb.remote_platform: + src_file_spec = lldb.SBFileSpec('stdout.txt', False) + dst_file_spec = lldb.SBFileSpec('stdout.txt', True) + lldb.remote_platform.Get(src_file_spec, dst_file_spec) + + with open('stdout.txt', 'r') as f: + output = f.read() + + self.RemoveTempFile("stdout.txt") + + self.assertEqual(output, args_out) diff --git a/packages/Python/lldbsuite/test/settings/quoting/main.c b/packages/Python/lldbsuite/test/settings/quoting/main.c new file mode 100644 index 000000000000..6e01c2d1cd87 --- /dev/null +++ b/packages/Python/lldbsuite/test/settings/quoting/main.c @@ -0,0 +1,13 @@ +#include +#include + +/* This program writes its arguments (separated by '\0') to stdout. */ +int +main(int argc, char const *argv[]) +{ + int i; + for (i = 1; i < argc; ++i) + fwrite(argv[i], strlen(argv[i])+1, 1, stdout); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/source-manager/Makefile b/packages/Python/lldbsuite/test/source-manager/Makefile new file mode 100644 index 000000000000..d6cd0db0506f --- /dev/null +++ b/packages/Python/lldbsuite/test/source-manager/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/source-manager/TestSourceManager.py b/packages/Python/lldbsuite/test/source-manager/TestSourceManager.py new file mode 100644 index 000000000000..b4e7541a709a --- /dev/null +++ b/packages/Python/lldbsuite/test/source-manager/TestSourceManager.py @@ -0,0 +1,172 @@ +""" +Test lldb core component: SourceManager. + +Test cases: + +o test_display_source_python: + Test display of source using the SBSourceManager API. +o test_modify_source_file_while_debugging: + Test the caching mechanism of the source manager. +""" + +from __future__ import print_function + + + +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class SourceManagerTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.c', '// Set break point at this line.') + + @add_test_categories(['pyapi']) + def test_display_source_python(self): + """Test display of source using the SBSourceManager API.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + # + # Exercise Python APIs to display source lines. + # + + # Create the filespec for 'main.c'. + filespec = lldb.SBFileSpec('main.c', False) + source_mgr = self.dbg.GetSourceManager() + # Use a string stream as the destination. + stream = lldb.SBStream() + source_mgr.DisplaySourceLinesWithLineNumbers(filespec, + self.line, + 2, # context before + 2, # context after + "=>", # prefix for current line + stream) + + # 2 + # 3 int main(int argc, char const *argv[]) { + # => 4 printf("Hello world.\n"); // Set break point at this line. + # 5 return 0; + # 6 } + self.expect(stream.GetData(), "Source code displayed correctly", + exe=False, + patterns = ['=> %d.*Hello world' % self.line]) + + # Boundary condition testings for SBStream(). LLDB should not crash! + stream.Print(None) + stream.RedirectToFile(None, True) + + def test_move_and_then_display_source(self): + """Test that target.source-map settings work by moving main.c to hidden/main.c.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Move main.c to hidden/main.c. + main_c = "main.c" + main_c_hidden = os.path.join("hidden", main_c) + os.rename(main_c, main_c_hidden) + + if self.TraceOn(): + system([["ls"]]) + system([["ls", "hidden"]]) + + # Restore main.c after the test. + self.addTearDownHook(lambda: os.rename(main_c_hidden, main_c)) + + # Set target.source-map settings. + self.runCmd("settings set target.source-map %s %s" % (os.getcwd(), os.path.join(os.getcwd(), "hidden"))) + # And verify that the settings work. + self.expect("settings show target.source-map", + substrs = [os.getcwd(), os.path.join(os.getcwd(), "hidden")]) + + # Display main() and verify that the source mapping has been kicked in. + self.expect("source list -n main", SOURCE_DISPLAYED_CORRECTLY, + substrs = ['Hello world']) + + def test_modify_source_file_while_debugging(self): + """Modify a source file while debugging the executable.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'main.c:%d' % self.line, + 'stop reason = breakpoint']) + + # Display some source code. + self.expect("source list -f main.c -l %d" % self.line, SOURCE_DISPLAYED_CORRECTLY, + substrs = ['Hello world']) + + # The '-b' option shows the line table locations from the debug information + # that indicates valid places to set source level breakpoints. + + # The file to display is implicit in this case. + self.runCmd("source list -l %d -c 3 -b" % self.line) + output = self.res.GetOutput().splitlines()[0] + + # If the breakpoint set command succeeded, we should expect a positive number + # of breakpoints for the current line, i.e., self.line. + import re + m = re.search('^\[(\d+)\].*// Set break point at this line.', output) + if not m: + self.fail("Fail to display source level breakpoints") + self.assertTrue(int(m.group(1)) > 0) + + # Read the main.c file content. + with open('main.c', 'r') as f: + original_content = f.read() + if self.TraceOn(): + print("original content:", original_content) + + # Modify the in-memory copy of the original source code. + new_content = original_content.replace('Hello world', 'Hello lldb', 1) + + # This is the function to restore the original content. + def restore_file(): + #print("os.path.getmtime() before restore:", os.path.getmtime('main.c')) + time.sleep(1) + with open('main.c', 'wb') as f: + f.write(original_content) + if self.TraceOn(): + with open('main.c', 'r') as f: + print("content restored to:", f.read()) + # Touch the file just to be sure. + os.utime('main.c', None) + if self.TraceOn(): + print("os.path.getmtime() after restore:", os.path.getmtime('main.c')) + + + + # Modify the source code file. + with open('main.c', 'wb') as f: + time.sleep(1) + f.write(new_content) + if self.TraceOn(): + print("new content:", new_content) + print("os.path.getmtime() after writing new content:", os.path.getmtime('main.c')) + # Add teardown hook to restore the file to the original content. + self.addTearDownHook(restore_file) + + # Display the source code again. We should see the updated line. + self.expect("source list -f main.c -l %d" % self.line, SOURCE_DISPLAYED_CORRECTLY, + substrs = ['Hello lldb']) diff --git a/packages/Python/lldbsuite/test/source-manager/hidden/.keep b/packages/Python/lldbsuite/test/source-manager/hidden/.keep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/Python/lldbsuite/test/source-manager/main.c b/packages/Python/lldbsuite/test/source-manager/main.c new file mode 100644 index 000000000000..9f62166357ce --- /dev/null +++ b/packages/Python/lldbsuite/test/source-manager/main.c @@ -0,0 +1,6 @@ +#include + +int main(int argc, char const *argv[]) { + printf("Hello world.\n"); // Set break point at this line. + return 0; +} diff --git a/packages/Python/lldbsuite/test/terminal/TestSTTYBeforeAndAfter.py b/packages/Python/lldbsuite/test/terminal/TestSTTYBeforeAndAfter.py new file mode 100644 index 000000000000..335042737d44 --- /dev/null +++ b/packages/Python/lldbsuite/test/terminal/TestSTTYBeforeAndAfter.py @@ -0,0 +1,121 @@ +""" +Test that 'stty -a' displays the same output before and after running the lldb command. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * + +class TestSTTYBeforeAndAfter(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @classmethod + def classCleanup(cls): + """Cleanup the test byproducts.""" + cls.RemoveTempFile("child_send1.txt") + cls.RemoveTempFile("child_read1.txt") + cls.RemoveTempFile("child_send2.txt") + cls.RemoveTempFile("child_read2.txt") + + @expectedFailureHostWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @no_debug_info_test + def test_stty_dash_a_before_and_afetr_invoking_lldb_command(self): + """Test that 'stty -a' displays the same output before and after running the lldb command.""" + import pexpect + if not which('expect'): + self.skipTest("The 'expect' program cannot be located, skip the test") + + # The expect prompt. + expect_prompt = "expect[0-9.]+> " + # The default lldb prompt. + lldb_prompt = "(lldb) " + + # So that the child gets torn down after the test. + self.child = pexpect.spawn('expect') + child = self.child + + child.expect(expect_prompt) + child.setecho(True) + if self.TraceOn(): + child.logfile = sys.stdout + + if self.platformIsDarwin(): + child.sendline('set env(TERM) xterm') + else: + child.sendline('set env(TERM) vt100') + child.expect(expect_prompt) + child.sendline('puts $env(TERM)') + child.expect(expect_prompt) + + # Turn on loggings for input/output to/from the child. + with open('child_send1.txt', 'w') as f_send1: + with open('child_read1.txt', 'w') as f_read1: + child.logfile_send = f_send1 + child.logfile_read = f_read1 + + child.sendline('stty -a') + child.expect(expect_prompt) + + # Now that the stage1 logging is done, restore logfile to None to + # stop further logging. + child.logfile_send = None + child.logfile_read = None + + # Invoke the lldb command. + child.sendline('%s %s' % (lldbtest_config.lldbExec, self.lldbOption)) + child.expect_exact(lldb_prompt) + + # Immediately quit. + child.sendline('quit') + child.expect(expect_prompt) + + with open('child_send2.txt', 'w') as f_send2: + with open('child_read2.txt', 'w') as f_read2: + child.logfile_send = f_send2 + child.logfile_read = f_read2 + + child.sendline('stty -a') + child.expect(expect_prompt) + + child.sendline('exit') + + # Now that the stage2 logging is done, restore logfile to None to + # stop further logging. + child.logfile_send = None + child.logfile_read = None + + with open('child_send1.txt', 'r') as fs: + if self.TraceOn(): + print("\n\nContents of child_send1.txt:") + print(fs.read()) + with open('child_read1.txt', 'r') as fr: + from_child1 = fr.read() + if self.TraceOn(): + print("\n\nContents of child_read1.txt:") + print(from_child1) + + with open('child_send2.txt', 'r') as fs: + if self.TraceOn(): + print("\n\nContents of child_send2.txt:") + print(fs.read()) + with open('child_read2.txt', 'r') as fr: + from_child2 = fr.read() + if self.TraceOn(): + print("\n\nContents of child_read2.txt:") + print(from_child2) + + stty_output1_lines = from_child1.splitlines() + stty_output2_lines = from_child2.splitlines() + zipped = list(zip(stty_output1_lines, stty_output2_lines)) + for tuple in zipped: + if self.TraceOn(): + print("tuple->%s" % str(tuple)) + # Every line should compare equal until the first blank line. + if len(tuple[0]) == 0: + break + self.assertTrue(tuple[0] == tuple[1]) diff --git a/packages/Python/lldbsuite/test/test_categories.py b/packages/Python/lldbsuite/test/test_categories.py new file mode 100644 index 000000000000..e3e446091050 --- /dev/null +++ b/packages/Python/lldbsuite/test/test_categories.py @@ -0,0 +1,70 @@ +""" +Provides definitions for various lldb test categories +""" + +from __future__ import absolute_import +from __future__ import print_function + +# System modules +import sys + +# Third-party modules + +# LLDB modules + +debug_info_categories = [ + 'dwarf', 'dwo', 'dsym' +] + +all_categories = { + 'dataformatters': 'Tests related to the type command and the data formatters subsystem', + 'dwarf' : 'Tests that can be run with DWARF debug information', + 'dwo' : 'Tests that can be run with DWO debug information', + 'dsym' : 'Tests that can be run with DSYM debug information', + 'expression' : 'Tests related to the expression parser', + 'objc' : 'Tests related to the Objective-C programming language support', + 'pyapi' : 'Tests related to the Python API', + 'basic_process' : 'Basic process execution sniff tests.', + 'cmdline' : 'Tests related to the LLDB command-line interface', + 'dyntype' : 'Tests related to dynamic type support', + 'stresstest' : 'Tests related to stressing lldb limits', + 'flakey' : 'Flakey test cases, i.e. tests that do not reliably pass at each execution', + 'lldb-mi' : 'lldb-mi tests' +} + +def unique_string_match(yourentry, list): + candidate = None + for item in list: + if not item.startswith(yourentry): + continue + if candidate: + return None + candidate = item + return candidate + +def is_supported_on_platform(category, platform): + if category == "dwo": + return platform in ["linux", "freebsd", "windows"] + elif category == "dsym": + return platform in ["darwin", "macosx", "ios"] + return True + +def validate(categories, exact_match): + """ + For each category in categories, ensure that it's a valid category (if exact_match is false, + unique prefixes are also accepted). If a category is invalid, print a message and quit. + If all categories are valid, return the list of categories. Prefixes are expanded in the + returned list. + """ + result = [] + for category in categories: + origCategory = category + if category not in all_categories and not exact_match: + category = unique_string_match(category, all_categories) + if (category not in all_categories) or category == None: + print("fatal error: category '" + origCategory + "' is not a valid category") + print("if you have added a new category, please edit test_categories.py, adding your new category to all_categories") + print("else, please specify one or more of the following: " + str(list(all_categories.keys()))) + sys.exit(1) + result.append(category) + return result diff --git a/packages/Python/lldbsuite/test/test_result.py b/packages/Python/lldbsuite/test/test_result.py new file mode 100644 index 000000000000..9665d672a79a --- /dev/null +++ b/packages/Python/lldbsuite/test/test_result.py @@ -0,0 +1,224 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Provides the LLDBTestResult class, which holds information about progress +and results of a single test run. +""" + +from __future__ import absolute_import +from __future__ import print_function + +# System modules +import inspect + +# Third-party modules +import unittest2 + +# LLDB Modules +import lldbsuite +from . import configuration +from .result_formatter import EventBuilder + + +class LLDBTestResult(unittest2.TextTestResult): + """ + Enforce a singleton pattern to allow introspection of test progress. + + Overwrite addError(), addFailure(), and addExpectedFailure() methods + to enable each test instance to track its failure/error status. It + is used in the LLDB test framework to emit detailed trace messages + to a log file for easier human inspection of test failures/errors. + """ + __singleton__ = None + __ignore_singleton__ = False + + @staticmethod + def getTerminalSize(): + import os + env = os.environ + def ioctl_GWINSZ(fd): + try: + import fcntl, termios, struct, os + cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, + '1234')) + except: + return + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + cr = (env.get('LINES', 25), env.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) + + def __init__(self, *args): + if not LLDBTestResult.__ignore_singleton__ and LLDBTestResult.__singleton__: + raise Exception("LLDBTestResult instantiated more than once") + super(LLDBTestResult, self).__init__(*args) + LLDBTestResult.__singleton__ = self + # Now put this singleton into the lldb module namespace. + configuration.test_result = self + # Computes the format string for displaying the counter. + counterWidth = len(str(configuration.suite.countTestCases())) + self.fmt = "%" + str(counterWidth) + "d: " + self.indentation = ' ' * (counterWidth + 2) + # This counts from 1 .. suite.countTestCases(). + self.counter = 0 + (width, height) = LLDBTestResult.getTerminalSize() + self.results_formatter = configuration.results_formatter_object + + def _config_string(self, test): + compiler = getattr(test, "getCompiler", None) + arch = getattr(test, "getArchitecture", None) + return "%s-%s" % (compiler() if compiler else "", arch() if arch else "") + + def _exc_info_to_string(self, err, test): + """Overrides superclass TestResult's method in order to append + our test config info string to the exception info string.""" + if hasattr(test, "getArchitecture") and hasattr(test, "getCompiler"): + return '%sConfig=%s-%s' % (super(LLDBTestResult, self)._exc_info_to_string(err, test), + test.getArchitecture(), + test.getCompiler()) + else: + return super(LLDBTestResult, self)._exc_info_to_string(err, test) + + def getDescription(self, test): + doc_first_line = test.shortDescription() + if self.descriptions and doc_first_line: + return '\n'.join((str(test), self.indentation + doc_first_line)) + else: + return str(test) + + def getCategoriesForTest(self, test): + """ + Gets all the categories for the currently running test method in test case + """ + test_categories = [] + test_method = getattr(test, test._testMethodName) + if test_method != None and hasattr(test_method, "categories"): + test_categories.extend(test_method.categories) + + test_categories.extend(test.getCategories()) + + return test_categories + + def hardMarkAsSkipped(self,test): + getattr(test, test._testMethodName).__func__.__unittest_skip__ = True + getattr(test, test._testMethodName).__func__.__unittest_skip_why__ = "test case does not fall in any category of interest for this run" + test.__class__.__unittest_skip__ = True + test.__class__.__unittest_skip_why__ = "test case does not fall in any category of interest for this run" + + def startTest(self, test): + if configuration.shouldSkipBecauseOfCategories(self.getCategoriesForTest(test)): + self.hardMarkAsSkipped(test) + configuration.setCrashInfoHook("%s at %s" % (str(test),inspect.getfile(test.__class__))) + self.counter += 1 + #if self.counter == 4: + # import crashinfo + # crashinfo.testCrashReporterDescription(None) + test.test_number = self.counter + if self.showAll: + self.stream.write(self.fmt % self.counter) + super(LLDBTestResult, self).startTest(test) + if self.results_formatter: + self.results_formatter.handle_event( + EventBuilder.event_for_start(test)) + + def addSuccess(self, test): + super(LLDBTestResult, self).addSuccess(test) + if configuration.parsable: + self.stream.write("PASS: LLDB (%s) :: %s\n" % (self._config_string(test), str(test))) + if self.results_formatter: + self.results_formatter.handle_event( + EventBuilder.event_for_success(test)) + + def addError(self, test, err): + configuration.sdir_has_content = True + super(LLDBTestResult, self).addError(test, err) + method = getattr(test, "markError", None) + if method: + method() + if configuration.parsable: + self.stream.write("FAIL: LLDB (%s) :: %s\n" % (self._config_string(test), str(test))) + if self.results_formatter: + self.results_formatter.handle_event( + EventBuilder.event_for_error(test, err)) + + def addCleanupError(self, test, err): + configuration.sdir_has_content = True + super(LLDBTestResult, self).addCleanupError(test, err) + method = getattr(test, "markCleanupError", None) + if method: + method() + if configuration.parsable: + self.stream.write("CLEANUP ERROR: LLDB (%s) :: %s\n" % (self._config_string(test), str(test))) + if self.results_formatter: + self.results_formatter.handle_event( + EventBuilder.event_for_cleanup_error( + test, err)) + + def addFailure(self, test, err): + configuration.sdir_has_content = True + super(LLDBTestResult, self).addFailure(test, err) + method = getattr(test, "markFailure", None) + if method: + method() + if configuration.parsable: + self.stream.write("FAIL: LLDB (%s) :: %s\n" % (self._config_string(test), str(test))) + if configuration.useCategories: + test_categories = self.getCategoriesForTest(test) + for category in test_categories: + if category in configuration.failuresPerCategory: + configuration.failuresPerCategory[category] = configuration.failuresPerCategory[category] + 1 + else: + configuration.failuresPerCategory[category] = 1 + if self.results_formatter: + self.results_formatter.handle_event( + EventBuilder.event_for_failure(test, err)) + + + def addExpectedFailure(self, test, err, bugnumber): + configuration.sdir_has_content = True + super(LLDBTestResult, self).addExpectedFailure(test, err, bugnumber) + method = getattr(test, "markExpectedFailure", None) + if method: + method(err, bugnumber) + if configuration.parsable: + self.stream.write("XFAIL: LLDB (%s) :: %s\n" % (self._config_string(test), str(test))) + if self.results_formatter: + self.results_formatter.handle_event( + EventBuilder.event_for_expected_failure( + test, err, bugnumber)) + + def addSkip(self, test, reason): + configuration.sdir_has_content = True + super(LLDBTestResult, self).addSkip(test, reason) + method = getattr(test, "markSkippedTest", None) + if method: + method() + if configuration.parsable: + self.stream.write("UNSUPPORTED: LLDB (%s) :: %s (%s) \n" % (self._config_string(test), str(test), reason)) + if self.results_formatter: + self.results_formatter.handle_event( + EventBuilder.event_for_skip(test, reason)) + + def addUnexpectedSuccess(self, test, bugnumber): + configuration.sdir_has_content = True + super(LLDBTestResult, self).addUnexpectedSuccess(test, bugnumber) + method = getattr(test, "markUnexpectedSuccess", None) + if method: + method(bugnumber) + if configuration.parsable: + self.stream.write("XPASS: LLDB (%s) :: %s\n" % (self._config_string(test), str(test))) + if self.results_formatter: + self.results_formatter.handle_event( + EventBuilder.event_for_unexpected_success( + test, bugnumber)) diff --git a/packages/Python/lldbsuite/test/test_runner/README.txt b/packages/Python/lldbsuite/test/test_runner/README.txt new file mode 100644 index 000000000000..bb40870e7965 --- /dev/null +++ b/packages/Python/lldbsuite/test/test_runner/README.txt @@ -0,0 +1,5 @@ +This directory contains source and tests for the lldb test runner +architecture. This directory is not for lldb python tests. It +is the test runner. The tests under this diretory are test-runner +tests (i.e. tests that verify the test runner itself runs properly). + diff --git a/packages/Python/lldbsuite/test/test_runner/lib/lldb_utils.py b/packages/Python/lldbsuite/test/test_runner/lib/lldb_utils.py new file mode 100644 index 000000000000..e469bbf12207 --- /dev/null +++ b/packages/Python/lldbsuite/test/test_runner/lib/lldb_utils.py @@ -0,0 +1,66 @@ +""" +The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Provides classes used by the test results reporting infrastructure +within the LLDB test suite. + + +This module contains utilities used by the lldb test framwork. +""" + + +class OptionalWith(object): + # pylint: disable=too-few-public-methods + # This is a wrapper - it is not meant to provide any extra methods. + """Provides a wrapper for objects supporting "with", allowing None. + + This lets a user use the "with object" syntax for resource usage + (e.g. locks) even when the wrapped with object is None. + + e.g. + + wrapped_lock = OptionalWith(thread.Lock()) + with wrapped_lock: + # Do something while the lock is obtained. + pass + + might_be_none = None + wrapped_none = OptionalWith(might_be_none) + with wrapped_none: + # This code here still works. + pass + + This prevents having to write code like this when + a lock is optional: + + if lock: + lock.acquire() + + try: + code_fragament_always_run() + finally: + if lock: + lock.release() + + And I'd posit it is safer, as it becomes impossible to + forget the try/finally using OptionalWith(), since + the with syntax can be used. + """ + def __init__(self, wrapped_object): + self.wrapped_object = wrapped_object + + def __enter__(self): + if self.wrapped_object is not None: + return self.wrapped_object.__enter__() + else: + return self + + def __exit__(self, the_type, value, traceback): + if self.wrapped_object is not None: + return self.wrapped_object.__exit__(the_type, value, traceback) + else: + # Don't suppress any exceptions + return False diff --git a/packages/Python/lldbsuite/test/test_runner/lib/process_control.py b/packages/Python/lldbsuite/test/test_runner/lib/process_control.py new file mode 100644 index 000000000000..a7e639e4b8b6 --- /dev/null +++ b/packages/Python/lldbsuite/test/test_runner/lib/process_control.py @@ -0,0 +1,705 @@ +""" +The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Provides classes used by the test results reporting infrastructure +within the LLDB test suite. + + +This module provides process-management support for the LLDB test +running infrasructure. +""" + +# System imports +import os +import re +import signal +import subprocess +import sys +import threading + + +class CommunicatorThread(threading.Thread): + """Provides a thread class that communicates with a subprocess.""" + def __init__(self, process, event, output_file): + super(CommunicatorThread, self).__init__() + # Don't let this thread prevent shutdown. + self.daemon = True + self.process = process + self.pid = process.pid + self.event = event + self.output_file = output_file + self.output = None + + def run(self): + try: + # Communicate with the child process. + # This will not complete until the child process terminates. + self.output = self.process.communicate() + except Exception as exception: # pylint: disable=broad-except + if self.output_file: + self.output_file.write( + "exception while using communicate() for pid: {}\n".format( + exception)) + finally: + # Signal that the thread's run is complete. + self.event.set() + + +# Provides a regular expression for matching gtimeout-based durations. +TIMEOUT_REGEX = re.compile(r"(^\d+)([smhd])?$") + + +def timeout_to_seconds(timeout): + """Converts timeout/gtimeout timeout values into seconds. + + @param timeout a timeout in the form of xm representing x minutes. + + @return None if timeout is None, or the number of seconds as a float + if a valid timeout format was specified. + """ + if timeout is None: + return None + else: + match = TIMEOUT_REGEX.match(timeout) + if match: + value = float(match.group(1)) + units = match.group(2) + if units is None: + # default is seconds. No conversion necessary. + return value + elif units == 's': + # Seconds. No conversion necessary. + return value + elif units == 'm': + # Value is in minutes. + return 60.0 * value + elif units == 'h': + # Value is in hours. + return (60.0 * 60.0) * value + elif units == 'd': + # Value is in days. + return 24 * (60.0 * 60.0) * value + else: + raise Exception("unexpected units value '{}'".format(units)) + else: + raise Exception("could not parse TIMEOUT spec '{}'".format( + timeout)) + + +class ProcessHelper(object): + """Provides an interface for accessing process-related functionality. + + This class provides a factory method that gives the caller a + platform-specific implementation instance of the class. + + Clients of the class should stick to the methods provided in this + base class. + + @see ProcessHelper.process_helper() + """ + def __init__(self): + super(ProcessHelper, self).__init__() + + @classmethod + def process_helper(cls): + """Returns a platform-specific ProcessHelper instance. + @return a ProcessHelper instance that does the right thing for + the current platform. + """ + + # If you add a new platform, create an instance here and + # return it. + if os.name == "nt": + return WindowsProcessHelper() + else: + # For all POSIX-like systems. + return UnixProcessHelper() + + def create_piped_process(self, command, new_process_group=True): + # pylint: disable=no-self-use,unused-argument + # As expected. We want derived classes to implement this. + """Creates a subprocess.Popen-based class with I/O piped to the parent. + + @param command the command line list as would be passed to + subprocess.Popen(). Use the list form rather than the string form. + + @param new_process_group indicates if the caller wants the + process to be created in its own process group. Each OS handles + this concept differently. It provides a level of isolation and + can simplify or enable terminating the process tree properly. + + @return a subprocess.Popen-like object. + """ + raise Exception("derived class must implement") + + def supports_soft_terminate(self): + # pylint: disable=no-self-use + # As expected. We want derived classes to implement this. + """Indicates if the platform supports soft termination. + + Soft termination is the concept of a terminate mechanism that + allows the target process to shut down nicely, but with the + catch that the process might choose to ignore it. + + Platform supporter note: only mark soft terminate as supported + if the target process has some way to evade the soft terminate + request; otherwise, just support the hard terminate method. + + @return True if the platform supports a soft terminate mechanism. + """ + # By default, we do not support a soft terminate mechanism. + return False + + def soft_terminate(self, popen_process, log_file=None, want_core=True): + # pylint: disable=no-self-use,unused-argument + # As expected. We want derived classes to implement this. + """Attempts to terminate the process in a polite way. + + This terminate method is intended to give the child process a + chance to clean up and exit on its own, possibly with a request + to drop a core file or equivalent (i.e. [mini-]crashdump, crashlog, + etc.) If new_process_group was set in the process creation method + and the platform supports it, this terminate call will attempt to + kill the whole process tree rooted in this child process. + + @param popen_process the subprocess.Popen-like object returned + by one of the process-creation methods of this class. + + @param log_file file-like object used to emit error-related + logging info. May be None if no error-related info is desired. + + @param want_core True if the caller would like to get a core + dump (or the analogous crash report) from the terminated process. + """ + popen_process.terminate() + + def hard_terminate(self, popen_process, log_file=None): + # pylint: disable=no-self-use,unused-argument + # As expected. We want derived classes to implement this. + """Attempts to terminate the process immediately. + + This terminate method is intended to kill child process in + a manner in which the child process has no ability to block, + and also has no ability to clean up properly. If new_process_group + was specified when creating the process, and if the platform + implementation supports it, this will attempt to kill the + whole process tree rooted in the child process. + + @param popen_process the subprocess.Popen-like object returned + by one of the process-creation methods of this class. + + @param log_file file-like object used to emit error-related + logging info. May be None if no error-related info is desired. + """ + popen_process.kill() + + def was_soft_terminate(self, returncode, with_core): + # pylint: disable=no-self-use,unused-argument + # As expected. We want derived classes to implement this. + """Returns if Popen-like object returncode matches soft terminate. + + @param returncode the returncode from the Popen-like object that + terminated with a given return code. + + @param with_core indicates whether the returncode should match + a core-generating return signal. + + @return True when the returncode represents what the system would + issue when a soft_terminate() with the given with_core arg occurred; + False otherwise. + """ + if not self.supports_soft_terminate(): + # If we don't support soft termination on this platform, + # then this should always be False. + return False + else: + # Once a platform claims to support soft terminate, it + # needs to be able to identify it by overriding this method. + raise Exception("platform needs to implement") + + def was_hard_terminate(self, returncode): + # pylint: disable=no-self-use,unused-argument + # As expected. We want derived classes to implement this. + """Returns if Popen-like object returncode matches that of a hard + terminate attempt. + + @param returncode the returncode from the Popen-like object that + terminated with a given return code. + + @return True when the returncode represents what the system would + issue when a hard_terminate() occurred; False + otherwise. + """ + raise Exception("platform needs to implement") + + def soft_terminate_signals(self): + # pylint: disable=no-self-use + """Retrieve signal numbers that can be sent to soft terminate. + @return a list of signal numbers that can be sent to soft terminate + a process, or None if not applicable. + """ + return None + + def is_exceptional_exit(self, popen_status): + """Returns whether the program exit status is exceptional. + + Returns whether the return code from a Popen process is exceptional + (e.g. signals on POSIX systems). + + Derived classes should override this if they can detect exceptional + program exit. + + @return True if the given popen_status represents an exceptional + program exit; False otherwise. + """ + return False + + def exceptional_exit_details(self, popen_status): + """Returns the normalized exceptional exit code and a description. + + Given an exceptional exit code, returns the integral value of the + exception (e.g. signal number for POSIX) and a description (e.g. + signal name on POSIX) for the result. + + Derived classes should override this if they can detect exceptional + program exit. + + It is fine to not implement this so long as is_exceptional_exit() + always returns False. + + @return (normalized exception code, symbolic exception description) + """ + raise Exception("exception_exit_details() called on unsupported class") + + +class UnixProcessHelper(ProcessHelper): + """Provides a ProcessHelper for Unix-like operating systems. + + This implementation supports anything that looks Posix-y + (e.g. Darwin, Linux, *BSD, etc.) + """ + def __init__(self): + super(UnixProcessHelper, self).__init__() + + @classmethod + def _create_new_process_group(cls): + """Creates a new process group for the calling process.""" + os.setpgid(os.getpid(), os.getpid()) + + def create_piped_process(self, command, new_process_group=True): + # Determine what to run after the fork but before the exec. + if new_process_group: + preexec_func = self._create_new_process_group + else: + preexec_func = None + + # Create the process. + process = subprocess.Popen( + command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, # Elicits automatic byte -> string decoding in Py3 + close_fds=True, + preexec_fn=preexec_func) + + # Remember whether we're using process groups for this + # process. + process.using_process_groups = new_process_group + return process + + def supports_soft_terminate(self): + # POSIX does support a soft terminate via: + # * SIGTERM (no core requested) + # * SIGQUIT (core requested if enabled, see ulimit -c) + return True + + @classmethod + def _validate_pre_terminate(cls, popen_process, log_file): + # Validate args. + if popen_process is None: + raise ValueError("popen_process is None") + + # Ensure we have something that looks like a valid process. + if popen_process.pid < 1: + if log_file: + log_file.write("skipping soft_terminate(): no process id") + return False + + # We only do the process liveness check if we're not using + # process groups. With process groups, checking if the main + # inferior process is dead and short circuiting here is no + # good - children of it in the process group could still be + # alive, and they should be killed during a timeout. + if not popen_process.using_process_groups: + # Don't kill if it's already dead. + popen_process.poll() + if popen_process.returncode is not None: + # It has a returncode. It has already stopped. + if log_file: + log_file.write( + "requested to terminate pid {} but it has already " + "terminated, returncode {}".format( + popen_process.pid, popen_process.returncode)) + # Move along... + return False + + # Good to go. + return True + + def _kill_with_signal(self, popen_process, log_file, signum): + # Validate we're ready to terminate this. + if not self._validate_pre_terminate(popen_process, log_file): + return + + # Choose kill mechanism based on whether we're targeting + # a process group or just a process. + if popen_process.using_process_groups: + # if log_file: + # log_file.write( + # "sending signum {} to process group {} now\n".format( + # signum, popen_process.pid)) + os.killpg(popen_process.pid, signum) + else: + # if log_file: + # log_file.write( + # "sending signum {} to process {} now\n".format( + # signum, popen_process.pid)) + os.kill(popen_process.pid, signum) + + def soft_terminate(self, popen_process, log_file=None, want_core=True): + # Choose signal based on desire for core file. + if want_core: + # SIGQUIT will generate core by default. Can be caught. + signum = signal.SIGQUIT + else: + # SIGTERM is the traditional nice way to kill a process. + # Can be caught, doesn't generate a core. + signum = signal.SIGTERM + + self._kill_with_signal(popen_process, log_file, signum) + + def hard_terminate(self, popen_process, log_file=None): + self._kill_with_signal(popen_process, log_file, signal.SIGKILL) + + def was_soft_terminate(self, returncode, with_core): + if with_core: + return returncode == -signal.SIGQUIT + else: + return returncode == -signal.SIGTERM + + def was_hard_terminate(self, returncode): + return returncode == -signal.SIGKILL + + def soft_terminate_signals(self): + return [signal.SIGQUIT, signal.SIGTERM] + + def is_exceptional_exit(self, popen_status): + return popen_status < 0 + + @classmethod + def _signal_names_by_number(cls): + return dict( + (k, v) for v, k in reversed(sorted(signal.__dict__.items())) + if v.startswith('SIG') and not v.startswith('SIG_')) + + def exceptional_exit_details(self, popen_status): + signo = -popen_status + signal_names_by_number = self._signal_names_by_number() + signal_name = signal_names_by_number.get(signo, "") + return (signo, signal_name) + +class WindowsProcessHelper(ProcessHelper): + """Provides a Windows implementation of the ProcessHelper class.""" + def __init__(self): + super(WindowsProcessHelper, self).__init__() + + def create_piped_process(self, command, new_process_group=True): + if new_process_group: + # We need this flag if we want os.kill() to work on the subprocess. + creation_flags = subprocess.CREATE_NEW_PROCESS_GROUP + else: + creation_flags = 0 + + return subprocess.Popen( + command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, # Elicits automatic byte -> string decoding in Py3 + creationflags=creation_flags) + + def was_hard_terminate(self, returncode): + return returncode != 0 + + +class ProcessDriver(object): + """Drives a child process, notifies on important events, and can timeout. + + Clients are expected to derive from this class and override the + on_process_started and on_process_exited methods if they want to + hook either of those. + + This class supports timing out the child process in a platform-agnostic + way. The on_process_exited method is informed if the exit was natural + or if it was due to a timeout. + """ + def __init__(self, soft_terminate_timeout=10.0): + super(ProcessDriver, self).__init__() + self.process_helper = ProcessHelper.process_helper() + self.pid = None + # Create the synchronization event for notifying when the + # inferior dotest process is complete. + self.done_event = threading.Event() + self.io_thread = None + self.process = None + # Number of seconds to wait for the soft terminate to + # wrap up, before moving to more drastic measures. + # Might want this longer if core dumps are generated and + # take a long time to write out. + self.soft_terminate_timeout = soft_terminate_timeout + # Number of seconds to wait for the hard terminate to + # wrap up, before giving up on the io thread. This should + # be fast. + self.hard_terminate_timeout = 5.0 + self.returncode = None + + # ============================================= + # Methods for subclasses to override if desired. + # ============================================= + + def on_process_started(self): + pass + + def on_process_exited(self, command, output, was_timeout, exit_status): + pass + + def write(self, content): + # pylint: disable=no-self-use + # Intended - we want derived classes to be able to override + # this and use any self state they may contain. + sys.stdout.write(content) + + # ============================================================== + # Operations used to drive processes. Clients will want to call + # one of these. + # ============================================================== + + def run_command(self, command): + # Start up the child process and the thread that does the + # communication pump. + self._start_process_and_io_thread(command) + + # Wait indefinitely for the child process to finish + # communicating. This indicates it has closed stdout/stderr + # pipes and is done. + self.io_thread.join() + self.returncode = self.process.wait() + if self.returncode is None: + raise Exception( + "no exit status available for pid {} after the " + " inferior dotest.py should have completed".format( + self.process.pid)) + + # Notify of non-timeout exit. + self.on_process_exited( + command, + self.io_thread.output, + False, + self.returncode) + + def run_command_with_timeout(self, command, timeout, want_core): + # Figure out how many seconds our timeout description is requesting. + timeout_seconds = timeout_to_seconds(timeout) + + # Start up the child process and the thread that does the + # communication pump. + self._start_process_and_io_thread(command) + + self._wait_with_timeout(timeout_seconds, command, want_core) + + # ================ + # Internal details. + # ================ + + def _start_process_and_io_thread(self, command): + # Create the process. + self.process = self.process_helper.create_piped_process(command) + self.pid = self.process.pid + self.on_process_started() + + # Ensure the event is cleared that is used for signaling + # from the communication() thread when communication is + # complete (i.e. the inferior process has finished). + self.done_event.clear() + + self.io_thread = CommunicatorThread( + self.process, self.done_event, self.write) + self.io_thread.start() + + def _attempt_soft_kill(self, want_core): + # The inferior dotest timed out. Attempt to clean it + # with a non-drastic method (so it can clean up properly + # and/or generate a core dump). Often the OS can't guarantee + # that the process will really terminate after this. + self.process_helper.soft_terminate( + self.process, + want_core=want_core, + log_file=self) + + # Now wait up to a certain timeout period for the io thread + # to say that the communication ended. If that wraps up + # within our soft terminate timeout, we're all done here. + self.io_thread.join(self.soft_terminate_timeout) + if not self.io_thread.is_alive(): + # stdout/stderr were closed on the child process side. We + # should be able to wait and reap the child process here. + self.returncode = self.process.wait() + # We terminated, and the done_trying result is n/a + terminated = True + done_trying = None + else: + self.write("soft kill attempt of process {} timed out " + "after {} seconds\n".format( + self.process.pid, self.soft_terminate_timeout)) + terminated = False + done_trying = False + return terminated, done_trying + + def _attempt_hard_kill(self): + # Instruct the process to terminate and really force it to + # happen. Don't give the process a chance to ignore. + self.process_helper.hard_terminate( + self.process, + log_file=self) + + # Reap the child process. This should not hang as the + # hard_kill() mechanism is supposed to really kill it. + # Improvement option: + # If this does ever hang, convert to a self.process.poll() + # loop checking on self.process.returncode until it is not + # None or the timeout occurs. + self.returncode = self.process.wait() + + # Wait a few moments for the io thread to finish... + self.io_thread.join(self.hard_terminate_timeout) + if self.io_thread.is_alive(): + # ... but this is not critical if it doesn't end for some + # reason. + self.write( + "hard kill of process {} timed out after {} seconds waiting " + "for the io thread (ignoring)\n".format( + self.process.pid, self.hard_terminate_timeout)) + + # Set if it terminated. (Set up for optional improvement above). + terminated = self.returncode is not None + # Nothing else to try. + done_trying = True + + return terminated, done_trying + + def _attempt_termination(self, attempt_count, want_core): + if self.process_helper.supports_soft_terminate(): + # When soft termination is supported, we first try to stop + # the process with a soft terminate. Failing that, we try + # the hard terminate option. + if attempt_count == 1: + return self._attempt_soft_kill(want_core) + elif attempt_count == 2: + return self._attempt_hard_kill() + else: + # We don't have anything else to try. + terminated = self.returncode is not None + done_trying = True + return terminated, done_trying + else: + # We only try the hard terminate option when there + # is no soft terminate available. + if attempt_count == 1: + return self._attempt_hard_kill() + else: + # We don't have anything else to try. + terminated = self.returncode is not None + done_trying = True + return terminated, done_trying + + def _wait_with_timeout(self, timeout_seconds, command, want_core): + # Allow up to timeout seconds for the io thread to wrap up. + # If that completes, the child process should be done. + completed_normally = self.done_event.wait(timeout_seconds) + if completed_normally: + # Reap the child process here. + self.returncode = self.process.wait() + else: + # Prepare to stop the process + process_terminated = completed_normally + terminate_attempt_count = 0 + + # Try as many attempts as we support for trying to shut down + # the child process if it's not already shut down. + while not process_terminated: + terminate_attempt_count += 1 + # Attempt to terminate. + process_terminated, done_trying = self._attempt_termination( + terminate_attempt_count, want_core) + # Check if there's nothing more to try. + if done_trying: + # Break out of our termination attempt loop. + break + + # At this point, we're calling it good. The process + # finished gracefully, was shut down after one or more + # attempts, or we failed but gave it our best effort. + self.on_process_exited( + command, + self.io_thread.output, + not completed_normally, + self.returncode) + + +def patched_init(self, *args, **kwargs): + self.original_init(*args, **kwargs) + # Initialize our condition variable that protects wait()/poll(). + self.wait_condition = threading.Condition() + + +def patched_wait(self, *args, **kwargs): + self.wait_condition.acquire() + try: + result = self.original_wait(*args, **kwargs) + # The process finished. Signal the condition. + self.wait_condition.notify_all() + return result + finally: + self.wait_condition.release() + + +def patched_poll(self, *args, **kwargs): + self.wait_condition.acquire() + try: + result = self.original_poll(*args, **kwargs) + if self.returncode is not None: + # We did complete, and we have the return value. + # Signal the event to indicate we're done. + self.wait_condition.notify_all() + return result + finally: + self.wait_condition.release() + + +def patch_up_subprocess_popen(): + subprocess.Popen.original_init = subprocess.Popen.__init__ + subprocess.Popen.__init__ = patched_init + + subprocess.Popen.original_wait = subprocess.Popen.wait + subprocess.Popen.wait = patched_wait + + subprocess.Popen.original_poll = subprocess.Popen.poll + subprocess.Popen.poll = patched_poll + +# Replace key subprocess.Popen() threading-unprotected methods with +# threading-protected versions. +patch_up_subprocess_popen() diff --git a/packages/Python/lldbsuite/test/test_runner/test/inferior.py b/packages/Python/lldbsuite/test/test_runner/test/inferior.py new file mode 100755 index 000000000000..4207bac300fd --- /dev/null +++ b/packages/Python/lldbsuite/test/test_runner/test/inferior.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +"""Inferior program used by process control tests.""" + +from __future__ import print_function + +import argparse +import datetime +import signal +import subprocess +import sys +import time + + +def parse_args(command_line): + """Parses the command line arguments given to it. + + @param command_line a list of command line arguments to be parsed. + + @return the argparse options dictionary. + """ + parser = argparse.ArgumentParser() + parser.add_argument( + "--ignore-signal", + "-i", + dest="ignore_signals", + metavar="SIGNUM", + action="append", + type=int, + default=[], + help="ignore the given signal number (if possible)") + parser.add_argument( + "--launch-child-share-handles", + action="store_true", + help=("launch a child inferior.py that shares stdout/stderr/stdio and " + "never returns")) + parser.add_argument( + "--never-return", + action="store_true", + help="run in an infinite loop, never return") + parser.add_argument( + "--return-code", + "-r", + type=int, + default=0, + help="specify the return code for the inferior upon exit") + parser.add_argument( + "--sleep", + "-s", + metavar="SECONDS", + dest="sleep_seconds", + type=float, + help="sleep for SECONDS seconds before returning") + parser.add_argument( + "--verbose", "-v", action="store_true", + help="log verbose operation details to stdout") + return parser.parse_args(command_line) + + +def handle_ignore_signals(options, signals): + """Ignores any signals provided to it. + + @param options the command line options parsed by the program. + General used to check flags for things like verbosity. + + @param signals the list of signals to ignore. Can be None or zero-length. + Entries should be type int. + """ + if signals is None: + return + + for signum in signals: + if options.verbose: + print("disabling signum {}".format(signum)) + signal.signal(signum, signal.SIG_IGN) + + +def handle_sleep(options, sleep_seconds): + """Sleeps the number of seconds specified, restarting as needed. + + @param options the command line options parsed by the program. + General used to check flags for things like verbosity. + + @param sleep_seconds the number of seconds to sleep. If None + or <= 0, no sleeping will occur. + """ + if sleep_seconds is None: + return + + if sleep_seconds <= 0: + return + + end_time = datetime.datetime.now() + datetime.timedelta(0, sleep_seconds) + if options.verbose: + print("sleep end time: {}".format(end_time)) + + # Do sleep in a loop: signals can interrupt. + while datetime.datetime.now() < end_time: + # We'll wrap this in a try/catch so we don't encounter + # a race if a signal (ignored) knocks us out of this + # loop and causes us to return. + try: + sleep_interval = end_time - datetime.datetime.now() + sleep_seconds = sleep_interval.total_seconds() + if sleep_seconds > 0: + time.sleep(sleep_seconds) + except: # pylint: disable=bare-except + pass + + +def handle_launch_children(options): + if options.launch_child_share_handles: + # Launch the child, share our file handles. + # We won't bother reaping it since it will likely outlive us. + subprocess.Popen([sys.executable, __file__, "--never-return"]) + + +def handle_never_return(options): + if not options.never_return: + return + + # Loop forever. + while True: + try: + time.sleep(10) + except: # pylint: disable=bare-except + # Ignore + pass + + +def main(command_line): + """Drives the main operation of the inferior test program. + + @param command_line the command line options to process. + + @return the exit value (program return code) for the process. + """ + options = parse_args(command_line) + handle_ignore_signals(options, options.ignore_signals) + handle_launch_children(options) + handle_sleep(options, options.sleep_seconds) + handle_never_return(options) + + return options.return_code + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/packages/Python/lldbsuite/test/test_runner/test/process_control_tests.py b/packages/Python/lldbsuite/test/test_runner/test/process_control_tests.py new file mode 100755 index 000000000000..354506d65812 --- /dev/null +++ b/packages/Python/lldbsuite/test/test_runner/test/process_control_tests.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +""" +The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Provides classes used by the test results reporting infrastructure +within the LLDB test suite. + + +Tests the process_control module. +""" + +# System imports. +import os +import platform +import unittest +import sys +import threading + +# Add lib dir to pythonpath +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'lib')) + +# Our imports. +import process_control + + +class TestInferiorDriver(process_control.ProcessDriver): + def __init__(self, soft_terminate_timeout=None): + super(TestInferiorDriver, self).__init__( + soft_terminate_timeout=soft_terminate_timeout) + self.started_event = threading.Event() + self.started_event.clear() + + self.completed_event = threading.Event() + self.completed_event.clear() + + self.was_timeout = False + self.returncode = None + self.output = None + + def write(self, content): + # We'll swallow this to keep tests non-noisy. + # Uncomment the following line if you want to see it. + # sys.stdout.write(content) + pass + + def on_process_started(self): + self.started_event.set() + + def on_process_exited(self, command, output, was_timeout, exit_status): + self.returncode = exit_status + self.was_timeout = was_timeout + self.output = output + self.returncode = exit_status + self.completed_event.set() + + +class ProcessControlTests(unittest.TestCase): + @classmethod + def _suppress_soft_terminate(cls, command): + # Do the right thing for your platform here. + # Right now only POSIX-y systems are reporting + # soft terminate support, so this is set up for + # those. + helper = process_control.ProcessHelper.process_helper() + signals = helper.soft_terminate_signals() + if signals is not None: + for signum in helper.soft_terminate_signals(): + command.extend(["--ignore-signal", str(signum)]) + + @classmethod + def inferior_command( + cls, + ignore_soft_terminate=False, + options=None): + + # Base command. + command = ([sys.executable, "inferior.py"]) + + if ignore_soft_terminate: + cls._suppress_soft_terminate(command) + + # Handle options as string or list. + if isinstance(options, str): + command.extend(options.split()) + elif isinstance(options, list): + command.extend(options) + + # Return full command. + return command + + +class ProcessControlNoTimeoutTests(ProcessControlTests): + """Tests the process_control module.""" + def test_run_completes(self): + """Test that running completes and gets expected stdout/stderr.""" + driver = TestInferiorDriver() + driver.run_command(self.inferior_command()) + self.assertTrue( + driver.completed_event.wait(5), "process failed to complete") + self.assertEqual(driver.returncode, 0, "return code does not match") + + def test_run_completes_with_code(self): + """Test that running completes and gets expected stdout/stderr.""" + driver = TestInferiorDriver() + driver.run_command(self.inferior_command(options="-r10")) + self.assertTrue( + driver.completed_event.wait(5), "process failed to complete") + self.assertEqual(driver.returncode, 10, "return code does not match") + + +class ProcessControlTimeoutTests(ProcessControlTests): + def test_run_completes(self): + """Test that running completes and gets expected return code.""" + driver = TestInferiorDriver() + timeout_seconds = 5 + driver.run_command_with_timeout( + self.inferior_command(), + "{}s".format(timeout_seconds), + False) + self.assertTrue( + driver.completed_event.wait(2*timeout_seconds), + "process failed to complete") + self.assertEqual(driver.returncode, 0) + + def _soft_terminate_works(self, with_core): + # Skip this test if the platform doesn't support soft ti + helper = process_control.ProcessHelper.process_helper() + if not helper.supports_soft_terminate(): + self.skipTest("soft terminate not supported by platform") + + driver = TestInferiorDriver() + timeout_seconds = 5 + + driver.run_command_with_timeout( + # Sleep twice as long as the timeout interval. This + # should force a timeout. + self.inferior_command( + options="--sleep {}".format(timeout_seconds*2)), + "{}s".format(timeout_seconds), + with_core) + + # We should complete, albeit with a timeout. + self.assertTrue( + driver.completed_event.wait(2*timeout_seconds), + "process failed to complete") + + # Ensure we received a timeout. + self.assertTrue(driver.was_timeout, "expected to end with a timeout") + + self.assertTrue( + helper.was_soft_terminate(driver.returncode, with_core), + ("timeout didn't return expected returncode " + "for soft terminate with core: {}").format(driver.returncode)) + + def test_soft_terminate_works_core(self): + """Driver uses soft terminate (with core request) when process times out. + """ + self._soft_terminate_works(True) + + def test_soft_terminate_works_no_core(self): + """Driver uses soft terminate (no core request) when process times out. + """ + self._soft_terminate_works(False) + + def test_hard_terminate_works(self): + """Driver falls back to hard terminate when soft terminate is ignored. + """ + + driver = TestInferiorDriver(soft_terminate_timeout=2.0) + timeout_seconds = 1 + + driver.run_command_with_timeout( + # Sleep much longer than the timeout interval,forcing a + # timeout. Do whatever is needed to have the inferior + # ignore soft terminate calls. + self.inferior_command( + ignore_soft_terminate=True, + options="--never-return"), + "{}s".format(timeout_seconds), + True) + + # We should complete, albeit with a timeout. + self.assertTrue( + driver.completed_event.wait(60), + "process failed to complete") + + # Ensure we received a timeout. + self.assertTrue(driver.was_timeout, "expected to end with a timeout") + + helper = process_control.ProcessHelper.process_helper() + self.assertTrue( + helper.was_hard_terminate(driver.returncode), + ("timeout didn't return expected returncode " + "for hard teriminate: {} ({})").format( + driver.returncode, + driver.output)) + + def test_inferior_exits_with_live_child_shared_handles(self): + """inferior exit detected when inferior children are live with shared + stdout/stderr handles. + """ + # Requires review D13362 or equivalent to be implemented. + self.skipTest("http://reviews.llvm.org/D13362") + + driver = TestInferiorDriver() + + # Create the inferior (I1), and instruct it to create a child (C1) + # that shares the stdout/stderr handles with the inferior. + # C1 will then loop forever. + driver.run_command_with_timeout( + self.inferior_command( + options="--launch-child-share-handles --return-code 3"), + "5s", + False) + + # We should complete without a timetout. I1 should end + # immediately after launching C1. + self.assertTrue( + driver.completed_event.wait(5), + "process failed to complete") + + # Ensure we didn't receive a timeout. + self.assertFalse( + driver.was_timeout, "inferior should have completed normally") + + self.assertEqual( + driver.returncode, 3, + "expected inferior process to end with expected returncode") + + +if __name__ == "__main__": + unittest.main() diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiExit.py b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiExit.py new file mode 100644 index 000000000000..86a0a65b05a7 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiExit.py @@ -0,0 +1,84 @@ +""" +Test that the lldb-mi driver exits properly. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiExitTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_gdb_exit(self): + """Test that '-gdb-exit' terminates local debug session and exits.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -gdb-exit: try to exit and check that program is finished + self.runCmd("-gdb-exit") + self.expect("\^exit") + import pexpect + self.expect(pexpect.EOF) + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_quit(self): + """Test that 'quit' exits immediately.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test quit: try to exit and check that program is finished + self.runCmd("quit") + import pexpect + self.expect(pexpect.EOF) + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_q(self): + """Test that 'q' exits immediately.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test q: try to exit and check that program is finished + self.runCmd("q") + import pexpect + self.expect(pexpect.EOF) diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiFile.py b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiFile.py new file mode 100644 index 000000000000..8b4eac156362 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiFile.py @@ -0,0 +1,77 @@ +""" +Test lldb-mi -file-xxx commands. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiFileTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_file_exec_and_symbols_file(self): + """Test that 'lldb-mi --interpreter' works for -file-exec-and-symbols exe.""" + + self.spawnLldbMi(args = None) + + # Test that -file-exec-and-symbols works for filename + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_file_exec_and_symbols_absolute_path(self): + """Test that 'lldb-mi --interpreter' works for -file-exec-and-symbols fullpath/exe.""" + + self.spawnLldbMi(args = None) + + # Test that -file-exec-and-symbols works for absolute path + import os + path = os.path.join(os.getcwd(), self.myexe) + self.runCmd("-file-exec-and-symbols \"%s\"" % path) + self.expect("\^done") + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_file_exec_and_symbols_relative_path(self): + """Test that 'lldb-mi --interpreter' works for -file-exec-and-symbols relpath/exe.""" + + self.spawnLldbMi(args = None) + + # Test that -file-exec-and-symbols works for relative path + path = "./%s" % self.myexe + self.runCmd("-file-exec-and-symbols %s" % path) + self.expect("\^done") + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_file_exec_and_symbols_unknown_path(self): + """Test that 'lldb-mi --interpreter' works for -file-exec-and-symbols badpath/exe.""" + + self.spawnLldbMi(args = None) + + # Test that -file-exec-and-symbols fails on unknown path + path = "unknown_dir/%s" % self.myexe + self.runCmd("-file-exec-and-symbols %s" % path) + self.expect("\^error") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiGdbSetShow.py b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiGdbSetShow.py new file mode 100644 index 000000000000..ab3eb1fb37de --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiGdbSetShow.py @@ -0,0 +1,190 @@ +""" +Test lldb-mi -gdb-set and -gdb-show commands. +""" + +from __future__ import print_function + + + +import unittest2 +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiGdbSetShowTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_gdb_set_target_async_default(self): + """Test that 'lldb-mi --interpreter' switches to async mode by default.""" + + self.spawnLldbMi(args = None) + + # Switch to sync mode + self.runCmd("-gdb-set target-async off") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"off\"") + + # Test that -gdb-set switches to async by default + self.runCmd("-gdb-set target-async") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"on\"") + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFlakeyLinux("llvm.org/pr26028") # Fails in ~1% of cases + def test_lldbmi_gdb_set_target_async_on(self): + """Test that 'lldb-mi --interpreter' can execute commands in async mode.""" + + self.spawnLldbMi(args = None) + + # Switch to sync mode + self.runCmd("-gdb-set target-async off") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"off\"") + + # Test that -gdb-set can switch to async mode + self.runCmd("-gdb-set target-async on") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"on\"") + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that program is executed in async mode + self.runCmd("-exec-run") + self.expect("\*running") + self.expect("@\"argc=1") + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFailureLinux # Failing in ~11/600 dosep runs (build 3120-3122) + def test_lldbmi_gdb_set_target_async_off(self): + """Test that 'lldb-mi --interpreter' can execute commands in sync mode.""" + + self.spawnLldbMi(args = None) + + # Test that -gdb-set can switch to sync mode + self.runCmd("-gdb-set target-async off") + self.expect("\^done") + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"off\"") + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that program is executed in async mode + self.runCmd("-exec-run") + unexpected = [ "\*running" ] # "\*running" is async notification + it = self.expect(unexpected + [ "@\"argc=1\\\\r\\\\n" ]) + if it < len(unexpected): + self.fail("unexpected found: %s" % unexpected[it]) + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_gdb_show_target_async(self): + """Test that 'lldb-mi --interpreter' in async mode by default.""" + + self.spawnLldbMi(args = None) + + # Test that default target-async value is "on" + self.runCmd("-gdb-show target-async") + self.expect("\^done,value=\"on\"") + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_gdb_show_language(self): + """Test that 'lldb-mi --interpreter' can get current language.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -gdb-show language gets current language + self.runCmd("-gdb-show language") + self.expect("\^done,value=\"c\+\+\"") + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @unittest2.expectedFailure("-gdb-set ignores unknown properties") + def test_lldbmi_gdb_set_unknown(self): + """Test that 'lldb-mi --interpreter' fails when setting an unknown property.""" + + self.spawnLldbMi(args = None) + + # Test that -gdb-set fails if property is unknown + self.runCmd("-gdb-set unknown some_value") + self.expect("\^error") + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @unittest2.expectedFailure("-gdb-show ignores unknown properties") + def test_lldbmi_gdb_show_unknown(self): + """Test that 'lldb-mi --interpreter' fails when showing an unknown property.""" + + self.spawnLldbMi(args = None) + + # Test that -gdb-show fails if property is unknown + self.runCmd("-gdb-show unknown") + self.expect("\^error") + + + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_gdb_set_ouptut_radix(self): + """Test that 'lldb-mi --interpreter' works for -gdb-set output-radix.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to BP_printf + line = line_number('main.cpp', '// BP_printf') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running"); + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Setup variable + self.runCmd("-var-create var_a * a"); + self.expect("\^done,name=\"var_a\",numchild=\"0\",value=\"10\",type=\"int\",thread-id=\"1\",has_more=\"0\"") + + # Test default output + self.runCmd("-var-evaluate-expression var_a"); + self.expect("\^done,value=\"10\""); + + # Test hex output + self.runCmd("-gdb-set output-radix 16"); + self.expect("\^done"); + self.runCmd("-var-evaluate-expression var_a"); + self.expect("\^done,value=\"0xa\""); + + # Test octal output + self.runCmd("-gdb-set output-radix 8"); + self.expect("\^done"); + self.runCmd("-var-evaluate-expression var_a"); + self.expect("\^done,value=\"012\""); + + # Test decimal output + self.runCmd("-gdb-set output-radix 10"); + self.expect("\^done"); + self.runCmd("-var-evaluate-expression var_a"); + self.expect("\^done,value=\"10\""); diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiLibraryLoaded.py b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiLibraryLoaded.py new file mode 100644 index 000000000000..4d9c935576df --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiLibraryLoaded.py @@ -0,0 +1,33 @@ +""" +Test lldb-mi =library-loaded notifications. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiLibraryLoadedTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_library_loaded(self): + """Test that 'lldb-mi --interpreter' shows the =library-loaded notifications.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test =library-loaded + import os + path = os.path.join(os.getcwd(), self.myexe) + symbols_path = os.path.join(path + ".dSYM", "Contents", "Resources", "DWARF", self.myexe) + def add_slashes(x): return x.replace("\\", "\\\\").replace("\"", "\\\"").replace("\'", "\\\'").replace("\0", "\\\0") + self.expect([ "=library-loaded,id=\"%s\",target-name=\"%s\",host-name=\"%s\",symbols-loaded=\"1\",symbols-path=\"%s\",loaded_addr=\"-\",size=\"[0-9]+\"" % (add_slashes(path), add_slashes(path), add_slashes(path), add_slashes(symbols_path)), + "=library-loaded,id=\"%s\",target-name=\"%s\",host-name=\"%s\",symbols-loaded=\"0\",loaded_addr=\"-\",size=\"[0-9]+\"" % (add_slashes(path), add_slashes(path), add_slashes(path)) ]) diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiPrompt.py b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiPrompt.py new file mode 100644 index 000000000000..d810267d9489 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiPrompt.py @@ -0,0 +1,54 @@ +""" +Test that the lldb-mi driver prints prompt properly. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiPromptTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_prompt(self): + """Test that 'lldb-mi --interpreter' echos '(gdb)' after commands and events.""" + + self.spawnLldbMi(args = None) + + # Test that lldb-mi is ready after unknown command + self.runCmd("-unknown-command") + self.expect("\^error,msg=\"Driver\. Received command '-unknown-command'\. It was not handled\. Command 'unknown-command' not in Command Factory\"") + self.expect(self.child_prompt, exactly = True) + + # Test that lldb-mi is ready after -file-exec-and-symbols + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + self.expect(self.child_prompt, exactly = True) + + # Test that lldb-mi is ready after -break-insert + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.expect(self.child_prompt, exactly = True) + + # Test that lldb-mi is ready after -exec-run + self.runCmd("-exec-run") + self.expect("\*running") + self.expect(self.child_prompt, exactly = True) + + # Test that lldb-mi is ready after BP hit + self.expect("\*stopped,reason=\"breakpoint-hit\"") + self.expect(self.child_prompt, exactly = True) + + # Test that lldb-mi is ready after -exec-continue + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect(self.child_prompt, exactly = True) + + # Test that lldb-mi is ready after program exited + self.expect("\*stopped,reason=\"exited-normally\"") + self.expect(self.child_prompt, exactly = True) diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/TestMiBreak.py b/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/TestMiBreak.py new file mode 100644 index 000000000000..020954ff9b41 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/TestMiBreak.py @@ -0,0 +1,246 @@ +""" +Test lldb-mi -break-xxx commands. +""" + +from __future__ import print_function + + + +import unittest2 +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiBreakTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_break_insert_function_pending(self): + """Test that 'lldb-mi --interpreter' works for pending function breakpoints.""" + + self.spawnLldbMi(args = None) + + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + self.runCmd("-break-insert -f printf") + #FIXME function name is unknown on Darwin, fullname should be ??, line is -1 + #self.expect("\^done,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"0xffffffffffffffff\",func=\"printf\",file=\"\?\?\",fullname=\"\?\?\",line=\"-1\",pending=\[\"printf\"\],times=\"0\",original-location=\"printf\"}") + self.expect("\^done,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"0xffffffffffffffff\",func=\"\?\?\",file=\"\?\?\",fullname=\"\?\?/\?\?\",line=\"0\",pending=\[\"printf\"\],times=\"0\",original-location=\"printf\"}") + #FIXME function name is unknown on Darwin, fullname should be ??, line -1 + #self.expect("=breakpoint-modified,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"0xffffffffffffffff\",func=\"printf\",file=\"\?\?\",fullname=\"\?\?\",line=\"-1\",pending=\[\"printf\"\],times=\"0\",original-location=\"printf\"}") + self.expect("=breakpoint-modified,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"0xffffffffffffffff\",func=\"\?\?\",file=\"\?\?\",fullname=\"\?\?/\?\?\",line=\"0\",pending=\[\"printf\"\],times=\"0\",original-location=\"printf\"}") + + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("=breakpoint-modified,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\".+?\",file=\".+?\",fullname=\".+?\",line=\"(-1|\d+)\",pending=\[\"printf\"\],times=\"0\",original-location=\"printf\"}") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_break_insert_function(self): + """Test that 'lldb-mi --interpreter' works for function breakpoints.""" + + self.spawnLldbMi(args = None) + + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"\d+\",pending=\[\"main\"\],times=\"0\",original-location=\"main\"}") + self.expect("=breakpoint-modified,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"\d+\",pending=\[\"main\"\],times=\"0\",original-location=\"main\"}") + + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("=breakpoint-modified,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"\d+\",pending=\[\"main\"\],times=\"0\",original-location=\"main\"}") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -break-insert can set non-pending BP + self.runCmd("-break-insert printf") + #FIXME function name is unknown on Darwin + #self.expect("\^done,bkpt={number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"printf\",file=\".+?\",fullname=\".+?\",line=\"(-1|\d+)\",times=\"0\",original-location=\"printf\"}") + self.expect("\^done,bkpt={number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\".+?\",file=\".+?\",fullname=\".+?\",line=\"(-1|\d+)\",times=\"0\",original-location=\"printf\"}") + #FIXME function name is unknown on Darwin + #self.expect("=breakpoint-modified,bkpt={number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"printf\",file=\".+?\",fullname=\".+?\",line=\"(-1|\d+)\",times=\"0\",original-location=\"printf\"}") + self.expect("=breakpoint-modified,bkpt={number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\".+?\",file=\".+?\",fullname=\".+?\",line=\"(-1|\d+)\",times=\"0\",original-location=\"printf\"}") + # FIXME function name is unknown on Darwin + #self.expect("=breakpoint-modified,bkpt={number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"printf\",file=\".+?\",fullname=\".+?\",line=\"(-1|\d+)\",times=\"0\",original-location=\"printf\"}") + self.expect("=breakpoint-modified,bkpt={number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\".+?\",file=\".+?\",fullname=\".+?\",line=\"(-1|\d+)\",times=\"0\",original-location=\"printf\"}") + + # Test that -break-insert fails if non-pending BP can't be resolved + self.runCmd("-break-insert unknown_func") + self.expect("\^error,msg=\"Command 'break-insert'. Breakpoint location 'unknown_func' not found\"") + + # Test that non-pending BP was set correctly + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\".*bkptno=\"2\"") + + # Test that we can set a BP using the file:func syntax + self.runCmd("-break-insert main.cpp:main") + self.expect("\^done,bkpt={number=\"4\"") + self.runCmd("-break-insert main.cpp:ns::foo1") + self.expect("\^done,bkpt={number=\"5\"") + #FIXME: quotes on filenames aren't handled correctly in lldb-mi. + #self.runCmd("-break-insert \"main.cpp\":main") + #self.expect("\^done,bkpt={number=\"6\"") + + # We should hit BP #5 on 'main.cpp:ns::foo1' + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\".*bkptno=\"5\"") + + #FIXME: this test is disabled due to lldb bug llvm.org/pr24271. + # Test that we can set a BP using the global namespace token + #self.runCmd("-break-insert ::main") + #self.expect("\^done,bkpt={number=\"7\"") + #self.runCmd("-break-insert main.cpp:::main") + #self.expect("\^done,bkpt={number=\"8\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_break_insert_file_line_pending(self): + """Test that 'lldb-mi --interpreter' works for pending file:line breakpoints.""" + + self.spawnLldbMi(args = None) + + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Find the line number to break inside main() and set + # pending BP + line = line_number('main.cpp', '// BP_return') + self.runCmd("-break-insert -f main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"%d\",pending=\[\"main.cpp:%d\"\],times=\"0\",original-location=\"main.cpp:%d\"}" % (line, line, line)) + self.expect("=breakpoint-modified,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"%d\",pending=\[\"main.cpp:%d\"\],times=\"0\",original-location=\"main.cpp:%d\"}" % (line, line, line)) + + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_break_insert_file_line(self): + """Test that 'lldb-mi --interpreter' works for file:line breakpoints.""" + + self.spawnLldbMi(args = None) + + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -break-insert can set non-pending BP + line = line_number('main.cpp', '// BP_return') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"%d\",times=\"0\",original-location=\"main.cpp:%d\"}" % (line, line)) + self.expect("=breakpoint-modified,bkpt={number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"(?!0xffffffffffffffff)0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"%d\",times=\"0\",original-location=\"main.cpp:%d\"}" % (line, line)) + + # Test that -break-insert fails if non-pending BP can't be resolved + self.runCmd("-break-insert unknown_file:1") + self.expect("\^error,msg=\"Command 'break-insert'. Breakpoint location 'unknown_file:1' not found\"") + + # Test that non-pending BP was set correctly + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @unittest2.expectedFailure("-break-insert doesn't work for absolute path") + def test_lldbmi_break_insert_file_line_absolute_path(self): + """Test that 'lldb-mi --interpreter' works for file:line breakpoints.""" + + self.spawnLldbMi(args = None) + + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + import os + path = os.path.join(os.getcwd(), "main.cpp") + line = line_number('main.cpp', '// BP_return') + self.runCmd("-break-insert %s:%d" % (path, line)) + self.expect("\^done,bkpt={number=\"2\"") + + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_break_insert_settings(self): + """Test that 'lldb-mi --interpreter' can set breakpoints accoridng to global options.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Set target.move-to-nearest-code=off and try to set BP #1 that shouldn't be hit + self.runCmd("-interpreter-exec console \"settings set target.move-to-nearest-code off\"") + self.expect("\^done") + line = line_number('main.cpp', '// BP_before_main') + self.runCmd("-break-insert -f main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + + # Test that non-pending BP will not be set on non-existing line if target.move-to-nearest-code=off + # Note: this increases the BP number by 1 even though BP #2 is invalid. + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^error,msg=\"Command 'break-insert'. Breakpoint location 'main.cpp:%d' not found\"" % line) + + # Set target.move-to-nearest-code=on and target.skip-prologue=on and set BP #3 + self.runCmd("-interpreter-exec console \"settings set target.move-to-nearest-code on\"") + self.runCmd("-interpreter-exec console \"settings set target.skip-prologue on\"") + self.expect("\^done") + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"3\"") + + # Set target.skip-prologue=off and set BP #4 + self.runCmd("-interpreter-exec console \"settings set target.skip-prologue off\"") + self.expect("\^done") + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"4\"") + + # Test that BP #4 is located before BP #3 + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"4\"") + + # Test that BP #3 is hit + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"3\"") + + # Test that the target.language=pascal setting works and that BP #5 is NOT set + self.runCmd("-interpreter-exec console \"settings set target.language c\"") + self.expect("\^done") + self.runCmd("-break-insert ns.foo1") + self.expect("\^error") + + # Test that the target.language=c++ setting works and that BP #6 is hit + self.runCmd("-interpreter-exec console \"settings set target.language c++\"") + self.expect("\^done") + self.runCmd("-break-insert ns::foo1") + self.expect("\^done,bkpt={number=\"6\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"6\"") + + # Test that BP #1 and #2 weren't set by running to program exit + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/main.cpp new file mode 100644 index 000000000000..9416a0d01c7d --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/breakpoint/main.cpp @@ -0,0 +1,27 @@ +//===-- 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 + +namespace ns +{ + int foo1(void) { printf("In foo1\n"); return 1; } + int foo2(void) { printf("In foo2\n"); return 2; } +} + +// BP_before_main + +int x; +int +main(int argc, char const *argv[]) +{ + printf("Print a formatted string so that GCC does not optimize this printf call: %s\n", argv[0]); + x = ns::foo1() + ns::foo2(); + return 0; // BP_return +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/control/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/control/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/control/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/control/TestMiExec.py b/packages/Python/lldbsuite/test/tools/lldb-mi/control/TestMiExec.py new file mode 100644 index 000000000000..742bbc8af6bd --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/control/TestMiExec.py @@ -0,0 +1,457 @@ +""" +Test lldb-mi -exec-xxx commands. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiExecTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFailureLinux # llvm.org/pr25000: lldb-mi does not receive broadcasted notification from Core/Process about process stopped + def test_lldbmi_exec_run(self): + """Test that 'lldb-mi --interpreter' can stop at entry.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that program is stopped at entry + self.runCmd("-exec-run --start") + self.expect("\^running") + self.expect("\*stopped,reason=\"signal-received\",signal-name=\"SIGSTOP\",signal-meaning=\"Stop\",.*?thread-id=\"1\",stopped-threads=\"all\"") + # Test that lldb-mi is ready to execute next commands + self.expect(self.child_prompt, exactly = True) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_exec_abort(self): + """Test that 'lldb-mi --interpreter' works for -exec-abort.""" + + self.spawnLldbMi(args = None) + + # Test that -exec-abort fails on invalid process + self.runCmd("-exec-abort") + self.expect("\^error,msg=\"Command 'exec-abort'\. Invalid process during debug session\"") + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Set arguments + self.runCmd("-exec-arguments arg1") + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that arguments were passed + self.runCmd("-data-evaluate-expression argc") + self.expect("\^done,value=\"2\"") + + # Test that program may be aborted + self.runCmd("-exec-abort") + self.expect("\^done") + self.expect("\*stopped,reason=\"exited-normally\"") + + # Test that program can be run again + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that arguments were passed again + self.runCmd("-data-evaluate-expression argc") + self.expect("\^done,value=\"2\"") + + # Test that program may be aborted again + self.runCmd("-exec-abort") + self.expect("\^done") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_exec_arguments_set(self): + """Test that 'lldb-mi --interpreter' can pass args using -exec-arguments.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Set arguments + self.runCmd("-exec-arguments --arg1 \"2nd arg\" third_arg fourth=\"4th arg\"") + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Check argc and argv to see if arg passed + self.runCmd("-data-evaluate-expression argc") + self.expect("\^done,value=\"5\"") + #self.runCmd("-data-evaluate-expression argv[1]") + #self.expect("\^done,value=\"--arg1\"") + self.runCmd("-interpreter-exec command \"print argv[1]\"") + self.expect("\"--arg1\"") + #self.runCmd("-data-evaluate-expression argv[2]") + #self.expect("\^done,value=\"2nd arg\"") + self.runCmd("-interpreter-exec command \"print argv[2]\"") + self.expect("\"2nd arg\"") + #self.runCmd("-data-evaluate-expression argv[3]") + #self.expect("\^done,value=\"third_arg\"") + self.runCmd("-interpreter-exec command \"print argv[3]\"") + self.expect("\"third_arg\"") + #self.runCmd("-data-evaluate-expression argv[4]") + #self.expect("\^done,value=\"fourth=\\\\\\\"4th arg\\\\\\\"\"") + self.runCmd("-interpreter-exec command \"print argv[4]\"") + self.expect("\"fourth=\\\\\\\"4th arg\\\\\\\"\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_exec_arguments_reset(self): + """Test that 'lldb-mi --interpreter' can reset previously set args using -exec-arguments.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Set arguments + self.runCmd("-exec-arguments arg1") + self.expect("\^done") + self.runCmd("-exec-arguments") + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Check argc to see if arg passed + self.runCmd("-data-evaluate-expression argc") + self.expect("\^done,value=\"1\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_exec_next(self): + """Test that 'lldb-mi --interpreter' works for stepping.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Warning: the following is sensitive to the lines in the source + + # Test -exec-next + self.runCmd("-exec-next --thread 1 --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"29\"") + + # Test that --thread is optional + self.runCmd("-exec-next --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"30\"") + + # Test that --frame is optional + self.runCmd("-exec-next --thread 1") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"31\"") + + # Test that both --thread and --frame are optional + self.runCmd("-exec-next") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"32\"") + + # Test that an invalid --thread is handled + self.runCmd("-exec-next --thread 0") + self.expect("\^error,message=\"error: Thread index 0 is out of range") + self.runCmd("-exec-next --thread 10") + self.expect("\^error,message=\"error: Thread index 10 is out of range") + + # Test that an invalid --frame is handled + # FIXME: no error is returned + self.runCmd("-exec-next --frame 10") + #self.expect("\^error: Frame index 10 is out of range") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFailurei386 #xfail to get buildbot green, failing config: i386 binary running on ubuntu 14.04 x86_64 + def test_lldbmi_exec_next_instruction(self): + """Test that 'lldb-mi --interpreter' works for instruction stepping.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Warning: the following is sensitive to the lines in the + # source and optimizations + + # Test -exec-next-instruction + self.runCmd("-exec-next-instruction --thread 1 --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"28\"") + + # Test that --thread is optional + self.runCmd("-exec-next-instruction --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"28\"") + + # Test that --frame is optional + self.runCmd("-exec-next-instruction --thread 1") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"29\"") + + # Test that both --thread and --frame are optional + self.runCmd("-exec-next-instruction") + self.expect("\^running") + # Depending on compiler, it can stop at different line + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"(29|30)\"") + + # Test that an invalid --thread is handled + self.runCmd("-exec-next-instruction --thread 0") + self.expect("\^error,message=\"error: Thread index 0 is out of range") + self.runCmd("-exec-next-instruction --thread 10") + self.expect("\^error,message=\"error: Thread index 10 is out of range") + + # Test that an invalid --frame is handled + # FIXME: no error is returned + self.runCmd("-exec-next-instruction --frame 10") + #self.expect("\^error: Frame index 10 is out of range") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_exec_step(self): + """Test that 'lldb-mi --interpreter' works for stepping into.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Warning: the following is sensitive to the lines in the source + + # Test that -exec-step steps into (or not) printf depending on debug info + # Note that message is different in Darwin and Linux: + # Darwin: "*stopped,reason=\"end-stepping-range\",frame={addr=\"0x[0-9a-f]+\",func=\"main\",args=[{name=\"argc\",value=\"1\"},{name=\"argv\",value="0x[0-9a-f]+\"}],file=\"main.cpp\",fullname=\".+main.cpp\",line=\"\d\"},thread-id=\"1\",stopped-threads=\"all\" + # Linux: "*stopped,reason=\"end-stepping-range\",frame={addr="0x[0-9a-f]+\",func=\"__printf\",args=[{name=\"format\",value=\"0x[0-9a-f]+\"}],file=\"printf.c\",fullname=\".+printf.c\",line="\d+"},thread-id=\"1\",stopped-threads=\"all\" + self.runCmd("-exec-step --thread 1 --frame 0") + self.expect("\^running") + it = self.expect([ "\*stopped,reason=\"end-stepping-range\".+?func=\"main\"", + "\*stopped,reason=\"end-stepping-range\".+?func=\"(?!main).+?\"" ]) + # Exit from printf if needed + if it == 1: + self.runCmd("-exec-finish") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") + + # Test that -exec-step steps into g_MyFunction and back out + # (and that --thread is optional) + self.runCmd("-exec-step --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") + # Use -exec-finish here to make sure that control reaches the caller. + # -exec-step can keep us in the g_MyFunction for gcc + self.runCmd("-exec-finish --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"30\"") + + # Test that -exec-step steps into s_MyFunction + # (and that --frame is optional) + self.runCmd("-exec-step --thread 1") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\".*?s_MyFunction.*?\"") + + # Test that -exec-step steps into g_MyFunction from inside + # s_MyFunction (and that both --thread and --frame are optional) + self.runCmd("-exec-step") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") + + # Test that an invalid --thread is handled + self.runCmd("-exec-step --thread 0") + self.expect("\^error,message=\"error: Thread index 0 is out of range") + self.runCmd("-exec-step --thread 10") + self.expect("\^error,message=\"error: Thread index 10 is out of range") + + # Test that an invalid --frame is handled + # FIXME: no error is returned + self.runCmd("-exec-step --frame 10") + #self.expect("\^error: Frame index 10 is out of range") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_exec_step_instruction(self): + """Test that 'lldb-mi --interpreter' works for instruction stepping into.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Warning: the following is sensitive to the lines in the + # source and optimizations + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -exec-next steps over printf + self.runCmd("-exec-next --thread 1 --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"29\"") + + # Test that -exec-step-instruction steps over non branching + # instruction + self.runCmd("-exec-step-instruction --thread 1 --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\"") + + # Test that -exec-step-instruction steps into g_MyFunction + # instruction (and that --thread is optional) + self.runCmd("-exec-step-instruction --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") + + # Test that -exec-step-instruction steps over non branching + # (and that --frame is optional) + self.runCmd("-exec-step-instruction --thread 1") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") + + # Test that -exec-step-instruction steps into g_MyFunction + # (and that both --thread and --frame are optional) + self.runCmd("-exec-step-instruction") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") + + # Test that an invalid --thread is handled + self.runCmd("-exec-step-instruction --thread 0") + self.expect("\^error,message=\"error: Thread index 0 is out of range") + self.runCmd("-exec-step-instruction --thread 10") + self.expect("\^error,message=\"error: Thread index 10 is out of range") + + # Test that an invalid --frame is handled + # FIXME: no error is returned + self.runCmd("-exec-step-instruction --frame 10") + #self.expect("\^error: Frame index 10 is out of range") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_exec_finish(self): + """Test that 'lldb-mi --interpreter' works for -exec-finish.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Set BP at g_MyFunction and run to BP + self.runCmd("-break-insert -f g_MyFunction") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -exec-finish returns from g_MyFunction + self.runCmd("-exec-finish --thread 1 --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") + + # Run to BP inside s_MyFunction call + self.runCmd("-break-insert s_MyFunction") + self.expect("\^done,bkpt={number=\"2\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -exec-finish hits BP at g_MyFunction call inside + # s_MyFunction (and that --thread is optional) + self.runCmd("-exec-finish --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -exec-finish returns from g_MyFunction call inside + # s_MyFunction (and that --frame is optional) + self.runCmd("-exec-finish --thread 1") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\".*?s_MyFunction.*?\"") + + # Test that -exec-finish returns from s_MyFunction + # (and that both --thread and --frame are optional) + self.runCmd("-exec-finish") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") + + # Test that an invalid --thread is handled + self.runCmd("-exec-finish --thread 0") + self.expect("\^error,message=\"error: Thread index 0 is out of range") + self.runCmd("-exec-finish --thread 10") + self.expect("\^error,message=\"error: Thread index 10 is out of range") + + # Test that an invalid --frame is handled + # FIXME: no error is returned + #self.runCmd("-exec-finish --frame 10") + #self.expect("\^error: Frame index 10 is out of range") + + # Set BP at printf and run to BP + self.runCmd("-break-insert -f printf") + self.expect("\^done,bkpt={number=\"3\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + ## Test that -exec-finish returns from printf + self.runCmd("-exec-finish --thread 1 --frame 0") + self.expect("\^running") + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/control/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/control/main.cpp new file mode 100644 index 000000000000..ae0c3d9800a7 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/control/main.cpp @@ -0,0 +1,33 @@ +//===-- 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 + +void +g_MyFunction(void) +{ + printf("g_MyFunction"); +} + +static void +s_MyFunction(void) +{ + g_MyFunction(); + printf("s_MyFunction"); +} + +int +main(int argc, char const *argv[]) +{ + printf("start"); + g_MyFunction(); + s_MyFunction(); + printf("exit"); + return 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/data/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/data/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/data/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/data/TestMiData.py b/packages/Python/lldbsuite/test/tools/lldb-mi/data/TestMiData.py new file mode 100644 index 000000000000..df9f54110f4b --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/data/TestMiData.py @@ -0,0 +1,337 @@ +""" +Test lldb-mi -data-xxx commands. +""" + +from __future__ import print_function + + + +import unittest2 +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiDataTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_data_disassemble(self): + """Test that 'lldb-mi --interpreter' works for -data-disassemble.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Get an address for disassembling: use main + self.runCmd("-data-evaluate-expression main") + self.expect("\^done,value=\"0x[0-9a-f]+ \(a.out`main at main.cpp:[0-9]+\)\"") + addr = int(self.child.after.split("\"")[1].split(" ")[0], 16) + + # Test -data-disassemble: try to disassemble some address + self.runCmd("-data-disassemble -s %#x -e %#x -- 0" % (addr, addr + 0x10)) + self.expect("\^done,asm_insns=\[{address=\"0x0*%x\",func-name=\"main\",offset=\"0\",size=\"[1-9]+\",inst=\".+?\"}," % addr) + + # Test -data-disassemble without "--" + self.runCmd("-data-disassemble -s %#x -e %#x 0" % (addr, addr + 0x10)) + self.expect("\^done,asm_insns=\[{address=\"0x0*%x\",func-name=\"main\",offset=\"0\",size=\"[1-9]+\",inst=\".+?\"}," % addr) + + # Run to hello_world + self.runCmd("-break-insert -f hello_world") + self.expect("\^done,bkpt={number=\"2\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Get an address for disassembling: use hello_world + self.runCmd("-data-evaluate-expression hello_world") + self.expect("\^done,value=\"0x[0-9a-f]+ \(a.out`hello_world\(\) at main.cpp:[0-9]+\)\"") + addr = int(self.child.after.split("\"")[1].split(" ")[0], 16) + + # Test -data-disassemble: try to disassemble some address + self.runCmd("-data-disassemble -s %#x -e %#x -- 0" % (addr, addr + 0x10)) + + # This matches a line similar to: + # Darwin: {address="0x0000000100000f18",func-name="hello_world()",offset="8",size="7",inst="leaq 0x65(%rip), %rdi; \"Hello, World!\\n\""}, + # Linux: {address="0x0000000000400642",func-name="hello_world()",offset="18",size="5",inst="callq 0x4004d0; symbol stub for: printf"} + # To match the escaped characters in the ouptut, we must use four backslashes per matches backslash + # See https://docs.python.org/2/howto/regex.html#the-backslash-plague + self.expect([ "{address=\"0x[0-9a-f]+\",func-name=\"hello_world\(\)\",offset=\"[0-9]+\",size=\"[0-9]+\",inst=\".+?; \\\\\"Hello, World!\\\\\\\\n\\\\\"\"}", + "{address=\"0x[0-9a-f]+\",func-name=\"hello_world\(\)\",offset=\"[0-9]+\",size=\"[0-9]+\",inst=\".+?; symbol stub for: printf\"}" ]) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @unittest2.skip("-data-evaluate-expression doesn't work on globals") #FIXME: the global case worked before refactoring + def test_lldbmi_data_read_memory_bytes_global(self): + """Test that -data-read-memory-bytes can access global buffers.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Get address of char[] (global) + self.runCmd("-data-evaluate-expression &g_CharArray") + self.expect("\^done,value=\"0x[0-9a-f]+\"") + addr = int(self.child.after.split("\"")[1], 16) + size = 5 + + # Test that -data-read-memory-bytes works for char[] type (global) + self.runCmd("-data-read-memory-bytes %#x %d" % (addr, size)) + self.expect("\^done,memory=\[{begin=\"0x0*%x\",offset=\"0x0+\",end=\"0x0*%x\",contents=\"1112131400\"}\]" % (addr, addr + size)) + + # Get address of static char[] + self.runCmd("-data-evaluate-expression &s_CharArray") + self.expect("\^done,value=\"0x[0-9a-f]+\"") + addr = int(self.child.after.split("\"")[1], 16) + size = 5 + + # Test that -data-read-memory-bytes works for static char[] type + self.runCmd("-data-read-memory-bytes %#x %d" % (addr, size)) + self.expect("\^done,memory=\[{begin=\"0x0*%x\",offset=\"0x0+\",end=\"0x0*%x\",contents=\"1112131400\"}\]" % (addr, addr + size)) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_data_read_memory_bytes_local(self): + """Test that -data-read-memory-bytes can access local buffers.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd('-file-exec-and-symbols %s' % self.myexe) + self.expect(r'\^done') + + # Run to BP_local_array_test_inner + line = line_number('main.cpp', '// BP_local_array_test_inner') + self.runCmd('-break-insert main.cpp:%d' % line) + self.expect(r'\^done,bkpt=\{number="1"') + self.runCmd('-exec-run') + self.expect(r'\^running') + self.expect(r'\*stopped,reason="breakpoint-hit"') + + # Get address of local char[] + self.runCmd('-data-evaluate-expression "(void *)&array"') + self.expect(r'\^done,value="0x[0-9a-f]+"') + addr = int(self.child.after.split('"')[1], 16) + size = 4 + + # Test that an unquoted hex literal address works + self.runCmd('-data-read-memory-bytes %#x %d' % (addr, size)) + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) + + # Test that a double-quoted hex literal address works + self.runCmd('-data-read-memory-bytes "%#x" %d' % (addr, size)) + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) + + # Test that unquoted expressions work + self.runCmd('-data-read-memory-bytes &array %d' % size) + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) + + # This doesn't work, and perhaps that makes sense, but it does work on GDB + self.runCmd('-data-read-memory-bytes array 4') + self.expect(r'\^error') + #self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) + + self.runCmd('-data-read-memory-bytes &array[2] 2') + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="0304"\}\]' % (addr + 2, addr + size)) + + self.runCmd('-data-read-memory-bytes first_element_ptr %d' % size) + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) + + # Test that double-quoted expressions work + self.runCmd('-data-read-memory-bytes "&array" %d' % size) + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) + + self.runCmd('-data-read-memory-bytes "&array[0] + 1" 3') + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="020304"\}\]' % (addr + 1, addr + size)) + + self.runCmd('-data-read-memory-bytes "first_element_ptr + 1" 3') + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="020304"\}\]' % (addr + 1, addr + size)) + + # Test the -o (offset) option + self.runCmd('-data-read-memory-bytes -o 1 &array 3') + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="020304"\}\]' % (addr + 1, addr + size)) + + # Test the --thread option + self.runCmd('-data-read-memory-bytes --thread 1 &array 4') + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) + + # Test the --thread option with an invalid value + self.runCmd('-data-read-memory-bytes --thread 999 &array 4') + self.expect(r'\^error') + + # Test the --frame option (current frame) + self.runCmd('-data-read-memory-bytes --frame 0 &array 4') + self.expect(r'\^done,memory=\[\{begin="0x0*%x",offset="0x0+",end="0x0*%x",contents="01020304"\}\]' % (addr, addr + size)) + + # Test the --frame option (outer frame) + self.runCmd('-data-read-memory-bytes --frame 1 &array 4') + self.expect(r'\^done,memory=\[\{begin="0x[0-9a-f]+",offset="0x0+",end="0x[0-9a-f]+",contents="05060708"\}\]') + + # Test the --frame option with an invalid value + self.runCmd('-data-read-memory-bytes --frame 999 &array 4') + self.expect(r'\^error') + + # Test all the options at once + self.runCmd('-data-read-memory-bytes --thread 1 --frame 1 -o 2 &array 2') + self.expect(r'\^done,memory=\[\{begin="0x[0-9a-f]+",offset="0x0+",end="0x[0-9a-f]+",contents="0708"\}\]') + + # Test that an expression that references undeclared variables doesn't work + self.runCmd('-data-read-memory-bytes "&undeclared_array1 + undeclared_array2[1]" 2') + self.expect(r'\^error') + + # Test that the address argument is required + self.runCmd('-data-read-memory-bytes') + self.expect(r'\^error') + + # Test that the count argument is required + self.runCmd('-data-read-memory-bytes &array') + self.expect(r'\^error') + + # Test that the address and count arguments are required when other options are present + self.runCmd('-data-read-memory-bytes --thread 1') + self.expect(r'\^error') + + self.runCmd('-data-read-memory-bytes --thread 1 --frame 0') + self.expect(r'\^error') + + # Test that the count argument is required when other options are present + self.runCmd('-data-read-memory-bytes --thread 1 &array') + self.expect(r'\^error') + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_data_list_register_names(self): + """Test that 'lldb-mi --interpreter' works for -data-list-register-names.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -data-list-register-names: try to get all registers + self.runCmd("-data-list-register-names") + self.expect("\^done,register-names=\[\".+?\",") + + # Test -data-list-register-names: try to get specified registers + self.runCmd("-data-list-register-names 0") + self.expect("\^done,register-names=\[\".+?\"\]") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_data_list_register_values(self): + """Test that 'lldb-mi --interpreter' works for -data-list-register-values.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -data-list-register-values: try to get all registers + self.runCmd("-data-list-register-values x") + self.expect("\^done,register-values=\[{number=\"0\",value=\"0x[0-9a-f]+\"") + + # Test -data-list-register-values: try to get specified registers + self.runCmd("-data-list-register-values x 0") + self.expect("\^done,register-values=\[{number=\"0\",value=\"0x[0-9a-f]+\"}\]") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_data_info_line(self): + """Test that 'lldb-mi --interpreter' works for -data-info-line.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Get the address of main and its line + self.runCmd("-data-evaluate-expression main") + self.expect("\^done,value=\"0x[0-9a-f]+ \(a.out`main at main.cpp:[0-9]+\)\"") + addr = int(self.child.after.split("\"")[1].split(" ")[0], 16) + line = line_number('main.cpp', '// FUNC_main') + + # Test that -data-info-line works for address + self.runCmd("-data-info-line *%#x" % addr) + self.expect("\^done,start=\"0x0*%x\",end=\"0x[0-9a-f]+\",file=\".+?main.cpp\",line=\"%d\"" % (addr, line)) + + # Test that -data-info-line works for file:line + self.runCmd("-data-info-line main.cpp:%d" % line) + self.expect("\^done,start=\"0x0*%x\",end=\"0x[0-9a-f]+\",file=\".+?main.cpp\",line=\"%d\"" % (addr, line)) + + # Test that -data-info-line fails when invalid address is specified + self.runCmd("-data-info-line *0x0") + self.expect("\^error,msg=\"Command 'data-info-line'\. Error: The LineEntry is absent or has an unknown format\.\"") + + # Test that -data-info-line fails when file is unknown + self.runCmd("-data-info-line unknown_file:1") + self.expect("\^error,msg=\"Command 'data-info-line'\. Error: The LineEntry is absent or has an unknown format\.\"") + + # Test that -data-info-line fails when line has invalid format + self.runCmd("-data-info-line main.cpp:bad_line") + self.expect("\^error,msg=\"error: invalid line number string 'bad_line'") + self.runCmd("-data-info-line main.cpp:0") + self.expect("\^error,msg=\"error: zero is an invalid line number") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_data_evaluate_expression(self): + """Test that 'lldb-mi --interpreter' works for -data-evaluate-expression.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + line = line_number('main.cpp', '// BP_local_2d_array_test') + self.runCmd('-break-insert main.cpp:%d' % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Check 2d array + self.runCmd("-data-evaluate-expression array2d") + self.expect("\^done,value=\"\{\[0\] = \{\[0\] = 1, \[1\] = 2, \[2\] = 3\}, \[1\] = \{\[0\] = 4, \[1\] = 5, \[2\] = 6\}\}\"") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/data/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/data/main.cpp new file mode 100644 index 000000000000..8030fe891de2 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/data/main.cpp @@ -0,0 +1,59 @@ +//===-- 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 + +const char g_CharArray[] = "\x10\x11\x12\x13"; +static const char s_CharArray[] = "\x20\x21\x22\x23"; + +void +local_array_test_inner() +{ + char array[] = { 0x01, 0x02, 0x03, 0x04 }; + char *first_element_ptr = &array[0]; + // BP_local_array_test_inner + return; +} + +void +local_array_test() +{ + char array[] = { 0x05, 0x06, 0x07, 0x08 }; + // BP_local_array_test + local_array_test_inner(); + return; +} + +void +local_2d_array_test() +{ + int array2d[2][3]; + array2d[0][0] = 1; + array2d[0][1] = 2; + array2d[0][2] = 3; + array2d[1][0] = 4; + array2d[1][1] = 5; + array2d[1][2] = 6; + return; // BP_local_2d_array_test +} + +void +hello_world() +{ + printf("Hello, World!\n"); // BP_hello_world +} + +int +main(int argc, char const *argv[]) +{ // FUNC_main + local_array_test(); + hello_world(); + local_2d_array_test(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/TestMiCliSupport.py b/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/TestMiCliSupport.py new file mode 100644 index 000000000000..562be912fbc1 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/TestMiCliSupport.py @@ -0,0 +1,206 @@ +""" +Test lldb-mi can interpret CLI commands directly. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiCliSupportTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_target_create(self): + """Test that 'lldb-mi --interpreter' can create target by 'target create' command.""" + + self.spawnLldbMi(args = None) + + # Test that "target create" loads executable + self.runCmd("target create \"%s\"" % self.myexe) + self.expect("\^done") + + # Test that executable was loaded properly + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_breakpoint_set(self): + """Test that 'lldb-mi --interpreter' can set breakpoint by 'breakpoint set' command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that "breakpoint set" sets a breakpoint + self.runCmd("breakpoint set --name main") + self.expect("\^done") + self.expect("=breakpoint-created,bkpt={number=\"1\"") + + # Test that breakpoint was set properly + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("=breakpoint-modified,bkpt={number=\"1\"") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_settings_set_target_run_args_before(self): + """Test that 'lldb-mi --interpreter' can set target arguments by 'setting set target.run-args' command before than target was created.""" + + self.spawnLldbMi(args = None) + + # Test that "settings set target.run-args" passes arguments to executable + #FIXME: --arg1 causes an error + self.runCmd("setting set target.run-args arg1 \"2nd arg\" third_arg fourth=\"4th arg\"") + self.expect("\^done") + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + + # Test that arguments were passed properly + self.expect("@\"argc=5\\\\r\\\\n\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_settings_set_target_run_args_after(self): + """Test that 'lldb-mi --interpreter' can set target arguments by 'setting set target.run-args' command after than target was created.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that "settings set target.run-args" passes arguments to executable + #FIXME: --arg1 causes an error + self.runCmd("setting set target.run-args arg1 \"2nd arg\" third_arg fourth=\"4th arg\"") + self.expect("\^done") + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + + # Test that arguments were passed properly + self.expect("@\"argc=5\\\\r\\\\n\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_process_launch(self): + """Test that 'lldb-mi --interpreter' can launch process by "process launch" command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Set breakpoint + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + + # Test that "process launch" launches executable + self.runCmd("process launch") + self.expect("\^done") + + # Test that breakpoint hit + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_thread_step_in(self): + """Test that 'lldb-mi --interpreter' can step in by "thread step-in" command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that "thread step-in" steps into (or not) printf depending on debug info + # Note that message is different in Darwin and Linux: + # Darwin: "*stopped,reason=\"end-stepping-range\",frame={addr=\"0x[0-9a-f]+\",func=\"main\",args=[{name=\"argc\",value=\"1\"},{name=\"argv\",value="0x[0-9a-f]+\"}],file=\"main.cpp\",fullname=\".+main.cpp\",line=\"\d\"},thread-id=\"1\",stopped-threads=\"all\" + # Linux: "*stopped,reason=\"end-stepping-range\",frame={addr="0x[0-9a-f]+\",func=\"__printf\",args=[{name=\"format\",value=\"0x[0-9a-f]+\"}],file=\"printf.c\",fullname=\".+printf.c\",line="\d+"},thread-id=\"1\",stopped-threads=\"all\" + self.runCmd("thread step-in") + self.expect("\^done") + it = self.expect([ "@\"argc=1\\\\r\\\\n\"", + "\*stopped,reason=\"end-stepping-range\".+?func=\"(?!main).+?\"" ]) + if it == 0: + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_thread_step_over(self): + """Test that 'lldb-mi --interpreter' can step over by "thread step-over" command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that "thread step-over" steps over + self.runCmd("thread step-over") + self.expect("\^done") + self.expect("@\"argc=1\\\\r\\\\n\"") + self.expect("\*stopped,reason=\"end-stepping-range\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_thread_continue(self): + """Test that 'lldb-mi --interpreter' can continue execution by "thread continue" command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that "thread continue" continues execution + self.runCmd("thread continue") + self.expect("\^done") + self.expect("@\"argc=1\\\\r\\\\n") + self.expect("\*stopped,reason=\"exited-normally\"") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/TestMiInterpreterExec.py b/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/TestMiInterpreterExec.py new file mode 100644 index 000000000000..93d9f25683b0 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/TestMiInterpreterExec.py @@ -0,0 +1,229 @@ +""" +Test lldb-mi -interpreter-exec command. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiInterpreterExecTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_target_create(self): + """Test that 'lldb-mi --interpreter' can create target by 'target create' command.""" + + self.spawnLldbMi(args = None) + + # Test that "target create" loads executable + self.runCmd("-interpreter-exec console \"target create \\\"%s\\\"\"" % self.myexe) + self.expect("\^done") + + # Test that executable was loaded properly + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_breakpoint_set(self): + """Test that 'lldb-mi --interpreter' can set breakpoint by 'breakpoint set' command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that "breakpoint set" sets a breakpoint + self.runCmd("-interpreter-exec console \"breakpoint set --name main\"") + self.expect("\^done") + self.expect("=breakpoint-created,bkpt={number=\"1\"") + + # Test that breakpoint was set properly + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("=breakpoint-modified,bkpt={number=\"1\"") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFailureLinux # Failing in ~9/600 dosep runs (build 3120-3122) + def test_lldbmi_settings_set_target_run_args_before(self): + """Test that 'lldb-mi --interpreter' can set target arguments by 'setting set target.run-args' command before than target was created.""" + + self.spawnLldbMi(args = None) + + # Test that "settings set target.run-args" passes arguments to executable + #FIXME: --arg1 causes an error + self.runCmd("-interpreter-exec console \"setting set target.run-args arg1 \\\"2nd arg\\\" third_arg fourth=\\\"4th arg\\\"\"") + self.expect("\^done") + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + + # Test that arguments were passed properly + self.expect("@\"argc=5\\\\r\\\\n\"") + self.expect("@\"argv.0.=.*lldb-mi") + self.expect("@\"argv.1.=arg1\\\\r\\\\n\"") + self.expect("@\"argv.2.=2nd arg\\\\r\\\\n\"") + self.expect("@\"argv.3.=third_arg\\\\r\\\\n\"") + self.expect("@\"argv.4.=fourth=4th arg\\\\r\\\\n\"") + + # Test that program exited normally + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFailureLinux # Failing in ~9/600 dosep runs (build 3120-3122) + def test_lldbmi_settings_set_target_run_args_after(self): + """Test that 'lldb-mi --interpreter' can set target arguments by 'setting set target.run-args' command after than target was created.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Test that "settings set target.run-args" passes arguments to executable + #FIXME: --arg1 causes an error + self.runCmd("-interpreter-exec console \"setting set target.run-args arg1 \\\"2nd arg\\\" third_arg fourth=\\\"4th arg\\\"\"") + self.expect("\^done") + + # Run to BP_printf + line = line_number('main.cpp', '// BP_printf') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running"); + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Run to BP_return + line = line_number('main.cpp', '// BP_return') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\"") + self.runCmd("-exec-continue") + self.expect("\^running"); + + # Test that arguments were passed properly + self.expect("@\"argc=5\\\\r\\\\n\"") + self.expect("@\"argv.0.=.*lldb-mi") + self.expect("@\"argv.1.=arg1\\\\r\\\\n\"") + self.expect("@\"argv.2.=2nd arg\\\\r\\\\n\"") + self.expect("@\"argv.3.=third_arg\\\\r\\\\n\"") + self.expect("@\"argv.4.=fourth=4th arg\\\\r\\\\n\"") + + # Hit BP_return + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_process_launch(self): + """Test that 'lldb-mi --interpreter' can launch process by "process launch" command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Set breakpoint + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + + # Test that "process launch" launches executable + self.runCmd("-interpreter-exec console \"process launch\"") + self.expect("\^done") + + # Test that breakpoint hit + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_thread_step_in(self): + """Test that 'lldb-mi --interpreter' can step in by "thread step-in" command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that "thread step-in" steps into (or not) printf depending on debug info + # Note that message is different in Darwin and Linux: + # Darwin: "*stopped,reason=\"end-stepping-range\",frame={addr=\"0x[0-9a-f]+\",func=\"main\",args=[{name=\"argc\",value=\"1\"},{name=\"argv\",value="0x[0-9a-f]+\"}],file=\"main.cpp\",fullname=\".+main.cpp\",line=\"\d\"},thread-id=\"1\",stopped-threads=\"all\" + # Linux: "*stopped,reason=\"end-stepping-range\",frame={addr="0x[0-9a-f]+\",func=\"__printf\",args=[{name=\"format\",value=\"0x[0-9a-f]+\"}],file=\"printf.c\",fullname=\".+printf.c\",line="\d+"},thread-id=\"1\",stopped-threads=\"all\" + self.runCmd("-interpreter-exec console \"thread step-in\"") + self.expect("\^done") + it = self.expect([ "@\"argc=1\\\\r\\\\n\"", + "\*stopped,reason=\"end-stepping-range\".+?func=\"(?!main).+?\"" ]) + if it == 0: + self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_thread_step_over(self): + """Test that 'lldb-mi --interpreter' can step over by "thread step-over" command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that "thread step-over" steps over + self.runCmd("-interpreter-exec console \"thread step-over\"") + self.expect("\^done") + self.expect("@\"argc=1\\\\r\\\\n\"") + self.expect("\*stopped,reason=\"end-stepping-range\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFlakeyLinux("llvm.org/pr25470") + def test_lldbmi_thread_continue(self): + """Test that 'lldb-mi --interpreter' can continue execution by "thread continue" command.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that "thread continue" continues execution + self.runCmd("-interpreter-exec console \"thread continue\"") + self.expect("\^done") + self.expect("@\"argc=1\\\\r\\\\n") + self.expect("\*stopped,reason=\"exited-normally\"") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/main.cpp new file mode 100644 index 000000000000..0c042d466988 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/interpreter/main.cpp @@ -0,0 +1,19 @@ +//===-- 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 + +int +main(int argc, char const *argv[]) +{ + printf("argc=%d\n", argc); // BP_printf + for (int i = 0; i < argc; ++i) + printf("argv[%d]=%s\n", i, argv[i]); + return 0; // BP_return +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/lldbmi_testcase.py b/packages/Python/lldbsuite/test/tools/lldb-mi/lldbmi_testcase.py new file mode 100644 index 000000000000..277ffe70c1ca --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/lldbmi_testcase.py @@ -0,0 +1,54 @@ +""" +Base class for lldb-mi test cases. +""" + +from __future__ import print_function + + + +from lldbsuite.test.lldbtest import * + +class MiTestCaseBase(Base): + + mydir = None + myexe = "a.out" + mylog = "child.log" + + def getCategories(self): + return ['lldb-mi'] + + @classmethod + def classCleanup(cls): + TestBase.RemoveTempFile(cls.myexe) + TestBase.RemoveTempFile(cls.mylog) + + def setUp(self): + Base.setUp(self) + self.buildDefault() + self.child_prompt = "(gdb)" + + def tearDown(self): + if self.TraceOn(): + print("\n\nContents of %s:" % self.mylog) + try: + print(open(self.mylog, "r").read()) + except IOError: + pass + Base.tearDown(self) + + def spawnLldbMi(self, args=None): + import pexpect + self.child = pexpect.spawn("%s --interpreter %s" % ( + self.lldbMiExec, args if args else "")) + self.child.setecho(True) + self.child.logfile_read = open(self.mylog, "w") + # wait until lldb-mi has started up and is ready to go + self.expect(self.child_prompt, exactly = True) + + def runCmd(self, cmd): + self.child.sendline(cmd) + + def expect(self, pattern, exactly=False, *args, **kwargs): + if exactly: + return self.child.expect_exact(pattern, *args, **kwargs) + return self.child.expect(pattern, *args, **kwargs) diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/main.cpp new file mode 100644 index 000000000000..6a2079f2ce74 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/main.cpp @@ -0,0 +1,19 @@ +//===-- 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 + +int +main(int argc, char const *argv[]) +{ + int a = 10; + + printf("argc=%d\n", argc); // BP_printf + return 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/signal/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/signal/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/signal/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/signal/TestMiSignal.py b/packages/Python/lldbsuite/test/tools/lldb-mi/signal/TestMiSignal.py new file mode 100644 index 000000000000..11e7b8a82f68 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/signal/TestMiSignal.py @@ -0,0 +1,197 @@ +""" +Test that the lldb-mi handles signals properly. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiSignalTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Fails on FreeBSD apparently due to thread race conditions + def test_lldbmi_stopped_when_interrupt(self): + """Test that 'lldb-mi --interpreter' interrupt and resume a looping app.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Set doloop=1 and run (to loop forever) + self.runCmd("-data-evaluate-expression \"do_loop=1\"") + self.expect("\^done,value=\"1\"") + self.runCmd("-exec-continue") + self.expect("\^running") + + # Test that -exec-interrupt can interrupt an execution + self.runCmd("-exec-interrupt") + self.expect("\*stopped,reason=\"signal-received\",signal-name=\"SIGINT\",signal-meaning=\"Interrupt\",.+?thread-id=\"1\",stopped-threads=\"all\"") + + # Continue (to loop forever) + self.runCmd("-exec-continue") + self.expect("\^running") + + # Test that Ctrl+C can interrupt an execution + self.child.sendintr() #FIXME: here uses self.child directly + self.expect("\*stopped,reason=\"signal-received\",signal-name=\"SIGINT\",signal-meaning=\"Interrupt\",.*thread-id=\"1\",stopped-threads=\"all\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Fails on FreeBSD apparently due to thread race conditions + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_stopped_when_stopatentry_local(self): + """Test that 'lldb-mi --interpreter' notifies after it was stopped on entry (local).""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run with stop-at-entry flag + self.runCmd("-interpreter-exec command \"process launch -s\"") + self.expect("\^done") + + # Test that *stopped is printed + # Note that message is different in Darwin and Linux: + # Darwin: "*stopped,reason=\"signal-received\",signal-name=\"SIGSTOP\",signal-meaning=\"Stop\",frame={level=\"0\",addr=\"0x[0-9a-f]+\",func=\"_dyld_start\",file=\"??\",fullname=\"??\",line=\"-1\"},thread-id=\"1\",stopped-threads=\"all\" + # Linux: "*stopped,reason=\"end-stepping-range\",frame={addr=\"0x[0-9a-f]+\",func=\"??\",args=[],file=\"??\",fullname=\"??\",line=\"-1\"},thread-id=\"1\",stopped-threads=\"all\" + self.expect([ "\*stopped,reason=\"signal-received\",signal-name=\"SIGSTOP\",signal-meaning=\"Stop\",frame=\{level=\"0\",addr=\"0x[0-9a-f]+\",func=\"_dyld_start\",file=\"\?\?\",fullname=\"\?\?\",line=\"-1\"\},thread-id=\"1\",stopped-threads=\"all\"", + "\*stopped,reason=\"end-stepping-range\",frame={addr=\"0x[0-9a-f]+\",func=\"\?\?\",args=\[\],file=\"\?\?\",fullname=\"\?\?\",line=\"-1\"},thread-id=\"1\",stopped-threads=\"all\"" ]) + + # Run to main to make sure we have not exited the application + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipUnlessDarwin + def test_lldbmi_stopped_when_stopatentry_remote(self): + """Test that 'lldb-mi --interpreter' notifies after it was stopped on entry (remote).""" + + # Prepare debugserver + import lldbgdbserverutils + debugserver_exe = lldbgdbserverutils.get_debugserver_exe() + if not debugserver_exe: + self.skipTest("debugserver exe not found") + hostname = "localhost" + import random + port = 12000 + random.randint(0,3999) # the same as GdbRemoteTestCaseBase.get_next_port + import pexpect + debugserver_child = pexpect.spawn("%s %s:%d" % (debugserver_exe, hostname, port)) + self.addTearDownHook(lambda: debugserver_child.terminate(force = True)) + + self.spawnLldbMi(args = None) + + # Connect to debugserver + self.runCmd("-interpreter-exec command \"platform select remote-macosx --sysroot /\"") + self.expect("\^done") + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + self.runCmd("-interpreter-exec command \"process connect connect://%s:%d\"" % (hostname, port)) + self.expect("\^done") + + # Run with stop-at-entry flag + self.runCmd("-interpreter-exec command \"process launch -s\"") + self.expect("\^done") + + # Test that *stopped is printed + self.expect("\*stopped,reason=\"signal-received\",signal-name=\"SIGSTOP\",signal-meaning=\"Stop\",.+?thread-id=\"1\",stopped-threads=\"all\"") + + # Exit + self.runCmd("-gdb-exit") + self.expect("\^exit") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_stopped_when_segfault_local(self): + """Test that 'lldb-mi --interpreter' notifies after it was stopped when segfault occurred (local).""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Set do_segfault=1 and run (to cause a segfault error) + self.runCmd("-data-evaluate-expression \"do_segfault=1\"") + self.expect("\^done,value=\"1\"") + self.runCmd("-exec-continue") + self.expect("\^running") + + # Test that *stopped is printed + # Note that message is different in Darwin and Linux: + # Darwin: "*stopped,reason=\"exception-received\",exception=\"EXC_BAD_ACCESS (code=1, address=0x0)\",thread-id=\"1\",stopped-threads=\"all\"" + # Linux: "*stopped,reason=\"exception-received\",exception=\"invalid address (fault address: 0x0)\",thread-id=\"1\",stopped-threads=\"all\"" + self.expect([ "\*stopped,reason=\"exception-received\",exception=\"EXC_BAD_ACCESS \(code=1, address=0x0\)\",thread-id=\"1\",stopped-threads=\"all\"", + "\*stopped,reason=\"exception-received\",exception=\"invalid address \(fault address: 0x0\)\",thread-id=\"1\",stopped-threads=\"all\"" ]) + + @skipUnlessDarwin + def test_lldbmi_stopped_when_segfault_remote(self): + """Test that 'lldb-mi --interpreter' notifies after it was stopped when segfault occurred (remote).""" + + # Prepare debugserver + import lldbgdbserverutils + debugserver_exe = lldbgdbserverutils.get_debugserver_exe() + if not debugserver_exe: + self.skipTest("debugserver exe not found") + hostname = "localhost" + import random + port = 12000 + random.randint(0,3999) # the same as GdbRemoteTestCaseBase.get_next_port + import pexpect + debugserver_child = pexpect.spawn("%s %s:%d" % (debugserver_exe, hostname, port)) + self.addTearDownHook(lambda: debugserver_child.terminate(force = True)) + + self.spawnLldbMi(args = None) + + # Connect to debugserver + self.runCmd("-interpreter-exec command \"platform select remote-macosx --sysroot /\"") + self.expect("\^done") + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + self.runCmd("-interpreter-exec command \"process connect connect://%s:%d\"" % (hostname, port)) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + #FIXME -exec-run doesn't work + self.runCmd("-interpreter-exec command \"process launch\"") #FIXME: self.runCmd("-exec-run") + self.expect("\^done") #FIXME: self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Set do_segfault=1 and run (to cause a segfault error) + self.runCmd("-data-evaluate-expression \"do_segfault=1\"") + self.expect("\^done,value=\"1\"") + self.runCmd("-exec-continue") + self.expect("\^running") + + # Test that *stopped is printed + self.expect("\*stopped,reason=\"exception-received\",exception=\"EXC_BAD_ACCESS \(code=1, address=0x0\)\",thread-id=\"1\",stopped-threads=\"all\"") + + # Exit + self.runCmd("-gdb-exit") + self.expect("\^exit") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/signal/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/signal/main.cpp new file mode 100644 index 000000000000..7f6eeca70c01 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/signal/main.cpp @@ -0,0 +1,33 @@ +//===-- 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 do_loop; +int do_segfault; + +int +main(int argc, char const *argv[]) +{ + if (do_loop) + { + do + sleep(1); + while (do_loop); // BP_loop_condition + } + + if (do_segfault) + { + int *null_ptr = NULL; + return *null_ptr; + } + + return 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/stack/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/stack/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/stack/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/stack/TestMiStack.py b/packages/Python/lldbsuite/test/tools/lldb-mi/stack/TestMiStack.py new file mode 100644 index 000000000000..14dab38bb338 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/stack/TestMiStack.py @@ -0,0 +1,479 @@ +""" +Test lldb-mi -stack-xxx commands. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiStackTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_stack_list_arguments(self): + """Test that 'lldb-mi --interpreter' can shows arguments.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -stack-list-arguments lists empty stack arguments if range is empty + self.runCmd("-stack-list-arguments 0 1 0") + self.expect("\^done,stack-args=\[\]") + + # Test that -stack-list-arguments lists stack arguments without values + # (and that low-frame and high-frame are optional) + self.runCmd("-stack-list-arguments 0") + self.expect("\^done,stack-args=\[frame={level=\"0\",args=\[name=\"argc\",name=\"argv\"\]}") + self.runCmd("-stack-list-arguments --no-values") + self.expect("\^done,stack-args=\[frame={level=\"0\",args=\[name=\"argc\",name=\"argv\"\]}") + + # Test that -stack-list-arguments lists stack arguments with all values + self.runCmd("-stack-list-arguments 1 0 0") + self.expect("\^done,stack-args=\[frame={level=\"0\",args=\[{name=\"argc\",value=\"1\"},{name=\"argv\",value=\".*\"}\]}\]") + self.runCmd("-stack-list-arguments --all-values 0 0") + self.expect("\^done,stack-args=\[frame={level=\"0\",args=\[{name=\"argc\",value=\"1\"},{name=\"argv\",value=\".*\"}\]}\]") + + # Test that -stack-list-arguments lists stack arguments with simple values + self.runCmd("-stack-list-arguments 2 0 1") + self.expect("\^done,stack-args=\[frame={level=\"0\",args=\[{name=\"argc\",type=\"int\",value=\"1\"},{name=\"argv\",type=\"const char \*\*\",value=\".*\"}\]}") + self.runCmd("-stack-list-arguments --simple-values 0 1") + self.expect("\^done,stack-args=\[frame={level=\"0\",args=\[{name=\"argc\",type=\"int\",value=\"1\"},{name=\"argv\",type=\"const char \*\*\",value=\".*\"}\]}") + + # Test that an invalid low-frame is handled + # FIXME: -1 is treated as unsigned int + self.runCmd("-stack-list-arguments 0 -1 0") + #self.expect("\^error") + self.runCmd("-stack-list-arguments 0 0") + self.expect("\^error,msg=\"Command 'stack-list-arguments'\. Thread frame range invalid\"") + + # Test that an invalid high-frame is handled + # FIXME: -1 is treated as unsigned int + self.runCmd("-stack-list-arguments 0 0 -1") + #self.expect("\^error") + + # Test that a missing low-frame or high-frame is handled + self.runCmd("-stack-list-arguments 0 0") + self.expect("\^error,msg=\"Command 'stack-list-arguments'\. Thread frame range invalid\"") + + # Test that an invalid low-frame is handled + self.runCmd("-stack-list-arguments 0 0") + self.expect("\^error,msg=\"Command 'stack-list-arguments'\. Thread frame range invalid\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_stack_list_locals(self): + """Test that 'lldb-mi --interpreter' can shows local variables.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test int local variables: + # Run to BP_local_int_test + line = line_number('main.cpp', '// BP_local_int_test') + self.runCmd("-break-insert --file main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -stack-list-locals: use 0 or --no-values + self.runCmd("-stack-list-locals 0") + self.expect("\^done,locals=\[name=\"a\",name=\"b\"\]") + self.runCmd("-stack-list-locals --no-values") + self.expect("\^done,locals=\[name=\"a\",name=\"b\"\]") + + # Test -stack-list-locals: use 1 or --all-values + self.runCmd("-stack-list-locals 1") + self.expect("\^done,locals=\[{name=\"a\",value=\"10\"},{name=\"b\",value=\"20\"}\]") + self.runCmd("-stack-list-locals --all-values") + self.expect("\^done,locals=\[{name=\"a\",value=\"10\"},{name=\"b\",value=\"20\"}\]") + + # Test -stack-list-locals: use 2 or --simple-values + self.runCmd("-stack-list-locals 2") + self.expect("\^done,locals=\[{name=\"a\",type=\"int\",value=\"10\"},{name=\"b\",type=\"int\",value=\"20\"}\]") + self.runCmd("-stack-list-locals --simple-values") + self.expect("\^done,locals=\[{name=\"a\",type=\"int\",value=\"10\"},{name=\"b\",type=\"int\",value=\"20\"}\]") + + # Test struct local variable: + # Run to BP_local_struct_test + line = line_number('main.cpp', '// BP_local_struct_test') + self.runCmd("-break-insert --file main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"3\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -stack-list-locals: use 0 or --no-values + self.runCmd("-stack-list-locals 0") + self.expect("\^done,locals=\[name=\"var_c\"\]") + self.runCmd("-stack-list-locals --no-values") + self.expect("\^done,locals=\[name=\"var_c\"\]") + + # Test -stack-list-locals: use 1 or --all-values + self.runCmd("-stack-list-locals 1") + self.expect("\^done,locals=\[{name=\"var_c\",value=\"{var_a = 10, var_b = 97 'a', inner_ = {var_d = 30}}\"}\]") + self.runCmd("-stack-list-locals --all-values") + self.expect("\^done,locals=\[{name=\"var_c\",value=\"{var_a = 10, var_b = 97 'a', inner_ = {var_d = 30}}\"}\]") + + # Test -stack-list-locals: use 2 or --simple-values + self.runCmd("-stack-list-locals 2") + self.expect("\^done,locals=\[{name=\"var_c\",type=\"my_type\"}\]") + self.runCmd("-stack-list-locals --simple-values") + self.expect("\^done,locals=\[{name=\"var_c\",type=\"my_type\"}\]") + + # Test array local variable: + # Run to BP_local_array_test + line = line_number('main.cpp', '// BP_local_array_test') + self.runCmd("-break-insert --file main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"4\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -stack-list-locals: use 0 or --no-values + self.runCmd("-stack-list-locals 0") + self.expect("\^done,locals=\[name=\"array\"\]") + self.runCmd("-stack-list-locals --no-values") + self.expect("\^done,locals=\[name=\"array\"\]") + + # Test -stack-list-locals: use 1 or --all-values + self.runCmd("-stack-list-locals 1") + self.expect("\^done,locals=\[{name=\"array\",value=\"{\[0\] = 100, \[1\] = 200, \[2\] = 300}\"}\]") + self.runCmd("-stack-list-locals --all-values") + self.expect("\^done,locals=\[{name=\"array\",value=\"{\[0\] = 100, \[1\] = 200, \[2\] = 300}\"}\]") + + # Test -stack-list-locals: use 2 or --simple-values + self.runCmd("-stack-list-locals 2") + self.expect("\^done,locals=\[{name=\"array\",type=\"int \[3\]\"}\]") + self.runCmd("-stack-list-locals --simple-values") + self.expect("\^done,locals=\[{name=\"array\",type=\"int \[3\]\"}\]") + + # Test pointers as local variable: + # Run to BP_local_pointer_test + line = line_number('main.cpp', '// BP_local_pointer_test') + self.runCmd("-break-insert --file main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"5\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -stack-list-locals: use 0 or --no-values + self.runCmd("-stack-list-locals 0") + self.expect("\^done,locals=\[name=\"test_str\",name=\"var_e\",name=\"ptr\"\]") + self.runCmd("-stack-list-locals --no-values") + self.expect("\^done,locals=\[name=\"test_str\",name=\"var_e\",name=\"ptr\"\]") + + # Test -stack-list-locals: use 1 or --all-values + self.runCmd("-stack-list-locals 1") + self.expect("\^done,locals=\[{name=\"test_str\",value=\".*?Rakaposhi.*?\"},{name=\"var_e\",value=\"24\"},{name=\"ptr\",value=\".*?\"}\]") + self.runCmd("-stack-list-locals --all-values") + self.expect("\^done,locals=\[{name=\"test_str\",value=\".*?Rakaposhi.*?\"},{name=\"var_e\",value=\"24\"},{name=\"ptr\",value=\".*?\"}\]") + + # Test -stack-list-locals: use 2 or --simple-values + self.runCmd("-stack-list-locals 2") + self.expect("\^done,locals=\[{name=\"test_str\",type=\"const char \*\",value=\".*?Rakaposhi.*?\"},{name=\"var_e\",type=\"int\",value=\"24\"},{name=\"ptr\",type=\"int \*\",value=\".*?\"}\]") + self.runCmd("-stack-list-locals --simple-values") + self.expect("\^done,locals=\[{name=\"test_str\",type=\"const char \*\",value=\".*?Rakaposhi.*?\"},{name=\"var_e\",type=\"int\",value=\"24\"},{name=\"ptr\",type=\"int \*\",value=\".*?\"}\]") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_stack_list_variables(self): + """Test that 'lldb-mi --interpreter' can shows local variables and arguments.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test int local variables: + # Run to BP_local_int_test + line = line_number('main.cpp', '// BP_local_int_test_with_args') + self.runCmd("-break-insert --file main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -stack-list-variables: use 0 or --no-values + self.runCmd("-stack-list-variables 0") + self.expect("\^done,variables=\[{arg=\"1\",name=\"c\"},{arg=\"1\",name=\"d\"},{name=\"a\"},{name=\"b\"}\]") + self.runCmd("-stack-list-variables --no-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"c\"},{arg=\"1\",name=\"d\"},{name=\"a\"},{name=\"b\"}\]") + + # Test -stack-list-variables: use 1 or --all-values + self.runCmd("-stack-list-variables 1") + self.expect("\^done,variables=\[{arg=\"1\",name=\"c\",value=\"30\"},{arg=\"1\",name=\"d\",value=\"40\"},{name=\"a\",value=\"10\"},{name=\"b\",value=\"20\"}\]") + self.runCmd("-stack-list-variables --all-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"c\",value=\"30\"},{arg=\"1\",name=\"d\",value=\"40\"},{name=\"a\",value=\"10\"},{name=\"b\",value=\"20\"}\]") + + # Test -stack-list-variables: use 2 or --simple-values + self.runCmd("-stack-list-variables 2") + self.expect("\^done,variables=\[{arg=\"1\",name=\"c\",type=\"int\",value=\"30\"},{arg=\"1\",name=\"d\",type=\"int\",value=\"40\"},{name=\"a\",type=\"int\",value=\"10\"},{name=\"b\",type=\"int\",value=\"20\"}\]") + self.runCmd("-stack-list-variables --simple-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"c\",type=\"int\",value=\"30\"},{arg=\"1\",name=\"d\",type=\"int\",value=\"40\"},{name=\"a\",type=\"int\",value=\"10\"},{name=\"b\",type=\"int\",value=\"20\"}\]") + + # Test struct local variable: + # Run to BP_local_struct_test + line = line_number('main.cpp', '// BP_local_struct_test_with_args') + self.runCmd("-break-insert --file main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"3\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -stack-list-variables: use 0 or --no-values + self.runCmd("-stack-list-variables 0") + self.expect("\^done,variables=\[{arg=\"1\",name=\"var_e\"},{name=\"var_c\"}\]") + self.runCmd("-stack-list-variables --no-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"var_e\"},{name=\"var_c\"}\]") + + # Test -stack-list-variables: use 1 or --all-values + self.runCmd("-stack-list-variables 1") + self.expect("\^done,variables=\[{arg=\"1\",name=\"var_e\",value=\"{var_a = 20, var_b = 98 'b', inner_ = {var_d = 40}}\"},{name=\"var_c\",value=\"{var_a = 10, var_b = 97 'a', inner_ = {var_d = 30}}\"}\]") + self.runCmd("-stack-list-variables --all-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"var_e\",value=\"{var_a = 20, var_b = 98 'b', inner_ = {var_d = 40}}\"},{name=\"var_c\",value=\"{var_a = 10, var_b = 97 'a', inner_ = {var_d = 30}}\"}\]") + + # Test -stack-list-variables: use 2 or --simple-values + self.runCmd("-stack-list-variables 2") + self.expect("\^done,variables=\[{arg=\"1\",name=\"var_e\",type=\"my_type\"},{name=\"var_c\",type=\"my_type\"}\]") + self.runCmd("-stack-list-variables --simple-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"var_e\",type=\"my_type\"},{name=\"var_c\",type=\"my_type\"}\]") + + # Test array local variable: + # Run to BP_local_array_test + line = line_number('main.cpp', '// BP_local_array_test_with_args') + self.runCmd("-break-insert --file main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"4\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -stack-list-variables: use 0 or --no-values + self.runCmd("-stack-list-variables 0") + self.expect("\^done,variables=\[{arg=\"1\",name=\"other_array\"},{name=\"array\"}\]") + self.runCmd("-stack-list-variables --no-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"other_array\"},{name=\"array\"}\]") + + # Test -stack-list-variables: use 1 or --all-values + self.runCmd("-stack-list-variables 1") + self.expect("\^done,variables=\[{arg=\"1\",name=\"other_array\",value=\".*?\"},{name=\"array\",value=\"{\[0\] = 100, \[1\] = 200, \[2\] = 300}\"}\]") + self.runCmd("-stack-list-variables --all-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"other_array\",value=\".*?\"},{name=\"array\",value=\"{\[0\] = 100, \[1\] = 200, \[2\] = 300}\"}\]") + + # Test -stack-list-variables: use 2 or --simple-values + self.runCmd("-stack-list-variables 2") + self.expect("\^done,variables=\[{arg=\"1\",name=\"other_array\",type=\"int \*\",value=\".*?\"},{name=\"array\",type=\"int \[3\]\"}\]") + self.runCmd("-stack-list-variables --simple-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"other_array\",type=\"int \*\",value=\".*?\"},{name=\"array\",type=\"int \[3\]\"}\]") + + # Test pointers as local variable: + # Run to BP_local_pointer_test + line = line_number('main.cpp', '// BP_local_pointer_test_with_args') + self.runCmd("-break-insert --file main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"5\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test -stack-list-variables: use 0 or --no-values + self.runCmd("-stack-list-variables 0") + self.expect("\^done,variables=\[{arg=\"1\",name=\"arg_str\"},{arg=\"1\",name=\"arg_ptr\"},{name=\"test_str\"},{name=\"var_e\"},{name=\"ptr\"}\]") + self.runCmd("-stack-list-variables --no-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"arg_str\"},{arg=\"1\",name=\"arg_ptr\"},{name=\"test_str\"},{name=\"var_e\"},{name=\"ptr\"}\]") + + # Test -stack-list-variables: use 1 or --all-values + self.runCmd("-stack-list-variables 1") + self.expect("\^done,variables=\[{arg=\"1\",name=\"arg_str\",value=\".*?String.*?\"},{arg=\"1\",name=\"arg_ptr\",value=\".*?\"},{name=\"test_str\",value=\".*?Rakaposhi.*?\"},{name=\"var_e\",value=\"24\"},{name=\"ptr\",value=\".*?\"}\]") + self.runCmd("-stack-list-variables --all-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"arg_str\",value=\".*?String.*?\"},{arg=\"1\",name=\"arg_ptr\",value=\".*?\"},{name=\"test_str\",value=\".*?Rakaposhi.*?\"},{name=\"var_e\",value=\"24\"},{name=\"ptr\",value=\".*?\"}\]") + + # Test -stack-list-variables: use 2 or --simple-values + self.runCmd("-stack-list-variables 2") + self.expect("\^done,variables=\[{arg=\"1\",name=\"arg_str\",type=\"const char \*\",value=\".*?String.*?\"},{arg=\"1\",name=\"arg_ptr\",type=\"int \*\",value=\".*?\"},{name=\"test_str\",type=\"const char \*\",value=\".*?Rakaposhi.*?\"},{name=\"var_e\",type=\"int\",value=\"24\"},{name=\"ptr\",type=\"int \*\",value=\".*?\"}\]") + self.runCmd("-stack-list-variables --simple-values") + self.expect("\^done,variables=\[{arg=\"1\",name=\"arg_str\",type=\"const char \*\",value=\".*?String.*?\"},{arg=\"1\",name=\"arg_ptr\",type=\"int \*\",value=\".*?\"},{name=\"test_str\",type=\"const char \*\",value=\".*?Rakaposhi.*?\"},{name=\"var_e\",type=\"int\",value=\"24\"},{name=\"ptr\",type=\"int \*\",value=\".*?\"}\]") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_stack_info_depth(self): + """Test that 'lldb-mi --interpreter' can shows depth of the stack.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -stack-info-depth works + # (and that max-depth is optional) + self.runCmd("-stack-info-depth") + self.expect("\^done,depth=\"[1-9]\"") + + # Test that max-depth restricts check of stack depth + #FIXME: max-depth argument is ignored + self.runCmd("-stack-info-depth 1") + #self.expect("\^done,depth=\"1\"") + + # Test that invalid max-depth argument is handled + #FIXME: max-depth argument is ignored + self.runCmd("-stack-info-depth -1") + #self.expect("\^error") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipUnlessDarwin + def test_lldbmi_stack_info_frame(self): + """Test that 'lldb-mi --interpreter' can show information about current frame.""" + + self.spawnLldbMi(args = None) + + # Test that -stack-info-frame fails when program isn't running + self.runCmd("-stack-info-frame") + self.expect("\^error,msg=\"Command 'stack-info-frame'\. Invalid process during debug session\"") + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -stack-info-frame works when program was stopped on BP + self.runCmd("-stack-info-frame") + self.expect("\^done,frame=\{level=\"0\",addr=\"0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"\d+\"\}") + + # Select frame #1 + self.runCmd("-stack-select-frame 1") + self.expect("\^done") + + # Test that -stack-info-frame works when specified frame was selected + self.runCmd("-stack-info-frame") + self.expect("\^done,frame=\{level=\"1\",addr=\"0x[0-9a-f]+\",func=\".+?\",file=\"\?\?\",fullname=\"\?\?\",line=\"-1\"\}") + + # Test that -stack-info-frame fails when an argument is specified + #FIXME: unknown argument is ignored + self.runCmd("-stack-info-frame unknown_arg") + #self.expect("\^error") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_stack_list_frames(self): + """Test that 'lldb-mi --interpreter' can lists the frames on the stack.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test stack frame: get frame #0 info + self.runCmd("-stack-list-frames 0 0") + self.expect("\^done,stack=\[frame=\{level=\"0\",addr=\"0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"\d+\"\}\]") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_stack_select_frame(self): + """Test that 'lldb-mi --interpreter' can choose current frame.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that -stack-select-frame requires 1 mandatory argument + self.runCmd("-stack-select-frame") + self.expect("\^error,msg=\"Command 'stack-select-frame'\. Command Args\. Validation failed. Mandatory args not found: frame_id\"") + + # Test that -stack-select-frame fails on invalid frame number + self.runCmd("-stack-select-frame 99") + self.expect("\^error,msg=\"Command 'stack-select-frame'\. Frame ID invalid\"") + + # Test that current frame is #0 + self.runCmd("-stack-info-frame") + self.expect("\^done,frame=\{level=\"0\",addr=\"0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"\d+\"\}") + + # Test that -stack-select-frame can select the selected frame + self.runCmd("-stack-select-frame 0") + self.expect("\^done") + + # Test that current frame is still #0 + self.runCmd("-stack-info-frame") + self.expect("\^done,frame=\{level=\"0\",addr=\"0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"\d+\"\}") + + # Test that -stack-select-frame can select frame #1 (parent frame) + self.runCmd("-stack-select-frame 1") + self.expect("\^done") + + # Test that current frame is #1 + # Note that message is different in Darwin and Linux: + # Darwin: "^done,frame={level=\"1\",addr=\"0x[0-9a-f]+\",func=\"start\",file=\"??\",fullname=\"??\",line=\"-1\"}" + # Linux: "^done,frame={level=\"1\",addr=\"0x[0-9a-f]+\",func=\".+\",file=\".+\",fullname=\".+\",line=\"\d+\"}" + self.runCmd("-stack-info-frame") + self.expect("\^done,frame=\{level=\"1\",addr=\"0x[0-9a-f]+\",func=\".+?\",file=\".+?\",fullname=\".+?\",line=\"(-1|\d+)\"\}") + + # Test that -stack-select-frame can select frame #0 (child frame) + self.runCmd("-stack-select-frame 0") + self.expect("\^done") + + # Test that current frame is #0 and it has the same information + self.runCmd("-stack-info-frame") + self.expect("\^done,frame=\{level=\"0\",addr=\"0x[0-9a-f]+\",func=\"main\",file=\"main\.cpp\",fullname=\".+?main\.cpp\",line=\"\d+\"\}") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/stack/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/stack/main.cpp new file mode 100644 index 000000000000..e11f83e108ec --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/stack/main.cpp @@ -0,0 +1,127 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +struct inner +{ + int var_d; +}; + +struct my_type +{ + int var_a; + char var_b; + struct inner inner_; +}; + +int +local_int_test(void) +{ + int a = 10, b = 20; + return 0; // BP_local_int_test +} + +int +local_int_test_with_args(int c, int d) +{ + int a = 10, b = 20; + return 0; // BP_local_int_test_with_args +} + +int +local_struct_test(void) +{ + struct my_type var_c; + var_c.var_a = 10; + var_c.var_b = 'a'; + var_c.inner_.var_d = 30; + return 0; // BP_local_struct_test +} + +int local_struct_test_with_args(struct my_type var_e) +{ + struct my_type var_c; + var_c.var_a = 10; + var_c.var_b = 'a'; + var_c.inner_.var_d = 30; + return 0; // BP_local_struct_test_with_args +} + +int +local_array_test(void) +{ + int array[3]; + array[0] = 100; + array[1] = 200; + array[2] = 300; + return 0; // BP_local_array_test +} + +int +local_array_test_with_args(int* other_array) +{ + int array[3]; + array[0] = 100; + array[1] = 200; + array[2] = 300; + return 0; // BP_local_array_test_with_args +} + +int +local_pointer_test(void) +{ + const char *test_str = "Rakaposhi"; + int var_e = 24; + int *ptr = &var_e; + return 0; // BP_local_pointer_test +} + +int +local_pointer_test_with_args(const char *arg_str, int *arg_ptr) +{ + const char *test_str = "Rakaposhi"; + int var_e = 24; + int *ptr = &var_e; + return 0; // BP_local_pointer_test_with_args +} + +int do_tests_with_args() +{ + local_int_test_with_args(30, 40); + + struct my_type var_e; + var_e.var_a = 20; + var_e.var_b = 'b'; + var_e.inner_.var_d = 40; + local_struct_test_with_args(var_e); + + int array[3]; + array[0] = 400; + array[1] = 500; + array[2] = 600; + local_array_test_with_args(array); + + const char *test_str = "String"; + int var_z = 25; + int *ptr = &var_z; + local_pointer_test_with_args(test_str, ptr); + + return 0; +} + +int +main(int argc, char const *argv[]) +{ + local_int_test(); + local_struct_test(); + local_array_test(); + local_pointer_test(); + + do_tests_with_args(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/TestMiStartupOptions.py b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/TestMiStartupOptions.py new file mode 100644 index 000000000000..8f02f1c1eca8 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/TestMiStartupOptions.py @@ -0,0 +1,290 @@ +""" +Test lldb-mi startup options. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiStartupOptionsTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_executable_option_file(self): + """Test that 'lldb-mi --interpreter %s' loads executable file.""" + + self.spawnLldbMi(args = "%s" % self.myexe) + + # Test that the executable is loaded when file was specified + self.expect("-file-exec-and-symbols \"%s\"" % self.myexe) + self.expect("\^done") + + # Test that lldb-mi is ready when executable was loaded + self.expect(self.child_prompt, exactly = True) + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Continue + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_executable_option_unknown_file(self): + """Test that 'lldb-mi --interpreter %s' fails on unknown executable file.""" + + # Prepare path to executable + path = "unknown_file" + + self.spawnLldbMi(args = "%s" % path) + + # Test that the executable isn't loaded when unknown file was specified + self.expect("-file-exec-and-symbols \"%s\"" % path) + self.expect("\^error,msg=\"Command 'file-exec-and-symbols'. Target binary '%s' is invalid. error: unable to find executable for '%s'\"" % (path, path)) + + # Test that lldb-mi is ready when executable was loaded + self.expect(self.child_prompt, exactly = True) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_executable_option_absolute_path(self): + """Test that 'lldb-mi --interpreter %s' loads executable which is specified via absolute path.""" + + # Prepare path to executable + import os + path = os.path.join(os.getcwd(), self.myexe) + + self.spawnLldbMi(args = "%s" % path) + + # Test that the executable is loaded when file was specified using absolute path + self.expect("-file-exec-and-symbols \"%s\"" % path) + self.expect("\^done") + + # Test that lldb-mi is ready when executable was loaded + self.expect(self.child_prompt, exactly = True) + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_executable_option_relative_path(self): + """Test that 'lldb-mi --interpreter %s' loads executable which is specified via relative path.""" + + # Prepare path to executable + path = "./%s" % self.myexe + + self.spawnLldbMi(args = "%s" % path) + + # Test that the executable is loaded when file was specified using relative path + self.expect("-file-exec-and-symbols \"%s\"" % path) + self.expect("\^done") + + # Test that lldb-mi is ready when executable was loaded + self.expect(self.child_prompt, exactly = True) + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_executable_option_unknown_path(self): + """Test that 'lldb-mi --interpreter %s' fails on executable file which is specified via unknown path.""" + + # Prepare path to executable + path = "unknown_dir/%s" % self.myexe + + self.spawnLldbMi(args = "%s" % path) + + # Test that the executable isn't loaded when file was specified using unknown path + self.expect("-file-exec-and-symbols \"%s\"" % path) + self.expect("\^error,msg=\"Command 'file-exec-and-symbols'. Target binary '%s' is invalid. error: unable to find executable for '%s'\"" % (path, path)) + + # Test that lldb-mi is ready when executable was loaded + self.expect(self.child_prompt, exactly = True) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_source_option_start_script(self): + """Test that 'lldb-mi --interpreter' can execute user's commands after initial commands were executed.""" + + # Prepared source file + sourceFile = "start_script" + + self.spawnLldbMi(args = "--source %s" % sourceFile) + + # After '-file-exec-and-symbols a.out' + self.expect("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # After '-break-insert -f main' + self.expect("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + + # After '-exec-run' + self.expect("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # After '-break-insert main.cpp:BP_return' + line = line_number('main.cpp', '//BP_return') + self.expect("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\"") + + # After '-exec-continue' + self.expect("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that lldb-mi is ready after execution of --source start_script + self.expect(self.child_prompt, exactly = True) + + # Try to evaluate 'a' expression + self.runCmd("-data-evaluate-expression a") + self.expect("\^done,value=\"10\"") + self.expect(self.child_prompt, exactly = True) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_source_option_start_script_exit(self): + """Test that 'lldb-mi --interpreter' can execute a prepared file which passed via --source option.""" + + # Prepared source file + sourceFile = "start_script_exit" + + self.spawnLldbMi(args = "--source %s" % sourceFile) + + # After '-file-exec-and-symbols a.out' + self.expect("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # After '-break-insert -f main' + self.expect("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + + # After '-exec-run' + self.expect("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # After '-break-insert main.cpp:BP_return' + line = line_number('main.cpp', '//BP_return') + self.expect("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\"") + + # After '-exec-continue' + self.expect("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # After '-data-evaluate-expression a' + self.expect("-data-evaluate-expression a") + self.expect("\^done,value=\"10\"") + + # After '-gdb-exit' + self.expect("-gdb-exit") + self.expect("\^exit") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_source_option_start_script_error(self): + """Test that 'lldb-mi --interpreter' stops execution of initial commands in case of error.""" + + # Prepared source file + sourceFile = "start_script_error" + + self.spawnLldbMi(args = "--source %s" % sourceFile) + + # After '-file-exec-and-symbols a.out' + self.expect("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # After '-break-ins -f main' + self.expect("-break-ins -f main") + self.expect("\^error") + + # Test that lldb-mi is ready after execution of --source start_script + self.expect(self.child_prompt, exactly = True) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_log_option(self): + """Test that 'lldb-mi --log' creates a log file in the current directory.""" + + logDirectory = "." + self.spawnLldbMi(args = "%s --log" % self.myexe) + + # Test that the executable is loaded when file was specified + self.expect("-file-exec-and-symbols \"%s\"" % self.myexe) + self.expect("\^done") + + # Test that lldb-mi is ready when executable was loaded + self.expect(self.child_prompt, exactly = True) + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + # Check log file is created + import glob,os + logFile = glob.glob(logDirectory + "/lldb-mi-*.log") + + if not logFile: + self.fail("log file not found") + + # Delete log + for f in logFile: + os.remove(f) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_log_directory_option(self): + """Test that 'lldb-mi --log --log-dir' creates a log file in the directory specified by --log-dir.""" + + # Create log in temp directory + import tempfile + logDirectory = tempfile.gettempdir() + + self.spawnLldbMi(args = "%s --log --log-dir=%s" % (self.myexe,logDirectory)) + + # Test that the executable is loaded when file was specified + self.expect("-file-exec-and-symbols \"%s\"" % self.myexe) + self.expect("\^done") + + # Test that lldb-mi is ready when executable was loaded + self.expect(self.child_prompt, exactly = True) + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + # Check log file is created + import glob,os + logFile = glob.glob(logDirectory + "/lldb-mi-*.log") + + if not logFile: + self.fail("log file not found") + + # Delete log + for f in logFile: + os.remove(f) diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/main.cpp new file mode 100644 index 000000000000..7f2d5246faf9 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/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. +// +//===----------------------------------------------------------------------===// + +int +main(int argc, char const *argv[]) +{ + int a = 10; + return 0; //BP_return +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script new file mode 100644 index 000000000000..511c02248258 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script @@ -0,0 +1,5 @@ +-file-exec-and-symbols a.out +-break-insert -f main +-exec-run +-break-insert main.cpp:14 +-exec-continue diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script_error b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script_error new file mode 100644 index 000000000000..d834e7407c57 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script_error @@ -0,0 +1,2 @@ +-file-exec-and-symbols a.out +-break-ins -f main diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script_exit b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script_exit new file mode 100644 index 000000000000..8379018c29df --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/start_script_exit @@ -0,0 +1,7 @@ +-file-exec-and-symbols a.out +-break-insert -f main +-exec-run +-break-insert main.cpp:14 +-exec-continue +-data-evaluate-expression a +-gdb-exit diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/Makefile new file mode 100644 index 000000000000..dde38f4e486c --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp symbol_list_lines_inline_test.cpp symbol_list_lines_inline_test2.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/TestMiSymbol.py b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/TestMiSymbol.py new file mode 100644 index 000000000000..3566b2f220c2 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/TestMiSymbol.py @@ -0,0 +1,81 @@ +""" +Test lldb-mi -symbol-xxx commands. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiSymbolTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFailureLinux # new failure after r256863 + def test_lldbmi_symbol_list_lines_file(self): + """Test that 'lldb-mi --interpreter' works for -symbol-list-lines when file exists.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Get address of main and its line + self.runCmd("-data-evaluate-expression main") + self.expect("\^done,value=\"0x[0-9a-f]+ \(a.out`main at main.cpp:[0-9]+\)\"") + addr = int(self.child.after.split("\"")[1].split(" ")[0], 16) + line = line_number('main.cpp', '// FUNC_main') + + # Test that -symbol-list-lines works on valid data + self.runCmd("-symbol-list-lines main.cpp") + self.expect("\^done,lines=\[\{pc=\"0x0*%x\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"\d+\"\})+\]" % (addr, line)) + + # Test that -symbol-list-lines doesn't include lines from other sources + # by checking the first and last line, and making sure the other lines + # are between 30 and 39. + sline = line_number('symbol_list_lines_inline_test2.cpp', '// FUNC_gfunc2') + eline = line_number('symbol_list_lines_inline_test2.cpp', '// END_gfunc2') + self.runCmd("-symbol-list-lines symbol_list_lines_inline_test2.cpp") + self.expect("\^done,lines=\[\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"3\d\"\})*,\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"3\d\"\})*\]" % (sline, eline)) + ##FIXME: This doesn't work for symbol_list_lines_inline_test.cpp due to clang bug llvm.org/pr24716 (fixed in newer versions of clang) + ##sline = line_number('symbol_list_lines_inline_test.cpp', '// FUNC_gfunc') + ##eline = line_number('symbol_list_lines_inline_test.cpp', '// STRUCT_s') + ##self.runCmd("-symbol-list-lines symbol_list_lines_inline_test.cpp") + ##self.expect("\^done,lines=\[\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"3\d\"\})*,\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}\]" % (sline, eline)) + + # Test that -symbol-list-lines works on header files by checking the first + # and last line, and making sure the other lines are under 29. + sline = line_number('symbol_list_lines_inline_test.h', '// FUNC_ifunc') + eline = line_number('symbol_list_lines_inline_test.h', '// FUNC_mfunc') + self.runCmd("-symbol-list-lines symbol_list_lines_inline_test.h") + self.expect("\^done,lines=\[\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"\d\"\})*(,\{pc=\"0x[0-9a-f]+\",line=\"1\d\"\})*,\{pc=\"0x[0-9a-f]+\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"2\d\"\})*\]" % (sline, eline)) + + # Test that -symbol-list-lines fails when file doesn't exist + self.runCmd("-symbol-list-lines unknown_file") + self.expect("\^error,message=\"error: No source filenames matched 'unknown_file'\. \"") + + # Test that -symbol-list-lines fails when file is specified using relative path + self.runCmd("-symbol-list-lines ./main.cpp") + self.expect("\^error,message=\"error: No source filenames matched '\./main\.cpp'\. \"") + + # Test that -symbol-list-lines works when file is specified using absolute path + import os + path = os.path.join(os.getcwd(), "main.cpp") + self.runCmd("-symbol-list-lines \"%s\"" % path) + self.expect("\^done,lines=\[\{pc=\"0x0*%x\",line=\"%d\"\}(,\{pc=\"0x[0-9a-f]+\",line=\"\d+\"\})+\]" % (addr, line)) + + # Test that -symbol-list-lines fails when file doesn't exist + self.runCmd("-symbol-list-lines unknown_dir/main.cpp") + self.expect("\^error,message=\"error: No source filenames matched 'unknown_dir/main\.cpp'\. \"") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/main.cpp new file mode 100644 index 000000000000..6d725a5759b5 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/main.cpp @@ -0,0 +1,18 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +extern int j; +extern int gfunc(int i); +extern int gfunc2(int i); +int +main() +{ // FUNC_main + int i = gfunc(j) + gfunc2(j); + return i == 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.cpp new file mode 100644 index 000000000000..c432ba8c477c --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.cpp @@ -0,0 +1,39 @@ +// Skip lines so we can make sure we're not seeing any lines from +// symbol_list_lines_inline_test.h included in -symbol-list-lines +// symbol_list_lines_inline_test.cpp, by checking that all the lines +// are between 30 and 39. +// line 5 +// line 6 +// line 7 +// line 8 +// line 9 +// line 10 +// line 11 +// line 12 +// line 13 +// line 14 +// line 15 +// line 16 +// line 17 +// line 18 +// line 19 +// line 20 +// line 21 +// line 22 +// line 23 +// line 24 +// line 25 +// line 26 +// line 27 +// line 28 +// line 29 +#include "symbol_list_lines_inline_test.h" +int +gfunc(int i) +{ // FUNC_gfunc + return ns::ifunc(i); +} +namespace ns +{ +S s; // STRUCT_s +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.h b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.h new file mode 100644 index 000000000000..4b986dc69321 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test.h @@ -0,0 +1,24 @@ +namespace ns +{ +inline int +ifunc(int i) +{ // FUNC_ifunc + return i; +} +struct S +{ + int a; + int b; + S() + : a(3) + , b(4) + { + } + int + mfunc() + { // FUNC_mfunc + return a + b; + } +}; +extern S s; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test2.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test2.cpp new file mode 100644 index 000000000000..cfedf47ad6b1 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/symbol/symbol_list_lines_inline_test2.cpp @@ -0,0 +1,38 @@ +// Skip lines so we can make sure we're not seeing any lines from +// symbol_list_lines_inline_test.h included in -symbol-list-lines +// symbol_list_lines_inline_test2.cpp, by checking that all the lines +// are between 30 and 39. +// line 5 +// line 6 +// line 7 +// line 8 +// line 9 +// line 10 +// line 11 +// line 12 +// line 13 +// line 14 +// line 15 +// line 16 +// line 17 +// line 18 +// line 19 +// line 20 +// line 21 +// line 22 +// line 23 +// line 24 +// line 25 +// line 26 +// line 27 +// line 28 +// line 29 +#include "symbol_list_lines_inline_test.h" +int j = 2; +int +gfunc2(int i) +{ // FUNC_gfunc2 + i += ns::s.mfunc(); + i += ns::ifunc(i); + return i == 0; // END_gfunc2 +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py b/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py new file mode 100644 index 000000000000..f8a6743eb16d --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py @@ -0,0 +1,80 @@ +""" +Test that the lldb-mi driver understands MI command syntax. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiSyntaxTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_tokens(self): + """Test that 'lldb-mi --interpreter' prints command tokens.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("000-file-exec-and-symbols %s" % self.myexe) + self.expect("000\^done") + + # Run to main + self.runCmd("100000001-break-insert -f main") + self.expect("100000001\^done,bkpt={number=\"1\"") + self.runCmd("2-exec-run") + self.expect("2\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Exit + self.runCmd("0000000000000000000003-exec-continue") + self.expect("0000000000000000000003\^running") + self.expect("\*stopped,reason=\"exited-normally\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_specialchars(self): + """Test that 'lldb-mi --interpreter' handles complicated strings.""" + + # Create an alias for myexe + complicated_myexe = "C--mpl-x file's`s @#$%^&*()_+-={}[]| name" + os.symlink(self.myexe, complicated_myexe) + self.addTearDownHook(lambda: os.unlink(complicated_myexe)) + + self.spawnLldbMi(args = "\"%s\"" % complicated_myexe) + + # Test that the executable was loaded + self.expect("-file-exec-and-symbols \"%s\"" % complicated_myexe, exactly = True) + self.expect("\^done") + + # Check that it was loaded correctly + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFailureLinux # Failing in ~6/600 dosep runs (build 3120-3122) + def test_lldbmi_process_output(self): + """Test that 'lldb-mi --interpreter' wraps process output correctly.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run + self.runCmd("-exec-run") + self.expect("\^running") + + # Test that a process output is wrapped correctly + self.expect("\@\"'\\\\r\\\\n\"") + self.expect("\@\"` - it's \\\\\\\\n\\\\x12\\\\\"\\\\\\\\\\\\\"") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/main.cpp new file mode 100644 index 000000000000..d2935b08f872 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/main.cpp @@ -0,0 +1,17 @@ +//===-- 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 + +int +main(int argc, char const *argv[]) +{ + printf("'\n` - it's \\n\x12\"\\\""); + return 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/target/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/target/Makefile new file mode 100644 index 000000000000..b2550fe780d9 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/target/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := test_attach.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/target/TestMiTarget.py b/packages/Python/lldbsuite/test/tools/lldb-mi/target/TestMiTarget.py new file mode 100644 index 000000000000..73ef913691cf --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/target/TestMiTarget.py @@ -0,0 +1,125 @@ +""" +Test lldb-mi -target-xxx commands. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiTargetTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # cannot attach to process on linux + def test_lldbmi_target_attach_wait_for(self): + """Test that 'lldb-mi --interpreter' works for -target-attach -n --waitfor.""" + + # Build target executable with unique name + exeName = self.testMethodName + d = {'EXE': exeName} + self.buildProgram("test_attach.cpp", exeName) + self.addTearDownCleanup(dictionary=d) + + self.spawnLldbMi(args = None) + + # Load executable + # FIXME: -file-exec-and-sybmols is not required for target attach, but the test will not pass without this + self.runCmd("-file-exec-and-symbols %s" % exeName) + self.expect("\^done") + + # Set up attach + self.runCmd("-target-attach -n %s --waitfor" % exeName) + time.sleep(4) # Give attach time to setup + + # Start target process + self.spawnSubprocess(os.path.join(os.path.dirname(__file__), exeName)); + self.addTearDownHook(self.cleanupSubprocesses) + self.expect("\^done") + + # Set breakpoint on printf + line = line_number('test_attach.cpp', '// BP_i++') + self.runCmd("-break-insert -f test_attach.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + + # Continue to breakpoint + self.runCmd("-exec-continue") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Detach + self.runCmd("-target-detach") + self.expect("\^done") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # cannot attach to process on linux + def test_lldbmi_target_attach_name(self): + """Test that 'lldb-mi --interpreter' works for -target-attach -n .""" + + # Build target executable with unique name + exeName = self.testMethodName + d = {'EXE': exeName} + self.buildProgram("test_attach.cpp", exeName) + self.addTearDownCleanup(dictionary=d) + + # Start target process + targetProcess = self.spawnSubprocess(os.path.join(os.path.dirname(__file__), exeName)); + self.addTearDownHook(self.cleanupSubprocesses) + + self.spawnLldbMi(args = None) + + # Set up atatch + self.runCmd("-target-attach -n %s" % exeName) + self.expect("\^done") + + # Set breakpoint on printf + line = line_number('test_attach.cpp', '// BP_i++') + self.runCmd("-break-insert -f test_attach.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + + # Continue to breakpoint + self.runCmd("-exec-continue") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Detach + self.runCmd("-target-detach") + self.expect("\^done") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # cannot attach to process on linux + def test_lldbmi_target_attach_pid(self): + """Test that 'lldb-mi --interpreter' works for -target-attach .""" + + # Build target executable with unique name + exeName = self.testMethodName + d = {'EXE': exeName} + self.buildProgram("test_attach.cpp", exeName) + self.addTearDownCleanup(dictionary=d) + + # Start target process + targetProcess = self.spawnSubprocess(os.path.join(os.path.dirname(__file__), exeName)); + self.addTearDownHook(self.cleanupSubprocesses) + + self.spawnLldbMi(args = None) + + # Set up atatch + self.runCmd("-target-attach %d" % targetProcess.pid) + self.expect("\^done") + + # Set breakpoint on printf + line = line_number('test_attach.cpp', '// BP_i++') + self.runCmd("-break-insert -f test_attach.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + + # Continue to breakpoint + self.runCmd("-exec-continue") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Detach + self.runCmd("-target-detach") + self.expect("\^done") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/target/test_attach.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/target/test_attach.cpp new file mode 100644 index 000000000000..caaf33a46faf --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/target/test_attach.cpp @@ -0,0 +1,21 @@ +//===-- 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 + +int +main(int argc, char const *argv[]) +{ + int i = 0; + for (;;) + { + i++; // BP_i++ + } + return 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/variable/Makefile b/packages/Python/lldbsuite/test/tools/lldb-mi/variable/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/variable/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/variable/TestMiGdbSetShowPrint.py b/packages/Python/lldbsuite/test/tools/lldb-mi/variable/TestMiGdbSetShowPrint.py new file mode 100644 index 000000000000..067df6408bd4 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/variable/TestMiGdbSetShowPrint.py @@ -0,0 +1,227 @@ +#coding=utf8 +""" +Test lldb-mi -gdb-set and -gdb-show commands for 'print option-name'. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiGdbSetShowTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + # evaluates array when char-array-as-string is off + def eval_and_check_array(self, var, typ, length): + self.runCmd("-var-create - * %s" % var) + self.expect('\^done,name="var\d+",numchild="%d",value="\[%d\]",type="%s \[%d\]",thread-id="1",has_more="0"' % (length, length, typ, length)) + + # evaluates any type which can be represented as string of characters + def eval_and_match_string(self, var, value, typ): + value=value.replace("\\", "\\\\").replace("\"", "\\\"") + self.runCmd("-var-create - * " + var) + self.expect('\^done,name="var\d+",numchild="[0-9]+",value="%s",type="%s",thread-id="1",has_more="0"' % (value, typ)) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_gdb_set_show_print_char_array_as_string(self): + """Test that 'lldb-mi --interpreter' can print array of chars as string.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to BP_gdb_set_show_print_char_array_as_string_test + line = line_number('main.cpp', '// BP_gdb_set_show_print_char_array_as_string_test') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that default print char-array-as-string value is "off" + self.runCmd("-gdb-show print char-array-as-string") + self.expect("\^done,value=\"off\"") + + # Test that a char* is expanded to string when print char-array-as-string is "off" + self.eval_and_match_string("cp", r'0x[0-9a-f]+ \"\\t\\\"hello\\\"\\n\"', r'const char \*') + + # Test that a char[] isn't expanded to string when print char-array-as-string is "off" + self.eval_and_check_array("ca", "const char", 10); + + # Test that a char16_t* is expanded to string when print char-array-as-string is "off" + self.eval_and_match_string("u16p", r'0x[0-9a-f]+ u\"\\t\\\"hello\\\"\\n\"', r'const char16_t \*') + + # Test that a char16_t[] isn't expanded to string when print char-array-as-string is "off" + self.eval_and_check_array("u16a", "const char16_t", 10); + + # Test that a char32_t* is expanded to string when print char-array-as-string is "off" + self.eval_and_match_string("u32p", r'0x[0-9a-f]+ U\"\\t\\\"hello\\\"\\n\"', r'const char32_t \*') + + # Test that a char32_t[] isn't expanded to string when print char-array-as-string is "off" + self.eval_and_check_array("u32a", "const char32_t", 10); + + # Test that -gdb-set can set print char-array-as-string flag + self.runCmd("-gdb-set print char-array-as-string on") + self.expect("\^done") + self.runCmd("-gdb-set print char-array-as-string 1") + self.expect("\^done") + self.runCmd("-gdb-show print char-array-as-string") + self.expect("\^done,value=\"on\"") + + # Test that a char* with escape chars is expanded to string when print char-array-as-string is "on" + self.eval_and_match_string("cp", r'0x[0-9a-f]+ \"\\t\\\"hello\\\"\\n\"', r'const char \*') + + # Test that a char[] with escape chars is expanded to string when print char-array-as-string is "on" + self.eval_and_match_string("ca", r'\"\\t\\\"hello\\\"\\n\"', r'const char \[10\]') + + # Test that a char16_t* with escape chars is expanded to string when print char-array-as-string is "on" + self.eval_and_match_string("u16p", r'0x[0-9a-f]+ u\"\\t\\\"hello\\\"\\n\"', r'const char16_t \*') + + # Test that a char16_t[] with escape chars is expanded to string when print char-array-as-string is "on" + self.eval_and_match_string("u16a", r'u\"\\t\\\"hello\\\"\\n\"', r'const char16_t \[10\]') + + # Test that a char32_t* with escape chars is expanded to string when print char-array-as-string is "on" + self.eval_and_match_string("u32p", r'0x[0-9a-f]+ U\"\\t\\\"hello\\\"\\n\"', r'const char32_t \*') + + # Test that a char32_t[] with escape chars is expanded to string when print char-array-as-string is "on" + self.eval_and_match_string("u32a", r'U\"\\t\\\"hello\\\"\\n\"', r'const char32_t \[10\]') + + # Test russian unicode strings + self.eval_and_match_string("u16p_rus", r'0x[0-9a-f]+ u\"\\\\Аламо-сквер\"', r'const char16_t \*') + self.eval_and_match_string("u16a_rus", r'u\"\\\\Бейвью\"', r'const char16_t \[8\]') + self.eval_and_match_string("u32p_rus", r'0x[0-9a-f]+ U\"\\\\Чайнатаун\"', r'const char32_t \*') + self.eval_and_match_string("u32a_rus", r'U\"\\\\Догпатч\"', r'const char32_t \[9\]') + + # Test that -gdb-set print char-array-as-string fails if "on"/"off" isn't specified + self.runCmd("-gdb-set print char-array-as-string") + self.expect("\^error,msg=\"The request ''print' expects option-name and \"on\" or \"off\"' failed.\"") + + # Test that -gdb-set print char-array-as-string fails when option is unknown + self.runCmd("-gdb-set print char-array-as-string unknown") + self.expect("\^error,msg=\"The request ''print' expects option-name and \"on\" or \"off\"' failed.\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi working on Windows + @expectedFailureGcc("https://llvm.org/bugs/show_bug.cgi?id=23357") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_gdb_set_show_print_expand_aggregates(self): + """Test that 'lldb-mi --interpreter' can expand aggregates everywhere.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to BP_gdb_set_show_print_expand_aggregates + line = line_number('main.cpp', '// BP_gdb_set_show_print_expand_aggregates') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that default print expand-aggregates value is "off" + self.runCmd("-gdb-show print expand-aggregates") + self.expect("\^done,value=\"off\"") + + # Test that composite type isn't expanded when print expand-aggregates is "off" + self.runCmd("-var-create var1 * complx") + self.expect("\^done,name=\"var1\",numchild=\"3\",value=\"{\.\.\.}\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"") + + # Test that composite type[] isn't expanded when print expand-aggregates is "off" + self.eval_and_check_array("complx_array", "complex_type", 2) + + # Test that a struct with a char first element is not formatted as a string + self.runCmd("-var-create - * &nstr") + self.expect("\^done,name=\"var\d+\",numchild=\"2\",value=\"0x[0-9a-f]+\",type=\"not_str \*\",thread-id=\"1\",has_more=\"0\"") + + # Test that -gdb-set can set print expand-aggregates flag + self.runCmd("-gdb-set print expand-aggregates on") + self.expect("\^done") + self.runCmd("-gdb-set print expand-aggregates 1") + self.expect("\^done") + self.runCmd("-gdb-show print expand-aggregates") + self.expect("\^done,value=\"on\"") + + # Test that composite type is expanded when print expand-aggregates is "on" + self.runCmd("-var-create var3 * complx") + self.expect("\^done,name=\"var3\",numchild=\"3\",value=\"{i = 3, inner = {l = 3}, complex_ptr = 0x[0-9a-f]+}\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"") + + # Test that composite type[] is expanded when print expand-aggregates is "on" + self.runCmd("-var-create var4 * complx_array") + self.expect("\^done,name=\"var4\",numchild=\"2\",value=\"{\[0\] = {i = 4, inner = {l = 4}, complex_ptr = 0x[0-9a-f]+}, \[1\] = {i = 5, inner = {l = 5}, complex_ptr = 0x[0-9a-f]+}}\",type=\"complex_type \[2\]\",thread-id=\"1\",has_more=\"0\"") + + # Test that -gdb-set print expand-aggregates fails if "on"/"off" isn't specified + self.runCmd("-gdb-set print expand-aggregates") + self.expect("\^error,msg=\"The request ''print' expects option-name and \"on\" or \"off\"' failed.\"") + + # Test that -gdb-set print expand-aggregates fails when option is unknown + self.runCmd("-gdb-set print expand-aggregates unknown") + self.expect("\^error,msg=\"The request ''print' expects option-name and \"on\" or \"off\"' failed.\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi working on Windows + @expectedFailureGcc("https://llvm.org/bugs/show_bug.cgi?id=23357") + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_gdb_set_show_print_aggregate_field_names(self): + """Test that 'lldb-mi --interpreter' can expand aggregates everywhere.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to BP_gdb_set_show_print_aggregate_field_names + line = line_number('main.cpp', '// BP_gdb_set_show_print_aggregate_field_names') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that default print aggregatep-field-names value is "on" + self.runCmd("-gdb-show print aggregate-field-names") + self.expect("\^done,value=\"on\"") + + # Set print expand-aggregates flag to "on" + self.runCmd("-gdb-set print expand-aggregates on") + self.expect("\^done") + + # Test that composite type is expanded with field name when print aggregate-field-names is "on" + self.runCmd("-var-create var1 * complx") + self.expect("\^done,name=\"var1\",numchild=\"3\",value=\"{i = 3, inner = {l = 3}, complex_ptr = 0x[0-9a-f]+}\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"") + + # Test that composite type[] is expanded with field name when print aggregate-field-names is "on" + self.runCmd("-var-create var2 * complx_array") + self.expect("\^done,name=\"var2\",numchild=\"2\",value=\"{\[0\] = {i = 4, inner = {l = 4}, complex_ptr = 0x[0-9a-f]+}, \[1\] = {i = 5, inner = {l = 5}, complex_ptr = 0x[0-9a-f]+}}\",type=\"complex_type \[2\]\",thread-id=\"1\",has_more=\"0\"") + + # Test that -gdb-set can set print aggregate-field-names flag + self.runCmd("-gdb-set print aggregate-field-names off") + self.expect("\^done") + self.runCmd("-gdb-set print aggregate-field-names 0") + self.expect("\^done") + self.runCmd("-gdb-show print aggregate-field-names") + self.expect("\^done,value=\"off\"") + + # Test that composite type is expanded without field name when print aggregate-field-names is "off" + self.runCmd("-var-create var3 * complx") + self.expect("\^done,name=\"var3\",numchild=\"3\",value=\"{3,\{3\},0x[0-9a-f]+}\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"") + + # Test that composite type[] is expanded without field name when print aggregate-field-names is "off" + self.runCmd("-var-create var4 * complx_array") + self.expect("\^done,name=\"var4\",numchild=\"2\",value=\"{{4,\{4\},0x[0-9a-f]+},{5,\{5\},0x[0-9a-f]+}}\",type=\"complex_type \[2\]\",thread-id=\"1\",has_more=\"0\"") + + # Test that -gdb-set print aggregate-field-names fails if "on"/"off" isn't specified + self.runCmd("-gdb-set print aggregate-field-names") + self.expect("\^error,msg=\"The request ''print' expects option-name and \"on\" or \"off\"' failed.\"") + + # Test that -gdb-set print aggregate-field-names fails when option is unknown + self.runCmd("-gdb-set print aggregate-field-names unknown") + self.expect("\^error,msg=\"The request ''print' expects option-name and \"on\" or \"off\"' failed.\"") diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/variable/TestMiVar.py b/packages/Python/lldbsuite/test/tools/lldb-mi/variable/TestMiVar.py new file mode 100644 index 000000000000..26f3a9c63bdc --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/variable/TestMiVar.py @@ -0,0 +1,396 @@ +""" +Test lldb-mi -var-xxx commands. +""" + +from __future__ import print_function + + + +import lldbmi_testcase +from lldbsuite.test.lldbtest import * + +class MiVarTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_eval(self): + """Test that 'lldb-mi --interpreter' works for evaluating.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to program return + line = line_number('main.cpp', '// BP_return') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Print non-existant variable + self.runCmd("-var-create var1 * undef") + self.expect("\^error,msg=\"error: error: use of undeclared identifier \'undef\'\\\\nerror: 1 errors parsing expression\\\\n\"") + self.runCmd("-data-evaluate-expression undef") + self.expect("\^error,msg=\"Could not evaluate expression\"") + + # Print global "g_MyVar", modify, delete and create again + self.runCmd("-data-evaluate-expression g_MyVar") + self.expect("\^done,value=\"3\"") + self.runCmd("-var-create var2 * g_MyVar") + self.expect("\^done,name=\"var2\",numchild=\"0\",value=\"3\",type=\"int\",thread-id=\"1\",has_more=\"0\"") + self.runCmd("-var-evaluate-expression var2") + self.expect("\^done,value=\"3\"") + self.runCmd("-var-show-attributes var2") + self.expect("\^done,status=\"editable\"") + self.runCmd("-var-list-children var2") + self.expect("\^done,numchild=\"0\",has_more=\"0\"") + # Ensure -var-list-children also works with quotes + self.runCmd("-var-list-children \"var2\"") + self.expect("\^done,numchild=\"0\",has_more=\"0\"") + self.runCmd("-data-evaluate-expression \"g_MyVar=30\"") + self.expect("\^done,value=\"30\"") + self.runCmd("-var-update --all-values var2") + #self.expect("\^done,changelist=\[\{name=\"var2\",value=\"30\",in_scope=\"true\",type_changed=\"false\",has_more=\"0\"\}\]") #FIXME -var-update doesn't work + self.runCmd("-var-delete var2") + self.expect("\^done") + self.runCmd("-var-create var2 * g_MyVar") + self.expect("\^done,name=\"var2\",numchild=\"0\",value=\"30\",type=\"int\",thread-id=\"1\",has_more=\"0\"") + + # Print static "s_MyVar", modify, delete and create again + self.runCmd("-data-evaluate-expression s_MyVar") + self.expect("\^done,value=\"30\"") + self.runCmd("-var-create var3 * s_MyVar") + self.expect("\^done,name=\"var3\",numchild=\"0\",value=\"30\",type=\"int\",thread-id=\"1\",has_more=\"0\"") + self.runCmd("-var-evaluate-expression var3") + self.expect("\^done,value=\"30\"") + self.runCmd("-var-show-attributes var3") + self.expect("\^done,status=\"editable\"") + self.runCmd("-var-list-children var3") + self.expect("\^done,numchild=\"0\",has_more=\"0\"") + self.runCmd("-data-evaluate-expression \"s_MyVar=3\"") + self.expect("\^done,value=\"3\"") + self.runCmd("-var-update --all-values var3") + #self.expect("\^done,changelist=\[\{name=\"var3\",value=\"3\",in_scope=\"true\",type_changed=\"false\",has_more=\"0\"\}\]") #FIXME -var-update doesn't work + self.runCmd("-var-delete var3") + self.expect("\^done") + self.runCmd("-var-create var3 * s_MyVar") + self.expect("\^done,name=\"var3\",numchild=\"0\",value=\"3\",type=\"int\",thread-id=\"1\",has_more=\"0\"") + + # Print local "b", modify, delete and create again + self.runCmd("-data-evaluate-expression b") + self.expect("\^done,value=\"20\"") + self.runCmd("-var-create var4 * b") + self.expect("\^done,name=\"var4\",numchild=\"0\",value=\"20\",type=\"int\",thread-id=\"1\",has_more=\"0\"") + self.runCmd("-var-evaluate-expression var4") + self.expect("\^done,value=\"20\"") + self.runCmd("-var-show-attributes var4") + self.expect("\^done,status=\"editable\"") + self.runCmd("-var-list-children var4") + self.expect("\^done,numchild=\"0\",has_more=\"0\"") + self.runCmd("-data-evaluate-expression \"b=2\"") + self.expect("\^done,value=\"2\"") + self.runCmd("-var-update --all-values var4") + #self.expect("\^done,changelist=\[\{name=\"var4\",value=\"2\",in_scope=\"true\",type_changed=\"false\",has_more=\"0\"\}\]") #FIXME -var-update doesn't work + self.runCmd("-var-delete var4") + self.expect("\^done") + self.runCmd("-var-create var4 * b") + self.expect("\^done,name=\"var4\",numchild=\"0\",value=\"2\",type=\"int\",thread-id=\"1\",has_more=\"0\"") + + # Print temp "a + b" + self.runCmd("-data-evaluate-expression \"a + b\"") + self.expect("\^done,value=\"12\"") + self.runCmd("-var-create var5 * \"a + b\"") + self.expect("\^done,name=\"var5\",numchild=\"0\",value=\"12\",type=\"int\",thread-id=\"1\",has_more=\"0\"") + self.runCmd("-var-evaluate-expression var5") + self.expect("\^done,value=\"12\"") + self.runCmd("-var-show-attributes var5") + self.expect("\^done,status=\"editable\"") #FIXME editable or not? + self.runCmd("-var-list-children var5") + self.expect("\^done,numchild=\"0\",has_more=\"0\"") + + # Print argument "argv[0]" + self.runCmd("-data-evaluate-expression \"argv[0]\"") + self.expect("\^done,value=\"0x[0-9a-f]+ \\\\\\\".*?%s\\\\\\\"\"" % self.myexe) + self.runCmd("-var-create var6 * \"argv[0]\"") + self.expect("\^done,name=\"var6\",numchild=\"1\",value=\"0x[0-9a-f]+ \\\\\\\".*?%s\\\\\\\"\",type=\"const char \*\",thread-id=\"1\",has_more=\"0\"" % self.myexe) + self.runCmd("-var-evaluate-expression var6") + self.expect("\^done,value=\"0x[0-9a-f]+ \\\\\\\".*?%s\\\\\\\"\"" % self.myexe) + self.runCmd("-var-show-attributes var6") + self.expect("\^done,status=\"editable\"") + self.runCmd("-var-list-children --all-values var6") + # FIXME: The name below is not correct. It should be "var.*argv[0]". + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var6\.\*\$[0-9]+\",exp=\"\*\$[0-9]+\",numchild=\"0\",type=\"const char\",thread-id=\"4294967295\",value=\"47 '/'\",has_more=\"0\"\}\],has_more=\"0\"") #FIXME -var-list-children shows invalid thread-id + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_var_update(self): + """Test that 'lldb-mi --interpreter' works for -var-update.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to BP_var_update_test_init + line = line_number('main.cpp', '// BP_var_update_test_init') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Setup variables + self.runCmd("-var-create var_l * l") + self.expect("\^done,name=\"var_l\",numchild=\"0\",value=\"1\",type=\"long\",thread-id=\"1\",has_more=\"0\"") + self.runCmd("-var-create var_complx * complx") + self.expect("\^done,name=\"var_complx\",numchild=\"3\",value=\"\{\.\.\.\}\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"") + self.runCmd("-var-create var_complx_array * complx_array") + self.expect("\^done,name=\"var_complx_array\",numchild=\"2\",value=\"\[2\]\",type=\"complex_type \[2\]\",thread-id=\"1\",has_more=\"0\"") + + # Go to BP_var_update_test_l + line = line_number('main.cpp', '// BP_var_update_test_l') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"2\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that var_l was updated + self.runCmd("-var-update --all-values var_l") + self.expect("\^done,changelist=\[\{name=\"var_l\",value=\"0\",in_scope=\"true\",type_changed=\"false\",has_more=\"0\"\}\]") + + # Go to BP_var_update_test_complx + line = line_number('main.cpp', '// BP_var_update_test_complx') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"3\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that var_complx was updated + self.runCmd("-var-update --all-values var_complx") + self.expect("\^done,changelist=\[\{name=\"var_complx\",value=\"\{\.\.\.\}\",in_scope=\"true\",type_changed=\"false\",has_more=\"0\"\}\]") + + # Go to BP_var_update_test_complx_array + line = line_number('main.cpp', '// BP_var_update_test_complx_array') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"4\"") + self.runCmd("-exec-continue") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test that var_complex_array was updated + self.runCmd("-var-update --all-values var_complx_array") + self.expect("\^done,changelist=\[\{name=\"var_complx_array\",value=\"\[2\]\",in_scope=\"true\",type_changed=\"false\",has_more=\"0\"\}\]") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + def test_lldbmi_var_create_register(self): + """Test that 'lldb-mi --interpreter' works for -var-create $regname.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Find name of register 0 + self.runCmd("-data-list-register-names 0") + self.expect("\^done,register-names=\[\".+?\"\]") + register_name = self.child.after.split("\"")[1] + + # Create variable for register 0 + # Note that message is different in Darwin and Linux: + # Darwin: "^done,name=\"var_reg\",numchild=\"0\",value=\"0x[0-9a-f]+\",type=\"unsigned long\",thread-id=\"1\",has_more=\"0\" + # Linux: "^done,name=\"var_reg\",numchild=\"0\",value=\"0x[0-9a-f]+\",type=\"unsigned int\",thread-id=\"1\",has_more=\"0\" + self.runCmd("-var-create var_reg * $%s" % register_name) + self.expect("\^done,name=\"var_reg\",numchild=\"0\",value=\"0x[0-9a-f]+\",type=\"unsigned (long|int)\",thread-id=\"1\",has_more=\"0\"") + + # Assign value to variable + self.runCmd("-var-assign var_reg \"6\"") + #FIXME: the output has different format for 32bit and 64bit values + self.expect("\^done,value=\"0x0*?6\"") + + # Assert register 0 updated + self.runCmd("-data-list-register-values d 0") + self.expect("\^done,register-values=\[{number=\"0\",value=\"6\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_var_list_children(self): + """Test that 'lldb-mi --interpreter' works for -var-list-children.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to BP_var_list_children_test + line = line_number('main.cpp', '// BP_var_list_children_test') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Create variable + self.runCmd("-var-create var_complx * complx") + self.expect("\^done,name=\"var_complx\",numchild=\"3\",value=\"\{\.\.\.\}\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"") + self.runCmd("-var-create var_complx_array * complx_array") + self.expect("\^done,name=\"var_complx_array\",numchild=\"2\",value=\"\[2\]\",type=\"complex_type \[2\]\",thread-id=\"1\",has_more=\"0\"") + self.runCmd("-var-create var_pcomplx * pcomplx") + self.expect("\^done,name=\"var_pcomplx\",numchild=\"2\",value=\"\{\.\.\.\}\",type=\"pcomplex_type\",thread-id=\"1\",has_more=\"0\"") + + # Test that -var-evaluate-expression can evaluate the children of created varobj + self.runCmd("-var-list-children var_complx") + self.runCmd("-var-evaluate-expression var_complx.i") + self.expect("\^done,value=\"3\"") + self.runCmd("-var-list-children var_complx_array") + self.runCmd("-var-evaluate-expression var_complx_array.[0]") + self.expect("\^done,value=\"\{...\}\"") + self.runCmd("-var-list-children var_pcomplx") + self.runCmd("-var-evaluate-expression var_pcomplx.complex_type") + self.expect("\^done,value=\"\{...\}\"") + + # Test that -var-list-children lists empty children if range is empty + # (and that print-values is optional) + self.runCmd("-var-list-children var_complx 0 0") + self.expect("\^done,numchild=\"0\",has_more=\"1\"") + self.runCmd("-var-list-children var_complx 99 0") + self.expect("\^done,numchild=\"0\",has_more=\"1\"") + self.runCmd("-var-list-children var_complx 99 3") + self.expect("\^done,numchild=\"0\",has_more=\"0\"") + + # Test that -var-list-children lists all children with their values + # (and that from and to are optional) + self.runCmd("-var-list-children --all-values var_complx") + self.expect("\^done,numchild=\"3\",children=\[child=\{name=\"var_complx\.i\",exp=\"i\",numchild=\"0\",type=\"int\",thread-id=\"1\",value=\"3\",has_more=\"0\"\},child=\{name=\"var_complx\.inner\",exp=\"inner\",numchild=\"1\",type=\"complex_type::\(anonymous struct\)\",thread-id=\"1\",value=\"\{\.\.\.\}\",has_more=\"0\"\},child=\{name=\"var_complx\.complex_ptr\",exp=\"complex_ptr\",numchild=\"3\",type=\"complex_type \*\",thread-id=\"1\",value=\"0x[0-9a-f]+\",has_more=\"0\"\}\],has_more=\"0\"") + self.runCmd("-var-list-children --simple-values var_complx_array") + self.expect("\^done,numchild=\"2\",children=\[child=\{name=\"var_complx_array\.\[0\]\",exp=\"\[0\]\",numchild=\"3\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"\},child=\{name=\"var_complx_array\.\[1\]\",exp=\"\[1\]\",numchild=\"3\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"\}\],has_more=\"0\"") + self.runCmd("-var-list-children 0 var_pcomplx") + self.expect("\^done,numchild=\"2\",children=\[child=\{name=\"var_pcomplx\.complex_type\",exp=\"complex_type\",numchild=\"3\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"\},child={name=\"var_pcomplx\.complx\",exp=\"complx\",numchild=\"3\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"\}\],has_more=\"0\"") + + # Test that -var-list-children lists children without values + self.runCmd("-var-list-children 0 var_complx 0 1") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_complx\.i\",exp=\"i\",numchild=\"0\",type=\"int\",thread-id=\"1\",has_more=\"0\"\}\],has_more=\"1\"") + self.runCmd("-var-list-children --no-values var_complx 0 1") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_complx\.i\",exp=\"i\",numchild=\"0\",type=\"int\",thread-id=\"1\",has_more=\"0\"\}\],has_more=\"1\"") + self.runCmd("-var-list-children --no-values var_complx_array 0 1") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_complx_array\.\[0\]\",exp=\"\[0\]\",numchild=\"3\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"\}\],has_more=\"1\"") + self.runCmd("-var-list-children --no-values var_pcomplx 0 1") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_pcomplx\.complex_type\",exp=\"complex_type\",numchild=\"3\",type=\"complex_type\",thread-id=\"1\",has_more=\"0\"\}\],has_more=\"1\"") + + # Test that -var-list-children lists children with all values + self.runCmd("-var-list-children 1 var_complx 1 2") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_complx\.inner\",exp=\"inner\",numchild=\"1\",type=\"complex_type::\(anonymous struct\)\",thread-id=\"1\",value=\"\{\.\.\.\}\",has_more=\"0\"\}\],has_more=\"1\"") + self.runCmd("-var-list-children --all-values var_complx 1 2") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_complx\.inner\",exp=\"inner\",numchild=\"1\",type=\"complex_type::\(anonymous struct\)\",thread-id=\"1\",value=\"\{\.\.\.\}\",has_more=\"0\"\}\],has_more=\"1\"") + self.runCmd("-var-list-children --all-values var_complx_array 1 2") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_complx_array\.\[1\]\",exp=\"\[1\]\",numchild=\"3\",type=\"complex_type\",thread-id=\"1\",value=\"\{\.\.\.\}\",has_more=\"0\"\}\],has_more=\"0\"") + self.runCmd("-var-list-children --all-values var_pcomplx 1 2") + self.expect("\^done,numchild=\"1\",children=\[child={name=\"var_pcomplx\.complx\",exp=\"complx\",numchild=\"3\",type=\"complex_type\",thread-id=\"1\",value=\"\{\.\.\.\}\",has_more=\"0\"\}\],has_more=\"0\"") + + # Test that -var-list-children lists children with simple values + self.runCmd("-var-list-children 2 var_complx 2 4") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_complx\.complex_ptr\",exp=\"complex_ptr\",numchild=\"3\",type=\"complex_type \*\",thread-id=\"1\",has_more=\"0\"\}\],has_more=\"0\"") + self.runCmd("-var-list-children --simple-values var_complx 2 4") + self.expect("\^done,numchild=\"1\",children=\[child=\{name=\"var_complx\.complex_ptr\",exp=\"complex_ptr\",numchild=\"3\",type=\"complex_type \*\",thread-id=\"1\",has_more=\"0\"\}\],has_more=\"0\"") + self.runCmd("-var-list-children --simple-values var_complx_array 2 4") + self.expect("\^done,numchild=\"0\",has_more=\"0\"") + self.runCmd("-var-list-children --simple-values var_pcomplx 2 4") + self.expect("\^done,numchild=\"0\",has_more=\"0\"") + + # Test that an invalid from is handled + # FIXME: -1 is treated as unsigned int + self.runCmd("-var-list-children 0 var_complx -1 0") + #self.expect("\^error,msg=\"Command 'var-list-children'\. Variable children range invalid\"") + + # Test that an invalid to is handled + # FIXME: -1 is treated as unsigned int + self.runCmd("-var-list-children 0 var_complx 0 -1") + #self.expect("\^error,msg=\"Command 'var-list-children'\. Variable children range invalid\"") + + # Test that a missing low-frame or high-frame is handled + self.runCmd("-var-list-children 0 var_complx 0") + self.expect("\^error,msg=\"Command 'var-list-children'. Variable children range invalid\"") + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_var_create_for_stl_types(self): + """Test that 'lldb-mi --interpreter' print summary for STL types.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to BP_gdb_set_show_print_char_array_as_string_test + line = line_number('main.cpp', '// BP_cpp_stl_types_test') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Test for std::string + self.runCmd("-var-create - * std_string") + self.expect('\^done,name="var\d+",numchild="[0-9]+",value="\\\\"hello\\\\"",type="std::[\S]*?string",thread-id="1",has_more="0"') + + @skipIfWindows #llvm.org/pr24452: Get lldb-mi working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots + def test_lldbmi_var_create_for_unnamed_objects(self): + """Test that 'lldb-mi --interpreter' can expand unnamed structures and unions.""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to breakpoint + line = line_number('main.cpp', '// BP_unnamed_objects_test') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Evaluate struct_with_unions type and its children + self.runCmd("-var-create v0 * swu") + self.expect('\^done,name="v0",numchild="2",value="\{\.\.\.\}",type="struct_with_unions",thread-id="1",has_more="0"') + + self.runCmd("-var-list-children v0") + + # inspect the first unnamed union + self.runCmd("-var-list-children v0.$0") + self.runCmd("-var-evaluate-expression v0.$0.u_i") + self.expect('\^done,value="1"') + + # inspect the second unnamed union + self.runCmd("-var-list-children v0.$1") + self.runCmd("-var-evaluate-expression v0.$1.u1") + self.expect('\^done,value="-1"') + # inspect unnamed structure + self.runCmd("-var-list-children v0.$1.$1") + self.runCmd("-var-evaluate-expression v0.$1.$1.s1") + self.expect('\^done,value="-1"') + diff --git a/packages/Python/lldbsuite/test/tools/lldb-mi/variable/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-mi/variable/main.cpp new file mode 100644 index 000000000000..8c79539d4d85 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-mi/variable/main.cpp @@ -0,0 +1,152 @@ +//===-- 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 + +struct complex_type +{ + int i; + struct { long l; } inner; + complex_type *complex_ptr; +}; + +struct pcomplex_type : complex_type +{ + pcomplex_type(const complex_type &complx_base, const complex_type &complx_member) + : complex_type(complx_base), complx(complx_member) { } + complex_type complx; + static int si; +}; + +int pcomplex_type::si; + +struct struct_with_unions +{ + struct_with_unions(): u_i(1), u1(-1) {} + union + { + int u_i; + int u_j; + }; + union + { + int u1; + struct + { + short s1; + short s2; + }; + }; +}; + +void +var_update_test(void) +{ + long l = 1; + complex_type complx = { 3, { 3L }, &complx }; + complex_type complx_array[2] = { { 4, { 4L }, &complx_array[1] }, { 5, { 5 }, &complx_array[0] } }; + // BP_var_update_test_init + + l = 0; + // BP_var_update_test_l + + complx.inner.l = 2; + // BP_var_update_test_complx + + complx_array[1].inner.l = 4; + // BP_var_update_test_complx_array +} + +void +var_list_children_test(void) +{ + complex_type complx = { 3, { 3L }, &complx }; + complex_type complx_array[2] = { { 4, { 4L }, &complx_array[1] }, { 5, { 5 }, &complx_array[0] } }; + pcomplex_type pcomplx({ 6, { 6L }, &pcomplx}, { 7, { 7L }, &pcomplx}); + + // BP_var_list_children_test +} + +void +gdb_set_show_print_char_array_as_string_test(void) +{ + const char *cp = "\t\"hello\"\n"; + const char ca[] = "\t\"hello\"\n"; + const char16_t *u16p = u"\t\"hello\"\n"; + const char16_t u16a[] = u"\t\"hello\"\n"; + const char32_t *u32p = U"\t\"hello\"\n"; + const char32_t u32a[] = U"\t\"hello\"\n"; + + const char16_t* u16p_rus = u"\\Аламо-сквер"; + const char16_t u16a_rus[] = u"\\Бейвью"; + const char32_t* u32p_rus = U"\\Чайнатаун"; + const char32_t u32a_rus[] = U"\\Догпатч"; + + // BP_gdb_set_show_print_char_array_as_string_test +} + +void +cpp_stl_types_test(void) +{ + std::string std_string = "hello"; + // BP_cpp_stl_types_test +} + +void +unnamed_objects_test(void) +{ + struct_with_unions swu; + // BP_unnamed_objects_test +} + +struct not_str +{ + not_str(char _c, int _f) + : c(_c), f(_f) { } + char c; + int f; +}; + +void +gdb_set_show_print_expand_aggregates(void) +{ + complex_type complx = { 3, { 3L }, &complx }; + complex_type complx_array[2] = { { 4, { 4L }, &complx_array[1] }, { 5, { 5 }, &complx_array[0] } }; + not_str nstr('a', 0); + + // BP_gdb_set_show_print_expand_aggregates +} + +void +gdb_set_show_print_aggregate_field_names(void) +{ + complex_type complx = { 3, { 3L }, &complx }; + complex_type complx_array[2] = { { 4, { 4L }, &complx_array[1] }, { 5, { 5 }, &complx_array[0] } }; + + // BP_gdb_set_show_print_aggregate_field_names +} + +int g_MyVar = 3; +static int s_MyVar = 4; + +int +main(int argc, char const *argv[]) +{ + int a = 10, b = 20; + s_MyVar = a + b; + var_update_test(); + var_list_children_test(); + gdb_set_show_print_char_array_as_string_test(); + cpp_stl_types_test(); + unnamed_objects_test(); + gdb_set_show_print_expand_aggregates(); + gdb_set_show_print_aggregate_field_names(); + return 0; // BP_return +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/Makefile b/packages/Python/lldbsuite/test/tools/lldb-server/Makefile new file mode 100644 index 000000000000..6ae4e6624eef --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../make + +CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp +MAKE_DSYM :=NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGDBRemoteMemoryRead.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGDBRemoteMemoryRead.py new file mode 100644 index 000000000000..7b974e548a58 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGDBRemoteMemoryRead.py @@ -0,0 +1,41 @@ +""" +Tests the binary ($x) and hex ($m) memory read packets of the remote stub +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil +import binascii + + +class MemoryReadTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessPlatform(getDarwinOSTriples()+["linux"]) + def test_memory_read(self): + self.build() + exe = os.path.join (os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + lldbutil.run_break_set_by_symbol(self, "main") + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + self.assertEqual(process.GetState(), lldb.eStateStopped, "Process is stopped") + + pc = process.GetSelectedThread().GetSelectedFrame().GetPC() + for size in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]: + error = lldb.SBError() + memory = process.ReadMemory(pc, size, error) + self.assertTrue(error.Success()) + self.match("process plugin packet send x%x,%x" % (pc, size), ["response:", memory]) + self.match("process plugin packet send m%x,%x" % (pc, size), ["response:", binascii.hexlify(memory)]) + + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited, "Process exited") diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAttach.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAttach.py new file mode 100644 index 000000000000..ca96a9a837b9 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAttach.py @@ -0,0 +1,60 @@ +from __future__ import print_function + + + +import gdbremote_testcase +import lldbgdbserverutils + +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteAttach(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def attach_with_vAttach(self): + # Start the inferior, start the debug monitor, nothing is attached yet. + procs = self.prep_debug_monitor_and_inferior(inferior_args=["sleep:60"]) + self.assertIsNotNone(procs) + + # Make sure the target process has been launched. + inferior = procs.get("inferior") + self.assertIsNotNone(inferior) + self.assertTrue(inferior.pid > 0) + self.assertTrue(lldbgdbserverutils.process_is_running(inferior.pid, True)) + + # Add attach packets. + self.test_sequence.add_log_lines([ + # Do the attach. + "read packet: $vAttach;{:x}#00".format(inferior.pid), + # Expect a stop notification from the attach. + { "direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})[^#]*#[0-9a-fA-F]{2}$", "capture":{1:"stop_signal_hex"} }, + ], True) + self.add_process_info_collection_packets() + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info response + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + + # Ensure the process id matches what we expected. + pid_text = process_info.get('pid', None) + self.assertIsNotNone(pid_text) + reported_pid = int(pid_text, base=16) + self.assertEqual(reported_pid, inferior.pid) + + @debugserver_test + def test_attach_with_vAttach_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_attach_manually() + self.attach_with_vAttach() + + @llgs_test + def test_attach_with_vAttach_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_attach_manually() + self.attach_with_vAttach() diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAuxvSupport.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAuxvSupport.py new file mode 100644 index 000000000000..1ce5779e7897 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteAuxvSupport.py @@ -0,0 +1,201 @@ +from __future__ import print_function + + + +import gdbremote_testcase +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteAuxvSupport(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + AUXV_SUPPORT_FEATURE_NAME = "qXfer:auxv:read" + + def has_auxv_support(self): + inferior_args = ["message:main entered", "sleep:5"] + procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args) + + # Don't do anything until we match the launched inferior main entry output. + # Then immediately interrupt the process. + # This prevents auxv data being asked for before it's ready and leaves + # us in a stopped state. + self.test_sequence.add_log_lines([ + # Start the inferior... + "read packet: $c#63", + # ... match output.... + { "type":"output_match", "regex":r"^message:main entered\r\n$" }, + ], True) + # ... then interrupt. + self.add_interrupt_packets() + self.add_qSupported_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + features = self.parse_qSupported_response(context) + return self.AUXV_SUPPORT_FEATURE_NAME in features and features[self.AUXV_SUPPORT_FEATURE_NAME] == "+" + + def get_raw_auxv_data(self): + # Start up llgs and inferior, and check for auxv support. + if not self.has_auxv_support(): + self.skipTest("auxv data not supported") + + # Grab pointer size for target. We'll assume that is equivalent to an unsigned long on the target. + # Auxv is specified in terms of pairs of unsigned longs. + self.reset_test_sequence() + self.add_process_info_collection_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + proc_info = self.parse_process_info_response(context) + self.assertIsNotNone(proc_info) + self.assertTrue("ptrsize" in proc_info) + word_size = int(proc_info["ptrsize"]) + + OFFSET = 0 + LENGTH = 0x400 + + # Grab the auxv data. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + "read packet: $qXfer:auxv:read::{:x},{:x}:#00".format(OFFSET, LENGTH), + {"direction":"send", "regex":re.compile(r"^\$([^E])(.*)#[0-9a-fA-F]{2}$", re.MULTILINE|re.DOTALL), "capture":{1:"response_type", 2:"content_raw"} } + ], True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Ensure we end up with all auxv data in one packet. + # FIXME don't assume it all comes back in one packet. + self.assertEqual(context.get("response_type"), "l") + + # Decode binary data. + content_raw = context.get("content_raw") + self.assertIsNotNone(content_raw) + return (word_size, self.decode_gdbremote_binary(content_raw)) + + def supports_auxv(self): + # When non-auxv platforms support llgs, skip the test on platforms + # that don't support auxv. + self.assertTrue(self.has_auxv_support()) + + # + # We skip the "supports_auxv" test on debugserver. The rest of the tests + # appropriately skip the auxv tests if the support flag is not present + # in the qSupported response, so the debugserver test bits are still there + # in case debugserver code one day does have auxv support and thus those + # tests don't get skipped. + # + + @llgs_test + def test_supports_auxv_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.supports_auxv() + + def auxv_data_is_correct_size(self): + (word_size, auxv_data) = self.get_raw_auxv_data() + self.assertIsNotNone(auxv_data) + + # Ensure auxv data is a multiple of 2*word_size (there should be two unsigned long fields per auxv entry). + self.assertEqual(len(auxv_data) % (2*word_size), 0) + # print("auxv contains {} entries".format(len(auxv_data) / (2*word_size))) + + @debugserver_test + def test_auxv_data_is_correct_size_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.auxv_data_is_correct_size() + + @llgs_test + def test_auxv_data_is_correct_size_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.auxv_data_is_correct_size() + + def auxv_keys_look_valid(self): + (word_size, auxv_data) = self.get_raw_auxv_data() + self.assertIsNotNone(auxv_data) + + # Grab endian. + self.reset_test_sequence() + self.add_process_info_collection_packets() + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + endian = process_info.get("endian") + self.assertIsNotNone(endian) + + auxv_dict = self.build_auxv_dict(endian, word_size, auxv_data) + self.assertIsNotNone(auxv_dict) + + # Verify keys look reasonable. + for auxv_key in auxv_dict: + self.assertTrue(auxv_key >= 1) + self.assertTrue(auxv_key <= 1000) + # print("auxv dict: {}".format(auxv_dict)) + + @debugserver_test + def test_auxv_keys_look_valid_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.auxv_keys_look_valid() + + @llgs_test + def test_auxv_keys_look_valid_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.auxv_keys_look_valid() + + def auxv_chunked_reads_work(self): + # Verify that multiple smaller offset,length reads of auxv data + # return the same data as a single larger read. + + # Grab the auxv data with a single large read here. + (word_size, auxv_data) = self.get_raw_auxv_data() + self.assertIsNotNone(auxv_data) + + # Grab endian. + self.reset_test_sequence() + self.add_process_info_collection_packets() + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + endian = process_info.get("endian") + self.assertIsNotNone(endian) + + auxv_dict = self.build_auxv_dict(endian, word_size, auxv_data) + self.assertIsNotNone(auxv_dict) + + iterated_auxv_data = self.read_binary_data_in_chunks("qXfer:auxv:read::", 2*word_size) + self.assertIsNotNone(iterated_auxv_data) + + auxv_dict_iterated = self.build_auxv_dict(endian, word_size, iterated_auxv_data) + self.assertIsNotNone(auxv_dict_iterated) + + # Verify both types of data collection returned same content. + self.assertEqual(auxv_dict_iterated, auxv_dict) + + @debugserver_test + def test_auxv_chunked_reads_work_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.auxv_chunked_reads_work() + + @llgs_test + def test_auxv_chunked_reads_work_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.auxv_chunked_reads_work() diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteExpeditedRegisters.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteExpeditedRegisters.py new file mode 100644 index 000000000000..6535ce40475d --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteExpeditedRegisters.py @@ -0,0 +1,144 @@ +from __future__ import print_function + + + +import gdbremote_testcase +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteExpeditedRegisters(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def gather_expedited_registers(self): + # Setup the stub and set the gdb remote command stream. + procs = self.prep_debug_monitor_and_inferior(inferior_args=["sleep:2"]) + self.test_sequence.add_log_lines([ + # Start up the inferior. + "read packet: $c#63", + # Immediately tell it to stop. We want to see what it reports. + "read packet: {}".format(chr(3)), + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result", 2:"key_vals_text"} }, + ], True) + + # Run the gdb remote command stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Pull out expedited registers. + key_vals_text = context.get("key_vals_text") + self.assertIsNotNone(key_vals_text) + + expedited_registers = self.extract_registers_from_stop_notification(key_vals_text) + self.assertIsNotNone(expedited_registers) + + return expedited_registers + + def stop_notification_contains_generic_register(self, generic_register_name): + # Generate a stop reply, parse out expedited registers from stop notification. + expedited_registers = self.gather_expedited_registers() + self.assertIsNotNone(expedited_registers) + self.assertTrue(len(expedited_registers) > 0) + + # Gather target register infos. + reg_infos = self.gather_register_infos() + + # Find the generic register. + reg_info = self.find_generic_register_with_name(reg_infos, generic_register_name) + self.assertIsNotNone(reg_info) + + # Ensure the expedited registers contained it. + self.assertTrue(reg_info["lldb_register_index"] in expedited_registers) + # print("{} reg_info:{}".format(generic_register_name, reg_info)) + + def stop_notification_contains_any_registers(self): + # Generate a stop reply, parse out expedited registers from stop notification. + expedited_registers = self.gather_expedited_registers() + # Verify we have at least one expedited register. + self.assertTrue(len(expedited_registers) > 0) + + @debugserver_test + def test_stop_notification_contains_any_registers_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_any_registers() + + @llgs_test + def test_stop_notification_contains_any_registers_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_any_registers() + + def stop_notification_contains_no_duplicate_registers(self): + # Generate a stop reply, parse out expedited registers from stop notification. + expedited_registers = self.gather_expedited_registers() + # Verify no expedited register was specified multiple times. + for (reg_num, value) in list(expedited_registers.items()): + if (type(value) == list) and (len(value) > 0): + self.fail("expedited register number {} specified more than once ({} times)".format(reg_num, len(value))) + + @debugserver_test + def test_stop_notification_contains_no_duplicate_registers_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_no_duplicate_registers() + + @llgs_test + def test_stop_notification_contains_no_duplicate_registers_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_no_duplicate_registers() + + def stop_notification_contains_pc_register(self): + self.stop_notification_contains_generic_register("pc") + + @debugserver_test + def test_stop_notification_contains_pc_register_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_pc_register() + + @llgs_test + def test_stop_notification_contains_pc_register_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_pc_register() + + def stop_notification_contains_fp_register(self): + self.stop_notification_contains_generic_register("fp") + + @debugserver_test + def test_stop_notification_contains_fp_register_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_fp_register() + + @llgs_test + def test_stop_notification_contains_fp_register_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_fp_register() + + def stop_notification_contains_sp_register(self): + self.stop_notification_contains_generic_register("sp") + + @debugserver_test + def test_stop_notification_contains_sp_register_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_sp_register() + + @llgs_test + def test_stop_notification_contains_sp_register_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.stop_notification_contains_sp_register() diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteKill.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteKill.py new file mode 100644 index 000000000000..b253254c78ed --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteKill.py @@ -0,0 +1,53 @@ +from __future__ import print_function + + + +import gdbremote_testcase +import lldbgdbserverutils + +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteKill(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def attach_commandline_kill_after_initial_stop(self): + procs = self.prep_debug_monitor_and_inferior() + self.test_sequence.add_log_lines([ + "read packet: $k#6b", + {"direction":"send", "regex":r"^\$X[0-9a-fA-F]+([^#]*)#[0-9A-Fa-f]{2}" }, + ], True) + + if self.stub_sends_two_stop_notifications_on_kill: + # Add an expectation for a second X result for stubs that send two of these. + self.test_sequence.add_log_lines([ + {"direction":"send", "regex":r"^\$X[0-9a-fA-F]+([^#]*)#[0-9A-Fa-f]{2}" }, + ], True) + + self.expect_gdbremote_sequence() + + # Wait a moment for completed and now-detached inferior process to clear. + time.sleep(1) + + if not lldb.remote_platform: + # Process should be dead now. Reap results. + poll_result = procs["inferior"].poll() + self.assertIsNotNone(poll_result) + + # Where possible, verify at the system level that the process is not running. + self.assertFalse(lldbgdbserverutils.process_is_running(procs["inferior"].pid, False)) + + @debugserver_test + def test_attach_commandline_kill_after_initial_stop_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_attach() + self.attach_commandline_kill_after_initial_stop() + + @llgs_test + def test_attach_commandline_kill_after_initial_stop_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_attach() + self.attach_commandline_kill_after_initial_stop() + diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteProcessInfo.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteProcessInfo.py new file mode 100644 index 000000000000..a11167b87c25 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteProcessInfo.py @@ -0,0 +1,179 @@ +from __future__ import print_function + + + +import gdbremote_testcase +import lldbgdbserverutils +import sys + +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteProcessInfo(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def qProcessInfo_returns_running_process(self): + procs = self.prep_debug_monitor_and_inferior() + self.add_process_info_collection_packets() + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info response + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + + # Ensure the process id looks reasonable. + pid_text = process_info.get("pid") + self.assertIsNotNone(pid_text) + pid = int(pid_text, base=16) + self.assertNotEqual(0, pid) + + # If possible, verify that the process is running. + self.assertTrue(lldbgdbserverutils.process_is_running(pid, True)) + + @debugserver_test + def test_qProcessInfo_returns_running_process_debugserver(self): + self.init_debugserver_test() + self.build() + self.qProcessInfo_returns_running_process() + + @llgs_test + def test_qProcessInfo_returns_running_process_llgs(self): + self.init_llgs_test() + self.build() + self.qProcessInfo_returns_running_process() + + def attach_commandline_qProcessInfo_reports_correct_pid(self): + procs = self.prep_debug_monitor_and_inferior() + self.assertIsNotNone(procs) + self.add_process_info_collection_packets() + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info response + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + + # Ensure the process id matches what we expected. + pid_text = process_info.get('pid', None) + self.assertIsNotNone(pid_text) + reported_pid = int(pid_text, base=16) + self.assertEqual(reported_pid, procs["inferior"].pid) + + @debugserver_test + def test_attach_commandline_qProcessInfo_reports_correct_pid_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_attach() + self.attach_commandline_qProcessInfo_reports_correct_pid() + + @llgs_test + def test_attach_commandline_qProcessInfo_reports_correct_pid_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_attach() + self.attach_commandline_qProcessInfo_reports_correct_pid() + + def qProcessInfo_reports_valid_endian(self): + procs = self.prep_debug_monitor_and_inferior() + self.add_process_info_collection_packets() + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info response + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + + # Ensure the process id looks reasonable. + endian = process_info.get("endian") + self.assertIsNotNone(endian) + self.assertTrue(endian in ["little", "big", "pdp"]) + + @debugserver_test + def test_qProcessInfo_reports_valid_endian_debugserver(self): + self.init_debugserver_test() + self.build() + self.qProcessInfo_reports_valid_endian() + + @llgs_test + def test_qProcessInfo_reports_valid_endian_llgs(self): + self.init_llgs_test() + self.build() + self.qProcessInfo_reports_valid_endian() + + def qProcessInfo_contains_keys(self, expected_key_set): + procs = self.prep_debug_monitor_and_inferior() + self.add_process_info_collection_packets() + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info response + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + + # Ensure the expected keys are present and non-None within the process info. + missing_key_set = set() + for expected_key in expected_key_set: + if expected_key not in process_info: + missing_key_set.add(expected_key) + + self.assertEqual(missing_key_set, set(), "the listed keys are missing in the qProcessInfo result") + + def qProcessInfo_does_not_contain_keys(self, absent_key_set): + procs = self.prep_debug_monitor_and_inferior() + self.add_process_info_collection_packets() + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info response + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + + # Ensure the unexpected keys are not present + unexpected_key_set = set() + for unexpected_key in absent_key_set: + if unexpected_key in process_info: + unexpected_key_set.add(unexpected_key) + + self.assertEqual(unexpected_key_set, set(), "the listed keys were present but unexpected in qProcessInfo result") + + @skipUnlessDarwin + @debugserver_test + def test_qProcessInfo_contains_cputype_cpusubtype_debugserver_darwin(self): + self.init_debugserver_test() + self.build() + self.qProcessInfo_contains_keys(set(['cputype', 'cpusubtype'])) + + @skipUnlessPlatform(["linux"]) + @llgs_test + def test_qProcessInfo_contains_triple_llgs_linux(self): + self.init_llgs_test() + self.build() + self.qProcessInfo_contains_keys(set(['triple'])) + + @skipUnlessDarwin + @debugserver_test + def test_qProcessInfo_does_not_contain_triple_debugserver_darwin(self): + self.init_debugserver_test() + self.build() + # We don't expect to see triple on darwin. If we do, we'll prefer triple + # to cputype/cpusubtype and skip some darwin-based ProcessGDBRemote ArchSpec setup + # for the remote Host and Process. + self.qProcessInfo_does_not_contain_keys(set(['triple'])) + + @skipUnlessPlatform(["linux"]) + @llgs_test + def test_qProcessInfo_does_not_contain_cputype_cpusubtype_llgs_linux(self): + self.init_llgs_test() + self.build() + self.qProcessInfo_does_not_contain_keys(set(['cputype', 'cpusubtype'])) diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteRegisterState.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteRegisterState.py new file mode 100644 index 000000000000..a36b4ae781ad --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteRegisterState.py @@ -0,0 +1,126 @@ +from __future__ import print_function + + + +import gdbremote_testcase +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteRegisterState(gdbremote_testcase.GdbRemoteTestCaseBase): + """Test QSaveRegisterState/QRestoreRegisterState support.""" + + mydir = TestBase.compute_mydir(__file__) + + def grp_register_save_restore_works(self, with_suffix): + # Start up the process, use thread suffix, grab main thread id. + inferior_args = ["message:main entered", "sleep:5"] + procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args) + + self.add_process_info_collection_packets() + self.add_register_info_collection_packets() + if with_suffix: + self.add_thread_suffix_request_packets() + self.add_threadinfo_collection_packets() + self.test_sequence.add_log_lines([ + # Start the inferior... + "read packet: $c#63", + # ... match output.... + { "type":"output_match", "regex":r"^message:main entered\r\n$" }, + ], True) + # ... then interrupt. + self.add_interrupt_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info. + process_info = self.parse_process_info_response(context) + endian = process_info.get("endian") + self.assertIsNotNone(endian) + + # Gather register info. + reg_infos = self.parse_register_info_packets(context) + self.assertIsNotNone(reg_infos) + self.add_lldb_register_index(reg_infos) + + # Pull out the register infos that we think we can bit flip successfully. + gpr_reg_infos = [reg_info for reg_info in reg_infos if self.is_bit_flippable_register(reg_info)] + self.assertTrue(len(gpr_reg_infos) > 0) + + # Gather thread info. + if with_suffix: + threads = self.parse_threadinfo_packets(context) + self.assertIsNotNone(threads) + thread_id = threads[0] + self.assertIsNotNone(thread_id) + # print("Running on thread: 0x{:x}".format(thread_id)) + else: + thread_id = None + + # Save register state. + self.reset_test_sequence() + self.add_QSaveRegisterState_packets(thread_id) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + (success, state_id) = self.parse_QSaveRegisterState_response(context) + self.assertTrue(success) + self.assertIsNotNone(state_id) + # print("saved register state id: {}".format(state_id)) + + # Remember initial register values. + initial_reg_values = self.read_register_values(gpr_reg_infos, endian, thread_id=thread_id) + # print("initial_reg_values: {}".format(initial_reg_values)) + + # Flip gpr register values. + (successful_writes, failed_writes) = self.flip_all_bits_in_each_register_value(gpr_reg_infos, endian, thread_id=thread_id) + # print("successful writes: {}, failed writes: {}".format(successful_writes, failed_writes)) + self.assertTrue(successful_writes > 0) + + flipped_reg_values = self.read_register_values(gpr_reg_infos, endian, thread_id=thread_id) + # print("flipped_reg_values: {}".format(flipped_reg_values)) + + # Restore register values. + self.reset_test_sequence() + self.add_QRestoreRegisterState_packets(state_id, thread_id) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Verify registers match initial register values. + final_reg_values = self.read_register_values(gpr_reg_infos, endian, thread_id=thread_id) + # print("final_reg_values: {}".format(final_reg_values)) + self.assertIsNotNone(final_reg_values) + self.assertEqual(final_reg_values, initial_reg_values) + + @debugserver_test + def test_grp_register_save_restore_works_with_suffix_debugserver(self): + USE_THREAD_SUFFIX = True + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.grp_register_save_restore_works(USE_THREAD_SUFFIX) + + @llgs_test + def test_grp_register_save_restore_works_with_suffix_llgs(self): + USE_THREAD_SUFFIX = True + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.grp_register_save_restore_works(USE_THREAD_SUFFIX) + + @debugserver_test + def test_grp_register_save_restore_works_no_suffix_debugserver(self): + USE_THREAD_SUFFIX = False + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.grp_register_save_restore_works(USE_THREAD_SUFFIX) + + @llgs_test + def test_grp_register_save_restore_works_no_suffix_llgs(self): + USE_THREAD_SUFFIX = False + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.grp_register_save_restore_works(USE_THREAD_SUFFIX) diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py new file mode 100644 index 000000000000..3b008249f557 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py @@ -0,0 +1,25 @@ +from __future__ import print_function + + + +import gdbremote_testcase +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteSingleStep(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @debugserver_test + def test_single_step_only_steps_one_instruction_with_s_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.single_step_only_steps_one_instruction(use_Hc_packet=True, step_instruction="s") + + @llgs_test + @expectedFailureAndroid(bugnumber="llvm.com/pr24739", archs=["arm", "aarch64"]) + def test_single_step_only_steps_one_instruction_with_s_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.single_step_only_steps_one_instruction(use_Hc_packet=True, step_instruction="s") diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py new file mode 100644 index 000000000000..a7938795b9bf --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py @@ -0,0 +1,164 @@ +from __future__ import print_function + + + +import gdbremote_testcase +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteThreadsInStopReply(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + ENABLE_THREADS_IN_STOP_REPLY_ENTRIES = [ + "read packet: $QListThreadsInStopReply#21", + "send packet: $OK#00", + ] + + def gather_stop_reply_threads(self, post_startup_log_lines, thread_count): + # Set up the inferior args. + inferior_args=[] + for i in range(thread_count - 1): + inferior_args.append("thread:new") + inferior_args.append("sleep:10") + procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args) + + # Assumes test_sequence has anything added needed to setup the initial state. + # (Like optionally enabling QThreadsInStopReply.) + if post_startup_log_lines: + self.test_sequence.add_log_lines(post_startup_log_lines, True) + self.test_sequence.add_log_lines([ + "read packet: $c#63" + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Give threads time to start up, then break. + time.sleep(1) + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + "read packet: {}".format(chr(3)), + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result", 2:"key_vals_text"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Wait until all threads have started. + threads = self.wait_for_thread_count(thread_count, timeout_seconds=3) + self.assertIsNotNone(threads) + self.assertEqual(len(threads), thread_count) + + # Run, then stop the process, grab the stop reply content. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + "read packet: $c#63", + "read packet: {}".format(chr(3)), + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result", 2:"key_vals_text"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Parse the stop reply contents. + key_vals_text = context.get("key_vals_text") + self.assertIsNotNone(key_vals_text) + kv_dict = self.parse_key_val_dict(key_vals_text) + self.assertIsNotNone(kv_dict) + + # Pull out threads from stop response. + stop_reply_threads_text = kv_dict.get("threads") + if stop_reply_threads_text: + return [int(thread_id, 16) for thread_id in stop_reply_threads_text.split(",")] + else: + return [] + + def QListThreadsInStopReply_supported(self): + procs = self.prep_debug_monitor_and_inferior() + self.test_sequence.add_log_lines(self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + @debugserver_test + def test_QListThreadsInStopReply_supported_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.QListThreadsInStopReply_supported() + + @llgs_test + def test_QListThreadsInStopReply_supported_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.QListThreadsInStopReply_supported() + + def stop_reply_reports_multiple_threads(self, thread_count): + # Gather threads from stop notification when QThreadsInStopReply is enabled. + stop_reply_threads = self.gather_stop_reply_threads(self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, thread_count) + self.assertEqual(len(stop_reply_threads), thread_count) + + @debugserver_test + def test_stop_reply_reports_multiple_threads_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.stop_reply_reports_multiple_threads(5) + + @llgs_test + def test_stop_reply_reports_multiple_threads_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.stop_reply_reports_multiple_threads(5) + + def no_QListThreadsInStopReply_supplies_no_threads(self, thread_count): + # Gather threads from stop notification when QThreadsInStopReply is not enabled. + stop_reply_threads = self.gather_stop_reply_threads(None, thread_count) + self.assertEqual(len(stop_reply_threads), 0) + + @debugserver_test + def test_no_QListThreadsInStopReply_supplies_no_threads_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.no_QListThreadsInStopReply_supplies_no_threads(5) + + @llgs_test + def test_no_QListThreadsInStopReply_supplies_no_threads_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.no_QListThreadsInStopReply_supplies_no_threads(5) + + def stop_reply_reports_correct_threads(self, thread_count): + # Gather threads from stop notification when QThreadsInStopReply is enabled. + stop_reply_threads = self.gather_stop_reply_threads(self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, thread_count) + self.assertEqual(len(stop_reply_threads), thread_count) + + # Gather threads from q{f,s}ThreadInfo. + self.reset_test_sequence() + self.add_threadinfo_collection_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + threads = self.parse_threadinfo_packets(context) + self.assertIsNotNone(threads) + self.assertEqual(len(threads), thread_count) + + # Ensure each thread in q{f,s}ThreadInfo appears in stop reply threads + for tid in threads: + self.assertTrue(tid in stop_reply_threads) + + @debugserver_test + def test_stop_reply_reports_correct_threads_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.stop_reply_reports_correct_threads(5) + + @llgs_test + def test_stop_reply_reports_correct_threads_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.stop_reply_reports_correct_threads(5) diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py new file mode 100644 index 000000000000..cce484451de4 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py @@ -0,0 +1,150 @@ +from __future__ import print_function + + + +import sys + +import unittest2 +import gdbremote_testcase +from lldbsuite.test.lldbtest import * + +class TestGdbRemote_qThreadStopInfo(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + THREAD_COUNT = 5 + + def gather_stop_replies_via_qThreadStopInfo(self, thread_count): + # Set up the inferior args. + inferior_args=[] + for i in range(thread_count - 1): + inferior_args.append("thread:new") + inferior_args.append("sleep:10") + procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args) + + # Assumes test_sequence has anything added needed to setup the initial state. + # (Like optionally enabling QThreadsInStopReply.) + self.test_sequence.add_log_lines([ + "read packet: $c#63" + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Give threads time to start up, then break. + time.sleep(1) + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + "read packet: {}".format(chr(3)), + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result", 2:"key_vals_text"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Wait until all threads have started. + threads = self.wait_for_thread_count(thread_count, timeout_seconds=3) + self.assertIsNotNone(threads) + self.assertEqual(len(threads), thread_count) + + # Grab stop reply for each thread via qThreadStopInfo{tid:hex}. + stop_replies = {} + thread_dicts = {} + for thread in threads: + # Run the qThreadStopInfo command. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + "read packet: $qThreadStopInfo{:x}#00".format(thread), + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result", 2:"key_vals_text"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Parse stop reply contents. + key_vals_text = context.get("key_vals_text") + self.assertIsNotNone(key_vals_text) + kv_dict = self.parse_key_val_dict(key_vals_text) + self.assertIsNotNone(kv_dict) + + # Verify there is a thread and that it matches the expected thread id. + kv_thread = kv_dict.get("thread") + self.assertIsNotNone(kv_thread) + kv_thread_id = int(kv_thread, 16) + self.assertEqual(kv_thread_id, thread) + + # Grab the stop id reported. + stop_result_text = context.get("stop_result") + self.assertIsNotNone(stop_result_text) + stop_replies[kv_thread_id] = int(stop_result_text, 16) + + # Hang on to the key-val dictionary for the thread. + thread_dicts[kv_thread_id] = kv_dict + + return (stop_replies, thread_dicts) + + def qThreadStopInfo_works_for_multiple_threads(self, thread_count): + (stop_replies, _) = self.gather_stop_replies_via_qThreadStopInfo(thread_count) + self.assertEqual(len(stop_replies), thread_count) + + @debugserver_test + def test_qThreadStopInfo_works_for_multiple_threads_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadStopInfo_works_for_multiple_threads(self.THREAD_COUNT) + + @llgs_test + def test_qThreadStopInfo_works_for_multiple_threads_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadStopInfo_works_for_multiple_threads(self.THREAD_COUNT) + + def qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt(self, thread_count): + (stop_replies, _) = self.gather_stop_replies_via_qThreadStopInfo(thread_count) + self.assertIsNotNone(stop_replies) + + no_stop_reason_count = sum(1 for stop_reason in list(stop_replies.values()) if stop_reason == 0) + with_stop_reason_count = sum(1 for stop_reason in list(stop_replies.values()) if stop_reason != 0) + + # All but one thread should report no stop reason. + self.assertEqual(no_stop_reason_count, thread_count - 1) + + # Only one thread should should indicate a stop reason. + self.assertEqual(with_stop_reason_count, 1) + + @debugserver_test + def test_qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt(self.THREAD_COUNT) + + @llgs_test + def test_qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt(self.THREAD_COUNT) + + def qThreadStopInfo_has_valid_thread_names(self, thread_count, expected_thread_name): + (_, thread_dicts) = self.gather_stop_replies_via_qThreadStopInfo(thread_count) + self.assertIsNotNone(thread_dicts) + + for thread_dict in list(thread_dicts.values()): + name = thread_dict.get("name") + self.assertIsNotNone(name) + self.assertEqual(name, expected_thread_name) + + @unittest2.skip("MacOSX doesn't have a default thread name") + @debugserver_test + def test_qThreadStopInfo_has_valid_thread_names_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadStopInfo_has_valid_thread_names(self.THREAD_COUNT, "a.out") + + @skipUnlessPlatform(["linux"]) # test requires OS with set, equal thread names by default. + @llgs_test + def test_qThreadStopInfo_has_valid_thread_names_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadStopInfo_has_valid_thread_names(self.THREAD_COUNT, "a.out") diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py new file mode 100644 index 000000000000..579e99d6d774 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py @@ -0,0 +1,116 @@ +from __future__ import print_function + + + +import gdbremote_testcase +from lldbsuite.test.lldbtest import * + +class TestGdbRemote_vCont(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def vCont_supports_mode(self, mode, inferior_args=None): + # Setup the stub and set the gdb remote command stream. + procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args) + self.add_vCont_query_packets() + + # Run the gdb remote command stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Pull out supported modes. + supported_vCont_modes = self.parse_vCont_query_response(context) + self.assertIsNotNone(supported_vCont_modes) + + # Verify we support the given mode. + self.assertTrue(mode in supported_vCont_modes) + + def vCont_supports_c(self): + self.vCont_supports_mode("c") + + def vCont_supports_C(self): + self.vCont_supports_mode("C") + + def vCont_supports_s(self): + self.vCont_supports_mode("s") + + def vCont_supports_S(self): + self.vCont_supports_mode("S") + + @debugserver_test + def test_vCont_supports_c_debugserver(self): + self.init_debugserver_test() + self.build() + self.vCont_supports_c() + + @llgs_test + def test_vCont_supports_c_llgs(self): + self.init_llgs_test() + self.build() + self.vCont_supports_c() + + @debugserver_test + def test_vCont_supports_C_debugserver(self): + self.init_debugserver_test() + self.build() + self.vCont_supports_C() + + @llgs_test + def test_vCont_supports_C_llgs(self): + self.init_llgs_test() + self.build() + self.vCont_supports_C() + + @debugserver_test + def test_vCont_supports_s_debugserver(self): + self.init_debugserver_test() + self.build() + self.vCont_supports_s() + + @llgs_test + def test_vCont_supports_s_llgs(self): + self.init_llgs_test() + self.build() + self.vCont_supports_s() + + @debugserver_test + def test_vCont_supports_S_debugserver(self): + self.init_debugserver_test() + self.build() + self.vCont_supports_S() + + @llgs_test + def test_vCont_supports_S_llgs(self): + self.init_llgs_test() + self.build() + self.vCont_supports_S() + + @debugserver_test + def test_single_step_only_steps_one_instruction_with_Hc_vCont_s_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.single_step_only_steps_one_instruction(use_Hc_packet=True, step_instruction="vCont;s") + + @llgs_test + @expectedFailureAndroid(bugnumber="llvm.com/pr24739", archs=["arm", "aarch64"]) + def test_single_step_only_steps_one_instruction_with_Hc_vCont_s_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.single_step_only_steps_one_instruction(use_Hc_packet=True, step_instruction="vCont;s") + + @debugserver_test + def test_single_step_only_steps_one_instruction_with_vCont_s_thread_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.single_step_only_steps_one_instruction(use_Hc_packet=False, step_instruction="vCont;s:{thread}") + + @llgs_test + @expectedFailureAndroid(bugnumber="llvm.com/pr24739", archs=["arm", "aarch64"]) + def test_single_step_only_steps_one_instruction_with_vCont_s_thread_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.single_step_only_steps_one_instruction(use_Hc_packet=False, step_instruction="vCont;s:{thread}") diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py new file mode 100644 index 000000000000..aec040c50569 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py @@ -0,0 +1,1473 @@ +""" +Test case for testing the gdbremote protocol. + +Tests run against debugserver and lldb-server (llgs). +lldb-server tests run where the lldb-server exe is +available. + +This class will be broken into smaller test case classes by +gdb remote packet functional areas. For now it contains +the initial set of tests implemented. +""" + +from __future__ import print_function + + + +import unittest2 +import gdbremote_testcase +import lldbgdbserverutils +import platform +import signal +from lldbsuite.test.lldbtest import * + +class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @debugserver_test + def test_exe_starts_debugserver(self): + self.init_debugserver_test() + server = self.connect_to_debug_monitor() + + @llgs_test + def test_exe_starts_llgs(self): + self.init_llgs_test() + server = self.connect_to_debug_monitor() + + def start_no_ack_mode(self): + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + self.add_no_ack_remote_stream() + self.expect_gdbremote_sequence() + + @debugserver_test + def test_start_no_ack_mode_debugserver(self): + self.init_debugserver_test() + self.start_no_ack_mode() + + @llgs_test + def test_start_no_ack_mode_llgs(self): + self.init_llgs_test() + self.start_no_ack_mode() + + def thread_suffix_supported(self): + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + self.add_no_ack_remote_stream() + self.test_sequence.add_log_lines( + ["lldb-server < 26> read packet: $QThreadSuffixSupported#e4", + "lldb-server < 6> send packet: $OK#9a"], + True) + + self.expect_gdbremote_sequence() + + @debugserver_test + def test_thread_suffix_supported_debugserver(self): + self.init_debugserver_test() + self.thread_suffix_supported() + + @llgs_test + def test_thread_suffix_supported_llgs(self): + self.init_llgs_test() + self.thread_suffix_supported() + + def list_threads_in_stop_reply_supported(self): + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + self.add_no_ack_remote_stream() + self.test_sequence.add_log_lines( + ["lldb-server < 27> read packet: $QListThreadsInStopReply#21", + "lldb-server < 6> send packet: $OK#9a"], + True) + self.expect_gdbremote_sequence() + + @debugserver_test + def test_list_threads_in_stop_reply_supported_debugserver(self): + self.init_debugserver_test() + self.list_threads_in_stop_reply_supported() + + @llgs_test + def test_list_threads_in_stop_reply_supported_llgs(self): + self.init_llgs_test() + self.list_threads_in_stop_reply_supported() + + def install_and_create_launch_args(self): + exe_path = os.path.abspath('a.out') + if not lldb.remote_platform: + return [exe_path] + remote_path = lldbutil.append_to_process_working_directory(os.path.basename(exe_path)) + remote_file_spec = lldb.SBFileSpec(remote_path, False) + err = lldb.remote_platform.Install(lldb.SBFileSpec(exe_path, True), remote_file_spec) + if err.Fail(): + raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (exe_path, remote_path, err)) + return [remote_path] + + def start_inferior(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + self.add_no_ack_remote_stream() + self.test_sequence.add_log_lines( + ["read packet: %s" % lldbgdbserverutils.build_gdbremote_A_packet(launch_args), + "send packet: $OK#9a"], + True) + self.expect_gdbremote_sequence() + + @debugserver_test + def test_start_inferior_debugserver(self): + self.init_debugserver_test() + self.build() + self.start_inferior() + + @llgs_test + def test_start_inferior_llgs(self): + self.init_llgs_test() + self.build() + self.start_inferior() + + def inferior_exit_0(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.test_sequence.add_log_lines( + ["read packet: $vCont;c#a8", + "send packet: $W00#00"], + True) + + self.expect_gdbremote_sequence() + + @debugserver_test + def test_inferior_exit_0_debugserver(self): + self.init_debugserver_test() + self.build() + self.inferior_exit_0() + + @llgs_test + def test_inferior_exit_0_llgs(self): + self.init_llgs_test() + self.build() + self.inferior_exit_0() + + def inferior_exit_42(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + RETVAL = 42 + + # build launch args + launch_args += ["retval:%d" % RETVAL] + + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.test_sequence.add_log_lines( + ["read packet: $vCont;c#a8", + "send packet: $W{0:02x}#00".format(RETVAL)], + True) + + self.expect_gdbremote_sequence() + + @debugserver_test + def test_inferior_exit_42_debugserver(self): + self.init_debugserver_test() + self.build() + self.inferior_exit_42() + + @llgs_test + def test_inferior_exit_42_llgs(self): + self.init_llgs_test() + self.build() + self.inferior_exit_42() + + def c_packet_works(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.test_sequence.add_log_lines( + ["read packet: $c#63", + "send packet: $W00#00"], + True) + + self.expect_gdbremote_sequence() + + @debugserver_test + def test_c_packet_works_debugserver(self): + self.init_debugserver_test() + self.build() + self.c_packet_works() + + @llgs_test + def test_c_packet_works_llgs(self): + self.init_llgs_test() + self.build() + self.c_packet_works() + + def inferior_print_exit(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + # build launch args + launch_args += ["hello, world"] + + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.test_sequence.add_log_lines( + ["read packet: $vCont;c#a8", + {"type":"output_match", "regex":r"^hello, world\r\n$" }, + "send packet: $W00#00"], + True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + @debugserver_test + def test_inferior_print_exit_debugserver(self): + self.init_debugserver_test() + self.build() + self.inferior_print_exit() + + @llgs_test + def test_inferior_print_exit_llgs(self): + self.init_llgs_test() + self.build() + self.inferior_print_exit() + + def first_launch_stop_reply_thread_matches_first_qC(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + # build launch args + launch_args += ["hello, world"] + + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.test_sequence.add_log_lines( + ["read packet: $qC#00", + { "direction":"send", "regex":r"^\$QC([0-9a-fA-F]+)#", "capture":{1:"thread_id"} }, + "read packet: $?#00", + { "direction":"send", "regex":r"^\$T[0-9a-fA-F]{2}thread:([0-9a-fA-F]+)", "expect_captures":{1:"thread_id"} }], + True) + self.expect_gdbremote_sequence() + + @debugserver_test + def test_first_launch_stop_reply_thread_matches_first_qC_debugserver(self): + self.init_debugserver_test() + self.build() + self.first_launch_stop_reply_thread_matches_first_qC() + + @llgs_test + def test_first_launch_stop_reply_thread_matches_first_qC_llgs(self): + self.init_llgs_test() + self.build() + self.first_launch_stop_reply_thread_matches_first_qC() + + def attach_commandline_continue_app_exits(self): + procs = self.prep_debug_monitor_and_inferior() + self.test_sequence.add_log_lines( + ["read packet: $vCont;c#a8", + "send packet: $W00#00"], + True) + self.expect_gdbremote_sequence() + + # Wait a moment for completed and now-detached inferior process to clear. + time.sleep(1) + + if not lldb.remote_platform: + # Process should be dead now. Reap results. + poll_result = procs["inferior"].poll() + self.assertIsNotNone(poll_result) + + # Where possible, verify at the system level that the process is not running. + self.assertFalse(lldbgdbserverutils.process_is_running(procs["inferior"].pid, False)) + + @debugserver_test + def test_attach_commandline_continue_app_exits_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_attach() + self.attach_commandline_continue_app_exits() + + @llgs_test + def test_attach_commandline_continue_app_exits_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_attach() + self.attach_commandline_continue_app_exits() + + def qRegisterInfo_returns_one_valid_result(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + # Build the expected protocol stream + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.test_sequence.add_log_lines( + ["read packet: $qRegisterInfo0#00", + { "direction":"send", "regex":r"^\$(.+);#[0-9A-Fa-f]{2}", "capture":{1:"reginfo_0"} }], + True) + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + reg_info_packet = context.get("reginfo_0") + self.assertIsNotNone(reg_info_packet) + self.assert_valid_reg_info(lldbgdbserverutils.parse_reg_info_response(reg_info_packet)) + + @debugserver_test + @expectedFailureDarwin("llvm.org/pr25486") + def test_qRegisterInfo_returns_one_valid_result_debugserver(self): + self.init_debugserver_test() + self.build() + self.qRegisterInfo_returns_one_valid_result() + + @llgs_test + def test_qRegisterInfo_returns_one_valid_result_llgs(self): + self.init_llgs_test() + self.build() + self.qRegisterInfo_returns_one_valid_result() + + def qRegisterInfo_returns_all_valid_results(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + # Build the expected protocol stream. + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.add_register_info_collection_packets() + + # Run the stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Validate that each register info returned validates. + for reg_info in self.parse_register_info_packets(context): + self.assert_valid_reg_info(reg_info) + + @debugserver_test + @expectedFailureDarwin("llvm.org/pr25486") + def test_qRegisterInfo_returns_all_valid_results_debugserver(self): + self.init_debugserver_test() + self.build() + self.qRegisterInfo_returns_all_valid_results() + + @llgs_test + def test_qRegisterInfo_returns_all_valid_results_llgs(self): + self.init_llgs_test() + self.build() + self.qRegisterInfo_returns_all_valid_results() + + def qRegisterInfo_contains_required_generics(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + # Build the expected protocol stream + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.add_register_info_collection_packets() + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather register info entries. + reg_infos = self.parse_register_info_packets(context) + + # Collect all generic registers found. + generic_regs = { reg_info['generic']:1 for reg_info in reg_infos if 'generic' in reg_info } + + # Ensure we have a program counter register. + self.assertTrue('pc' in generic_regs) + + # Ensure we have a frame pointer register. + self.assertTrue('fp' in generic_regs) + + # Ensure we have a stack pointer register. + self.assertTrue('sp' in generic_regs) + + # Ensure we have a flags register. + self.assertTrue('flags' in generic_regs) + + @debugserver_test + def test_qRegisterInfo_contains_required_generics_debugserver(self): + self.init_debugserver_test() + self.build() + self.qRegisterInfo_contains_required_generics() + + @llgs_test + def test_qRegisterInfo_contains_required_generics_llgs(self): + self.init_llgs_test() + self.build() + self.qRegisterInfo_contains_required_generics() + + def qRegisterInfo_contains_at_least_one_register_set(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + # Build the expected protocol stream + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.add_register_info_collection_packets() + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather register info entries. + reg_infos = self.parse_register_info_packets(context) + + # Collect all register sets found. + register_sets = { reg_info['set']:1 for reg_info in reg_infos if 'set' in reg_info } + self.assertTrue(len(register_sets) >= 1) + + @debugserver_test + def test_qRegisterInfo_contains_at_least_one_register_set_debugserver(self): + self.init_debugserver_test() + self.build() + self.qRegisterInfo_contains_at_least_one_register_set() + + @llgs_test + def test_qRegisterInfo_contains_at_least_one_register_set_llgs(self): + self.init_llgs_test() + self.build() + self.qRegisterInfo_contains_at_least_one_register_set() + + def targetHasAVX(self): + triple = self.dbg.GetSelectedPlatform().GetTriple() + + # TODO other platforms, please implement this function + if not re.match(".*-.*-linux", triple): + return True + + # Need to do something different for non-Linux/Android targets + if lldb.remote_platform: + self.runCmd('platform get-file "/proc/cpuinfo" "cpuinfo"') + cpuinfo_path = "cpuinfo" + self.addTearDownHook(lambda: os.unlink("cpuinfo")) + else: + cpuinfo_path = "/proc/cpuinfo" + + f = open(cpuinfo_path, 'r') + cpuinfo = f.read() + f.close() + return " avx " in cpuinfo + + def qRegisterInfo_contains_avx_registers(self): + launch_args = self.install_and_create_launch_args() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + # Build the expected protocol stream + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.add_register_info_collection_packets() + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather register info entries. + reg_infos = self.parse_register_info_packets(context) + + # Collect all generics found. + register_sets = { reg_info['set']:1 for reg_info in reg_infos if 'set' in reg_info } + self.assertEqual(self.targetHasAVX(), "Advanced Vector Extensions" in register_sets) + + @llgs_test + def test_qRegisterInfo_contains_avx_registers_llgs(self): + self.init_llgs_test() + self.build() + self.qRegisterInfo_contains_avx_registers() + + def qThreadInfo_contains_thread(self): + procs = self.prep_debug_monitor_and_inferior() + self.add_threadinfo_collection_packets() + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather threadinfo entries. + threads = self.parse_threadinfo_packets(context) + self.assertIsNotNone(threads) + + # We should have exactly one thread. + self.assertEqual(len(threads), 1) + + @debugserver_test + def test_qThreadInfo_contains_thread_launch_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadInfo_contains_thread() + + @llgs_test + def test_qThreadInfo_contains_thread_launch_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadInfo_contains_thread() + + @debugserver_test + def test_qThreadInfo_contains_thread_attach_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_attach() + self.qThreadInfo_contains_thread() + + @llgs_test + def test_qThreadInfo_contains_thread_attach_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_attach() + self.qThreadInfo_contains_thread() + + def qThreadInfo_matches_qC(self): + procs = self.prep_debug_monitor_and_inferior() + + self.add_threadinfo_collection_packets() + self.test_sequence.add_log_lines( + ["read packet: $qC#00", + { "direction":"send", "regex":r"^\$QC([0-9a-fA-F]+)#", "capture":{1:"thread_id"} } + ], True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather threadinfo entries. + threads = self.parse_threadinfo_packets(context) + self.assertIsNotNone(threads) + + # We should have exactly one thread from threadinfo. + self.assertEqual(len(threads), 1) + + # We should have a valid thread_id from $QC. + QC_thread_id_hex = context.get("thread_id") + self.assertIsNotNone(QC_thread_id_hex) + QC_thread_id = int(QC_thread_id_hex, 16) + + # Those two should be the same. + self.assertEqual(threads[0], QC_thread_id) + + @debugserver_test + def test_qThreadInfo_matches_qC_launch_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadInfo_matches_qC() + + @llgs_test + def test_qThreadInfo_matches_qC_launch_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qThreadInfo_matches_qC() + + @debugserver_test + def test_qThreadInfo_matches_qC_attach_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_attach() + self.qThreadInfo_matches_qC() + + @llgs_test + def test_qThreadInfo_matches_qC_attach_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_attach() + self.qThreadInfo_matches_qC() + + def p_returns_correct_data_size_for_each_qRegisterInfo(self): + procs = self.prep_debug_monitor_and_inferior() + self.add_register_info_collection_packets() + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather register info entries. + reg_infos = self.parse_register_info_packets(context) + self.assertIsNotNone(reg_infos) + self.assertTrue(len(reg_infos) > 0) + + # Read value for each register. + reg_index = 0 + for reg_info in reg_infos: + # Skip registers that don't have a register set. For x86, these are + # the DRx registers, which have no LLDB-kind register number and thus + # cannot be read via normal NativeRegisterContext::ReadRegister(reg_info,...) calls. + if not "set" in reg_info: + continue + + # Clear existing packet expectations. + self.reset_test_sequence() + + # Run the register query + self.test_sequence.add_log_lines( + ["read packet: $p{0:x}#00".format(reg_index), + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }], + True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Verify the response length. + p_response = context.get("p_response") + self.assertIsNotNone(p_response) + self.assertEqual(len(p_response), 2 * int(reg_info["bitsize"]) / 8) + + # Increment loop + reg_index += 1 + + @debugserver_test + def test_p_returns_correct_data_size_for_each_qRegisterInfo_launch_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.p_returns_correct_data_size_for_each_qRegisterInfo() + + @llgs_test + def test_p_returns_correct_data_size_for_each_qRegisterInfo_launch_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.p_returns_correct_data_size_for_each_qRegisterInfo() + + @debugserver_test + def test_p_returns_correct_data_size_for_each_qRegisterInfo_attach_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_attach() + self.p_returns_correct_data_size_for_each_qRegisterInfo() + + @llgs_test + def test_p_returns_correct_data_size_for_each_qRegisterInfo_attach_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_attach() + self.p_returns_correct_data_size_for_each_qRegisterInfo() + + def Hg_switches_to_3_threads(self): + # Startup the inferior with three threads (main + 2 new ones). + procs = self.prep_debug_monitor_and_inferior(inferior_args=["thread:new", "thread:new"]) + + # Let the inferior process have a few moments to start up the thread when launched. (The launch scenario has no time to run, so threads won't be there yet.) + self.run_process_then_stop(run_seconds=1) + + # Wait at most x seconds for 3 threads to be present. + threads = self.wait_for_thread_count(3, timeout_seconds=5) + self.assertEqual(len(threads), 3) + + # verify we can $H to each thead, and $qC matches the thread we set. + for thread in threads: + # Change to each thread, verify current thread id. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $Hg{0:x}#00".format(thread), # Set current thread. + "send packet: $OK#00", + "read packet: $qC#00", + { "direction":"send", "regex":r"^\$QC([0-9a-fA-F]+)#", "capture":{1:"thread_id"} }], + True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Verify the thread id. + self.assertIsNotNone(context.get("thread_id")) + self.assertEqual(int(context.get("thread_id"), 16), thread) + + @debugserver_test + def test_Hg_switches_to_3_threads_launch_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.Hg_switches_to_3_threads() + + @llgs_test + def test_Hg_switches_to_3_threads_launch_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.Hg_switches_to_3_threads() + + @debugserver_test + def test_Hg_switches_to_3_threads_attach_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_attach() + self.Hg_switches_to_3_threads() + + @llgs_test + def test_Hg_switches_to_3_threads_attach_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_attach() + self.Hg_switches_to_3_threads() + + def Hc_then_Csignal_signals_correct_thread(self, segfault_signo): + # NOTE only run this one in inferior-launched mode: we can't grab inferior stdout when running attached, + # and the test requires getting stdout from the exe. + + NUM_THREADS = 3 + + # Startup the inferior with three threads (main + NUM_THREADS-1 worker threads). + # inferior_args=["thread:print-ids"] + inferior_args=["thread:segfault"] + for i in range(NUM_THREADS - 1): + # if i > 0: + # Give time between thread creation/segfaulting for the handler to work. + # inferior_args.append("sleep:1") + inferior_args.append("thread:new") + inferior_args.append("sleep:10") + + # Launch/attach. (In our case, this should only ever be launched since we need inferior stdout/stderr). + procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args) + self.test_sequence.add_log_lines(["read packet: $c#63"], True) + context = self.expect_gdbremote_sequence() + + # Let the inferior process have a few moments to start up the thread when launched. + # context = self.run_process_then_stop(run_seconds=1) + + # Wait at most x seconds for all threads to be present. + # threads = self.wait_for_thread_count(NUM_THREADS, timeout_seconds=5) + # self.assertEquals(len(threads), NUM_THREADS) + + signaled_tids = {} + print_thread_ids = {} + + # Switch to each thread, deliver a signal, and verify signal delivery + for i in range(NUM_THREADS - 1): + # Run until SIGSEGV comes in. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + [{"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"signo", 2:"thread_id"} } + ], True) + + context = self.expect_gdbremote_sequence(timeout_seconds=10) + self.assertIsNotNone(context) + signo = context.get("signo") + self.assertEqual(int(signo, 16), segfault_signo) + + # Ensure we haven't seen this tid yet. + thread_id = int(context.get("thread_id"), 16) + self.assertFalse(thread_id in signaled_tids) + signaled_tids[thread_id] = 1 + + # Send SIGUSR1 to the thread that signaled the SIGSEGV. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + [ + # Set the continue thread. + "read packet: $Hc{0:x}#00".format(thread_id), # Set current thread. + "send packet: $OK#00", + + # Continue sending the signal number to the continue thread. + # The commented out packet is a way to do this same operation without using + # a $Hc (but this test is testing $Hc, so we'll stick with the former). + "read packet: $C{0:x}#00".format(lldbutil.get_signal_number('SIGUSR1')), + # "read packet: $vCont;C{0:x}:{1:x};c#00".format(lldbutil.get_signal_number('SIGUSR1'), thread_id), + + # FIXME: Linux does not report the thread stop on the delivered signal (SIGUSR1 here). MacOSX debugserver does. + # But MacOSX debugserver isn't guaranteeing the thread the signal handler runs on, so currently its an XFAIL. + # Need to rectify behavior here. The linux behavior is more intuitive to me since we're essentially swapping out + # an about-to-be-delivered signal (for which we already sent a stop packet) to a different signal. + # {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }, + # "read packet: $c#63", + { "type":"output_match", "regex":r"^received SIGUSR1 on thread id: ([0-9a-fA-F]+)\r\nthread ([0-9a-fA-F]+): past SIGSEGV\r\n", "capture":{ 1:"print_thread_id", 2:"post_handle_thread_id" } }, + ], + True) + + # Run the sequence. + context = self.expect_gdbremote_sequence(timeout_seconds=10) + self.assertIsNotNone(context) + + # Ensure the stop signal is the signal we delivered. + # stop_signo = context.get("stop_signo") + # self.assertIsNotNone(stop_signo) + # self.assertEquals(int(stop_signo,16), lldbutil.get_signal_number('SIGUSR1')) + + # Ensure the stop thread is the thread to which we delivered the signal. + # stop_thread_id = context.get("stop_thread_id") + # self.assertIsNotNone(stop_thread_id) + # self.assertEquals(int(stop_thread_id,16), thread_id) + + # Ensure we haven't seen this thread id yet. The inferior's self-obtained thread ids are not guaranteed to match the stub tids (at least on MacOSX). + print_thread_id = context.get("print_thread_id") + self.assertIsNotNone(print_thread_id) + print_thread_id = int(print_thread_id, 16) + self.assertFalse(print_thread_id in print_thread_ids) + + # Now remember this print (i.e. inferior-reflected) thread id and ensure we don't hit it again. + print_thread_ids[print_thread_id] = 1 + + # Ensure post signal-handle thread id matches the thread that initially raised the SIGSEGV. + post_handle_thread_id = context.get("post_handle_thread_id") + self.assertIsNotNone(post_handle_thread_id) + post_handle_thread_id = int(post_handle_thread_id, 16) + self.assertEqual(post_handle_thread_id, print_thread_id) + + @unittest2.expectedFailure() + @debugserver_test + def test_Hc_then_Csignal_signals_correct_thread_launch_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + # Darwin debugserver translates some signals like SIGSEGV into some gdb expectations about fixed signal numbers. + self.Hc_then_Csignal_signals_correct_thread(self.TARGET_EXC_BAD_ACCESS) + + @llgs_test + def test_Hc_then_Csignal_signals_correct_thread_launch_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.Hc_then_Csignal_signals_correct_thread(lldbutil.get_signal_number('SIGSEGV')) + + def m_packet_reads_memory(self): + # This is the memory we will write into the inferior and then ensure we can read back with $m. + MEMORY_CONTENTS = "Test contents 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz" + + # Start up the inferior. + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["set-message:%s" % MEMORY_CONTENTS, "get-data-address-hex:g_message", "sleep:5"]) + + # Run the process + self.test_sequence.add_log_lines( + [ + # Start running after initial stop. + "read packet: $c#63", + # Match output line that prints the memory address of the message buffer within the inferior. + # Note we require launch-only testing so we can get inferior otuput. + { "type":"output_match", "regex":r"^data address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"message_address"} }, + # Now stop the inferior. + "read packet: {}".format(chr(3)), + # And wait for the stop notification. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }], + True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Grab the message address. + self.assertIsNotNone(context.get("message_address")) + message_address = int(context.get("message_address"), 16) + + # Grab contents from the inferior. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $m{0:x},{1:x}#00".format(message_address, len(MEMORY_CONTENTS)), + {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"read_contents"} }], + True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Ensure what we read from inferior memory is what we wrote. + self.assertIsNotNone(context.get("read_contents")) + read_contents = context.get("read_contents").decode("hex") + self.assertEqual(read_contents, MEMORY_CONTENTS) + + @debugserver_test + def test_m_packet_reads_memory_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.m_packet_reads_memory() + + @llgs_test + def test_m_packet_reads_memory_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.m_packet_reads_memory() + + def qMemoryRegionInfo_is_supported(self): + # Start up the inferior. + procs = self.prep_debug_monitor_and_inferior() + + # Ask if it supports $qMemoryRegionInfo. + self.test_sequence.add_log_lines( + ["read packet: $qMemoryRegionInfo#00", + "send packet: $OK#00" + ], True) + self.expect_gdbremote_sequence() + + @debugserver_test + def test_qMemoryRegionInfo_is_supported_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qMemoryRegionInfo_is_supported() + + @llgs_test + def test_qMemoryRegionInfo_is_supported_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qMemoryRegionInfo_is_supported() + + def qMemoryRegionInfo_reports_code_address_as_executable(self): + # Start up the inferior. + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["get-code-address-hex:hello", "sleep:5"]) + + # Run the process + self.test_sequence.add_log_lines( + [ + # Start running after initial stop. + "read packet: $c#63", + # Match output line that prints the memory address of the message buffer within the inferior. + # Note we require launch-only testing so we can get inferior otuput. + { "type":"output_match", "regex":r"^code address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"code_address"} }, + # Now stop the inferior. + "read packet: {}".format(chr(3)), + # And wait for the stop notification. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }], + True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Grab the code address. + self.assertIsNotNone(context.get("code_address")) + code_address = int(context.get("code_address"), 16) + + # Grab memory region info from the inferior. + self.reset_test_sequence() + self.add_query_memory_region_packets(code_address) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + mem_region_dict = self.parse_memory_region_packet(context) + + # Ensure there are no errors reported. + self.assertFalse("error" in mem_region_dict) + + # Ensure code address is readable and executable. + self.assertTrue("permissions" in mem_region_dict) + self.assertTrue("r" in mem_region_dict["permissions"]) + self.assertTrue("x" in mem_region_dict["permissions"]) + + # Ensure the start address and size encompass the address we queried. + self.assert_address_within_memory_region(code_address, mem_region_dict) + + @debugserver_test + def test_qMemoryRegionInfo_reports_code_address_as_executable_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qMemoryRegionInfo_reports_code_address_as_executable() + + @llgs_test + def test_qMemoryRegionInfo_reports_code_address_as_executable_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qMemoryRegionInfo_reports_code_address_as_executable() + + def qMemoryRegionInfo_reports_stack_address_as_readable_writeable(self): + # Start up the inferior. + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["get-stack-address-hex:", "sleep:5"]) + + # Run the process + self.test_sequence.add_log_lines( + [ + # Start running after initial stop. + "read packet: $c#63", + # Match output line that prints the memory address of the message buffer within the inferior. + # Note we require launch-only testing so we can get inferior otuput. + { "type":"output_match", "regex":r"^stack address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"stack_address"} }, + # Now stop the inferior. + "read packet: {}".format(chr(3)), + # And wait for the stop notification. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }], + True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Grab the address. + self.assertIsNotNone(context.get("stack_address")) + stack_address = int(context.get("stack_address"), 16) + + # Grab memory region info from the inferior. + self.reset_test_sequence() + self.add_query_memory_region_packets(stack_address) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + mem_region_dict = self.parse_memory_region_packet(context) + + # Ensure there are no errors reported. + self.assertFalse("error" in mem_region_dict) + + # Ensure address is readable and executable. + self.assertTrue("permissions" in mem_region_dict) + self.assertTrue("r" in mem_region_dict["permissions"]) + self.assertTrue("w" in mem_region_dict["permissions"]) + + # Ensure the start address and size encompass the address we queried. + self.assert_address_within_memory_region(stack_address, mem_region_dict) + + @debugserver_test + def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable() + + @llgs_test + def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable() + + def qMemoryRegionInfo_reports_heap_address_as_readable_writeable(self): + # Start up the inferior. + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["get-heap-address-hex:", "sleep:5"]) + + # Run the process + self.test_sequence.add_log_lines( + [ + # Start running after initial stop. + "read packet: $c#63", + # Match output line that prints the memory address of the message buffer within the inferior. + # Note we require launch-only testing so we can get inferior otuput. + { "type":"output_match", "regex":r"^heap address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"heap_address"} }, + # Now stop the inferior. + "read packet: {}".format(chr(3)), + # And wait for the stop notification. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }], + True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Grab the address. + self.assertIsNotNone(context.get("heap_address")) + heap_address = int(context.get("heap_address"), 16) + + # Grab memory region info from the inferior. + self.reset_test_sequence() + self.add_query_memory_region_packets(heap_address) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + mem_region_dict = self.parse_memory_region_packet(context) + + # Ensure there are no errors reported. + self.assertFalse("error" in mem_region_dict) + + # Ensure address is readable and executable. + self.assertTrue("permissions" in mem_region_dict) + self.assertTrue("r" in mem_region_dict["permissions"]) + self.assertTrue("w" in mem_region_dict["permissions"]) + + # Ensure the start address and size encompass the address we queried. + self.assert_address_within_memory_region(heap_address, mem_region_dict) + + + @debugserver_test + def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable() + + @llgs_test + def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable() + + def software_breakpoint_set_and_remove_work(self): + # Start up the inferior. + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["get-code-address-hex:hello", "sleep:1", "call-function:hello"]) + + # Run the process + self.add_register_info_collection_packets() + self.add_process_info_collection_packets() + self.test_sequence.add_log_lines( + [# Start running after initial stop. + "read packet: $c#63", + # Match output line that prints the memory address of the function call entry point. + # Note we require launch-only testing so we can get inferior otuput. + { "type":"output_match", "regex":r"^code address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"function_address"} }, + # Now stop the inferior. + "read packet: {}".format(chr(3)), + # And wait for the stop notification. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }], + True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info - we need endian of target to handle register value conversions. + process_info = self.parse_process_info_response(context) + endian = process_info.get("endian") + self.assertIsNotNone(endian) + + # Gather register info entries. + reg_infos = self.parse_register_info_packets(context) + (pc_lldb_reg_index, pc_reg_info) = self.find_pc_reg_info(reg_infos) + self.assertIsNotNone(pc_lldb_reg_index) + self.assertIsNotNone(pc_reg_info) + + # Grab the function address. + self.assertIsNotNone(context.get("function_address")) + function_address = int(context.get("function_address"), 16) + + # Set the breakpoint. + if self.getArchitecture() == "arm": + # TODO: Handle case when setting breakpoint in thumb code + BREAKPOINT_KIND = 4 + else: + BREAKPOINT_KIND = 1 + self.reset_test_sequence() + self.add_set_breakpoint_packets(function_address, do_continue=True, breakpoint_kind=BREAKPOINT_KIND) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Verify the stop signal reported was the breakpoint signal number. + stop_signo = context.get("stop_signo") + self.assertIsNotNone(stop_signo) + self.assertEqual(int(stop_signo,16), lldbutil.get_signal_number('SIGTRAP')) + + # Ensure we did not receive any output. If the breakpoint was not set, we would + # see output (from a launched process with captured stdio) printing a hello, world message. + # That would indicate the breakpoint didn't take. + self.assertEqual(len(context["O_content"]), 0) + + # Verify that the PC for the main thread is where we expect it - right at the breakpoint address. + # This acts as a another validation on the register reading code. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + [ + # Print the PC. This should match the breakpoint address. + "read packet: $p{0:x}#00".format(pc_lldb_reg_index), + # Capture $p results. + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, + ], True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Verify the PC is where we expect. Note response is in endianness of the inferior. + p_response = context.get("p_response") + self.assertIsNotNone(p_response) + + # Convert from target endian to int. + returned_pc = lldbgdbserverutils.unpack_register_hex_unsigned(endian, p_response) + self.assertEqual(returned_pc, function_address) + + # Verify that a breakpoint remove and continue gets us the expected output. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + [ + # Remove the breakpoint. + "read packet: $z0,{0:x},{1}#00".format(function_address, BREAKPOINT_KIND), + # Verify the stub could unset it. + "send packet: $OK#00", + # Continue running. + "read packet: $c#63", + # We should now receive the output from the call. + { "type":"output_match", "regex":r"^hello, world\r\n$" }, + # And wait for program completion. + {"direction":"send", "regex":r"^\$W00(.*)#[0-9a-fA-F]{2}$" }, + ], True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + @debugserver_test + def test_software_breakpoint_set_and_remove_work_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.software_breakpoint_set_and_remove_work() + + @llgs_test + def test_software_breakpoint_set_and_remove_work_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.software_breakpoint_set_and_remove_work() + + def qSupported_returns_known_stub_features(self): + # Start up the stub and start/prep the inferior. + procs = self.prep_debug_monitor_and_inferior() + self.add_qSupported_packets() + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Retrieve the qSupported features. + supported_dict = self.parse_qSupported_response(context) + self.assertIsNotNone(supported_dict) + self.assertTrue(len(supported_dict) > 0) + + @debugserver_test + def test_qSupported_returns_known_stub_features_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.qSupported_returns_known_stub_features() + + @llgs_test + def test_qSupported_returns_known_stub_features_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.qSupported_returns_known_stub_features() + + def written_M_content_reads_back_correctly(self): + TEST_MESSAGE = "Hello, memory" + + # Start up the stub and start/prep the inferior. + procs = self.prep_debug_monitor_and_inferior(inferior_args=["set-message:xxxxxxxxxxxxxX", "get-data-address-hex:g_message", "sleep:1", "print-message:"]) + self.test_sequence.add_log_lines( + [ + # Start running after initial stop. + "read packet: $c#63", + # Match output line that prints the memory address of the message buffer within the inferior. + # Note we require launch-only testing so we can get inferior otuput. + { "type":"output_match", "regex":r"^data address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"message_address"} }, + # Now stop the inferior. + "read packet: {}".format(chr(3)), + # And wait for the stop notification. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }], + True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Grab the message address. + self.assertIsNotNone(context.get("message_address")) + message_address = int(context.get("message_address"), 16) + + # Hex-encode the test message, adding null termination. + hex_encoded_message = TEST_MESSAGE.encode("hex") + + # Write the message to the inferior. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $M{0:x},{1:x}:{2}#00".format(message_address, len(hex_encoded_message)/2, hex_encoded_message), + "send packet: $OK#00", + "read packet: $c#63", + { "type":"output_match", "regex":r"^message: (.+)\r\n$", "capture":{ 1:"printed_message"} }, + "send packet: $W00#00", + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Ensure what we read from inferior memory is what we wrote. + printed_message = context.get("printed_message") + self.assertIsNotNone(printed_message) + self.assertEqual(printed_message, TEST_MESSAGE + "X") + + @debugserver_test + def test_written_M_content_reads_back_correctly_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.written_M_content_reads_back_correctly() + + @llgs_test + def test_written_M_content_reads_back_correctly_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.written_M_content_reads_back_correctly() + + def P_writes_all_gpr_registers(self): + # Start inferior debug session, grab all register info. + procs = self.prep_debug_monitor_and_inferior(inferior_args=["sleep:2"]) + self.add_register_info_collection_packets() + self.add_process_info_collection_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Process register infos. + reg_infos = self.parse_register_info_packets(context) + self.assertIsNotNone(reg_infos) + self.add_lldb_register_index(reg_infos) + + # Process endian. + process_info = self.parse_process_info_response(context) + endian = process_info.get("endian") + self.assertIsNotNone(endian) + + # Pull out the register infos that we think we can bit flip successfully,. + gpr_reg_infos = [reg_info for reg_info in reg_infos if self.is_bit_flippable_register(reg_info)] + self.assertTrue(len(gpr_reg_infos) > 0) + + # Write flipped bit pattern of existing value to each register. + (successful_writes, failed_writes) = self.flip_all_bits_in_each_register_value(gpr_reg_infos, endian) + # print("successful writes: {}, failed writes: {}".format(successful_writes, failed_writes)) + self.assertTrue(successful_writes > 0) + + # Note: as of this moment, a hefty number of the GPR writes are failing with E32 (everything except rax-rdx, rdi, rsi, rbp). + # Come back to this. I have the test rigged to verify that at least some of the bit-flip writes work. + @debugserver_test + def test_P_writes_all_gpr_registers_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.P_writes_all_gpr_registers() + + @llgs_test + def test_P_writes_all_gpr_registers_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.P_writes_all_gpr_registers() + + def P_and_p_thread_suffix_work(self): + # Startup the inferior with three threads. + procs = self.prep_debug_monitor_and_inferior(inferior_args=["thread:new", "thread:new"]) + self.add_thread_suffix_request_packets() + self.add_register_info_collection_packets() + self.add_process_info_collection_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + endian = process_info.get("endian") + self.assertIsNotNone(endian) + + reg_infos = self.parse_register_info_packets(context) + self.assertIsNotNone(reg_infos) + self.add_lldb_register_index(reg_infos) + + reg_index = self.select_modifiable_register(reg_infos) + self.assertIsNotNone(reg_index) + reg_byte_size = int(reg_infos[reg_index]["bitsize"]) / 8 + self.assertTrue(reg_byte_size > 0) + + # Run the process a bit so threads can start up, and collect register info. + context = self.run_process_then_stop(run_seconds=1) + self.assertIsNotNone(context) + + # Wait for 3 threads to be present. + threads = self.wait_for_thread_count(3, timeout_seconds=5) + self.assertEqual(len(threads), 3) + + expected_reg_values = [] + register_increment = 1 + next_value = None + + # Set the same register in each of 3 threads to a different value. + # Verify each one has the unique value. + for thread in threads: + # If we don't have a next value yet, start it with the initial read value + 1 + if not next_value: + # Read pre-existing register value. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $p{0:x};thread:{1:x}#00".format(reg_index, thread), + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Set the next value to use for writing as the increment plus current value. + p_response = context.get("p_response") + self.assertIsNotNone(p_response) + next_value = lldbgdbserverutils.unpack_register_hex_unsigned(endian, p_response) + + # Set new value using P and thread suffix. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $P{0:x}={1};thread:{2:x}#00".format(reg_index, lldbgdbserverutils.pack_register_hex(endian, next_value, byte_size=reg_byte_size), thread), + "send packet: $OK#00", + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Save the value we set. + expected_reg_values.append(next_value) + + # Increment value for next thread to use (we want them all different so we can verify they wrote to each thread correctly next.) + next_value += register_increment + + # Revisit each thread and verify they have the expected value set for the register we wrote. + thread_index = 0 + for thread in threads: + # Read pre-existing register value. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $p{0:x};thread:{1:x}#00".format(reg_index, thread), + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Get the register value. + p_response = context.get("p_response") + self.assertIsNotNone(p_response) + read_value = lldbgdbserverutils.unpack_register_hex_unsigned(endian, p_response) + + # Make sure we read back what we wrote. + self.assertEqual(read_value, expected_reg_values[thread_index]) + thread_index += 1 + + # Note: as of this moment, a hefty number of the GPR writes are failing with E32 (everything except rax-rdx, rdi, rsi, rbp). + @debugserver_test + def test_P_and_p_thread_suffix_work_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.P_and_p_thread_suffix_work() + + @llgs_test + def test_P_and_p_thread_suffix_work_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.P_and_p_thread_suffix_work() diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/commandline/TestStubReverseConnect.py b/packages/Python/lldbsuite/test/tools/lldb-server/commandline/TestStubReverseConnect.py new file mode 100644 index 000000000000..9035237b982a --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/commandline/TestStubReverseConnect.py @@ -0,0 +1,87 @@ +from __future__ import print_function + +import gdbremote_testcase +import lldbgdbserverutils +import re +import select +import socket +import time +from lldbsuite.test.lldbtest import * + +class TestStubReverseConnect(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + _DEFAULT_TIMEOUT = 20 + + def setUp(self): + # Set up the test. + gdbremote_testcase.GdbRemoteTestCaseBase.setUp(self) + + # Create a listener on a local port. + self.listener_socket = self.create_listener_socket() + self.assertIsNotNone(self.listener_socket) + self.listener_port = self.listener_socket.getsockname()[1] + + def create_listener_socket(self, timeout_seconds=_DEFAULT_TIMEOUT): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.assertIsNotNone(sock) + + sock.settimeout(timeout_seconds) + sock.bind(("127.0.0.1",0)) + sock.listen(1) + + def tear_down_listener(): + try: + sock.shutdown(socket.SHUT_RDWR) + except: + # ignore + None + + self.addTearDownHook(tear_down_listener) + return sock + + def reverse_connect_works(self): + # Indicate stub startup should do a reverse connect. + appended_stub_args = ["--reverse-connect"] + if self.debug_monitor_extra_args: + self.debug_monitor_extra_args += appended_stub_args + else: + self.debug_monitor_extra_args = appended_stub_args + + self.stub_hostname = "127.0.0.1" + self.port = self.listener_port + + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-.*-android", triple): + self.forward_adb_port(self.port, self.port, "reverse", self.stub_device) + + # Start the stub. + server = self.launch_debug_monitor(logfile=sys.stdout) + self.assertIsNotNone(server) + self.assertTrue(lldbgdbserverutils.process_is_running(server.pid, True)) + + # Listen for the stub's connection to us. + (stub_socket, address) = self.listener_socket.accept() + self.assertIsNotNone(stub_socket) + self.assertIsNotNone(address) + print("connected to stub {} on {}".format(address, stub_socket.getsockname())) + + # Verify we can do the handshake. If that works, we'll call it good. + self.do_handshake(stub_socket, timeout_seconds=self._DEFAULT_TIMEOUT) + + # Clean up. + stub_socket.shutdown(socket.SHUT_RDWR) + + @debugserver_test + def test_reverse_connect_works_debugserver(self): + self.init_debugserver_test(use_named_pipe=False) + self.set_inferior_startup_launch() + self.reverse_connect_works() + + @llgs_test + @skipIfRemote # reverse connect is not a supported use case for now + def test_reverse_connect_works_llgs(self): + self.init_llgs_test(use_named_pipe=False) + self.set_inferior_startup_launch() + self.reverse_connect_works() diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/commandline/TestStubSetSID.py b/packages/Python/lldbsuite/test/tools/lldb-server/commandline/TestStubSetSID.py new file mode 100644 index 000000000000..2b2b0e82379b --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/commandline/TestStubSetSID.py @@ -0,0 +1,82 @@ +from __future__ import print_function + + + +import gdbremote_testcase +import lldbgdbserverutils +import os +import select +import tempfile +import time +from lldbsuite.test.lldbtest import * + +class TestStubSetSIDTestCase(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def get_stub_sid(self, extra_stub_args=None): + # Launch debugserver + if extra_stub_args: + self.debug_monitor_extra_args += extra_stub_args + + server = self.launch_debug_monitor() + self.assertIsNotNone(server) + self.assertTrue(lldbgdbserverutils.process_is_running(server.pid, True)) + + # Get the process id for the stub. + return os.getsid(server.pid) + + def sid_is_same_without_setsid(self): + stub_sid = self.get_stub_sid() + self.assertEqual(stub_sid, os.getsid(0)) + + def sid_is_different_with_setsid(self): + stub_sid = self.get_stub_sid(["--setsid"]) + self.assertNotEqual(stub_sid, os.getsid(0)) + + def sid_is_different_with_S(self): + stub_sid = self.get_stub_sid(["-S"]) + self.assertNotEqual(stub_sid, os.getsid(0)) + + @debugserver_test + @skipIfRemote # --setsid not used on remote platform and currently it is also impossible to get the sid of lldb-platform running on a remote target + def test_sid_is_same_without_setsid_debugserver(self): + self.init_debugserver_test() + self.set_inferior_startup_launch() + self.sid_is_same_without_setsid() + + @llgs_test + @skipIfRemote # --setsid not used on remote platform and currently it is also impossible to get the sid of lldb-platform running on a remote target + @expectedFailureFreeBSD() + def test_sid_is_same_without_setsid_llgs(self): + self.init_llgs_test() + self.set_inferior_startup_launch() + self.sid_is_same_without_setsid() + + @debugserver_test + @skipIfRemote # --setsid not used on remote platform and currently it is also impossible to get the sid of lldb-platform running on a remote target + def test_sid_is_different_with_setsid_debugserver(self): + self.init_debugserver_test() + self.set_inferior_startup_launch() + self.sid_is_different_with_setsid() + + @llgs_test + @skipIfRemote # --setsid not used on remote platform and currently it is also impossible to get the sid of lldb-platform running on a remote target + def test_sid_is_different_with_setsid_llgs(self): + self.init_llgs_test() + self.set_inferior_startup_launch() + self.sid_is_different_with_setsid() + + @debugserver_test + @skipIfRemote # --setsid not used on remote platform and currently it is also impossible to get the sid of lldb-platform running on a remote target + def test_sid_is_different_with_S_debugserver(self): + self.init_debugserver_test() + self.set_inferior_startup_launch() + self.sid_is_different_with_S() + + @llgs_test + @skipIfRemote # --setsid not used on remote platform and currently it is also impossible to get the sid of lldb-platform running on a remote target + def test_sid_is_different_with_S_llgs(self): + self.init_llgs_test() + self.set_inferior_startup_launch() + self.sid_is_different_with_S() diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py new file mode 100644 index 000000000000..113e01e36ba7 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -0,0 +1,1299 @@ +""" +Base class for gdb-remote test cases. +""" + +from __future__ import print_function + + + +import errno +import os +import os.path +import platform +import random +import re +import select +import signal +import socket +import subprocess +import sys +import tempfile +import time +from lldbsuite.test import configuration +from lldbsuite.test.lldbtest import * +from lldbgdbserverutils import * +import logging + +class GdbRemoteTestCaseBase(TestBase): + + _TIMEOUT_SECONDS = 5 + + _GDBREMOTE_KILL_PACKET = "$k#6b" + + _LOGGING_LEVEL = logging.WARNING + # _LOGGING_LEVEL = logging.DEBUG + + # Start the inferior separately, attach to the inferior on the stub command line. + _STARTUP_ATTACH = "attach" + # Start the inferior separately, start the stub without attaching, allow the test to attach to the inferior however it wants (e.g. $vAttach;pid). + _STARTUP_ATTACH_MANUALLY = "attach_manually" + # Start the stub, and launch the inferior with an $A packet via the initial packet stream. + _STARTUP_LAUNCH = "launch" + + # GDB Signal numbers that are not target-specific used for common exceptions + TARGET_EXC_BAD_ACCESS = 0x91 + TARGET_EXC_BAD_INSTRUCTION = 0x92 + TARGET_EXC_ARITHMETIC = 0x93 + TARGET_EXC_EMULATION = 0x94 + TARGET_EXC_SOFTWARE = 0x95 + TARGET_EXC_BREAKPOINT = 0x96 + + def setUp(self): + TestBase.setUp(self) + FORMAT = '%(asctime)-15s %(levelname)-8s %(message)s' + logging.basicConfig(format=FORMAT) + self.logger = logging.getLogger(__name__) + self.logger.setLevel(self._LOGGING_LEVEL) + self.test_sequence = GdbRemoteTestSequence(self.logger) + self.set_inferior_startup_launch() + self.port = self.get_next_port() + self.named_pipe_path = None + self.named_pipe = None + self.named_pipe_fd = None + self.stub_sends_two_stop_notifications_on_kill = False + if configuration.lldb_platform_url: + if configuration.lldb_platform_url.startswith('unix-'): + url_pattern = '(.+)://\[?(.+?)\]?/.*' + else: + url_pattern = '(.+)://(.+):\d+' + scheme, host = re.match(url_pattern, configuration.lldb_platform_url).groups() + if configuration.lldb_platform_name == 'remote-android' and host != 'localhost': + self.stub_device = host + self.stub_hostname = 'localhost' + else: + self.stub_device = None + self.stub_hostname = host + else: + self.stub_hostname = "localhost" + + def get_next_port(self): + return 12000 + random.randint(0,3999) + + def reset_test_sequence(self): + self.test_sequence = GdbRemoteTestSequence(self.logger) + + def create_named_pipe(self): + # Create a temp dir and name for a pipe. + temp_dir = tempfile.mkdtemp() + named_pipe_path = os.path.join(temp_dir, "stub_port_number") + + # Create the named pipe. + os.mkfifo(named_pipe_path) + + # Open the read side of the pipe in non-blocking mode. This will return right away, ready or not. + named_pipe_fd = os.open(named_pipe_path, os.O_RDONLY | os.O_NONBLOCK) + + # Create the file for the named pipe. Note this will follow semantics of + # a non-blocking read side of a named pipe, which has different semantics + # than a named pipe opened for read in non-blocking mode. + named_pipe = os.fdopen(named_pipe_fd, "r") + self.assertIsNotNone(named_pipe) + + def shutdown_named_pipe(): + # Close the pipe. + try: + named_pipe.close() + except: + print("failed to close named pipe") + None + + # Delete the pipe. + try: + os.remove(named_pipe_path) + except: + print("failed to delete named pipe: {}".format(named_pipe_path)) + None + + # Delete the temp directory. + try: + os.rmdir(temp_dir) + except: + print("failed to delete temp dir: {}, directory contents: '{}'".format(temp_dir, os.listdir(temp_dir))) + None + + # Add the shutdown hook to clean up the named pipe. + self.addTearDownHook(shutdown_named_pipe) + + # Clear the port so the stub selects a port number. + self.port = 0 + + return (named_pipe_path, named_pipe, named_pipe_fd) + + def get_stub_port_from_named_socket(self, read_timeout_seconds=5): + # Wait for something to read with a max timeout. + (ready_readers, _, _) = select.select([self.named_pipe_fd], [], [], read_timeout_seconds) + self.assertIsNotNone(ready_readers, "write side of pipe has not written anything - stub isn't writing to pipe.") + self.assertNotEqual(len(ready_readers), 0, "write side of pipe has not written anything - stub isn't writing to pipe.") + + # Read the port from the named pipe. + stub_port_raw = self.named_pipe.read() + self.assertIsNotNone(stub_port_raw) + self.assertNotEqual(len(stub_port_raw), 0, "no content to read on pipe") + + # Trim null byte, convert to int. + stub_port_raw = stub_port_raw[:-1] + stub_port = int(stub_port_raw) + self.assertTrue(stub_port > 0) + + return stub_port + + def run_shell_cmd(self, cmd): + platform = self.dbg.GetSelectedPlatform() + shell_cmd = lldb.SBPlatformShellCommand(cmd) + err = platform.Run(shell_cmd) + if err.Fail() or shell_cmd.GetStatus(): + m = "remote_platform.RunShellCommand('%s') failed:\n" % cmd + m += ">>> return code: %d\n" % shell_cmd.GetStatus() + if err.Fail(): + m += ">>> %s\n" % str(err).strip() + m += ">>> %s\n" % (shell_cmd.GetOutput() or + "Command generated no output.") + raise Exception(m) + return shell_cmd.GetOutput().strip() + + def init_llgs_test(self, use_named_pipe=True): + if lldb.remote_platform: + # Remote platforms don't support named pipe based port negotiation + use_named_pipe = False + + # Grab the ppid from /proc/[shell pid]/stat + shell_stat = self.run_shell_cmd("cat /proc/$$/stat") + # [pid] ([executable]) [state] [*ppid*] + pid = re.match(r"^\d+ \(.+\) . (\d+)", shell_stat).group(1) + ls_output = self.run_shell_cmd("ls -l /proc/%s/exe" % pid) + exe = ls_output.split()[-1] + + # If the binary has been deleted, the link name has " (deleted)" appended. + # Remove if it's there. + self.debug_monitor_exe = re.sub(r' \(deleted\)$', '', exe) + else: + self.debug_monitor_exe = get_lldb_server_exe() + if not self.debug_monitor_exe: + self.skipTest("lldb-server exe not found") + + self.debug_monitor_extra_args = ["gdbserver"] + + if len(lldbtest_config.channels) > 0: + self.debug_monitor_extra_args.append("--log-file={}-server.log".format(self.log_basename)) + self.debug_monitor_extra_args.append("--log-channels={}".format(":".join(lldbtest_config.channels))) + + if use_named_pipe: + (self.named_pipe_path, self.named_pipe, self.named_pipe_fd) = self.create_named_pipe() + + def init_debugserver_test(self, use_named_pipe=True): + self.debug_monitor_exe = get_debugserver_exe() + if not self.debug_monitor_exe: + self.skipTest("debugserver exe not found") + self.debug_monitor_extra_args = ["--log-file={}-server.log".format(self.log_basename), "--log-flags=0x800000"] + if use_named_pipe: + (self.named_pipe_path, self.named_pipe, self.named_pipe_fd) = self.create_named_pipe() + # The debugserver stub has a race on handling the 'k' command, so it sends an X09 right away, then sends the real X notification + # when the process truly dies. + self.stub_sends_two_stop_notifications_on_kill = True + + def forward_adb_port(self, source, target, direction, device): + adb = [ 'adb' ] + ([ '-s', device ] if device else []) + [ direction ] + def remove_port_forward(): + subprocess.call(adb + [ "--remove", "tcp:%d" % source]) + + subprocess.call(adb + [ "tcp:%d" % source, "tcp:%d" % target]) + self.addTearDownHook(remove_port_forward) + + def create_socket(self): + sock = socket.socket() + logger = self.logger + + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-.*-android", triple): + self.forward_adb_port(self.port, self.port, "forward", self.stub_device) + + connect_info = (self.stub_hostname, self.port) + sock.connect(connect_info) + + def shutdown_socket(): + if sock: + try: + # send the kill packet so lldb-server shuts down gracefully + sock.sendall(GdbRemoteTestCaseBase._GDBREMOTE_KILL_PACKET) + except: + logger.warning("failed to send kill packet to debug monitor: {}; ignoring".format(sys.exc_info()[0])) + + try: + sock.close() + except: + logger.warning("failed to close socket to debug monitor: {}; ignoring".format(sys.exc_info()[0])) + + self.addTearDownHook(shutdown_socket) + + return sock + + def set_inferior_startup_launch(self): + self._inferior_startup = self._STARTUP_LAUNCH + + def set_inferior_startup_attach(self): + self._inferior_startup = self._STARTUP_ATTACH + + def set_inferior_startup_attach_manually(self): + self._inferior_startup = self._STARTUP_ATTACH_MANUALLY + + def get_debug_monitor_command_line_args(self, attach_pid=None): + if lldb.remote_platform: + commandline_args = self.debug_monitor_extra_args + ["*:{}".format(self.port)] + else: + commandline_args = self.debug_monitor_extra_args + ["localhost:{}".format(self.port)] + + if attach_pid: + commandline_args += ["--attach=%d" % attach_pid] + if self.named_pipe_path: + commandline_args += ["--named-pipe", self.named_pipe_path] + return commandline_args + + def run_platform_command(self, cmd): + platform = self.dbg.GetSelectedPlatform() + shell_command = lldb.SBPlatformShellCommand(cmd) + err = platform.Run(shell_command) + return (err, shell_command.GetOutput()) + + def launch_debug_monitor(self, attach_pid=None, logfile=None): + # Create the command line. + commandline_args = self.get_debug_monitor_command_line_args(attach_pid=attach_pid) + + # Start the server. + server = self.spawnSubprocess(self.debug_monitor_exe, commandline_args, install_remote=False) + self.addTearDownHook(self.cleanupSubprocesses) + self.assertIsNotNone(server) + + # If we're receiving the stub's listening port from the named pipe, do that here. + if self.named_pipe: + self.port = self.get_stub_port_from_named_socket() + + return server + + def connect_to_debug_monitor(self, attach_pid=None): + if self.named_pipe: + # Create the stub. + server = self.launch_debug_monitor(attach_pid=attach_pid) + self.assertIsNotNone(server) + + def shutdown_debug_monitor(): + try: + server.terminate() + except: + logger.warning("failed to terminate server for debug monitor: {}; ignoring".format(sys.exc_info()[0])) + self.addTearDownHook(shutdown_debug_monitor) + + # Schedule debug monitor to be shut down during teardown. + logger = self.logger + + # Attach to the stub and return a socket opened to it. + self.sock = self.create_socket() + return server + + # We're using a random port algorithm to try not to collide with other ports, + # and retry a max # times. + attempts = 0 + MAX_ATTEMPTS = 20 + + while attempts < MAX_ATTEMPTS: + server = self.launch_debug_monitor(attach_pid=attach_pid) + + # Schedule debug monitor to be shut down during teardown. + logger = self.logger + def shutdown_debug_monitor(): + try: + server.terminate() + except: + logger.warning("failed to terminate server for debug monitor: {}; ignoring".format(sys.exc_info()[0])) + self.addTearDownHook(shutdown_debug_monitor) + + connect_attemps = 0 + MAX_CONNECT_ATTEMPTS = 10 + + while connect_attemps < MAX_CONNECT_ATTEMPTS: + # Create a socket to talk to the server + try: + self.sock = self.create_socket() + return server + except socket.error as serr: + # We're only trying to handle connection refused. + if serr.errno != errno.ECONNREFUSED: + raise serr + time.sleep(0.5) + connect_attemps += 1 + + # We should close the server here to be safe. + server.terminate() + + # Increment attempts. + print("connect to debug monitor on port %d failed, attempt #%d of %d" % (self.port, attempts + 1, MAX_ATTEMPTS)) + attempts += 1 + + # And wait a random length of time before next attempt, to avoid collisions. + time.sleep(random.randint(1,5)) + + # Now grab a new port number. + self.port = self.get_next_port() + + raise Exception("failed to create a socket to the launched debug monitor after %d tries" % attempts) + + def launch_process_for_attach(self, inferior_args=None, sleep_seconds=3, exe_path=None): + # We're going to start a child process that the debug monitor stub can later attach to. + # This process needs to be started so that it just hangs around for a while. We'll + # have it sleep. + if not exe_path: + exe_path = os.path.abspath("a.out") + + args = [] + if inferior_args: + args.extend(inferior_args) + if sleep_seconds: + args.append("sleep:%d" % sleep_seconds) + + inferior = self.spawnSubprocess(exe_path, args) + def shutdown_process_for_attach(): + try: + inferior.terminate() + except: + logger.warning("failed to terminate inferior process for attach: {}; ignoring".format(sys.exc_info()[0])) + self.addTearDownHook(shutdown_process_for_attach) + return inferior + + def prep_debug_monitor_and_inferior(self, inferior_args=None, inferior_sleep_seconds=3, inferior_exe_path=None): + """Prep the debug monitor, the inferior, and the expected packet stream. + + Handle the separate cases of using the debug monitor in attach-to-inferior mode + and in launch-inferior mode. + + For attach-to-inferior mode, the inferior process is first started, then + the debug monitor is started in attach to pid mode (using --attach on the + stub command line), and the no-ack-mode setup is appended to the packet + stream. The packet stream is not yet executed, ready to have more expected + packet entries added to it. + + For launch-inferior mode, the stub is first started, then no ack mode is + setup on the expected packet stream, then the verified launch packets are added + to the expected socket stream. The packet stream is not yet executed, ready + to have more expected packet entries added to it. + + The return value is: + {inferior:, server:} + """ + inferior = None + attach_pid = None + + if self._inferior_startup == self._STARTUP_ATTACH or self._inferior_startup == self._STARTUP_ATTACH_MANUALLY: + # Launch the process that we'll use as the inferior. + inferior = self.launch_process_for_attach(inferior_args=inferior_args, sleep_seconds=inferior_sleep_seconds, exe_path=inferior_exe_path) + self.assertIsNotNone(inferior) + self.assertTrue(inferior.pid > 0) + if self._inferior_startup == self._STARTUP_ATTACH: + # In this case, we want the stub to attach via the command line, so set the command line attach pid here. + attach_pid = inferior.pid + + if self._inferior_startup == self._STARTUP_LAUNCH: + # Build launch args + if not inferior_exe_path: + inferior_exe_path = os.path.abspath("a.out") + + if lldb.remote_platform: + remote_path = lldbutil.append_to_process_working_directory(os.path.basename(inferior_exe_path)) + remote_file_spec = lldb.SBFileSpec(remote_path, False) + err = lldb.remote_platform.Install(lldb.SBFileSpec(inferior_exe_path, True), remote_file_spec) + if err.Fail(): + raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (inferior_exe_path, remote_path, err)) + inferior_exe_path = remote_path + + launch_args = [inferior_exe_path] + if inferior_args: + launch_args.extend(inferior_args) + + # Launch the debug monitor stub, attaching to the inferior. + server = self.connect_to_debug_monitor(attach_pid=attach_pid) + self.assertIsNotNone(server) + + # Build the expected protocol stream + self.add_no_ack_remote_stream() + if self._inferior_startup == self._STARTUP_LAUNCH: + self.add_verified_launch_packets(launch_args) + + return {"inferior":inferior, "server":server} + + def expect_socket_recv(self, sock, expected_content_regex, timeout_seconds): + response = "" + timeout_time = time.time() + timeout_seconds + + while not expected_content_regex.match(response) and time.time() < timeout_time: + can_read, _, _ = select.select([sock], [], [], timeout_seconds) + if can_read and sock in can_read: + recv_bytes = sock.recv(4096) + if recv_bytes: + response += recv_bytes + + self.assertTrue(expected_content_regex.match(response)) + + def expect_socket_send(self, sock, content, timeout_seconds): + request_bytes_remaining = content + timeout_time = time.time() + timeout_seconds + + while len(request_bytes_remaining) > 0 and time.time() < timeout_time: + _, can_write, _ = select.select([], [sock], [], timeout_seconds) + if can_write and sock in can_write: + written_byte_count = sock.send(request_bytes_remaining) + request_bytes_remaining = request_bytes_remaining[written_byte_count:] + self.assertEqual(len(request_bytes_remaining), 0) + + def do_handshake(self, stub_socket, timeout_seconds=5): + # Write the ack. + self.expect_socket_send(stub_socket, "+", timeout_seconds) + + # Send the start no ack mode packet. + NO_ACK_MODE_REQUEST = "$QStartNoAckMode#b0" + bytes_sent = stub_socket.send(NO_ACK_MODE_REQUEST) + self.assertEqual(bytes_sent, len(NO_ACK_MODE_REQUEST)) + + # Receive the ack and "OK" + self.expect_socket_recv(stub_socket, re.compile(r"^\+\$OK#[0-9a-fA-F]{2}$"), timeout_seconds) + + # Send the final ack. + self.expect_socket_send(stub_socket, "+", timeout_seconds) + + def add_no_ack_remote_stream(self): + self.test_sequence.add_log_lines( + ["read packet: +", + "read packet: $QStartNoAckMode#b0", + "send packet: +", + "send packet: $OK#9a", + "read packet: +"], + True) + + def add_verified_launch_packets(self, launch_args): + self.test_sequence.add_log_lines( + ["read packet: %s" % build_gdbremote_A_packet(launch_args), + "send packet: $OK#00", + "read packet: $qLaunchSuccess#a5", + "send packet: $OK#00"], + True) + + def add_thread_suffix_request_packets(self): + self.test_sequence.add_log_lines( + ["read packet: $QThreadSuffixSupported#e4", + "send packet: $OK#00", + ], True) + + def add_process_info_collection_packets(self): + self.test_sequence.add_log_lines( + ["read packet: $qProcessInfo#dc", + { "direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"process_info_raw"} }], + True) + + _KNOWN_PROCESS_INFO_KEYS = [ + "pid", + "parent-pid", + "real-uid", + "real-gid", + "effective-uid", + "effective-gid", + "cputype", + "cpusubtype", + "ostype", + "triple", + "vendor", + "endian", + "ptrsize" + ] + + def parse_process_info_response(self, context): + # Ensure we have a process info response. + self.assertIsNotNone(context) + process_info_raw = context.get("process_info_raw") + self.assertIsNotNone(process_info_raw) + + # Pull out key:value; pairs. + process_info_dict = { match.group(1):match.group(2) for match in re.finditer(r"([^:]+):([^;]+);", process_info_raw) } + + # Validate keys are known. + for (key, val) in list(process_info_dict.items()): + self.assertTrue(key in self._KNOWN_PROCESS_INFO_KEYS) + self.assertIsNotNone(val) + + return process_info_dict + + def add_register_info_collection_packets(self): + self.test_sequence.add_log_lines( + [ { "type":"multi_response", "query":"qRegisterInfo", "append_iteration_suffix":True, + "end_regex":re.compile(r"^\$(E\d+)?#[0-9a-fA-F]{2}$"), + "save_key":"reg_info_responses" } ], + True) + + def parse_register_info_packets(self, context): + """Return an array of register info dictionaries, one per register info.""" + reg_info_responses = context.get("reg_info_responses") + self.assertIsNotNone(reg_info_responses) + + # Parse register infos. + return [parse_reg_info_response(reg_info_response) for reg_info_response in reg_info_responses] + + def expect_gdbremote_sequence(self, timeout_seconds=None): + if not timeout_seconds: + timeout_seconds = self._TIMEOUT_SECONDS + return expect_lldb_gdbserver_replay(self, self.sock, self.test_sequence, timeout_seconds, self.logger) + + _KNOWN_REGINFO_KEYS = [ + "name", + "alt-name", + "bitsize", + "offset", + "encoding", + "format", + "set", + "ehframe", + "dwarf", + "generic", + "container-regs", + "invalidate-regs" + ] + + def assert_valid_reg_info(self, reg_info): + # Assert we know about all the reginfo keys parsed. + for key in reg_info: + self.assertTrue(key in self._KNOWN_REGINFO_KEYS) + + # Check the bare-minimum expected set of register info keys. + self.assertTrue("name" in reg_info) + self.assertTrue("bitsize" in reg_info) + self.assertTrue("offset" in reg_info) + self.assertTrue("encoding" in reg_info) + self.assertTrue("format" in reg_info) + + def find_pc_reg_info(self, reg_infos): + lldb_reg_index = 0 + for reg_info in reg_infos: + if ("generic" in reg_info) and (reg_info["generic"] == "pc"): + return (lldb_reg_index, reg_info) + lldb_reg_index += 1 + + return (None, None) + + def add_lldb_register_index(self, reg_infos): + """Add a "lldb_register_index" key containing the 0-baed index of each reg_infos entry. + + We'll use this when we want to call packets like P/p with a register index but do so + on only a subset of the full register info set. + """ + self.assertIsNotNone(reg_infos) + + reg_index = 0 + for reg_info in reg_infos: + reg_info["lldb_register_index"] = reg_index + reg_index += 1 + + def add_query_memory_region_packets(self, address): + self.test_sequence.add_log_lines( + ["read packet: $qMemoryRegionInfo:{0:x}#00".format(address), + {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"memory_region_response"} }], + True) + + def parse_key_val_dict(self, key_val_text, allow_dupes=True): + self.assertIsNotNone(key_val_text) + kv_dict = {} + for match in re.finditer(r";?([^:]+):([^;]+)", key_val_text): + key = match.group(1) + val = match.group(2) + if key in kv_dict: + if allow_dupes: + if type(kv_dict[key]) == list: + kv_dict[key].append(val) + else: + # Promote to list + kv_dict[key] = [kv_dict[key], val] + else: + self.fail("key '{}' already present when attempting to add value '{}' (text='{}', dict={})".format(key, val, key_val_text, kv_dict)) + else: + kv_dict[key] = val + return kv_dict + + def parse_memory_region_packet(self, context): + # Ensure we have a context. + self.assertIsNotNone(context.get("memory_region_response")) + + # Pull out key:value; pairs. + mem_region_dict = self.parse_key_val_dict(context.get("memory_region_response")) + + # Validate keys are known. + for (key, val) in list(mem_region_dict.items()): + self.assertTrue(key in ["start", "size", "permissions", "error"]) + self.assertIsNotNone(val) + + # Return the dictionary of key-value pairs for the memory region. + return mem_region_dict + + def assert_address_within_memory_region(self, test_address, mem_region_dict): + self.assertIsNotNone(mem_region_dict) + self.assertTrue("start" in mem_region_dict) + self.assertTrue("size" in mem_region_dict) + + range_start = int(mem_region_dict["start"], 16) + range_size = int(mem_region_dict["size"], 16) + range_end = range_start + range_size + + if test_address < range_start: + self.fail("address 0x{0:x} comes before range 0x{1:x} - 0x{2:x} (size 0x{3:x})".format(test_address, range_start, range_end, range_size)) + elif test_address >= range_end: + self.fail("address 0x{0:x} comes after range 0x{1:x} - 0x{2:x} (size 0x{3:x})".format(test_address, range_start, range_end, range_size)) + + def add_threadinfo_collection_packets(self): + self.test_sequence.add_log_lines( + [ { "type":"multi_response", "first_query":"qfThreadInfo", "next_query":"qsThreadInfo", + "append_iteration_suffix":False, "end_regex":re.compile(r"^\$(l)?#[0-9a-fA-F]{2}$"), + "save_key":"threadinfo_responses" } ], + True) + + def parse_threadinfo_packets(self, context): + """Return an array of thread ids (decimal ints), one per thread.""" + threadinfo_responses = context.get("threadinfo_responses") + self.assertIsNotNone(threadinfo_responses) + + thread_ids = [] + for threadinfo_response in threadinfo_responses: + new_thread_infos = parse_threadinfo_response(threadinfo_response) + thread_ids.extend(new_thread_infos) + return thread_ids + + def wait_for_thread_count(self, thread_count, timeout_seconds=3): + start_time = time.time() + timeout_time = start_time + timeout_seconds + + actual_thread_count = 0 + while actual_thread_count < thread_count: + self.reset_test_sequence() + self.add_threadinfo_collection_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + threads = self.parse_threadinfo_packets(context) + self.assertIsNotNone(threads) + + actual_thread_count = len(threads) + + if time.time() > timeout_time: + raise Exception( + 'timed out after {} seconds while waiting for theads: waiting for at least {} threads, found {}'.format( + timeout_seconds, thread_count, actual_thread_count)) + + return threads + + def add_set_breakpoint_packets(self, address, do_continue=True, breakpoint_kind=1): + self.test_sequence.add_log_lines( + [# Set the breakpoint. + "read packet: $Z0,{0:x},{1}#00".format(address, breakpoint_kind), + # Verify the stub could set it. + "send packet: $OK#00", + ], True) + + if (do_continue): + self.test_sequence.add_log_lines( + [# Continue the inferior. + "read packet: $c#63", + # Expect a breakpoint stop report. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }, + ], True) + + def add_remove_breakpoint_packets(self, address, breakpoint_kind=1): + self.test_sequence.add_log_lines( + [# Remove the breakpoint. + "read packet: $z0,{0:x},{1}#00".format(address, breakpoint_kind), + # Verify the stub could unset it. + "send packet: $OK#00", + ], True) + + def add_qSupported_packets(self): + self.test_sequence.add_log_lines( + ["read packet: $qSupported#00", + {"direction":"send", "regex":r"^\$(.*)#[0-9a-fA-F]{2}", "capture":{1: "qSupported_response"}}, + ], True) + + _KNOWN_QSUPPORTED_STUB_FEATURES = [ + "augmented-libraries-svr4-read", + "PacketSize", + "QStartNoAckMode", + "QThreadSuffixSupported", + "QListThreadsInStopReply", + "qXfer:auxv:read", + "qXfer:libraries:read", + "qXfer:libraries-svr4:read", + "qXfer:features:read", + "qEcho" + ] + + def parse_qSupported_response(self, context): + self.assertIsNotNone(context) + + raw_response = context.get("qSupported_response") + self.assertIsNotNone(raw_response) + + # For values with key=val, the dict key and vals are set as expected. For feature+, feature- and feature?, the + # +,-,? is stripped from the key and set as the value. + supported_dict = {} + for match in re.finditer(r";?([^=;]+)(=([^;]+))?", raw_response): + key = match.group(1) + val = match.group(3) + + # key=val: store as is + if val and len(val) > 0: + supported_dict[key] = val + else: + if len(key) < 2: + raise Exception("singular stub feature is too short: must be stub_feature{+,-,?}") + supported_type = key[-1] + key = key[:-1] + if not supported_type in ["+", "-", "?"]: + raise Exception("malformed stub feature: final character {} not in expected set (+,-,?)".format(supported_type)) + supported_dict[key] = supported_type + # Ensure we know the supported element + if not key in self._KNOWN_QSUPPORTED_STUB_FEATURES: + raise Exception("unknown qSupported stub feature reported: %s" % key) + + return supported_dict + + def run_process_then_stop(self, run_seconds=1): + # Tell the stub to continue. + self.test_sequence.add_log_lines( + ["read packet: $vCont;c#a8"], + True) + context = self.expect_gdbremote_sequence() + + # Wait for run_seconds. + time.sleep(run_seconds) + + # Send an interrupt, capture a T response. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: {}".format(chr(3)), + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result"} }], + True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + self.assertIsNotNone(context.get("stop_result")) + + return context + + def select_modifiable_register(self, reg_infos): + """Find a register that can be read/written freely.""" + PREFERRED_REGISTER_NAMES = set(["rax",]) + + # First check for the first register from the preferred register name set. + alternative_register_index = None + + self.assertIsNotNone(reg_infos) + for reg_info in reg_infos: + if ("name" in reg_info) and (reg_info["name"] in PREFERRED_REGISTER_NAMES): + # We found a preferred register. Use it. + return reg_info["lldb_register_index"] + if ("generic" in reg_info) and (reg_info["generic"] == "fp"): + # A frame pointer register will do as a register to modify temporarily. + alternative_register_index = reg_info["lldb_register_index"] + + # We didn't find a preferred register. Return whatever alternative register + # we found, if any. + return alternative_register_index + + def extract_registers_from_stop_notification(self, stop_key_vals_text): + self.assertIsNotNone(stop_key_vals_text) + kv_dict = self.parse_key_val_dict(stop_key_vals_text) + + registers = {} + for (key, val) in list(kv_dict.items()): + if re.match(r"^[0-9a-fA-F]+$", key): + registers[int(key, 16)] = val + return registers + + def gather_register_infos(self): + self.reset_test_sequence() + self.add_register_info_collection_packets() + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + reg_infos = self.parse_register_info_packets(context) + self.assertIsNotNone(reg_infos) + self.add_lldb_register_index(reg_infos) + + return reg_infos + + def find_generic_register_with_name(self, reg_infos, generic_name): + self.assertIsNotNone(reg_infos) + for reg_info in reg_infos: + if ("generic" in reg_info) and (reg_info["generic"] == generic_name): + return reg_info + return None + + def decode_gdbremote_binary(self, encoded_bytes): + decoded_bytes = "" + i = 0 + while i < len(encoded_bytes): + if encoded_bytes[i] == "}": + # Handle escaped char. + self.assertTrue(i + 1 < len(encoded_bytes)) + decoded_bytes += chr(ord(encoded_bytes[i+1]) ^ 0x20) + i +=2 + elif encoded_bytes[i] == "*": + # Handle run length encoding. + self.assertTrue(len(decoded_bytes) > 0) + self.assertTrue(i + 1 < len(encoded_bytes)) + repeat_count = ord(encoded_bytes[i+1]) - 29 + decoded_bytes += decoded_bytes[-1] * repeat_count + i += 2 + else: + decoded_bytes += encoded_bytes[i] + i += 1 + return decoded_bytes + + def build_auxv_dict(self, endian, word_size, auxv_data): + self.assertIsNotNone(endian) + self.assertIsNotNone(word_size) + self.assertIsNotNone(auxv_data) + + auxv_dict = {} + + while len(auxv_data) > 0: + # Chop off key. + raw_key = auxv_data[:word_size] + auxv_data = auxv_data[word_size:] + + # Chop of value. + raw_value = auxv_data[:word_size] + auxv_data = auxv_data[word_size:] + + # Convert raw text from target endian. + key = unpack_endian_binary_string(endian, raw_key) + value = unpack_endian_binary_string(endian, raw_value) + + # Handle ending entry. + if key == 0: + self.assertEqual(value, 0) + return auxv_dict + + # The key should not already be present. + self.assertFalse(key in auxv_dict) + auxv_dict[key] = value + + self.fail("should not reach here - implies required double zero entry not found") + return auxv_dict + + def read_binary_data_in_chunks(self, command_prefix, chunk_length): + """Collect command_prefix{offset:x},{chunk_length:x} until a single 'l' or 'l' with data is returned.""" + offset = 0 + done = False + decoded_data = "" + + while not done: + # Grab the next iteration of data. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + "read packet: ${}{:x},{:x}:#00".format(command_prefix, offset, chunk_length), + {"direction":"send", "regex":re.compile(r"^\$([^E])(.*)#[0-9a-fA-F]{2}$", re.MULTILINE|re.DOTALL), "capture":{1:"response_type", 2:"content_raw"} } + ], True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + response_type = context.get("response_type") + self.assertIsNotNone(response_type) + self.assertTrue(response_type in ["l", "m"]) + + # Move offset along. + offset += chunk_length + + # Figure out if we're done. We're done if the response type is l. + done = response_type == "l" + + # Decode binary data. + content_raw = context.get("content_raw") + if content_raw and len(content_raw) > 0: + self.assertIsNotNone(content_raw) + decoded_data += self.decode_gdbremote_binary(content_raw) + return decoded_data + + def add_interrupt_packets(self): + self.test_sequence.add_log_lines([ + # Send the intterupt. + "read packet: {}".format(chr(3)), + # And wait for the stop notification. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})(.*)#[0-9a-fA-F]{2}$", "capture":{1:"stop_signo", 2:"stop_key_val_text" } }, + ], True) + + def parse_interrupt_packets(self, context): + self.assertIsNotNone(context.get("stop_signo")) + self.assertIsNotNone(context.get("stop_key_val_text")) + return (int(context["stop_signo"], 16), self.parse_key_val_dict(context["stop_key_val_text"])) + + def add_QSaveRegisterState_packets(self, thread_id): + if thread_id: + # Use the thread suffix form. + request = "read packet: $QSaveRegisterState;thread:{:x}#00".format(thread_id) + else: + request = "read packet: $QSaveRegisterState#00" + + self.test_sequence.add_log_lines([ + request, + {"direction":"send", "regex":r"^\$(E?.*)#[0-9a-fA-F]{2}$", "capture":{1:"save_response" } }, + ], True) + + def parse_QSaveRegisterState_response(self, context): + self.assertIsNotNone(context) + + save_response = context.get("save_response") + self.assertIsNotNone(save_response) + + if len(save_response) < 1 or save_response[0] == "E": + # error received + return (False, None) + else: + return (True, int(save_response)) + + def add_QRestoreRegisterState_packets(self, save_id, thread_id=None): + if thread_id: + # Use the thread suffix form. + request = "read packet: $QRestoreRegisterState:{};thread:{:x}#00".format(save_id, thread_id) + else: + request = "read packet: $QRestoreRegisterState:{}#00".format(save_id) + + self.test_sequence.add_log_lines([ + request, + "send packet: $OK#00" + ], True) + + def flip_all_bits_in_each_register_value(self, reg_infos, endian, thread_id=None): + self.assertIsNotNone(reg_infos) + + successful_writes = 0 + failed_writes = 0 + + for reg_info in reg_infos: + # Use the lldb register index added to the reg info. We're not necessarily + # working off a full set of register infos, so an inferred register index could be wrong. + reg_index = reg_info["lldb_register_index"] + self.assertIsNotNone(reg_index) + + reg_byte_size = int(reg_info["bitsize"])/8 + self.assertTrue(reg_byte_size > 0) + + # Handle thread suffix. + if thread_id: + p_request = "read packet: $p{:x};thread:{:x}#00".format(reg_index, thread_id) + else: + p_request = "read packet: $p{:x}#00".format(reg_index) + + # Read the existing value. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + p_request, + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Verify the response length. + p_response = context.get("p_response") + self.assertIsNotNone(p_response) + initial_reg_value = unpack_register_hex_unsigned(endian, p_response) + + # Flip the value by xoring with all 1s + all_one_bits_raw = "ff" * (int(reg_info["bitsize"]) / 8) + flipped_bits_int = initial_reg_value ^ int(all_one_bits_raw, 16) + # print("reg (index={}, name={}): val={}, flipped bits (int={}, hex={:x})".format(reg_index, reg_info["name"], initial_reg_value, flipped_bits_int, flipped_bits_int)) + + # Handle thread suffix for P. + if thread_id: + P_request = "read packet: $P{:x}={};thread:{:x}#00".format(reg_index, pack_register_hex(endian, flipped_bits_int, byte_size=reg_byte_size), thread_id) + else: + P_request = "read packet: $P{:x}={}#00".format(reg_index, pack_register_hex(endian, flipped_bits_int, byte_size=reg_byte_size)) + + # Write the flipped value to the register. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + P_request, + { "direction":"send", "regex":r"^\$(OK|E[0-9a-fA-F]+)#[0-9a-fA-F]{2}", "capture":{1:"P_response"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Determine if the write succeeded. There are a handful of registers that can fail, or partially fail + # (e.g. flags, segment selectors, etc.) due to register value restrictions. Don't worry about them + # all flipping perfectly. + P_response = context.get("P_response") + self.assertIsNotNone(P_response) + if P_response == "OK": + successful_writes += 1 + else: + failed_writes += 1 + # print("reg (index={}, name={}) write FAILED (error: {})".format(reg_index, reg_info["name"], P_response)) + + # Read back the register value, ensure it matches the flipped value. + if P_response == "OK": + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + p_request, + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + verify_p_response_raw = context.get("p_response") + self.assertIsNotNone(verify_p_response_raw) + verify_bits = unpack_register_hex_unsigned(endian, verify_p_response_raw) + + if verify_bits != flipped_bits_int: + # Some registers, like mxcsrmask and others, will permute what's written. Adjust succeed/fail counts. + # print("reg (index={}, name={}): read verify FAILED: wrote {:x}, verify read back {:x}".format(reg_index, reg_info["name"], flipped_bits_int, verify_bits)) + successful_writes -= 1 + failed_writes +=1 + + return (successful_writes, failed_writes) + + def is_bit_flippable_register(self, reg_info): + if not reg_info: + return False + if not "set" in reg_info: + return False + if reg_info["set"] != "General Purpose Registers": + return False + if ("container-regs" in reg_info) and (len(reg_info["container-regs"]) > 0): + # Don't try to bit flip registers contained in another register. + return False + if re.match("^.s$", reg_info["name"]): + # This is a 2-letter register name that ends in "s", like a segment register. + # Don't try to bit flip these. + return False + if re.match("^(c|)psr$", reg_info["name"]): + # This is an ARM program status register; don't flip it. + return False + # Okay, this looks fine-enough. + return True + + def read_register_values(self, reg_infos, endian, thread_id=None): + self.assertIsNotNone(reg_infos) + values = {} + + for reg_info in reg_infos: + # We append a register index when load reg infos so we can work with subsets. + reg_index = reg_info.get("lldb_register_index") + self.assertIsNotNone(reg_index) + + # Handle thread suffix. + if thread_id: + p_request = "read packet: $p{:x};thread:{:x}#00".format(reg_index, thread_id) + else: + p_request = "read packet: $p{:x}#00".format(reg_index) + + # Read it with p. + self.reset_test_sequence() + self.test_sequence.add_log_lines([ + p_request, + { "direction":"send", "regex":r"^\$([0-9a-fA-F]+)#", "capture":{1:"p_response"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Convert value from target endian to integral. + p_response = context.get("p_response") + self.assertIsNotNone(p_response) + self.assertTrue(len(p_response) > 0) + self.assertFalse(p_response[0] == "E") + + values[reg_index] = unpack_register_hex_unsigned(endian, p_response) + + return values + + def add_vCont_query_packets(self): + self.test_sequence.add_log_lines([ + "read packet: $vCont?#49", + {"direction":"send", "regex":r"^\$(vCont)?(.*)#[0-9a-fA-F]{2}$", "capture":{2:"vCont_query_response" } }, + ], True) + + def parse_vCont_query_response(self, context): + self.assertIsNotNone(context) + vCont_query_response = context.get("vCont_query_response") + + # Handle case of no vCont support at all - in which case the capture group will be none or zero length. + if not vCont_query_response or len(vCont_query_response) == 0: + return {} + + return {key:1 for key in vCont_query_response.split(";") if key and len(key) > 0} + + def count_single_steps_until_true(self, thread_id, predicate, args, max_step_count=100, use_Hc_packet=True, step_instruction="s"): + """Used by single step test that appears in a few different contexts.""" + single_step_count = 0 + + while single_step_count < max_step_count: + self.assertIsNotNone(thread_id) + + # Build the packet for the single step instruction. We replace {thread}, if present, with the thread_id. + step_packet = "read packet: ${}#00".format(re.sub(r"{thread}", "{:x}".format(thread_id), step_instruction)) + # print("\nstep_packet created: {}\n".format(step_packet)) + + # Single step. + self.reset_test_sequence() + if use_Hc_packet: + self.test_sequence.add_log_lines( + [# Set the continue thread. + "read packet: $Hc{0:x}#00".format(thread_id), + "send packet: $OK#00", + ], True) + self.test_sequence.add_log_lines([ + # Single step. + step_packet, + # "read packet: $vCont;s:{0:x}#00".format(thread_id), + # Expect a breakpoint stop report. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }, + ], True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + self.assertIsNotNone(context.get("stop_signo")) + self.assertEqual(int(context.get("stop_signo"), 16), + lldbutil.get_signal_number('SIGTRAP')) + + single_step_count += 1 + + # See if the predicate is true. If so, we're done. + if predicate(args): + return (True, single_step_count) + + # The predicate didn't return true within the runaway step count. + return (False, single_step_count) + + def g_c1_c2_contents_are(self, args): + """Used by single step test that appears in a few different contexts.""" + g_c1_address = args["g_c1_address"] + g_c2_address = args["g_c2_address"] + expected_g_c1 = args["expected_g_c1"] + expected_g_c2 = args["expected_g_c2"] + + # Read g_c1 and g_c2 contents. + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $m{0:x},{1:x}#00".format(g_c1_address, 1), + {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"g_c1_contents"} }, + "read packet: $m{0:x},{1:x}#00".format(g_c2_address, 1), + {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"g_c2_contents"} }], + True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Check if what we read from inferior memory is what we are expecting. + self.assertIsNotNone(context.get("g_c1_contents")) + self.assertIsNotNone(context.get("g_c2_contents")) + + return (context.get("g_c1_contents").decode("hex") == expected_g_c1) and (context.get("g_c2_contents").decode("hex") == expected_g_c2) + + def single_step_only_steps_one_instruction(self, use_Hc_packet=True, step_instruction="s"): + """Used by single step test that appears in a few different contexts.""" + # Start up the inferior. + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["get-code-address-hex:swap_chars", "get-data-address-hex:g_c1", "get-data-address-hex:g_c2", "sleep:1", "call-function:swap_chars", "sleep:5"]) + + # Run the process + self.test_sequence.add_log_lines( + [# Start running after initial stop. + "read packet: $c#63", + # Match output line that prints the memory address of the function call entry point. + # Note we require launch-only testing so we can get inferior otuput. + { "type":"output_match", "regex":r"^code address: 0x([0-9a-fA-F]+)\r\ndata address: 0x([0-9a-fA-F]+)\r\ndata address: 0x([0-9a-fA-F]+)\r\n$", + "capture":{ 1:"function_address", 2:"g_c1_address", 3:"g_c2_address"} }, + # Now stop the inferior. + "read packet: {}".format(chr(3)), + # And wait for the stop notification. + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }], + True) + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Grab the main thread id. + self.assertIsNotNone(context.get("stop_thread_id")) + main_thread_id = int(context.get("stop_thread_id"), 16) + + # Grab the function address. + self.assertIsNotNone(context.get("function_address")) + function_address = int(context.get("function_address"), 16) + + # Grab the data addresses. + self.assertIsNotNone(context.get("g_c1_address")) + g_c1_address = int(context.get("g_c1_address"), 16) + + self.assertIsNotNone(context.get("g_c2_address")) + g_c2_address = int(context.get("g_c2_address"), 16) + + # Set a breakpoint at the given address. + if self.getArchitecture() == "arm": + # TODO: Handle case when setting breakpoint in thumb code + BREAKPOINT_KIND = 4 + else: + BREAKPOINT_KIND = 1 + self.reset_test_sequence() + self.add_set_breakpoint_packets(function_address, do_continue=True, breakpoint_kind=BREAKPOINT_KIND) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Remove the breakpoint. + self.reset_test_sequence() + self.add_remove_breakpoint_packets(function_address, breakpoint_kind=BREAKPOINT_KIND) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Verify g_c1 and g_c2 match expected initial state. + args = {} + args["g_c1_address"] = g_c1_address + args["g_c2_address"] = g_c2_address + args["expected_g_c1"] = "0" + args["expected_g_c2"] = "1" + + self.assertTrue(self.g_c1_c2_contents_are(args)) + + # Verify we take only a small number of steps to hit the first state. Might need to work through function entry prologue code. + args["expected_g_c1"] = "1" + args["expected_g_c2"] = "1" + (state_reached, step_count) = self.count_single_steps_until_true(main_thread_id, self.g_c1_c2_contents_are, args, max_step_count=25, use_Hc_packet=use_Hc_packet, step_instruction=step_instruction) + self.assertTrue(state_reached) + + # Verify we hit the next state. + args["expected_g_c1"] = "1" + args["expected_g_c2"] = "0" + (state_reached, step_count) = self.count_single_steps_until_true(main_thread_id, self.g_c1_c2_contents_are, args, max_step_count=5, use_Hc_packet=use_Hc_packet, step_instruction=step_instruction) + self.assertTrue(state_reached) + expected_step_count = 1 + arch = self.getArchitecture() + + #MIPS required "3" (ADDIU, SB, LD) machine instructions for updation of variable value + if re.match("mips",arch): + expected_step_count = 3 + self.assertEqual(step_count, expected_step_count) + + # Verify we hit the next state. + args["expected_g_c1"] = "0" + args["expected_g_c2"] = "0" + (state_reached, step_count) = self.count_single_steps_until_true(main_thread_id, self.g_c1_c2_contents_are, args, max_step_count=5, use_Hc_packet=use_Hc_packet, step_instruction=step_instruction) + self.assertTrue(state_reached) + self.assertEqual(step_count, expected_step_count) + + # Verify we hit the next state. + args["expected_g_c1"] = "0" + args["expected_g_c2"] = "1" + (state_reached, step_count) = self.count_single_steps_until_true(main_thread_id, self.g_c1_c2_contents_are, args, max_step_count=5, use_Hc_packet=use_Hc_packet, step_instruction=step_instruction) + self.assertTrue(state_reached) + self.assertEqual(step_count, expected_step_count) + diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/Makefile b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/Makefile new file mode 100644 index 000000000000..a47e2797fd84 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../make + +CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS -std=c++11 +# LD_EXTRAS := -lpthread +CXX_SOURCES := main.cpp +MAKE_DSYM :=NO + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/TestGdbRemoteAbort.py b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/TestGdbRemoteAbort.py new file mode 100644 index 000000000000..d13433252e12 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/TestGdbRemoteAbort.py @@ -0,0 +1,42 @@ +from __future__ import print_function + + + +import gdbremote_testcase +import signal +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteAbort(gdbremote_testcase.GdbRemoteTestCaseBase): + mydir = TestBase.compute_mydir(__file__) + + def inferior_abort_received(self): + procs = self.prep_debug_monitor_and_inferior(inferior_args=["abort"]) + self.assertIsNotNone(procs) + + self.test_sequence.add_log_lines([ + "read packet: $vCont;c#a8", + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", "capture":{ 1:"hex_exit_code"} }, + ], True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + hex_exit_code = context.get("hex_exit_code") + self.assertIsNotNone(hex_exit_code) + self.assertEqual(int(hex_exit_code, 16), + lldbutil.get_signal_number('SIGABRT')) + + @debugserver_test + def test_inferior_abort_received_debugserver(self): + self.init_debugserver_test() + self.build() + self.inferior_abort_received() + + @llgs_test + # std::abort() on <= API 16 raises SIGSEGV - b.android.com/179836 + @expectedFailureAndroid(api_levels=list(range(16 + 1))) + def test_inferior_abort_received_llgs(self): + self.init_llgs_test() + self.build() + self.inferior_abort_received() + diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/TestGdbRemoteSegFault.py b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/TestGdbRemoteSegFault.py new file mode 100644 index 000000000000..6618d8f75fc9 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/TestGdbRemoteSegFault.py @@ -0,0 +1,40 @@ +from __future__ import print_function + + + +import gdbremote_testcase +import signal +from lldbsuite.test.lldbtest import * + +class TestGdbRemoteSegFault(gdbremote_testcase.GdbRemoteTestCaseBase): + mydir = TestBase.compute_mydir(__file__) + + GDB_REMOTE_STOP_CODE_BAD_ACCESS = 0x91 + + def inferior_seg_fault_received(self, expected_signo): + procs = self.prep_debug_monitor_and_inferior(inferior_args=["segfault"]) + self.assertIsNotNone(procs) + + self.test_sequence.add_log_lines([ + "read packet: $vCont;c#a8", + {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", "capture":{ 1:"hex_exit_code"} }, + ], True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + hex_exit_code = context.get("hex_exit_code") + self.assertIsNotNone(hex_exit_code) + self.assertEqual(int(hex_exit_code, 16), expected_signo) + + @debugserver_test + def test_inferior_seg_fault_received_debugserver(self): + self.init_debugserver_test() + self.build() + self.inferior_seg_fault_received(self.GDB_REMOTE_STOP_CODE_BAD_ACCESS) + + @llgs_test + def test_inferior_seg_fault_received_llgs(self): + self.init_llgs_test() + self.build() + self.inferior_seg_fault_received(lldbutil.get_signal_number('SIGSEGV')) diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/main.cpp new file mode 100644 index 000000000000..69d60071aa45 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/main.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +namespace +{ + const char *const SEGFAULT_COMMAND = "segfault"; + const char *const ABORT_COMMAND = "abort"; +} + +int main (int argc, char **argv) +{ + if (argc < 2) + { + std::cout << "expected at least one command provided on the command line" << std::endl; + } + + // Process command line args. + for (int i = 1; i < argc; ++i) + { + const char *const command = argv[i]; + if (std::strstr (command, SEGFAULT_COMMAND)) + { + // Perform a null pointer access. + int *const null_int_ptr = nullptr; + *null_int_ptr = 0xDEAD; + } + else if (std::strstr (command, ABORT_COMMAND)) + { + std::abort(); + } + else + { + std::cout << "Unsupported command: " << command << std::endl; + } + } + + return 0; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py b/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py new file mode 100644 index 000000000000..c0ea841e2a03 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py @@ -0,0 +1,843 @@ +"""Module for supporting unit testing of the lldb-server debug monitor exe. +""" + +from __future__ import print_function + + + +import os +import os.path +import platform +import re +import six +import socket_packet_pump +import subprocess +import time +from lldbsuite.test.lldbtest import * + +from six.moves import queue + +def _get_debug_monitor_from_lldb(lldb_exe, debug_monitor_basename): + """Return the debug monitor exe path given the lldb exe path. + + This method attempts to construct a valid debug monitor exe name + from a given lldb exe name. It will return None if the synthesized + debug monitor name is not found to exist. + + The debug monitor exe path is synthesized by taking the directory + of the lldb exe, and replacing the portion of the base name that + matches "lldb" (case insensitive) and replacing with the value of + debug_monitor_basename. + + Args: + lldb_exe: the path to an lldb executable. + + debug_monitor_basename: the base name portion of the debug monitor + that will replace 'lldb'. + + Returns: + A path to the debug monitor exe if it is found to exist; otherwise, + returns None. + + """ + if not lldb_exe: + return None + + exe_dir = os.path.dirname(lldb_exe) + exe_base = os.path.basename(lldb_exe) + + # we'll rebuild the filename by replacing lldb with + # the debug monitor basename, keeping any prefix or suffix in place. + regex = re.compile(r"lldb", re.IGNORECASE) + new_base = regex.sub(debug_monitor_basename, exe_base) + + debug_monitor_exe = os.path.join(exe_dir, new_base) + if os.path.exists(debug_monitor_exe): + return debug_monitor_exe + + new_base = regex.sub( 'LLDB.framework/Versions/A/Resources/' + debug_monitor_basename, exe_base) + debug_monitor_exe = os.path.join(exe_dir, new_base) + if os.path.exists(debug_monitor_exe): + return debug_monitor_exe + + return None + + +def get_lldb_server_exe(): + """Return the lldb-server exe path. + + Returns: + A path to the lldb-server exe if it is found to exist; otherwise, + returns None. + """ + if "LLDB_DEBUGSERVER_PATH" in os.environ: + return os.environ["LLDB_DEBUGSERVER_PATH"] + + return _get_debug_monitor_from_lldb(lldbtest_config.lldbExec, "lldb-server") + +def get_debugserver_exe(): + """Return the debugserver exe path. + + Returns: + A path to the debugserver exe if it is found to exist; otherwise, + returns None. + """ + if "LLDB_DEBUGSERVER_PATH" in os.environ: + return os.environ["LLDB_DEBUGSERVER_PATH"] + + return _get_debug_monitor_from_lldb(lldbtest_config.lldbExec, "debugserver") + +_LOG_LINE_REGEX = re.compile(r'^(lldb-server|debugserver)\s+<\s*(\d+)>' + + '\s+(read|send)\s+packet:\s+(.+)$') + + +def _is_packet_lldb_gdbserver_input(packet_type, llgs_input_is_read): + """Return whether a given packet is input for lldb-gdbserver. + + Args: + packet_type: a string indicating 'send' or 'receive', from a + gdbremote packet protocol log. + + llgs_input_is_read: true if lldb-gdbserver input (content sent to + lldb-gdbserver) is listed as 'read' or 'send' in the packet + log entry. + + Returns: + True if the packet should be considered input for lldb-gdbserver; False + otherwise. + """ + if packet_type == 'read': + # when llgs is the read side, then a read packet is meant for + # input to llgs (when captured from the llgs/debugserver exe). + return llgs_input_is_read + elif packet_type == 'send': + # when llgs is the send side, then a send packet is meant to + # be input to llgs (when captured from the lldb exe). + return not llgs_input_is_read + else: + # don't understand what type of packet this is + raise "Unknown packet type: {}".format(packet_type) + + +def handle_O_packet(context, packet_contents, logger): + """Handle O packets.""" + if (not packet_contents) or (len(packet_contents) < 1): + return False + elif packet_contents[0] != "O": + return False + elif packet_contents == "OK": + return False + + new_text = gdbremote_hex_decode_string(packet_contents[1:]) + context["O_content"] += new_text + context["O_count"] += 1 + + if logger: + logger.debug("text: new \"{}\", cumulative: \"{}\"".format(new_text, context["O_content"])) + + return True + +_STRIP_CHECKSUM_REGEX = re.compile(r'#[0-9a-fA-F]{2}$') +_STRIP_COMMAND_PREFIX_REGEX = re.compile(r"^\$") +_STRIP_COMMAND_PREFIX_M_REGEX = re.compile(r"^\$m") + + +def assert_packets_equal(asserter, actual_packet, expected_packet): + # strip off the checksum digits of the packet. When we're in + # no-ack mode, the # checksum is ignored, and should not be cause + # for a mismatched packet. + actual_stripped = _STRIP_CHECKSUM_REGEX.sub('', actual_packet) + expected_stripped = _STRIP_CHECKSUM_REGEX.sub('', expected_packet) + asserter.assertEqual(actual_stripped, expected_stripped) + +def expect_lldb_gdbserver_replay( + asserter, + sock, + test_sequence, + timeout_seconds, + logger=None): + """Replay socket communication with lldb-gdbserver and verify responses. + + Args: + asserter: the object providing assertEqual(first, second, msg=None), e.g. TestCase instance. + + sock: the TCP socket connected to the lldb-gdbserver exe. + + test_sequence: a GdbRemoteTestSequence instance that describes + the messages sent to the gdb remote and the responses + expected from it. + + timeout_seconds: any response taking more than this number of + seconds will cause an exception to be raised. + + logger: a Python logger instance. + + Returns: + The context dictionary from running the given gdbremote + protocol sequence. This will contain any of the capture + elements specified to any GdbRemoteEntry instances in + test_sequence. + + The context will also contain an entry, context["O_content"] + which contains the text from the inferior received via $O + packets. $O packets should not attempt to be matched + directly since they are not entirely deterministic as to + how many arrive and how much text is in each one. + + context["O_count"] will contain an integer of the number of + O packets received. + """ + + # Ensure we have some work to do. + if len(test_sequence.entries) < 1: + return {} + + context = {"O_count":0, "O_content":""} + with socket_packet_pump.SocketPacketPump(sock, logger) as pump: + # Grab the first sequence entry. + sequence_entry = test_sequence.entries.pop(0) + + # While we have an active sequence entry, send messages + # destined for the stub and collect/match/process responses + # expected from the stub. + while sequence_entry: + if sequence_entry.is_send_to_remote(): + # This is an entry to send to the remote debug monitor. + send_packet = sequence_entry.get_send_packet() + if logger: + if len(send_packet) == 1 and send_packet[0] == chr(3): + packet_desc = "^C" + else: + packet_desc = send_packet + logger.info("sending packet to remote: {}".format(packet_desc)) + sock.sendall(send_packet) + else: + # This is an entry expecting to receive content from the remote debug monitor. + + # We'll pull from (and wait on) the queue appropriate for the type of matcher. + # We keep separate queues for process output (coming from non-deterministic + # $O packet division) and for all other packets. + if sequence_entry.is_output_matcher(): + try: + # Grab next entry from the output queue. + content = pump.output_queue().get(True, timeout_seconds) + except queue.Empty: + if logger: + logger.warning("timeout waiting for stub output (accumulated output:{})".format(pump.get_accumulated_output())) + raise Exception("timed out while waiting for output match (accumulated output: {})".format(pump.get_accumulated_output())) + else: + try: + content = pump.packet_queue().get(True, timeout_seconds) + except queue.Empty: + if logger: + logger.warning("timeout waiting for packet match (receive buffer: {})".format(pump.get_receive_buffer())) + raise Exception("timed out while waiting for packet match (receive buffer: {})".format(pump.get_receive_buffer())) + + # Give the sequence entry the opportunity to match the content. + # Output matchers might match or pass after more output accumulates. + # Other packet types generally must match. + asserter.assertIsNotNone(content) + context = sequence_entry.assert_match(asserter, content, context=context) + + # Move on to next sequence entry as needed. Some sequence entries support executing multiple + # times in different states (for looping over query/response packets). + if sequence_entry.is_consumed(): + if len(test_sequence.entries) > 0: + sequence_entry = test_sequence.entries.pop(0) + else: + sequence_entry = None + + # Fill in the O_content entries. + context["O_count"] = 1 + context["O_content"] = pump.get_accumulated_output() + + return context + +def gdbremote_hex_encode_string(str): + output = '' + for c in str: + output += '{0:02x}'.format(ord(c)) + return output + +def gdbremote_hex_decode_string(str): + return str.decode("hex") + +def gdbremote_packet_encode_string(str): + checksum = 0 + for c in str: + checksum += ord(c) + return '$' + str + '#{0:02x}'.format(checksum % 256) + +def build_gdbremote_A_packet(args_list): + """Given a list of args, create a properly-formed $A packet containing each arg. + """ + payload = "A" + + # build the arg content + arg_index = 0 + for arg in args_list: + # Comma-separate the args. + if arg_index > 0: + payload += ',' + + # Hex-encode the arg. + hex_arg = gdbremote_hex_encode_string(arg) + + # Build the A entry. + payload += "{},{},{}".format(len(hex_arg), arg_index, hex_arg) + + # Next arg index, please. + arg_index += 1 + + # return the packetized payload + return gdbremote_packet_encode_string(payload) + + +def parse_reg_info_response(response_packet): + if not response_packet: + raise Exception("response_packet cannot be None") + + # Strip off prefix $ and suffix #xx if present. + response_packet = _STRIP_COMMAND_PREFIX_REGEX.sub("", response_packet) + response_packet = _STRIP_CHECKSUM_REGEX.sub("", response_packet) + + # Build keyval pairs + values = {} + for kv in response_packet.split(";"): + if len(kv) < 1: + continue + (key, val) = kv.split(':') + values[key] = val + + return values + + +def parse_threadinfo_response(response_packet): + if not response_packet: + raise Exception("response_packet cannot be None") + + # Strip off prefix $ and suffix #xx if present. + response_packet = _STRIP_COMMAND_PREFIX_M_REGEX.sub("", response_packet) + response_packet = _STRIP_CHECKSUM_REGEX.sub("", response_packet) + + # Return list of thread ids + return [int(thread_id_hex,16) for thread_id_hex in response_packet.split(",") if len(thread_id_hex) > 0] + +def unpack_endian_binary_string(endian, value_string): + """Unpack a gdb-remote binary (post-unescaped, i.e. not escaped) response to an unsigned int given endianness of the inferior.""" + if not endian: + raise Exception("endian cannot be None") + if not value_string or len(value_string) < 1: + raise Exception("value_string cannot be None or empty") + + if endian == 'little': + value = 0 + i = 0 + while len(value_string) > 0: + value += (ord(value_string[0]) << i) + value_string = value_string[1:] + i += 8 + return value + elif endian == 'big': + value = 0 + while len(value_string) > 0: + value = (value << 8) + ord(value_string[0]) + value_string = value_string[1:] + return value + else: + # pdp is valid but need to add parse code once needed. + raise Exception("unsupported endian:{}".format(endian)) + +def unpack_register_hex_unsigned(endian, value_string): + """Unpack a gdb-remote $p-style response to an unsigned int given endianness of inferior.""" + if not endian: + raise Exception("endian cannot be None") + if not value_string or len(value_string) < 1: + raise Exception("value_string cannot be None or empty") + + if endian == 'little': + value = 0 + i = 0 + while len(value_string) > 0: + value += (int(value_string[0:2], 16) << i) + value_string = value_string[2:] + i += 8 + return value + elif endian == 'big': + return int(value_string, 16) + else: + # pdp is valid but need to add parse code once needed. + raise Exception("unsupported endian:{}".format(endian)) + +def pack_register_hex(endian, value, byte_size=None): + """Unpack a gdb-remote $p-style response to an unsigned int given endianness of inferior.""" + if not endian: + raise Exception("endian cannot be None") + + if endian == 'little': + # Create the litt-endian return value. + retval = "" + while value != 0: + retval = retval + "{:02x}".format(value & 0xff) + value = value >> 8 + if byte_size: + # Add zero-fill to the right/end (MSB side) of the value. + retval += "00" * (byte_size - len(retval)/2) + return retval + + elif endian == 'big': + retval = value.encode("hex") + if byte_size: + # Add zero-fill to the left/front (MSB side) of the value. + retval = ("00" * (byte_size - len(retval)/2)) + retval + return retval + + else: + # pdp is valid but need to add parse code once needed. + raise Exception("unsupported endian:{}".format(endian)) + +class GdbRemoteEntryBase(object): + def is_output_matcher(self): + return False + +class GdbRemoteEntry(GdbRemoteEntryBase): + + def __init__(self, is_send_to_remote=True, exact_payload=None, regex=None, capture=None, expect_captures=None): + """Create an entry representing one piece of the I/O to/from a gdb remote debug monitor. + + Args: + + is_send_to_remote: True if this entry is a message to be + sent to the gdbremote debug monitor; False if this + entry represents text to be matched against the reply + from the gdbremote debug monitor. + + exact_payload: if not None, then this packet is an exact + send (when sending to the remote) or an exact match of + the response from the gdbremote. The checksums are + ignored on exact match requests since negotiation of + no-ack makes the checksum content essentially + undefined. + + regex: currently only valid for receives from gdbremote. + When specified (and only if exact_payload is None), + indicates the gdbremote response must match the given + regex. Match groups in the regex can be used for two + different purposes: saving the match (see capture + arg), or validating that a match group matches a + previously established value (see expect_captures). It + is perfectly valid to have just a regex arg and to + specify neither capture or expect_captures args. This + arg only makes sense if exact_payload is not + specified. + + capture: if specified, is a dictionary of regex match + group indices (should start with 1) to variable names + that will store the capture group indicated by the + index. For example, {1:"thread_id"} will store capture + group 1's content in the context dictionary where + "thread_id" is the key and the match group value is + the value. The value stored off can be used later in a + expect_captures expression. This arg only makes sense + when regex is specified. + + expect_captures: if specified, is a dictionary of regex + match group indices (should start with 1) to variable + names, where the match group should match the value + existing in the context at the given variable name. + For example, {2:"thread_id"} indicates that the second + match group must match the value stored under the + context's previously stored "thread_id" key. This arg + only makes sense when regex is specified. + """ + self._is_send_to_remote = is_send_to_remote + self.exact_payload = exact_payload + self.regex = regex + self.capture = capture + self.expect_captures = expect_captures + + def is_send_to_remote(self): + return self._is_send_to_remote + + def is_consumed(self): + # For now, all packets are consumed after first use. + return True + + def get_send_packet(self): + if not self.is_send_to_remote(): + raise Exception("get_send_packet() called on GdbRemoteEntry that is not a send-to-remote packet") + if not self.exact_payload: + raise Exception("get_send_packet() called on GdbRemoteEntry but it doesn't have an exact payload") + return self.exact_payload + + def _assert_exact_payload_match(self, asserter, actual_packet): + assert_packets_equal(asserter, actual_packet, self.exact_payload) + return None + + def _assert_regex_match(self, asserter, actual_packet, context): + # Ensure the actual packet matches from the start of the actual packet. + match = self.regex.match(actual_packet) + if not match: + asserter.fail("regex '{}' failed to match against content '{}'".format(self.regex.pattern, actual_packet)) + + if self.capture: + # Handle captures. + for group_index, var_name in list(self.capture.items()): + capture_text = match.group(group_index) + # It is okay for capture text to be None - which it will be if it is a group that can match nothing. + # The user must be okay with it since the regex itself matched above. + context[var_name] = capture_text + + if self.expect_captures: + # Handle comparing matched groups to context dictionary entries. + for group_index, var_name in list(self.expect_captures.items()): + capture_text = match.group(group_index) + if not capture_text: + raise Exception("No content to expect for group index {}".format(group_index)) + asserter.assertEqual(capture_text, context[var_name]) + + return context + + def assert_match(self, asserter, actual_packet, context=None): + # This only makes sense for matching lines coming from the + # remote debug monitor. + if self.is_send_to_remote(): + raise Exception("Attempted to match a packet being sent to the remote debug monitor, doesn't make sense.") + + # Create a new context if needed. + if not context: + context = {} + + # If this is an exact payload, ensure they match exactly, + # ignoring the packet checksum which is optional for no-ack + # mode. + if self.exact_payload: + self._assert_exact_payload_match(asserter, actual_packet) + return context + elif self.regex: + return self._assert_regex_match(asserter, actual_packet, context) + else: + raise Exception("Don't know how to match a remote-sent packet when exact_payload isn't specified.") + +class MultiResponseGdbRemoteEntry(GdbRemoteEntryBase): + """Represents a query/response style packet. + + Assumes the first item is sent to the gdb remote. + An end sequence regex indicates the end of the query/response + packet sequence. All responses up through (but not including) the + end response are stored in a context variable. + + Settings accepted from params: + + next_query or query: required. The typical query packet without the $ prefix or #xx suffix. + If there is a special first packet to start the iteration query, see the + first_query key. + + first_query: optional. If the first query requires a special query command, specify + it with this key. Do not specify the $ prefix or #xx suffix. + + append_iteration_suffix: defaults to False. Specify True if the 0-based iteration + index should be appended as a suffix to the command. e.g. qRegisterInfo with + this key set true will generate query packets of qRegisterInfo0, qRegisterInfo1, + etc. + + end_regex: required. Specifies a compiled regex object that will match the full text + of any response that signals an end to the iteration. It must include the + initial $ and ending #xx and must match the whole packet. + + save_key: required. Specifies the key within the context where an array will be stored. + Each packet received from the gdb remote that does not match the end_regex will get + appended to the array stored within the context at that key. + + runaway_response_count: optional. Defaults to 10000. If this many responses are retrieved, + assume there is something wrong with either the response collection or the ending + detection regex and throw an exception. + """ + def __init__(self, params): + self._next_query = params.get("next_query", params.get("query")) + if not self._next_query: + raise "either next_query or query key must be specified for MultiResponseGdbRemoteEntry" + + self._first_query = params.get("first_query", self._next_query) + self._append_iteration_suffix = params.get("append_iteration_suffix", False) + self._iteration = 0 + self._end_regex = params["end_regex"] + self._save_key = params["save_key"] + self._runaway_response_count = params.get("runaway_response_count", 10000) + self._is_send_to_remote = True + self._end_matched = False + + def is_send_to_remote(self): + return self._is_send_to_remote + + def get_send_packet(self): + if not self.is_send_to_remote(): + raise Exception("get_send_packet() called on MultiResponseGdbRemoteEntry that is not in the send state") + if self._end_matched: + raise Exception("get_send_packet() called on MultiResponseGdbRemoteEntry but end of query/response sequence has already been seen.") + + # Choose the first or next query for the base payload. + if self._iteration == 0 and self._first_query: + payload = self._first_query + else: + payload = self._next_query + + # Append the suffix as needed. + if self._append_iteration_suffix: + payload += "%x" % self._iteration + + # Keep track of the iteration. + self._iteration += 1 + + # Now that we've given the query packet, flip the mode to receive/match. + self._is_send_to_remote = False + + # Return the result, converted to packet form. + return gdbremote_packet_encode_string(payload) + + def is_consumed(self): + return self._end_matched + + def assert_match(self, asserter, actual_packet, context=None): + # This only makes sense for matching lines coming from the remote debug monitor. + if self.is_send_to_remote(): + raise Exception("assert_match() called on MultiResponseGdbRemoteEntry but state is set to send a query packet.") + + if self._end_matched: + raise Exception("assert_match() called on MultiResponseGdbRemoteEntry but end of query/response sequence has already been seen.") + + # Set up a context as needed. + if not context: + context = {} + + # Check if the packet matches the end condition. + match = self._end_regex.match(actual_packet) + if match: + # We're done iterating. + self._end_matched = True + return context + + # Not done iterating - save the packet. + context[self._save_key] = context.get(self._save_key, []) + context[self._save_key].append(actual_packet) + + # Check for a runaway response cycle. + if len(context[self._save_key]) >= self._runaway_response_count: + raise Exception("runaway query/response cycle detected: %d responses captured so far. Last response: %s" % + (len(context[self._save_key]), context[self._save_key][-1])) + + # Flip the mode to send for generating the query. + self._is_send_to_remote = True + return context + +class MatchRemoteOutputEntry(GdbRemoteEntryBase): + """Waits for output from the debug monitor to match a regex or time out. + + This entry type tries to match each time new gdb remote output is accumulated + using a provided regex. If the output does not match the regex within the + given timeframe, the command fails the playback session. If the regex does + match, any capture fields are recorded in the context. + + Settings accepted from params: + + regex: required. Specifies a compiled regex object that must either succeed + with re.match or re.search (see regex_mode below) within the given timeout + (see timeout_seconds below) or cause the playback to fail. + + regex_mode: optional. Available values: "match" or "search". If "match", the entire + stub output as collected so far must match the regex. If search, then the regex + must match starting somewhere within the output text accumulated thus far. + Default: "match" (i.e. the regex must match the entirety of the accumulated output + buffer, so unexpected text will generally fail the match). + + capture: optional. If specified, is a dictionary of regex match group indices (should start + with 1) to variable names that will store the capture group indicated by the + index. For example, {1:"thread_id"} will store capture group 1's content in the + context dictionary where "thread_id" is the key and the match group value is + the value. The value stored off can be used later in a expect_captures expression. + This arg only makes sense when regex is specified. + """ + def __init__(self, regex=None, regex_mode="match", capture=None): + self._regex = regex + self._regex_mode = regex_mode + self._capture = capture + self._matched = False + + if not self._regex: + raise Exception("regex cannot be None") + + if not self._regex_mode in ["match", "search"]: + raise Exception("unsupported regex mode \"{}\": must be \"match\" or \"search\"".format(self._regex_mode)) + + def is_output_matcher(self): + return True + + def is_send_to_remote(self): + # This is always a "wait for remote" command. + return False + + def is_consumed(self): + return self._matched + + def assert_match(self, asserter, accumulated_output, context): + # Validate args. + if not accumulated_output: + raise Exception("accumulated_output cannot be none") + if not context: + raise Exception("context cannot be none") + + # Validate that we haven't already matched. + if self._matched: + raise Exception("invalid state - already matched, attempting to match again") + + # If we don't have any content yet, we don't match. + if len(accumulated_output) < 1: + return context + + # Check if we match + if self._regex_mode == "match": + match = self._regex.match(accumulated_output) + elif self._regex_mode == "search": + match = self._regex.search(accumulated_output) + else: + raise Exception("Unexpected regex mode: {}".format(self._regex_mode)) + + # If we don't match, wait to try again after next $O content, or time out. + if not match: + # print("re pattern \"{}\" did not match against \"{}\"".format(self._regex.pattern, accumulated_output)) + return context + + # We do match. + self._matched = True + # print("re pattern \"{}\" matched against \"{}\"".format(self._regex.pattern, accumulated_output)) + + # Collect up any captures into the context. + if self._capture: + # Handle captures. + for group_index, var_name in list(self._capture.items()): + capture_text = match.group(group_index) + if not capture_text: + raise Exception("No content for group index {}".format(group_index)) + context[var_name] = capture_text + + return context + + +class GdbRemoteTestSequence(object): + + _LOG_LINE_REGEX = re.compile(r'^.*(read|send)\s+packet:\s+(.+)$') + + def __init__(self, logger): + self.entries = [] + self.logger = logger + + def add_log_lines(self, log_lines, remote_input_is_read): + for line in log_lines: + if type(line) == str: + # Handle log line import + # if self.logger: + # self.logger.debug("processing log line: {}".format(line)) + match = self._LOG_LINE_REGEX.match(line) + if match: + playback_packet = match.group(2) + direction = match.group(1) + if _is_packet_lldb_gdbserver_input(direction, remote_input_is_read): + # Handle as something to send to the remote debug monitor. + # if self.logger: + # self.logger.info("processed packet to send to remote: {}".format(playback_packet)) + self.entries.append(GdbRemoteEntry(is_send_to_remote=True, exact_payload=playback_packet)) + else: + # Log line represents content to be expected from the remote debug monitor. + # if self.logger: + # self.logger.info("receiving packet from llgs, should match: {}".format(playback_packet)) + self.entries.append(GdbRemoteEntry(is_send_to_remote=False,exact_payload=playback_packet)) + else: + raise Exception("failed to interpret log line: {}".format(line)) + elif type(line) == dict: + entry_type = line.get("type", "regex_capture") + if entry_type == "regex_capture": + # Handle more explicit control over details via dictionary. + direction = line.get("direction", None) + regex = line.get("regex", None) + capture = line.get("capture", None) + expect_captures = line.get("expect_captures", None) + + # Compile the regex. + if regex and (type(regex) == str): + regex = re.compile(regex) + + if _is_packet_lldb_gdbserver_input(direction, remote_input_is_read): + # Handle as something to send to the remote debug monitor. + # if self.logger: + # self.logger.info("processed dict sequence to send to remote") + self.entries.append(GdbRemoteEntry(is_send_to_remote=True, regex=regex, capture=capture, expect_captures=expect_captures)) + else: + # Log line represents content to be expected from the remote debug monitor. + # if self.logger: + # self.logger.info("processed dict sequence to match receiving from remote") + self.entries.append(GdbRemoteEntry(is_send_to_remote=False, regex=regex, capture=capture, expect_captures=expect_captures)) + elif entry_type == "multi_response": + self.entries.append(MultiResponseGdbRemoteEntry(line)) + elif entry_type == "output_match": + + regex = line.get("regex", None) + # Compile the regex. + if regex and (type(regex) == str): + regex = re.compile(regex) + + regex_mode = line.get("regex_mode", "match") + capture = line.get("capture", None) + self.entries.append(MatchRemoteOutputEntry(regex=regex, regex_mode=regex_mode, capture=capture)) + else: + raise Exception("unknown entry type \"%s\"" % entry_type) + +def process_is_running(pid, unknown_value=True): + """If possible, validate that the given pid represents a running process on the local system. + + Args: + + pid: an OS-specific representation of a process id. Should be an integral value. + + unknown_value: value used when we cannot determine how to check running local + processes on the OS. + + Returns: + + If we can figure out how to check running process ids on the given OS: + return True if the process is running, or False otherwise. + + If we don't know how to check running process ids on the given OS: + return the value provided by the unknown_value arg. + """ + if not isinstance(pid, six.integer_types): + raise Exception("pid must be an integral type (actual type: %s)" % str(type(pid))) + + process_ids = [] + + if lldb.remote_platform: + # Don't know how to get list of running process IDs on a remote + # platform + return unknown_value + elif platform.system() in ['Darwin', 'Linux', 'FreeBSD', 'NetBSD']: + # Build the list of running process ids + output = subprocess.check_output("ps ax | awk '{ print $1; }'", shell=True) + text_process_ids = output.split('\n')[1:] + # Convert text pids to ints + process_ids = [int(text_pid) for text_pid in text_process_ids if text_pid != ''] + # elif {your_platform_here}: + # fill in process_ids as a list of int type process IDs running on + # the local system. + else: + # Don't know how to get list of running process IDs on this + # OS, so return the "don't know" value. + return unknown_value + + # Check if the pid is in the process_ids + return pid in process_ids + +if __name__ == '__main__': + EXE_PATH = get_lldb_server_exe() + if EXE_PATH: + print("lldb-server path detected: {}".format(EXE_PATH)) + else: + print("lldb-server could not be found") diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/main.cpp new file mode 100644 index 000000000000..c65b22571596 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/main.cpp @@ -0,0 +1,404 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2) +int pthread_threadid_np(pthread_t,__uint64_t*); +#elif defined(__linux__) +#include +#endif + +#if defined(__linux__) +#include +#endif + +static const char *const RETVAL_PREFIX = "retval:"; +static const char *const SLEEP_PREFIX = "sleep:"; +static const char *const STDERR_PREFIX = "stderr:"; +static const char *const SET_MESSAGE_PREFIX = "set-message:"; +static const char *const PRINT_MESSAGE_COMMAND = "print-message:"; +static const char *const GET_DATA_ADDRESS_PREFIX = "get-data-address-hex:"; +static const char *const GET_STACK_ADDRESS_COMMAND = "get-stack-address-hex:"; +static const char *const GET_HEAP_ADDRESS_COMMAND = "get-heap-address-hex:"; + +static const char *const GET_CODE_ADDRESS_PREFIX = "get-code-address-hex:"; +static const char *const CALL_FUNCTION_PREFIX = "call-function:"; + +static const char *const THREAD_PREFIX = "thread:"; +static const char *const THREAD_COMMAND_NEW = "new"; +static const char *const THREAD_COMMAND_PRINT_IDS = "print-ids"; +static const char *const THREAD_COMMAND_SEGFAULT = "segfault"; + +static bool g_print_thread_ids = false; +static pthread_mutex_t g_print_mutex = PTHREAD_MUTEX_INITIALIZER; +static bool g_threads_do_segfault = false; + +static pthread_mutex_t g_jump_buffer_mutex = PTHREAD_MUTEX_INITIALIZER; +static jmp_buf g_jump_buffer; +static bool g_is_segfaulting = false; + +static char g_message[256]; + +static volatile char g_c1 = '0'; +static volatile char g_c2 = '1'; + +static void +print_thread_id () +{ + // Put in the right magic here for your platform to spit out the thread id (tid) that debugserver/lldb-gdbserver would see as a TID. + // Otherwise, let the else clause print out the unsupported text so that the unit test knows to skip verifying thread ids. +#if defined(__APPLE__) + __uint64_t tid = 0; + pthread_threadid_np(pthread_self(), &tid); + printf ("%" PRIx64, tid); +#elif defined (__linux__) + // This is a call to gettid() via syscall. + printf ("%" PRIx64, static_cast (syscall (__NR_gettid))); +#else + printf("{no-tid-support}"); +#endif +} + +static void +signal_handler (int signo) +{ + const char *signal_name = nullptr; + switch (signo) + { + case SIGUSR1: signal_name = "SIGUSR1"; break; + case SIGSEGV: signal_name = "SIGSEGV"; break; + default: signal_name = nullptr; + } + + // Print notice that we received the signal on a given thread. + pthread_mutex_lock (&g_print_mutex); + if (signal_name) + printf ("received %s on thread id: ", signal_name); + else + printf ("received signo %d (%s) on thread id: ", signo, strsignal (signo)); + print_thread_id (); + printf ("\n"); + pthread_mutex_unlock (&g_print_mutex); + + // Reset the signal handler if we're one of the expected signal handlers. + switch (signo) + { + case SIGSEGV: + if (g_is_segfaulting) + { + // Fix up the pointer we're writing to. This needs to happen if nothing intercepts the SIGSEGV + // (i.e. if somebody runs this from the command line). + longjmp(g_jump_buffer, 1); + } + break; + case SIGUSR1: + if (g_is_segfaulting) + { + // Fix up the pointer we're writing to. This is used to test gdb remote signal delivery. + // A SIGSEGV will be raised when the thread is created, switched out for a SIGUSR1, and + // then this code still needs to fix the seg fault. + // (i.e. if somebody runs this from the command line). + longjmp(g_jump_buffer, 1); + } + break; + } + + // Reset the signal handler. + sig_t sig_result = signal (signo, signal_handler); + if (sig_result == SIG_ERR) + { + fprintf(stderr, "failed to set signal handler: errno=%d\n", errno); + exit (1); + } +} + +static void +swap_chars () +{ + g_c1 = '1'; + g_c2 = '0'; + + g_c1 = '0'; + g_c2 = '1'; +} + +static void +hello () +{ + pthread_mutex_lock (&g_print_mutex); + printf ("hello, world\n"); + pthread_mutex_unlock (&g_print_mutex); +} + +static void* +thread_func (void *arg) +{ + static pthread_mutex_t s_thread_index_mutex = PTHREAD_MUTEX_INITIALIZER; + static int s_thread_index = 1; + + pthread_mutex_lock (&s_thread_index_mutex); + const int this_thread_index = s_thread_index++; + pthread_mutex_unlock (&s_thread_index_mutex); + + if (g_print_thread_ids) + { + pthread_mutex_lock (&g_print_mutex); + printf ("thread %d id: ", this_thread_index); + print_thread_id (); + printf ("\n"); + pthread_mutex_unlock (&g_print_mutex); + } + + if (g_threads_do_segfault) + { + // Sleep for a number of seconds based on the thread index. + // TODO add ability to send commands to test exe so we can + // handle timing more precisely. This is clunky. All we're + // trying to do is add predictability as to the timing of + // signal generation by created threads. + int sleep_seconds = 2 * (this_thread_index - 1); + while (sleep_seconds > 0) + sleep_seconds = sleep(sleep_seconds); + + // Test creating a SEGV. + pthread_mutex_lock (&g_jump_buffer_mutex); + g_is_segfaulting = true; + int *bad_p = nullptr; + if (setjmp(g_jump_buffer) == 0) + { + // Force a seg fault signal on this thread. + *bad_p = 0; + } + else + { + // Tell the system we're no longer seg faulting. + // Used by the SIGUSR1 signal handler that we inject + // in place of the SIGSEGV so it only tries to + // recover from the SIGSEGV if this seg fault code + // was in play. + g_is_segfaulting = false; + } + pthread_mutex_unlock (&g_jump_buffer_mutex); + + pthread_mutex_lock (&g_print_mutex); + printf ("thread "); + print_thread_id (); + printf (": past SIGSEGV\n"); + pthread_mutex_unlock (&g_print_mutex); + } + + int sleep_seconds_remaining = 60; + while (sleep_seconds_remaining > 0) + { + sleep_seconds_remaining = sleep (sleep_seconds_remaining); + } + + return nullptr; +} + +int main (int argc, char **argv) +{ +#if defined(__linux__) + // Immediately enable any ptracer so that we can allow the stub attach + // operation to succeed. Some Linux kernels are locked down so that + // only an ancestor can be a ptracer of a process. This disables that + // restriction. Without it, attach-related stub tests will fail. +#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY) + const int prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); + static_cast (prctl_result); +#endif +#endif + + std::vector threads; + std::unique_ptr heap_array_up; + int return_value = 0; + + // Set the signal handler. + sig_t sig_result = signal (SIGALRM, signal_handler); + if (sig_result == SIG_ERR) + { + fprintf(stderr, "failed to set SIGALRM signal handler: errno=%d\n", errno); + exit (1); + } + + sig_result = signal (SIGUSR1, signal_handler); + if (sig_result == SIG_ERR) + { + fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno); + exit (1); + } + + sig_result = signal (SIGSEGV, signal_handler); + if (sig_result == SIG_ERR) + { + fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno); + exit (1); + } + + // Process command line args. + for (int i = 1; i < argc; ++i) + { + if (std::strstr (argv[i], STDERR_PREFIX)) + { + // Treat remainder as text to go to stderr. + fprintf (stderr, "%s\n", (argv[i] + strlen (STDERR_PREFIX))); + } + else if (std::strstr (argv[i], RETVAL_PREFIX)) + { + // Treat as the return value for the program. + return_value = std::atoi (argv[i] + strlen (RETVAL_PREFIX)); + } + else if (std::strstr (argv[i], SLEEP_PREFIX)) + { + // Treat as the amount of time to have this process sleep (in seconds). + int sleep_seconds_remaining = std::atoi (argv[i] + strlen (SLEEP_PREFIX)); + + // Loop around, sleeping until all sleep time is used up. Note that + // signals will cause sleep to end early with the number of seconds remaining. + for (int i = 0; sleep_seconds_remaining > 0; ++i) + { + sleep_seconds_remaining = sleep (sleep_seconds_remaining); + // std::cout << "sleep result (call " << i << "): " << sleep_seconds_remaining << std::endl; + } + } + else if (std::strstr (argv[i], SET_MESSAGE_PREFIX)) + { + // Copy the contents after "set-message:" to the g_message buffer. + // Used for reading inferior memory and verifying contents match expectations. + strncpy (g_message, argv[i] + strlen (SET_MESSAGE_PREFIX), sizeof (g_message)); + + // Ensure we're null terminated. + g_message[sizeof (g_message) - 1] = '\0'; + + } + else if (std::strstr (argv[i], PRINT_MESSAGE_COMMAND)) + { + pthread_mutex_lock (&g_print_mutex); + printf ("message: %s\n", g_message); + pthread_mutex_unlock (&g_print_mutex); + } + else if (std::strstr (argv[i], GET_DATA_ADDRESS_PREFIX)) + { + volatile void *data_p = nullptr; + + if (std::strstr (argv[i] + strlen (GET_DATA_ADDRESS_PREFIX), "g_message")) + data_p = &g_message[0]; + else if (std::strstr (argv[i] + strlen (GET_DATA_ADDRESS_PREFIX), "g_c1")) + data_p = &g_c1; + else if (std::strstr (argv[i] + strlen (GET_DATA_ADDRESS_PREFIX), "g_c2")) + data_p = &g_c2; + + pthread_mutex_lock (&g_print_mutex); + printf ("data address: %p\n", data_p); + pthread_mutex_unlock (&g_print_mutex); + } + else if (std::strstr (argv[i], GET_HEAP_ADDRESS_COMMAND)) + { + // Create a byte array if not already present. + if (!heap_array_up) + heap_array_up.reset (new uint8_t[32]); + + pthread_mutex_lock (&g_print_mutex); + printf ("heap address: %p\n", heap_array_up.get ()); + pthread_mutex_unlock (&g_print_mutex); + } + else if (std::strstr (argv[i], GET_STACK_ADDRESS_COMMAND)) + { + pthread_mutex_lock (&g_print_mutex); + printf ("stack address: %p\n", &return_value); + pthread_mutex_unlock (&g_print_mutex); + } + else if (std::strstr (argv[i], GET_CODE_ADDRESS_PREFIX)) + { + void (*func_p)() = nullptr; + + if (std::strstr (argv[i] + strlen (GET_CODE_ADDRESS_PREFIX), "hello")) + func_p = hello; + else if (std::strstr (argv[i] + strlen (GET_CODE_ADDRESS_PREFIX), "swap_chars")) + func_p = swap_chars; + + pthread_mutex_lock (&g_print_mutex); + printf ("code address: %p\n", func_p); + pthread_mutex_unlock (&g_print_mutex); + } + else if (std::strstr (argv[i], CALL_FUNCTION_PREFIX)) + { + // Defaut to providing the address of main. + if (std::strcmp (argv[i] + strlen (CALL_FUNCTION_PREFIX), "hello") == 0) + hello(); + else if (std::strcmp (argv[i] + strlen (CALL_FUNCTION_PREFIX), "swap_chars") == 0) + swap_chars(); + else + { + pthread_mutex_lock (&g_print_mutex); + printf ("unknown function: %s\n", argv[i] + strlen (CALL_FUNCTION_PREFIX)); + pthread_mutex_unlock (&g_print_mutex); + } + } + else if (std::strstr (argv[i], THREAD_PREFIX)) + { + // Check if we're creating a new thread. + if (std::strstr (argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_NEW)) + { + // Create a new thread. + pthread_t new_thread; + const int err = ::pthread_create (&new_thread, nullptr, thread_func, nullptr); + if (err) + { + fprintf (stderr, "pthread_create() failed with error code %d\n", err); + exit (err); + } + threads.push_back (new_thread); + } + else if (std::strstr (argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_PRINT_IDS)) + { + // Turn on thread id announcing. + g_print_thread_ids = true; + + // And announce us. + pthread_mutex_lock (&g_print_mutex); + printf ("thread 0 id: "); + print_thread_id (); + printf ("\n"); + pthread_mutex_unlock (&g_print_mutex); + } + else if (std::strstr (argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_SEGFAULT)) + { + g_threads_do_segfault = true; + } + else + { + // At this point we don't do anything else with threads. + // Later use thread index and send command to thread. + } + } + else + { + // Treat the argument as text for stdout. + printf("%s\n", argv[i]); + } + } + + // If we launched any threads, join them + for (std::vector::iterator it = threads.begin (); it != threads.end (); ++it) + { + void *thread_retval = nullptr; + const int err = ::pthread_join (*it, &thread_retval); + if (err != 0) + fprintf (stderr, "pthread_join() failed with error code %d\n", err); + } + + return return_value; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/Makefile b/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/TestPlatformProcessConnect.py b/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/TestPlatformProcessConnect.py new file mode 100644 index 000000000000..b50a03015462 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/TestPlatformProcessConnect.py @@ -0,0 +1,56 @@ +from __future__ import print_function + +import gdbremote_testcase +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestPlatformProcessConnect(gdbremote_testcase.GdbRemoteTestCaseBase): + mydir = TestBase.compute_mydir(__file__) + + @llgs_test + @no_debug_info_test + @skipIf(remote=False) + @expectedFailureAll(hostoslist=["windows"], triple='.*-android') + def test_platform_process_connect(self): + self.build() + self.init_llgs_test(False) + + working_dir = lldb.remote_platform.GetWorkingDirectory() + err = lldb.remote_platform.Put(lldb.SBFileSpec(os.path.join(os.getcwd(), "a.out")), + lldb.SBFileSpec(os.path.join(working_dir, "a.out"))) + if err.Fail(): + raise RuntimeError("Unable copy '%s' to '%s'.\n>>> %s" % (f, wd, err.GetCString())) + + port_file = "%s/port" % working_dir + commandline_args = ["platform", "--listen", "*:0", "--socket-file", port_file, "--", "%s/a.out" % working_dir, "foo"] + self.spawnSubprocess(self.debug_monitor_exe, commandline_args, install_remote=False) + self.addTearDownHook(self.cleanupSubprocesses) + new_port = self.run_shell_cmd("while [ ! -f %s ]; do sleep 0.25; done && cat %s" % (port_file, port_file)) + + new_debugger = lldb.SBDebugger.Create() + new_debugger.SetAsync(False) + def del_debugger(new_debugger=new_debugger): + del new_debugger + self.addTearDownHook(del_debugger) + + new_platform = lldb.SBPlatform(lldb.remote_platform.GetName()) + new_debugger.SetSelectedPlatform(new_platform) + new_interpreter = new_debugger.GetCommandInterpreter() + + m = re.search("(.*):[0-9]+", configuration.lldb_platform_url) + command = "platform connect %s:%s" % (m.group(1), new_port) + result = lldb.SBCommandReturnObject() + new_interpreter.HandleCommand(command, result) + self.assertTrue(result.Succeeded(), "platform process connect failed: %s" % result.GetOutput()) + + target = new_debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetThreadAtIndex(0) + + breakpoint = target.BreakpointCreateByName("main") + process.Continue() + + frame = thread.GetFrameAtIndex(0) + self.assertEqual(frame.GetFunction().GetName(), "main") + self.assertEqual(frame.FindVariable("argc").GetValueAsSigned(), 2) + process.Continue() diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp new file mode 100644 index 000000000000..70ae5094fde7 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp @@ -0,0 +1,7 @@ +#include + +int main (int argc, char **argv) +{ + printf("argc: %d\n", argc); + return argv[0][0]; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/socket_packet_pump.py b/packages/Python/lldbsuite/test/tools/lldb-server/socket_packet_pump.py new file mode 100644 index 000000000000..795a8c6652d0 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/socket_packet_pump.py @@ -0,0 +1,187 @@ + +from __future__ import print_function + + + +import re +import select +import threading +import traceback +import codecs + +from six.moves import queue + +def _handle_output_packet_string(packet_contents): + if (not packet_contents) or (len(packet_contents) < 1): + return None + elif packet_contents[0] != "O": + return None + elif packet_contents == "OK": + return None + else: + return packet_contents[1:].decode("hex") + +def _dump_queue(the_queue): + while not the_queue.empty(): + print(codecs.encode(the_queue.get(True), "string_escape")) + print("\n") + +class SocketPacketPump(object): + """A threaded packet reader that partitions packets into two streams. + + All incoming $O packet content is accumulated with the current accumulation + state put into the OutputQueue. + + All other incoming packets are placed in the packet queue. + + A select thread can be started and stopped, and runs to place packet + content into the two queues. + """ + + _GDB_REMOTE_PACKET_REGEX = re.compile(r'^\$([^\#]*)#[0-9a-fA-F]{2}') + + def __init__(self, pump_socket, logger=None): + if not pump_socket: + raise Exception("pump_socket cannot be None") + + self._output_queue = queue.Queue() + self._packet_queue = queue.Queue() + self._thread = None + self._stop_thread = False + self._socket = pump_socket + self._logger = logger + self._receive_buffer = "" + self._accumulated_output = "" + + def __enter__(self): + """Support the python 'with' statement. + + Start the pump thread.""" + self.start_pump_thread() + return self + + def __exit__(self, exit_type, value, the_traceback): + """Support the python 'with' statement. + + Shut down the pump thread.""" + self.stop_pump_thread() + + # Warn if there is any content left in any of the queues. + # That would represent unmatched packets. + if not self.output_queue().empty(): + print("warning: output queue entries still exist:") + _dump_queue(self.output_queue()) + print("from here:") + traceback.print_stack() + + if not self.packet_queue().empty(): + print("warning: packet queue entries still exist:") + _dump_queue(self.packet_queue()) + print("from here:") + traceback.print_stack() + + def start_pump_thread(self): + if self._thread: + raise Exception("pump thread is already running") + self._stop_thread = False + self._thread = threading.Thread(target=self._run_method) + self._thread.start() + + def stop_pump_thread(self): + self._stop_thread = True + if self._thread: + self._thread.join() + + def output_queue(self): + return self._output_queue + + def packet_queue(self): + return self._packet_queue + + def _process_new_bytes(self, new_bytes): + if not new_bytes: + return + if len(new_bytes) < 1: + return + + # Add new bytes to our accumulated unprocessed packet bytes. + self._receive_buffer += new_bytes + + # Parse fully-formed packets into individual packets. + has_more = len(self._receive_buffer) > 0 + while has_more: + if len(self._receive_buffer) <= 0: + has_more = False + # handle '+' ack + elif self._receive_buffer[0] == "+": + self._packet_queue.put("+") + self._receive_buffer = self._receive_buffer[1:] + if self._logger: + self._logger.debug( + "parsed packet from stub: +\n" + + "new receive_buffer: {}".format( + self._receive_buffer)) + else: + packet_match = self._GDB_REMOTE_PACKET_REGEX.match( + self._receive_buffer) + if packet_match: + # Our receive buffer matches a packet at the + # start of the receive buffer. + new_output_content = _handle_output_packet_string( + packet_match.group(1)) + if new_output_content: + # This was an $O packet with new content. + self._accumulated_output += new_output_content + self._output_queue.put(self._accumulated_output) + else: + # Any packet other than $O. + self._packet_queue.put(packet_match.group(0)) + + # Remove the parsed packet from the receive + # buffer. + self._receive_buffer = self._receive_buffer[ + len(packet_match.group(0)):] + if self._logger: + self._logger.debug( + "parsed packet from stub: " + + packet_match.group(0)) + self._logger.debug( + "new receive_buffer: " + + self._receive_buffer) + else: + # We don't have enough in the receive bufferto make a full + # packet. Stop trying until we read more. + has_more = False + + def _run_method(self): + self._receive_buffer = "" + self._accumulated_output = "" + + if self._logger: + self._logger.info("socket pump starting") + + # Keep looping around until we're asked to stop the thread. + while not self._stop_thread: + can_read, _, _ = select.select([self._socket], [], [], 0) + if can_read and self._socket in can_read: + try: + new_bytes = self._socket.recv(4096) + if self._logger and new_bytes and len(new_bytes) > 0: + self._logger.debug( + "pump received bytes: {}".format(new_bytes)) + except: + # Likely a closed socket. Done with the pump thread. + if self._logger: + self._logger.debug( + "socket read failed, stopping pump read thread") + break + self._process_new_bytes(new_bytes) + + if self._logger: + self._logger.info("socket pump exiting") + + def get_accumulated_output(self): + return self._accumulated_output + + def get_receive_buffer(self): + return self._receive_buffer diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/test/test_lldbgdbserverutils.py b/packages/Python/lldbsuite/test/tools/lldb-server/test/test_lldbgdbserverutils.py new file mode 100644 index 000000000000..8b3b6b68cf02 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/test/test_lldbgdbserverutils.py @@ -0,0 +1,49 @@ +from __future__ import print_function + + + +import unittest2 +import os.path +import re +import sys + +from lldbgdbserverutils import * + + +class TestLldbGdbServerUtils(unittest2.TestCase): + def test_entry_exact_payload_match(self): + entry = GdbRemoteEntry(is_send_to_remote=False, exact_payload="$OK#9a") + entry.assert_match(self, "$OK#9a") + + def test_entry_exact_payload_match_ignores_checksum(self): + entry = GdbRemoteEntry(is_send_to_remote=False, exact_payload="$OK#9a") + entry.assert_match(self, "$OK#00") + + def test_entry_creates_context(self): + entry = GdbRemoteEntry(is_send_to_remote=False, exact_payload="$OK#9a") + context = entry.assert_match(self, "$OK#9a") + self.assertIsNotNone(context) + + def test_entry_regex_matches(self): + entry = GdbRemoteEntry(is_send_to_remote=False, regex=re.compile(r"^\$QC([0-9a-fA-F]+)#"), capture={ 1:"thread_id" }) + context = entry.assert_match(self, "$QC980#00") + + def test_entry_regex_saves_match(self): + entry = GdbRemoteEntry(is_send_to_remote=False, regex=re.compile(r"^\$QC([0-9a-fA-F]+)#"), capture={ 1:"thread_id" }) + context = entry.assert_match(self, "$QC980#00") + self.assertEqual(context["thread_id"], "980") + + def test_entry_regex_expect_captures_success(self): + context = { "thread_id":"980" } + entry = GdbRemoteEntry(is_send_to_remote=False, regex=re.compile(r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+)"), expect_captures={ 2:"thread_id" }) + entry.assert_match(self, "$T11thread:980;", context=context) + + def test_entry_regex_expect_captures_raises_on_fail(self): + context = { "thread_id":"980" } + entry = GdbRemoteEntry(is_send_to_remote=False, regex=re.compile(r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+)"), expect_captures={ 2:"thread_id" }) + try: + entry.assert_match(self, "$T11thread:970;", context=context) + self.fail() + except AssertionError: + # okay + return None diff --git a/packages/Python/lldbsuite/test/types/AbstractBase.py b/packages/Python/lldbsuite/test/types/AbstractBase.py new file mode 100644 index 000000000000..dd09c91f4d02 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/AbstractBase.py @@ -0,0 +1,259 @@ +""" +Abstract base class of basic types provides a generic type tester method. +""" + +from __future__ import print_function + +import os, time +import re +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +def Msg(var, val, using_frame_variable): + return "'%s %s' matches the output (from compiled code): %s" % ( + 'frame variable --show-types' if using_frame_variable else 'expression' ,var, val) + +class GenericTester(TestBase): + + # This is the pattern by design to match the " var = 'value'" output from + # printf() stmts (see basic_type.cpp). + pattern = re.compile(" (\*?a[^=]*) = '([^=]*)'$") + + # Assert message. + DATA_TYPE_GROKKED = "Data type from expr parser output is parsed correctly" + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # We'll use the test method name as the exe_name. + # There are a bunch of test cases under test/types and we don't want the + # module cacheing subsystem to be confused with executable name "a.out" + # used for all the test cases. + self.exe_name = self.testMethodName + self.golden_filename = os.path.join(os.getcwd(), "golden-output.txt") + + def tearDown(self): + """Cleanup the test byproducts.""" + #print("Removing golden-output.txt...") + if os.path.exists(self.golden_filename): + os.remove(self.golden_filename) + TestBase.tearDown(self) + + #==========================================================================# + # Functions build_and_run() and build_and_run_expr() are generic functions # + # which are called from the Test*Types*.py test cases. The API client is # + # responsible for supplying two mandatory arguments: the source file, e.g.,# + # 'int.cpp', and the atoms, e.g., set(['unsigned', 'long long']) to the # + # functions. There are also three optional keyword arguments of interest, # + # as follows: # + # # + # bc -> blockCaptured (defaulted to False) # + # True: testing vars of various basic types from inside a block # + # False: testing vars of various basic types from a function # + # qd -> quotedDisplay (defaulted to False) # + # True: the output from 'frame var' or 'expr var' contains a pair # + # of single quotes around the value # + # False: no single quotes are to be found around the value of # + # variable # + #==========================================================================# + + def build_and_run(self, source, atoms, bc=False, qd=False): + self.build_and_run_with_source_atoms_expr(source, atoms, expr=False, bc=bc, qd=qd) + + def build_and_run_expr(self, source, atoms, bc=False, qd=False): + self.build_and_run_with_source_atoms_expr(source, atoms, expr=True, bc=bc, qd=qd) + + def build_and_run_with_source_atoms_expr(self, source, atoms, expr, bc=False, qd=False): + # See also Makefile and basic_type.cpp:177. + if bc: + d = {'CXX_SOURCES': source, 'EXE': self.exe_name, 'CFLAGS_EXTRAS': '-DTEST_BLOCK_CAPTURED_VARS'} + else: + d = {'CXX_SOURCES': source, 'EXE': self.exe_name} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + if expr: + self.generic_type_expr_tester(self.exe_name, atoms, blockCaptured=bc, quotedDisplay=qd) + else: + self.generic_type_tester(self.exe_name, atoms, blockCaptured=bc, quotedDisplay=qd) + + def process_launch_o(self, localPath): + # process launch command output redirect always goes to host the process is running on + if lldb.remote_platform: + # process launch -o requires a path that is valid on the target + self.assertIsNotNone(lldb.remote_platform.GetWorkingDirectory()) + remote_path = lldbutil.append_to_process_working_directory("lldb-stdout-redirect.txt") + self.runCmd('process launch -o {remote}'.format(remote=remote_path)) + # copy remote_path to local host + self.runCmd('platform get-file {remote} "{local}"'.format( + remote=remote_path, local=self.golden_filename)) + else: + self.runCmd('process launch -o "{local}"'.format(local=self.golden_filename)) + + def generic_type_tester(self, exe_name, atoms, quotedDisplay=False, blockCaptured=False): + """Test that variables with basic types are displayed correctly.""" + + self.runCmd("file %s" % exe_name, CURRENT_EXECUTABLE_SET) + + # First, capture the golden output emitted by the oracle, i.e., the + # series of printf statements. + + self.process_launch_o(self.golden_filename) + + with open(self.golden_filename) as f: + go = f.read() + + # This golden list contains a list of (variable, value) pairs extracted + # from the golden output. + gl = [] + + # Scan the golden output line by line, looking for the pattern: + # + # variable = 'value' + # + for line in go.split(os.linesep): + # We'll ignore variables of array types from inside a block. + if blockCaptured and '[' in line: + continue + match = self.pattern.search(line) + if match: + var, val = match.group(1), match.group(2) + gl.append((var, val)) + #print("golden list:", gl) + + # This test uses a #include of "basic_type.cpp" so we need to enable + # always setting inlined breakpoints. + self.runCmd('settings set target.inline-breakpoint-strategy always') + # And add hooks to restore the settings during tearDown(). + self.addTearDownHook( + lambda: self.runCmd("settings set target.inline-breakpoint-strategy headers")) + + # Bring the program to the point where we can issue a series of + # 'frame variable --show-types' command. + if blockCaptured: + break_line = line_number ("basic_type.cpp", "// Break here to test block captured variables.") + else: + break_line = line_number ("basic_type.cpp", "// Here is the line we will break on to check variables.") + lldbutil.run_break_set_by_file_and_line (self, "basic_type.cpp", break_line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + self.expect("process status", STOPPED_DUE_TO_BREAKPOINT, + substrs = [" at basic_type.cpp:%d" % break_line, + "stop reason = breakpoint"]) + + #self.runCmd("frame variable --show-types") + + # Now iterate through the golden list, comparing against the output from + # 'frame variable --show-types var'. + for var, val in gl: + self.runCmd("frame variable --show-types %s" % var) + output = self.res.GetOutput() + + # The input type is in a canonical form as a set of named atoms. + # The display type string must contain each and every element. + # + # Example: + # runCmd: frame variable --show-types a_array_bounded[0] + # output: (char) a_array_bounded[0] = 'a' + # + try: + dt = re.match("^\((.*)\)", output).group(1) + except: + self.fail(self.DATA_TYPE_GROKKED) + + # Expect the display type string to contain each and every atoms. + self.expect(dt, + "Display type: '%s' must contain the type atoms: '%s'" % + (dt, atoms), + exe=False, + substrs = list(atoms)) + + # The (var, val) pair must match, too. + nv = ("%s = '%s'" if quotedDisplay else "%s = %s") % (var, val) + self.expect(output, Msg(var, val, True), exe=False, + substrs = [nv]) + pass + + def generic_type_expr_tester(self, exe_name, atoms, quotedDisplay=False, blockCaptured=False): + """Test that variable expressions with basic types are evaluated correctly.""" + + self.runCmd("file %s" % exe_name, CURRENT_EXECUTABLE_SET) + + # First, capture the golden output emitted by the oracle, i.e., the + # series of printf statements. + + self.process_launch_o(self.golden_filename) + + with open(self.golden_filename) as f: + go = f.read() + + # This golden list contains a list of (variable, value) pairs extracted + # from the golden output. + gl = [] + + # Scan the golden output line by line, looking for the pattern: + # + # variable = 'value' + # + for line in go.split(os.linesep): + # We'll ignore variables of array types from inside a block. + if blockCaptured and '[' in line: + continue + match = self.pattern.search(line) + if match: + var, val = match.group(1), match.group(2) + gl.append((var, val)) + #print("golden list:", gl) + + # This test uses a #include of "basic_type.cpp" so we need to enable + # always setting inlined breakpoints. + self.runCmd('settings set target.inline-breakpoint-strategy always') + # And add hooks to restore the settings during tearDown(). + self.addTearDownHook( + lambda: self.runCmd("settings set target.inline-breakpoint-strategy headers")) + + # Bring the program to the point where we can issue a series of + # 'expr' command. + if blockCaptured: + break_line = line_number ("basic_type.cpp", "// Break here to test block captured variables.") + else: + break_line = line_number ("basic_type.cpp", "// Here is the line we will break on to check variables.") + lldbutil.run_break_set_by_file_and_line (self, "basic_type.cpp", break_line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + self.expect("process status", STOPPED_DUE_TO_BREAKPOINT, + substrs = [" at basic_type.cpp:%d" % break_line, + "stop reason = breakpoint"]) + + #self.runCmd("frame variable --show-types") + + # Now iterate through the golden list, comparing against the output from + # 'expr var'. + for var, val in gl: + self.runCmd("expression %s" % var) + output = self.res.GetOutput() + + # The input type is in a canonical form as a set of named atoms. + # The display type string must contain each and every element. + # + # Example: + # runCmd: expr a + # output: (double) $0 = 1100.12 + # + try: + dt = re.match("^\((.*)\) \$[0-9]+ = ", output).group(1) + except: + self.fail(self.DATA_TYPE_GROKKED) + + # Expect the display type string to contain each and every atoms. + self.expect(dt, + "Display type: '%s' must contain the type atoms: '%s'" % + (dt, atoms), + exe=False, + substrs = list(atoms)) + + # The val part must match, too. + valPart = ("'%s'" if quotedDisplay else "%s") % val + self.expect(output, Msg(var, val, False), exe=False, + substrs = [valPart]) + pass diff --git a/packages/Python/lldbsuite/test/types/HideTestFailures.py b/packages/Python/lldbsuite/test/types/HideTestFailures.py new file mode 100644 index 000000000000..5818166ef5ee --- /dev/null +++ b/packages/Python/lldbsuite/test/types/HideTestFailures.py @@ -0,0 +1,78 @@ +""" +Test that variables of integer basic types are displayed correctly. +""" + +from __future__ import print_function + + + +import AbstractBase +import sys +import lldb +from lldbsuite.test.lldbtest import * + +# rdar://problem/9649573 +# Capture the lldb and gdb-remote log files for test failures when run with no "-w" option +class DebugIntegerTypesFailures(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # If we're lucky, test_long_type_with_dsym fails. + # Let's turn on logging just for that. + try: + if "test_long_type_with_dsym" in self.id(): + self.runCmd( + "log enable -n -f %s lldb commands event process state" % + os.environ["DEBUG_LLDB_LOG"]) + self.runCmd( + "log enable -n -f %s gdb-remote packets process" % + os.environ["DEBUG_GDB_REMOTE_LOG"]) + except: + pass + + def tearDown(self): + # If we're lucky, test_long_type_with_dsym fails. + # Let's turn off logging just for that. + if "test_long_type_with_dsym" in self.id(): + self.runCmd("log disable lldb") + self.runCmd("log disable gdb-remote") + # Call super's tearDown(). + TestBase.tearDown(self) + + def test_char_type(self): + """Test that char-type variables are displayed correctly.""" + d = {'CXX_SOURCES': 'char.cpp'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.generic_type_tester(set(['char']), quotedDisplay=True) + + def test_short_type(self): + """Test that short-type variables are displayed correctly.""" + d = {'CXX_SOURCES': 'short.cpp'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.generic_type_tester(set(['short'])) + + def test_int_type(self): + """Test that int-type variables are displayed correctly.""" + d = {'CXX_SOURCES': 'int.cpp'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.generic_type_tester(set(['int'])) + + def test_long_type(self): + """Test that long-type variables are displayed correctly.""" + d = {'CXX_SOURCES': 'long.cpp'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.generic_type_tester(set(['long'])) + + def test_long_long_type(self): + """Test that 'long long'-type variables are displayed correctly.""" + d = {'CXX_SOURCES': 'long_long.cpp'} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + self.generic_type_tester(set(['long long'])) diff --git a/packages/Python/lldbsuite/test/types/Makefile b/packages/Python/lldbsuite/test/types/Makefile new file mode 100644 index 000000000000..c1b4cb32561d --- /dev/null +++ b/packages/Python/lldbsuite/test/types/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../make + +# Example: +# +# CXX_SOURCES := int.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/types/TestFloatTypes.py b/packages/Python/lldbsuite/test/types/TestFloatTypes.py new file mode 100644 index 000000000000..01fd4a267804 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/TestFloatTypes.py @@ -0,0 +1,41 @@ +""" +Test that variables of floating point types are displayed correctly. +""" + +from __future__ import print_function + + + +import AbstractBase +import lldb +import sys +from lldbsuite.test.lldbtest import * + +class FloatTypesTestCase(AbstractBase.GenericTester): + + mydir = AbstractBase.GenericTester.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + AbstractBase.GenericTester.setUp(self) + # disable "There is a running process, kill it and restart?" prompt + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + def test_float_type(self): + """Test that float-type variables are displayed correctly.""" + self.build_and_run('float.cpp', set(['float'])) + + @skipUnlessDarwin + def test_float_type_from_block(self): + """Test that float-type variables are displayed correctly from a block.""" + self.build_and_run('float.cpp', set(['float']), bc=True) + + def test_double_type(self): + """Test that double-type variables are displayed correctly.""" + self.build_and_run('double.cpp', set(['double'])) + + @skipUnlessDarwin + def test_double_type_from_block(self): + """Test that double-type variables are displayed correctly from a block.""" + self.build_and_run('double.cpp', set(['double']), bc=True) diff --git a/packages/Python/lldbsuite/test/types/TestFloatTypesExpr.py b/packages/Python/lldbsuite/test/types/TestFloatTypesExpr.py new file mode 100644 index 000000000000..a825a92c66cc --- /dev/null +++ b/packages/Python/lldbsuite/test/types/TestFloatTypesExpr.py @@ -0,0 +1,44 @@ +""" +Test that variable expressions of floating point types are evaluated correctly. +""" + +from __future__ import print_function + + + +import AbstractBase +import lldb +import sys +from lldbsuite.test.lldbtest import * + +class FloatTypesExprTestCase(AbstractBase.GenericTester): + + mydir = AbstractBase.GenericTester.compute_mydir(__file__) + + # rdar://problem/8493023 + # test/types failures for Test*TypesExpr.py: element offset computed wrong and sign error? + + def setUp(self): + # Call super's setUp(). + AbstractBase.GenericTester.setUp(self) + # disable "There is a running process, kill it and restart?" prompt + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + def test_float_type(self): + """Test that float-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('float.cpp', set(['float'])) + + @skipUnlessDarwin + def test_float_type_from_block(self): + """Test that float-type variables are displayed correctly from a block.""" + self.build_and_run_expr('float.cpp', set(['float']), bc=True) + + def test_double_type(self): + """Test that double-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('double.cpp', set(['double'])) + + @skipUnlessDarwin + def test_double_type_from_block(self): + """Test that double-type variables are displayed correctly from a block.""" + self.build_and_run_expr('double.cpp', set(['double']), bc=True) diff --git a/packages/Python/lldbsuite/test/types/TestIntegerTypes.py b/packages/Python/lldbsuite/test/types/TestIntegerTypes.py new file mode 100644 index 000000000000..4b979df51db3 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/TestIntegerTypes.py @@ -0,0 +1,113 @@ +""" +Test that variables of integer basic types are displayed correctly. +""" + +from __future__ import print_function + + + +import AbstractBase +import lldb +import sys +from lldbsuite.test.lldbtest import * + +class IntegerTypesTestCase(AbstractBase.GenericTester): + + mydir = AbstractBase.GenericTester.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + AbstractBase.GenericTester.setUp(self) + # disable "There is a running process, kill it and restart?" prompt + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + def test_char_type(self): + """Test that char-type variables are displayed correctly.""" + self.build_and_run('char.cpp', set(['char']), qd=True) + + @skipUnlessDarwin + def test_char_type_from_block(self): + """Test that char-type variables are displayed correctly from a block.""" + self.build_and_run('char.cpp', set(['char']), bc=True, qd=True) + + def test_unsigned_char_type(self): + """Test that 'unsigned_char'-type variables are displayed correctly.""" + self.build_and_run('unsigned_char.cpp', set(['unsigned', 'char']), qd=True) + + @skipUnlessDarwin + def test_unsigned_char_type_from_block(self): + """Test that 'unsigned char'-type variables are displayed correctly from a block.""" + self.build_and_run('unsigned_char.cpp', set(['unsigned', 'char']), bc=True, qd=True) + + def test_short_type(self): + """Test that short-type variables are displayed correctly.""" + self.build_and_run('short.cpp', set(['short'])) + + @skipUnlessDarwin + def test_short_type_from_block(self): + """Test that short-type variables are displayed correctly from a block.""" + self.build_and_run('short.cpp', set(['short']), bc=True) + + def test_unsigned_short_type(self): + """Test that 'unsigned_short'-type variables are displayed correctly.""" + self.build_and_run('unsigned_short.cpp', set(['unsigned', 'short'])) + + @skipUnlessDarwin + def test_unsigned_short_type_from_block(self): + """Test that 'unsigned short'-type variables are displayed correctly from a block.""" + self.build_and_run('unsigned_short.cpp', set(['unsigned', 'short']), bc=True) + + def test_int_type(self): + """Test that int-type variables are displayed correctly.""" + self.build_and_run('int.cpp', set(['int'])) + + @skipUnlessDarwin + def test_int_type_from_block(self): + """Test that int-type variables are displayed correctly from a block.""" + self.build_and_run('int.cpp', set(['int'])) + + def test_unsigned_int_type(self): + """Test that 'unsigned_int'-type variables are displayed correctly.""" + self.build_and_run('unsigned_int.cpp', set(['unsigned', 'int'])) + + @skipUnlessDarwin + def test_unsigned_int_type_from_block(self): + """Test that 'unsigned int'-type variables are displayed correctly from a block.""" + self.build_and_run('unsigned_int.cpp', set(['unsigned', 'int']), bc=True) + + def test_long_type(self): + """Test that long-type variables are displayed correctly.""" + self.build_and_run('long.cpp', set(['long'])) + + @skipUnlessDarwin + def test_long_type_from_block(self): + """Test that long-type variables are displayed correctly from a block.""" + self.build_and_run('long.cpp', set(['long']), bc=True) + + def test_unsigned_long_type(self): + """Test that 'unsigned long'-type variables are displayed correctly.""" + self.build_and_run('unsigned_long.cpp', set(['unsigned', 'long'])) + + @skipUnlessDarwin + def test_unsigned_long_type_from_block(self): + """Test that 'unsigned_long'-type variables are displayed correctly from a block.""" + self.build_and_run('unsigned_long.cpp', set(['unsigned', 'long']), bc=True) + + def test_long_long_type(self): + """Test that 'long long'-type variables are displayed correctly.""" + self.build_and_run('long_long.cpp', set(['long long'])) + + @skipUnlessDarwin + def test_long_long_type_from_block(self): + """Test that 'long_long'-type variables are displayed correctly from a block.""" + self.build_and_run('long_long.cpp', set(['long long']), bc=True) + + def test_unsigned_long_long_type(self): + """Test that 'unsigned long long'-type variables are displayed correctly.""" + self.build_and_run('unsigned_long_long.cpp', set(['unsigned', 'long long'])) + + @skipUnlessDarwin + def test_unsigned_long_long_type_from_block(self): + """Test that 'unsigned_long_long'-type variables are displayed correctly from a block.""" + self.build_and_run('unsigned_long_long.cpp', set(['unsigned', 'long long']), bc=True) diff --git a/packages/Python/lldbsuite/test/types/TestIntegerTypesExpr.py b/packages/Python/lldbsuite/test/types/TestIntegerTypesExpr.py new file mode 100644 index 000000000000..c6dda91ad13e --- /dev/null +++ b/packages/Python/lldbsuite/test/types/TestIntegerTypesExpr.py @@ -0,0 +1,113 @@ +""" +Test that variable expressions of integer basic types are evaluated correctly. +""" + +from __future__ import print_function + + + +import AbstractBase +import lldb +import sys +from lldbsuite.test.lldbtest import * + +class IntegerTypesExprTestCase(AbstractBase.GenericTester): + + mydir = AbstractBase.GenericTester.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + AbstractBase.GenericTester.setUp(self) + # disable "There is a running process, kill it and restart?" prompt + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + + def test_char_type(self): + """Test that char-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('char.cpp', set(['char']), qd=True) + + @skipUnlessDarwin + def test_char_type_from_block(self): + """Test that char-type variables are displayed correctly from a block.""" + self.build_and_run_expr('char.cpp', set(['char']), bc=True, qd=True) + + def test_unsigned_char_type(self): + """Test that 'unsigned_char'-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('unsigned_char.cpp', set(['unsigned', 'char']), qd=True) + + @skipUnlessDarwin + def test_unsigned_char_type_from_block(self): + """Test that 'unsigned char'-type variables are displayed correctly from a block.""" + self.build_and_run_expr('unsigned_char.cpp', set(['unsigned', 'char']), bc=True, qd=True) + + def test_short_type(self): + """Test that short-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('short.cpp', set(['short'])) + + @skipUnlessDarwin + def test_short_type_from_block(self): + """Test that short-type variables are displayed correctly from a block.""" + self.build_and_run_expr('short.cpp', set(['short']), bc=True) + + def test_unsigned_short_type(self): + """Test that 'unsigned_short'-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('unsigned_short.cpp', set(['unsigned', 'short'])) + + @skipUnlessDarwin + def test_unsigned_short_type_from_block(self): + """Test that 'unsigned short'-type variables are displayed correctly from a block.""" + self.build_and_run_expr('unsigned_short.cpp', set(['unsigned', 'short']), bc=True) + + def test_int_type(self): + """Test that int-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('int.cpp', set(['int'])) + + @skipUnlessDarwin + def test_int_type_from_block(self): + """Test that int-type variables are displayed correctly from a block.""" + self.build_and_run_expr('int.cpp', set(['int'])) + + def test_unsigned_int_type(self): + """Test that 'unsigned_int'-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('unsigned_int.cpp', set(['unsigned', 'int'])) + + @skipUnlessDarwin + def test_unsigned_int_type_from_block(self): + """Test that 'unsigned int'-type variables are displayed correctly from a block.""" + self.build_and_run_expr('unsigned_int.cpp', set(['unsigned', 'int']), bc=True) + + def test_long_type(self): + """Test that long-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('long.cpp', set(['long'])) + + @skipUnlessDarwin + def test_long_type_from_block(self): + """Test that long-type variables are displayed correctly from a block.""" + self.build_and_run_expr('long.cpp', set(['long']), bc=True) + + def test_unsigned_long_type(self): + """Test that 'unsigned long'-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('unsigned_long.cpp', set(['unsigned', 'long'])) + + @skipUnlessDarwin + def test_unsigned_long_type_from_block(self): + """Test that 'unsigned_long'-type variables are displayed correctly from a block.""" + self.build_and_run_expr('unsigned_long.cpp', set(['unsigned', 'long']), bc=True) + + def test_long_long_type(self): + """Test that 'long long'-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('long_long.cpp', set(['long long'])) + + @skipUnlessDarwin + def test_long_long_type_from_block(self): + """Test that 'long_long'-type variables are displayed correctly from a block.""" + self.build_and_run_expr('long_long.cpp', set(['long long']), bc=True) + + def test_unsigned_long_long_type(self): + """Test that 'unsigned long long'-type variable expressions are evaluated correctly.""" + self.build_and_run_expr('unsigned_long_long.cpp', set(['unsigned', 'long long'])) + + @skipUnlessDarwin + def test_unsigned_long_long_type_from_block(self): + """Test that 'unsigned_long_long'-type variables are displayed correctly from a block.""" + self.build_and_run_expr('unsigned_long_long.cpp', set(['unsigned', 'long long']), bc=True) diff --git a/packages/Python/lldbsuite/test/types/TestRecursiveTypes.py b/packages/Python/lldbsuite/test/types/TestRecursiveTypes.py new file mode 100644 index 000000000000..0b60b5a4d63a --- /dev/null +++ b/packages/Python/lldbsuite/test/types/TestRecursiveTypes.py @@ -0,0 +1,51 @@ +""" +Test that recursive types are handled correctly. +""" + +from __future__ import print_function + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +import sys +from lldbsuite.test.lldbtest import * + +class RecursiveTypesTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # disable "There is a running process, kill it and restart?" prompt + self.runCmd("settings set auto-confirm true") + self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm")) + # Find the line number to break for main.c. + self.line = line_number('recursive_type_main.cpp', + '// Test at this line.') + + self.d1 = {'CXX_SOURCES': 'recursive_type_main.cpp recursive_type_1.cpp'} + self.d2 = {'CXX_SOURCES': 'recursive_type_main.cpp recursive_type_2.cpp'} + + def test_recursive_type_1(self): + """Test that recursive structs are displayed correctly.""" + self.build(dictionary=self.d1) + self.setTearDownCleanup(dictionary=self.d1) + self.print_struct() + + def test_recursive_type_2(self): + """Test that recursive structs are displayed correctly.""" + self.build(dictionary=self.d2) + self.setTearDownCleanup(dictionary=self.d2) + self.print_struct() + + def print_struct(self): + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "recursive_type_main.cpp", self.line, num_expected_locations=-1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("print tpi", RUN_SUCCEEDED) + self.expect("print *tpi", RUN_SUCCEEDED) diff --git a/packages/Python/lldbsuite/test/types/basic_type.cpp b/packages/Python/lldbsuite/test/types/basic_type.cpp new file mode 100644 index 000000000000..1651815fa0ca --- /dev/null +++ b/packages/Python/lldbsuite/test/types/basic_type.cpp @@ -0,0 +1,211 @@ +// This file must have the following defined before it is included: +// T defined to the type to test (int, float, etc) +// T_CSTR a C string representation of the type T ("int", "float") +// T_VALUE_1 defined to a valid initializer value for TEST_TYPE (7 for int, 2.0 for float) +// T_VALUE_2, T_VALUE_3, T_VALUE_4 defined to a valid initializer value for TEST_TYPE that is different from TEST_VALUE_1 +// T_PRINTF_FORMAT defined if T can be printed with printf +// +// An example for integers is below +#if 0 + +#define T int +#define T_CSTR "int" +#define T_VALUE_1 11001110 +#define T_VALUE_2 22002220 +#define T_VALUE_3 33003330 +#define T_VALUE_4 44044440 +#define T_PRINTF_FORMAT "%i" + +#include "basic_type.cpp" + +#endif + +#ifdef TEST_BLOCK_CAPTURED_VARS +#include +#endif + +class a_class +{ +public: + a_class (const T& a, const T& b) : + m_a (a), + m_b (b) + { + } + + ~a_class () + { + } + + const T& + get_a() const + { + return m_a; + } + + void + set_a (const T& a) + { + m_a = a; + } + + const T& + get_b() const + { + return m_b; + } + + void + set_b (const T& b) + { + m_b = b; + } + +protected: + T m_a; + T m_b; +}; + +typedef struct a_struct_tag { + T a; + T b; +} a_struct_t; + + +typedef union a_union_zero_tag { + T a; + double a_double; +} a_union_zero_t; + +typedef struct a_union_nonzero_tag { + double a_double; + a_union_zero_t u; +} a_union_nonzero_t; + + +#include +#include + +int +main (int argc, char const *argv[]) +{ + T a = T_VALUE_1; + T* a_ptr = &a; + T& a_ref = a; + T a_array_bounded[2] = { T_VALUE_1, T_VALUE_2 }; + T a_array_unbounded[] = { T_VALUE_1, T_VALUE_2 }; + + a_class a_class_instance (T_VALUE_1, T_VALUE_2); + a_class *a_class_ptr = &a_class_instance; + a_class &a_class_ref = a_class_instance; + + a_struct_t a_struct = { T_VALUE_1, T_VALUE_2 }; + a_struct_t *a_struct_ptr = &a_struct; + a_struct_t &a_struct_ref = a_struct; + + // Create a union with type T at offset zero + a_union_zero_t a_union_zero; + a_union_zero.a = T_VALUE_1; + a_union_zero_t *a_union_zero_ptr = &a_union_zero; + a_union_zero_t &a_union_zero_ref = a_union_zero; + + // Create a union with type T at a non-zero offset + a_union_nonzero_t a_union_nonzero; + a_union_nonzero.u.a = T_VALUE_1; + a_union_nonzero_t *a_union_nonzero_ptr = &a_union_nonzero; + a_union_nonzero_t &a_union_nonzero_ref = a_union_nonzero; + + a_struct_t a_struct_array_bounded[2] = {{ T_VALUE_1, T_VALUE_2 }, { T_VALUE_3, T_VALUE_4 }}; + a_struct_t a_struct_array_unbounded[] = {{ T_VALUE_1, T_VALUE_2 }, { T_VALUE_3, T_VALUE_4 }}; + a_union_zero_t a_union_zero_array_bounded[2]; + a_union_zero_array_bounded[0].a = T_VALUE_1; + a_union_zero_array_bounded[1].a = T_VALUE_2; + a_union_zero_t a_union_zero_array_unbounded[] = {{ T_VALUE_1 }, { T_VALUE_2 }}; + +#ifdef T_PRINTF_FORMAT + printf ("%s: a = '" T_PRINTF_FORMAT "'\n", T_CSTR, a); + printf ("%s*: %p => *a_ptr = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_ptr, *a_ptr); + printf ("%s&: @%p => a_ref = '" T_PRINTF_FORMAT "'\n", T_CSTR, &a_ref, a_ref); + + printf ("%s[2]: a_array_bounded[0] = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_array_bounded[0]); + printf ("%s[2]: a_array_bounded[1] = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_array_bounded[1]); + + printf ("%s[]: a_array_unbounded[0] = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_array_unbounded[0]); + printf ("%s[]: a_array_unbounded[1] = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_array_unbounded[1]); + + printf ("(a_class) a_class_instance.m_a = '" T_PRINTF_FORMAT "'\n", a_class_instance.get_a()); + printf ("(a_class) a_class_instance.m_b = '" T_PRINTF_FORMAT "'\n", a_class_instance.get_b()); + printf ("(a_class*) a_class_ptr = %p, a_class_ptr->m_a = '" T_PRINTF_FORMAT "'\n", a_class_ptr, a_class_ptr->get_a()); + printf ("(a_class*) a_class_ptr = %p, a_class_ptr->m_b = '" T_PRINTF_FORMAT "'\n", a_class_ptr, a_class_ptr->get_b()); + printf ("(a_class&) a_class_ref = %p, a_class_ref.m_a = '" T_PRINTF_FORMAT "'\n", &a_class_ref, a_class_ref.get_a()); + printf ("(a_class&) a_class_ref = %p, a_class_ref.m_b = '" T_PRINTF_FORMAT "'\n", &a_class_ref, a_class_ref.get_b()); + + printf ("(a_struct_t) a_struct.a = '" T_PRINTF_FORMAT "'\n", a_struct.a); + printf ("(a_struct_t) a_struct.b = '" T_PRINTF_FORMAT "'\n", a_struct.b); + printf ("(a_struct_t*) a_struct_ptr = %p, a_struct_ptr->a = '" T_PRINTF_FORMAT "'\n", a_struct_ptr, a_struct_ptr->a); + printf ("(a_struct_t*) a_struct_ptr = %p, a_struct_ptr->b = '" T_PRINTF_FORMAT "'\n", a_struct_ptr, a_struct_ptr->b); + printf ("(a_struct_t&) a_struct_ref = %p, a_struct_ref.a = '" T_PRINTF_FORMAT "'\n", &a_struct_ref, a_struct_ref.a); + printf ("(a_struct_t&) a_struct_ref = %p, a_struct_ref.b = '" T_PRINTF_FORMAT "'\n", &a_struct_ref, a_struct_ref.b); + + printf ("(a_union_zero_t) a_union_zero.a = '" T_PRINTF_FORMAT "'\n", a_union_zero.a); + printf ("(a_union_zero_t*) a_union_zero_ptr = %p, a_union_zero_ptr->a = '" T_PRINTF_FORMAT "'\n", a_union_zero_ptr, a_union_zero_ptr->a); + printf ("(a_union_zero_t&) a_union_zero_ref = %p, a_union_zero_ref.a = '" T_PRINTF_FORMAT "'\n", &a_union_zero_ref, a_union_zero_ref.a); + + printf ("(a_union_nonzero_t) a_union_nonzero.u.a = '" T_PRINTF_FORMAT "'\n", a_union_nonzero.u.a); + printf ("(a_union_nonzero_t*) a_union_nonzero_ptr = %p, a_union_nonzero_ptr->u.a = '" T_PRINTF_FORMAT "'\n", a_union_nonzero_ptr, a_union_nonzero_ptr->u.a); + printf ("(a_union_nonzero_t&) a_union_nonzero_ref = %p, a_union_nonzero_ref.u.a = '" T_PRINTF_FORMAT "'\n", &a_union_nonzero_ref, a_union_nonzero_ref.u.a); + + printf ("(a_struct_t[2]) a_struct_array_bounded[0].a = '" T_PRINTF_FORMAT "'\n", a_struct_array_bounded[0].a); + printf ("(a_struct_t[2]) a_struct_array_bounded[0].b = '" T_PRINTF_FORMAT "'\n", a_struct_array_bounded[0].b); + printf ("(a_struct_t[2]) a_struct_array_bounded[1].a = '" T_PRINTF_FORMAT "'\n", a_struct_array_bounded[1].a); + printf ("(a_struct_t[2]) a_struct_array_bounded[1].b = '" T_PRINTF_FORMAT "'\n", a_struct_array_bounded[1].b); + + printf ("(a_struct_t[]) a_struct_array_unbounded[0].a = '" T_PRINTF_FORMAT "'\n", a_struct_array_unbounded[0].a); + printf ("(a_struct_t[]) a_struct_array_unbounded[0].b = '" T_PRINTF_FORMAT "'\n", a_struct_array_unbounded[0].b); + printf ("(a_struct_t[]) a_struct_array_unbounded[1].a = '" T_PRINTF_FORMAT "'\n", a_struct_array_unbounded[1].a); + printf ("(a_struct_t[]) a_struct_array_unbounded[1].b = '" T_PRINTF_FORMAT "'\n", a_struct_array_unbounded[1].b); + + printf ("(a_union_zero_t[2]) a_union_zero_array_bounded[0].a = '" T_PRINTF_FORMAT "'\n", a_union_zero_array_bounded[0].a); + printf ("(a_union_zero_t[2]) a_union_zero_array_bounded[1].a = '" T_PRINTF_FORMAT "'\n", a_union_zero_array_bounded[1].a); + + printf ("(a_union_zero_t[]) a_union_zero_array_unbounded[0].a = '" T_PRINTF_FORMAT "'\n", a_union_zero_array_unbounded[0].a); + printf ("(a_union_zero_t[]) a_union_zero_array_unbounded[1].a = '" T_PRINTF_FORMAT "'\n", a_union_zero_array_unbounded[1].a); + +#endif + puts("About to exit, break here to check values..."); // Here is the line we will break on to check variables. + +#ifdef TEST_BLOCK_CAPTURED_VARS + void (^myBlock)() = ^() { + printf ("%s: a = '" T_PRINTF_FORMAT "'\n", T_CSTR, a); + printf ("%s*: %p => *a_ptr = '" T_PRINTF_FORMAT "'\n", T_CSTR, a_ptr, *a_ptr); + printf ("%s&: @%p => a_ref = '" T_PRINTF_FORMAT "'\n", T_CSTR, &a_ref, a_ref); + + printf ("(a_class) a_class_instance.m_a = '" T_PRINTF_FORMAT "'\n", a_class_instance.get_a()); + printf ("(a_class) a_class_instance.m_b = '" T_PRINTF_FORMAT "'\n", a_class_instance.get_b()); + printf ("(a_class*) a_class_ptr = %p, a_class_ptr->m_a = '" T_PRINTF_FORMAT "'\n", a_class_ptr, a_class_ptr->get_a()); + printf ("(a_class*) a_class_ptr = %p, a_class_ptr->m_b = '" T_PRINTF_FORMAT "'\n", a_class_ptr, a_class_ptr->get_b()); + printf ("(a_class&) a_class_ref = %p, a_class_ref.m_a = '" T_PRINTF_FORMAT "'\n", &a_class_ref, a_class_ref.get_a()); + printf ("(a_class&) a_class_ref = %p, a_class_ref.m_b = '" T_PRINTF_FORMAT "'\n", &a_class_ref, a_class_ref.get_b()); + + printf ("(a_struct_t) a_struct.a = '" T_PRINTF_FORMAT "'\n", a_struct.a); + printf ("(a_struct_t) a_struct.b = '" T_PRINTF_FORMAT "'\n", a_struct.b); + printf ("(a_struct_t*) a_struct_ptr = %p, a_struct_ptr->a = '" T_PRINTF_FORMAT "'\n", a_struct_ptr, a_struct_ptr->a); + printf ("(a_struct_t*) a_struct_ptr = %p, a_struct_ptr->b = '" T_PRINTF_FORMAT "'\n", a_struct_ptr, a_struct_ptr->b); + printf ("(a_struct_t&) a_struct_ref = %p, a_struct_ref.a = '" T_PRINTF_FORMAT "'\n", &a_struct_ref, a_struct_ref.a); + printf ("(a_struct_t&) a_struct_ref = %p, a_struct_ref.b = '" T_PRINTF_FORMAT "'\n", &a_struct_ref, a_struct_ref.b); + + printf ("(a_union_zero_t) a_union_zero.a = '" T_PRINTF_FORMAT "'\n", a_union_zero.a); + printf ("(a_union_zero_t*) a_union_zero_ptr = %p, a_union_zero_ptr->a = '" T_PRINTF_FORMAT "'\n", a_union_zero_ptr, a_union_zero_ptr->a); + printf ("(a_union_zero_t&) a_union_zero_ref = %p, a_union_zero_ref.a = '" T_PRINTF_FORMAT "'\n", &a_union_zero_ref, a_union_zero_ref.a); + + printf ("(a_union_nonzero_t) a_union_nonzero.u.a = '" T_PRINTF_FORMAT "'\n", a_union_nonzero.u.a); + printf ("(a_union_nonzero_t*) a_union_nonzero_ptr = %p, a_union_nonzero_ptr->u.a = '" T_PRINTF_FORMAT "'\n", a_union_nonzero_ptr, a_union_nonzero_ptr->u.a); + printf ("(a_union_nonzero_t&) a_union_nonzero_ref = %p, a_union_nonzero_ref.u.a = '" T_PRINTF_FORMAT "'\n", &a_union_nonzero_ref, a_union_nonzero_ref.u.a); + + printf ("That's All Folks!\n"); // Break here to test block captured variables. + }; + + myBlock(); +#endif + return 0; +} diff --git a/packages/Python/lldbsuite/test/types/char.cpp b/packages/Python/lldbsuite/test/types/char.cpp new file mode 100644 index 000000000000..584582ed9b9c --- /dev/null +++ b/packages/Python/lldbsuite/test/types/char.cpp @@ -0,0 +1,9 @@ +#define T char +#define T_CSTR "char" +#define T_VALUE_1 'a' +#define T_VALUE_2 'b' +#define T_VALUE_3 '!' +#define T_VALUE_4 '~' +#define T_PRINTF_FORMAT "%c" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/double.cpp b/packages/Python/lldbsuite/test/types/double.cpp new file mode 100644 index 000000000000..6788dadfe02f --- /dev/null +++ b/packages/Python/lldbsuite/test/types/double.cpp @@ -0,0 +1,9 @@ +#define T double +#define T_CSTR "double" +#define T_VALUE_1 1100.125 +#define T_VALUE_2 2200.250 +#define T_VALUE_3 33.00 +#define T_VALUE_4 44.00 +#define T_PRINTF_FORMAT "%lg" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/float.cpp b/packages/Python/lldbsuite/test/types/float.cpp new file mode 100644 index 000000000000..4bc124661aad --- /dev/null +++ b/packages/Python/lldbsuite/test/types/float.cpp @@ -0,0 +1,9 @@ +#define T float +#define T_CSTR "float" +#define T_VALUE_1 1100.125 +#define T_VALUE_2 2200.250 +#define T_VALUE_3 33.00 +#define T_VALUE_4 44.00 +#define T_PRINTF_FORMAT "%g" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/int.cpp b/packages/Python/lldbsuite/test/types/int.cpp new file mode 100644 index 000000000000..922398b1c6e3 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/int.cpp @@ -0,0 +1,9 @@ +#define T int +#define T_CSTR "int" +#define T_VALUE_1 11001110 +#define T_VALUE_2 22002220 +#define T_VALUE_3 33003330 +#define T_VALUE_4 44004440 +#define T_PRINTF_FORMAT "%i" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/long.cpp b/packages/Python/lldbsuite/test/types/long.cpp new file mode 100644 index 000000000000..9056b42ee77d --- /dev/null +++ b/packages/Python/lldbsuite/test/types/long.cpp @@ -0,0 +1,18 @@ +#define T long +#define T_CSTR "long" + +#ifdef __LP64__ +#define T_VALUE_1 110011101111 +#define T_VALUE_2 220022202222 +#define T_VALUE_3 330033303333 +#define T_VALUE_4 440044404444 +#else +#define T_VALUE_1 110011101 +#define T_VALUE_2 220022202 +#define T_VALUE_3 330033303 +#define T_VALUE_4 440044404 +#endif + +#define T_PRINTF_FORMAT "%ld" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/long_long.cpp b/packages/Python/lldbsuite/test/types/long_long.cpp new file mode 100644 index 000000000000..5b442e7257e9 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/long_long.cpp @@ -0,0 +1,18 @@ +#define T long long +#define T_CSTR "long long" + +#ifdef __LP64__ +#define T_VALUE_1 110011101111 +#define T_VALUE_2 220022202222 +#define T_VALUE_3 330033303333 +#define T_VALUE_4 440044404444 +#else +#define T_VALUE_1 110011101 +#define T_VALUE_2 220022202 +#define T_VALUE_3 330033303 +#define T_VALUE_4 440044404 +#endif + +#define T_PRINTF_FORMAT "%lld" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/recursive_type_1.cpp b/packages/Python/lldbsuite/test/types/recursive_type_1.cpp new file mode 100644 index 000000000000..81d923ffd945 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/recursive_type_1.cpp @@ -0,0 +1,12 @@ +typedef struct t *tp; +typedef tp (*get_tp)(); + +struct s { + get_tp get_tp_p; +}; + +struct t { + struct s *s; +}; + +struct t t; diff --git a/packages/Python/lldbsuite/test/types/recursive_type_2.cpp b/packages/Python/lldbsuite/test/types/recursive_type_2.cpp new file mode 100644 index 000000000000..304739049a06 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/recursive_type_2.cpp @@ -0,0 +1,10 @@ +typedef struct t *tp; +typedef tp (*get_tp)(); + +struct t { + struct { + get_tp get_tp_p; + }; +}; + +struct t t; diff --git a/packages/Python/lldbsuite/test/types/recursive_type_main.cpp b/packages/Python/lldbsuite/test/types/recursive_type_main.cpp new file mode 100644 index 000000000000..e06cd309be30 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/recursive_type_main.cpp @@ -0,0 +1,8 @@ +typedef struct t *tp; +extern struct t t; + +int main() { + tp tpi = &t; + // Test at this line. + return 0; +} diff --git a/packages/Python/lldbsuite/test/types/short.cpp b/packages/Python/lldbsuite/test/types/short.cpp new file mode 100644 index 000000000000..470c68aa8ba7 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/short.cpp @@ -0,0 +1,9 @@ +#define T short +#define T_CSTR "short" +#define T_VALUE_1 11001 +#define T_VALUE_2 22002 +#define T_VALUE_3 -32768 +#define T_VALUE_4 32767 +#define T_PRINTF_FORMAT "%hd" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/unsigned_char.cpp b/packages/Python/lldbsuite/test/types/unsigned_char.cpp new file mode 100644 index 000000000000..0ac555a5a183 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/unsigned_char.cpp @@ -0,0 +1,9 @@ +#define T unsigned char +#define T_CSTR "unsigned char" +#define T_VALUE_1 '0' +#define T_VALUE_2 '9' +#define T_VALUE_3 '@' +#define T_VALUE_4 '$' +#define T_PRINTF_FORMAT "%c" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/unsigned_int.cpp b/packages/Python/lldbsuite/test/types/unsigned_int.cpp new file mode 100644 index 000000000000..1b307b04afc1 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/unsigned_int.cpp @@ -0,0 +1,9 @@ +#define T unsigned int +#define T_CSTR "unsigned int" +#define T_VALUE_1 11001110 +#define T_VALUE_2 22002220 +#define T_VALUE_3 33003330 +#define T_VALUE_4 44004440 +#define T_PRINTF_FORMAT "%u" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/unsigned_long.cpp b/packages/Python/lldbsuite/test/types/unsigned_long.cpp new file mode 100644 index 000000000000..0c442e399d53 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/unsigned_long.cpp @@ -0,0 +1,18 @@ +#define T unsigned long +#define T_CSTR "unsigned long" + +#ifdef __LP64__ +#define T_VALUE_1 110011101111 +#define T_VALUE_2 220022202222 +#define T_VALUE_3 330033303333 +#define T_VALUE_4 440044404444 +#else +#define T_VALUE_1 110011101 +#define T_VALUE_2 220022202 +#define T_VALUE_3 330033303 +#define T_VALUE_4 440044404 +#endif + +#define T_PRINTF_FORMAT "%lu" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/unsigned_long_long.cpp b/packages/Python/lldbsuite/test/types/unsigned_long_long.cpp new file mode 100644 index 000000000000..648aad01b3a3 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/unsigned_long_long.cpp @@ -0,0 +1,18 @@ +#define T unsigned long long +#define T_CSTR "unsigned long long" + +#ifdef __LP64__ +#define T_VALUE_1 110011101111 +#define T_VALUE_2 220022202222 +#define T_VALUE_3 330033303333 +#define T_VALUE_4 440044404444 +#else +#define T_VALUE_1 110011101 +#define T_VALUE_2 220022202 +#define T_VALUE_3 330033303 +#define T_VALUE_4 440044404 +#endif + +#define T_PRINTF_FORMAT "%llu" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/types/unsigned_short.cpp b/packages/Python/lldbsuite/test/types/unsigned_short.cpp new file mode 100644 index 000000000000..09af2aad62c2 --- /dev/null +++ b/packages/Python/lldbsuite/test/types/unsigned_short.cpp @@ -0,0 +1,9 @@ +#define T unsigned short +#define T_CSTR "unsigned short" +#define T_VALUE_1 11001 +#define T_VALUE_2 22002 +#define T_VALUE_3 0 +#define T_VALUE_4 65535 +#define T_PRINTF_FORMAT "%hu" + +#include "basic_type.cpp" diff --git a/packages/Python/lldbsuite/test/warnings/uuid/Makefile b/packages/Python/lldbsuite/test/warnings/uuid/Makefile new file mode 100644 index 000000000000..8a7102e347af --- /dev/null +++ b/packages/Python/lldbsuite/test/warnings/uuid/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/warnings/uuid/TestAddDsymCommand.py b/packages/Python/lldbsuite/test/warnings/uuid/TestAddDsymCommand.py new file mode 100644 index 000000000000..002a7aef0c76 --- /dev/null +++ b/packages/Python/lldbsuite/test/warnings/uuid/TestAddDsymCommand.py @@ -0,0 +1,107 @@ +"""Test that the 'add-dsym', aka 'target symbols add', command informs the user about success or failure.""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +@skipUnlessDarwin +class AddDsymCommandCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.template = 'main.cpp.template' + self.source = 'main.cpp' + self.teardown_hook_added = False + + @no_debug_info_test + def test_add_dsym_command_with_error(self): + """Test that the 'add-dsym' command informs the user about failures.""" + + # Call the program generator to produce main.cpp, version 1. + self.generate_main_cpp(version=1) + self.buildDsym(clean=True) + + # Insert some delay and then call the program generator to produce main.cpp, version 2. + time.sleep(5) + self.generate_main_cpp(version=101) + # Now call make again, but this time don't generate the dSYM. + self.buildDwarf(clean=False) + + self.exe_name = 'a.out' + self.do_add_dsym_with_error(self.exe_name) + + @no_debug_info_test + def test_add_dsym_command_with_success(self): + """Test that the 'add-dsym' command informs the user about success.""" + + # Call the program generator to produce main.cpp, version 1. + self.generate_main_cpp(version=1) + self.buildDsym(clean=True) + + self.exe_name = 'a.out' + self.do_add_dsym_with_success(self.exe_name) + + @no_debug_info_test + def test_add_dsym_with_dSYM_bundle(self): + """Test that the 'add-dsym' command informs the user about success.""" + + # Call the program generator to produce main.cpp, version 1. + self.generate_main_cpp(version=1) + self.buildDsym(clean=True) + + self.exe_name = 'a.out' + self.do_add_dsym_with_dSYM_bundle(self.exe_name) + + + def generate_main_cpp(self, version=0): + """Generate main.cpp from main.cpp.template.""" + temp = os.path.join(os.getcwd(), self.template) + with open(temp, 'r') as f: + content = f.read() + + new_content = content.replace('%ADD_EXTRA_CODE%', + 'printf("This is version %d\\n");' % version) + src = os.path.join(os.getcwd(), self.source) + with open(src, 'w') as f: + f.write(new_content) + + # The main.cpp has been generated, add a teardown hook to remove it. + if not self.teardown_hook_added: + self.addTearDownHook(lambda: os.remove(src)) + self.teardown_hook_added = True + + def do_add_dsym_with_error(self, exe_name): + """Test that the 'add-dsym' command informs the user about failures.""" + self.runCmd("file " + exe_name, CURRENT_EXECUTABLE_SET) + + wrong_path = os.path.join("%s.dSYM" % exe_name, "Contents") + self.expect("add-dsym " + wrong_path, error=True, + substrs = ['invalid module path']) + + right_path = os.path.join("%s.dSYM" % exe_name, "Contents", "Resources", "DWARF", exe_name) + self.expect("add-dsym " + right_path, error=True, + substrs = ['symbol file', 'does not match']) + + def do_add_dsym_with_success(self, exe_name): + """Test that the 'add-dsym' command informs the user about success.""" + self.runCmd("file " + exe_name, CURRENT_EXECUTABLE_SET) + + # This time, the UUID should match and we expect some feedback from lldb. + right_path = os.path.join("%s.dSYM" % exe_name, "Contents", "Resources", "DWARF", exe_name) + self.expect("add-dsym " + right_path, + substrs = ['symbol file', 'has been added to']) + + def do_add_dsym_with_dSYM_bundle(self, exe_name): + """Test that the 'add-dsym' command informs the user about success when loading files in bundles.""" + self.runCmd("file " + exe_name, CURRENT_EXECUTABLE_SET) + + # This time, the UUID should be found inside the bundle + right_path = "%s.dSYM" % exe_name + self.expect("add-dsym " + right_path, + substrs = ['symbol file', 'has been added to']) diff --git a/packages/Python/lldbsuite/test/warnings/uuid/main.cpp.template b/packages/Python/lldbsuite/test/warnings/uuid/main.cpp.template new file mode 100644 index 000000000000..d952c9686b57 --- /dev/null +++ b/packages/Python/lldbsuite/test/warnings/uuid/main.cpp.template @@ -0,0 +1,19 @@ +//===-- 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 + +int +main(int argc, char const *argv[]) +{ + int my_int = argc + 3; + printf("Hello UUID Mismatch: %d\n", my_int); // Set breakpoint here. + %ADD_EXTRA_CODE% + return 0; +} diff --git a/packages/Python/lldbsuite/test/xunit_formatter.py b/packages/Python/lldbsuite/test/xunit_formatter.py new file mode 100644 index 000000000000..35fdba449a0c --- /dev/null +++ b/packages/Python/lldbsuite/test/xunit_formatter.py @@ -0,0 +1,565 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Provides an xUnit ResultsFormatter for integrating the LLDB +test suite with the Jenkins xUnit aggregator and other xUnit-compliant +test output processors. +""" +from __future__ import print_function +from __future__ import absolute_import + +# System modules +import re +import sys +import xml.sax.saxutils + +# Third-party modules +import six + +# Local modules +from .result_formatter import EventBuilder +from .result_formatter import ResultsFormatter + + +class XunitFormatter(ResultsFormatter): + """Provides xUnit-style formatted output. + """ + + # Result mapping arguments + RM_IGNORE = 'ignore' + RM_SUCCESS = 'success' + RM_FAILURE = 'failure' + RM_PASSTHRU = 'passthru' + + @staticmethod + def _build_illegal_xml_regex(): + """Contructs a regex to match all illegal xml characters. + + Expects to be used against a unicode string.""" + # Construct the range pairs of invalid unicode chareacters. + illegal_chars_u = [ + (0x00, 0x08), (0x0B, 0x0C), (0x0E, 0x1F), (0x7F, 0x84), + (0x86, 0x9F), (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF)] + + # For wide builds, we have more. + if sys.maxunicode >= 0x10000: + illegal_chars_u.extend( + [(0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), (0x3FFFE, 0x3FFFF), + (0x4FFFE, 0x4FFFF), (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF), + (0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF), (0x9FFFE, 0x9FFFF), + (0xAFFFE, 0xAFFFF), (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF), + (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF), (0xFFFFE, 0xFFFFF), + (0x10FFFE, 0x10FFFF)]) + + # Build up an array of range expressions. + illegal_ranges = [ + "%s-%s" % (six.unichr(low), six.unichr(high)) + for (low, high) in illegal_chars_u] + + # Compile the regex + return re.compile(six.u('[%s]') % six.u('').join(illegal_ranges)) + + @staticmethod + def _quote_attribute(text): + """Returns the given text in a manner safe for usage in an XML attribute. + + @param text the text that should appear within an XML attribute. + @return the attribute-escaped version of the input text. + """ + return xml.sax.saxutils.quoteattr(text) + + def _replace_invalid_xml(self, str_or_unicode): + """Replaces invalid XML characters with a '?'. + + @param str_or_unicode a string to replace invalid XML + characters within. Can be unicode or not. If not unicode, + assumes it is a byte string in utf-8 encoding. + + @returns a utf-8-encoded byte string with invalid + XML replaced with '?'. + """ + # Get the content into unicode + if isinstance(str_or_unicode, str): + unicode_content = str_or_unicode.decode('utf-8') + else: + unicode_content = str_or_unicode + return self.invalid_xml_re.sub( + six.u('?'), unicode_content).encode('utf-8') + + @classmethod + def arg_parser(cls): + """@return arg parser used to parse formatter-specific options.""" + parser = super(XunitFormatter, cls).arg_parser() + + # These are valid choices for results mapping. + results_mapping_choices = [ + XunitFormatter.RM_IGNORE, + XunitFormatter.RM_SUCCESS, + XunitFormatter.RM_FAILURE, + XunitFormatter.RM_PASSTHRU] + parser.add_argument( + "--assert-on-unknown-events", + action="store_true", + help=('cause unknown test events to generate ' + 'a python assert. Default is to ignore.')) + parser.add_argument( + "--ignore-skip-name", + "-n", + metavar='PATTERN', + action="append", + dest='ignore_skip_name_patterns', + help=('a python regex pattern, where ' + 'any skipped test with a test method name where regex ' + 'matches (via search) will be ignored for xUnit test ' + 'result purposes. Can be specified multiple times.')) + parser.add_argument( + "--ignore-skip-reason", + "-r", + metavar='PATTERN', + action="append", + dest='ignore_skip_reason_patterns', + help=('a python regex pattern, where ' + 'any skipped test with a skip reason where the regex ' + 'matches (via search) will be ignored for xUnit test ' + 'result purposes. Can be specified multiple times.')) + parser.add_argument( + "--xpass", action="store", choices=results_mapping_choices, + default=XunitFormatter.RM_FAILURE, + help=('specify mapping from unexpected success to jUnit/xUnit ' + 'result type')) + parser.add_argument( + "--xfail", action="store", choices=results_mapping_choices, + default=XunitFormatter.RM_IGNORE, + help=('specify mapping from expected failure to jUnit/xUnit ' + 'result type')) + return parser + + @staticmethod + def _build_regex_list_from_patterns(patterns): + """Builds a list of compiled regexes from option value. + + @param option string containing a comma-separated list of regex + patterns. Zero-length or None will produce an empty regex list. + + @return list of compiled regular expressions, empty if no + patterns provided. + """ + regex_list = [] + if patterns is not None: + for pattern in patterns: + regex_list.append(re.compile(pattern)) + return regex_list + + def __init__(self, out_file, options): + """Initializes the XunitFormatter instance. + @param out_file file-like object where formatted output is written. + @param options_dict specifies a dictionary of options for the + formatter. + """ + # Initialize the parent + super(XunitFormatter, self).__init__(out_file, options) + self.text_encoding = "UTF-8" + self.invalid_xml_re = XunitFormatter._build_illegal_xml_regex() + self.total_test_count = 0 + self.ignore_skip_name_regexes = ( + XunitFormatter._build_regex_list_from_patterns( + options.ignore_skip_name_patterns)) + self.ignore_skip_reason_regexes = ( + XunitFormatter._build_regex_list_from_patterns( + options.ignore_skip_reason_patterns)) + + self.elements = { + "successes": [], + "errors": [], + "failures": [], + "skips": [], + "unexpected_successes": [], + "expected_failures": [], + "all": [] + } + + self.status_handlers = { + EventBuilder.STATUS_SUCCESS: self._handle_success, + EventBuilder.STATUS_FAILURE: self._handle_failure, + EventBuilder.STATUS_ERROR: self._handle_error, + EventBuilder.STATUS_SKIP: self._handle_skip, + EventBuilder.STATUS_EXPECTED_FAILURE: + self._handle_expected_failure, + EventBuilder.STATUS_EXPECTED_TIMEOUT: + self._handle_expected_timeout, + EventBuilder.STATUS_UNEXPECTED_SUCCESS: + self._handle_unexpected_success, + EventBuilder.STATUS_EXCEPTIONAL_EXIT: + self._handle_exceptional_exit, + EventBuilder.STATUS_TIMEOUT: + self._handle_timeout + } + + RESULT_TYPES = set( + [EventBuilder.TYPE_TEST_RESULT, + EventBuilder.TYPE_JOB_RESULT]) + + def handle_event(self, test_event): + super(XunitFormatter, self).handle_event(test_event) + + event_type = test_event["event"] + if event_type is None: + return + + if event_type == "terminate": + # Process all the final result events into their + # XML counterparts. + for result_event in self.result_events.values(): + self._process_test_result(result_event) + self._finish_output() + else: + # This is an unknown event. + if self.options.assert_on_unknown_events: + raise Exception("unknown event type {} from {}\n".format( + event_type, test_event)) + + def _handle_success(self, test_event): + """Handles a test success. + @param test_event the test event to handle. + """ + result = self._common_add_testcase_entry(test_event) + with self.lock: + self.elements["successes"].append(result) + + def _handle_failure(self, test_event): + """Handles a test failure. + @param test_event the test event to handle. + """ + message = self._replace_invalid_xml(test_event["issue_message"]) + backtrace = self._replace_invalid_xml( + "".join(test_event.get("issue_backtrace", []))) + + result = self._common_add_testcase_entry( + test_event, + inner_content=( + ''.format( + XunitFormatter._quote_attribute(test_event["issue_class"]), + XunitFormatter._quote_attribute(message), + backtrace) + )) + with self.lock: + self.elements["failures"].append(result) + + def _handle_error(self, test_event): + """Handles a test error. + @param test_event the test event to handle. + """ + message = self._replace_invalid_xml(test_event["issue_message"]) + backtrace = self._replace_invalid_xml( + "".join(test_event.get("issue_backtrace", []))) + + result = self._common_add_testcase_entry( + test_event, + inner_content=( + ''.format( + XunitFormatter._quote_attribute(test_event["issue_class"]), + XunitFormatter._quote_attribute(message), + backtrace) + )) + with self.lock: + self.elements["errors"].append(result) + + def _handle_exceptional_exit(self, test_event): + """Handles an exceptional exit. + @param test_event the test method or job result event to handle. + """ + if "test_name" in test_event: + name = test_event["test_name"] + else: + name = test_event.get("test_filename", "") + + message_text = "ERROR: {} ({}): {}".format( + test_event.get("exception_code", 0), + test_event.get("exception_description", ""), + name) + message = self._replace_invalid_xml(message_text) + + result = self._common_add_testcase_entry( + test_event, + inner_content=( + ''.format( + "exceptional_exit", + XunitFormatter._quote_attribute(message)) + )) + with self.lock: + self.elements["errors"].append(result) + + def _handle_timeout(self, test_event): + """Handles a test method or job timeout. + @param test_event the test method or job result event to handle. + """ + if "test_name" in test_event: + name = test_event["test_name"] + else: + name = test_event.get("test_filename", "") + + message_text = "TIMEOUT: {}".format(name) + message = self._replace_invalid_xml(message_text) + + result = self._common_add_testcase_entry( + test_event, + inner_content=( + ''.format( + "timeout", + XunitFormatter._quote_attribute(message)) + )) + with self.lock: + self.elements["errors"].append(result) + + @staticmethod + def _ignore_based_on_regex_list(test_event, test_key, regex_list): + """Returns whether to ignore a test event based on patterns. + + @param test_event the test event dictionary to check. + @param test_key the key within the dictionary to check. + @param regex_list a list of zero or more regexes. May contain + zero or more compiled regexes. + + @return True if any o the regex list match based on the + re.search() method; false otherwise. + """ + for regex in regex_list: + match = regex.search(test_event.get(test_key, '')) + if match: + return True + return False + + def _handle_skip(self, test_event): + """Handles a skipped test. + @param test_event the test event to handle. + """ + + # Are we ignoring this test based on test name? + if XunitFormatter._ignore_based_on_regex_list( + test_event, 'test_name', self.ignore_skip_name_regexes): + return + + # Are we ignoring this test based on skip reason? + if XunitFormatter._ignore_based_on_regex_list( + test_event, 'skip_reason', self.ignore_skip_reason_regexes): + return + + # We're not ignoring this test. Process the skip. + reason = self._replace_invalid_xml(test_event.get("skip_reason", "")) + result = self._common_add_testcase_entry( + test_event, + inner_content=''.format( + XunitFormatter._quote_attribute(reason))) + with self.lock: + self.elements["skips"].append(result) + + def _handle_expected_failure(self, test_event): + """Handles a test that failed as expected. + @param test_event the test event to handle. + """ + if self.options.xfail == XunitFormatter.RM_PASSTHRU: + # This is not a natively-supported junit/xunit + # testcase mode, so it might fail a validating + # test results viewer. + if "bugnumber" in test_event: + bug_id_attribute = 'bug-id={} '.format( + XunitFormatter._quote_attribute(test_event["bugnumber"])) + else: + bug_id_attribute = '' + + result = self._common_add_testcase_entry( + test_event, + inner_content=( + ''.format( + bug_id_attribute, + XunitFormatter._quote_attribute( + test_event["issue_class"]), + XunitFormatter._quote_attribute( + test_event["issue_message"])) + )) + with self.lock: + self.elements["expected_failures"].append(result) + elif self.options.xfail == XunitFormatter.RM_SUCCESS: + result = self._common_add_testcase_entry(test_event) + with self.lock: + self.elements["successes"].append(result) + elif self.options.xfail == XunitFormatter.RM_FAILURE: + result = self._common_add_testcase_entry( + test_event, + inner_content=''.format( + XunitFormatter._quote_attribute(test_event["issue_class"]), + XunitFormatter._quote_attribute( + test_event["issue_message"]))) + with self.lock: + self.elements["failures"].append(result) + elif self.options.xfail == XunitFormatter.RM_IGNORE: + pass + else: + raise Exception( + "unknown xfail option: {}".format(self.options.xfail)) + + def _handle_expected_timeout(self, test_event): + """Handles expected_timeout. + @param test_event the test event to handle. + """ + # We don't do anything with expected timeouts, not even report. + pass + + def _handle_unexpected_success(self, test_event): + """Handles a test that passed but was expected to fail. + @param test_event the test event to handle. + """ + if self.options.xpass == XunitFormatter.RM_PASSTHRU: + # This is not a natively-supported junit/xunit + # testcase mode, so it might fail a validating + # test results viewer. + result = self._common_add_testcase_entry( + test_event, + inner_content=("")) + with self.lock: + self.elements["unexpected_successes"].append(result) + elif self.options.xpass == XunitFormatter.RM_SUCCESS: + # Treat the xpass as a success. + result = self._common_add_testcase_entry(test_event) + with self.lock: + self.elements["successes"].append(result) + elif self.options.xpass == XunitFormatter.RM_FAILURE: + # Treat the xpass as a failure. + if "bugnumber" in test_event: + message = "unexpected success (bug_id:{})".format( + test_event["bugnumber"]) + else: + message = "unexpected success (bug_id:none)" + result = self._common_add_testcase_entry( + test_event, + inner_content=''.format( + XunitFormatter._quote_attribute("unexpected_success"), + XunitFormatter._quote_attribute(message))) + with self.lock: + self.elements["failures"].append(result) + elif self.options.xpass == XunitFormatter.RM_IGNORE: + # Ignore the xpass result as far as xUnit reporting goes. + pass + else: + raise Exception("unknown xpass option: {}".format( + self.options.xpass)) + + def _process_test_result(self, test_event): + """Processes the test_event known to be a test result. + + This categorizes the event appropriately and stores the data needed + to generate the final xUnit report. This method skips events that + cannot be represented in xUnit output. + """ + if "status" not in test_event: + raise Exception("test event dictionary missing 'status' key") + + status = test_event["status"] + if status not in self.status_handlers: + raise Exception("test event status '{}' unsupported".format( + status)) + + # Call the status handler for the test result. + self.status_handlers[status](test_event) + + def _common_add_testcase_entry(self, test_event, inner_content=None): + """Registers a testcase result, and returns the text created. + + The caller is expected to manage failure/skip/success counts + in some kind of appropriate way. This call simply constructs + the XML and appends the returned result to the self.all_results + list. + + @param test_event the test event dictionary. + + @param inner_content if specified, gets included in the + inner section, at the point before stdout and stderr would be + included. This is where a , , , etc. + could go. + + @return the text of the xml testcase element. + """ + + # Get elapsed time. + test_class = test_event["test_class"] + test_name = test_event["test_name"] + event_time = test_event["event_time"] + time_taken = self.elapsed_time_for_test( + test_class, test_name, event_time) + + # Plumb in stdout/stderr once we shift over to only test results. + test_stdout = '' + test_stderr = '' + + # Formulate the output xml. + if not inner_content: + inner_content = "" + result = ( + '' + '{}{}{}'.format( + test_class, + test_name, + time_taken, + inner_content, + test_stdout, + test_stderr)) + + # Save the result, update total test count. + with self.lock: + self.total_test_count += 1 + self.elements["all"].append(result) + + return result + + def _finish_output_no_lock(self): + """Flushes out the report of test executions to form valid xml output. + + xUnit output is in XML. The reporting system cannot complete the + formatting of the output without knowing when there is no more input. + This call addresses notifcation of the completed test run and thus is + when we can finish off the report output. + """ + + # Figure out the counts line for the testsuite. If we have + # been counting either unexpected successes or expected + # failures, we'll output those in the counts, at the risk of + # being invalidated by a validating test results viewer. + # These aren't counted by default so they won't show up unless + # the user specified a formatter option to include them. + xfail_count = len(self.elements["expected_failures"]) + xpass_count = len(self.elements["unexpected_successes"]) + if xfail_count > 0 or xpass_count > 0: + extra_testsuite_attributes = ( + ' expected-failures="{}"' + ' unexpected-successes="{}"'.format(xfail_count, xpass_count)) + else: + extra_testsuite_attributes = "" + + # Output the header. + self.out_file.write( + '\n' + '' + '\n'.format( + self.text_encoding, + "LLDB test suite", + self.total_test_count, + len(self.elements["errors"]), + len(self.elements["failures"]), + len(self.elements["skips"]), + extra_testsuite_attributes)) + + # Output each of the test result entries. + for result in self.elements["all"]: + self.out_file.write(result + '\n') + + # Close off the test suite. + self.out_file.write('\n') + + def _finish_output(self): + """Finish writing output as all incoming events have arrived.""" + with self.lock: + self._finish_output_no_lock() diff --git a/resources/LLDB-Info.plist b/resources/LLDB-Info.plist new file mode 100644 index 000000000000..57e5a51fd797 --- /dev/null +++ b/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.${CURRENT_PROJECT_VERSION} + CFBundleSignature + ???? + CFBundleVersion + 350.99.0 + CFBundleName + ${EXECUTABLE_NAME} + + diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt new file mode 100644 index 000000000000..00bec0b2dae5 --- /dev/null +++ b/scripts/CMakeLists.txt @@ -0,0 +1,37 @@ +file(GLOB SWIG_INTERFACES interface/*.i) +file(GLOB_RECURSE SWIG_SOURCES *.swig) +set(SWIG_HEADERS + ${LLDB_SOURCE_DIR}/include/lldb/API/SBDefines.h + ${LLDB_SOURCE_DIR}/include/lldb/lldb-defines.h + ${LLDB_SOURCE_DIR}/include/lldb/lldb-enumerations.h + ${LLDB_SOURCE_DIR}/include/lldb/lldb-forward.h + ${LLDB_SOURCE_DIR}/include/lldb/lldb-types.h + ${LLDB_SOURCE_DIR}/include/lldb/lldb-versioning.h +) + +find_package(SWIG REQUIRED) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lldb.py + DEPENDS ${SWIG_SOURCES} + DEPENDS ${SWIG_INTERFACES} + DEPENDS ${SWIG_HEADERS} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Python/prepare_binding_Python.py + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Python/modify-python-lldb.py + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/prepare_bindings.py "--srcRoot=${LLDB_SOURCE_DIR}" "--targetDir=${CMAKE_CURRENT_BINARY_DIR}" "--cfgBldDir=${CMAKE_CURRENT_BINARY_DIR}" "--prefix=${CMAKE_BINARY_DIR}" "--swigExecutable=${SWIG_EXECUTABLE}" + COMMENT "Python script building LLDB Python wrapper") +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp PROPERTIES GENERATED 1) +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lldb.py PROPERTIES GENERATED 1) + +add_custom_target(swig_wrapper ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp + ) + +# Install the LLDB python module on all operating systems (except Windows) +if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") + install(DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR} + DESTINATION lib${LLVM_LIBDIR_SUFFIX}) +endif() + +# build Python modules +add_subdirectory(Python/modules) diff --git a/scripts/Makefile b/scripts/Makefile new file mode 100644 index 000000000000..807823f43e44 --- /dev/null +++ b/scripts/Makefile @@ -0,0 +1,17 @@ +##===- scripts/Makefile ------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := .. +include $(LLDB_LEVEL)/../../Makefile.config + +ifeq (,$(findstring -DLLDB_DISABLE_PYTHON,$(CXXFLAGS))) +DIRS := Python +endif + +include $(LLDB_LEVEL)/Makefile diff --git a/scripts/Python/Makefile b/scripts/Python/Makefile new file mode 100644 index 000000000000..ad6c0af442b4 --- /dev/null +++ b/scripts/Python/Makefile @@ -0,0 +1,15 @@ +##===- scripts/Python/Makefile------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +include $(LLDB_LEVEL)/../../Makefile.config + +DIRS := modules + +include $(LLDB_LEVEL)/Makefile diff --git a/scripts/Python/android/host_art_bt.py b/scripts/Python/android/host_art_bt.py new file mode 100644 index 000000000000..0893662869f2 --- /dev/null +++ b/scripts/Python/android/host_art_bt.py @@ -0,0 +1,192 @@ +# Usage: +# art/test/run-test --host --gdb [--64] [--interpreter] 004-JniTest +# 'b Java_Main_shortMethod' +# 'r' +# 'command script import host_art_bt.py' +# 'host_art_bt' + +import sys +import re + +import lldb + +def host_art_bt(debugger, command, result, internal_dict): + prettified_frames = [] + lldb_frame_index = 0 + art_frame_index = 0 + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + while lldb_frame_index < thread.GetNumFrames(): + frame = thread.GetFrameAtIndex(lldb_frame_index) + if frame.GetModule() and re.match(r'JIT\(.*?\)', frame.GetModule().GetFileSpec().GetFilename()): + # Compiled Java frame + + # Get function/filename/lineno from symbol context + symbol = frame.GetSymbol() + if not symbol: + print 'No symbol info for compiled Java frame: ', frame + sys.exit(1) + line_entry = frame.GetLineEntry() + prettified_frames.append({ + 'function': symbol.GetName(), + 'file' : str(line_entry.GetFileSpec()) if line_entry else None, + 'line' : line_entry.GetLine() if line_entry else -1 + }) + + # Skip art frames + while True: + art_stack_visitor = frame.EvaluateExpression("""struct GetStackVisitor : public StackVisitor { GetStackVisitor(int depth_) : StackVisitor(Thread::Current(), NULL), depth(depth_) {} bool VisitFrame() { if (cur_depth_ == depth) { return false; } else { return true; } } int depth; }; GetStackVisitor visitor(""" + str(art_frame_index) + """); visitor.WalkStack(true); visitor""") + art_method = frame.EvaluateExpression(art_stack_visitor.GetName() + """.GetMethod()""") + if art_method.GetValueAsUnsigned() != 0: + art_method_name = frame.EvaluateExpression("""art::PrettyMethod(""" + art_method.GetName() + """, true)""") + art_method_name_data = frame.EvaluateExpression(art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned() + art_method_name_size = frame.EvaluateExpression(art_method_name.GetName() + """.length()""").GetValueAsUnsigned() + error = lldb.SBError() + art_method_name = process.ReadCStringFromMemory(art_method_name_data, art_method_name_size + 1, error) + if not error.Success: + print 'Failed to read method name' + sys.exit(1) + if art_method_name != symbol.GetName(): + print 'Function names in native symbol and art runtime stack do not match: ', symbol.GetName(), ' != ', art_method_name + art_frame_index = art_frame_index + 1 + break + art_frame_index = art_frame_index + 1 + + # Skip native frames + lldb_frame_index = lldb_frame_index + 1 + if lldb_frame_index < thread.GetNumFrames(): + frame = thread.GetFrameAtIndex(lldb_frame_index) + if frame.GetModule() and re.match(r'JIT\(.*?\)', frame.GetModule().GetFileSpec().GetFilename()): + # Another compile Java frame + # Don't skip; leave it to the next iteration + continue + elif frame.GetSymbol() and (frame.GetSymbol().GetName() == 'art_quick_invoke_stub' or frame.GetSymbol().GetName() == 'art_quick_invoke_static_stub'): + # art_quick_invoke_stub / art_quick_invoke_static_stub + # Skip until we get past the next ArtMethod::Invoke() + while True: + lldb_frame_index = lldb_frame_index + 1 + if lldb_frame_index >= thread.GetNumFrames(): + print 'ArtMethod::Invoke not found below art_quick_invoke_stub/art_quick_invoke_static_stub' + sys.exit(1) + frame = thread.GetFrameAtIndex(lldb_frame_index) + if frame.GetSymbol() and frame.GetSymbol().GetName() == 'art::mirror::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)': + lldb_frame_index = lldb_frame_index + 1 + break + else: + print 'Invalid frame below compiled Java frame: ', frame + elif frame.GetSymbol() and frame.GetSymbol().GetName() == 'art_quick_generic_jni_trampoline': + # Interpreted JNI frame for x86_64 + + # Skip art frames + while True: + art_stack_visitor = frame.EvaluateExpression("""struct GetStackVisitor : public StackVisitor { GetStackVisitor(int depth_) : StackVisitor(Thread::Current(), NULL), depth(depth_) {} bool VisitFrame() { if (cur_depth_ == depth) { return false; } else { return true; } } int depth; }; GetStackVisitor visitor(""" + str(art_frame_index) + """); visitor.WalkStack(true); visitor""") + art_method = frame.EvaluateExpression(art_stack_visitor.GetName() + """.GetMethod()""") + if art_method.GetValueAsUnsigned() != 0: + # Get function/filename/lineno from ART runtime + art_method_name = frame.EvaluateExpression("""art::PrettyMethod(""" + art_method.GetName() + """, true)""") + art_method_name_data = frame.EvaluateExpression(art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned() + art_method_name_size = frame.EvaluateExpression(art_method_name.GetName() + """.length()""").GetValueAsUnsigned() + error = lldb.SBError() + function = process.ReadCStringFromMemory(art_method_name_data, art_method_name_size + 1, error) + + prettified_frames.append({ + 'function': function, + 'file' : None, + 'line' : -1 + }) + + art_frame_index = art_frame_index + 1 + break + art_frame_index = art_frame_index + 1 + + # Skip native frames + lldb_frame_index = lldb_frame_index + 1 + if lldb_frame_index < thread.GetNumFrames(): + frame = thread.GetFrameAtIndex(lldb_frame_index) + if frame.GetSymbol() and (frame.GetSymbol().GetName() == 'art_quick_invoke_stub' or frame.GetSymbol().GetName() == 'art_quick_invoke_static_stub'): + # art_quick_invoke_stub / art_quick_invoke_static_stub + # Skip until we get past the next ArtMethod::Invoke() + while True: + lldb_frame_index = lldb_frame_index + 1 + if lldb_frame_index >= thread.GetNumFrames(): + print 'ArtMethod::Invoke not found below art_quick_invoke_stub/art_quick_invoke_static_stub' + sys.exit(1) + frame = thread.GetFrameAtIndex(lldb_frame_index) + if frame.GetSymbol() and frame.GetSymbol().GetName() == 'art::mirror::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)': + lldb_frame_index = lldb_frame_index + 1 + break + else: + print 'Invalid frame below compiled Java frame: ', frame + elif frame.GetSymbol() and re.search(r'art::interpreter::', frame.GetSymbol().GetName()): + # Interpreted Java frame + + while True: + lldb_frame_index = lldb_frame_index + 1 + if lldb_frame_index >= thread.GetNumFrames(): + print 'art::interpreter::Execute not found in interpreter frame' + sys.exit(1) + frame = thread.GetFrameAtIndex(lldb_frame_index) + if frame.GetSymbol() and frame.GetSymbol().GetName() == 'art::interpreter::Execute(art::Thread*, art::MethodHelper&, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)': + break + + # Skip art frames + while True: + art_stack_visitor = frame.EvaluateExpression("""struct GetStackVisitor : public StackVisitor { GetStackVisitor(int depth_) : StackVisitor(Thread::Current(), NULL), depth(depth_) {} bool VisitFrame() { if (cur_depth_ == depth) { return false; } else { return true; } } int depth; }; GetStackVisitor visitor(""" + str(art_frame_index) + """); visitor.WalkStack(true); visitor""") + art_method = frame.EvaluateExpression(art_stack_visitor.GetName() + """.GetMethod()""") + if art_method.GetValueAsUnsigned() != 0: + # Get function/filename/lineno from ART runtime + art_method_name = frame.EvaluateExpression("""art::PrettyMethod(""" + art_method.GetName() + """, true)""") + art_method_name_data = frame.EvaluateExpression(art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned() + art_method_name_size = frame.EvaluateExpression(art_method_name.GetName() + """.length()""").GetValueAsUnsigned() + error = lldb.SBError() + function = process.ReadCStringFromMemory(art_method_name_data, art_method_name_size + 1, error) + + line = frame.EvaluateExpression(art_stack_visitor.GetName() + """.GetMethod()->GetLineNumFromDexPC(""" + art_stack_visitor.GetName() + """.GetDexPc(true))""").GetValueAsUnsigned() + + file_name = frame.EvaluateExpression(art_method.GetName() + """->GetDeclaringClassSourceFile()""") + file_name_data = file_name.GetValueAsUnsigned() + file_name_size = frame.EvaluateExpression("""(size_t)strlen(""" + file_name.GetName() + """)""").GetValueAsUnsigned() + error = lldb.SBError() + file_name = process.ReadCStringFromMemory(file_name_data, file_name_size + 1, error) + if not error.Success(): + print 'Failed to read source file name' + sys.exit(1) + + prettified_frames.append({ + 'function': function, + 'file' : file_name, + 'line' : line + }) + + art_frame_index = art_frame_index + 1 + break + art_frame_index = art_frame_index + 1 + + # Skip native frames + while True: + lldb_frame_index = lldb_frame_index + 1 + if lldb_frame_index >= thread.GetNumFrames(): + print 'Can not get past interpreter native frames' + sys.exit(1) + frame = thread.GetFrameAtIndex(lldb_frame_index) + if frame.GetSymbol() and not re.search(r'art::interpreter::', frame.GetSymbol().GetName()): + break + else: + # Other frames. Add them as-is. + frame = thread.GetFrameAtIndex(lldb_frame_index) + lldb_frame_index = lldb_frame_index + 1 + if frame.GetModule(): + module_name = frame.GetModule().GetFileSpec().GetFilename() + if not module_name in ['libartd.so', 'dalvikvm32', 'dalvikvm64', 'libc.so.6']: + prettified_frames.append({ + 'function': frame.GetSymbol().GetName() if frame.GetSymbol() else None, + 'file' : str(frame.GetLineEntry().GetFileSpec()) if frame.GetLineEntry() else None, + 'line' : frame.GetLineEntry().GetLine() if frame.GetLineEntry() else -1 + }) + + for prettified_frame in prettified_frames: + print prettified_frame['function'], prettified_frame['file'], prettified_frame['line'] + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand('command script add -f host_art_bt.host_art_bt host_art_bt') diff --git a/scripts/Python/finish-swig-Python-LLDB.sh b/scripts/Python/finish-swig-Python-LLDB.sh new file mode 100755 index 000000000000..92b99181c7cc --- /dev/null +++ b/scripts/Python/finish-swig-Python-LLDB.sh @@ -0,0 +1,310 @@ +#!/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 two files, lldb.py and _lldb.so, that +# it can find. lldb.py is generated by SWIG at the same time it generates the +# C++ file. _lldb.so is actually a symlink file that points to the +# LLDB shared library/framework. +# +# The Python script interpreter needs to be able to automatically find +# these two files. On Darwin systems it searches in the LLDB.framework, as +# well as in all the normal Python search paths. On non-Darwin systems +# these files will need to be put someplace where Python will find them. +# +# This shell script creates the _lldb.so symlink in the appropriate place, +# and copies the lldb.py (and embedded_interpreter.py) file to the correct +# directory. +# + +# SRC_ROOT is the root of the lldb source tree. +# TARGET_DIR is where the lldb framework/shared library gets put. +# CONFIG_BUILD_DIR is where the build-swig-Python-LLDB.sh shell script +# put the lldb.py file it was generated from running SWIG. +# PYTHON_INSTALL_DIR is where non-Darwin systems want to put the .py and .so +# files so that Python can find them automatically. +# debug_flag (optional) determines whether or not this script outputs +# additional information when running. + +SRC_ROOT=$1 +TARGET_DIR=$2 +CONFIG_BUILD_DIR=$3 +PYTHON_INSTALL_DIR=$4 +debug_flag=$5 +makefile_flag=$6 + +# If we don't want Python, then just do nothing here. +# Note, at present iOS doesn't have Python, so if you're building for iOS be sure to +# set LLDB_DISABLE_PYTHON to 1. + +if [ ! "$LLDB_DISABLE_PYTHON" = "1" ] ; then + +if [ -n "$debug_flag" -a "$debug_flag" = "-debug" ] +then + Debug=1 +else + Debug=0 +fi + +if [ -n "$makefile_flag" -a "$makefile_flag" = "-m" ] +then + MakefileCalled=1 +else + MakefileCalled=0 +fi + +OS_NAME=`uname -s` +PYTHON=${PYTHON_EXECUTABLE:-/usr/bin/env python} +PYTHON_VERSION=`${PYTHON} --version 2>&1 | sed -e 's,Python ,,' -e 's,[.][0-9],,2' -e 's,[a-z][a-z][0-9],,'` + + +if [ $Debug -eq 1 ] +then + echo "The current OS is $OS_NAME" + echo "The Python version is $PYTHON_VERSION" +fi + +if [ ${OS_NAME} = "Darwin" ] +then + SOEXT=".dylib" +else + SOEXT=".so" +fi + +# +# Determine where to put the files. + +if [ $MakefileCalled -eq 0 ] +then + # We are being built by Xcode, so all the lldb Python files can go + # into the LLDB.framework/Resources/Python subdirectory. + + if [ ! -d "${TARGET_DIR}/LLDB.framework" ] + then + echo "Error: Unable to find LLDB.framework" >&2 + exit 1 + else + if [ $Debug -eq 1 ] + then + echo "Found ${TARGET_DIR}/LLDB.framework." + fi + fi + + # Make the Python directory in the framework if it doesn't already exist + + framework_python_dir="${TARGET_DIR}/LLDB.framework/Resources/Python/lldb" +else + # We are being built by LLVM, so use the PYTHON_INSTALL_DIR argument, + # and append the python version directory to the end of it. Depending on + # the system other stuff may need to be put here as well. + + if [ -n "${PYTHON_INSTALL_DIR}" ] + then + framework_python_dir=`${PYTHON} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(True, False, \"${PYTHON_INSTALL_DIR}\");"`/lldb + else + framework_python_dir=`${PYTHON} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(True, False);"`/lldb + fi +fi + +[ -n "${CONFIG_BUILD_DIR}" ] || CONFIG_BUILD_DIR=${framework_python_dir} + +# +# Look for the directory in which to put the Python files; if it does not +# already exist, attempt to make it. +# + +if [ $Debug -eq 1 ] +then + echo "Python files will be put in ${framework_python_dir}" +fi + +python_dirs="${framework_python_dir}" + +for python_dir in $python_dirs +do + if [ ! -d "${python_dir}" ] + then + if [ $Debug -eq 1 ] + then + echo "Making directory ${python_dir}" + fi + mkdir -p "${python_dir}" + else + if [ $Debug -eq 1 ] + then + echo "${python_dir} already exists." + fi + fi + + if [ ! -d "${python_dir}" ] + then + echo "Error: Unable to find or create ${python_dir}" >&2 + exit 1 + fi +done + +# Make the symlink that the script bridge for Python will need in the +# Python framework directory + +if [ ! -L "${framework_python_dir}/_lldb.so" ] +then + if [ $Debug -eq 1 ] + then + echo "Creating symlink for _lldb.so" + fi + cd "${framework_python_dir}" + if [ $MakefileCalled -eq 0 ] + then + ln -s "../../../LLDB" _lldb.so + else + ln -s "../../../liblldb${SOEXT}" _lldb.so + fi +else + if [ $Debug -eq 1 ] + then + echo "${framework_python_dir}/_lldb.so already exists." + fi +fi + +# Make symlink for darwin-debug on Darwin +if [ ${OS_NAME} = "Darwin" ] && [ $MakefileCalled -ne 0 ] +then + # We are being built by CMake on Darwin + + if [ ! -L "${framework_python_dir}/darwin-debug" ] + then + if [ $Debug -eq 1 ] + then + echo "Creating symlink for darwin-debug" + fi + cd "${framework_python_dir}" + ln -s "../../../../bin/lldb-launcher" darwin-debug + else + if [ $Debug -eq 1 ] + then + echo "${framework_python_dir}/darwin-debug already exists." + fi + fi +fi + +# Make symlink for lldb-argdumper on any platform +if [ $MakefileCalled -ne 0 ] +then + # We are being built by CMake + + if [ ! -L "${framework_python_dir}/lldb-argdumper" ] + then + if [ $Debug -eq 1 ] + then + echo "Creating symlink for lldb-argdumper" + fi + cd "${framework_python_dir}" + ln -s "../../../../bin/lldb-argdumper" lldb-argdumper + else + if [ $Debug -eq 1 ] + then + echo "${framework_python_dir}/lldb-argdumper already exists." + fi + fi +fi + +create_python_package () { + package_dir="${framework_python_dir}$1" + package_files="$2" + package_name=`echo $1 | tr '/' '.'` + package_name="lldb${package_name}" + + if [ ! -d "${package_dir}" ] + then + mkdir -p "${package_dir}" + fi + + for package_file in $package_files + do + if [ -f "${package_file}" ] + then + cp "${package_file}" "${package_dir}" + package_file_basename=$(basename "${package_file}") + fi + done + + + # Create a packate init file if there wasn't one + package_init_file="${package_dir}/__init__.py" + if [ ! -f "${package_init_file}" ] + then + printf "__all__ = [" > "${package_init_file}" + python_module_separator="" + for package_file in $package_files + do + if [ -f "${package_file}" ] + then + package_file_basename=$(basename "${package_file}") + printf "${python_module_separator}\"${package_file_basename%.*}\"" >> "${package_init_file}" + python_module_separator=", " + fi + done + echo "]" >> "${package_init_file}" + echo "for x in __all__:" >> "${package_init_file}" + echo " __import__('${package_name}.'+x)" >> "${package_init_file}" + fi + + +} + +# Copy the lldb.py file into the lldb package directory and rename to __init_.py +cp "${CONFIG_BUILD_DIR}/lldb.py" "${framework_python_dir}/__init__.py" + +# lldb +package_files="${SRC_ROOT}/source/Interpreter/embedded_interpreter.py" +create_python_package "" "${package_files}" + +# lldb/formatters/cpp +package_files="${SRC_ROOT}/examples/synthetic/gnu_libstdcpp.py +${SRC_ROOT}/examples/synthetic/libcxx.py" +create_python_package "/formatters/cpp" "${package_files}" + +# make an empty __init__.py in lldb/runtime +# this is required for Python to recognize lldb.runtime as a valid package +# (and hence, lldb.runtime.objc as a valid contained package) +create_python_package "/runtime" "" + +# lldb/formatters +# having these files copied here ensures that lldb/formatters is a valid package itself +package_files="${SRC_ROOT}/examples/summaries/cocoa/cache.py +${SRC_ROOT}/examples/summaries/cocoa/metrics.py +${SRC_ROOT}/examples/summaries/cocoa/attrib_fromdict.py +${SRC_ROOT}/examples/summaries/cocoa/Logger.py" +create_python_package "/formatters" "${package_files}" + +# lldb/utils +package_files="${SRC_ROOT}/examples/python/symbolication.py" +create_python_package "/utils" "${package_files}" + +if [ ${OS_NAME} = "Darwin" ] +then + # lldb/macosx + package_files="${SRC_ROOT}/examples/python/crashlog.py + ${SRC_ROOT}/examples/darwin/heap_find/heap.py" + create_python_package "/macosx" "${package_files}" + + # lldb/diagnose + package_files="${SRC_ROOT}/examples/python/diagnose_unwind.py + ${SRC_ROOT}/examples/python/diagnose_nsstring.py" + create_python_package "/diagnose" "${package_files}" + + # Copy files needed by lldb/macosx/heap.py to build libheap.dylib + heap_dir="${framework_python_dir}/macosx/heap" + if [ ! -d "${heap_dir}" ] + then + mkdir -p "${heap_dir}" + cp "${SRC_ROOT}/examples/darwin/heap_find/heap/heap_find.cpp" "${heap_dir}" + cp "${SRC_ROOT}/examples/darwin/heap_find/heap/Makefile" "${heap_dir}" + fi +fi + +fi + +exit 0 + diff --git a/scripts/Python/finishSwigPythonLLDB.py b/scripts/Python/finishSwigPythonLLDB.py new file mode 100644 index 000000000000..435cb88c20f0 --- /dev/null +++ b/scripts/Python/finishSwigPythonLLDB.py @@ -0,0 +1,793 @@ +""" Python SWIG post process script for each language + + -------------------------------------------------------------------------- + File: finishSwigPythonLLDB.py + + Overview: Python script(s) to post process SWIG Python C++ Script + Bridge wrapper code on the Windows/LINUX/OSX platform. + The Python scripts are equivalent to the shell script (.sh) + files. + For the Python script interpreter (external to liblldb) to + be able to import and use the lldb module, there must be + two files, lldb.py and _lldb.so, that it can find. lldb.py + is generated by SWIG at the same time it generates the C++ + file. _lldb.so is actually a symlink file that points to + the LLDB shared library/framework. + The Python script interpreter needs to be able to + automatically find these two files. On Darwin systems it + searches in the LLDB.framework, as well as in all the normal + Python search paths. On non-Darwin systems these files will + need to be put some place where Python will find them. + This shell script creates the _lldb.so symlink in the + appropriate place, and copies the lldb.py (and + embedded_interpreter.py) file to the correct directory. + + Gotchas: Python debug complied pythonXX_d.lib is required for SWIG + to build correct LLDBWrapperPython.cpp in order for Visual + Studio to compile successfully. The release version of the + Python lib will not work (20/12/2013). + LLDB (dir) CMakeLists.txt uses windows environmental + variables $PYTHON_INCLUDE and $PYTHON_LIB to locate + Python files required for the build. + + Copyright: None. + -------------------------------------------------------------------------- + +""" + +# Python modules: +import os # Provide directory and file handling, determine OS information +import sys # System specific parameters and functions +import shutil # High-level operations on files and collections of files +import ctypes # Invoke Windows API for creating symlinks + +# Third party modules: + +# In-house modules: +import utilsOsType # Determine the OS type this script is running on +import utilsDebug # Debug Python scripts + +# User facing text: +strMsgOsVersion = "The current OS is %s" +strMsgPyVersion = "The Python version is %d.%d" +strErrMsgProgFail = "Program failure: " +strErrMsgLLDBPyFileNotNotFound = "Unable to locate lldb.py at path '%s'" +strMsgCopyLLDBPy = "Copying lldb.py from '%s' to '%s'" +strErrMsgFrameWkPyDirNotExist = "Unable to find the LLDB.framework directory '%s'" +strMsgCreatePyPkgCopyPkgFile = "create_py_pkg: Copied file '%s' to folder '%s'" +strMsgCreatePyPkgInitFile = "create_py_pkg: Creating pakage init file '%s'" +strMsgCreatePyPkgMkDir = "create_py_pkg: Created folder '%s'" +strMsgConfigBuildDir = "Configuration build directory located at '%s'" +strMsgFoundLldbFrameWkDir = "Found '%s'" +strMsgPyFileLocatedHere = "Python file will be put in '%s'" +strMsgFrameWkPyExists = "Python output folder '%s' already exists" +strMsgFrameWkPyMkDir = "Python output folder '%s' will be created" +strErrMsgCreateFrmWkPyDirFailed = "Unable to create directory '%s' error: %s" +strMsgSymlinkExists = "Symlink for '%s' already exists" +strMsgSymlinkMk = "Creating symlink for %s (%s -> %s)" +strErrMsgCpLldbpy = "copying lldb to lldb package directory" +strErrMsgCreatePyPkgMissingSlash = "Parameter 3 fn create_py_pkg() missing slash" +strErrMsgMkLinkExecute = "Command mklink failed: %s" +strErrMsgMakeSymlink = "creating symbolic link" +strErrMsgUnexpected = "Unexpected error: %s" +strMsgCopySixPy = "Copying six.py from '%s' to '%s'" +strErrMsgCopySixPyFailed = "Unable to copy '%s' to '%s'" + +def is_debug_interpreter(): + return hasattr(sys, 'gettotalrefcount') + +#++--------------------------------------------------------------------------- +# Details: Copy files needed by lldb/macosx/heap.py to build libheap.dylib. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def macosx_copy_file_for_heap(vDictArgs, vstrFrameworkPythonDir): + dbg = utilsDebug.CDebugFnVerbose("Python script macosx_copy_file_for_heap()") + bOk = True + strMsg = "" + + eOSType = utilsOsType.determine_os_type() + if eOSType != utilsOsType.EnumOsType.Darwin: + return (bOk, strMsg) + + strHeapDir = os.path.join(vstrFrameworkPythonDir, "macosx", "heap") + strHeapDir = os.path.normcase(strHeapDir) + if os.path.exists(strHeapDir) and os.path.isdir(strHeapDir): + return (bOk, strMsg) + + os.makedirs(strHeapDir) + + strRoot = os.path.normpath(vDictArgs["--srcRoot"]) + strSrc = os.path.join(strRoot, "examples", "darwin", "heap_find", "heap", "heap_find.cpp") + shutil.copy(strSrc, strHeapDir) + strSrc = os.path.join(strRoot, "examples", "darwin", "heap_find", "heap", "Makefile") + shutil.copy(strSrc, strHeapDir) + + return (bOk, strMsg) + +#++--------------------------------------------------------------------------- +# Details: Create Python packages and Python __init__ files. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# vstrPkgDir - (R) Destination for copied Python files. +# vListPkgFiles - (R) List of source Python files. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def create_py_pkg(vDictArgs, vstrFrameworkPythonDir, vstrPkgDir, vListPkgFiles): + dbg = utilsDebug.CDebugFnVerbose("Python script create_py_pkg()") + dbg.dump_object("Package file(s):", vListPkgFiles) + bDbg = "-d" in vDictArgs + + bOk = True + strMsg = "" + + if vstrPkgDir.__len__() != 0 and vstrPkgDir[0] != "/": + bOk = False + strMsg = strErrMsgCreatePyPkgMissingSlash + return (bOk, strMsg) + + strPkgName = vstrPkgDir + strPkgName = "lldb" + strPkgName.replace("/", ".") + + strPkgDir = vstrFrameworkPythonDir + strPkgDir += vstrPkgDir + strPkgDir = os.path.normcase(strPkgDir) + + if not(os.path.exists(strPkgDir) and os.path.isdir(strPkgDir)): + if bDbg: + print((strMsgCreatePyPkgMkDir % strPkgDir)) + os.makedirs(strPkgDir) + + for strPkgFile in vListPkgFiles: + if os.path.exists(strPkgFile) and os.path.isfile(strPkgFile): + if bDbg: + print((strMsgCreatePyPkgCopyPkgFile % (strPkgFile, strPkgDir))) + shutil.copy(strPkgFile, strPkgDir) + + # Create a packet init files if there wasn't one + strPkgIniFile = os.path.normpath(os.path.join(strPkgDir, "__init__.py")) + if os.path.exists(strPkgIniFile) and os.path.isfile(strPkgIniFile): + return (bOk, strMsg) + + strPyScript = "__all__ = [" + strDelimiter = "" + for strPkgFile in vListPkgFiles: + if os.path.exists(strPkgFile) and os.path.isfile(strPkgFile): + strBaseName = os.path.basename(strPkgFile) + nPos = strBaseName.find(".") + if nPos != -1: + strBaseName = strBaseName[0 : nPos] + strPyScript += "%s\"%s\"" % (strDelimiter, strBaseName) + strDelimiter = "," + strPyScript += "]\n" + strPyScript += "for x in __all__:\n" + strPyScript += "\t__import__('%s.' + x)" % strPkgName + + if bDbg: + print((strMsgCreatePyPkgInitFile % strPkgIniFile)) + file = open(strPkgIniFile, "w") + file.write(strPyScript) + file.close() + + return (bOk, strMsg) + +#++--------------------------------------------------------------------------- +# Details: Copy the lldb.py file into the lldb package directory and rename +# to __init_.py. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# vstrCfgBldDir - (R) Config directory path. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def copy_lldbpy_file_to_lldb_pkg_dir(vDictArgs, vstrFrameworkPythonDir, vstrCfgBldDir): + dbg = utilsDebug.CDebugFnVerbose("Python script copy_lldbpy_file_to_lldb_pkg_dir()") + bOk = True + bDbg = "-d" in vDictArgs + strMsg = "" + + strSrc = os.path.join(vstrCfgBldDir, "lldb.py") + strSrc = os.path.normcase(strSrc) + strDst = os.path.join(vstrFrameworkPythonDir, "__init__.py") + strDst = os.path.normcase(strDst) + + if not os.path.exists(strSrc): + strMsg = strErrMsgLLDBPyFileNotNotFound % strSrc + return (bOk, strMsg) + + try: + if bDbg: + print((strMsgCopyLLDBPy % (strSrc, strDst))) + shutil.copyfile(strSrc, strDst) + except IOError as e: + bOk = False + strMsg = "I/O error(%d): %s %s" % (e.errno, e.strerror, strErrMsgCpLldbpy) + if e.errno == 2: + strMsg += " Src:'%s' Dst:'%s'" % (strSrc, strDst) + except: + bOk = False + strMsg = strErrMsgUnexpected % sys.exec_info()[0] + + return (bOk, strMsg) + +#++--------------------------------------------------------------------------- +# Details: Make the symbolic link on a Windows platform. +# Args: vstrSrcFile - (R) Source file name. +# vstrTargetFile - (R) Destination file name. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def make_symlink_windows(vstrSrcPath, vstrTargetPath): + print(("Making symlink from %s to %s" % (vstrSrcPath, vstrTargetPath))) + dbg = utilsDebug.CDebugFnVerbose("Python script make_symlink_windows()") + bOk = True + strErrMsg = "" + + try: + csl = ctypes.windll.kernel32.CreateHardLinkW + csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32) + csl.restype = ctypes.c_ubyte + if csl(vstrTargetPath, vstrSrcPath, 0) == 0: + raise ctypes.WinError() + except Exception as e: + if e.errno != 17: + bOk = False + strErrMsg = "WinError(%d): %s %s" % (e.errno, e.strerror, strErrMsgMakeSymlink) + strErrMsg += " Src:'%s' Target:'%s'" % (vstrSrcPath, vstrTargetPath) + + return (bOk, strErrMsg) + +#++--------------------------------------------------------------------------- +# Details: Make the symbolic link on a UNIX style platform. +# Args: vstrSrcFile - (R) Source file name. +# vstrTargetFile - (R) Destination file name. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def make_symlink_other_platforms(vstrSrcPath, vstrTargetPath): + dbg = utilsDebug.CDebugFnVerbose("Python script make_symlink_other_platforms()") + bOk = True + strErrMsg = "" + + try: + os.symlink(vstrSrcPath, vstrTargetPath) + except OSError as e: + bOk = False + strErrMsg = "OSError(%d): %s %s" % (e.errno, e.strerror, strErrMsgMakeSymlink) + strErrMsg += " Src:'%s' Target:'%s'" % (vstrSrcPath, vstrTargetPath) + except: + bOk = False + strErrMsg = strErrMsgUnexpected % sys.exec_info()[0] + + return (bOk, strErrMsg) + +def make_symlink_native(vDictArgs, strSrc, strTarget): + eOSType = utilsOsType.determine_os_type() + bDbg = "-d" in vDictArgs + bOk = True + strErrMsg = "" + + target_filename = os.path.basename(strTarget) + if eOSType == utilsOsType.EnumOsType.Unknown: + bOk = False + strErrMsg = strErrMsgOsTypeUnknown + elif eOSType == utilsOsType.EnumOsType.Windows: + if os.path.isfile(strTarget): + if bDbg: + print((strMsgSymlinkExists % target_filename)) + return (bOk, strErrMsg) + if bDbg: + print((strMsgSymlinkMk % (target_filename, strSrc, strTarget))) + bOk, strErrMsg = make_symlink_windows(strSrc, + strTarget) + else: + if os.path.islink(strTarget): + if bDbg: + print((strMsgSymlinkExists % target_filename)) + return (bOk, strErrMsg) + if bDbg: + print((strMsgSymlinkMk % (target_filename, strSrc, strTarget))) + bOk, strErrMsg = make_symlink_other_platforms(strSrc, + strTarget) + + return (bOk, strErrMsg) + +#++--------------------------------------------------------------------------- +# Details: Make the symbolic link. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# vstrSrcFile - (R) Source file name. +# vstrTargetFile - (R) Destination file name. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def make_symlink(vDictArgs, vstrFrameworkPythonDir, vstrSrcFile, vstrTargetFile): + dbg = utilsDebug.CDebugFnVerbose("Python script make_symlink()") + bOk = True + strErrMsg = "" + bDbg = "-d" in vDictArgs + strTarget = os.path.join(vstrFrameworkPythonDir, vstrTargetFile) + strTarget = os.path.normcase(strTarget) + strSrc = "" + + os.chdir(vstrFrameworkPythonDir) + bMakeFileCalled = "-m" in vDictArgs + eOSType = utilsOsType.determine_os_type() + if not bMakeFileCalled: + return (bOk, strErrMsg) + else: + # Resolve vstrSrcFile path relatively the build directory + if eOSType == utilsOsType.EnumOsType.Windows: + # On a Windows platform the vstrFrameworkPythonDir looks like: + # llvm\\build\\Lib\\site-packages\\lldb + strBuildDir = os.path.join("..", "..", "..") + else: + # On a UNIX style platform the vstrFrameworkPythonDir looks like: + # llvm/build/lib/python2.7/site-packages/lldb + strBuildDir = os.path.join("..", "..", "..", "..") + strSrc = os.path.normcase(os.path.join(strBuildDir, vstrSrcFile)) + + return make_symlink_native(vDictArgs, strSrc, strTarget) + + +#++--------------------------------------------------------------------------- +# Details: Make the symbolic that the script bridge for Python will need in +# the Python framework directory. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# vstrLiblldbName - (R) File name for _lldb library. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def make_symlink_liblldb(vDictArgs, vstrFrameworkPythonDir, vstrLiblldbFileName): + dbg = utilsDebug.CDebugFnVerbose("Python script make_symlink_liblldb()") + bOk = True + strErrMsg = "" + strTarget = vstrLiblldbFileName + strSrc = "" + + eOSType = utilsOsType.determine_os_type() + if eOSType == utilsOsType.EnumOsType.Windows: + # When importing an extension module using a debug version of python, you + # write, for example, "import foo", but the interpreter searches for + # "foo_d.pyd" + if is_debug_interpreter(): + strTarget += "_d" + strTarget += ".pyd" + else: + strTarget += ".so" + + bMakeFileCalled = "-m" in vDictArgs + if not bMakeFileCalled: + strSrc = os.path.join("lib", "LLDB") + else: + strLibFileExtn = "" + if eOSType == utilsOsType.EnumOsType.Windows: + strSrc = os.path.join("bin", "liblldb.dll") + else: + if eOSType == utilsOsType.EnumOsType.Darwin: + strLibFileExtn = ".dylib" + else: + strLibFileExtn = ".so" + strSrc = os.path.join("lib", "liblldb" + strLibFileExtn) + + bOk, strErrMsg = make_symlink(vDictArgs, vstrFrameworkPythonDir, strSrc, strTarget) + + return (bOk, strErrMsg) + +#++--------------------------------------------------------------------------- +# Details: Make the symbolic link to the darwin-debug. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# vstrDarwinDebugFileName - (R) File name for darwin-debug. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def make_symlink_darwin_debug(vDictArgs, vstrFrameworkPythonDir, vstrDarwinDebugFileName): + dbg = utilsDebug.CDebugFnVerbose("Python script make_symlink_darwin_debug()") + bOk = True + strErrMsg = "" + strTarget = vstrDarwinDebugFileName + strSrc = "" + + bMakeFileCalled = "-m" in vDictArgs + if not bMakeFileCalled: + return (bOk, strErrMsg) + else: + strSrc = os.path.join("bin", "lldb-launcher") + + bOk, strErrMsg = make_symlink(vDictArgs, vstrFrameworkPythonDir, strSrc, strTarget) + + return (bOk, strErrMsg) + +#++--------------------------------------------------------------------------- +# Details: Make the symbolic link to the lldb-argdumper. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# vstrArgdumperFileName - (R) File name for lldb-argdumper. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def make_symlink_lldb_argdumper(vDictArgs, vstrFrameworkPythonDir, vstrArgdumperFileName): + dbg = utilsDebug.CDebugFnVerbose("Python script make_symlink_lldb_argdumper()") + bOk = True + strErrMsg = "" + strTarget = vstrArgdumperFileName + strSrc = "" + + eOSType = utilsOsType.determine_os_type() + if eOSType == utilsOsType.EnumOsType.Windows: + strTarget += ".exe" + + bMakeFileCalled = "-m" in vDictArgs + if not bMakeFileCalled: + return (bOk, strErrMsg) + else: + strExeFileExtn = "" + if eOSType == utilsOsType.EnumOsType.Windows: + strExeFileExtn = ".exe" + strSrc = os.path.join("bin", "lldb-argdumper" + strExeFileExtn) + + bOk, strErrMsg = make_symlink(vDictArgs, vstrFrameworkPythonDir, strSrc, strTarget) + + return (bOk, strErrMsg) + +#++--------------------------------------------------------------------------- +# Details: Make the symlink that the script bridge for Python will need in +# the Python framework directory. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# Returns: Bool - True = function success, False = failure. +# strErrMsg - Error description on task failure. +# Throws: None. +#-- +def create_symlinks(vDictArgs, vstrFrameworkPythonDir): + dbg = utilsDebug.CDebugFnVerbose("Python script create_symlinks()") + bOk = True + strErrMsg = "" + eOSType = utilsOsType.determine_os_type() + + # Make symlink for _lldb + strLibLldbFileName = "_lldb" + if bOk: + bOk, strErrMsg = make_symlink_liblldb(vDictArgs, + vstrFrameworkPythonDir, + strLibLldbFileName) + + # Make symlink for darwin-debug on Darwin + strDarwinDebugFileName = "darwin-debug" + if bOk and eOSType == utilsOsType.EnumOsType.Darwin: + bOk, strErrMsg = make_symlink_darwin_debug(vDictArgs, + vstrFrameworkPythonDir, + strDarwinDebugFileName) + + # Make symlink for lldb-argdumper + strArgdumperFileName = "lldb-argdumper" + if bOk: + bOk, strErrMsg = make_symlink_lldb_argdumper(vDictArgs, + vstrFrameworkPythonDir, + strArgdumperFileName) + + return (bOk, strErrMsg) + +def copy_six(vDictArgs, vstrFrameworkPythonDir): + dbg = utilsDebug.CDebugFnVerbose("Python script copy_six()") + bDbg = "-d" in vDictArgs + bOk = True + strMsg = "" + site_packages_dir = os.path.dirname(vstrFrameworkPythonDir) + six_module_filename = "six.py" + src_file = os.path.join(vDictArgs['--srcRoot'], "third_party", "Python", "module", "six", six_module_filename) + src_file = os.path.normpath(src_file) + target = os.path.join(site_packages_dir, six_module_filename) + + if bDbg: + print((strMsgCopySixPy % (src_file, target))) + try: + shutil.copyfile(src_file, target) + except: + bOk = False + strMsg = strErrMsgCopySixPyFailed % (src_file, target) + + return (bOk, strMsg) + +#++--------------------------------------------------------------------------- +# Details: Look for the directory in which to put the Python files if it +# does not already exist, attempt to make it. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# Returns: Bool - True = function success, False = failure. +# Str - Error description on task failure. +# Throws: None. +#-- +def find_or_create_python_dir(vDictArgs, vstrFrameworkPythonDir): + dbg = utilsDebug.CDebugFnVerbose("Python script find_or_create_python_dir()") + bOk = True + strMsg = "" + bDbg = "-d" in vDictArgs + + if os.path.isdir(vstrFrameworkPythonDir): + if bDbg: + print((strMsgFrameWkPyExists % vstrFrameworkPythonDir)) + return (bOk, strMsg) + + if bDbg: + print((strMsgFrameWkPyMkDir % vstrFrameworkPythonDir)) + + try: + os.makedirs(vstrFrameworkPythonDir) + except OSError as exception: + bOk = False + strMsg = strErrMsgCreateFrmWkPyDirFailed % (vstrFrameworkPythonDir, + os.strerror(exception.errno)) + + return (bOk, strMsg) + +#++--------------------------------------------------------------------------- +# Details: Retrieve the configuration build path if present and valid (using +# parameter --cfgBlddir or copy the Python Framework directory. +# Args: vDictArgs - (R) Program input parameters. +# vstrFrameworkPythonDir - (R) Python framework directory. +# Returns: Bool - True = function success, False = failure. +# Str - Config directory path. +# strErrMsg - Error description on task failure. +# Throws: None. +#-- +def get_config_build_dir(vDictArgs, vstrFrameworkPythonDir): + dbg = utilsDebug.CDebugFnVerbose("Python script get_config_build_dir()") + bOk = True + strErrMsg = "" + + strConfigBldDir = "" + bHaveConfigBldDir = "--cfgBldDir" in vDictArgs + if bHaveConfigBldDir: + strConfigBldDir = vDictArgs["--cfgBldDir"] + if (bHaveConfigBldDir == False) or (strConfigBldDir.__len__() == 0): + strConfigBldDir = vstrFrameworkPythonDir + + return (bOk, strConfigBldDir, strErrMsg) + +#++--------------------------------------------------------------------------- +# Details: Determine where to put the files. Retrieve the directory path for +# Python's dist_packages/ site_package folder on a Windows platform. +# Args: vDictArgs - (R) Program input parameters. +# Returns: Bool - True = function success, False = failure. +# Str - Python Framework directory path. +# strErrMsg - Error description on task failure. +# Throws: None. +#-- +def get_framework_python_dir_windows(vDictArgs): + dbg = utilsDebug.CDebugFnVerbose("Python script get_framework_python_dir_windows()") + bOk = True + strWkDir = "" + strErrMsg = "" + + # We are being built by LLVM, so use the PYTHON_INSTALL_DIR argument, + # and append the python version directory to the end of it. Depending + # on the system other stuff may need to be put here as well. + from distutils.sysconfig import get_python_lib + strPythonInstallDir = "" + bHaveArgPrefix = "--prefix" in vDictArgs + if bHaveArgPrefix: + strPythonInstallDir = os.path.normpath(vDictArgs["--prefix"]) + + bHaveArgCmakeBuildConfiguration = "--cmakeBuildConfiguration" in vDictArgs + if bHaveArgCmakeBuildConfiguration: + strPythonInstallDir = os.path.join(strPythonInstallDir, vDictArgs["--cmakeBuildConfiguration"]) + + if strPythonInstallDir.__len__() != 0: + strWkDir = get_python_lib(True, False, strPythonInstallDir) + else: + strWkDir = get_python_lib(True, False) + strWkDir = os.path.normcase(os.path.join(strWkDir, "lldb")) + + return (bOk, strWkDir, strErrMsg) + +#++--------------------------------------------------------------------------- +# Details: Retrieve the directory path for Python's dist_packages/ +# site_package folder on a UNIX style platform. +# Args: vDictArgs - (R) Program input parameters. +# Returns: Bool - True = function success, False = failure. +# Str - Python Framework directory path. +# strErrMsg - Error description on task failure. +# Throws: None. +#-- +def get_framework_python_dir_other_platforms(vDictArgs): + dbg = utilsDebug.CDebugFnVerbose("Python script get_framework_python_dir_other_platform()") + bOk = True + strWkDir = "" + strErrMsg = "" + bDbg = "-d" in vDictArgs + + bMakeFileCalled = "-m" in vDictArgs + if bMakeFileCalled: + dbg.dump_text("Built by LLVM") + return get_framework_python_dir_windows(vDictArgs) + else: + dbg.dump_text("Built by XCode") + # We are being built by XCode, so all the lldb Python files can go + # into the LLDB.framework/Resources/Python subdirectory. + strWkDir = vDictArgs["--targetDir"] + strWkDir += os.path.join(strWkDir, "LLDB.framework") + if os.path.exists(strWkDir): + if bDbg: + print((strMsgFoundLldbFrameWkDir % strWkDir)) + strWkDir = os.path.join(strWkDir, "Resources", "Python", "lldb") + strWkDir = os.path.normcase(strWkDir) + else: + bOk = False + strErrMsg = strErrMsgFrameWkPyDirNotExist % strWkDir + + return (bOk, strWkDir, strErrMsg) + +#++--------------------------------------------------------------------------- +# Details: Retrieve the directory path for Python's dist_packages/ +# site_package folder depending on the type of OS platform being +# used. +# Args: vDictArgs - (R) Program input parameters. +# Returns: Bool - True = function success, False = failure. +# Str - Python Framework directory path. +# strErrMsg - Error description on task failure. +# Throws: None. +#-- +def get_framework_python_dir(vDictArgs): + dbg = utilsDebug.CDebugFnVerbose("Python script get_framework_python_dir()") + bOk = True + strWkDir = "" + strErrMsg = "" + + eOSType = utilsOsType.determine_os_type() + if eOSType == utilsOsType.EnumOsType.Unknown: + bOk = False + strErrMsg = strErrMsgOsTypeUnknown + elif eOSType == utilsOsType.EnumOsType.Windows: + bOk, strWkDir, strErrMsg = get_framework_python_dir_windows(vDictArgs) + else: + bOk, strWkDir, strErrMsg = get_framework_python_dir_other_platforms(vDictArgs) + + return (bOk, strWkDir, strErrMsg) + +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- + +""" Details: Program main entry point fn. Called by another Python script. + + -------------------------------------------------------------------------- + Details: This script is to be called by another Python script. It is not + intended to be called directly i.e from the command line. + Args: vDictArgs - (R) Map of parameter names to values. + -d (optional) Determines whether or not this script + outputs additional information when running. + -m (optional) Specify called from Makefile system. If given locate + the LLDBWrapPython.cpp in --srcRoot/source folder + else in the --targetDir folder. + --srcRoot The root of the lldb source tree. + --targetDir Where the lldb framework/shared library gets put. + --cfgBlddir Where the buildSwigPythonLLDB.py program will + (optional) put the lldb.py file it generated from running + SWIG. + --prefix Is the root directory used to determine where + (optional) third-party modules for scripting languages should + be installed. Where non-Darwin systems want to put + the .py and .so files so that Python can find them + automatically. Python install directory. + Results: 0 Success + -100+ Error from this script to the caller script. + -100 Error program failure with optional message. + + -------------------------------------------------------------------------- + +""" +def main(vDictArgs): + dbg = utilsDebug.CDebugFnVerbose("Python script main()") + bOk = True + strMsg = "" + strErrMsgProgFail = "" + + bDbg = "-d" in vDictArgs + + eOSType = utilsOsType.determine_os_type() + if bDbg: + pyVersion = sys.version_info + print((strMsgOsVersion % utilsOsType.EnumOsType.name_of(eOSType))) + print((strMsgPyVersion % (pyVersion[0], pyVersion[1]))) + + bOk, strFrameworkPythonDir, strMsg = get_framework_python_dir(vDictArgs) + + if bOk: + bOk, strCfgBldDir, strMsg = get_config_build_dir(vDictArgs, strFrameworkPythonDir) + if bOk and bDbg: + print((strMsgPyFileLocatedHere % strFrameworkPythonDir)) + print((strMsgConfigBuildDir % strCfgBldDir)) + + if bOk: + bOk, strMsg = find_or_create_python_dir(vDictArgs, strFrameworkPythonDir) + + if bOk: + bOk, strMsg = create_symlinks(vDictArgs, strFrameworkPythonDir) + + if bOk: + bOk, strMsg = copy_six(vDictArgs, strFrameworkPythonDir) + + if bOk: + bOk, strMsg = copy_lldbpy_file_to_lldb_pkg_dir(vDictArgs, + strFrameworkPythonDir, + strCfgBldDir) + strRoot = os.path.normpath(vDictArgs["--srcRoot"]) + if bOk: + # lldb + listPkgFiles = [os.path.join(strRoot, "source", "Interpreter", "embedded_interpreter.py")] + bOk, strMsg = create_py_pkg(vDictArgs, strFrameworkPythonDir, "", listPkgFiles) + + if bOk: + # lldb/formatters/cpp + listPkgFiles = [os.path.join(strRoot, "examples", "synthetic", "gnu_libstdcpp.py"), + os.path.join(strRoot, "examples", "synthetic", "libcxx.py")] + bOk, strMsg = create_py_pkg(vDictArgs, strFrameworkPythonDir, "/formatters/cpp", listPkgFiles) + + if bOk: + # Make an empty __init__.py in lldb/runtime as this is required for + # Python to recognize lldb.runtime as a valid package (and hence, + # lldb.runtime.objc as a valid contained package) + listPkgFiles = [] + bOk, strMsg = create_py_pkg(vDictArgs, strFrameworkPythonDir, "/runtime", listPkgFiles) + + if bOk: + # lldb/formatters + # Having these files copied here ensure that lldb/formatters is a + # valid package itself + listPkgFiles = [os.path.join(strRoot, "examples", "summaries", "cocoa", "cache.py"), + os.path.join(strRoot, "examples", "summaries", "cocoa", "metrics.py"), + os.path.join(strRoot, "examples", "summaries", "cocoa", "attrib_fromdict.py"), + os.path.join(strRoot, "examples", "summaries", "cocoa", "Logger.py")] + bOk, strMsg = create_py_pkg(vDictArgs, strFrameworkPythonDir, "/formatters", listPkgFiles) + + if bOk: + # lldb/utils + listPkgFiles = [os.path.join(strRoot, "examples", "python", "symbolication.py")] + bOk, strMsg = create_py_pkg(vDictArgs, strFrameworkPythonDir, "/utils", listPkgFiles) + + if bOk and (eOSType == utilsOsType.EnumOsType.Darwin): + # lldb/macosx + listPkgFiles = [os.path.join(strRoot, "examples", "python", "crashlog.py"), + os.path.join(strRoot, "examples", "darwin", "heap_find", "heap.py")] + bOk, strMsg = create_py_pkg(vDictArgs, strFrameworkPythonDir, "/macosx", listPkgFiles) + + if bOk and (eOSType == utilsOsType.EnumOsType.Darwin): + # lldb/diagnose + listPkgFiles = [os.path.join(strRoot, "examples", "python", "diagnose_unwind.py"), + os.path.join(strRoot, "examples", "python", "diagnose_nsstring.py")] + bOk, strMsg = create_py_pkg(vDictArgs, strFrameworkPythonDir, "/diagnose", listPkgFiles) + + if bOk: + bOk, strMsg = macosx_copy_file_for_heap(vDictArgs, strFrameworkPythonDir) + + if bOk: + return (0, strMsg) + else: + strErrMsgProgFail += strMsg + return (-100, strErrMsgProgFail) + + +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- + +# This script can be called by another Python script by calling the main() +# function directly +if __name__ == "__main__": + print("Script cannot be called directly, called by finishSwigWrapperClasses.py") + diff --git a/scripts/Python/modify-python-lldb.py b/scripts/Python/modify-python-lldb.py new file mode 100644 index 000000000000..56323d6679a1 --- /dev/null +++ b/scripts/Python/modify-python-lldb.py @@ -0,0 +1,486 @@ +# +# modify-python-lldb.py +# +# This script modifies the lldb module (which was automatically generated via +# running swig) to support iteration and/or equality operations for certain lldb +# objects, implements truth value testing for certain lldb objects, and adds a +# global variable 'debugger_unique_id' which is initialized to 0. +# +# As a cleanup step, it also removes the 'residues' from the autodoc features of +# swig. For an example, take a look at SBTarget.h header file, where we take +# advantage of the already existing doxygen C++-docblock and make it the Python +# docstring for the same method. The 'residues' in this context include the +# '#endif', the '#ifdef SWIG', the c comment marker, the trailing blank (SPC's) +# line, and the doxygen comment start marker. +# +# In addition to the 'residues' removal during the cleanup step, it also +# transforms the 'char' data type (which was actually 'char *' but the 'autodoc' +# feature of swig removes ' *' from it) into 'str' (as a Python str type). +# +# It also calls SBDebugger.Initialize() to initialize the lldb debugger +# subsystem. +# + +# System modules +import sys, re +if sys.version_info.major >= 3: + import io as StringIO +else: + import StringIO + +# import use_lldb_suite so we can find third-party and helper modules +import use_lldb_suite + +# Third party modules +import six + +# LLDB modules + +if len(sys.argv) != 2: + output_name = "./lldb.py" +else: + output_name = sys.argv[1] + "/lldb.py" + +# print "output_name is '" + output_name + "'" + +# +# Version string +# +version_line = "swig_version = %s" + +# +# Residues to be removed. +# +c_endif_swig = "#endif" +c_ifdef_swig = "#ifdef SWIG" +c_comment_marker = "//------------" +# The pattern for recognizing the doxygen comment block line. +doxygen_comment_start = re.compile("^\s*(/// ?)") +# The demarcation point for turning on/off residue removal state. +# When bracketed by the lines, the CLEANUP_DOCSTRING state (see below) is ON. +toggle_docstring_cleanup_line = ' """' + +def char_to_str_xform(line): + """This transforms the 'char', i.e, 'char *' to 'str', Python string.""" + line = line.replace(' char', ' str') + line = line.replace('char ', 'str ') + # Special case handling of 'char **argv' and 'char **envp'. + line = line.replace('str argv', 'list argv') + line = line.replace('str envp', 'list envp') + return line + +# +# The one-liner docstring also needs char_to_str transformation, btw. +# +TWO_SPACES = ' ' * 2 +EIGHT_SPACES = ' ' * 8 +one_liner_docstring_pattern = re.compile('^(%s|%s)""".*"""$' % (TWO_SPACES, EIGHT_SPACES)) + +# +# lldb_helpers and lldb_iter() should appear before our first SB* class definition. +# +lldb_helpers = ''' +# ================================== +# Helper function for SBModule class +# ================================== +def in_range(symbol, section): + """Test whether a symbol is within the range of a section.""" + symSA = symbol.GetStartAddress().GetFileAddress() + symEA = symbol.GetEndAddress().GetFileAddress() + secSA = section.GetFileAddress() + secEA = secSA + section.GetByteSize() + + if symEA != LLDB_INVALID_ADDRESS: + if secSA <= symSA and symEA <= secEA: + return True + else: + return False + else: + if secSA <= symSA and symSA < secEA: + return True + else: + return False +''' + +lldb_iter_def = ''' +# =================================== +# Iterator for lldb container objects +# =================================== +def lldb_iter(obj, getsize, getelem): + """A generator adaptor to support iteration for lldb container objects.""" + size = getattr(obj, getsize) + elem = getattr(obj, getelem) + for i in range(size()): + yield elem(i) + +# ============================================================================== +# The modify-python-lldb.py script is responsible for post-processing this SWIG- +# generated lldb.py module. It is responsible for adding the above lldb_iter() +# function definition as well as the supports, in the following, for iteration +# protocol: __iter__, rich comparison methods: __eq__ and __ne__, truth value +# testing (and built-in operation bool()): __nonzero__, and built-in function +# len(): __len__. +# ============================================================================== +''' + +# +# linked_list_iter() is a special purpose iterator to treat the SBValue as the +# head of a list data structure, where you specify the child member name which +# points to the next item on the list and you specify the end-of-list function +# which takes an SBValue and returns True if EOL is reached and False if not. +# +linked_list_iter_def = ''' + def __eol_test__(val): + """Default function for end of list test takes an SBValue object. + + Return True if val is invalid or it corresponds to a null pointer. + Otherwise, return False. + """ + if not val or val.GetValueAsUnsigned() == 0: + return True + else: + return False + + # ================================================== + # Iterator for lldb.SBValue treated as a linked list + # ================================================== + def linked_list_iter(self, next_item_name, end_of_list_test=__eol_test__): + """Generator adaptor to support iteration for SBValue as a linked list. + + linked_list_iter() is a special purpose iterator to treat the SBValue as + the head of a list data structure, where you specify the child member + name which points to the next item on the list and you specify the + end-of-list test function which takes an SBValue for an item and returns + True if EOL is reached and False if not. + + linked_list_iter() also detects infinite loop and bails out early. + + The end_of_list_test arg, if omitted, defaults to the __eol_test__ + function above. + + For example, + + # Get Frame #0. + ... + + # Get variable 'task_head'. + task_head = frame0.FindVariable('task_head') + ... + + for t in task_head.linked_list_iter('next'): + print t + """ + if end_of_list_test(self): + return + item = self + visited = set() + try: + while not end_of_list_test(item) and not item.GetValueAsUnsigned() in visited: + visited.add(item.GetValueAsUnsigned()) + yield item + # Prepare for the next iteration. + item = item.GetChildMemberWithName(next_item_name) + except: + # Exception occurred. Stop the generator. + pass + + return +''' + +# This supports the iteration protocol. +iter_def = " def __iter__(self): return lldb_iter(self, '%s', '%s')" +module_iter = " def module_iter(self): return lldb_iter(self, '%s', '%s')" +breakpoint_iter = " def breakpoint_iter(self): return lldb_iter(self, '%s', '%s')" +watchpoint_iter = " def watchpoint_iter(self): return lldb_iter(self, '%s', '%s')" +section_iter = " def section_iter(self): return lldb_iter(self, '%s', '%s')" +compile_unit_iter = " def compile_unit_iter(self): return lldb_iter(self, '%s', '%s')" + +# Called to implement the built-in function len(). +# Eligible objects are those containers with unambiguous iteration support. +len_def = " def __len__(self): return self.%s()" + +# This supports the rich comparison methods of __eq__ and __ne__. +eq_def = " def __eq__(self, other): return isinstance(other, %s) and %s" +ne_def = " def __ne__(self, other): return not self.__eq__(other)" + +# Called to implement truth value testing and the built-in operation bool(); +# Note that Python 2 uses __nonzero__(), whereas Python 3 uses __bool__() +# should return False or True, or their integer equivalents 0 or 1. +# Delegate to self.IsValid() if it is defined for the current lldb object. + +if six.PY2: + nonzero_def = " def __nonzero__(self): return self.IsValid()" +else: + nonzero_def = " def __bool__(self): return self.IsValid()" + +# A convenience iterator for SBSymbol! +symbol_in_section_iter_def = ''' + def symbol_in_section_iter(self, section): + """Given a module and its contained section, returns an iterator on the + symbols within the section.""" + for sym in self: + if in_range(sym, section): + yield sym +''' + +# +# This dictionary defines a mapping from classname to (getsize, getelem) tuple. +# +d = { 'SBBreakpoint': ('GetNumLocations', 'GetLocationAtIndex'), + 'SBCompileUnit': ('GetNumLineEntries', 'GetLineEntryAtIndex'), + 'SBDebugger': ('GetNumTargets', 'GetTargetAtIndex'), + 'SBModule': ('GetNumSymbols', 'GetSymbolAtIndex'), + 'SBProcess': ('GetNumThreads', 'GetThreadAtIndex'), + 'SBSection': ('GetNumSubSections', 'GetSubSectionAtIndex'), + 'SBThread': ('GetNumFrames', 'GetFrameAtIndex'), + + 'SBInstructionList': ('GetSize', 'GetInstructionAtIndex'), + 'SBStringList': ('GetSize', 'GetStringAtIndex',), + 'SBSymbolContextList': ('GetSize', 'GetContextAtIndex'), + 'SBTypeList': ('GetSize', 'GetTypeAtIndex'), + 'SBValueList': ('GetSize', 'GetValueAtIndex'), + + 'SBType': ('GetNumberChildren', 'GetChildAtIndex'), + 'SBValue': ('GetNumChildren', 'GetChildAtIndex'), + + # SBTarget needs special processing, see below. + 'SBTarget': {'module': ('GetNumModules', 'GetModuleAtIndex'), + 'breakpoint': ('GetNumBreakpoints', 'GetBreakpointAtIndex'), + 'watchpoint': ('GetNumWatchpoints', 'GetWatchpointAtIndex') + }, + + # SBModule has an additional section_iter(), see below. + 'SBModule-section': ('GetNumSections', 'GetSectionAtIndex'), + # And compile_unit_iter(). + 'SBModule-compile-unit': ('GetNumCompileUnits', 'GetCompileUnitAtIndex'), + # As well as symbol_in_section_iter(). + 'SBModule-symbol-in-section': symbol_in_section_iter_def + } + +# +# This dictionary defines a mapping from classname to equality method name(s). +# +e = { 'SBAddress': ['GetFileAddress', 'GetModule'], + 'SBBreakpoint': ['GetID'], + 'SBWatchpoint': ['GetID'], + 'SBFileSpec': ['GetFilename', 'GetDirectory'], + 'SBModule': ['GetFileSpec', 'GetUUIDString'], + 'SBType': ['GetByteSize', 'GetName'] + } + +def list_to_frag(list): + """Transform a list to equality program fragment. + + For example, ['GetID'] is transformed to 'self.GetID() == other.GetID()', + and ['GetFilename', 'GetDirectory'] to 'self.GetFilename() == other.GetFilename() + and self.GetDirectory() == other.GetDirectory()'. + """ + if not list: + raise Exception("list should be non-empty") + frag = StringIO.StringIO() + for i in range(len(list)): + if i > 0: + frag.write(" and ") + frag.write("self.{0}() == other.{0}()".format(list[i])) + return frag.getvalue() + +class NewContent(StringIO.StringIO): + """Simple facade to keep track of the previous line to be committed.""" + def __init__(self): + StringIO.StringIO.__init__(self) + self.prev_line = None + def add_line(self, a_line): + """Add a line to the content, if there is a previous line, commit it.""" + if self.prev_line != None: + self.write(self.prev_line + "\n") + self.prev_line = a_line + def del_line(self): + """Forget about the previous line, do not commit it.""" + self.prev_line = None + def del_blank_line(self): + """Forget about the previous line if it is a blank line.""" + if self.prev_line != None and not self.prev_line.strip(): + self.prev_line = None + def finish(self): + """Call this when you're finished with populating content.""" + if self.prev_line != None: + self.write(self.prev_line + "\n") + self.prev_line = None + +# The new content will have the iteration protocol defined for our lldb objects. +new_content = NewContent() + +with open(output_name, 'r') as f_in: + content = f_in.read() + +# The pattern for recognizing the SWIG Version string +version_pattern = re.compile("^# Version:? (.*)$") + +# The pattern for recognizing the beginning of an SB class definition. +class_pattern = re.compile("^class (SB.*)\(_object\):$") + +# The pattern for recognizing the beginning of the __init__ method definition. +init_pattern = re.compile("^ def __init__\(self.*\):") + +# The pattern for recognizing the beginning of the IsValid method definition. +isvalid_pattern = re.compile("^ def IsValid\(") + +# These define the states of our finite state machine. +EXPECTING_VERSION = 0 +NORMAL = 1 +DEFINING_ITERATOR = 2 +DEFINING_EQUALITY = 4 +CLEANUP_DOCSTRING = 8 + +# The lldb_iter_def only needs to be inserted once. +lldb_iter_defined = False; + +# Our FSM begins its life in the NORMAL state, and transitions to the +# DEFINING_ITERATOR and/or DEFINING_EQUALITY state whenever it encounters the +# beginning of certain class definitions, see dictionaries 'd' and 'e' above. +# +# Note that the two states DEFINING_ITERATOR and DEFINING_EQUALITY are +# orthogonal in that our FSM can be in one, the other, or both states at the +# same time. During such time, the FSM is eagerly searching for the __init__ +# method definition in order to insert the appropriate method(s) into the lldb +# module. +# +# The state CLEANUP_DOCSTRING can be entered from either the NORMAL or the +# DEFINING_ITERATOR/EQUALITY states. While in this state, the FSM is fixing/ +# cleaning the Python docstrings generated by the swig docstring features. +# +# The FSM, in all possible states, also checks the current input for IsValid() +# definition, and inserts a __nonzero__() method definition to implement truth +# value testing and the built-in operation bool(). +state = EXPECTING_VERSION + +swig_version_tuple = None +for line in content.splitlines(): + # Handle the state transition into CLEANUP_DOCSTRING state as it is possible + # to enter this state from either NORMAL or DEFINING_ITERATOR/EQUALITY. + # + # If ' """' is the sole line, prepare to transition to the + # CLEANUP_DOCSTRING state or out of it. + + if line == toggle_docstring_cleanup_line: + if state & CLEANUP_DOCSTRING: + # Special handling of the trailing blank line right before the '"""' + # end docstring marker. + new_content.del_blank_line() + state ^= CLEANUP_DOCSTRING + else: + state |= CLEANUP_DOCSTRING + + if state == EXPECTING_VERSION: + # We haven't read the version yet, read it now. + if swig_version_tuple is None: + match = version_pattern.search(line) + if match: + v = match.group(1) + swig_version_tuple = tuple(map(int, (v.split(".")))) + elif not line.startswith('#'): + # This is the first non-comment line after the header. Inject the version + new_line = version_line % str(swig_version_tuple) + new_content.add_line(new_line) + state = NORMAL + + if state == NORMAL: + match = class_pattern.search(line) + # Inserts lldb_helpers and the lldb_iter() definition before the first + # class definition. + if not lldb_iter_defined and match: + new_content.add_line(lldb_helpers) + new_content.add_line(lldb_iter_def) + lldb_iter_defined = True + + # If we are at the beginning of the class definitions, prepare to + # transition to the DEFINING_ITERATOR/DEFINING_EQUALITY state for the + # right class names. + if match: + cls = match.group(1) + if cls in d: + # Adding support for iteration for the matched SB class. + state |= DEFINING_ITERATOR + if cls in e: + # Adding support for eq and ne for the matched SB class. + state |= DEFINING_EQUALITY + + if (state & DEFINING_ITERATOR) or (state & DEFINING_EQUALITY): + match = init_pattern.search(line) + if match: + # We found the beginning of the __init__ method definition. + # This is a good spot to insert the iter and/or eq-ne support. + # + # But note that SBTarget has three types of iterations. + if cls == "SBTarget": + new_content.add_line(module_iter % (d[cls]['module'])) + new_content.add_line(breakpoint_iter % (d[cls]['breakpoint'])) + new_content.add_line(watchpoint_iter % (d[cls]['watchpoint'])) + else: + if (state & DEFINING_ITERATOR): + new_content.add_line(iter_def % d[cls]) + new_content.add_line(len_def % d[cls][0]) + if (state & DEFINING_EQUALITY): + new_content.add_line(eq_def % (cls, list_to_frag(e[cls]))) + new_content.add_line(ne_def) + + # SBModule has extra SBSection, SBCompileUnit iterators and symbol_in_section_iter()! + if cls == "SBModule": + new_content.add_line(section_iter % d[cls+'-section']) + new_content.add_line(compile_unit_iter % d[cls+'-compile-unit']) + new_content.add_line(d[cls+'-symbol-in-section']) + + # This special purpose iterator is for SBValue only!!! + if cls == "SBValue": + new_content.add_line(linked_list_iter_def) + + # Next state will be NORMAL. + state = NORMAL + + if (state & CLEANUP_DOCSTRING): + # Cleanse the lldb.py of the autodoc'ed residues. + if c_ifdef_swig in line or c_endif_swig in line: + continue + # As well as the comment marker line. + if c_comment_marker in line: + continue + + # Also remove the '\a ' and '\b 'substrings. + line = line.replace('\a ', '') + line = line.replace('\b ', '') + # And the leading '///' substring. + doxygen_comment_match = doxygen_comment_start.match(line) + if doxygen_comment_match: + line = line.replace(doxygen_comment_match.group(1), '', 1) + + line = char_to_str_xform(line) + + # Note that the transition out of CLEANUP_DOCSTRING is handled at the + # beginning of this function already. + + # This deals with one-liner docstring, for example, SBThread.GetName: + # """GetName(self) -> char""". + if one_liner_docstring_pattern.match(line): + line = char_to_str_xform(line) + + # Look for 'def IsValid(*args):', and once located, add implementation + # of truth value testing for this object by delegation. + if isvalid_pattern.search(line): + new_content.add_line(nonzero_def) + + # Pass the original line of content to new_content. + new_content.add_line(line) + +# We are finished with recording new content. +new_content.finish() + +with open(output_name, 'w') as f_out: + f_out.write(new_content.getvalue()) + f_out.write('''debugger_unique_id = 0 +SBDebugger.Initialize() +debugger = None +target = SBTarget() +process = SBProcess() +thread = SBThread() +frame = SBFrame()''') + diff --git a/scripts/Python/modules/CMakeLists.txt b/scripts/Python/modules/CMakeLists.txt new file mode 100644 index 000000000000..396d447ff267 --- /dev/null +++ b/scripts/Python/modules/CMakeLists.txt @@ -0,0 +1,11 @@ +# Disable some warnings triggered by Python's headers. +check_cxx_compiler_flag("-Wno-macro-redefined" + CXX_SUPPORTS_NO_MACRO_REDEFINED) +if (CXX_SUPPORTS_NO_MACRO_REDEFINED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-macro-redefined") +endif () + +# build the Python readline suppression module only on Linux +if (CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT __ANDROID_NDK__) + add_subdirectory(readline) +endif() diff --git a/scripts/Python/modules/Makefile b/scripts/Python/modules/Makefile new file mode 100644 index 000000000000..b6989889858d --- /dev/null +++ b/scripts/Python/modules/Makefile @@ -0,0 +1,20 @@ +##===- scripts/Python/modules/Makefile ---------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../.. +include $(LLDB_LEVEL)/../../Makefile.config + +DIRS:= + +# only build the readline suppression module on Linux, Kfreebsd & Hurd +ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux GNU GNU/kFreeBSD)) +DIRS += readline +endif + +include $(LLDB_LEVEL)/Makefile diff --git a/scripts/Python/modules/readline/CMakeLists.txt b/scripts/Python/modules/readline/CMakeLists.txt new file mode 100644 index 000000000000..0a4376c1c324 --- /dev/null +++ b/scripts/Python/modules/readline/CMakeLists.txt @@ -0,0 +1,25 @@ +# FIXME: if a non-standard version of python is requested, the cmake macro +# below will need Python_ADDITIONAL_VERSIONS set in order to find it. +include(FindPythonInterp) +SET(PYTHON_DIRECTORY python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages) + +# Build the readline python module +include_directories(${PYTHON_INCLUDE_DIR}) +add_library(readline SHARED readline.cpp) + +if (NOT LLDB_DISABLE_LIBEDIT) + target_link_libraries(readline ${PYTHON_LIBRARY} edit) +else() + target_link_libraries(readline ${PYTHON_LIBRARY}) +endif() + +# FIXME: the LIBRARY_OUTPUT_PATH seems to be ignored - this is not a +# functional issue for the build dir, though, since the shared lib dir +# for the build is in the python shared library load path, and thus +# python finds it when loading the python readline module. +set_target_properties(readline PROPERTIES + PREFIX "" + LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/${PYTHON_DIRECTORY}) + +# Install the readline module. +install(TARGETS readline LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}/${PYTHON_DIRECTORY}) diff --git a/scripts/Python/modules/readline/Makefile b/scripts/Python/modules/readline/Makefile new file mode 100644 index 000000000000..dc0d757bc175 --- /dev/null +++ b/scripts/Python/modules/readline/Makefile @@ -0,0 +1,100 @@ +##===- scripts/Python/modules/readline/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +# Skip this entire Makefile if python is disabled. +ifeq (,$(findstring -DLLDB_DISABLE_PYTHON,$(CXXFLAGS))) + +LEVEL := ../../../../../.. +LLDB_LEVEL := ../../../.. + +LIBRARYNAME = readline + +NO_BUILD_ARCHIVE = 1 +LINK_LIBS_IN_SHARED = 1 +SHARED_LIBRARY = 1 +LOADABLE_MODULE = 1 + +PYTHON_CONFIG?= python-config +PYTHON_INC_DIR = $(shell $(PYTHON_CONFIG) --includes) + +# Include all archives in the shared lib +USEDLIBS := + +include $(LLDB_LEVEL)/../../Makefile.config + +LINK_COMPONENTS := + +include $(LEVEL)/Makefile.common + +# include python headers +CPP.Flags += $(PYTHON_INC_DIR) + +ifeq ($(HOST_OS),Darwin) + LLVMLibsOptions += -Wl,-all_load + # set dylib internal version number to llvmCore submission number + ifdef LLDB_SUBMIT_VERSION + LLVMLibsOptions += -Wl,-current_version \ + -Wl,$(LLDB_SUBMIT_VERSION).$(LLDB_SUBMIT_SUBVERSION) \ + -Wl,-compatibility_version -Wl,1 + endif + # extra options to override libtool defaults + LVMLibsOptions += -F/System/Library/Frameworks -F/System/Library/PrivateFrameworks + LLVMLibsOptions += -framework Foundation -framework CoreFoundation + LLVMLibsOptions += -framework CoreServices -framework Carbon -framework Security + LLVMLibsOptions += -framework DebugSymbols $(PYTHON_BUILD_FLAGS) -lobjc + # Mac OS X 10.4 and earlier tools do not allow a second -install_name on command line + DARWIN_VERS := $(shell echo $(TARGET_TRIPLE) | sed 's/.*darwin\([0-9]*\).*/\1/') + ifneq ($(DARWIN_VERS),8) + LLVMLibsOptions += -Wl,-install_name \ + -Wl,"@executable_path/../lib/$(LIBRARYNAME)$(SHLIBEXT)" + endif +endif + +ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux GNU GNU/kFreeBSD)) + # Include everything from the .a's into the shared library. + ProjLibsOptions := -Wl,--whole-archive $(ProjLibsOptions) \ + -Wl,--no-whole-archive + # Link in libedit + # LLVMLibsOptions += -ledit + LLVMLibsOptions += -Wl,--soname,$(LIBRARYNAME)$(SHLIBEXT) +endif + +ifeq ($(HOST_OS),FreeBSD) + # Include everything from the .a's into the shared library. + ProjLibsOptions := -Wl,--whole-archive $(ProjLibsOptions) \ + -Wl,--no-whole-archive + # Allow unresolved symbols. + LLVMLibsOptions += -Wl,--allow-shlib-undefined + # Link in libedit + # LLVMLibsOptions += -L/usr/local/lib -ledit +endif + +# FIXME: dynamically construct the version from `python -V` +PYTHON_VERSION:=2.7 +LLDB_PYTHON_MODULE_REL_DIR:=python$(PYTHON_VERSION)/site-packages +LLDB_PYTHON_MODULE_DIR:=$(LibDir)/$(LLDB_PYTHON_MODULE_REL_DIR) + +# Target to move readline module from shared lib build location to +# local python module directory. +all-local:: $(LLDB_PYTHON_MODULE_DIR)/$(LIBRARYNAME)$(SHLIBEXT) + +$(LLDB_PYTHON_MODULE_DIR)/$(LIBRARYNAME)$(SHLIBEXT): $(SharedLibDir)/$(LIBRARYNAME)$(SHLIBEXT) + $(Echo) Staging $(BuildMode) $(LIBRARYNAME)$(SHLIBEXT) to $(LLDB_PYTHON_MODULE_DIR) + $(Verb) $(MKDIR) "$(LLDB_PYTHON_MODULE_DIR)" + $(Verb) $(ProgInstall) $(SharedLibDir)/$(LIBRARYNAME)$(SHLIBEXT) $(LLDB_PYTHON_MODULE_DIR) + +# Target to move the shared library from the build python lib dir to +# the install python lib dir. +install-local:: $(LLDB_PYTHON_MODULE_DIR)/$(LIBRARYNAME)$(SHLIBEXT) + $(Echo) Installing $(BuildMode) $(LLDB_PYTHON_MODULE_DIR)/$(LIBRARYNAME)$(SHLIBEXT) to $(DESTDIR)$(prefix)/lib/$(LLDB_PYTHON_MODULE_REL_DIR) + $(Verb) $(MKDIR) "$(DESTDIR)$(prefix)/lib/$(LLDB_PYTHON_MODULE_REL_DIR)" + $(Verb) $(ProgInstall) "$(LLDB_PYTHON_MODULE_DIR)/$(LIBRARYNAME)$(SHLIBEXT)" "$(DESTDIR)$(prefix)/lib/$(LLDB_PYTHON_MODULE_REL_DIR)" + $(Verb) $(RM) "$(DESTDIR)$(prefix)/lib/$(LIBRARYNAME)$(SHLIBEXT)" + +endif # if !defined(LLDB_DISABLE_PYTHON) diff --git a/scripts/Python/modules/readline/readline.cpp b/scripts/Python/modules/readline/readline.cpp new file mode 100644 index 000000000000..d66ccf4b6b7d --- /dev/null +++ b/scripts/Python/modules/readline/readline.cpp @@ -0,0 +1,76 @@ +// NOTE: Since Python may define some pre-processor definitions which affect the +// standard headers on some systems, you must include Python.h before any +// standard headers are included. +#include "Python.h" + +#include + +#ifndef LLDB_DISABLE_LIBEDIT +#include +#endif + +// Simple implementation of the Python readline module using libedit. +// In the event that libedit is excluded from the build, this turns +// back into a null implementation that blocks the module from pulling +// in the GNU readline shared lib, which causes linkage confusion when +// both readline and libedit's readline compatibility symbols collide. +// +// Currently it only installs a PyOS_ReadlineFunctionPointer, without +// implementing any of the readline module methods. This is meant to +// work around LLVM pr18841 to avoid seg faults in the stock Python +// readline.so linked against GNU readline. + +static struct PyMethodDef moduleMethods[] = +{ + {nullptr, nullptr, 0, nullptr} +}; + +#ifndef LLDB_DISABLE_LIBEDIT +PyDoc_STRVAR( + moduleDocumentation, + "Simple readline module implementation based on libedit."); +#else +PyDoc_STRVAR( + moduleDocumentation, + "Stub module meant to avoid linking GNU readline."); +#endif + +#ifndef LLDB_DISABLE_LIBEDIT +static char* +simple_readline(FILE *stdin, FILE *stdout, char *prompt) +{ + rl_instream = stdin; + rl_outstream = stdout; + char* line = readline(prompt); + if (!line) + { + char* ret = (char*)PyMem_Malloc(1); + if (ret != NULL) + *ret = '\0'; + return ret; + } + if (*line) + add_history(line); + int n = strlen(line); + char* ret = (char*)PyMem_Malloc(n + 2); + strncpy(ret, line, n); + free(line); + ret[n] = '\n'; + ret[n+1] = '\0'; + return ret; +} +#endif + +PyMODINIT_FUNC +initreadline(void) +{ +#ifndef LLDB_DISABLE_LIBEDIT + PyOS_ReadlineFunctionPointer = simple_readline; +#endif + Py_InitModule4( + "readline", + moduleMethods, + moduleDocumentation, + static_cast(NULL), + PYTHON_API_VERSION); +} diff --git a/scripts/Python/prepare_binding_Python.py b/scripts/Python/prepare_binding_Python.py new file mode 100644 index 000000000000..1996841baf18 --- /dev/null +++ b/scripts/Python/prepare_binding_Python.py @@ -0,0 +1,435 @@ +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Python binding preparation script. +""" + +# Python modules: +from __future__ import print_function + +import logging +import os +import re +import shutil +import subprocess +import sys + + +class SwigSettings(object): + """Provides a single object to represent swig files and settings.""" + def __init__(self): + self.extensions_file = None + self.header_files = None + self.input_file = None + self.interface_files = None + self.output_file = None + self.safecast_file = None + self.typemaps_file = None + self.wrapper_file = None + + @classmethod + def _any_files_newer(cls, files, check_mtime): + """Returns if any of the given files has a newer modified time. + + @param cls the class + @param files a list of zero or more file paths to check + @param check_mtime the modification time to use as a reference. + + @return True if any file's modified time is newer than check_mtime. + """ + for path in files: + path_mtime = os.path.getmtime(path) + if path_mtime > check_mtime: + # This path was modified more recently than the + # check_mtime. + return True + # If we made it here, nothing was newer than the check_mtime + return False + + @classmethod + def _file_newer(cls, path, check_mtime): + """Tests how recently a file has been modified. + + @param cls the class + @param path a file path to check + @param check_mtime the modification time to use as a reference. + + @return True if the file's modified time is newer than check_mtime. + """ + path_mtime = os.path.getmtime(path) + return path_mtime > check_mtime + + def output_out_of_date(self): + """Returns whether the output file is out of date. + + Compares output file time to all the input files. + + @return True if any of the input files are newer than + the output file, or if the output file doesn't exist; + False otherwise. + """ + if not os.path.exists(self.output_file): + logging.info("will generate, missing binding output file") + return True + output_mtime = os.path.getmtime(self.output_file) + if self._any_files_newer(self.header_files, output_mtime): + logging.info("will generate, header files newer") + return True + if self._any_files_newer(self.interface_files, output_mtime): + logging.info("will generate, interface files newer") + return True + if self._file_newer(self.input_file, output_mtime): + logging.info("will generate, swig input file newer") + return True + if self._file_newer(self.extensions_file, output_mtime): + logging.info("will generate, swig extensions file newer") + return True + if self._file_newer(self.wrapper_file, output_mtime): + logging.info("will generate, swig wrapper file newer") + return True + if self._file_newer(self.typemaps_file, output_mtime): + logging.info("will generate, swig typemaps file newer") + return True + if self._file_newer(self.safecast_file, output_mtime): + logging.info("will generate, swig safecast file newer") + return True + + # If we made it here, nothing is newer than the output file. + # Thus, the output file is not out of date. + return False + + +def get_header_files(options): + """Returns a list of paths to C++ header files for the LLDB API. + + These are the files that define the C++ API that will be wrapped by Python. + + @param options the dictionary of options parsed from the command line. + + @return a list of full paths to the include files used to define the public + LLDB C++ API. + """ + + header_file_paths = [] + header_base_dir = os.path.join(options.src_root, "include", "lldb") + + # Specify the include files in include/lldb that are not easy to + # grab programatically. + for header in [ + "lldb-defines.h", + "lldb-enumerations.h", + "lldb-forward.h", + "lldb-types.h"]: + header_file_paths.append(os.path.normcase( + os.path.join(header_base_dir, header))) + + # Include the main LLDB.h file. + api_dir = os.path.join(header_base_dir, "API") + header_file_paths.append(os.path.normcase( + os.path.join(api_dir, "LLDB.h"))) + + filename_regex = re.compile(r"^SB.+\.h$") + + # Include all the SB*.h files in the API dir. + for filename in os.listdir(api_dir): + if filename_regex.match(filename): + header_file_paths.append( + os.path.normcase(os.path.join(api_dir, filename))) + + logging.debug("found public API header file paths: %s", header_file_paths) + return header_file_paths + + +def get_interface_files(options): + """Returns a list of interface files used as input to swig. + + @param options the options dictionary parsed from the command line args. + + @return a list of full paths to the interface (.i) files used to describe + the public API language binding. + """ + interface_file_paths = [] + interface_dir = os.path.join(options.src_root, "scripts", "interface") + + for filepath in [f for f in os.listdir(interface_dir) + if os.path.splitext(f)[1] == ".i"]: + interface_file_paths.append( + os.path.normcase(os.path.join(interface_dir, filepath))) + + logging.debug("found swig interface files: %s", interface_file_paths) + return interface_file_paths + + +def remove_ignore_enoent(filename): + """Removes given file, ignoring error if it doesn't exist. + + @param filename the path of the file to remove. + """ + try: + os.remove(filename) + except OSError as error: + import errno + if error.errno != errno.ENOENT: + raise + + +def do_swig_rebuild(options, dependency_file, config_build_dir, settings): + """Generates Python bindings file from swig. + + This method will do a sys.exit() if something fails. If it returns to + the caller, it succeeded. + + @param options the parsed command line options structure. + @param dependency_file path to the bindings dependency file + to be generated; otherwise, None if a dependency file is not + to be generated. + @param config_build_dir used as the output directory used by swig + @param settings the SwigSettings that specify a number of aspects used + to configure building the Python binding with swig (mostly paths) + """ + if options.generate_dependency_file: + temp_dep_file_path = dependency_file + ".tmp" + + # Build the SWIG args list + command = [ + options.swig_executable, + "-c++", + "-shadow", + "-python", + "-threads", + "-I\"%s\"" % os.path.normcase( + os.path.join(options.src_root, "include")), + "-I\"%s\"" % os.path.normcase("./."), + "-D__STDC_LIMIT_MACROS", + "-D__STDC_CONSTANT_MACROS"] + if options.generate_dependency_file: + command.append("-MMD -MF \"%s\"" % temp_dep_file_path) + command.extend([ + "-outdir", "\"%s\"" % config_build_dir, + "-o", "\"%s\"" % settings.output_file, + "\"%s\"" % settings.input_file + ]) + logging.info("running swig with: %s", command) + + # Execute swig + process = subprocess.Popen( + ' '.join(command), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True) + # Wait for SWIG process to terminate + swig_stdout, swig_stderr = process.communicate() + return_code = process.returncode + if return_code != 0: + logging.error( + "swig failed with error code %d: stdout=%s, stderr=%s", + return_code, + swig_stdout, + swig_stderr) + logging.error( + "command line:\n%s", ' '.join(command)) + sys.exit(return_code) + + logging.info("swig generation succeeded") + if swig_stdout is not None and len(swig_stdout) > 0: + logging.info("swig output: %s", swig_stdout) + + # Move the depedency file we just generated to the proper location. + if options.generate_dependency_file: + if os.path.exists(temp_dep_file_path): + shutil.move(temp_dep_file_path, dependency_file) + else: + logging.error( + "failed to generate Python binding depedency file '%s'", + temp_dep_file_path) + if os.path.exists(dependency_file): + # Delete the old one. + os.remove(dependency_file) + sys.exit(-10) + + +def run_python_script(script_and_args): + """Runs a python script, logging appropriately. + + If the command returns anything non-zero, it is registered as + an error and exits the program. + + @param script_and_args the python script to execute, along with + the command line arguments to pass to it. + """ + command_line = "%s %s" % (sys.executable, script_and_args) + process = subprocess.Popen(command_line, shell=True) + script_stdout, script_stderr = process.communicate() + return_code = process.returncode + if return_code != 0: + logging.error("failed to run '%s': %s", command_line, script_stderr) + sys.exit(return_code) + else: + logging.info("ran script '%s'", command_line) + if script_stdout is not None: + logging.info("output: %s", script_stdout) + + +def do_modify_python_lldb(options, config_build_dir): + """Executes the modify-python-lldb.py script. + + @param options the parsed command line arguments + @param config_build_dir the directory where the Python output was created. + """ + script_path = os.path.normcase( + os.path.join( + options.src_root, + "scripts", + "Python", + "modify-python-lldb.py")) + + if not os.path.exists(script_path): + logging.error("failed to find python script: '%s'", script_path) + sys.exit(-11) + + script_invocation = "%s %s" % (script_path, config_build_dir) + run_python_script(script_invocation) + + +def get_python_module_path(options): + """Returns the location where the lldb Python module should be placed. + + @param options dictionary of options parsed from the command line. + + @return the directory where the lldb module should be placed. + """ + if options.framework: + # Caller wants to use the OS X framework packaging. + + # We are packaging in an OS X-style framework bundle. The + # module dir will be within the + # LLDB.framework/Resources/Python subdirectory. + return os.path.join( + options.target_dir, + "LLDB.framework", + "Resources", + "Python", + "lldb") + else: + from distutils.sysconfig import get_python_lib + + if options.prefix is not None: + module_path = get_python_lib(True, False, options.prefix) + else: + module_path = get_python_lib(True, False) + return os.path.normcase( + os.path.join(module_path, "lldb")) + + +def main(options): + """Pepares the Python language binding to LLDB. + + @param options the parsed command line argument dictionary + """ + # Setup generated dependency file options. + if options.generate_dependency_file: + dependency_file = os.path.normcase(os.path.join( + options.target_dir, "LLDBWrapPython.cpp.d")) + else: + dependency_file = None + + # Keep track of all the swig-related settings. + settings = SwigSettings() + + # Determine the final binding file path. + settings.output_file = os.path.normcase( + os.path.join(options.target_dir, "LLDBWrapPython.cpp")) + + # Touch the output file (but don't really generate it) if python + # is disabled. + disable_python = os.getenv("LLDB_DISABLE_PYTHON", None) + if disable_python is not None and disable_python == "1": + remove_ignore_enoent(settings.output_file) + # Touch the file. + open(settings.output_file, 'w').close() + logging.info( + "Created empty python binding file due to LLDB_DISABLE_PYTHON " + "being set") + return + + # We also check the GCC_PREPROCESSOR_DEFINITIONS to see if it + # contains LLDB_DISABLE_PYTHON. If so, we skip generating + # the binding. + gcc_preprocessor_defs = os.getenv("GCC_PREPROCESSOR_DEFINITIONS", None) + if gcc_preprocessor_defs is not None: + if re.search(r"LLDB_DISABLE_PYTHON", gcc_preprocessor_defs): + remove_ignore_enoent(settings.output_file) + # Touch the file + open(settings.output_file, 'w').close() + logging.info( + "Created empty python binding file due to " + "finding LLDB_DISABLE_PYTHON in GCC_PREPROCESSOR_DEFINITIONS") + return + + # Setup paths used during swig invocation. + settings.input_file = os.path.normcase( + os.path.join(options.src_root, "scripts", "lldb.swig")) + scripts_python_dir = os.path.dirname(os.path.realpath(__file__)) + settings.extensions_file = os.path.normcase( + os.path.join(scripts_python_dir, "python-extensions.swig")) + settings.wrapper_file = os.path.normcase( + os.path.join(scripts_python_dir, "python-wrapper.swig")) + settings.typemaps_file = os.path.normcase( + os.path.join(scripts_python_dir, "python-typemaps.swig")) + settings.safecast_file = os.path.normcase( + os.path.join(scripts_python_dir, "python-swigsafecast.swig")) + + settings.header_files = get_header_files(options) + settings.interface_files = get_interface_files(options) + + generate_output = settings.output_out_of_date() + + # Determine where to put the module. + python_module_path = get_python_module_path(options) + logging.info("python module path: %s", python_module_path) + + # Handle the configuration build dir. + if options.config_build_dir is not None: + config_build_dir = options.config_build_dir + else: + config_build_dir = python_module_path + + # Allow missing/non-link _lldb.so to force regeneration. + if not generate_output: + # Ensure the _lldb.so file exists. + so_path = os.path.join(python_module_path, "_lldb.so") + if not os.path.exists(so_path) or not os.path.islink(so_path): + logging.info("_lldb.so doesn't exist or isn't a symlink") + generate_output = True + + # Allow missing __init__.py to force regeneration. + if not generate_output: + # Ensure the __init__.py for the lldb module can be found. + init_path = os.path.join(python_module_path, "__init__.py") + if not os.path.exists(init_path): + logging.info("__init__.py doesn't exist") + generate_output = True + + if not generate_output: + logging.info( + "Skipping Python binding generation: everything is up to date") + return + + # Generate the Python binding with swig. + logging.info("Python binding is out of date, regenerating") + do_swig_rebuild(options, dependency_file, config_build_dir, settings) + if options.generate_dependency_file: + return + + # Post process the swig-generated file. + do_modify_python_lldb(options, config_build_dir) + + +# This script can be called by another Python script by calling the main() +# function directly +if __name__ == "__main__": + print("Script cannot be called directly.") + sys.exit(-1) diff --git a/scripts/Python/python-extensions.swig b/scripts/Python/python-extensions.swig new file mode 100644 index 000000000000..fae7f401bf13 --- /dev/null +++ b/scripts/Python/python-extensions.swig @@ -0,0 +1,1087 @@ + +%extend lldb::SBAddress { + PyObject *lldb::SBAddress::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBBlock { + PyObject *lldb::SBBlock::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBBreakpoint { + PyObject *lldb::SBBreakpoint::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} + +} +%extend lldb::SBBreakpointLocation { + PyObject *lldb::SBBreakpointLocation::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelFull); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} + +%extend lldb::SBBroadcaster { + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBCommandReturnObject { + PyObject *lldb::SBCommandReturnObject::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + + /* the write() and flush() calls are not part of the SB API proper, and are solely for Python usage + they are meant to make an SBCommandReturnObject into a file-like object so that instructions of the sort + print >>sb_command_return_object, "something" + will work correctly */ + + void lldb::SBCommandReturnObject::write (const char* str) + { + if (str) + $self->Printf("%s",str); + } + void lldb::SBCommandReturnObject::flush () + {} +} +%extend lldb::SBCompileUnit { + PyObject *lldb::SBCompileUnit::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBData { + PyObject *lldb::SBData::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBDebugger { + PyObject *lldb::SBDebugger::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBDeclaration { + PyObject *lldb::SBDeclaration::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} + +} +%extend lldb::SBError { + PyObject *lldb::SBError::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBFileSpec { + PyObject *lldb::SBFileSpec::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBFrame { + PyObject *lldb::SBFrame::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBFunction { + PyObject *lldb::SBFunction::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} + +} +%extend lldb::SBInstruction { + PyObject *lldb::SBInstruction::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBInstructionList { + PyObject *lldb::SBInstructionList::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBLineEntry { + PyObject *lldb::SBLineEntry::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBModule { + PyObject *lldb::SBModule::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBModuleSpec { + PyObject *lldb::SBModuleSpec::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} + +%extend lldb::SBModuleSpecList { + PyObject *lldb::SBModuleSpecList::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} + +%extend lldb::SBProcess { + PyObject *lldb::SBProcess::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBSection { + PyObject *lldb::SBSection::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBStream { + /* the write() and flush() calls are not part of the SB API proper, and are solely for Python usage + they are meant to make an SBStream into a file-like object so that instructions of the sort + print >>sb_stream, "something" + will work correctly */ + + void lldb::SBStream::write (const char* str) + { + if (str) + $self->Printf("%s",str); + } + void lldb::SBStream::flush () + {} +} +%extend lldb::SBSymbol { + PyObject *lldb::SBSymbol::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBSymbolContext { + PyObject *lldb::SBSymbolContext::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBSymbolContextList { + PyObject *lldb::SBSymbolContextList::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} + +%extend lldb::SBTarget { + PyObject *lldb::SBTarget::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} + +%extend lldb::SBType { + PyObject *lldb::SBType::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBTypeCategory { + PyObject *lldb::SBTypeCategory::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBTypeFilter { + PyObject *lldb::SBTypeFilter::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBTypeFormat { + PyObject *lldb::SBTypeFormat::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBTypeMember { + PyObject *lldb::SBTypeMember::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBTypeMemberFunction { + PyObject *lldb::SBTypeMemberFunction::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBTypeEnumMember { + PyObject *lldb::SBTypeEnumMember::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBTypeNameSpecifier { + PyObject *lldb::SBTypeNameSpecifier::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBTypeSummary { + PyObject *lldb::SBTypeSummary::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBTypeSynthetic { + PyObject *lldb::SBTypeSynthetic::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelBrief); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBThread { + PyObject *lldb::SBThread::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } + %pythoncode %{ + def __eq__(self, rhs): + if not isinstance(rhs, type(self)): + return False + + return getattr(_lldb,self.__class__.__name__+"___eq__")(self, rhs) + + def __ne__(self, rhs): + if not isinstance(rhs, type(self)): + return True + + return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) + %} +} +%extend lldb::SBValue { + PyObject *lldb::SBValue::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBValueList { + PyObject *lldb::SBValueList::__str__ (){ + lldb::SBStream description; + const size_t n = $self->GetSize(); + if (n) + { + for (size_t i=0; iGetValueAtIndex(i).GetDescription(description); + } + else + { + description.Printf(" lldb.SBValueList()"); + } + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} +%extend lldb::SBWatchpoint { + PyObject *lldb::SBWatchpoint::__str__ (){ + lldb::SBStream description; + $self->GetDescription (description, lldb::eDescriptionLevelVerbose); + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + if (desc_len > 0) + return lldb_private::PythonString(llvm::StringRef(desc, desc_len)).release(); + else + return lldb_private::PythonString("").release(); + } +} + + +// %extend lldb::SBDebugger { +// // FIXME: We can't get the callback and baton +// PyObject *lldb::SBDebugger (){ +// // Only call Py_XDECREF if we have a Python object (or NULL) +// if (LLDBSwigPythonCallPythonLogOutputCallback == $self->GetLogOutPutCallback()) +// Py_XDECREF($self->GetCallbackBaton()); +// } +// } + +%pythoncode %{ + +def command(*args, **kwargs): + import lldb + """A decorator function that registers an LLDB command line + command that is bound to the function it is attached to.""" + class obj(object): + """The object that tracks adding the command to LLDB one time and handles + calling the function on subsequent calls.""" + def __init__(self, function, command_name, doc = None): + if doc: + function.__doc__ = doc + command = "command script add -f %s.%s %s" % (function.__module__, function.__name__, command_name) + lldb.debugger.HandleCommand(command) + self.function = function + def __call__(self, *args, **kwargs): + self.function(*args, **kwargs) + def callable(function): + """Creates a callable object that gets used.""" + return obj(function, *args, **kwargs) + return callable + +class declaration(object): + '''A class that represents a source declaration location with file, line and column.''' + def __init__(self, file, line, col): + self.file = file + self.line = line + self.col = col + +class value_iter(object): + def __iter__(self): + return self + + def next(self): + if self.index >= self.length: + raise StopIteration() + child_sbvalue = self.sbvalue.GetChildAtIndex(self.index) + self.index += 1 + return value(child_sbvalue) + + def __init__(self,value): + self.index = 0 + self.sbvalue = value + if type(self.sbvalue) is value: + self.sbvalue = self.sbvalue.sbvalue + self.length = self.sbvalue.GetNumChildren() + +class value(object): + '''A class designed to wrap lldb.SBValue() objects so the resulting object + can be used as a variable would be in code. So if you have a Point structure + variable in your code in the current frame named "pt", you can initialize an instance + of this class with it: + + pt = lldb.value(lldb.frame.FindVariable("pt")) + print pt + print pt.x + print pt.y + + pt = lldb.value(lldb.frame.FindVariable("rectangle_array")) + print rectangle_array[12] + print rectangle_array[5].origin.x''' + def __init__(self, sbvalue): + self.sbvalue = sbvalue + + def __nonzero__(self): + return self.sbvalue.__nonzero__() + + def __str__(self): + return self.sbvalue.__str__() + + def __getitem__(self, key): + # Allow array access if this value has children... + if type(key) is value: + key = int(key) + if type(key) is int: + child_sbvalue = (self.sbvalue.GetValueForExpressionPath("[%i]" % key)) + if child_sbvalue and child_sbvalue.IsValid(): + return value(child_sbvalue) + raise IndexError("Index '%d' is out of range" % key) + raise TypeError("No array item of type %s" % str(type(key))) + + def __iter__(self): + return value_iter(self.sbvalue) + + def __getattr__(self, name): + child_sbvalue = self.sbvalue.GetChildMemberWithName (name) + if child_sbvalue and child_sbvalue.IsValid(): + return value(child_sbvalue) + raise AttributeError("Attribute '%s' is not defined" % name) + + def __add__(self, other): + return int(self) + int(other) + + def __sub__(self, other): + return int(self) - int(other) + + def __mul__(self, other): + return int(self) * int(other) + + def __floordiv__(self, other): + return int(self) // int(other) + + def __mod__(self, other): + return int(self) % int(other) + + def __divmod__(self, other): + return int(self) % int(other) + + def __pow__(self, other): + return int(self) ** int(other) + + def __lshift__(self, other): + return int(self) << int(other) + + def __rshift__(self, other): + return int(self) >> int(other) + + def __and__(self, other): + return int(self) & int(other) + + def __xor__(self, other): + return int(self) ^ int(other) + + def __or__(self, other): + return int(self) | int(other) + + def __div__(self, other): + return int(self) / int(other) + + def __truediv__(self, other): + return int(self) / int(other) + + def __iadd__(self, other): + result = self.__add__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __isub__(self, other): + result = self.__sub__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __imul__(self, other): + result = self.__mul__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __idiv__(self, other): + result = self.__div__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __itruediv__(self, other): + result = self.__truediv__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ifloordiv__(self, other): + result = self.__floordiv__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __imod__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ipow__(self, other): + result = self.__pow__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ipow__(self, other, modulo): + result = self.__pow__(self, other, modulo) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ilshift__(self, other): + result = self.__lshift__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __irshift__(self, other): + result = self.__rshift__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __iand__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ixor__(self, other): + result = self.__xor__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ior__(self, other): + result = self.__ior__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __neg__(self): + return -int(self) + + def __pos__(self): + return +int(self) + + def __abs__(self): + return abs(int(self)) + + def __invert__(self): + return ~int(self) + + def __complex__(self): + return complex (int(self)) + + def __int__(self): + return self.sbvalue.GetValueAsSigned() + + def __long__(self): + return self.sbvalue.GetValueAsSigned() + + def __float__(self): + return float (self.sbvalue.GetValueAsSigned()) + + def __oct__(self): + return '0%o' % self.sbvalue.GetValueAsUnsigned() + + def __hex__(self): + return '0x%x' % self.sbvalue.GetValueAsUnsigned() + + def __len__(self): + return self.sbvalue.GetNumChildren() + + def __eq__(self, other): + if type(other) is int: + return int(self) == other + elif type(other) is str: + return str(self) == other + elif type(other) is value: + self_err = SBError() + other_err = SBError() + self_val = self.sbvalue.GetValueAsUnsigned(self_err) + if self_err.fail: + raise ValueError("unable to extract value of self") + other_val = other.sbvalue.GetValueAsUnsigned(other_err) + if other_err.fail: + raise ValueError("unable to extract value of other") + return self_val == other_val + raise TypeError("Unknown type %s, No equality operation defined." % str(type(other))) + + def __ne__(self, other): + return not self.__eq__(other) +%} + +%pythoncode %{ + +class SBSyntheticValueProvider(object): + def __init__(self,valobj): + pass + + def num_children(self): + return 0 + + def get_child_index(self,name): + return None + + def get_child_at_index(self,idx): + return None + + def update(self): + pass + + def has_children(self): + return False + + +%} \ No newline at end of file diff --git a/scripts/Python/python-swigsafecast.swig b/scripts/Python/python-swigsafecast.swig new file mode 100644 index 000000000000..ea3f21f859ad --- /dev/null +++ b/scripts/Python/python-swigsafecast.swig @@ -0,0 +1,142 @@ +// leaving this undefined ensures we will get a linker error if we try to use SBTypeToSWIGWrapper() +// for a type for which we did not specialze this function +template +PyObject* +SBTypeToSWIGWrapper (SBClass* sb_object); + +template +PyObject* +SBTypeToSWIGWrapper (SBClass& sb_object) +{ + return SBTypeToSWIGWrapper(&sb_object); +} + +template +PyObject* +SBTypeToSWIGWrapper (const SBClass& sb_object) +{ + return SBTypeToSWIGWrapper(&sb_object); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (PyObject* py_object) +{ + return py_object; +} + +template <> +PyObject* +SBTypeToSWIGWrapper (const char* c_str) +{ + if (c_str) + return PyString_FromString(c_str); + return NULL; +} + +template <> +PyObject* +SBTypeToSWIGWrapper (unsigned int* c_int) +{ + if (!c_int) + return NULL; + return PyInt_FromLong(*c_int); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBEvent* event_sb) +{ + return SWIG_NewPointerObj((void *) event_sb, SWIGTYPE_p_lldb__SBEvent, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBProcess* process_sb) +{ + return SWIG_NewPointerObj((void *) process_sb, SWIGTYPE_p_lldb__SBProcess, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBThread* thread_sb) +{ + return SWIG_NewPointerObj((void *) thread_sb, SWIGTYPE_p_lldb__SBThread, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBThreadPlan* thread_plan_sb) +{ + return SWIG_NewPointerObj((void *) thread_plan_sb, SWIGTYPE_p_lldb__SBThreadPlan, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBTarget* target_sb) +{ + return SWIG_NewPointerObj((void *) target_sb, SWIGTYPE_p_lldb__SBTarget, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBFrame* frame_sb) +{ + return SWIG_NewPointerObj((void *) frame_sb, SWIGTYPE_p_lldb__SBFrame, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBDebugger* debugger_sb) +{ + return SWIG_NewPointerObj((void *) debugger_sb, SWIGTYPE_p_lldb__SBDebugger, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBBreakpoint* breakpoint_sb) +{ + return SWIG_NewPointerObj((void *) breakpoint_sb, SWIGTYPE_p_lldb__SBBreakpoint, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBWatchpoint* watchpoint_sb) +{ + return SWIG_NewPointerObj((void *) watchpoint_sb, SWIGTYPE_p_lldb__SBWatchpoint, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBBreakpointLocation* breakpoint_location_sb) +{ + return SWIG_NewPointerObj((void *) breakpoint_location_sb, SWIGTYPE_p_lldb__SBBreakpointLocation, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBValue* value_sb) +{ + return SWIG_NewPointerObj((void *) value_sb, SWIGTYPE_p_lldb__SBValue, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBCommandReturnObject* cmd_ret_obj_sb) +{ + return SWIG_NewPointerObj((void *) cmd_ret_obj_sb, SWIGTYPE_p_lldb__SBCommandReturnObject, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBExecutionContext* ctx_sb) +{ + return SWIG_NewPointerObj((void *) ctx_sb, SWIGTYPE_p_lldb__SBExecutionContext, 0); +} + +template <> +PyObject* +SBTypeToSWIGWrapper (lldb::SBTypeSummaryOptions* summary_options_sb) +{ + return SWIG_NewPointerObj((void *) summary_options_sb, SWIGTYPE_p_lldb__SBTypeSummaryOptions, 0); +} diff --git a/scripts/Python/python-typemaps.swig b/scripts/Python/python-typemaps.swig new file mode 100644 index 000000000000..ec9302a15cd7 --- /dev/null +++ b/scripts/Python/python-typemaps.swig @@ -0,0 +1,601 @@ +/* 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(o); + else { + PyErr_SetString(PyExc_TypeError,"list must contain strings"); + free($1); + return NULL; + } + } + $1[i] = 0; + } else if ($input == Py_None) { + $1 = NULL; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(in) lldb::tid_t { + if (PyInt_Check($input)) + $1 = PyInt_AsLong($input); + else if (PyLong_Check($input)) + $1 = PyLong_AsLongLong($input); + else + { + PyErr_SetString(PyExc_ValueError, "Expecting an integer"); + return NULL; + } +} + +%typemap(typecheck) char ** { + /* Check if is a list */ + $1 = 1; + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (!PyString_Check(o)) { $1 = 0; } + } + } + else + { + $1 = ( ($input == Py_None) ? 1 : 0); + } +} + +%typemap(freearg) char** { + free((char *) $1); +} + +%typemap(out) char** { + int len; + int i; + len = 0; + while ($1[len]) len++; + using namespace lldb_private; + PythonList list(len); + for (i = 0; i < len; i++) + list.SetItemAtIndex(i, PythonString($1[i])); + $result = list.release(); +} + +%typemap(in) char const ** { + /* Check if is a list */ + using namespace lldb_private; + if (PythonList::Check($input)) { + PythonList py_list(PyRefType::Borrowed, $input); + int size = py_list.GetSize(); + + $1 = (char**)malloc((size+1)*sizeof(char*)); + for (int i = 0; i < size; i++) { + PythonObject o = py_list.GetItemAtIndex(i); + if (!PythonString::Check(o.get())) { + PyErr_SetString(PyExc_TypeError,"list must contain strings"); + free($1); + return nullptr; + } + auto py_str = o.AsType(); + $1[i] = const_cast(py_str.GetString().data()); + } + + $1[size] = 0; + } else if ($input == Py_None) { + $1 = nullptr; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return nullptr; + } +} + +%typemap(typecheck) char const ** { + /* Check if is a list */ + $1 = 1; + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (!PyString_Check(o)) { $1 = 0; } + } + } + else + { + $1 = ( ($input == Py_None) ? 1 : 0); + } +} + +%typemap(freearg) char const ** { + free((char *) $1); +} + +%typemap(out) char const ** { + 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])); + } +} + +/* Typemap definitions to allow SWIG to properly handle char buffer. */ + +// typemap for a char buffer +// See also SBThread::GetStopDescription. +%typemap(in) (char *dst, size_t dst_len) { + if (!PyInt_Check($input)) { + PyErr_SetString(PyExc_ValueError, "Expecting an integer"); + return NULL; + } + $2 = PyInt_AsLong($input); + if ($2 <= 0) { + PyErr_SetString(PyExc_ValueError, "Positive integer expected"); + return NULL; + } + $1 = (char *) malloc($2); +} + +// Return the char buffer. Discarding any previous return result +// See also SBThread::GetStopDescription. +%typemap(argout) (char *dst, size_t dst_len) { + Py_XDECREF($result); /* Blow away any previous result */ + if (result == 0) { + $result = Py_None; + Py_INCREF($result); + } else { + llvm::StringRef ref(static_cast($1), result); + lldb_private::PythonString string(ref); + $result = string.release(); + } + free($1); +} + + +// typemap for an outgoing buffer +// See also SBEvent::SBEvent(uint32_t event, const char *cstr, uint32_t cstr_len). +%typemap(in) (const char *cstr, uint32_t cstr_len) { + if (PyString_Check($input)) { + $1 = (char *) PyString_AsString($input); + $2 = PyString_Size($input); + } + else if(PyByteArray_Check($input)) { + $1 = (char *) PyByteArray_AsString($input); + $2 = PyByteArray_Size($input); + } + else { + PyErr_SetString(PyExc_ValueError, "Expecting a string"); + return NULL; + } +} +// Ditto for SBProcess::PutSTDIN(const char *src, size_t src_len). +%typemap(in) (const char *src, size_t src_len) { + if (PyString_Check($input)) { + $1 = (char *) PyString_AsString($input); + $2 = PyString_Size($input); + } + else if(PyByteArray_Check($input)) { + $1 = (char *) PyByteArray_AsString($input); + $2 = PyByteArray_Size($input); + } + else { + PyErr_SetString(PyExc_ValueError, "Expecting a string"); + return NULL; + } +} +// And SBProcess::WriteMemory. +%typemap(in) (const void *buf, size_t size) { + if (PyString_Check($input)) { + $1 = (void *) PyString_AsString($input); + $2 = PyString_Size($input); + } + else if(PyByteArray_Check($input)) { + $1 = (void *) PyByteArray_AsString($input); + $2 = PyByteArray_Size($input); + } + else { + PyErr_SetString(PyExc_ValueError, "Expecting a string"); + return NULL; + } +} + +// For SBDebugger::DispatchInput +%typemap(in) (const void *data, size_t data_len) { + if (PyString_Check($input)) { + $1 = static_cast(PyString_AsString($input)); + $2 = PyString_Size($input); + } + else if(PyByteArray_Check($input)) { + $1 = static_cast(PyByteArray_AsString($input)); + $2 = PyByteArray_Size($input); + } + else { + PyErr_SetString(PyExc_ValueError, "Expecting a string or byte array"); + return NULL; + } +} + +// typemap for an incoming buffer +// See also SBProcess::ReadMemory. +%typemap(in) (void *buf, size_t size) { + if (PyInt_Check($input)) { + $2 = PyInt_AsLong($input); + } else if (PyLong_Check($input)) { + $2 = PyLong_AsLong($input); + } else { + PyErr_SetString(PyExc_ValueError, "Expecting an integer or long object"); + return NULL; + } + if ($2 <= 0) { + PyErr_SetString(PyExc_ValueError, "Positive integer expected"); + return NULL; + } + $1 = (void *) malloc($2); +} + +// Return the buffer. Discarding any previous return result +// See also SBProcess::ReadMemory. +%typemap(argout) (void *buf, size_t size) { + Py_XDECREF($result); /* Blow away any previous result */ + if (result == 0) { + $result = Py_None; + Py_INCREF($result); + } else { + llvm::StringRef ref(static_cast($1), result); + lldb_private::PythonString string(ref); + $result = string.release(); + } + free($1); +} + +// these typemaps allow Python users to pass list objects +// and have them turn into C++ arrays (this is useful, for instance +// when creating SBData objects from lists of numbers) +%typemap(in) (uint64_t* array, size_t array_len) { + /* Check if is a list */ + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + $2 = size; + $1 = (uint64_t*) malloc(size * sizeof(uint64_t)); + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (PyInt_Check(o)) { + $1[i] = PyInt_AsLong(o); + } + else if (PyLong_Check(o)) { + $1[i] = PyLong_AsUnsignedLongLong(o); + } + else { + PyErr_SetString(PyExc_TypeError,"list must contain numbers"); + free($1); + return NULL; + } + + if (PyErr_Occurred()) { + free($1); + return NULL; + } + } + } else if ($input == Py_None) { + $1 = NULL; + $2 = 0; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(freearg) (uint64_t* array, size_t array_len) { + free($1); +} + +%typemap(in) (uint32_t* array, size_t array_len) { + /* Check if is a list */ + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + $2 = size; + $1 = (uint32_t*) malloc(size * sizeof(uint32_t)); + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (PyInt_Check(o)) { + $1[i] = PyInt_AsLong(o); + } + else if (PyLong_Check(o)) { + $1[i] = PyLong_AsUnsignedLong(o); + } + else { + PyErr_SetString(PyExc_TypeError,"list must contain numbers"); + free($1); + return NULL; + } + + if (PyErr_Occurred()) { + free($1); + return NULL; + } + } + } else if ($input == Py_None) { + $1 = NULL; + $2 = 0; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(freearg) (uint32_t* array, size_t array_len) { + free($1); +} + +%typemap(in) (int64_t* array, size_t array_len) { + /* Check if is a list */ + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + $2 = size; + $1 = (int64_t*) malloc(size * sizeof(int64_t)); + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (PyInt_Check(o)) { + $1[i] = PyInt_AsLong(o); + } + else if (PyLong_Check(o)) { + $1[i] = PyLong_AsLongLong(o); + } + else { + PyErr_SetString(PyExc_TypeError,"list must contain numbers"); + free($1); + return NULL; + } + + if (PyErr_Occurred()) { + free($1); + return NULL; + } + } + } else if ($input == Py_None) { + $1 = NULL; + $2 = 0; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(freearg) (int64_t* array, size_t array_len) { + free($1); +} + +%typemap(in) (int32_t* array, size_t array_len) { + /* Check if is a list */ + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + $2 = size; + $1 = (int32_t*) malloc(size * sizeof(int32_t)); + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (PyInt_Check(o)) { + $1[i] = PyInt_AsLong(o); + } + else if (PyLong_Check(o)) { + $1[i] = PyLong_AsLong(o); + } + else { + PyErr_SetString(PyExc_TypeError,"list must contain numbers"); + free($1); + return NULL; + } + + if (PyErr_Occurred()) { + free($1); + return NULL; + } + } + } else if ($input == Py_None) { + $1 = NULL; + $2 = 0; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(freearg) (int32_t* array, size_t array_len) { + free($1); +} + +%typemap(in) (double* array, size_t array_len) { + /* Check if is a list */ + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + $2 = size; + $1 = (double*) malloc(size * sizeof(double)); + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (PyFloat_Check(o)) { + $1[i] = PyFloat_AsDouble(o); + } + else { + PyErr_SetString(PyExc_TypeError,"list must contain floating-point numbers"); + free($1); + return NULL; + } + } + } else if ($input == Py_None) { + $1 = NULL; + $2 = 0; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(freearg) (double* array, size_t array_len) { + free($1); +} + +// these typemaps wrap SBModule::GetVersion() from requiring a memory buffer +// to the more Pythonic style where a list is returned and no previous allocation +// is necessary - this will break if more than 50 versions are ever returned +%typemap(typecheck) (uint32_t *versions, uint32_t num_versions) { + $1 = ($input == Py_None ? 1 : 0); +} + +%typemap(in, numinputs=0) (uint32_t *versions) { + $1 = (uint32_t*)malloc(sizeof(uint32_t) * 50); +} + +%typemap(in, numinputs=0) (uint32_t num_versions) { + $1 = 50; +} + +%typemap(argout) (uint32_t *versions, uint32_t num_versions) { + uint32_t count = result; + if (count >= $2) + count = $2; + PyObject* list = PyList_New(count); + for (uint32_t j = 0; j < count; j++) + { + if ($1[j] < UINT32_MAX) + { + PyObject* item = PyInt_FromLong($1[j]); + int ok = PyList_SetItem(list,j,item); + if (ok != 0) + { + $result = Py_None; + break; + } + } + else + break; + } + $result = list; +} + +%typemap(freearg) (uint32_t *versions) { + free($1); +} + + +// For Log::LogOutputCallback +%typemap(in) (lldb::LogOutputCallback log_callback, void *baton) { + if (!($input == Py_None || PyCallable_Check(reinterpret_cast($input)))) { + PyErr_SetString(PyExc_TypeError, "Need a callable object or None!"); + return NULL; + } + + // FIXME (filcab): We can't currently check if our callback is already + // LLDBSwigPythonCallPythonLogOutputCallback (to DECREF the previous + // baton) nor can we just remove all traces of a callback, if we want to + // revert to a file logging mechanism. + + // Don't lose the callback reference + Py_INCREF($input); + $1 = LLDBSwigPythonCallPythonLogOutputCallback; + $2 = $input; +} + +%typemap(typecheck) (lldb::LogOutputCallback log_callback, void *baton) { + $1 = $input == Py_None; + $1 = $1 || PyCallable_Check(reinterpret_cast($input)); +} + +%typemap(in) FILE * { + using namespace lldb_private; + if ($input == Py_None) + $1 = nullptr; + else if (!lldb_private::PythonFile::Check($input)) { + int fd = PyObject_AsFileDescriptor($input); + PythonObject py_input(PyRefType::Borrowed, $input); + PythonString py_mode = py_input.GetAttributeValue("mode").AsType(); + + if (-1 != fd && py_mode.IsValid()) { + FILE *f; + if ((f = fdopen(fd, py_mode.GetString().str().c_str()))) + $1 = f; + else + PyErr_SetString(PyExc_TypeError, strerror(errno)); + } else { + PyErr_SetString(PyExc_TypeError,"not a file-like object"); + return nullptr; + } + } + else + { + PythonFile py_file(PyRefType::Borrowed, $input); + File file; + if (!py_file.GetUnderlyingFile(file)) + return nullptr; + + $1 = file.GetStream(); + } +} + +%typemap(out) FILE * { + char mode[4] = {0}; +#ifdef __MACOSX__ + int i = 0; + short flags = $1->_flags; + + if (flags & __SRD) + mode[i++] = 'r'; + else if (flags & __SWR) + mode[i++] = 'w'; + else // if (flags & __SRW) + mode[i++] = 'a'; +#endif + using namespace lldb_private; + File file($1, false); + PythonFile py_file(file, mode); + $result = py_file.release(); + if (!$result) + { + $result = Py_None; + Py_INCREF(Py_None); + } +} + +%typemap(in) (const char* string, int len) { + using namespace lldb_private; + if ($input == Py_None) + { + $1 = NULL; + $2 = 0; + } + else if (PythonString::Check($input)) + { + PythonString py_str(PyRefType::Borrowed, $input); + llvm::StringRef str = py_str.GetString(); + $1 = const_cast(str.data()); + $2 = str.size(); + // In Python 2, if $input is a PyUnicode object then this + // will trigger a Unicode -> String conversion, in which + // case the `PythonString` will now own the PyString. Thus + // if it goes out of scope, the data will be deleted. The + // only way to avoid this is to leak the Python object in + // that case. Note that if there was no conversion, then + // releasing the string will not leak anything, since we + // created this as a borrowed reference. + py_str.release(); + } + else + { + PyErr_SetString(PyExc_TypeError,"not a string-like object"); + return NULL; + } +} diff --git a/scripts/Python/python-wrapper.swig b/scripts/Python/python-wrapper.swig new file mode 100644 index 000000000000..5d7bfaa89439 --- /dev/null +++ b/scripts/Python/python-wrapper.swig @@ -0,0 +1,936 @@ +%header %{ + +template +PyObject * +SBTypeToSWIGWrapper (T* item); + +class PyErr_Cleaner +{ +public: + PyErr_Cleaner(bool print=false) : + m_print(print) + { + } + + ~PyErr_Cleaner() + { + if (PyErr_Occurred()) + { + if(m_print && !PyErr_ExceptionMatches(PyExc_SystemExit)) + PyErr_Print(); + PyErr_Clear(); + } + } + +private: + bool m_print; +}; + +%} + +%wrapper %{ + +// resolve a dotted Python name in the form +// foo.bar.baz.Foobar to an actual Python object +// if pmodule is NULL, the __main__ module will be used +// as the starting point for the search + + +// This function is called by lldb_private::ScriptInterpreterPython::BreakpointCallbackFunction(...) +// and is used when a script command is attached to a breakpoint for execution. + +SWIGEXPORT bool +LLDBSwigPythonBreakpointCallbackFunction +( + const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& frame_sp, + const lldb::BreakpointLocationSP& bp_loc_sp +) +{ + using namespace lldb_private; + lldb::SBFrame sb_frame (frame_sp); + lldb::SBBreakpointLocation sb_bp_loc(bp_loc_sp); + + bool stop_at_breakpoint = true; + + PyErr_Cleaner py_err_cleaner(true); + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return stop_at_breakpoint; + + PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_frame)); + PythonObject bp_loc_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_bp_loc)); + PythonObject result = pfunc(frame_arg, bp_loc_arg, dict); + + if (result.get() == Py_False) + stop_at_breakpoint = false; + + return stop_at_breakpoint; +} + +// This function is called by lldb_private::ScriptInterpreterPython::WatchpointCallbackFunction(...) +// and is used when a script command is attached to a watchpoint for execution. + +SWIGEXPORT bool +LLDBSwigPythonWatchpointCallbackFunction +( + const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& frame_sp, + const lldb::WatchpointSP& wp_sp +) +{ + using namespace lldb_private; + lldb::SBFrame sb_frame (frame_sp); + lldb::SBWatchpoint sb_wp(wp_sp); + + bool stop_at_watchpoint = true; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return stop_at_watchpoint; + + PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_frame)); + PythonObject wp_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_wp)); + PythonObject result = pfunc(frame_arg, wp_arg, dict); + + if (result.get() == Py_False) + stop_at_watchpoint = false; + + return stop_at_watchpoint; +} + +SWIGEXPORT bool +LLDBSwigPythonCallTypeScript +( + const char *python_function_name, + const void *session_dictionary, + const lldb::ValueObjectSP& valobj_sp, + void** pyfunct_wrapper, + const lldb::TypeSummaryOptionsSP& options_sp, + std::string& retval +) +{ + using namespace lldb_private; + lldb::SBValue sb_value (valobj_sp); + lldb::SBTypeSummaryOptions sb_options(options_sp.get()); + + retval.clear(); + + if (!python_function_name || !session_dictionary) + return false; + + PyObject *pfunc_impl = nullptr; + + if (pyfunct_wrapper && *pyfunct_wrapper && PyFunction_Check (*pyfunct_wrapper)) + { + pfunc_impl = (PyObject*)(*pyfunct_wrapper); + if (pfunc_impl->ob_refcnt == 1) + { + Py_XDECREF(pfunc_impl); + pfunc_impl = NULL; + } + } + + PyObject *py_dict = (PyObject*)session_dictionary; + if (!PythonDictionary::Check(py_dict)) + return true; + + PythonDictionary dict(PyRefType::Borrowed, py_dict); + + PyErr_Cleaner pyerr_cleanup(true); // show Python errors + + PythonCallable pfunc(PyRefType::Borrowed, pfunc_impl); + + if (!pfunc.IsAllocated()) + { + pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); + if (!pfunc.IsAllocated()) + return false; + + if (pyfunct_wrapper) + { + *pyfunct_wrapper = pfunc.get(); + Py_XINCREF(pfunc.get()); + } + } + + PythonObject result; + auto argc = pfunc.GetNumArguments(); + // if the third argument is supported, or varargs are allowed + PythonObject value_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_value)); + PythonObject options_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_options)); + if (argc.count == 3 || argc.has_varargs) + result = pfunc(value_arg,dict,options_arg); + else + result = pfunc(value_arg,dict); + + retval = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT void* +LLDBSwigPythonCreateSyntheticProvider +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp +) +{ + using namespace lldb_private; + + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_class_name,dict); + + if (!pfunc.IsAllocated()) + Py_RETURN_NONE; + + // I do not want the SBValue to be deallocated when going out of scope because python + // has ownership of it and will manage memory for this object by itself + lldb::SBValue *sb_value = new lldb::SBValue(valobj_sp); + sb_value->SetPreferSyntheticValue(false); + + PythonObject val_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_value)); + if (!val_arg.IsAllocated()) + Py_RETURN_NONE; + + PythonObject result = pfunc(val_arg, dict); + + if (result.IsAllocated()) + return result.release(); + + Py_RETURN_NONE; +} + +SWIGEXPORT void* +LLDBSwigPythonCreateCommandObject +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::DebuggerSP debugger_sp +) +{ + using namespace lldb_private; + + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_class_name, dict); + + if (!pfunc.IsAllocated()) + return nullptr; + + lldb::SBDebugger debugger_sb(debugger_sp); + PythonObject debugger_arg(PyRefType::Owned, SBTypeToSWIGWrapper(debugger_sb)); + PythonObject result = pfunc(debugger_arg, dict); + + if (result.IsAllocated()) + return result.release(); + + Py_RETURN_NONE; +} + +SWIGEXPORT void* +LLDBSwigPythonCreateScriptedThreadPlan +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::ThreadPlanSP& thread_plan_sp +) +{ + using namespace lldb_private; + + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + // I do not want the SBThreadPlan to be deallocated when going out of scope because python + // has ownership of it and will manage memory for this object by itself + lldb::SBThreadPlan *tp_value = new lldb::SBThreadPlan(thread_plan_sp); + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_class_name, dict); + + if (!pfunc.IsAllocated()) + return nullptr; + + PythonObject tp_arg(PyRefType::Owned, SBTypeToSWIGWrapper(tp_value)); + + if (!tp_arg.IsAllocated()) + Py_RETURN_NONE; + + PythonObject result = pfunc(tp_arg, dict); + // FIXME: At this point we should check that the class we found supports all the methods + // that we need. + + if (result.IsAllocated()) + return result.release(); + Py_RETURN_NONE; +} + +SWIGEXPORT bool +LLDBSWIGPythonCallThreadPlan +( + void *implementor, + const char *method_name, + lldb_private::Event *event, + bool &got_error +) +{ + using namespace lldb_private; + + got_error = false; + + PyErr_Cleaner py_err_cleaner(false); + PythonObject self(PyRefType::Borrowed, static_cast(implementor)); + auto pfunc = self.ResolveName(method_name); + + if (!pfunc.IsAllocated()) + return false; + + PythonObject result; + if (event != nullptr) + { + lldb::SBEvent sb_event(event); + PythonObject event_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_event)); + result = pfunc(event_arg); + } + else + result = pfunc(); + + if (PyErr_Occurred()) + { + got_error = true; + printf ("Return value was neither false nor true for call to %s.\n", method_name); + PyErr_Print(); + return false; + } + + if (result.get() == Py_True) + return true; + else if (result.get() == Py_False) + return false; + + // Somebody returned the wrong thing... + got_error = true; + printf ("Wrong return value type for call to %s.\n", method_name); + return false; +} + +// wrapper that calls an optional instance member of an object taking no arguments +static PyObject* +LLDBSwigPython_CallOptionalMember +( + PyObject* implementor, + char* callee_name, + PyObject* ret_if_not_found = Py_None, + bool* was_found = NULL +) +{ + using namespace lldb_private; + + PyErr_Cleaner py_err_cleaner(false); + + PythonObject self(PyRefType::Borrowed, static_cast(implementor)); + auto pfunc = self.ResolveName(callee_name); + + if (!pfunc.IsAllocated()) + { + if (was_found) + *was_found = false; + Py_XINCREF(ret_if_not_found); + return ret_if_not_found; + } + + if (was_found) + *was_found = true; + + PythonObject result = pfunc(); + return result.release(); +} + +SWIGEXPORT size_t +LLDBSwigPython_CalculateNumChildren +( + PyObject *implementor, + uint32_t max +) +{ + using namespace lldb_private; + + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName("num_children"); + + if (!pfunc.IsAllocated()) + return 0; + + PythonObject result; + auto argc = pfunc.GetNumArguments(); + if (argc.count == 1) + result = pfunc(); + else if (argc.count == 2) + result = pfunc(PythonInteger(max)); + + if (!result.IsAllocated()) + return 0; + + PythonInteger int_result = result.AsType(); + if (!int_result.IsAllocated()) + return 0; + + size_t ret_val = int_result.GetInteger(); + + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + if (argc.count == 1) + ret_val = std::min(ret_val, static_cast(max)); + + return ret_val; +} + +SWIGEXPORT PyObject* +LLDBSwigPython_GetChildAtIndex +( + PyObject *implementor, + uint32_t idx +) +{ + using namespace lldb_private; + PyErr_Cleaner py_err_cleaner(true); + + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName("get_child_at_index"); + + if (!pfunc.IsAllocated()) + return nullptr; + + PythonObject result = pfunc(PythonInteger(idx)); + + if (!result.IsAllocated()) + return nullptr; + + lldb::SBValue* sbvalue_ptr = nullptr; + if (SWIG_ConvertPtr(result.get(), (void**)&sbvalue_ptr, SWIGTYPE_p_lldb__SBValue, 0) == -1) + return nullptr; + + if (sbvalue_ptr == nullptr) + return nullptr; + + return result.release(); +} + +SWIGEXPORT int +LLDBSwigPython_GetIndexOfChildWithName +( + PyObject *implementor, + const char* child_name +) +{ + using namespace lldb_private; + PyErr_Cleaner py_err_cleaner(true); + + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName("get_child_index"); + + if (!pfunc.IsAllocated()) + return UINT32_MAX; + + PythonObject result = pfunc(PythonString(child_name)); + + if (!result.IsAllocated()) + return UINT32_MAX; + + PythonInteger int_result = result.AsType(); + if (!int_result.IsAllocated()) + return UINT32_MAX; + + int64_t retval = int_result.GetInteger(); + if (retval >= 0) + return (uint32_t)retval; + + return UINT32_MAX; +} + +SWIGEXPORT bool +LLDBSwigPython_UpdateSynthProviderInstance +( + PyObject *implementor +) +{ + bool ret_val = false; + + static char callee_name[] = "update"; + + PyObject* py_return = LLDBSwigPython_CallOptionalMember(implementor,callee_name); + + if (py_return == Py_True) + ret_val = true; + + Py_XDECREF(py_return); + + return ret_val; +} + +SWIGEXPORT bool +LLDBSwigPython_MightHaveChildrenSynthProviderInstance +( + PyObject *implementor +) +{ + bool ret_val = false; + + static char callee_name[] = "has_children"; + + PyObject* py_return = LLDBSwigPython_CallOptionalMember(implementor,callee_name, Py_True); + + if (py_return == Py_True) + ret_val = true; + + Py_XDECREF(py_return); + + return ret_val; +} + +SWIGEXPORT PyObject* +LLDBSwigPython_GetValueSynthProviderInstance +( + PyObject *implementor +) +{ + PyObject* ret_val = nullptr; + + static char callee_name[] = "get_value"; + + PyObject* py_return = LLDBSwigPython_CallOptionalMember(implementor,callee_name, Py_None); + + if (py_return == Py_None || py_return == nullptr) + ret_val = nullptr; + + lldb::SBValue* sbvalue_ptr = NULL; + + if (SWIG_ConvertPtr(py_return, (void**)&sbvalue_ptr, SWIGTYPE_p_lldb__SBValue, 0) == -1) + ret_val = nullptr; + else if (sbvalue_ptr == NULL) + ret_val = nullptr; + else + ret_val = py_return; + + Py_XDECREF(py_return); + return ret_val; +} + +SWIGEXPORT void* +LLDBSWIGPython_CastPyObjectToSBValue +( + PyObject* data +) +{ + lldb::SBValue* sb_ptr = NULL; + + int valid_cast = SWIG_ConvertPtr(data, (void**)&sb_ptr, SWIGTYPE_p_lldb__SBValue, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + +// Currently, SBCommandReturnObjectReleaser wraps a unique pointer to an +// lldb_private::CommandReturnObject. This means that the destructor for the +// SB object will deallocate its contained CommandReturnObject. Because that +// object is used as the real return object for Python-based commands, we want +// it to stay around. Thus, we release the unique pointer before returning from +// LLDBSwigPythonCallCommand, and to guarantee that the release will occur no +// matter how we exit from the function, we have a releaser object whose +// destructor does the right thing for us +class SBCommandReturnObjectReleaser +{ +public: + SBCommandReturnObjectReleaser (lldb::SBCommandReturnObject &obj) : + m_command_return_object_ref (obj) + { + } + + ~SBCommandReturnObjectReleaser () + { + m_command_return_object_ref.Release(); + } +private: + lldb::SBCommandReturnObject &m_command_return_object_ref; +}; + +SWIGEXPORT bool +LLDBSwigPythonCallCommand +( + const char *python_function_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger, + const char* args, + lldb_private::CommandReturnObject& cmd_retobj, + lldb::ExecutionContextRefSP exe_ctx_ref_sp +) +{ + using namespace lldb_private; + lldb::SBCommandReturnObject cmd_retobj_sb(&cmd_retobj); + SBCommandReturnObjectReleaser cmd_retobj_sb_releaser(cmd_retobj_sb); + lldb::SBDebugger debugger_sb(debugger); + lldb::SBExecutionContext exe_ctx_sb(exe_ctx_ref_sp); + + PyErr_Cleaner py_err_cleaner(true); + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return false; + + // pass the pointer-to cmd_retobj_sb or watch the underlying object disappear from under you + // see comment above for SBCommandReturnObjectReleaser for further details + auto argc = pfunc.GetNumArguments(); + PythonObject debugger_arg(PyRefType::Owned, SBTypeToSWIGWrapper(debugger_sb)); + PythonObject exe_ctx_arg(PyRefType::Owned, SBTypeToSWIGWrapper(exe_ctx_sb)); + PythonObject cmd_retobj_arg(PyRefType::Owned, SBTypeToSWIGWrapper(&cmd_retobj_sb)); + + if (argc.count == 5 || argc.has_varargs) + pfunc(debugger_arg, PythonString(args), exe_ctx_arg, cmd_retobj_arg, dict); + else + pfunc(debugger_arg, PythonString(args), cmd_retobj_arg, dict); + + return true; +} + +SWIGEXPORT bool +LLDBSwigPythonCallCommandObject +( + PyObject *implementor, + lldb::DebuggerSP& debugger, + const char* args, + lldb_private::CommandReturnObject& cmd_retobj, + lldb::ExecutionContextRefSP exe_ctx_ref_sp +) +{ + using namespace lldb_private; + lldb::SBCommandReturnObject cmd_retobj_sb(&cmd_retobj); + SBCommandReturnObjectReleaser cmd_retobj_sb_releaser(cmd_retobj_sb); + lldb::SBDebugger debugger_sb(debugger); + lldb::SBExecutionContext exe_ctx_sb(exe_ctx_ref_sp); + + PyErr_Cleaner py_err_cleaner(true); + + PythonObject self(PyRefType::Borrowed, implementor); + auto pfunc = self.ResolveName("__call__"); + + if (!pfunc.IsAllocated()) + return false; + + // pass the pointer-to cmd_retobj_sb or watch the underlying object disappear from under you + // see comment above for SBCommandReturnObjectReleaser for further details + PythonObject debugger_arg(PyRefType::Owned, SBTypeToSWIGWrapper(debugger_sb)); + PythonObject exe_ctx_arg(PyRefType::Owned, SBTypeToSWIGWrapper(exe_ctx_sb)); + PythonObject cmd_retobj_arg(PyRefType::Owned, SBTypeToSWIGWrapper(&cmd_retobj_sb)); + + pfunc(debugger_arg, PythonString(args), exe_ctx_arg, cmd_retobj_arg); + + return true; +} + +SWIGEXPORT void* +LLDBSWIGPythonCreateOSPlugin +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::ProcessSP& process_sp +) +{ + using namespace lldb_private; + + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_class_name, dict); + + if (!pfunc.IsAllocated()) + Py_RETURN_NONE; + + // I do not want the SBProcess to be deallocated when going out of scope because python + // has ownership of it and will manage memory for this object by itself + lldb::SBProcess *process_sb = new lldb::SBProcess(process_sp); + PythonObject process_arg(PyRefType::Owned, SBTypeToSWIGWrapper(process_sb)); + if (!process_arg.IsAllocated()) + Py_RETURN_NONE; + + auto result = pfunc(process_arg); + + if (result.IsAllocated()) + return result.release(); + + Py_RETURN_NONE; +} + +SWIGEXPORT void* +LLDBSWIGPython_GetDynamicSetting (void* module, const char* setting, const lldb::TargetSP& target_sp) +{ + using namespace lldb_private; + + if (!module || !setting) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + PythonObject py_module(PyRefType::Borrowed, (PyObject *)module); + auto pfunc = py_module.ResolveName("get_dynamic_setting"); + + if (!pfunc.IsAllocated()) + Py_RETURN_NONE; + + lldb::SBTarget target_sb(target_sp); + PythonObject target_arg(PyRefType::Owned, SBTypeToSWIGWrapper(target_sb)); + auto result = pfunc(target_arg, PythonString(setting)); + + return result.release(); +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordProcess +(const char* python_function_name, +const char* session_dictionary_name, +lldb::ProcessSP& process, +std::string& output) + +{ + using namespace lldb_private; + + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBProcess process_sb(process); + PythonObject process_arg(PyRefType::Owned, SBTypeToSWIGWrapper(process_sb)); + auto result = pfunc(process_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordThread +(const char* python_function_name, +const char* session_dictionary_name, +lldb::ThreadSP& thread, +std::string& output) + +{ + using namespace lldb_private; + + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBThread thread_sb(thread); + PythonObject thread_arg(PyRefType::Owned, SBTypeToSWIGWrapper(thread_sb)); + auto result = pfunc(thread_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordTarget +(const char* python_function_name, +const char* session_dictionary_name, +lldb::TargetSP& target, +std::string& output) + +{ + using namespace lldb_private; + + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name,dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBTarget target_sb(target); + PythonObject target_arg(PyRefType::Owned, SBTypeToSWIGWrapper(target_sb)); + auto result = pfunc(target_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordFrame +(const char* python_function_name, +const char* session_dictionary_name, +lldb::StackFrameSP& frame, +std::string& output) + +{ + using namespace lldb_private; + + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name,dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBFrame frame_sb(frame); + PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(frame_sb)); + auto result = pfunc(frame_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSWIGPythonRunScriptKeywordValue +(const char* python_function_name, +const char* session_dictionary_name, +lldb::ValueObjectSP& value, +std::string& output) + +{ + using namespace lldb_private; + + if (python_function_name == NULL || python_function_name[0] == '\0' || !session_dictionary_name) + return false; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); + + if (!pfunc.IsAllocated()) + return false; + + lldb::SBValue value_sb(value); + PythonObject value_arg(PyRefType::Owned, SBTypeToSWIGWrapper(value_sb)); + auto result = pfunc(value_arg, dict); + + output = result.Str().GetString().str(); + + return true; +} + +SWIGEXPORT bool +LLDBSwigPythonCallModuleInit +( + const char *python_module_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger +) +{ + using namespace lldb_private; + + std::string python_function_name_string = python_module_name; + python_function_name_string += ".__lldb_init_module"; + const char* python_function_name = python_function_name_string.c_str(); + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); + + // This method is optional and need not exist. So if we don't find it, + // it's actually a success, not a failure. + if (!pfunc.IsAllocated()) + return true; + + lldb::SBDebugger debugger_sb(debugger); + PythonObject debugger_arg(PyRefType::Owned, SBTypeToSWIGWrapper(debugger_sb)); + pfunc(debugger_arg, dict); + + return true; +} +%} + + +%runtime %{ +// Forward declaration to be inserted at the start of LLDBWrapPython.h +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBValue.h" + +SWIGEXPORT lldb::ValueObjectSP +LLDBSWIGPython_GetValueObjectSPFromSBValue (void* data) +{ + lldb::ValueObjectSP valobj_sp; + if (data) + { + lldb::SBValue* sb_ptr = (lldb::SBValue *)data; + valobj_sp = sb_ptr->GetSP(); + } + return valobj_sp; +} + +#ifdef __cplusplus +extern "C" { +#endif + +void LLDBSwigPythonCallPythonLogOutputCallback(const char *str, void *baton); + +#ifdef __cplusplus +} +#endif +%} + +%wrapper %{ + + +// For the LogOutputCallback functions +void LLDBSwigPythonCallPythonLogOutputCallback(const char *str, void *baton) { + if (baton != Py_None) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyObject_CallFunction(reinterpret_cast(baton), const_cast("s"), str); + SWIG_PYTHON_THREAD_END_BLOCK; + } +} +%} diff --git a/scripts/Python/remote-build.py b/scripts/Python/remote-build.py new file mode 100755 index 000000000000..72986a0bf8fe --- /dev/null +++ b/scripts/Python/remote-build.py @@ -0,0 +1,300 @@ +#!/usr/bin/python + +from __future__ import print_function + +import argparse +import getpass +import os +import os.path +import re +import select +import sys +import subprocess + +_COMMON_SYNC_OPTS = "-avzh --delete" +_COMMON_EXCLUDE_OPTS = "--exclude=DerivedData --exclude=.svn --exclude=.git --exclude=llvm-build/Release+Asserts" + +def normalize_configuration(config_text): + if not config_text: + return "debug" + + config_lower = config_text.lower() + if config_lower in ["debug", "release"]: + return config_lower + else: + raise Exception("unknown configuration specified: %s" % config_text) + +def parse_args(): + DEFAULT_REMOTE_ROOT_DIR = "/mnt/ssd/work/macosx.sync" + DEFAULT_REMOTE_HOSTNAME = "tfiala2.mtv.corp.google.com" + OPTIONS_FILENAME = ".remote-build.conf" + DEFAULT_SSH_PORT = "22" + + parser = argparse.ArgumentParser(fromfile_prefix_chars='@') + + parser.add_argument( + "--configuration", "-c", + help="specify configuration (Debug, Release)", + default=normalize_configuration(os.environ.get('CONFIGURATION', 'Debug'))) + parser.add_argument( + "--debug", "-d", + action="store_true", + help="help debug the remote-build script by adding extra logging") + parser.add_argument( + "--local-lldb-dir", "-l", metavar="DIR", + help="specify local lldb directory (Xcode layout assumed for llvm/clang)", + default=os.getcwd()) + parser.add_argument( + "--port", "-p", + help="specify the port ssh should use to connect to the remote side", + default=DEFAULT_SSH_PORT) + parser.add_argument( + "--remote-address", "-r", metavar="REMOTE-ADDR", + help="specify the dns name or ip address of the remote linux system", + default=DEFAULT_REMOTE_HOSTNAME) + parser.add_argument( + "--remote-dir", metavar="DIR", + help="specify the root of the linux source/build dir", + default=DEFAULT_REMOTE_ROOT_DIR) + parser.add_argument( + "--user", "-u", help="specify the user name for the remote system", + default=getpass.getuser()) + parser.add_argument( + "--xcode-action", "-x", help="$(ACTION) from Xcode", nargs='?', default=None) + + command_line_args = sys.argv[1:] + if os.path.exists(OPTIONS_FILENAME): + # Prepend the file so that command line args override the file contents. + command_line_args.insert(0, "@%s" % OPTIONS_FILENAME) + + return parser.parse_args(command_line_args) + + +def maybe_create_remote_root_dir(args): + commandline = [ + "ssh", + "-p", args.port, + "%s@%s" % (args.user, args.remote_address), + "mkdir", + "-p", + args.remote_dir] + print("create remote root dir command:\n{}".format(commandline)) + return subprocess.call(commandline) + + +def init_with_args(args): + # Expand any user directory specs in local-side source dir (on MacOSX). + args.local_lldb_dir = os.path.expanduser(args.local_lldb_dir) + + # Append the configuration type to the remote build dir. + args.configuration = normalize_configuration(args.configuration) + args.remote_build_dir = os.path.join( + args.remote_dir, + "build-%s" % args.configuration) + + # We assume the local lldb directory is really named 'lldb'. + # This is because on the remote end, the local lldb root dir + # is copied over underneath llvm/tools and will be named there + # whatever it is named locally. The remote build will assume + # is is called lldb. + if os.path.basename(args.local_lldb_dir) != 'lldb': + raise Exception( + "local lldb root needs to be called 'lldb' but was {} instead" + .format(os.path.basename(args.local_lldb_dir))) + + args.lldb_dir_relative_regex = re.compile("%s/llvm/tools/lldb/" % args.remote_dir) + args.llvm_dir_relative_regex = re.compile("%s/" % args.remote_dir) + + print("Xcode action:", args.xcode_action) + + # Ensure the remote directory exists. + result = maybe_create_remote_root_dir(args) + if result == 0: + print("using remote root dir: %s" % args.remote_dir) + else: + print("remote root dir doesn't exist and could not be created, " + + "error code:", result) + return False + + return True + +def sync_llvm(args): + commandline = ["rsync"] + commandline.extend(_COMMON_SYNC_OPTS.split()) + commandline.extend(_COMMON_EXCLUDE_OPTS.split()) + commandline.append("--exclude=/llvm/tools/lldb") + commandline.extend(["-e", "ssh -p {}".format(args.port)]) + commandline.extend([ + "%s/llvm" % args.local_lldb_dir, + "%s@%s:%s" % (args.user, args.remote_address, args.remote_dir)]) + if args.debug: + print("going to execute llvm sync: {}".format(commandline)) + return subprocess.call(commandline) + + +def sync_lldb(args): + commandline = ["rsync"] + commandline.extend(_COMMON_SYNC_OPTS.split()) + commandline.extend(_COMMON_EXCLUDE_OPTS.split()) + commandline.append("--exclude=/lldb/llvm") + commandline.extend(["-e", "ssh -p {}".format(args.port)]) + commandline.extend([ + args.local_lldb_dir, + "%s@%s:%s/llvm/tools" % (args.user, args.remote_address, args.remote_dir)]) + if args.debug: + print("going to execute lldb sync: {}".format(commandline)) + return subprocess.call(commandline) + + +def build_cmake_command(args): + # args.remote_build_dir + # args.configuration in ('release', 'debug') + + if args.configuration == 'debug-optimized': + build_type_name = "RelWithDebInfo" + elif args.configuration == 'release': + build_type_name = "Release" + else: + build_type_name = "Debug" + + ld_flags = "\"-lstdc++ -lm\"" + + install_dir = os.path.join( + args.remote_build_dir, "..", "install-{}".format(args.configuration)) + + command_line = [ + "cmake", + "-GNinja", + "-DCMAKE_CXX_COMPILER=clang", + "-DCMAKE_C_COMPILER=clang", + # "-DCMAKE_CXX_FLAGS=%s" % cxx_flags, + "-DCMAKE_SHARED_LINKER_FLAGS=%s" % ld_flags, + "-DCMAKE_EXE_LINKER_FLAGS=%s" % ld_flags, + "-DCMAKE_INSTALL_PREFIX:PATH=%s" % install_dir, + "-DCMAKE_BUILD_TYPE=%s" % build_type_name, + "-Wno-dev", + os.path.join("..", "llvm") + ] + + return command_line + + +def maybe_configure(args): + commandline = [ + "ssh", + "-p", args.port, + "%s@%s" % (args.user, args.remote_address), + "cd", args.remote_dir, "&&", + "mkdir", "-p", args.remote_build_dir, "&&", + "cd", args.remote_build_dir, "&&" + ] + commandline.extend(build_cmake_command(args)) + + if args.debug: + print("configure command: {}".format(commandline)) + + return subprocess.call(commandline) + + +def filter_build_line(args, line): + lldb_relative_line = args.lldb_dir_relative_regex.sub('', line) + if len(lldb_relative_line) != len(line): + # We substituted - return the modified line + return lldb_relative_line + + # No match on lldb path (longer on linux than llvm path). Try + # the llvm path match. + return args.llvm_dir_relative_regex.sub('', line) + + +def run_remote_build_command(args, build_command_list): + commandline = [ + "ssh", + "-p", args.port, + "%s@%s" % (args.user, args.remote_address), + "cd", args.remote_build_dir, "&&"] + commandline.extend(build_command_list) + + if args.debug: + print("running remote build command: {}".format(commandline)) + + proc = subprocess.Popen( + commandline, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + # Filter stdout/stderr output for file path mapping. + # We do this to enable Xcode to see filenames relative to the + # MacOSX-side directory structure. + while True: + reads = [proc.stdout.fileno(), proc.stderr.fileno()] + select_result = select.select(reads, [], []) + + for fd in select_result[0]: + if fd == proc.stdout.fileno(): + line = proc.stdout.readline() + display_line = filter_build_line(args, line.rstrip()) + if display_line and len(display_line) > 0: + print(display_line) + elif fd == proc.stderr.fileno(): + line = proc.stderr.readline() + display_line = filter_build_line(args, line.rstrip()) + if display_line and len(display_line) > 0: + print(display_line, file=sys.stderr) + + proc_retval = proc.poll() + if proc_retval != None: + # Process stopped. Drain output before finishing up. + + # Drain stdout. + while True: + line = proc.stdout.readline() + if line: + display_line = filter_build_line(args, line.rstrip()) + if display_line and len(display_line) > 0: + print(display_line) + else: + break + + # Drain stderr. + while True: + line = proc.stderr.readline() + if line: + display_line = filter_build_line(args, line.rstrip()) + if display_line and len(display_line) > 0: + print(display_line, file=sys.stderr) + else: + break + + return proc_retval + + +def build(args): + return run_remote_build_command(args, ["time", "ninja"]) + + +def clean(args): + return run_remote_build_command(args, ["ninja", "clean"]) + + +if __name__ == "__main__": + # Handle arg parsing. + args = parse_args() + + # Initialize the system. + if not init_with_args(args): + exit(1) + + # Sync over llvm and clang source. + sync_llvm(args) + + # Sync over lldb source. + sync_lldb(args) + + # Configure the remote build if it's not already. + maybe_configure(args) + + if args.xcode_action == 'clean': + exit(clean(args)) + else: + exit(build(args)) diff --git a/scripts/Python/use_lldb_suite.py b/scripts/Python/use_lldb_suite.py new file mode 100644 index 000000000000..63a098cea220 --- /dev/null +++ b/scripts/Python/use_lldb_suite.py @@ -0,0 +1,22 @@ +import inspect +import os +import sys + +def find_lldb_root(): + lldb_root = os.path.dirname(inspect.getfile(inspect.currentframe())) + while True: + lldb_root = os.path.dirname(lldb_root) + if lldb_root is None: + return None + + test_path = os.path.join(lldb_root, "use_lldb_suite_root.py") + if os.path.isfile(test_path): + return lldb_root + return None + +lldb_root = find_lldb_root() +if lldb_root is not None: + import imp + module = imp.find_module("use_lldb_suite_root", [lldb_root]) + if module is not None: + imp.load_module("use_lldb_suite_root", *module) diff --git a/scripts/build-lldb-llvm-clang b/scripts/build-lldb-llvm-clang new file mode 100755 index 000000000000..822e9944bf6a --- /dev/null +++ b/scripts/build-lldb-llvm-clang @@ -0,0 +1,74 @@ +#!/bin/sh -x + +# Usage: +# build-lldb-llvm-clang [Debug|Release|BuildAndIntegration] +# build-lldb-llvm-clang [Debug|Release|BuildAndIntegration] + +LLVM_REVISION=$1 +CLANG_REVISION=$2 +LLVM_CONFIGURATION=$3 + +if [ "$LLVM_REVISION" = "" ]; then + echo "Usage:\n build-lldb-llvm-clang [ Debug|Release||BuildAndIntegration]" + exit 1 +fi + +if [ "$CLANG_REVISION" = "" ]; then + $CLANG_REVISION = $LLVM_REVISION +fi + +# Checkout LLVM +svn co -q -r $LLVM_REVISION http://llvm.org/svn/llvm-project/llvm/trunk llvm + +# change directory to "./llvm" +cd llvm + +# Checkout Clang +# change directory to "./llvm/tools" +cd tools +svn co -q -r $CLANG_REVISION http://llvm.org/svn/llvm-project/cfe/trunk clang + +# change directory to "./llvm" +cd .. +for patch_file in ../scripts/llvm.*.diff +do + echo "Applying patch from '$patch_file'" + patch -p0 < "$patch_file" +done + +# change directory to "./llvm/tools/clang" +cd tools/clang +for patch_file in ../../../scripts/clang.*.diff +do + echo "Applying patch from '$patch_file'" + patch -p0 < "$patch_file" +done + +# change directory to "./" +cd ../../.. +pwd + +if [ "$LLVM_CONFIGURATION" = "Debug" ]; then + # Configure "Debug+Asserts" build + mkdir llvm-debug + cd llvm-debug + ../llvm/configure --enable-targets=x86_64,arm + make -j8 clang-only VERBOSE=1 PROJECT_NAME='llvm' + make -j8 tools-only VERBOSE=1 PROJECT_NAME='llvm' EDIS_VERSION=1 +elif [ "$LLVM_CONFIGURATION" = "Release" ]; then + # Configure "Release" build + mkdir llvm-release + cd llvm-release + ../llvm/configure --enable-targets=x86_64,arm --enable-optimized --disable-assertions + make -j8 clang-only VERBOSE=1 PROJECT_NAME='llvm' + make -j8 tools-only VERBOSE=1 PROJECT_NAME='llvm' EDIS_VERSION=1 +elif [ "$LLVM_CONFIGURATION" = "BuildAndIntegration" ]; then + # Don't configure or build for "BuildAndIntegration", this configuration + # is a preparation step for a build submission + + # Remove all patches, and the llvm and clang "test" directories + rm -rf ./scripts/*.diff ./llvm/test ./llvm/tools/clang/test +else + echo "checked out llvm (revision $LLVM_REVISION) and clang (revision $CLANG_REVISION)." + exit 0 +fi diff --git a/scripts/build-llvm.pl b/scripts/build-llvm.pl new file mode 100644 index 000000000000..f7a60d8d3d3e --- /dev/null +++ b/scripts/build-llvm.pl @@ -0,0 +1,407 @@ +#!/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 Cwd 'abs_path'; +use File::Basename; +use File::Glob ':glob'; +use File::Slurp; +use List::Util qw[min max]; +use Digest::MD5 qw(md5_hex); + +our $llvm_srcroot = $ENV{SCRIPT_INPUT_FILE_0}; +our $llvm_dstroot = $ENV{SCRIPT_INPUT_FILE_1}; +our $archive_filelist_file = $ENV{SCRIPT_INPUT_FILE_2}; + +our $llvm_configuration = $ENV{LLVM_CONFIGURATION}; + +our $llvm_revision = "HEAD"; +our $clang_revision = "HEAD"; +our $compiler_rt_revision = "HEAD"; + +our $SRCROOT = "$ENV{SRCROOT}"; +our @archs = split (/\s+/, $ENV{ARCHS}); +my $os_release = 11; + +my $original_env_path = $ENV{PATH}; + +my $common_configure_options = "--disable-terminfo"; + +our %llvm_config_info = ( + 'Debug' => { configure_options => '--disable-optimized --disable-assertions --enable-cxx11 --enable-libcpp', make_options => 'DEBUG_SYMBOLS=1'}, + 'Debug+Asserts' => { configure_options => '--disable-optimized --enable-assertions --enable-cxx11 --enable-libcpp' , make_options => 'DEBUG_SYMBOLS=1'}, + 'Release' => { configure_options => '--enable-optimized --disable-assertions --enable-cxx11 --enable-libcpp' , make_options => ''}, + 'Release+Debug' => { configure_options => '--enable-optimized --disable-assertions --enable-cxx11 --enable-libcpp' , make_options => 'DEBUG_SYMBOLS=1'}, + 'Release+Asserts' => { configure_options => '--enable-optimized --enable-assertions --enable-cxx11 --enable-libcpp' , make_options => ''}, +); + +our $llvm_config_href = undef; +if (exists $llvm_config_info{"$llvm_configuration"}) +{ + $llvm_config_href = $llvm_config_info{$llvm_configuration}; +} +else +{ + die "Unsupported LLVM configuration: '$llvm_configuration'\n"; +} +our @llvm_repositories = ( + abs_path("$llvm_srcroot"), + abs_path("$llvm_srcroot/tools/clang"), +# abs_path("$llvm_srcroot/projects/compiler-rt") +); + +if (-e "$llvm_srcroot/lib") +{ + print "Using existing llvm sources in: '$llvm_srcroot'\n"; + print "Using standard LLVM build directory:\n SRC = '$llvm_srcroot'\n DST = '$llvm_dstroot'\n"; +} +else +{ + print "Checking out llvm sources from revision $llvm_revision...\n"; + do_command ("cd '$SRCROOT' && svn co --quiet --revision $llvm_revision http://llvm.org/svn/llvm-project/llvm/trunk llvm", "checking out llvm from repository", 1); + print "Checking out clang sources from revision $clang_revision...\n"; + do_command ("cd '$llvm_srcroot/tools' && svn co --quiet --revision $clang_revision http://llvm.org/svn/llvm-project/cfe/trunk clang", "checking out clang from repository", 1); +# print "Checking out compiler-rt sources from revision $compiler_rt_revision...\n"; +# do_command ("cd '$llvm_srcroot/projects' && svn co --quiet --revision $compiler_rt_revision http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt", "checking out compiler-rt from repository", 1); + print "Applying any local patches to LLVM/Clang..."; + + my @llvm_patches = bsd_glob("$ENV{SRCROOT}/scripts/llvm.*.diff"); + foreach my $patch (@llvm_patches) + { + do_command ("cd '$llvm_srcroot' && patch -p0 < $patch"); + } + + my @clang_patches = bsd_glob("$ENV{SRCROOT}/scripts/clang.*.diff"); + foreach my $patch (@clang_patches) + { + do_command ("cd '$llvm_srcroot/tools/clang' && patch -p0 < $patch"); + } + +# my @compiler_rt_patches = bsd_glob("$ENV{SRCROOT}/scripts/compiler-rt.*.diff"); +# foreach my $patch (@compiler_rt_patches) +# { +# do_command ("cd '$llvm_srcroot/projects/compiler-rt' && patch -p0 < $patch"); +# } +} + +# Get our options + +our $debug = 1; + +sub parallel_guess +{ + my $cpus = `sysctl -n hw.ncpu`; + 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"; + # Make the llvm build directory + my $arch_idx = 0; + + # Calculate if the current source digest so we can compare it to each architecture + # build folder + my @llvm_md5_strings; + foreach my $repo (@llvm_repositories) + { + if (-d "$repo/.svn") + { + push(@llvm_md5_strings, `cd '$repo'; svn info`); + push(@llvm_md5_strings, `cd '$repo'; svn diff`); + } + elsif (-d "$repo/.git") + { + push(@llvm_md5_strings, `cd '$repo'; git branch -v`); + push(@llvm_md5_strings, `cd '$repo'; git diff`); + } + } + + # open my $md5_data_file, '>', "/tmp/a.txt" or die "Can't open $! for writing...\n"; + # foreach my $md5_string (@llvm_md5_strings) + # { + # print $md5_data_file $md5_string; + # } + # close ($md5_data_file); + + #print "LLVM MD5 will be generated from:\n"; + #print @llvm_md5_strings; + my $llvm_hex_digest = md5_hex(@llvm_md5_strings); + my $did_make = 0; + + #print "llvm MD5: $llvm_hex_digest\n"; + + my @archive_dirs; + + 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 $is_arm = $arch =~ /^arm/; + my $save_arch_digest = 1; + my $arch_digest_file = "$llvm_dstroot_arch/md5"; + my $llvm_dstroot_arch_archive_dir = "$llvm_dstroot_arch/$llvm_configuration/lib"; + + push @archive_dirs, $llvm_dstroot_arch_archive_dir; + + 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"; + + my @archive_modtimes; + if ($do_make == 0) + { + if (-e $arch_digest_file) + { + my $arch_hex_digest = read_file($arch_digest_file); + if ($arch_hex_digest eq $llvm_hex_digest) + { + # No sources have been changed or updated + $save_arch_digest = 0; + } + else + { + # Sources have changed, or svn has been updated + print "Sources have changed, rebuilding...\n"; + $do_make = 1; + } + } + else + { + # No MD5 digest, we need to make + print "Missing MD5 digest file '$arch_digest_file', rebuilding...\n"; + $do_make = 1; + } + + if ($do_make == 0) + { + if (-e $archive_filelist_file) + { + # the final archive exists, check the modification times on all .a files that + # make the final archive to make sure we don't need to rebuild + my $archive_filelist_file_modtime = (stat($archive_filelist_file))[9]; + + our @archive_files = glob "$llvm_dstroot_arch_archive_dir/*.a"; + + for my $llvm_lib (@archive_files) + { + if (-e $llvm_lib) + { + if ($archive_filelist_file_modtime < (stat($llvm_lib))[9]) + { + print "'$llvm_dstroot_arch/$llvm_lib' is newer than '$archive_filelist_file', rebuilding...\n"; + $do_make = 1; + last; + } + } + } + } + else + { + $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 ($is_arm) + { + my $llvm_dstroot_arch_bin = "${llvm_dstroot_arch}/bin"; + if (!-d $llvm_dstroot_arch_bin) + { + do_command ("mkdir -p '$llvm_dstroot_arch_bin'", "making llvm build arch bin directory '$llvm_dstroot_arch_bin'", 1); + my @tools = ("ar", "nm", "strip", "lipo", "ld", "as"); + my $script_mode = 0755; + my $prog; + for $prog (@tools) + { + chomp(my $actual_prog_path = `xcrun -sdk '$ENV{SDKROOT}' -find ${prog}`); + symlink($actual_prog_path, "$llvm_dstroot_arch_bin/${prog}"); + my $script_prog_path = "$llvm_dstroot_arch_bin/arm-apple-darwin${os_release}-${prog}"; + open (SCRIPT, ">$script_prog_path") or die "Can't open $! for writing...\n"; + print SCRIPT "#!/bin/sh\nexec '$actual_prog_path' \"\$\@\"\n"; + close (SCRIPT); + chmod($script_mode, $script_prog_path); + } + # Tools that must have the "-arch" and "-sysroot" specified + my @arch_sysroot_tools = ("clang", "clang++", "gcc", "g++"); + for $prog (@arch_sysroot_tools) + { + chomp(my $actual_prog_path = `xcrun -sdk '$ENV{SDKROOT}' -find ${prog}`); + symlink($actual_prog_path, "$llvm_dstroot_arch_bin/${prog}"); + my $script_prog_path = "$llvm_dstroot_arch_bin/arm-apple-darwin${os_release}-${prog}"; + open (SCRIPT, ">$script_prog_path") or die "Can't open $! for writing...\n"; + print SCRIPT "#!/bin/sh\nexec '$actual_prog_path' -arch ${arch} -isysroot '$ENV{SDKROOT}' \"\$\@\"\n"; + close (SCRIPT); + chmod($script_mode, $script_prog_path); + } + my $new_path = "$original_env_path:$llvm_dstroot_arch_bin"; + print "Setting new environment PATH = '$new_path'\n"; + $ENV{PATH} = $new_path; + } + } + } + + if ($save_arch_digest) + { + write_file($arch_digest_file, \$llvm_hex_digest); + } + + if ($do_configure) + { + # Build llvm and clang + print "Configuring clang ($arch) in '$llvm_dstroot_arch'...\n"; + my $lldb_configuration_options = "--enable-targets=x86_64,arm,arm64 $common_configure_options $llvm_config_href->{configure_options}"; + + # We're configuring llvm/clang with --enable-cxx11 and --enable-libcpp but llvm/configure doesn't + # pick up the right C++ standard library. If we have a MACOSX_DEPLOYMENT_TARGET of 10.7 or 10.8 + # (or are using actually building on those releases), we need to specify "-stdlib=libc++" at link + # time or llvm/configure will not see as available and error out (v. llvm r199313). + $ENV{LDFLAGS} = $ENV{LDFLAGS} . " -stdlib=libc++"; + + if ($is_arm) + { + $lldb_configuration_options .= " --host=arm-apple-darwin${os_release} --target=arm-apple-darwin${os_release} --build=i686-apple-darwin${os_release} --program-prefix=\"\""; + } + else + { + $lldb_configuration_options .= " --build=$arch-apple-darwin${os_release}"; + } + if ($is_arm) + { + # Unset "SDKROOT" for ARM builds + do_command ("cd '$llvm_dstroot_arch' && unset SDKROOT && '$llvm_srcroot/configure' $lldb_configuration_options", + "configuring llvm build", 1); + } + else + { + do_command ("cd '$llvm_dstroot_arch' && '$llvm_srcroot/configure' $lldb_configuration_options", + "configuring llvm build", 1); + } + } + + if ($do_make) + { + $did_make = 1; + # Build llvm and clang + my $num_cpus = parallel_guess(); + print "Building clang using $num_cpus cpus ($arch)...\n"; + my $extra_make_flags = ''; + if ($is_arm) + { + $extra_make_flags = "UNIVERSAL=1 UNIVERSAL_ARCH=${arch} UNIVERSAL_SDK_PATH='$ENV{SDKROOT}' SDKROOT="; + } + do_command ("cd '$llvm_dstroot_arch' && make -j$num_cpus clang-only VERBOSE=1 $llvm_config_href->{make_options} PROJECT_NAME='llvm' $extra_make_flags", "making llvm and clang", 1); + do_command ("cd '$llvm_dstroot_arch' && make -j$num_cpus tools-only VERBOSE=1 $llvm_config_href->{make_options} PROJECT_NAME='llvm' $extra_make_flags EDIS_VERSION=1", "making libedis", 1); + + } + + ++$arch_idx; + } + + # If we did any makes update the archive filenames file with any .a files from + # each architectures "lib" folder... + if ($did_make) + { + open my $fh, '>', $archive_filelist_file or die "Can't open $! for writing...\n"; + foreach my $archive_dir (@archive_dirs) + { + append_all_archive_files ($archive_dir, $fh); + } + close($fh); + } +} + +#---------------------------------------------------------------------- +# 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 append_all_archive_files +{ + my $archive_dir = shift; + my $fh = shift; + + our @archive_files = glob "$archive_dir/*.a"; + for my $archive_fullpath (@archive_files) + { + print $fh "$archive_fullpath\n"; + } +} + +build_llvm(); diff --git a/scripts/buildbot.py b/scripts/buildbot.py new file mode 100755 index 000000000000..0c04d9c4be82 --- /dev/null +++ b/scripts/buildbot.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python + +import argparse +import os +import os.path +import shutil +import subprocess +import sys + +class BuildError(Exception): + def __init__(self, + string=None, + path=None, + inferior_error=None): + self.m_string = string + self.m_path = path + self.m_inferior_error = inferior_error + def __str__(self): + if self.m_path and self.m_string: + return "Build error: %s (referring to %s)" % (self.m_string, self.m_path) + if self.m_path: + return "Build error (referring to %s)" % (self.m_path) + if self.m_string: + return "Build error: %s" % (self.m_string) + return "Build error" + +class LLDBBuildBot: + def __init__(self, + build_directory_path, + log_path, + lldb_repository_url="http://llvm.org/svn/llvm-project/lldb/trunk", + llvm_repository_url="http://llvm.org/svn/llvm-project/llvm/trunk", + clang_repository_url="http://llvm.org/svn/llvm-project/cfe/trunk", + revision=None): + self.m_build_directory_path = os.path.abspath(build_directory_path) + self.m_log_path = os.path.abspath(log_path) + self.m_lldb_repository_url = lldb_repository_url + self.m_llvm_repository_url = llvm_repository_url + self.m_clang_repository_url = clang_repository_url + self.m_revision = revision + self.m_log_stream = None + def Setup(self): + if os.path.exists(self.m_build_directory_path): + raise BuildError(string="Build directory exists", path=self.m_build_directory_path) + if os.path.exists(self.m_log_path): + raise BuildError(string="Log file exists", path=self.m_log_path) + self.m_log_stream = open(self.m_log_path, 'w') + os.mkdir(self.m_build_directory_path) + def Checkout(self): + os.chdir(self.m_build_directory_path) + + cmdline_prefix = [] + + if self.m_revision != None: + cmdline_prefix = ["svn", "-r %s" % (self.m_revision), "co"] + else: + cmdline_prefix = ["svn", "co"] + + returncode = subprocess.call(cmdline_prefix + [self.m_lldb_repository_url, "lldb"], + stdout=self.m_log_stream, + stderr=self.m_log_stream) + if returncode != 0: + raise BuildError(string="Couldn't checkout LLDB") + + os.chdir("lldb") + + returncode = subprocess.call(cmdline_prefix + [self.m_llvm_repository_url, "llvm.checkout"], + stdout=self.m_log_stream, + stderr=self.m_log_stream) + + if returncode != 0: + raise BuildError(string="Couldn't checkout LLVM") + + os.symlink("llvm.checkout", "llvm") + + os.chdir("llvm/tools") + + returncode = subprocess.call(cmdline_prefix + [self.m_clang_repository_url, "clang"], + stdout=self.m_log_stream, + stderr=self.m_log_stream) + + if returncode != 0: + raise BuildError(string="Couldn't checkout Clang") + def Build(self): + os.chdir(self.m_build_directory_path) + os.chdir("lldb/llvm") + + returncode = subprocess.call(["./configure", "--disable-optimized", "--enable-assertions", "--enable-targets=x86,x86_64,arm"], + stdout=self.m_log_stream, + stderr=self.m_log_stream) + + if returncode != 0: + raise BuildError(string="Couldn't configure LLVM/Clang") + + returncode = subprocess.call(["make"], + stdout=self.m_log_stream, + stderr=self.m_log_stream) + + if returncode != 0: + raise BuildError(string="Couldn't build LLVM/Clang") + + os.chdir(self.m_build_directory_path) + os.chdir("lldb") + + returncode = subprocess.call(["xcodebuild", + "-project", "lldb.xcodeproj", + "-target", "lldb-tool", + "-configuration", "Debug", + "-arch", "x86_64", + "LLVM_CONFIGURATION=Debug+Asserts", + "OBJROOT=build"], + stdout=self.m_log_stream, + stderr=self.m_log_stream) + + if returncode != 0: + raise BuildError(string="Couldn't build LLDB") + def Test(self): + os.chdir(self.m_build_directory_path) + os.chdir("lldb/test") + + returncode = subprocess.call(["./dotest.py", "-t"], + stdout=self.m_log_stream, + stderr=self.m_log_stream) + def Takedown(self): + os.chdir("/tmp") + self.m_log_stream.close() + shutil.rmtree(self.m_build_directory_path) + def Run(self): + self.Setup() + self.Checkout() + self.Build() + #self.Test() + self.Takedown() + +def GetArgParser(): + parser = argparse.ArgumentParser(description="Try to build LLDB/LLVM/Clang and run the full test suite.") + parser.add_argument("--build-path", "-b", required=True, help="A (nonexistent) path to put temporary build products into", metavar="path") + parser.add_argument("--log-file", "-l", required=True, help="The name of a (nonexistent) log file", metavar="file") + parser.add_argument("--revision", "-r", required=False, help="The LLVM revision to use", metavar="N") + return parser + +parser = GetArgParser() +arg_dict = vars(parser.parse_args()) + +build_bot = LLDBBuildBot(build_directory_path=arg_dict["build_path"], + log_path=arg_dict["log_file"], + revision=arg_dict["revision"]) + +try: + build_bot.Run() +except BuildError as err: + print err diff --git a/scripts/checkpoint-llvm.pl b/scripts/checkpoint-llvm.pl new file mode 100755 index 000000000000..2372e7e509fa --- /dev/null +++ b/scripts/checkpoint-llvm.pl @@ -0,0 +1,126 @@ +#!/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 repository +# 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; + } +} + +sub do_rsync_paths +{ + while (@_) + { + my $rsync_src = shift @_; + my $rsync_dst = shift @_; + print "rsync_src = '$rsync_src'\n"; + print "rsync_dst = '$rsync_dst'\n"; + + if (!-d $rsync_dst) + { + mkdir $rsync_dst; + } + + 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'"); + } + else + { + die "$rsync_src does not exist!\n"; + } + } +} + +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"; + + 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 @generic_rsync_src_dst_paths = + ( + "$llvm_source_dir/include", "$llvm_checkpoint_dir", + "$llvm_source_dir/tools/clang/include", "$llvm_checkpoint_dir/tools/clang", + ); + + do_rsync_paths (@generic_rsync_src_dst_paths); + + for my $arch (@ARGV) + { + my @specific_rsync_src_dst_paths = + ( + "$llvm_build_dir/$arch/include", "$llvm_checkpoint_dir/$arch", + "$llvm_build_dir/$arch/tools/clang/include", "$llvm_checkpoint_dir/$arch/tools/clang", + ); + + do_rsync_paths (@specific_rsync_src_dst_paths); + + do_command ("cp '$llvm_build_dir/$arch/libllvmclang.a' '$llvm_checkpoint_dir/$arch/libllvmclang.a'", "Copying .a file", 1); + + } + + #do_command ("cp '$llvm_build_dir/libllvmclang.a' '$llvm_checkpoint_dir'", "Copying libllvmclang.a", 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/llvm build/BuildAndIntegration llvm.zip x86_64 i386\n"; +} diff --git a/scripts/disasm-gdb-remote.pl b/scripts/disasm-gdb-remote.pl new file mode 100755 index 000000000000..e4c7066ff21f --- /dev/null +++ b/scripts/disasm-gdb-remote.pl @@ -0,0 +1,2283 @@ +#!/usr/bin/perl + +use strict; + +#---------------------------------------------------------------------- +# Globals +#---------------------------------------------------------------------- +our $unimplemented_str = "UNIMPLEMENTED"; +our $success_str = "OK"; +our $swap = 1; +our $addr_size = 4; +our $thread_suffix_supported = 0; +our $max_bytes_per_line = 32; +our $addr_format = sprintf("0x%%%u.%ux", $addr_size*2, $addr_size*2); +our $pid_format = "%04.4x"; +our $tid_format = "%04.4x"; +our $reg8_href = { extract => \&get8, format => "0x%2.2x" }; +our $reg16_href = { extract => \&get16, format => "0x%4.4x" }; +our $reg32_href = { extract => \&get32, format => "0x%8.8x" }; +our $reg64_href = { extract => \&get64, format => "0x%s" }; +our $reg80_href = { extract => \&get80, format => "0x%s" }; +our $reg128_href = { extract => \&get128, format => "0x%s" }; +our $reg256_href = { extract => \&get256, format => "0x%s" }; +our $float32_href = { extract => \&get32, format => "0x%8.8x" }; +our $float64_href = { extract => \&get64, format => "0x%s" }; +our $float96_href = { extract => \&get96, format => "0x%s" }; +our $curr_cmd = undef; +our $curr_full_cmd = undef; +our %packet_times; +our $curr_time = 0.0; +our $last_time = 0.0; +our $base_time = 0.0; +our $packet_start_time = 0.0; +our $reg_cmd_reg; +our %reg_map = ( + 'i386-gdb' => [ + { name => 'eax', info => $reg32_href }, + { name => 'ecx', info => $reg32_href }, + { name => 'edx', info => $reg32_href }, + { name => 'ebx', info => $reg32_href }, + { name => 'esp', info => $reg32_href }, + { name => 'ebp', info => $reg32_href }, + { name => 'esi', info => $reg32_href }, + { name => 'edi', info => $reg32_href }, + { name => 'eip', info => $reg32_href }, + { name => 'eflags', info => $reg32_href }, + { name => 'cs', info => $reg32_href }, + { name => 'ss', info => $reg32_href }, + { name => 'ds', info => $reg32_href }, + { name => 'es', info => $reg32_href }, + { name => 'fs', info => $reg32_href }, + { name => 'gs', info => $reg32_href }, + { name => 'st0', info => $reg80_href }, + { name => 'st1', info => $reg80_href }, + { name => 'st2', info => $reg80_href }, + { name => 'st3', info => $reg80_href }, + { name => 'st4', info => $reg80_href }, + { name => 'st5', info => $reg80_href }, + { name => 'st6', info => $reg80_href }, + { name => 'st7', info => $reg80_href }, + { name => 'fctrl', info => $reg32_href }, + { name => 'fstat', info => $reg32_href }, + { name => 'ftag', info => $reg32_href }, + { name => 'fiseg', info => $reg32_href }, + { name => 'fioff', info => $reg32_href }, + { name => 'foseg', info => $reg32_href }, + { name => 'fooff', info => $reg32_href }, + { name => 'fop', info => $reg32_href }, + { name => 'xmm0', info => $reg128_href }, + { name => 'xmm1', info => $reg128_href }, + { name => 'xmm2', info => $reg128_href }, + { name => 'xmm3', info => $reg128_href }, + { name => 'xmm4', info => $reg128_href }, + { name => 'xmm5', info => $reg128_href }, + { name => 'xmm6', info => $reg128_href }, + { name => 'xmm7', info => $reg128_href }, + { name => 'mxcsr', info => $reg32_href }, + { name => 'mm0', info => $reg64_href }, + { name => 'mm1', info => $reg64_href }, + { name => 'mm2', info => $reg64_href }, + { name => 'mm3', info => $reg64_href }, + { name => 'mm4', info => $reg64_href }, + { name => 'mm5', info => $reg64_href }, + { name => 'mm6', info => $reg64_href }, + { name => 'mm7', info => $reg64_href }, + ], + + 'i386-lldb' => [ + { name => 'eax', info => $reg32_href }, + { name => 'ebx', info => $reg32_href }, + { name => 'ecx', info => $reg32_href }, + { name => 'edx', info => $reg32_href }, + { name => 'edi', info => $reg32_href }, + { name => 'esi', info => $reg32_href }, + { name => 'ebp', info => $reg32_href }, + { name => 'esp', info => $reg32_href }, + { name => 'ss', info => $reg32_href }, + { name => 'eflags', info => $reg32_href }, + { name => 'eip', info => $reg32_href }, + { name => 'cs', info => $reg32_href }, + { name => 'ds', info => $reg32_href }, + { name => 'es', info => $reg32_href }, + { name => 'fs', info => $reg32_href }, + { name => 'gs', info => $reg32_href }, + { name => 'fctrl', info => $reg16_href }, + { name => 'fstat', info => $reg16_href }, + { name => 'ftag', info => $reg8_href }, + { name => 'fop', info => $reg16_href }, + { name => 'fioff', info => $reg32_href }, + { name => 'fiseg', info => $reg16_href }, + { name => 'fooff', info => $reg32_href }, + { name => 'foseg', info => $reg16_href }, + { name => 'mxcsr', info => $reg32_href }, + { name => 'mxcsrmask', info => $reg32_href }, + { name => 'stmm0', info => $reg80_href }, + { name => 'stmm1', info => $reg80_href }, + { name => 'stmm2', info => $reg80_href }, + { name => 'stmm3', info => $reg80_href }, + { name => 'stmm4', info => $reg80_href }, + { name => 'stmm5', info => $reg80_href }, + { name => 'stmm6', info => $reg80_href }, + { name => 'stmm7', info => $reg80_href }, + { name => 'xmm0', info => $reg128_href }, + { name => 'xmm1', info => $reg128_href }, + { name => 'xmm2', info => $reg128_href }, + { name => 'xmm3', info => $reg128_href }, + { name => 'xmm4', info => $reg128_href }, + { name => 'xmm5', info => $reg128_href }, + { name => 'xmm6', info => $reg128_href }, + { name => 'xmm7', info => $reg128_href }, + { name => 'trapno', info => $reg32_href }, + { name => 'err', info => $reg32_href }, + { name => 'faultvaddr', info => $reg32_href }, + ], + + 'arm-gdb' => [ + { name => 'r0' , info => $reg32_href }, + { name => 'r1' , info => $reg32_href }, + { name => 'r2' , info => $reg32_href }, + { name => 'r3' , info => $reg32_href }, + { name => 'r4' , info => $reg32_href }, + { name => 'r5' , info => $reg32_href }, + { name => 'r6' , info => $reg32_href }, + { name => 'r7' , info => $reg32_href }, + { name => 'r8' , info => $reg32_href }, + { name => 'r9' , info => $reg32_href }, + { name => 'r10' , info => $reg32_href }, + { name => 'r11' , info => $reg32_href }, + { name => 'r12' , info => $reg32_href }, + { name => 'sp' , info => $reg32_href }, + { name => 'lr' , info => $reg32_href }, + { name => 'pc' , info => $reg32_href }, + { name => 'f0' , info => $float96_href }, + { name => 'f1' , info => $float96_href }, + { name => 'f2' , info => $float96_href }, + { name => 'f3' , info => $float96_href }, + { name => 'f4' , info => $float96_href }, + { name => 'f5' , info => $float96_href }, + { name => 'f6' , info => $float96_href }, + { name => 'f7' , info => $float96_href }, + { name => 'fps' , info => $reg32_href }, + { name => 'cpsr' , info => $reg32_href }, + { name => 's0' , info => $float32_href }, + { name => 's1' , info => $float32_href }, + { name => 's2' , info => $float32_href }, + { name => 's3' , info => $float32_href }, + { name => 's4' , info => $float32_href }, + { name => 's5' , info => $float32_href }, + { name => 's6' , info => $float32_href }, + { name => 's7' , info => $float32_href }, + { name => 's8' , info => $float32_href }, + { name => 's9' , info => $float32_href }, + { name => 's10' , info => $float32_href }, + { name => 's11' , info => $float32_href }, + { name => 's12' , info => $float32_href }, + { name => 's13' , info => $float32_href }, + { name => 's14' , info => $float32_href }, + { name => 's15' , info => $float32_href }, + { name => 's16' , info => $float32_href }, + { name => 's17' , info => $float32_href }, + { name => 's18' , info => $float32_href }, + { name => 's19' , info => $float32_href }, + { name => 's20' , info => $float32_href }, + { name => 's21' , info => $float32_href }, + { name => 's22' , info => $float32_href }, + { name => 's23' , info => $float32_href }, + { name => 's24' , info => $float32_href }, + { name => 's25' , info => $float32_href }, + { name => 's26' , info => $float32_href }, + { name => 's27' , info => $float32_href }, + { name => 's28' , info => $float32_href }, + { name => 's29' , info => $float32_href }, + { name => 's30' , info => $float32_href }, + { name => 's31' , info => $float32_href }, + { name => 'fpscr' , info => $reg32_href }, + { name => 'd16' , info => $float64_href }, + { name => 'd17' , info => $float64_href }, + { name => 'd18' , info => $float64_href }, + { name => 'd19' , info => $float64_href }, + { name => 'd20' , info => $float64_href }, + { name => 'd21' , info => $float64_href }, + { name => 'd22' , info => $float64_href }, + { name => 'd23' , info => $float64_href }, + { name => 'd24' , info => $float64_href }, + { name => 'd25' , info => $float64_href }, + { name => 'd26' , info => $float64_href }, + { name => 'd27' , info => $float64_href }, + { name => 'd28' , info => $float64_href }, + { name => 'd29' , info => $float64_href }, + { name => 'd30' , info => $float64_href }, + { name => 'd31' , info => $float64_href }, + ], + + + 'arm-lldb' => [ + { name => 'r0' , info => $reg32_href }, + { name => 'r1' , info => $reg32_href }, + { name => 'r2' , info => $reg32_href }, + { name => 'r3' , info => $reg32_href }, + { name => 'r4' , info => $reg32_href }, + { name => 'r5' , info => $reg32_href }, + { name => 'r6' , info => $reg32_href }, + { name => 'r7' , info => $reg32_href }, + { name => 'r8' , info => $reg32_href }, + { name => 'r9' , info => $reg32_href }, + { name => 'r10' , info => $reg32_href }, + { name => 'r11' , info => $reg32_href }, + { name => 'r12' , info => $reg32_href }, + { name => 'sp' , info => $reg32_href }, + { name => 'lr' , info => $reg32_href }, + { name => 'pc' , info => $reg32_href }, + { name => 'cpsr' , info => $reg32_href }, + { name => 's0' , info => $float32_href }, + { name => 's1' , info => $float32_href }, + { name => 's2' , info => $float32_href }, + { name => 's3' , info => $float32_href }, + { name => 's4' , info => $float32_href }, + { name => 's5' , info => $float32_href }, + { name => 's6' , info => $float32_href }, + { name => 's7' , info => $float32_href }, + { name => 's8' , info => $float32_href }, + { name => 's9' , info => $float32_href }, + { name => 's10' , info => $float32_href }, + { name => 's11' , info => $float32_href }, + { name => 's12' , info => $float32_href }, + { name => 's13' , info => $float32_href }, + { name => 's14' , info => $float32_href }, + { name => 's15' , info => $float32_href }, + { name => 's16' , info => $float32_href }, + { name => 's17' , info => $float32_href }, + { name => 's18' , info => $float32_href }, + { name => 's19' , info => $float32_href }, + { name => 's20' , info => $float32_href }, + { name => 's21' , info => $float32_href }, + { name => 's22' , info => $float32_href }, + { name => 's23' , info => $float32_href }, + { name => 's24' , info => $float32_href }, + { name => 's25' , info => $float32_href }, + { name => 's26' , info => $float32_href }, + { name => 's27' , info => $float32_href }, + { name => 's28' , info => $float32_href }, + { name => 's29' , info => $float32_href }, + { name => 's30' , info => $float32_href }, + { name => 's31' , info => $float32_href }, + { name => 'd0' , info => $float64_href }, + { name => 'd1' , info => $float64_href }, + { name => 'd2' , info => $float64_href }, + { name => 'd3' , info => $float64_href }, + { name => 'd4' , info => $float64_href }, + { name => 'd5' , info => $float64_href }, + { name => 'd6' , info => $float64_href }, + { name => 'd7' , info => $float64_href }, + { name => 'd8' , info => $float64_href }, + { name => 'd9' , info => $float64_href }, + { name => 'd10' , info => $float64_href }, + { name => 'd11' , info => $float64_href }, + { name => 'd12' , info => $float64_href }, + { name => 'd13' , info => $float64_href }, + { name => 'd14' , info => $float64_href }, + { name => 'd15' , info => $float64_href }, + { name => 'd16' , info => $float64_href }, + { name => 'd17' , info => $float64_href }, + { name => 'd18' , info => $float64_href }, + { name => 'd19' , info => $float64_href }, + { name => 'd20' , info => $float64_href }, + { name => 'd21' , info => $float64_href }, + { name => 'd22' , info => $float64_href }, + { name => 'd23' , info => $float64_href }, + { name => 'd24' , info => $float64_href }, + { name => 'd25' , info => $float64_href }, + { name => 'd26' , info => $float64_href }, + { name => 'd27' , info => $float64_href }, + { name => 'd28' , info => $float64_href }, + { name => 'd29' , info => $float64_href }, + { name => 'd30' , info => $float64_href }, + { name => 'd31' , info => $float64_href }, + { name => 'fpscr' , info => $reg32_href }, + { name => 'exc' , info => $reg32_href }, + { name => 'fsr' , info => $reg32_href }, + { name => 'far' , info => $reg32_href }, + ], + + 'x86_64-gdb' => [ + { name => 'rax' , info => $reg64_href }, + { name => 'rbx' , info => $reg64_href }, + { name => 'rcx' , info => $reg64_href }, + { name => 'rdx' , info => $reg64_href }, + { name => 'rsi' , info => $reg64_href }, + { name => 'rdi' , info => $reg64_href }, + { name => 'rbp' , info => $reg64_href }, + { name => 'rsp' , info => $reg64_href }, + { name => 'r8' , info => $reg64_href }, + { name => 'r9' , info => $reg64_href }, + { name => 'r10' , info => $reg64_href }, + { name => 'r11' , info => $reg64_href }, + { name => 'r12' , info => $reg64_href }, + { name => 'r13' , info => $reg64_href }, + { name => 'r14' , info => $reg64_href }, + { name => 'r15' , info => $reg64_href }, + { name => 'rip' , info => $reg64_href }, + { name => 'eflags' , info => $reg32_href }, + { name => 'cs' , info => $reg32_href }, + { name => 'ss' , info => $reg32_href }, + { name => 'ds' , info => $reg32_href }, + { name => 'es' , info => $reg32_href }, + { name => 'fs' , info => $reg32_href }, + { name => 'gs' , info => $reg32_href }, + { name => 'stmm0' , info => $reg80_href }, + { name => 'stmm1' , info => $reg80_href }, + { name => 'stmm2' , info => $reg80_href }, + { name => 'stmm3' , info => $reg80_href }, + { name => 'stmm4' , info => $reg80_href }, + { name => 'stmm5' , info => $reg80_href }, + { name => 'stmm6' , info => $reg80_href }, + { name => 'stmm7' , info => $reg80_href }, + { name => 'fctrl' , info => $reg32_href }, + { name => 'fstat' , info => $reg32_href }, + { name => 'ftag' , info => $reg32_href }, + { name => 'fiseg' , info => $reg32_href }, + { name => 'fioff' , info => $reg32_href }, + { name => 'foseg' , info => $reg32_href }, + { name => 'fooff' , info => $reg32_href }, + { name => 'fop' , info => $reg32_href }, + { name => 'xmm0' , info => $reg128_href }, + { name => 'xmm1' , info => $reg128_href }, + { name => 'xmm2' , info => $reg128_href }, + { name => 'xmm3' , info => $reg128_href }, + { name => 'xmm4' , info => $reg128_href }, + { name => 'xmm5' , info => $reg128_href }, + { name => 'xmm6' , info => $reg128_href }, + { name => 'xmm7' , info => $reg128_href }, + { name => 'xmm8' , info => $reg128_href }, + { name => 'xmm9' , info => $reg128_href }, + { name => 'xmm10' , info => $reg128_href }, + { name => 'xmm11' , info => $reg128_href }, + { name => 'xmm12' , info => $reg128_href }, + { name => 'xmm13' , info => $reg128_href }, + { name => 'xmm14' , info => $reg128_href }, + { name => 'xmm15' , info => $reg128_href }, + { name => 'mxcsr' , info => $reg32_href }, + ], + + 'x86_64-lldb' => [ + { name => 'rax' , info => $reg64_href }, + { name => 'rbx' , info => $reg64_href }, + { name => 'rcx' , info => $reg64_href }, + { name => 'rdx' , info => $reg64_href }, + { name => 'rdi' , info => $reg64_href }, + { name => 'rsi' , info => $reg64_href }, + { name => 'rbp' , info => $reg64_href }, + { name => 'rsp' , info => $reg64_href }, + { name => 'r8 ' , info => $reg64_href }, + { name => 'r9 ' , info => $reg64_href }, + { name => 'r10' , info => $reg64_href }, + { name => 'r11' , info => $reg64_href }, + { name => 'r12' , info => $reg64_href }, + { name => 'r13' , info => $reg64_href }, + { name => 'r14' , info => $reg64_href }, + { name => 'r15' , info => $reg64_href }, + { name => 'rip' , info => $reg64_href }, + { name => 'rflags' , info => $reg64_href }, + { name => 'cs' , info => $reg64_href }, + { name => 'fs' , info => $reg64_href }, + { name => 'gs' , info => $reg64_href }, + { name => 'fctrl' , info => $reg16_href }, + { name => 'fstat' , info => $reg16_href }, + { name => 'ftag' , info => $reg8_href }, + { name => 'fop' , info => $reg16_href }, + { name => 'fioff' , info => $reg32_href }, + { name => 'fiseg' , info => $reg16_href }, + { name => 'fooff' , info => $reg32_href }, + { name => 'foseg' , info => $reg16_href }, + { name => 'mxcsr' , info => $reg32_href }, + { name => 'mxcsrmask' , info => $reg32_href }, + { name => 'stmm0' , info => $reg80_href }, + { name => 'stmm1' , info => $reg80_href }, + { name => 'stmm2' , info => $reg80_href }, + { name => 'stmm3' , info => $reg80_href }, + { name => 'stmm4' , info => $reg80_href }, + { name => 'stmm5' , info => $reg80_href }, + { name => 'stmm6' , info => $reg80_href }, + { name => 'stmm7' , info => $reg80_href }, + { name => 'xmm0' , info => $reg128_href }, + { name => 'xmm1' , info => $reg128_href }, + { name => 'xmm2' , info => $reg128_href }, + { name => 'xmm3' , info => $reg128_href }, + { name => 'xmm4' , info => $reg128_href }, + { name => 'xmm5' , info => $reg128_href }, + { name => 'xmm6' , info => $reg128_href }, + { name => 'xmm7' , info => $reg128_href }, + { name => 'xmm8' , info => $reg128_href }, + { name => 'xmm9' , info => $reg128_href }, + { name => 'xmm10' , info => $reg128_href }, + { name => 'xmm11' , info => $reg128_href }, + { name => 'xmm12' , info => $reg128_href }, + { name => 'xmm13' , info => $reg128_href }, + { name => 'xmm14' , info => $reg128_href }, + { name => 'xmm15' , info => $reg128_href }, + { name => 'trapno' , info => $reg32_href }, + { name => 'err' , info => $reg32_href }, + { name => 'faultvaddr' , info => $reg64_href }, + ] +); + +our $max_register_name_len = 0; +calculate_max_register_name_length(); +our @point_types = ( "software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp" ); +our $opt_v = 0; # verbose +our $opt_g = 0; # debug +our $opt_q = 0; # quiet +our $opt_r = undef; +use Getopt::Std; +getopts('gvqr:'); + +our $registers_aref = undef; + +if (length($opt_r)) +{ + if (exists $reg_map{$opt_r}) + { + $registers_aref = $reg_map{$opt_r}; + } + else + { + die "Can't get registers group for '$opt_r'\n"; + } +} + +sub extract_key_value_pairs +{ + my $kv_href = {}; + my $arrayref = shift; + my $str = join('',@$arrayref); + my @kv_strs = split(/;/, $str); + foreach my $kv_str (@kv_strs) + { + my ($key, $value) = split(/:/, $kv_str); + $kv_href->{$key} = $value; + } + return $kv_href; +} + +sub get_thread_from_thread_suffix +{ + if ($thread_suffix_supported) + { + my $arrayref = shift; + # Skip leading semi-colon if needed + $$arrayref[0] == ';' and shift @$arrayref; + my $thread_href = extract_key_value_pairs ($arrayref); + if (exists $thread_href->{thread}) + { + return $thread_href->{thread}; + } + } + return undef; +} + +sub calculate_max_register_name_length +{ + $max_register_name_len = 7; + foreach my $reg_href (@$registers_aref) + { + my $name_len = length($reg_href->{name}); + if ($max_register_name_len < $name_len) + { + $max_register_name_len = $name_len; + } + } +} +#---------------------------------------------------------------------- +# Hash that maps command characters to the appropriate functions using +# the command character as the key and the value being a reference to +# the dump function for dumping the command itself. +#---------------------------------------------------------------------- +our %cmd_callbacks = +( + '?' => \&dump_last_signal_cmd, + 'H' => \&dump_set_thread_cmd, + 'T' => \&dump_thread_is_alive_cmd, + 'q' => \&dump_general_query_cmd, + 'Q' => \&dump_general_set_cmd, + 'g' => \&dump_read_regs_cmd, + 'G' => \&dump_write_regs_cmd, + 'p' => \&dump_read_single_register_cmd, + 'P' => \&dump_write_single_register_cmd, + 'm' => \&dump_read_mem_cmd, + 'M' => \&dump_write_mem_cmd, + 'X' => \&dump_write_mem_binary_cmd, + 'Z' => \&dump_bp_wp_command, + 'z' => \&dump_bp_wp_command, + 'k' => \&dump_kill_cmd, + 'A' => \&dump_A_command, + 'c' => \&dump_continue_cmd, + 's' => \&dump_continue_cmd, + 'C' => \&dump_continue_with_signal_cmd, + 'S' => \&dump_continue_with_signal_cmd, + '_M' => \&dump_allocate_memory_cmd, + '_m' => \&dump_deallocate_memory_cmd, + # extended commands + 'v' => \&dump_extended_cmd +); + +#---------------------------------------------------------------------- +# Hash that maps command characters to the appropriate functions using +# the command character as the key and the value being a reference to +# the dump function for the response to the command. +#---------------------------------------------------------------------- +our %rsp_callbacks = +( + 'c' => \&dump_stop_reply_packet, + 's' => \&dump_stop_reply_packet, + 'C' => \&dump_stop_reply_packet, + '?' => \&dump_stop_reply_packet, + 'T' => \&dump_thread_is_alive_rsp, + 'H' => \&dump_set_thread_rsp, + 'q' => \&dump_general_query_rsp, + 'g' => \&dump_read_regs_rsp, + 'p' => \&dump_read_single_register_rsp, + 'm' => \&dump_read_mem_rsp, + '_M' => \&dump_allocate_memory_rsp, + + # extended commands + 'v' => \&dump_extended_rsp, +); + + +sub dump_register_value +{ + my $indent = shift; + my $arrayref = shift; + my $reg_num = shift; + + if ($reg_num >= @$registers_aref) + { + printf("\tinvalid register index %d\n", $reg_num); + return; + } + + my $reg_href = $$registers_aref[$reg_num]; + my $reg_name = $reg_href->{name}; + if ($$arrayref[0] eq '#') + { + printf("\t%*s: error: EOS reached when trying to read register %d\n", $max_register_name_len, $reg_name, $reg_num); + } + + my $reg_info = $reg_href->{info}; + my $reg_extract = $reg_info->{extract}; + my $reg_format = $reg_info->{format}; + my $reg_val = &$reg_extract($arrayref); + if ($indent) { + printf("\t%*s = $reg_format", $max_register_name_len, $reg_name, $reg_val); + } else { + printf("%s = $reg_format", $reg_name, $reg_val); + } +} + +#---------------------------------------------------------------------- +# Extract the command into an array of ASCII char strings for easy +# processing +#---------------------------------------------------------------------- +sub extract_command +{ + my $cmd_str = shift; + my @cmd_chars = split(/ */, $cmd_str); + if ($cmd_chars[0] ne '$') + { + # only set the current command if it isn't a reply + $curr_cmd = $cmd_chars[0]; + } + return @cmd_chars; +} + +#---------------------------------------------------------------------- +# Strip the 3 checksum array entries after we don't need them anymore +#---------------------------------------------------------------------- +sub strip_checksum +{ + my $arrayref = shift; + splice(@$arrayref, -3); +} + +#---------------------------------------------------------------------- +# Dump all strings in array by joining them together with no space +# between them +#---------------------------------------------------------------------- +sub dump_chars +{ + print join('',@_); +} + +#---------------------------------------------------------------------- +# Check if the response is an error 'EXX' +#---------------------------------------------------------------------- +sub is_error_response +{ + if ($_[0] eq 'E') + { + shift; + print "ERROR = " . join('',@_) . "\n"; + return 1; + } + return 0; +} + +#---------------------------------------------------------------------- +# 'H' command +#---------------------------------------------------------------------- +sub dump_set_thread_cmd +{ + my $cmd = shift; + my $mod = shift; + print "set_thread ( $mod, " . join('',@_) . " )\n"; +} + +#---------------------------------------------------------------------- +# 'T' command +#---------------------------------------------------------------------- +our $T_cmd_tid = -1; +sub dump_thread_is_alive_cmd +{ + my $cmd = shift; + $T_cmd_tid = get_hex(\@_); + printf("thread_is_alive ( $tid_format )\n", $T_cmd_tid); +} + +sub dump_thread_is_alive_rsp +{ + my $rsp = join('',@_); + + printf("thread_is_alive ( $tid_format ) =>", $T_cmd_tid); + if ($rsp eq 'OK') + { + print " alive.\n"; + } + else + { + print " dead.\n"; + } +} + +#---------------------------------------------------------------------- +# 'H' response +#---------------------------------------------------------------------- +sub dump_set_thread_rsp +{ + if (!is_error_response(@_)) + { + print join('',@_) . "\n"; + } +} + +#---------------------------------------------------------------------- +# 'q' command +#---------------------------------------------------------------------- +our $gen_query_cmd; +our $qRegisterInfo_reg_num = -1; +sub dump_general_query_cmd +{ + $gen_query_cmd = join('',@_); + if ($gen_query_cmd eq 'qC') + { + print 'get_current_pid ()'; + } + elsif ($gen_query_cmd eq 'qfThreadInfo') + { + print 'get_first_active_threads ()'; + } + elsif ($gen_query_cmd eq 'qsThreadInfo') + { + print 'get_subsequent_active_threads ()'; + } + elsif (index($gen_query_cmd, 'qThreadExtraInfo') == 0) + { + # qThreadExtraInfo,id + print 'get_thread_extra_info ()'; + } + elsif (index($gen_query_cmd, 'qThreadStopInfo') == 0) + { + # qThreadStopInfoXXXX + @_ = splice(@_, length('qThreadStopInfo')); + my $tid = get_addr(\@_); + printf('get_thread_stop_info ( thread = 0x%4.4x )', $tid); + } + elsif (index($gen_query_cmd, 'qSymbol:') == 0) + { + # qCRC:addr,length + print 'gdb_ready_to_serve_symbol_lookups ()'; + } + elsif (index($gen_query_cmd, 'qCRC:') == 0) + { + # qCRC:addr,length + @_ = splice(@_, length('qCRC:')); + my $address = get_addr(\@_); + shift @_; + my $length = join('', @_); + printf("compute_crc (addr = $addr_format, length = $length)", $address); + } + elsif (index($gen_query_cmd, 'qGetTLSAddr:') == 0) + { + # qGetTLSAddr:thread-id,offset,lm + @_ = splice(@_, length('qGetTLSAddr:')); + my ($tid, $offset, $lm) = split (/,/, join('', @_)); + print "get_thread_local_storage_addr (thread-id = $tid, offset = $offset, lm = $lm)"; + } + elsif ($gen_query_cmd eq 'qOffsets') + { + print 'get_section_offsets ()'; + } + elsif (index($gen_query_cmd, 'qRegisterInfo') == 0) + { + @_ = splice(@_, length('qRegisterInfo')); + $qRegisterInfo_reg_num = get_hex(\@_); + + printf "get_dynamic_register_info ($qRegisterInfo_reg_num)"; + } + else + { + print $gen_query_cmd; + } + print "\n"; +} + +#---------------------------------------------------------------------- +# 'q' response +#---------------------------------------------------------------------- +sub dump_general_query_rsp +{ + my $gen_query_rsp = join('',@_); + my $gen_query_rsp_len = length ($gen_query_rsp); + if ($gen_query_cmd eq 'qC' and index($gen_query_rsp, 'QC') == 0) + { + shift @_; shift @_; + my $pid = get_hex(\@_); + printf("pid = $pid_format\n", $pid); + return; + } + elsif (index($gen_query_cmd, 'qRegisterInfo') == 0) + { + if ($gen_query_rsp_len == 0) + { + print "$unimplemented_str\n"; + } + else + { + if (index($gen_query_rsp, 'name') == 0) + { + $qRegisterInfo_reg_num == 0 and $registers_aref = []; + + my @name_and_values = split (/;/, $gen_query_rsp); + + my $reg_name = undef; + my $byte_size = 0; + my $pseudo = 0; + foreach (@name_and_values) + { + my ($name, $value) = split /:/; + if ($name eq "name") { $reg_name = $value; } + elsif ($name eq "bitsize") { $byte_size = $value / 8; } + elsif ($name eq "container-regs") { $pseudo = 1; } + } + if (defined $reg_name and $byte_size > 0) + { + if ($byte_size == 4) {push @$registers_aref, { name => $reg_name, info => $reg32_href , pseudo => $pseudo };} + elsif ($byte_size == 8) {push @$registers_aref, { name => $reg_name, info => $reg64_href , pseudo => $pseudo };} + elsif ($byte_size == 1) {push @$registers_aref, { name => $reg_name, info => $reg8_href , pseudo => $pseudo };} + elsif ($byte_size == 2) {push @$registers_aref, { name => $reg_name, info => $reg16_href , pseudo => $pseudo };} + elsif ($byte_size == 10) {push @$registers_aref, { name => $reg_name, info => $reg80_href , pseudo => $pseudo };} + elsif ($byte_size == 12) {push @$registers_aref, { name => $reg_name, info => $float96_href , pseudo => $pseudo };} + elsif ($byte_size == 16) {push @$registers_aref, { name => $reg_name, info => $reg128_href , pseudo => $pseudo };} + elsif ($byte_size == 32) {push @$registers_aref, { name => $reg_name, info => $reg256_href , pseudo => $pseudo };} + } + } + elsif ($gen_query_rsp_len == 3 and index($gen_query_rsp, 'E') == 0) + { + calculate_max_register_name_length(); + } + } + } + elsif ($gen_query_cmd =~ 'qThreadStopInfo') + { + dump_stop_reply_packet (@_); + } + if (dump_standard_response(\@_)) + { + # Do nothing... + } + else + { + print join('',@_) . "\n"; + } +} + +#---------------------------------------------------------------------- +# 'Q' command +#---------------------------------------------------------------------- +our $gen_set_cmd; +sub dump_general_set_cmd +{ + $gen_query_cmd = join('',@_); + if ($gen_query_cmd eq 'QStartNoAckMode') + { + print "StartNoAckMode ()" + } + elsif ($gen_query_cmd eq 'QThreadSuffixSupported') + { + $thread_suffix_supported = 1; + print "ThreadSuffixSupported ()" + } + elsif (index($gen_query_cmd, 'QSetMaxPayloadSize:') == 0) + { + @_ = splice(@_, length('QSetMaxPayloadSize:')); + my $max_payload_size = get_hex(\@_); + # QSetMaxPayloadSize:XXXX where XXXX is a hex length of the max + # packet payload size supported by gdb + printf("SetMaxPayloadSize ( 0x%x (%u))", $max_payload_size, $max_payload_size); + } + elsif (index ($gen_query_cmd, 'QSetSTDIN:') == 0) + { + @_ = splice(@_, length('QSetSTDIN:')); + printf ("SetSTDIN (path ='%s')\n", get_hex_string (\@_)); + } + elsif (index ($gen_query_cmd, 'QSetSTDOUT:') == 0) + { + @_ = splice(@_, length('QSetSTDOUT:')); + printf ("SetSTDOUT (path ='%s')\n", get_hex_string (\@_)); + } + elsif (index ($gen_query_cmd, 'QSetSTDERR:') == 0) + { + @_ = splice(@_, length('QSetSTDERR:')); + printf ("SetSTDERR (path ='%s')\n", get_hex_string (\@_)); + } + else + { + print $gen_query_cmd; + } + print "\n"; +} + +#---------------------------------------------------------------------- +# 'k' command +#---------------------------------------------------------------------- +sub dump_kill_cmd +{ + my $cmd = shift; + print "kill (" . join('',@_) . ")\n"; +} + +#---------------------------------------------------------------------- +# 'g' command +#---------------------------------------------------------------------- +sub dump_read_regs_cmd +{ + my $cmd = shift; + print "read_registers ()\n"; +} + +#---------------------------------------------------------------------- +# 'G' command +#---------------------------------------------------------------------- +sub dump_write_regs_cmd +{ + print "write_registers:\n"; + my $cmd = shift; + foreach my $reg_href (@$registers_aref) + { + last if ($_[0] eq '#'); + if ($reg_href->{pseudo} == 0) + { + my $reg_info_href = $reg_href->{info}; + my $reg_name = $reg_href->{name}; + my $reg_extract = $reg_info_href->{extract}; + my $reg_format = $reg_info_href->{format}; + my $reg_val = &$reg_extract(\@_); + printf("\t%*s = $reg_format\n", $max_register_name_len, $reg_name, $reg_val); + } + } +} + +sub dump_read_regs_rsp +{ + print "read_registers () =>\n"; + if (!is_error_response(@_)) + { + # print join('',@_) . "\n"; + foreach my $reg_href (@$registers_aref) + { + last if ($_[0] eq '#'); + if ($reg_href->{pseudo} == 0) + { + my $reg_info_href = $reg_href->{info}; + my $reg_name = $reg_href->{name}; + my $reg_extract = $reg_info_href->{extract}; + my $reg_format = $reg_info_href->{format}; + my $reg_val = &$reg_extract(\@_); + printf("\t%*s = $reg_format\n", $max_register_name_len, $reg_name, $reg_val); + } + } + } +} + +sub dump_read_single_register_rsp +{ + dump_register_value(0, \@_, $reg_cmd_reg); + print "\n"; +} + +#---------------------------------------------------------------------- +# '_M' - allocate memory command (LLDB extension) +# +# Command: '_M' +# Arg1: Hex byte size as big endian hex string +# Separator: ',' +# Arg2: permissions as string that must be a string that contains any +# combination of 'r' (readable) 'w' (writable) or 'x' (executable) +# +# Returns: The address that was allocated as a big endian hex string +# on success, else an error "EXX" where XX are hex bytes +# that indicate an error code. +# +# Examples: +# _M10,rw # allocate 16 bytes with read + write permissions +# _M100,rx # allocate 256 bytes with read + execute permissions +#---------------------------------------------------------------------- +sub dump_allocate_memory_cmd +{ + shift; shift; # shift off the '_' and the 'M' + my $byte_size = get_addr(\@_); + shift; # Skip ',' + printf("allocate_memory ( byte_size = %u (0x%x), permissions = %s)\n", $byte_size, $byte_size, join('',@_)); +} + +sub dump_allocate_memory_rsp +{ + if (@_ == 3 and $_[0] == 'E') + { + printf("allocated memory addr = ERROR (%s))\n", join('',@_)); + } + else + { + printf("allocated memory addr = 0x%s\n", join('',@_)); + } +} + +#---------------------------------------------------------------------- +# '_m' - deallocate memory command (LLDB extension) +# +# Command: '_m' +# Arg1: Hex address as big endian hex string +# +# Returns: "OK" on success "EXX" on error +# +# Examples: +# _m201000 # Free previously allocated memory at address 0x201000 +#---------------------------------------------------------------------- +sub dump_deallocate_memory_cmd +{ + shift; shift; # shift off the '_' and the 'm' + printf("deallocate_memory ( addr = 0x%s)\n", join('',@_)); +} + + +#---------------------------------------------------------------------- +# 'p' command +#---------------------------------------------------------------------- +sub dump_read_single_register_cmd +{ + my $cmd = shift; + $reg_cmd_reg = get_hex(\@_); + my $thread = get_thread_from_thread_suffix (\@_); + my $reg_href = $$registers_aref[$reg_cmd_reg]; + + if (defined $thread) + { + print "read_register ( reg = \"$reg_href->{name}\", thread = $thread )\n"; + } + else + { + print "read_register ( reg = \"$reg_href->{name}\" )\n"; + } +} + + +#---------------------------------------------------------------------- +# 'P' command +#---------------------------------------------------------------------- +sub dump_write_single_register_cmd +{ + my $cmd = shift; + my $reg_num = get_hex(\@_); + shift (@_); # Discard the '=' + + print "write_register ( "; + dump_register_value(0, \@_, $reg_num); + my $thread = get_thread_from_thread_suffix (\@_); + if (defined $thread) + { + print ", thread = $thread"; + } + print " )\n"; +} + +#---------------------------------------------------------------------- +# 'm' command +#---------------------------------------------------------------------- +our $read_mem_address = 0; +sub dump_read_mem_cmd +{ + my $cmd = shift; + $read_mem_address = get_addr(\@_); + shift; # Skip ',' + printf("read_mem ( $addr_format, %s )\n", $read_mem_address, join('',@_)); +} + +#---------------------------------------------------------------------- +# 'm' response +#---------------------------------------------------------------------- +sub dump_read_mem_rsp +{ + # If the memory read was 2 or 4 bytes, print it out in native format + # instead of just as bytes. + my $num_nibbles = @_; + if ($num_nibbles == 2) + { + printf(" 0x%2.2x", get8(\@_)); + } + elsif ($num_nibbles == 4) + { + printf(" 0x%4.4x", get16(\@_)); + } + elsif ($num_nibbles == 8) + { + printf(" 0x%8.8x", get32(\@_)); + } + elsif ($num_nibbles == 16) + { + printf(" 0x%s", get64(\@_)); + } + else + { + my $curr_address = $read_mem_address; + my $nibble; + my $nibble_offset = 0; + my $max_nibbles_per_line = 2 * $max_bytes_per_line; + foreach $nibble (@_) + { + if (($nibble_offset % $max_nibbles_per_line) == 0) + { + ($nibble_offset > 0) and print "\n "; + printf("$addr_format: ", $curr_address + $nibble_offset/2); + } + (($nibble_offset % 2) == 0) and print ' '; + print $nibble; + $nibble_offset++; + } + } + print "\n"; +} + +#---------------------------------------------------------------------- +# 'c' or 's' command +#---------------------------------------------------------------------- +sub dump_continue_cmd +{ + my $cmd = shift; + my $cmd_str; + $cmd eq 'c' and $cmd_str = 'continue'; + $cmd eq 's' and $cmd_str = 'step'; + my $address = -1; + if (@_) + { + my $address = get_addr(\@_); + printf("%s ($addr_format)\n", $cmd_str, $address); + } + else + { + printf("%s ()\n", $cmd_str); + } +} + +#---------------------------------------------------------------------- +# 'Css' continue (C) with signal (ss where 'ss' is two hex digits) +# 'Sss' step (S) with signal (ss where 'ss' is two hex digits) +#---------------------------------------------------------------------- +sub dump_continue_with_signal_cmd +{ + my $cmd = shift; + my $address = -1; + my $cmd_str; + $cmd eq 'c' and $cmd_str = 'continue'; + $cmd eq 's' and $cmd_str = 'step'; + my $signal = get_hex(\@_); + if (@_) + { + my $address = 0; + if (@_ && $_[0] == ';') + { + shift; + $address = get_addr(\@_); + } + } + + if ($address != -1) + { + printf("%s_with_signal (signal = 0x%2.2x, address = $addr_format)\n", $cmd_str, $signal, $address); + } + else + { + printf("%s_with_signal (signal = 0x%2.2x)\n", $cmd_str, $signal); + } +} + +#---------------------------------------------------------------------- +# 'A' command +#---------------------------------------------------------------------- +sub dump_A_command +{ + my $cmd = get_expected_char(\@_, 'A') or print "error: incorrect command letter for argument packet, expected 'A'\n"; + printf("set_program_arguments (\n"); + do + { + my $arg_len = get_uint(\@_); + get_expected_char(\@_, ',') or die "error: missing comma after argument length...?\n"; + my $arg_idx = get_uint(\@_); + get_expected_char(\@_, ',') or die "error: missing comma after argument number...?\n"; + + my $arg = ''; + my $num_hex8_bytes = $arg_len/2; + for (1 .. $num_hex8_bytes) + { + $arg .= sprintf("%c", get8(\@_)) + } + printf(" <%3u> argv[%u] = '%s'\n", $arg_len, $arg_idx, $arg); + if (@_ > 0) + { + get_expected_char(\@_, ',') or die "error: missing comma after argument argument ASCII hex bytes...?\n"; + } + } while (@_ > 0); + printf(" )\n"); +} + + +#---------------------------------------------------------------------- +# 'z' and 'Z' command +#---------------------------------------------------------------------- +sub dump_bp_wp_command +{ + my $cmd = shift; + my $type = shift; + shift; # Skip ',' + my $address = get_addr(\@_); + shift; # Skip ',' + my $length = join('',@_); + if ($cmd eq 'z') + { + printf("remove $point_types[$type]($addr_format, %d)\n", $address, $length); + } + else + { + printf("insert $point_types[$type]($addr_format, %d)\n", $address, $length); + } +} + + +#---------------------------------------------------------------------- +# 'X' command +#---------------------------------------------------------------------- +sub dump_write_mem_binary_cmd +{ + my $cmd = shift; + my $address = get_addr(\@_); + shift; # Skip ',' + + my ($length, $binary) = split(/:/, join('',@_)); + printf("write_mem_binary ( $addr_format, %d, %s)\n", $address, $length, $binary); + +} + +#---------------------------------------------------------------------- +# 'M' command +#---------------------------------------------------------------------- +sub dump_write_mem_cmd +{ + my $cmd = shift; + my $address = get_addr(\@_); + shift; # Skip ',' + my ($length, $hex_bytes) = split(/:/, join('',@_)); +# printf("write_mem ( $addr_format, %d, %s)\n", $address, $length, $hex_bytes); + printf("write_mem ( addr = $addr_format, len = %d (0x%x), bytes = ", $address, $length, $length); + splice(@_, 0, length($length)+1); + + my $curr_address = $address; + my $nibble; + my $nibble_count = 0; + my $max_nibbles_per_line = 2 * $max_bytes_per_line; + foreach $nibble (@_) + { + (($nibble_count % 2) == 0) and print ' '; + print $nibble; + $nibble_count++; + } + + # If the memory to write is 2 or 4 bytes, print it out in native format + # instead of just as bytes. + if (@_ == 4) + { + printf(" ( 0x%4.4x )", get16(\@_)); + } + elsif (@_ == 8) + { + printf(" ( 0x%8.8x )", get32(\@_)); + } + print " )\n"; + +} + +#---------------------------------------------------------------------- +# 'v' command +#---------------------------------------------------------------------- +our $extended_rsp_callback = 0; +sub dump_extended_cmd +{ + $extended_rsp_callback = 0; + if (join('', @_[0..4]) eq "vCont") + { + dump_extended_continue_cmd(splice(@_,5)); + } + elsif (join('', @_[0..7]) eq 'vAttach;') + { + dump_attach_command (splice(@_,8)); + } + elsif (join('', @_[0..11]) eq 'vAttachWait;') + { + dump_attach_wait_command (splice(@_,12)); + } +} + +#---------------------------------------------------------------------- +# 'v' response +#---------------------------------------------------------------------- +sub dump_extended_rsp +{ + if ($extended_rsp_callback) + { + &$extended_rsp_callback(@_); + } + $extended_rsp_callback = 0; +} + +#---------------------------------------------------------------------- +# 'vAttachWait' command +#---------------------------------------------------------------------- +sub dump_attach_wait_command +{ + print "attach_wait ( "; + while (@_) + { + printf("%c", get8(\@_)) + } + printf " )\n"; + +} + +#---------------------------------------------------------------------- +# 'vAttach' command +#---------------------------------------------------------------------- +sub dump_attach_command +{ + printf("attach ( pid = %i )", get_hex(\@_)); + $extended_rsp_callback = \&dump_stop_reply_packet; +} + +#---------------------------------------------------------------------- +# 'vCont' command +#---------------------------------------------------------------------- +sub dump_extended_continue_cmd +{ + print "extended_continue ( "; + my $cmd = shift; + if ($cmd eq '?') + { + print "list supported modes )\n"; + $extended_rsp_callback = \&dump_extended_continue_rsp; + } + elsif ($cmd eq ';') + { + $extended_rsp_callback = \&dump_stop_reply_packet; + my $i = 0; + while ($#_ >= 0) + { + if ($i > 0) + { + print ", "; + } + my $continue_cmd = shift; + my $tmp; + if ($continue_cmd eq 'c') + { + print "continue"; + } + elsif ($continue_cmd eq 'C') + { + print "continue with signal "; + print shift; + print shift; + } + elsif ($continue_cmd eq 's') + { + print "step"; + } + elsif ($continue_cmd eq 'S') + { + print "step with signal "; + print shift; + print shift; + } + + if ($_[0] eq ':') + { + shift; # Skip ':' + print " for thread "; + while ($#_ >= 0) + { + $tmp = shift; + if (length($tmp) > 0 && $tmp ne ';') { + print $tmp; + } else { + last; + } + } + } + $i++; + } + + printf " )\n"; + } +} + +#---------------------------------------------------------------------- +# 'vCont' response +#---------------------------------------------------------------------- +sub dump_extended_continue_rsp +{ + if (scalar(@_) == 0) + { + print "$unimplemented_str\n"; + } + else + { + print "extended_continue supports " . join('',@_) . "\n"; + } +} + +#---------------------------------------------------------------------- +# Dump the command ascii for any unknown commands +#---------------------------------------------------------------------- +sub dump_other_cmd +{ + print "other = " . join('',@_) . "\n"; +} + +#---------------------------------------------------------------------- +# Check to see if the response was unsupported with appropriate checksum +#---------------------------------------------------------------------- +sub rsp_is_unsupported +{ + return join('',@_) eq "#00"; +} + +#---------------------------------------------------------------------- +# Check to see if the response was "OK" with appropriate checksum +#---------------------------------------------------------------------- +sub rsp_is_OK +{ + return join('',@_) eq "OK#9a"; +} + +#---------------------------------------------------------------------- +# Dump a response for an unknown command +#---------------------------------------------------------------------- +sub dump_other_rsp +{ + print "other = " . join('',@_) . "\n"; +} + +#---------------------------------------------------------------------- +# Get a byte from the ascii string assuming that the 2 nibble ascii +# characters are in hex. +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get8 +{ + my $arrayref = shift; + my $val = hex(shift(@$arrayref) . shift(@$arrayref)); + return $val; +} + +#---------------------------------------------------------------------- +# Get a 16 bit integer and swap if $swap global is set to a non-zero +# value. +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get16 +{ + my $arrayref = shift; + my $val = 0; + if ($swap) + { + $val = get8($arrayref) | + get8($arrayref) << 8; + } + else + { + $val = get8($arrayref) << 8 | + get8($arrayref) ; + } + return $val; +} + +#---------------------------------------------------------------------- +# Get a 32 bit integer and swap if $swap global is set to a non-zero +# value. +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get32 +{ + my $arrayref = shift; + my $val = 0; + if ($swap) + { + $val = get8($arrayref) | + get8($arrayref) << 8 | + get8($arrayref) << 16 | + get8($arrayref) << 24 ; + } + else + { + $val = get8($arrayref) << 24 | + get8($arrayref) << 16 | + get8($arrayref) << 8 | + get8($arrayref) ; + } + return $val; +} + +#---------------------------------------------------------------------- +# Get a 64 bit hex value as a string +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get64 +{ + my $arrayref = shift; + my $val = ''; + my @nibbles; + if ($swap) + { + push @nibbles, splice(@$arrayref, 14, 2); + push @nibbles, splice(@$arrayref, 12, 2); + push @nibbles, splice(@$arrayref, 10, 2); + push @nibbles, splice(@$arrayref, 8, 2); + push @nibbles, splice(@$arrayref, 6, 2); + push @nibbles, splice(@$arrayref, 4, 2); + push @nibbles, splice(@$arrayref, 2, 2); + push @nibbles, splice(@$arrayref, 0, 2); + } + else + { + (@nibbles) = splice(@$arrayref, 0, ((64/8) * 2)); + } + $val = join('', @nibbles); + return $val; +} + +#---------------------------------------------------------------------- +# Get a 80 bit hex value as a string +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get80 +{ + my $arrayref = shift; + my $val = ''; + my @nibbles; + if ($swap) + { + push @nibbles, splice(@$arrayref, 18, 2); + push @nibbles, splice(@$arrayref, 16, 2); + push @nibbles, splice(@$arrayref, 14, 2); + push @nibbles, splice(@$arrayref, 12, 2); + push @nibbles, splice(@$arrayref, 10, 2); + push @nibbles, splice(@$arrayref, 8, 2); + push @nibbles, splice(@$arrayref, 6, 2); + push @nibbles, splice(@$arrayref, 4, 2); + push @nibbles, splice(@$arrayref, 2, 2); + push @nibbles, splice(@$arrayref, 0, 2); + } + else + { + (@nibbles) = splice(@$arrayref, 0, ((80/8) * 2)); + } + $val = join('', @nibbles); + return $val; +} + +#---------------------------------------------------------------------- +# Get a 96 bit hex value as a string +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get96 +{ + my $arrayref = shift; + my $val = ''; + my @nibbles; + if ($swap) + { + push @nibbles, splice(@$arrayref, 22, 2); + push @nibbles, splice(@$arrayref, 20, 2); + push @nibbles, splice(@$arrayref, 18, 2); + push @nibbles, splice(@$arrayref, 16, 2); + push @nibbles, splice(@$arrayref, 14, 2); + push @nibbles, splice(@$arrayref, 12, 2); + push @nibbles, splice(@$arrayref, 10, 2); + push @nibbles, splice(@$arrayref, 8, 2); + push @nibbles, splice(@$arrayref, 6, 2); + push @nibbles, splice(@$arrayref, 4, 2); + push @nibbles, splice(@$arrayref, 2, 2); + push @nibbles, splice(@$arrayref, 0, 2); + } + else + { + (@nibbles) = splice(@$arrayref, 0, ((96/8) * 2)); + } + $val = join('', @nibbles); + return $val; +} + +#---------------------------------------------------------------------- +# Get a 128 bit hex value as a string +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get128 +{ + my $arrayref = shift; + my $val = ''; + my @nibbles; + if ($swap) + { + push @nibbles, splice(@$arrayref, 30, 2); + push @nibbles, splice(@$arrayref, 28, 2); + push @nibbles, splice(@$arrayref, 26, 2); + push @nibbles, splice(@$arrayref, 24, 2); + push @nibbles, splice(@$arrayref, 22, 2); + push @nibbles, splice(@$arrayref, 20, 2); + push @nibbles, splice(@$arrayref, 18, 2); + push @nibbles, splice(@$arrayref, 16, 2); + push @nibbles, splice(@$arrayref, 14, 2); + push @nibbles, splice(@$arrayref, 12, 2); + push @nibbles, splice(@$arrayref, 10, 2); + push @nibbles, splice(@$arrayref, 8, 2); + push @nibbles, splice(@$arrayref, 6, 2); + push @nibbles, splice(@$arrayref, 4, 2); + push @nibbles, splice(@$arrayref, 2, 2); + push @nibbles, splice(@$arrayref, 0, 2); + } + else + { + (@nibbles) = splice(@$arrayref, 0, ((128/8) * 2)); + } + $val = join('', @nibbles); + return $val; +} + +#---------------------------------------------------------------------- +# Get a 256 bit hex value as a string +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get256 +{ + my $arrayref = shift; + my $val = ''; + my @nibbles; + if ($swap) + { + push @nibbles, splice(@$arrayref, 62, 2); + push @nibbles, splice(@$arrayref, 60, 2); + push @nibbles, splice(@$arrayref, 58, 2); + push @nibbles, splice(@$arrayref, 56, 2); + push @nibbles, splice(@$arrayref, 54, 2); + push @nibbles, splice(@$arrayref, 52, 2); + push @nibbles, splice(@$arrayref, 50, 2); + push @nibbles, splice(@$arrayref, 48, 2); + push @nibbles, splice(@$arrayref, 46, 2); + push @nibbles, splice(@$arrayref, 44, 2); + push @nibbles, splice(@$arrayref, 42, 2); + push @nibbles, splice(@$arrayref, 40, 2); + push @nibbles, splice(@$arrayref, 38, 2); + push @nibbles, splice(@$arrayref, 36, 2); + push @nibbles, splice(@$arrayref, 34, 2); + push @nibbles, splice(@$arrayref, 32, 2); + push @nibbles, splice(@$arrayref, 30, 2); + push @nibbles, splice(@$arrayref, 28, 2); + push @nibbles, splice(@$arrayref, 26, 2); + push @nibbles, splice(@$arrayref, 24, 2); + push @nibbles, splice(@$arrayref, 22, 2); + push @nibbles, splice(@$arrayref, 20, 2); + push @nibbles, splice(@$arrayref, 18, 2); + push @nibbles, splice(@$arrayref, 16, 2); + push @nibbles, splice(@$arrayref, 14, 2); + push @nibbles, splice(@$arrayref, 12, 2); + push @nibbles, splice(@$arrayref, 10, 2); + push @nibbles, splice(@$arrayref, 8, 2); + push @nibbles, splice(@$arrayref, 6, 2); + push @nibbles, splice(@$arrayref, 4, 2); + push @nibbles, splice(@$arrayref, 2, 2); + push @nibbles, splice(@$arrayref, 0, 2); + } + else + { + (@nibbles) = splice(@$arrayref, 0, ((256/8) * 2)); + } + $val = join('', @nibbles); + return $val; +} + +#---------------------------------------------------------------------- +# Get an unsigned integer value by grabbing items off the front of +# the array stopping when a non-digit char string is encountered. +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it +#---------------------------------------------------------------------- +sub get_uint +{ + my $arrayref = shift; + @$arrayref == 0 and return 0; + my $val = 0; + while ($$arrayref[0] =~ /[0-9]/) + { + $val = $val * 10 + int(shift(@$arrayref)); + } + return $val; +} + +#---------------------------------------------------------------------- +# Check the first character in the array and if it matches the expected +# character, return that character, else return undef; +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it. If the expected +# character doesn't match, it won't touch the array. If the first +# character does match, it will shift it off and return it. +#---------------------------------------------------------------------- +sub get_expected_char +{ + my $arrayref = shift; + my $expected_char = shift; + if ($expected_char eq $$arrayref[0]) + { + return shift(@$arrayref); + } + return undef; +} +#---------------------------------------------------------------------- +# Get a hex value by grabbing items off the front of the array and +# stopping when a non-hex char string is encountered. +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get_hex +{ + my $arrayref = shift; + my $my_swap = @_ ? shift : 0; + my $shift = 0; + my $val = 0; + while ($$arrayref[0] =~ /[0-9a-fA-F]/) + { + if ($my_swap) + { + my $byte = hex(shift(@$arrayref)) << 4 | hex(shift(@$arrayref)); + $val |= $byte << $shift; + $shift += 8; + } + else + { + $val <<= 4; + $val |= hex(shift(@$arrayref)); + } + } + return $val; +} + +#---------------------------------------------------------------------- +# Get an address value by grabbing items off the front of the array. +# +# The argument for this function needs to be a reference to an array +# that contains single character strings and the array will get +# updated by shifting characters off the front of it (no leading # "0x") +#---------------------------------------------------------------------- +sub get_addr +{ + get_hex(shift); +} + +sub get_hex_string +{ + my $arrayref = shift; + my $str = ''; + while ($$arrayref[0] =~ /[0-9a-fA-F]/ and $$arrayref[1] =~ /[0-9a-fA-F]/) + { + my $hi_nibble = hex(shift(@$arrayref)); + my $lo_nibble = hex(shift(@$arrayref)); + my $byte = ($hi_nibble << 4) | $lo_nibble; + $str .= chr($byte); + } + return $str; +} + +sub dump_stop_reply_data +{ + while ($#_ >= 0) + { + last unless ($_[0] ne '#'); + + + my $key = ''; + my $value = ''; + my $comment = ''; + if ($_[0] =~ /[0-9a-fA-F]/ && $_[1] =~ /[0-9a-fA-F]/) + { + my $reg_num = get8(\@_); + shift(@_); # Skip ':' + if (defined ($registers_aref) && $reg_num < @$registers_aref) + { + dump_register_value(1, \@_, $reg_num); + print "\n"; + shift(@_); # Skip ';' + next; + } + $key = sprintf("reg %u", $reg_num); + } + my $char; + + if (length($key) == 0) + { + while (1) + { + $char = shift(@_); + if (length($char) == 0 or $char eq ':' or $char eq '#') { last; } + $key .= $char; + } + } + + while (1) + { + $char = shift(@_); + if (length($char) == 0 or $char eq ';' or $char eq '#') { last; } + $value .= $char; + } + if ($key eq 'metype') + { + our %metype_to_name = ( + '1' => ' (EXC_BAD_ACCESS)', + '2' => ' (EXC_BAD_INSTRUCTION)', + '3' => ' (EXC_ARITHMETIC)', + '4' => ' (EXC_EMULATION)', + '5' => ' (EXC_SOFTWARE)', + '6' => ' (EXC_BREAKPOINT)', + '7' => ' (EXC_SYSCALL)', + '8' => ' (EXC_MACH_SYSCALL)', + '9' => ' (EXC_RPC_ALERT)', + '10' => ' (EXC_CRASH)' + ); + if (exists $metype_to_name{$value}) + { + $comment = $metype_to_name{$value}; + } + } + printf("\t%*s = %s$comment\n", $max_register_name_len, $key, $value); + } +} + +#---------------------------------------------------------------------- +# Dumps a Stop Reply Packet which happens in response to a step, +# continue, last signal, and probably a few other commands. +#---------------------------------------------------------------------- +sub dump_stop_reply_packet +{ + my $what = shift(@_); + if ($what eq 'S' or $what eq 'T') + { + my $signo = get8(\@_); + + our %signo_to_name = ( + '1' => ' SIGHUP', + '2' => ' SIGINT', + '3' => ' SIGQUIT', + '4' => ' SIGILL', + '5' => ' SIGTRAP', + '6' => ' SIGABRT', + '7' => ' SIGPOLL/SIGEMT', + '8' => ' SIGFPE', + '9' => ' SIGKILL', + '10' => ' SIGBUS', + '11' => ' SIGSEGV', + '12' => ' SIGSYS', + '13' => ' SIGPIPE', + '14' => ' SIGALRM', + '15' => ' SIGTERM', + '16' => ' SIGURG', + '17' => ' SIGSTOP', + '18' => ' SIGTSTP', + '19' => ' SIGCONT', + '20' => ' SIGCHLD', + '21' => ' SIGTTIN', + '22' => ' SIGTTOU', + '23' => ' SIGIO', + '24' => ' SIGXCPU', + '25' => ' SIGXFSZ', + '26' => ' SIGVTALRM', + '27' => ' SIGPROF', + '28' => ' SIGWINCH', + '29' => ' SIGINFO', + '30' => ' SIGUSR1', + '31' => ' SIGUSR2', + '145' => ' TARGET_EXC_BAD_ACCESS', # 0x91 + '146' => ' TARGET_EXC_BAD_INSTRUCTION', # 0x92 + '147' => ' TARGET_EXC_ARITHMETIC', # 0x93 + '148' => ' TARGET_EXC_EMULATION', # 0x94 + '149' => ' TARGET_EXC_SOFTWARE', # 0x95 + '150' => ' TARGET_EXC_BREAKPOINT' # 0x96 + ); + my $signo_str = sprintf("%i", $signo); + my $signo_name = ''; + if (exists $signo_to_name{$signo_str}) + { + $signo_name = $signo_to_name{$signo_str}; + } + printf ("signal (signo=%u$signo_name)\n", $signo); + dump_stop_reply_data (@_); + } + elsif ($what eq 'W') + { + print 'process_exited( ' . shift(@_) . shift(@_) . " )\n"; + } + elsif ($what eq 'X') + { + print 'process_terminated( ' . shift(@_) . shift(@_) . " )\n"; + } + elsif ($what eq 'O') + { + my $console_output = ''; + my $num_hex8_bytes = @_/2; + for (1 .. $num_hex8_bytes) + { + $console_output .= sprintf("%c", get8(\@_)) + } + + print "program_console_output('$console_output')\n"; + } +} + +#---------------------------------------------------------------------- +# '?' command +#---------------------------------------------------------------------- +sub dump_last_signal_cmd +{ + my $cmd = shift; + print 'last_signal (' . join('',@_) . ")\n"; +} + +sub dump_raw_command +{ + my $cmd_aref = shift; + my $callback_ref; + $curr_cmd = $$cmd_aref[0]; + + if ($curr_cmd eq 'q' or $curr_cmd eq 'Q' or $curr_cmd eq '_') + { + $curr_full_cmd = ''; + foreach my $ch (@$cmd_aref) + { + $ch !~ /[A-Za-z_]/ and last; + $curr_full_cmd .= $ch; + } + } + else + { + $curr_full_cmd = $curr_cmd; + } + + $curr_cmd eq '_' and $curr_cmd .= $$cmd_aref[1]; + $callback_ref = $cmd_callbacks{$curr_cmd}; + if ($callback_ref) + { + &$callback_ref(@$cmd_aref); + } + else + { + # Strip the command byte for responses since we injected that above + dump_other_cmd(@$cmd_aref); + } +} + +sub dump_standard_response +{ + my $cmd_aref = shift; + + my $cmd_len = scalar(@$cmd_aref); + if ($cmd_len == 0) + { + print "$unimplemented_str\n"; + return 1; + } + + my $response = join('', @$cmd_aref); + if ($response eq 'OK') + { + print "$success_str\n"; + return 1; + } + + if ($cmd_len == 3 and index($response, 'E') == 0) + { + print "ERROR: " . substr($response, 1) . "\n"; + return 1; + } + + return 0; +} +sub dump_raw_response +{ + my $cmd_aref = shift; + my $callback_ref; + + if ($packet_start_time != 0.0) + { + if (length($curr_full_cmd) > 0) + { + $packet_times{$curr_full_cmd} += $curr_time - $packet_start_time; + } + else + { + $packet_times{$curr_cmd} += $curr_time - $packet_start_time; + } + $packet_start_time = 0.0; + } + + $callback_ref = $rsp_callbacks{$curr_cmd}; + + if ($callback_ref) + { + &$callback_ref(@$cmd_aref); + } + else + { + dump_standard_response($cmd_aref) or dump_other_rsp(@$cmd_aref); + } + +} +#---------------------------------------------------------------------- +# Dumps any command and handles simple error checking on the responses +# for commands that are unsupported or OK. +#---------------------------------------------------------------------- +sub dump_command +{ + my $cmd_str = shift; + + # Dump the original command string if verbose is on + if ($opt_v) + { + print "dump_command($cmd_str)\n "; + } + + my @cmd_chars = extract_command($cmd_str); + my $is_cmd = 1; + + my $cmd = $cmd_chars[0]; + if ($cmd eq '$') + { + $is_cmd = 0; # Note that this is a reply + $cmd = $curr_cmd; # set the command byte appropriately + shift @cmd_chars; # remove the '$' from the cmd bytes + } + + # Check for common responses across all commands and handle them + # if we can + if ( $is_cmd == 0 ) + { + if (rsp_is_unsupported(@cmd_chars)) + { + print "$unimplemented_str\n"; + return; + } + elsif (rsp_is_OK(@cmd_chars)) + { + print "$success_str\n"; + return; + } + # Strip the checksum information for responses + strip_checksum(\@cmd_chars); + } + + my $callback_ref; + if ($is_cmd) { + $callback_ref = $cmd_callbacks{$cmd}; + } else { + $callback_ref = $rsp_callbacks{$cmd}; + } + + if ($callback_ref) + { + &$callback_ref(@cmd_chars); + } + else + { + # Strip the command byte for responses since we injected that above + if ($is_cmd) { + dump_other_cmd(@cmd_chars); + } else { + dump_other_rsp(@cmd_chars); + } + + } +} + + +#---------------------------------------------------------------------- +# Process a gdbserver log line by looking for getpkt and putkpt and +# tossing any other lines. + +#---------------------------------------------------------------------- +sub process_log_line +{ + my $line = shift; + #($opt_v and $opt_g) and print "# $line"; + + my $extract_cmd = 0; + my $delta_time = 0.0; + if ($line =~ /^(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$/) + { + my $leading_space = $1; + $curr_time = $2; + $line = $3; + if ($base_time == 0.0) + { + $base_time = $curr_time; + } + else + { + $delta_time = $curr_time - $last_time; + } + printf ("(%.6f, %+.6f): ", $curr_time - $base_time, $delta_time); + $last_time = $curr_time; + } + else + { + $curr_time = 0.0 + } + + if ($line =~ /getpkt /) + { + $extract_cmd = 1; + print "\n--> "; + $packet_start_time = $curr_time; + } + elsif ($line =~ /putpkt /) + { + $extract_cmd = 1; + print "<-- "; + } + elsif ($line =~ /.*Sent: \[[0-9]+\.[0-9]+[:0-9]*\] (.*)/) + { + $opt_g and print "maintenance dump-packets command: $1\n"; + my @raw_cmd_bytes = split(/ */, $1); + $packet_start_time = $curr_time; + print "\n--> "; + dump_raw_command(\@raw_cmd_bytes); + process_log_line($2); + } + elsif ($line =~ /.*Recvd: \[[0-9]+\.[0-9]+[:0-9]*\] (.*)/) + { + $opt_g and print "maintenance dump-packets reply: $1\n"; + my @raw_rsp_bytes = split(/ */, $1); + print "<-- "; + dump_raw_response(\@raw_rsp_bytes); + print "\n"; + } + elsif ($line =~ /getpkt: (.*)/) + { + if ($1 =~ /\$([^#]+)#[0-9a-fA-F]{2}/) + { + $opt_g and print "command: $1\n"; + my @raw_cmd_bytes = split(/ */, $1); + print "--> "; + $packet_start_time = $curr_time; + dump_raw_command(\@raw_cmd_bytes); + } + elsif ($1 =~ /\+/) + { + #print "--> ACK\n"; + } + elsif ($1 =~ /-/) + { + #print "--> NACK\n"; + } + } + elsif ($line =~ /putpkt: (.*)/) + { + if ($1 =~ /\$([^#]+)#[0-9a-fA-F]{2}/) + { + $opt_g and print "response: $1\n"; + my @raw_rsp_bytes = split(/ */, $1); + print "<-- "; + dump_raw_response(\@raw_rsp_bytes); + print "\n"; + } + elsif ($1 =~ /\+/) + { + #print "<-- ACK\n"; + } + elsif ($1 =~ /-/) + { + #print "<-- NACK\n"; + } + } + elsif ($line =~ /send packet: (.*)/) + { + if ($1 =~ /\$([^#]+)#[0-9a-fA-F]{2}/) + { + $opt_g and print "command: $1\n"; + my @raw_cmd_bytes = split(/ */, $1); + print "--> "; + $packet_start_time = $curr_time; + dump_raw_command(\@raw_cmd_bytes); + } + elsif ($1 =~ /\+/) + { + #print "--> ACK\n"; + } + elsif ($1 =~ /-/) + { + #print "--> NACK\n"; + } + } + elsif ($line =~ /read packet: (.*)/) + { + if ($1 =~ /\$([^#]*)#[0-9a-fA-F]{2}/) + { + $opt_g and print "response: $1\n"; + my @raw_rsp_bytes = split(/ */, $1); + print "<-- "; + dump_raw_response(\@raw_rsp_bytes); + print "\n"; + } + elsif ($1 =~ /\+/) + { + #print "<-- ACK\n"; + } + elsif ($1 =~ /-/) + { + #print "<-- NACK\n"; + } + } + elsif ($line =~ /Sending packet: \$([^#]+)#[0-9a-fA-F]{2}\.\.\.(.*)/) + { + $opt_g and print "command: $1\n"; + my @raw_cmd_bytes = split(/ */, $1); + print "\n--> "; + $packet_start_time = $curr_time; + dump_raw_command(\@raw_cmd_bytes); + process_log_line($2); + } + elsif ($line =~ /Packet received: (.*)/) + { + $opt_g and print "response: $1\n"; + my @raw_rsp_bytes = split(/ */, $1); + print "<-- "; + dump_raw_response(\@raw_rsp_bytes); + print "\n"; + } + + if ($extract_cmd) + { + my $beg = index($line, '("') + 2; + my $end = rindex($line, '");'); + $packet_start_time = $curr_time; + dump_command(substr($line, $beg, $end - $beg)); + } +} + + +our $line_num = 0; +while(<>) +{ + $line_num++; + $opt_q or printf("# %5d: $_", $line_num); + process_log_line($_); +} + +if (%packet_times) +{ + print "----------------------------------------------------------------------\n"; + print "Packet timing summary:\n"; + print "----------------------------------------------------------------------\n"; + print "Packet Time %\n"; + print "---------------------- -------- ------\n"; + my @packet_names = keys %packet_times; + my $total_packet_times = 0.0; + foreach my $key (@packet_names) + { + $total_packet_times += $packet_times{$key}; + } + + foreach my $value (sort {$packet_times{$b} cmp $packet_times{$a}} @packet_names) + { + my $percent = ($packet_times{$value} / $total_packet_times) * 100.0; + if ($percent < 10.0) + { + printf("%22s %1.6f %2.2f\n", $value, $packet_times{$value}, $percent); + + } + else + { + printf("%22s %1.6f %2.2f\n", $value, $packet_times{$value}, $percent); + } + } + print "---------------------- -------- ------\n"; + printf (" Total %1.6f 100.00\n", $total_packet_times); +} + + + + + + + diff --git a/scripts/finish-swig-wrapper-classes.sh b/scripts/finish-swig-wrapper-classes.sh new file mode 100755 index 000000000000..806f2862af69 --- /dev/null +++ b/scripts/finish-swig-wrapper-classes.sh @@ -0,0 +1,101 @@ +#! /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. + +# SRC_ROOT is the root of the lldb source tree. +# TARGET_DIR is where the lldb framework/shared library gets put. +# CONFIG_BUILD_DIR is where the build-swig-Python-LLDB.sh shell script +# put the lldb.py file it generated from running SWIG. +# PREFIX is the root directory used to determine where third-party modules +# for scripting languages should be installed. +# debug_flag (optional) determines whether or not this script outputs +# additional information when running. + +SRC_ROOT=$1 +TARGET_DIR=$2 +CONFIG_BUILD_DIR=$3 +PREFIX=$4 + +shift 4 + +if [ -n "$1" -a "$1" = "-debug" ] +then + debug_flag=$1 + Debug=1 + shift +else + debug_flag="" + Debug=0 +fi + +if [ -n "$1" -a "$1" = "-m" ] +then + makefile_flag="$1" + shift +else + makefile_flag="" +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=${SRC_ROOT}/scripts + +for curlang in $languages +do + if [ $Debug -eq 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 -eq 1 ] + then + echo "Found $curlang sub-directory" + fi + + cd $cwd/$curlang + + filename="./finish-swig-${curlang}-LLDB.sh" + + if [ -f $filename ] + then + if [ $Debug -eq 1 ] + then + echo "Found $curlang post-processing script for LLDB" + echo "Executing $curlang post-processing script..." + fi + + + ./finish-swig-${curlang}-LLDB.sh $SRC_ROOT $TARGET_DIR $CONFIG_BUILD_DIR "${PREFIX}" "${debug_flag}" "${makefile_flag}" + retval=$? + if [ $retval -ne 0 ]; then + echo "$(pwd)/finish-swig-${curlang}-LLDB.sh failed with exit code $retval" + exit $retval + fi + fi + fi +done + +exit 0 diff --git a/scripts/finishSwigWrapperClasses.py b/scripts/finishSwigWrapperClasses.py new file mode 100644 index 000000000000..8d7d19ef1bdf --- /dev/null +++ b/scripts/finishSwigWrapperClasses.py @@ -0,0 +1,368 @@ +""" Post process SWIG Bridge wrapper code Python script for Windows/LINUX/OSX platform + + -------------------------------------------------------------------------- + File: finishSwigWrapperClasses.py + + Overview: Python script(s) to finish off the SWIG Python C++ Script + Bridge wrapper code on the Windows/LINUX/OSX platform. + The Python scripts are equivalent to the shell script (.sh) + files. + We use SWIG to create a C++ file containing the appropriate + wrapper classes and functions 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 Python scripts called here. + + Gotchas: None. + + Copyright: None. + -------------------------------------------------------------------------- + +""" + +# Python modules: +import sys # Provide argument parsing +import os # Provide directory and file handling + +# Third party modules: + +# In-house modules: +import utilsArgsParse # Parse and validate this script's input arguments +import utilsOsType # Determine the OS type this script is running on +import utilsDebug # Debug Python scripts + +# Instantiations: +gbDbgVerbose = False # True = Turn on script function tracing, False = off. +gbDbgFlag = False # Global debug mode flag, set by input parameter + # --dbgFlag. True = operate in debug mode. +gbMakeFileFlag = False # True = yes called from makefile system, False = not. + +# User facing text: +strMsgErrorNoMain = "Program called by another Python script not allowed" +strExitMsgSuccess = "Program successful" +strExitMsgError = "Program error: " +strParameter = "Parameter: " +strMsgErrorOsTypeUnknown = "Unable to determine OS type" +strScriptDirNotFound = "Unable to locate the script directory \'/script\'" +strScriptLangsFound = "Found the following script languages:" +strPostProcessError = "Executing \'%s\' post process script failed: " +strScriptNotFound = "Unable to locate the post process script file \'%s\' in \'%s\'" +strScriptLangFound = "Found \'%s\' build script." +strScriptLangsFound = "Found the following script languages:" +strExecuteMsg = "Executing \'%s\' build script..." +strExecuteError = "Executing \'%s\' build script failed: " +strHelpInfo = "\ +Python script(s) to finish off the SWIG Python C++ Script \n\ +Bridge wrapper code on the Windows/LINUX/OSX platform. The Python \n\ +scripts are equivalent to the shell script (.sh) files \n\ +run on others platforms.\n\ +Args: -h (optional) Print help information on this program.\n\ + -d (optional) Determines whether or not this script\n\ + outputs additional information when running.\n\ + -m (optional) Specify called from Makefile system.\n\ + --srcRoot= The root of the lldb source tree.\n\ + --targetDir= Where the lldb framework/shared library gets put.\n\ + --cfgBldDir= (optional) Where the build-swig-Python-LLDB.py program \n\ + will put the lldb.py file it generated from running\n\ + SWIG.\n\ + --prefix= (optional) Is the root directory used to determine where\n\ + third-party modules for scripting languages should\n\ + be installed. Where non-Darwin systems want to put\n\ + the .py and .so files so that Python can find them\n\ + automatically. Python install directory.\n\ + --cmakeBuildConfiguration= (optional) Is the build configuration(Debug, Release, RelWithDebugInfo)\n\ + used to determine where the bin and lib directories are \n\ + created for a Windows build.\n\ + --argsFile= The args are read from a file instead of the\n\ + command line. Other command line args are ignored.\n\ +\n\ +Usage:\n\ + finishSwigWrapperClasses.py --srcRoot=ADirPath --targetDir=ADirPath\n\ + --cfgBldDir=ADirPath --prefix=ADirPath -m -d\n\ +\n\ +" #TAG_PROGRAM_HELP_INFO + +#++--------------------------------------------------------------------------- +# Details: Exit the program on success. Called on program successfully done +# its work. Returns a status result to the caller. +# Args: vnResult - (R) 0 or greater indicating success. +# vMsg - (R) Success message if any to show success to user. +# Returns: None. +# Throws: None. +#-- +def program_exit_success(vnResult, vMsg): + strMsg = "" + + if vMsg.__len__() != 0: + strMsg = "%s: %s (%d)" % (strExitMsgSuccess, vMsg, vnResult) + print(strMsg) + + sys.exit(vnResult) + +#++--------------------------------------------------------------------------- +# Details: Exit the program with error. Called on exit program failed its +# task. Returns a status result to the caller. +# Args: vnResult - (R) A negative number indicating error condition. +# vMsg - (R) Error message to show to user. +# Returns: None. +# Throws: None. +#-- +def program_exit_on_failure(vnResult, vMsg): + print(("%s%s (%d)" % (strExitMsgError, vMsg, vnResult))) + sys.exit(vnResult) + +#++--------------------------------------------------------------------------- +# Details: Exit the program return a exit result number and print a message. +# Positive numbers and zero are returned for success other error +# occurred. +# Args: vnResult - (R) A -ve (an error), 0 or +ve number (ok or status). +# vMsg - (R) Error message to show to user. +# Returns: None. +# Throws: None. +#-- +def program_exit(vnResult, vMsg): + if vnResult >= 0: + program_exit_success(vnResult, vMsg) + else: + program_exit_on_failure(vnResult, vMsg) + +#++--------------------------------------------------------------------------- +# Details: Dump input parameters. +# Args: vDictArgs - (R) Map of input args to value. +# Returns: None. +# Throws: None. +#-- +def print_out_input_parameters(vDictArgs): + for arg, val in list(vDictArgs.items()): + strEqs = "" + strQ = "" + if val.__len__() != 0: + strEqs = " =" + strQ = "\"" + print(("%s%s%s %s%s%s\n" % (strParameter, arg, strEqs, strQ, val, strQ))) + +#++--------------------------------------------------------------------------- +# Details: Validate the arguments passed to the program. This function exits +# the program should error with the arguments be found. +# Args: vArgv - (R) List of arguments and values. +# Returns: Int - 0 = success, -ve = some failure. +# Dict - Map of arguments names to argument values +# Throws: None. +#-- +def validate_arguments(vArgv): + dbg = utilsDebug.CDebugFnVerbose("validate_arguments()") + strMsg = "" + dictArgs = {} + nResult = 0 + strListArgs = "hdm" # Format "hiox:" = -h -i -o -x + listLongArgs = ["srcRoot=", "targetDir=", "cfgBldDir=", "prefix=", "cmakeBuildConfiguration=", + "argsFile"] + dictArgReq = { "-h": "o", # o = optional, m = mandatory + "-d": "o", + "-m": "o", + "--srcRoot": "m", + "--targetDir": "m", + "--cfgBldDir": "o", + "--prefix": "o", + "--cmakeBuildConfiguration": "o", + "--argsFile": "o" } + + # Check for mandatory parameters + nResult, dictArgs, strMsg = utilsArgsParse.parse(vArgv, strListArgs, + listLongArgs, + dictArgReq, + strHelpInfo) + if nResult < 0: + program_exit_on_failure(nResult, strMsg) + + # User input -h for help + if nResult == 1: + program_exit_success(0, strMsg) + + return (nResult, dictArgs) + +#++--------------------------------------------------------------------------- +# Details: Locate post process script language directory and the script within +# and execute. +# Args: vStrScriptLang - (R) Name of the script language to build. +# vstrFinishFileName - (R) Prefix file name to build full name. +# vDictArgs - (R) Program input parameters. +# Returns: Int - 0 = Success, < 0 some error condition. +# Str - Error message. +# Throws: None. +#-- +def run_post_process(vStrScriptLang, vstrFinishFileName, vDictArgs): + dbg = utilsDebug.CDebugFnVerbose("run_post_process()") + nResult = 0 + strStatusMsg = "" + strScriptFile = vstrFinishFileName % vStrScriptLang + strScriptFileDir = os.path.normpath(os.path.join(vDictArgs["--srcRoot"], "scripts", vStrScriptLang)) + strScriptFilePath = os.path.join(strScriptFileDir, strScriptFile) + + # Check for the existence of the script file + strPath = os.path.normcase(strScriptFilePath) + bOk = os.path.exists(strPath) + if bOk == False: + strDir = os.path.normcase(strScriptFileDir) + strStatusMsg = strScriptNotFound % (strScriptFile, strDir) + return (-9, strStatusMsg) + + if gbDbgFlag: + print((strScriptLangFound % vStrScriptLang)) + print((strExecuteMsg % vStrScriptLang)) + + # Change where Python looks for our modules + strDir = os.path.normcase(strScriptFileDir) + sys.path.append(strDir) + + # Execute the specific language script + dictArgs = vDictArgs # Remove any args not required before passing on + strModuleName = strScriptFile[: strScriptFile.__len__() - 3] + module = __import__(strModuleName) + nResult, strStatusMsg = module.main(dictArgs) + + # Revert sys path + sys.path.remove(strDir) + + return (nResult, strStatusMsg) + +#++--------------------------------------------------------------------------- +# Details: Step through each script language sub directory supported +# and execute post processing script for each scripting language, +# make sure the build script for that language exists. +# For now the only language we support is Python, but we expect this +# to change. +# Args: vDictArgs - (R) Program input parameters. +# Returns: Int - 0 = Success, < 0 some error condition. +# Str - Error message. +# Throws: None. +#-- +def run_post_process_for_each_script_supported(vDictArgs): + dbg = utilsDebug.CDebugFnVerbose("run_post_process_for_each_script_supported()") + nResult = 0 + strStatusMsg = "" + strScriptDir = os.path.normpath(os.path.join(vDictArgs["--srcRoot"], "scripts")) + strFinishFileName = "finishSwig%sLLDB.py" + + # Check for the existence of the scripts folder + strScriptsDir = os.path.normcase(strScriptDir) + bOk = os.path.exists(strScriptsDir) + if bOk == False: + return (-8, strScriptDirNotFound) + + # Look for any script language directories to build for + listDirs = ["Python"] + + # Iterate script directory find any script language directories + for scriptLang in listDirs: + # __pycache__ is a magic directory in Python 3 that holds .pyc files + if scriptLang != "__pycache__" and scriptLang != "swig_bot_lib": + dbg.dump_text("Executing language script for \'%s\'" % scriptLang) + nResult, strStatusMsg = run_post_process(scriptLang, strFinishFileName, + vDictArgs) + if nResult < 0: + break + + if nResult < 0: + strTmp = strPostProcessError % scriptLang + strTmp += strStatusMsg + strStatusMsg = strTmp + + return (nResult, strStatusMsg) + +#++--------------------------------------------------------------------------- +# Details: Program's main() with arguments passed in from the command line. +# Program either exits normally or with error from this function - +# top most level function. +# Args: vArgv - (R) List of arguments and values. +# Returns: None +# Throws: None. +#-- +def main(vArgv): + dbg = utilsDebug.CDebugFnVerbose("main()") + bOk = False + dictArgs = {} + nResult = 0 + strMsg = "" + + # The validate arguments fn will exit the program if tests fail + nResult, dictArgs = validate_arguments(vArgv) + + eOSType = utilsOsType.determine_os_type() + if eOSType == utilsOsType.EnumOsType.Unknown: + program_exit(-4, strMsgErrorOsTypeUnknown) + + global gbDbgFlag + gbDbgFlag = "-d" in dictArgs + if gbDbgFlag: + print_out_input_parameters(dictArgs) + + # Check to see if we were called from the Makefile system. If we were, check + # if the caller wants SWIG to generate a dependency file. + # Not used in this program, but passed through to the language script file + # called by this program + global gbMakeFileFlag + gbMakeFileFlag = "-m" in dictArgs + + nResult, strMsg = run_post_process_for_each_script_supported(dictArgs) + + program_exit(nResult, strMsg) + +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- + +#TAG_PROGRAM_HELP_INFO +""" Details: Program main entry point. + + -------------------------------------------------------------------------- + Args: -h (optional) Print help information on this program. + -d (optional) Determines whether or not this script + outputs additional information when running. + -m (optional) Specify called from Makefile system. If given locate + the LLDBWrapPython.cpp in --srcRoot/source folder + else in the --targetDir folder. + --srcRoot= The root of the lldb source tree. + --targetDir= Where the lldb framework/shared library gets put. + --cfgBldDir= Where the buildSwigPythonLLDB.py program will + (optional) put the lldb.py file it generated from running + SWIG. + --prefix= Is the root directory used to determine where + (optional) third-party modules for scripting languages should + be installed. Where non-Darwin systems want to put + the .py and .so files so that Python can find them + automatically. Python install directory. + --cmakeBuildConfiguration= (optional) Is the build configuration(Debug, Release, RelWithDebugInfo)\n\ + used to determine where the bin and lib directories are \n\ + created for a Windows build.\n\ + --argsFile= The args are read from a file instead of the + command line. Other command line args are ignored. + Usage: + finishSwigWrapperClasses.py --srcRoot=ADirPath --targetDir=ADirPath + --cfgBldDir=ADirPath --prefix=ADirPath -m -d + + Results: 0 Success + -1 Error - invalid parameters passed. + -2 Error - incorrect number of mandatory parameters passed. + + -4 Error - unable to determine OS type. + -5 Error - program not run with name of "__main__". + -8 Error - unable to locate the scripts folder. + -9 Error - unable to locate the post process language script + file. + + -100+ - Error messages from the child language script file. + + -------------------------------------------------------------------------- + +""" + +# Called using "__main__" when not imported i.e. from the command line +if __name__ == "__main__": + utilsDebug.CDebugFnVerbose.bVerboseOn = gbDbgVerbose + dbg = utilsDebug.CDebugFnVerbose("__main__") + main(sys.argv[1:]) +else: + program_exit(-5, strMsgErrorNoMain) diff --git a/scripts/generate-vers.pl b/scripts/generate-vers.pl new file mode 100755 index 000000000000..63374981e01e --- /dev/null +++ b/scripts/generate-vers.pl @@ -0,0 +1,56 @@ +#!/usr/bin/perl + +sub usage() +{ + print "Usage: generate-vers.pl /path/toproject.pbxproj program_name"; + exit(0); +} + +(scalar @ARGV == 2) or usage(); + +open $pbxproj, $ARGV[0] or die "Couldn't open ".$ARGV[0]; + +$lldb_version = None; +$lldb_train = None; +$lldb_revision = None; +$lldb_version_string = None; + +$product_name = "lldb"; + +while ($line = <$pbxproj>) +{ + chomp ($line); + + if ($lldb_version == None && + $line =~ /CURRENT_PROJECT_VERSION = ([0-9]+).([0-9]+).([0-9]+)(.[0-9])?/) + { + $lldb_version = $1; + $lldb_train = $2; + $lldb_revision = $3; + $lldb_patchlevel = $4; + + if ($lldb_patchlevel != None) + { + $lldb_version_string = $lldb_version.".".$lldb_train.".".$lldb_revision.".".$lldb_patchlevel; + } + else + { + $lldb_version_string = $lldb_version.".".$lldb_train.".".$lldb_revision; + } + } +} + +if (!$product_name || !$lldb_version_string) +{ + print "Couldn't get needed information from the .pbxproj"; + exit(-1); +} + +$uppercase_name = uc $product_name; +$lowercase_name = lc $product_name; + +close $pbxproj; + +$file_string = " const unsigned char ".$ARGV[1]."VersionString[] __attribute__ ((used)) = \"@(#)PROGRAM:".$uppercase_name." PROJECT:".$lowercase_name."-".$lldb_version_string."\" \"\\n\"; const double ".$ARGV[1]."VersionNumber __attribute__ ((used)) = (double)".$lldb_version.".".$lldb_train.";\n"; + +print $file_string; diff --git a/scripts/get_relative_lib_dir.py b/scripts/get_relative_lib_dir.py new file mode 100644 index 000000000000..f7020d653fdd --- /dev/null +++ b/scripts/get_relative_lib_dir.py @@ -0,0 +1,44 @@ +import distutils.sysconfig +import os +import platform +import re +import sys + + +def get_python_relative_libdir(): + """Returns the appropropriate python libdir relative to the build directory. + + @param exe_path the path to the lldb executable + + @return the python path that needs to be added to sys.path (PYTHONPATH) + in order to find the lldb python module. + """ + if platform.system() != 'Linux': + return None + + # We currently have a bug in lldb -P that does not account for + # architecture variants in python paths for + # architecture-specific modules. Handle the lookup here. + # When that bug is fixed, we should just ask lldb for the + # right answer always. + arch_specific_libdir = distutils.sysconfig.get_python_lib(True, False) + split_libdir = arch_specific_libdir.split(os.sep) + lib_re = re.compile(r"^lib.+$") + + for i in range(len(split_libdir)): + match = lib_re.match(split_libdir[i]) + if match is not None: + # We'll call this the relative root of the lib dir. + # Things like RHEL will have an arch-specific python + # lib dir, which isn't 'lib' on x86_64. + return os.sep.join(split_libdir[i:]) + # Didn't resolve it. + return None + +if __name__ == '__main__': + lib_dir = get_python_relative_libdir() + if lib_dir is not None: + sys.stdout.write(lib_dir) + sys.exit(0) + else: + sys.exit(1) diff --git a/scripts/install-lldb.sh b/scripts/install-lldb.sh new file mode 100755 index 000000000000..0ba4e7c5ee2b --- /dev/null +++ b/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/scripts/install_custom_python.py b/scripts/install_custom_python.py new file mode 100644 index 000000000000..5a8f48aac949 --- /dev/null +++ b/scripts/install_custom_python.py @@ -0,0 +1,134 @@ +""" Copies the build output of a custom python interpreter to a directory + structure that mirrors that of an official Python distribution. + + -------------------------------------------------------------------------- + File: install_custom_python.py + + Overview: Most users build LLDB by linking against the standard + Python distribution installed on their system. Occasionally + a user may want to build their own version of Python, and on + platforms such as Windows this is a hard requirement. This + script will take the build output of a custom interpreter and + install it into a canonical structure that mirrors that of an + official Python distribution, thus allowing PYTHONHOME to be + set appropriately. + + Gotchas: None. + + Copyright: None. + -------------------------------------------------------------------------- + +""" + +import argparse +import itertools +import os +import shutil +import sys + +def copy_one_file(dest_dir, source_dir, filename): + source_path = os.path.join(source_dir, filename) + dest_path = os.path.join(dest_dir, filename) + print 'Copying file %s ==> %s...' % (source_path, dest_path) + shutil.copyfile(source_path, dest_path) + +def copy_named_files(dest_dir, source_dir, files, extensions, copy_debug_suffix_also): + for (file, ext) in itertools.product(files, extensions): + copy_one_file(dest_dir, source_dir, file + '.' + ext) + if copy_debug_suffix_also: + copy_one_file(dest_dir, source_dir, file + '_d.' + ext) + +def copy_subdirectory(dest_dir, source_dir, subdir): + dest_dir = os.path.join(dest_dir, subdir) + source_dir = os.path.join(source_dir, subdir) + print 'Copying directory %s ==> %s...' % (source_dir, dest_dir) + shutil.copytree(source_dir, dest_dir) + +def copy_distro(dest_dir, dest_subdir, source_dir, source_prefix): + dest_dir = os.path.join(dest_dir, dest_subdir) + + print 'Copying distribution %s ==> %s' % (source_dir, dest_dir) + + os.mkdir(dest_dir) + PCbuild_dir = os.path.join(source_dir, 'PCbuild') + if source_prefix: + PCbuild_dir = os.path.join(PCbuild_dir, source_prefix) + # First copy the files that go into the root of the new distribution. This + # includes the Python executables, python27(_d).dll, and relevant PDB files. + print 'Copying Python executables...' + copy_named_files(dest_dir, PCbuild_dir, ['w9xpopen'], ['exe', 'pdb'], False) + copy_named_files(dest_dir, PCbuild_dir, ['python_d', 'pythonw_d'], ['exe'], False) + copy_named_files(dest_dir, PCbuild_dir, ['python', 'pythonw'], ['exe', 'pdb'], False) + copy_named_files(dest_dir, PCbuild_dir, ['python27'], ['dll', 'pdb'], True) + + # Next copy everything in the Include directory. + print 'Copying Python include directory' + copy_subdirectory(dest_dir, source_dir, 'Include') + + # Copy Lib folder (builtin Python modules) + print 'Copying Python Lib directory' + copy_subdirectory(dest_dir, source_dir, 'Lib') + + # Copy tools folder. These are probably not necessary, but we copy them anyway to + # match an official distribution as closely as possible. Note that we don't just copy + # the subdirectory recursively. The source distribution ships with many more tools + # than what you get by installing python regularly. We only copy the tools that appear + # in an installed distribution. + tools_dest_dir = os.path.join(dest_dir, 'Tools') + tools_source_dir = os.path.join(source_dir, 'Tools') + os.mkdir(tools_dest_dir) + copy_subdirectory(tools_dest_dir, tools_source_dir, 'i18n') + copy_subdirectory(tools_dest_dir, tools_source_dir, 'pynche') + copy_subdirectory(tools_dest_dir, tools_source_dir, 'scripts') + copy_subdirectory(tools_dest_dir, tools_source_dir, 'versioncheck') + copy_subdirectory(tools_dest_dir, tools_source_dir, 'webchecker') + + pyd_names = ['_ctypes', '_ctypes_test', '_elementtree', '_multiprocessing', '_socket', + '_testcapi', 'pyexpat', 'select', 'unicodedata', 'winsound'] + + # Copy builtin extension modules (pyd files) + dlls_dir = os.path.join(dest_dir, 'DLLs') + os.mkdir(dlls_dir) + print 'Copying DLLs directory' + copy_named_files(dlls_dir, PCbuild_dir, pyd_names, ['pyd', 'pdb'], True) + + # Copy libs folder (implibs for the pyd files) + libs_dir = os.path.join(dest_dir, 'libs') + os.mkdir(libs_dir) + print 'Copying libs directory' + copy_named_files(libs_dir, PCbuild_dir, pyd_names, ['lib'], False) + copy_named_files(libs_dir, PCbuild_dir, ['python27'], ['lib'], True) + + +parser = argparse.ArgumentParser(description='Install a custom Python distribution') +parser.add_argument('--source', required=True, help='The root of the source tree where Python is built.') +parser.add_argument('--dest', required=True, help='The location to install the Python distributions.') +parser.add_argument('--overwrite', default=False, action='store_true', help='If the destination directory already exists, destroys its contents first.') +parser.add_argument('--silent', default=False, action='store_true', help='If --overwite was specified, suppress confirmation before deleting a directory tree.') + +args = parser.parse_args() + +args.source = os.path.normpath(args.source) +args.dest = os.path.normpath(args.dest) + +if not os.path.exists(args.source): + print 'The source directory %s does not exist. Exiting...' + sys.exit(1) + +if os.path.exists(args.dest): + if not args.overwrite: + print 'The destination directory \'%s\' already exists and --overwrite was not specified. Exiting...' % args.dest + sys.exit(1) + while not args.silent: + print 'Ok to recursively delete \'%s\' and all contents (Y/N)? Choosing Y will permanently delete the contents.' % args.dest + result = str.upper(sys.stdin.read(1)) + if result == 'N': + print 'Unable to copy files to the destination. The destination already exists.' + sys.exit(1) + elif result == 'Y': + break + shutil.rmtree(args.dest) + +os.mkdir(args.dest) +copy_distro(args.dest, 'x86', args.source, None) +copy_distro(args.dest, 'x64', args.source, 'amd64') diff --git a/scripts/interface/SBAddress.i b/scripts/interface/SBAddress.i new file mode 100644 index 000000000000..735b42c1d5b6 --- /dev/null +++ b/scripts/interface/SBAddress.i @@ -0,0 +1,201 @@ +//===-- SWIG Interface for SBAddress ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"A section + offset based address class. + +The SBAddress 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: + o file addresses + o load addresses + +File addresses represents 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 represents 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. + +See docstring of SBFunction for example usage of SBAddress." +) SBAddress; +class SBAddress +{ +public: + + SBAddress (); + + SBAddress (const lldb::SBAddress &rhs); + + SBAddress (lldb::SBSection section, + lldb::addr_t offset); + + %feature("docstring", " + Create an address by resolving a load address using the supplied target. + ") SBAddress; + SBAddress (lldb::addr_t load_addr, lldb::SBTarget &target); + + ~SBAddress (); + + bool + IsValid () const; + + void + Clear (); + + addr_t + GetFileAddress () const; + + addr_t + GetLoadAddress (const lldb::SBTarget &target) const; + + void + SetLoadAddress (lldb::addr_t load_addr, + lldb::SBTarget &target); + + bool + OffsetAddress (addr_t offset); + + bool + GetDescription (lldb::SBStream &description); + + lldb::SBSection + GetSection (); + + lldb::addr_t + SBAddress::GetOffset (); + + void + SetAddress (lldb::SBSection section, + lldb::addr_t offset); + + + lldb::AddressClass + GetAddressClass (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// GetSymbolContext() and the following can lookup symbol information for a given address. + /// An address might refer to code or data from an existing module, or it + /// might refer to something on the stack or heap. The following functions + /// will only return valid values if the address has been resolved to a code + /// or data address using 'void SBAddress::SetLoadAddress(...)' or + /// 'lldb::SBAddress SBTarget::ResolveLoadAddress (...)'. + //------------------------------------------------------------------ + ") GetSymbolContext; + lldb::SBSymbolContext + GetSymbolContext (uint32_t resolve_scope); + + %feature("docstring", " + //------------------------------------------------------------------ + /// GetModule() and the following grab individual objects for a given address and + /// are less efficient if you want more than one symbol related objects. + /// Use one of the following when you want multiple debug symbol related + /// objects for an address: + /// lldb::SBSymbolContext SBAddress::GetSymbolContext (uint32_t resolve_scope); + /// lldb::SBSymbolContext SBTarget::ResolveSymbolContextForAddress (const SBAddress &addr, uint32_t resolve_scope); + /// One or more bits from the SymbolContextItem enumerations can be logically + /// OR'ed together to more efficiently retrieve multiple symbol objects. + //------------------------------------------------------------------ + ") GetModule; + lldb::SBModule + GetModule (); + + lldb::SBCompileUnit + GetCompileUnit (); + + lldb::SBFunction + GetFunction (); + + lldb::SBBlock + GetBlock (); + + lldb::SBSymbol + GetSymbol (); + + lldb::SBLineEntry + GetLineEntry (); + + %pythoncode %{ + def __get_load_addr_property__ (self): + '''Get the load address for a lldb.SBAddress using the current target.''' + return self.GetLoadAddress (target) + + def __set_load_addr_property__ (self, load_addr): + '''Set the load address for a lldb.SBAddress using the current target.''' + return self.SetLoadAddress (load_addr, target) + + def __int__(self): + '''Convert an address to a load address if there is a process and that process is alive, or to a file address otherwise.''' + if process.is_alive: + return self.GetLoadAddress (target) + else: + return self.GetFileAddress () + + def __oct__(self): + '''Convert the address to an octal string''' + return '%o' % int(self) + + def __hex__(self): + '''Convert the address to an hex string''' + return '0x%x' % int(self) + + __swig_getmethods__["module"] = GetModule + if _newclass: module = property(GetModule, None, doc='''A read only property that returns an lldb object that represents the module (lldb.SBModule) that this address resides within.''') + + __swig_getmethods__["compile_unit"] = GetCompileUnit + if _newclass: compile_unit = property(GetCompileUnit, None, doc='''A read only property that returns an lldb object that represents the compile unit (lldb.SBCompileUnit) that this address resides within.''') + + __swig_getmethods__["line_entry"] = GetLineEntry + if _newclass: line_entry = property(GetLineEntry, None, doc='''A read only property that returns an lldb object that represents the line entry (lldb.SBLineEntry) that this address resides within.''') + + __swig_getmethods__["function"] = GetFunction + if _newclass: function = property(GetFunction, None, doc='''A read only property that returns an lldb object that represents the function (lldb.SBFunction) that this address resides within.''') + + __swig_getmethods__["block"] = GetBlock + if _newclass: block = property(GetBlock, None, doc='''A read only property that returns an lldb object that represents the block (lldb.SBBlock) that this address resides within.''') + + __swig_getmethods__["symbol"] = GetSymbol + if _newclass: symbol = property(GetSymbol, None, doc='''A read only property that returns an lldb object that represents the symbol (lldb.SBSymbol) that this address resides within.''') + + __swig_getmethods__["offset"] = GetOffset + if _newclass: offset = property(GetOffset, None, doc='''A read only property that returns the section offset in bytes as an integer.''') + + __swig_getmethods__["section"] = GetSection + if _newclass: section = property(GetSection, None, doc='''A read only property that returns an lldb object that represents the section (lldb.SBSection) that this address resides within.''') + + __swig_getmethods__["file_addr"] = GetFileAddress + if _newclass: file_addr = property(GetFileAddress, None, doc='''A read only property that returns file address for the section as an integer. This is the address that represents the address as it is found in the object file that defines it.''') + + __swig_getmethods__["load_addr"] = __get_load_addr_property__ + __swig_setmethods__["load_addr"] = __set_load_addr_property__ + if _newclass: load_addr = property(__get_load_addr_property__, __set_load_addr_property__, doc='''A read/write property that gets/sets the SBAddress using load address. The setter resolves SBAddress using the SBTarget from lldb.target so this property can ONLY be used in the interactive script interpreter (i.e. under the lldb script command) and not in Python based commands, or breakpoint commands.''') + + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBAttachInfo.i b/scripts/interface/SBAttachInfo.i new file mode 100644 index 000000000000..4c8ef643c90a --- /dev/null +++ b/scripts/interface/SBAttachInfo.i @@ -0,0 +1,116 @@ +//===-- SWIG Interface for SBAttachInfo--------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBAttachInfo +{ +public: + SBAttachInfo (); + + SBAttachInfo (lldb::pid_t pid); + + SBAttachInfo (const char *path, bool wait_for); + + SBAttachInfo (const char *path, bool wait_for, bool async); + + SBAttachInfo (const lldb::SBAttachInfo &rhs); + + lldb::pid_t + GetProcessID (); + + void + SetProcessID (lldb::pid_t pid); + + void + SetExecutable (const char *path); + + void + SetExecutable (lldb::SBFileSpec exe_file); + + bool + GetWaitForLaunch (); + + void + SetWaitForLaunch (bool b); + + void + SetWaitForLaunch (bool b, bool async); + + bool + GetIgnoreExisting (); + + void + SetIgnoreExisting (bool b); + + uint32_t + GetResumeCount (); + + void + SetResumeCount (uint32_t c); + + const char * + GetProcessPluginName (); + + void + SetProcessPluginName (const char *plugin_name); + + uint32_t + GetUserID(); + + uint32_t + GetGroupID(); + + bool + UserIDIsValid (); + + bool + GroupIDIsValid (); + + void + SetUserID (uint32_t uid); + + void + SetGroupID (uint32_t gid); + + uint32_t + GetEffectiveUserID(); + + uint32_t + GetEffectiveGroupID(); + + bool + EffectiveUserIDIsValid (); + + bool + EffectiveGroupIDIsValid (); + + void + SetEffectiveUserID (uint32_t uid); + + void + SetEffectiveGroupID (uint32_t gid); + + lldb::pid_t + GetParentProcessID (); + + void + SetParentProcessID (lldb::pid_t pid); + + bool + ParentProcessIDIsValid(); + + lldb::SBListener + GetListener (); + + void + SetListener (lldb::SBListener &listener); +}; + +} // namespace lldb diff --git a/scripts/interface/SBBlock.i b/scripts/interface/SBBlock.i new file mode 100644 index 000000000000..9180ef581d57 --- /dev/null +++ b/scripts/interface/SBBlock.i @@ -0,0 +1,179 @@ +//===-- SWIG Interface for SBBlock ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a lexical block. SBFunction contains SBBlock(s)." +) SBBlock; +class SBBlock +{ +public: + + SBBlock (); + + SBBlock (const lldb::SBBlock &rhs); + + ~SBBlock (); + + %feature("docstring", + "Does this block represent an inlined function?" + ) IsInlined; + bool + IsInlined () const; + + bool + IsValid () const; + + %feature("docstring", " + Get the function name if this block represents an inlined function; + otherwise, return None. + ") GetInlinedName; + const char * + GetInlinedName () const; + + %feature("docstring", " + Get the call site file if this block represents an inlined function; + otherwise, return an invalid file spec. + ") GetInlinedCallSiteFile; + lldb::SBFileSpec + GetInlinedCallSiteFile () const; + + %feature("docstring", " + Get the call site line if this block represents an inlined function; + otherwise, return 0. + ") GetInlinedCallSiteLine; + uint32_t + GetInlinedCallSiteLine () const; + + %feature("docstring", " + Get the call site column if this block represents an inlined function; + otherwise, return 0. + ") GetInlinedCallSiteColumn; + uint32_t + GetInlinedCallSiteColumn () const; + + %feature("docstring", "Get the parent block.") GetParent; + lldb::SBBlock + GetParent (); + + %feature("docstring", "Get the inlined block that is or contains this block.") GetContainingInlinedBlock; + lldb::SBBlock + GetContainingInlinedBlock (); + + %feature("docstring", "Get the sibling block for this block.") GetSibling; + lldb::SBBlock + GetSibling (); + + %feature("docstring", "Get the first child block.") GetFirstChild; + lldb::SBBlock + GetFirstChild (); + + uint32_t + GetNumRanges (); + + lldb::SBAddress + GetRangeStartAddress (uint32_t idx); + + lldb::SBAddress + GetRangeEndAddress (uint32_t idx); + + uint32_t + GetRangeIndexForBlockAddress (lldb::SBAddress block_addr); + + bool + GetDescription (lldb::SBStream &description); + + lldb::SBValueList + GetVariables (lldb::SBFrame& frame, + bool arguments, + bool locals, + bool statics, + lldb::DynamicValueType use_dynamic); + + lldb::SBValueList + GetVariables (lldb::SBTarget& target, + bool arguments, + bool locals, + bool statics); + + %pythoncode %{ + def get_range_at_index(self, idx): + if idx < self.GetNumRanges(): + return [self.GetRangeStartAddress(idx), self.GetRangeEndAddress(idx)] + return [] + + class ranges_access(object): + '''A helper object that will lazily hand out an array of lldb.SBAddress that represent address ranges for a block.''' + def __init__(self, sbblock): + self.sbblock = sbblock + + def __len__(self): + if self.sbblock: + return int(self.sbblock.GetNumRanges()) + return 0 + + def __getitem__(self, key): + count = len(self) + if type(key) is int: + return self.sbblock.get_range_at_index (key); + if isinstance(key, SBAddress): + range_idx = self.sbblock.GetRangeIndexForBlockAddress(key); + if range_idx < len(self): + return [self.sbblock.GetRangeStartAddress(range_idx), self.sbblock.GetRangeEndAddress(range_idx)] + else: + print("error: unsupported item type: %s" % type(key)) + return None + + def get_ranges_access_object(self): + '''An accessor function that returns a ranges_access() object which allows lazy block address ranges access.''' + return self.ranges_access (self) + + def get_ranges_array(self): + '''An accessor function that returns an array object that contains all ranges in this block object.''' + if not hasattr(self, 'ranges_array'): + self.ranges_array = [] + for idx in range(self.num_ranges): + self.ranges_array.append ([self.GetRangeStartAddress(idx), self.GetRangeEndAddress(idx)]) + return self.ranges_array + + def get_call_site(self): + return declaration(self.GetInlinedCallSiteFile(), self.GetInlinedCallSiteLine(), self.GetInlinedCallSiteColumn()) + + __swig_getmethods__["parent"] = GetParent + if _newclass: parent = property(GetParent, None, doc='''A read only property that returns the same result as GetParent().''') + + __swig_getmethods__["first_child"] = GetFirstChild + if _newclass: first_child = property(GetFirstChild, None, doc='''A read only property that returns the same result as GetFirstChild().''') + + __swig_getmethods__["call_site"] = get_call_site + if _newclass: call_site = property(get_call_site, None, doc='''A read only property that returns a lldb.declaration object that contains the inlined call site file, line and column.''') + + __swig_getmethods__["sibling"] = GetSibling + if _newclass: sibling = property(GetSibling, None, doc='''A read only property that returns the same result as GetSibling().''') + + __swig_getmethods__["name"] = GetInlinedName + if _newclass: name = property(GetInlinedName, None, doc='''A read only property that returns the same result as GetInlinedName().''') + + __swig_getmethods__["inlined_block"] = GetContainingInlinedBlock + if _newclass: inlined_block = property(GetContainingInlinedBlock, None, doc='''A read only property that returns the same result as GetContainingInlinedBlock().''') + + __swig_getmethods__["range"] = get_ranges_access_object + if _newclass: range = property(get_ranges_access_object, None, doc='''A read only property that allows item access to the address ranges for a block by integer (range = block.range[0]) and by lldb.SBAdddress (find the range that contains the specified lldb.SBAddress like "pc_range = lldb.frame.block.range[frame.addr]").''') + + __swig_getmethods__["ranges"] = get_ranges_array + if _newclass: ranges = property(get_ranges_array, None, doc='''A read only property that returns a list() object that contains all of the address ranges for the block.''') + + __swig_getmethods__["num_ranges"] = GetNumRanges + if _newclass: num_ranges = property(GetNumRanges, None, doc='''A read only property that returns the same result as GetNumRanges().''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBBreakpoint.i b/scripts/interface/SBBreakpoint.i new file mode 100644 index 000000000000..6394d741b851 --- /dev/null +++ b/scripts/interface/SBBreakpoint.i @@ -0,0 +1,266 @@ +//===-- SWIG Interface for SBBreakpoint -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a logical breakpoint and its associated settings. + +For example (from test/functionalities/breakpoint/breakpoint_ignore_count/ +TestBreakpointIgnoreCount.py), + + def breakpoint_ignore_count_python(self): + '''Use Python APIs to set breakpoint ignore count.''' + exe = os.path.join(os.getcwd(), 'a.out') + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Get the breakpoint location from breakpoint after we verified that, + # indeed, it has one location. + location = breakpoint.GetLocationAtIndex(0) + self.assertTrue(location and + location.IsEnabled(), + VALID_BREAKPOINT_LOCATION) + + # Set the ignore count on the breakpoint location. + location.SetIgnoreCount(2) + self.assertTrue(location.GetIgnoreCount() == 2, + 'SetIgnoreCount() works correctly') + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple(None, None, os.getcwd()) + self.assertTrue(process, PROCESS_IS_VALID) + + # Frame#0 should be on main.c:37, frame#1 should be on main.c:25, and + # frame#2 should be on main.c:48. + #lldbutil.print_stacktraces(process) + from lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread != None, 'There should be a thread stopped due to breakpoint') + frame0 = thread.GetFrameAtIndex(0) + frame1 = thread.GetFrameAtIndex(1) + frame2 = thread.GetFrameAtIndex(2) + self.assertTrue(frame0.GetLineEntry().GetLine() == self.line1 and + frame1.GetLineEntry().GetLine() == self.line3 and + frame2.GetLineEntry().GetLine() == self.line4, + STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT) + + # The hit count for the breakpoint should be 3. + self.assertTrue(breakpoint.GetHitCount() == 3) + + process.Continue() + +SBBreakpoint supports breakpoint location iteration, for example, + + for bl in breakpoint: + print('breakpoint location load addr: %s' % hex(bl.GetLoadAddress())) + print('breakpoint location condition: %s' % hex(bl.GetCondition())) + +and rich comparion methods which allow the API program to use, + + if aBreakpoint == bBreakpoint: + ... + +to compare two breakpoints for equality." +) SBBreakpoint; +class SBBreakpoint +{ +public: + + typedef bool (*BreakpointHitCallback) (void *baton, + SBProcess &process, + SBThread &thread, + lldb::SBBreakpointLocation &location); + + SBBreakpoint (); + + SBBreakpoint (const lldb::SBBreakpoint& rhs); + + ~SBBreakpoint(); + + break_id_t + GetID () const; + + bool + IsValid() const; + + void + ClearAllBreakpointSites (); + + lldb::SBBreakpointLocation + FindLocationByAddress (lldb::addr_t vm_addr); + + lldb::break_id_t + FindLocationIDByAddress (lldb::addr_t vm_addr); + + lldb::SBBreakpointLocation + FindLocationByID (lldb::break_id_t bp_loc_id); + + lldb::SBBreakpointLocation + GetLocationAtIndex (uint32_t index); + + void + SetEnabled (bool enable); + + bool + IsEnabled (); + + void + SetOneShot (bool one_shot); + + bool + IsOneShot (); + + bool + IsInternal (); + + uint32_t + GetHitCount () const; + + void + SetIgnoreCount (uint32_t count); + + uint32_t + GetIgnoreCount () const; + + %feature("docstring", " + //-------------------------------------------------------------------------- + /// The breakpoint stops only if the condition expression evaluates to true. + //-------------------------------------------------------------------------- + ") SetCondition; + void + SetCondition (const char *condition); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get the condition expression for the breakpoint. + //------------------------------------------------------------------ + ") GetCondition; + const char * + GetCondition (); + + void + SetThreadID (lldb::tid_t sb_thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + %feature("docstring", " + //------------------------------------------------------------------ + /// Set the name of the script function to be called when the breakpoint is hit. + //------------------------------------------------------------------ + ") SetScriptCallbackFunction; + void + SetScriptCallbackFunction (const char *callback_function_name); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Provide the body for the script function to be called when the breakpoint is hit. + /// The body will be wrapped in a function, which be passed two arguments: + /// 'frame' - which holds the bottom-most SBFrame of the thread that hit the breakpoint + /// 'bpno' - which is the SBBreakpointLocation to which the callback was attached. + /// + /// The error parameter is currently ignored, but will at some point hold the Python + /// compilation diagnostics. + /// Returns true if the body compiles successfully, false if not. + //------------------------------------------------------------------ + ") SetScriptCallbackBody; + SBError + SetScriptCallbackBody (const char *script_body_text); + + bool + AddName (const char *new_name); + + void + RemoveName (const char *name_to_remove); + + bool + MatchesName (const char *name); + + void + GetNames (SBStringList &names); + + size_t + GetNumResolvedLocations() const; + + size_t + GetNumLocations() const; + + bool + GetDescription (lldb::SBStream &description); + + bool + operator == (const lldb::SBBreakpoint& rhs); + + bool + operator != (const lldb::SBBreakpoint& rhs); + + static bool + EventIsBreakpointEvent (const lldb::SBEvent &event); + + static lldb::BreakpointEventType + GetBreakpointEventTypeFromEvent (const lldb::SBEvent& event); + + static lldb::SBBreakpoint + GetBreakpointFromEvent (const lldb::SBEvent& event); + + static lldb::SBBreakpointLocation + GetBreakpointLocationAtIndexFromEvent (const lldb::SBEvent& event, uint32_t loc_idx); + + static uint32_t + GetNumBreakpointLocationsFromEvent (const lldb::SBEvent &event_sp); + + %pythoncode %{ + + __swig_getmethods__["id"] = GetID + if _newclass: id = property(GetID, None, doc='''A read only property that returns the ID of this breakpoint.''') + + __swig_getmethods__["enabled"] = IsEnabled + __swig_setmethods__["enabled"] = SetEnabled + if _newclass: enabled = property(IsEnabled, SetEnabled, doc='''A read/write property that configures whether this breakpoint is enabled or not.''') + + __swig_getmethods__["one_shot"] = IsOneShot + __swig_setmethods__["one_shot"] = SetOneShot + if _newclass: one_shot = property(IsOneShot, SetOneShot, doc='''A read/write property that configures whether this breakpoint is one-shot (deleted when hit) or not.''') + + __swig_getmethods__["num_locations"] = GetNumLocations + if _newclass: num_locations = property(GetNumLocations, None, doc='''A read only property that returns the count of locations of this breakpoint.''') + + %} + + +}; + +} // namespace lldb diff --git a/scripts/interface/SBBreakpointLocation.i b/scripts/interface/SBBreakpointLocation.i new file mode 100644 index 000000000000..a3073538e676 --- /dev/null +++ b/scripts/interface/SBBreakpointLocation.i @@ -0,0 +1,130 @@ +//===-- SWIG Interface for SBBreakpointLocation -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents one unique instance (by address) of a logical breakpoint. + +A breakpoint location is defined by the breakpoint that produces it, +and the address that resulted in this particular instantiation. +Each breakpoint location has its settable options. + +SBBreakpoint contains SBBreakpointLocation(s). See docstring of SBBreakpoint +for retrieval of an SBBreakpointLocation from an SBBreakpoint." +) SBBreakpointLocation; +class SBBreakpointLocation +{ +public: + + SBBreakpointLocation (); + + SBBreakpointLocation (const lldb::SBBreakpointLocation &rhs); + + ~SBBreakpointLocation (); + + break_id_t + GetID (); + + bool + IsValid() const; + + lldb::SBAddress + GetAddress(); + + lldb::addr_t + GetLoadAddress (); + + void + SetEnabled(bool enabled); + + bool + IsEnabled (); + + uint32_t + GetIgnoreCount (); + + void + SetIgnoreCount (uint32_t n); + + %feature("docstring", " + //-------------------------------------------------------------------------- + /// The breakpoint location stops only if the condition expression evaluates + /// to true. + //-------------------------------------------------------------------------- + ") SetCondition; + void + SetCondition (const char *condition); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get the condition expression for the breakpoint location. + //------------------------------------------------------------------ + ") GetCondition; + const char * + GetCondition (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Set the callback to the given Python function name. + //------------------------------------------------------------------ + ") SetScriptCallbackFunction; + void + SetScriptCallbackFunction (const char *callback_function_name); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Provide the body for the script function to be called when the breakpoint location is hit. + /// The body will be wrapped in a function, which be passed two arguments: + /// 'frame' - which holds the bottom-most SBFrame of the thread that hit the breakpoint + /// 'bpno' - which is the SBBreakpointLocation to which the callback was attached. + /// + /// The error parameter is currently ignored, but will at some point hold the Python + /// compilation diagnostics. + /// Returns true if the body compiles successfully, false if not. + //------------------------------------------------------------------ + ") SetScriptCallbackBody; + SBError + SetScriptCallbackBody (const char *script_body_text); + + void + SetThreadID (lldb::tid_t sb_thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + bool + IsResolved (); + + bool + GetDescription (lldb::SBStream &description, DescriptionLevel level); + + SBBreakpoint + GetBreakpoint (); +}; + +} // namespace lldb diff --git a/scripts/interface/SBBroadcaster.i b/scripts/interface/SBBroadcaster.i new file mode 100644 index 000000000000..b5e25b6d5204 --- /dev/null +++ b/scripts/interface/SBBroadcaster.i @@ -0,0 +1,68 @@ +//===-- SWIG Interface for SBBroadcaster ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents an entity which can broadcast events. A default broadcaster is +associated with an SBCommandInterpreter, SBProcess, and SBTarget. For +example, use + + broadcaster = process.GetBroadcaster() + +to retrieve the process's broadcaster. + +See also SBEvent for example usage of interacting with a broadcaster." +) SBBroadcaster; +class SBBroadcaster +{ +public: + SBBroadcaster (); + + SBBroadcaster (const char *name); + + SBBroadcaster (const SBBroadcaster &rhs); + + ~SBBroadcaster(); + + bool + IsValid () const; + + void + Clear (); + + void + BroadcastEventByType (uint32_t event_type, bool unique = false); + + void + BroadcastEvent (const lldb::SBEvent &event, bool unique = false); + + void + AddInitialEventsToListener (const lldb::SBListener &listener, uint32_t requested_events); + + uint32_t + AddListener (const lldb::SBListener &listener, uint32_t event_mask); + + const char * + GetName () const; + + bool + EventTypeHasListeners (uint32_t event_type); + + bool + RemoveListener (const lldb::SBListener &listener, uint32_t event_mask = UINT32_MAX); + + bool + operator == (const lldb::SBBroadcaster &rhs) const; + + bool + operator != (const lldb::SBBroadcaster &rhs) const; +}; + +} // namespace lldb diff --git a/scripts/interface/SBCommandInterpreter.i b/scripts/interface/SBCommandInterpreter.i new file mode 100644 index 000000000000..c427d38b14d8 --- /dev/null +++ b/scripts/interface/SBCommandInterpreter.i @@ -0,0 +1,218 @@ +//===-- SWIG Interface for SBCommandInterpreter -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"SBCommandInterpreterRunOptions controls how the RunCommandInterpreter runs the code it is fed. +A default SBCommandInterpreterRunOptions object has: + StopOnContinue: false + StopOnError: false + StopOnCrash: false + EchoCommands: true + PrintResults: true + AddToHistory: true + + +") SBCommandInterpreterRunOptions; +class SBCommandInterpreterRunOptions +{ +friend class SBDebugger; +public: + SBCommandInterpreterRunOptions(); + ~SBCommandInterpreterRunOptions(); + + bool + GetStopOnContinue () const; + + void + SetStopOnContinue (bool); + + bool + GetStopOnError () const; + + void + SetStopOnError (bool); + + bool + GetStopOnCrash () const; + + void + SetStopOnCrash (bool); + + bool + GetEchoCommands () const; + + void + SetEchoCommands (bool); + + bool + GetPrintResults () const; + + void + SetPrintResults (bool); + + bool + GetAddToHistory () const; + + void + SetAddToHistory (bool); +private: + lldb_private::CommandInterpreterRunOptions * + get () const; + + lldb_private::CommandInterpreterRunOptions & + ref () const; + + // This is set in the constructor and will always be valid. + mutable std::unique_ptr m_opaque_up; +}; + +%feature("docstring", +"SBCommandInterpreter handles/interprets commands for lldb. You get the +command interpreter from the SBDebugger instance. For example (from test/ +python_api/interpreter/TestCommandInterpreterAPI.py), + + def command_interpreter_api(self): + '''Test the SBCommandInterpreter APIs.''' + exe = os.path.join(os.getcwd(), 'a.out') + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Retrieve the associated command interpreter from our debugger. + ci = self.dbg.GetCommandInterpreter() + self.assertTrue(ci, VALID_COMMAND_INTERPRETER) + + # Exercise some APIs.... + + self.assertTrue(ci.HasCommands()) + self.assertTrue(ci.HasAliases()) + self.assertTrue(ci.HasAliasOptions()) + self.assertTrue(ci.CommandExists('breakpoint')) + self.assertTrue(ci.CommandExists('target')) + self.assertTrue(ci.CommandExists('platform')) + self.assertTrue(ci.AliasExists('file')) + self.assertTrue(ci.AliasExists('run')) + self.assertTrue(ci.AliasExists('bt')) + + res = lldb.SBCommandReturnObject() + ci.HandleCommand('breakpoint set -f main.c -l %d' % self.line, res) + self.assertTrue(res.Succeeded()) + ci.HandleCommand('process launch', res) + self.assertTrue(res.Succeeded()) + + process = ci.GetProcess() + self.assertTrue(process) + + ... + +The HandleCommand() instance method takes two args: the command string and +an SBCommandReturnObject instance which encapsulates the result of command +execution. +") SBCommandInterpreter; +class SBCommandInterpreter +{ +public: + enum + { + eBroadcastBitThreadShouldExit = (1 << 0), + eBroadcastBitResetPrompt = (1 << 1), + eBroadcastBitQuitCommandReceived = (1 << 2), // User entered quit + eBroadcastBitAsynchronousOutputData = (1 << 3), + eBroadcastBitAsynchronousErrorData = (1 << 4) + }; + + SBCommandInterpreter (const lldb::SBCommandInterpreter &rhs); + + ~SBCommandInterpreter (); + + static const char * + GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type); + + static const char * + GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type); + + static bool + EventIsCommandInterpreterEvent (const lldb::SBEvent &event); + + bool + IsValid() const; + + const char * + GetIOHandlerControlSequence(char ch); + + bool + GetPromptOnQuit(); + + void + SetPromptOnQuit(bool b); + + void + ResolveCommand(const char *command_line, SBCommandReturnObject &result); + + bool + CommandExists (const char *cmd); + + bool + AliasExists (const char *cmd); + + lldb::SBBroadcaster + GetBroadcaster (); + + static const char * + GetBroadcasterClass (); + + bool + HasCommands (); + + bool + HasAliases (); + + bool + HasAliasOptions (); + + lldb::SBProcess + GetProcess (); + + lldb::SBDebugger + GetDebugger (); + + 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); + + lldb::ReturnStatus + HandleCommand (const char *command_line, SBExecutionContext &exe_ctx, SBCommandReturnObject &result, bool add_to_history = false); + + void + HandleCommandsFromFile (lldb::SBFileSpec &file, + lldb::SBExecutionContext &override_context, + lldb::SBCommandInterpreterRunOptions &options, + lldb::SBCommandReturnObject result); + + int + HandleCompletion (const char *current_line, + uint32_t cursor_pos, + int match_start_point, + int max_return_elements, + lldb::SBStringList &matches); + + bool + IsActive (); + +}; + +} // namespace lldb diff --git a/scripts/interface/SBCommandReturnObject.i b/scripts/interface/SBCommandReturnObject.i new file mode 100644 index 000000000000..5ade97bebfec --- /dev/null +++ b/scripts/interface/SBCommandReturnObject.i @@ -0,0 +1,107 @@ +//===-- SWIG Interface for SBCommandReturnObject ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a container which holds the result from command execution. +It works with SBCommandInterpreter.HandleCommand() to encapsulate the result +of command execution. + +See SBCommandInterpreter for example usage of SBCommandReturnObject." +) SBCommandReturnObject; +class SBCommandReturnObject +{ +public: + + SBCommandReturnObject (); + + SBCommandReturnObject (const lldb::SBCommandReturnObject &rhs); + + ~SBCommandReturnObject (); + + bool + IsValid() const; + + const char * + GetOutput (); + + const char * + GetError (); + + size_t + GetOutputSize (); + + size_t + GetErrorSize (); + + const char * + GetOutput (bool only_if_no_immediate); + + const char * + GetError (bool if_no_immediate); + + size_t + PutOutput (FILE *fh); + + size_t + PutError (FILE *fh); + + void + Clear(); + + void + SetStatus (lldb::ReturnStatus status); + + void + SetError (lldb::SBError &error, + const char *fallback_error_cstr = NULL); + + void + SetError (const char *error_cstr); + + lldb::ReturnStatus + GetStatus(); + + bool + Succeeded (); + + bool + HasResult (); + + void + AppendMessage (const char *message); + + void + AppendWarning (const char *message); + + bool + GetDescription (lldb::SBStream &description); + + void + SetImmediateOutputFile (FILE *fh); + + void + SetImmediateErrorFile (FILE *fh); + + void + PutCString(const char* string, int len); + + // wrapping the variadic Printf() with a plain Print() + // because it is hard to support varargs in SWIG bridgings + %extend { + void Print (const char* str) + { + self->Printf("%s", str); + } + } + +}; + +} // namespace lldb diff --git a/scripts/interface/SBCommunication.i b/scripts/interface/SBCommunication.i new file mode 100644 index 000000000000..99814d9f3034 --- /dev/null +++ b/scripts/interface/SBCommunication.i @@ -0,0 +1,82 @@ +//===-- SWIG Interface for SBCommunication ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBCommunication +{ +public: + enum { + eBroadcastBitDisconnected = (1 << 0), ///< Sent when the communications connection is lost. + eBroadcastBitReadThreadGotBytes = (1 << 1), ///< Sent by the read thread when bytes become available. + eBroadcastBitReadThreadDidExit = (1 << 2), ///< Sent by the read thread when it exits to inform clients. + eBroadcastBitReadThreadShouldExit = (1 << 3), ///< Sent by clients that need to cancel the read thread. + eBroadcastBitPacketAvailable = (1 << 4), ///< Sent when data received makes a complete packet. + eAllEventBits = 0xffffffff + }; + + typedef void (*ReadThreadBytesReceived) (void *baton, const void *src, size_t src_len); + + SBCommunication (); + SBCommunication (const char * broadcaster_name); + ~SBCommunication (); + + + bool + IsValid () const; + + lldb::SBBroadcaster + GetBroadcaster (); + + static const char *GetBroadcasterClass(); + + lldb::ConnectionStatus + AdoptFileDesriptor (int fd, bool owns_fd); + + lldb::ConnectionStatus + Connect (const char *url); + + lldb::ConnectionStatus + Disconnect (); + + bool + IsConnected () const; + + bool + GetCloseOnEOF (); + + void + SetCloseOnEOF (bool b); + + size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status); + + size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status); + + bool + ReadThreadStart (); + + bool + ReadThreadStop (); + + bool + ReadThreadIsRunning (); + + bool + SetReadThreadBytesReceivedCallback (ReadThreadBytesReceived callback, + void *callback_baton); +}; + +} // namespace lldb diff --git a/scripts/interface/SBCompileUnit.i b/scripts/interface/SBCompileUnit.i new file mode 100644 index 000000000000..430e7447d997 --- /dev/null +++ b/scripts/interface/SBCompileUnit.i @@ -0,0 +1,130 @@ +//===-- SWIG Interface for SBCompileUnit ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a compilation unit, or compiled source file. + +SBCompileUnit supports line entry iteration. For example, + + # Now get the SBSymbolContext from this frame. We want everything. :-) + context = frame0.GetSymbolContext(lldb.eSymbolContextEverything) + ... + + compileUnit = context.GetCompileUnit() + + for lineEntry in compileUnit: + print('line entry: %s:%d' % (str(lineEntry.GetFileSpec()), + lineEntry.GetLine())) + print('start addr: %s' % str(lineEntry.GetStartAddress())) + print('end addr: %s' % str(lineEntry.GetEndAddress())) + +produces: + +line entry: /Volumes/data/lldb/svn/trunk/test/python_api/symbol-context/main.c:20 +start addr: a.out[0x100000d98] +end addr: a.out[0x100000da3] +line entry: /Volumes/data/lldb/svn/trunk/test/python_api/symbol-context/main.c:21 +start addr: a.out[0x100000da3] +end addr: a.out[0x100000da9] +line entry: /Volumes/data/lldb/svn/trunk/test/python_api/symbol-context/main.c:22 +start addr: a.out[0x100000da9] +end addr: a.out[0x100000db6] +line entry: /Volumes/data/lldb/svn/trunk/test/python_api/symbol-context/main.c:23 +start addr: a.out[0x100000db6] +end addr: a.out[0x100000dbc] +... + +See also SBSymbolContext and SBLineEntry" +) SBCompileUnit; +class SBCompileUnit +{ +public: + + SBCompileUnit (); + + SBCompileUnit (const lldb::SBCompileUnit &rhs); + + ~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; + + uint32_t + FindLineEntryIndex (uint32_t start_idx, + uint32_t line, + lldb::SBFileSpec *inline_file_spec, + bool exact) const; + + SBFileSpec + GetSupportFileAtIndex (uint32_t idx) const; + + uint32_t + GetNumSupportFiles () const; + + uint32_t + FindSupportFileIndex (uint32_t start_idx, const SBFileSpec &sb_file, bool full); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get all types matching \a type_mask from debug info in this + /// compile unit. + /// + /// @param[in] type_mask + /// A bitfield that consists of one or more bits logically OR'ed + /// together from the lldb::TypeClass enumeration. This allows + /// you to request only structure types, or only class, struct + /// and union types. Passing in lldb::eTypeClassAny will return + /// all types found in the debug information for this compile + /// unit. + /// + /// @return + /// A list of types in this compile unit that match \a type_mask + //------------------------------------------------------------------ + ") GetTypes; + lldb::SBTypeList + GetTypes (uint32_t type_mask = lldb::eTypeClassAny); + + lldb::LanguageType + GetLanguage (); + + bool + GetDescription (lldb::SBStream &description); + + bool + operator == (const lldb::SBCompileUnit &rhs) const; + + bool + operator != (const lldb::SBCompileUnit &rhs) const; + + %pythoncode %{ + __swig_getmethods__["file"] = GetFileSpec + if _newclass: file = property(GetFileSpec, None, doc='''A read only property that returns the same result an lldb object that represents the source file (lldb.SBFileSpec) for the compile unit.''') + + __swig_getmethods__["num_line_entries"] = GetNumLineEntries + if _newclass: num_line_entries = property(GetNumLineEntries, None, doc='''A read only property that returns the number of line entries in a compile unit as an integer.''') + %} +}; + +} // namespace lldb diff --git a/scripts/interface/SBData.i b/scripts/interface/SBData.i new file mode 100644 index 000000000000..41aaf8dbdedc --- /dev/null +++ b/scripts/interface/SBData.i @@ -0,0 +1,340 @@ +//===-- SWIG Interface for SBData -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +namespace lldb { + +class SBData +{ +public: + + SBData (); + + SBData (const SBData &rhs); + + ~SBData (); + + uint8_t + GetAddressByteSize (); + + void + SetAddressByteSize (uint8_t addr_byte_size); + + void + Clear (); + + bool + IsValid(); + + size_t + GetByteSize (); + + lldb::ByteOrder + GetByteOrder(); + + void + SetByteOrder (lldb::ByteOrder endian); + + float + GetFloat (lldb::SBError& error, lldb::offset_t offset); + + double + GetDouble (lldb::SBError& error, lldb::offset_t offset); + + long double + GetLongDouble (lldb::SBError& error, lldb::offset_t offset); + + lldb::addr_t + GetAddress (lldb::SBError& error, lldb::offset_t offset); + + uint8_t + GetUnsignedInt8 (lldb::SBError& error, lldb::offset_t offset); + + uint16_t + GetUnsignedInt16 (lldb::SBError& error, lldb::offset_t offset); + + uint32_t + GetUnsignedInt32 (lldb::SBError& error, lldb::offset_t offset); + + uint64_t + GetUnsignedInt64 (lldb::SBError& error, lldb::offset_t offset); + + int8_t + GetSignedInt8 (lldb::SBError& error, lldb::offset_t offset); + + int16_t + GetSignedInt16 (lldb::SBError& error, lldb::offset_t offset); + + int32_t + GetSignedInt32 (lldb::SBError& error, lldb::offset_t offset); + + int64_t + GetSignedInt64 (lldb::SBError& error, lldb::offset_t offset); + + const char* + GetString (lldb::SBError& error, lldb::offset_t offset); + + bool + GetDescription (lldb::SBStream &description, lldb::addr_t base_addr); + + size_t + ReadRawData (lldb::SBError& error, + lldb::offset_t offset, + void *buf, + size_t size); + + void + SetData (lldb::SBError& error, const void *buf, size_t size, lldb::ByteOrder endian, uint8_t addr_size); + + bool + Append (const SBData& rhs); + + static lldb::SBData + CreateDataFromCString (lldb::ByteOrder endian, uint32_t addr_byte_size, const char* data); + + // in the following CreateData*() and SetData*() prototypes, the two parameters array and array_len + // should not be renamed or rearranged, because doing so will break the SWIG typemap + static lldb::SBData + CreateDataFromUInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint64_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromUInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint32_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromSInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int64_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromSInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int32_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromDoubleArray (lldb::ByteOrder endian, uint32_t addr_byte_size, double* array, size_t array_len); + + bool + SetDataFromCString (const char* data); + + bool + SetDataFromUInt64Array (uint64_t* array, size_t array_len); + + bool + SetDataFromUInt32Array (uint32_t* array, size_t array_len); + + bool + SetDataFromSInt64Array (int64_t* array, size_t array_len); + + bool + SetDataFromSInt32Array (int32_t* array, size_t array_len); + + bool + SetDataFromDoubleArray (double* array, size_t array_len); + + %pythoncode %{ + + class read_data_helper: + def __init__(self, sbdata, readerfunc, item_size): + self.sbdata = sbdata + self.readerfunc = readerfunc + self.item_size = item_size + def __getitem__(self,key): + if isinstance(key,slice): + list = [] + for x in range(*key.indices(self.__len__())): + list.append(self.__getitem__(x)) + return list + if not (isinstance(key,six.integer_types)): + raise TypeError('must be int') + key = key * self.item_size # SBData uses byte-based indexes, but we want to use itemsize-based indexes here + error = SBError() + my_data = self.readerfunc(self.sbdata,error,key) + if error.Fail(): + raise IndexError(error.GetCString()) + else: + return my_data + def __len__(self): + return int(self.sbdata.GetByteSize()/self.item_size) + def all(self): + return self[0:len(self)] + + @classmethod + def CreateDataFromInt (cls, value, size = None, target = None, ptr_size = None, endian = None): + import sys + lldbmodule = sys.modules[cls.__module__] + lldbdict = lldbmodule.__dict__ + if 'target' in lldbdict: + lldbtarget = lldbdict['target'] + else: + lldbtarget = None + if target == None and lldbtarget != None and lldbtarget.IsValid(): + target = lldbtarget + if ptr_size == None: + if target and target.IsValid(): + ptr_size = target.addr_size + else: + ptr_size = 8 + if endian == None: + if target and target.IsValid(): + endian = target.byte_order + else: + endian = lldbdict['eByteOrderLittle'] + if size == None: + if value > 2147483647: + size = 8 + elif value < -2147483648: + size = 8 + elif value > 4294967295: + size = 8 + else: + size = 4 + if size == 4: + if value < 0: + return SBData().CreateDataFromSInt32Array(endian, ptr_size, [value]) + return SBData().CreateDataFromUInt32Array(endian, ptr_size, [value]) + if size == 8: + if value < 0: + return SBData().CreateDataFromSInt64Array(endian, ptr_size, [value]) + return SBData().CreateDataFromUInt64Array(endian, ptr_size, [value]) + return None + + def _make_helper(self, sbdata, getfunc, itemsize): + return self.read_data_helper(sbdata, getfunc, itemsize) + + def _make_helper_uint8(self): + return self._make_helper(self, SBData.GetUnsignedInt8, 1) + + def _make_helper_uint16(self): + return self._make_helper(self, SBData.GetUnsignedInt16, 2) + + def _make_helper_uint32(self): + return self._make_helper(self, SBData.GetUnsignedInt32, 4) + + def _make_helper_uint64(self): + return self._make_helper(self, SBData.GetUnsignedInt64, 8) + + def _make_helper_sint8(self): + return self._make_helper(self, SBData.GetSignedInt8, 1) + + def _make_helper_sint16(self): + return self._make_helper(self, SBData.GetSignedInt16, 2) + + def _make_helper_sint32(self): + return self._make_helper(self, SBData.GetSignedInt32, 4) + + def _make_helper_sint64(self): + return self._make_helper(self, SBData.GetSignedInt64, 8) + + def _make_helper_float(self): + return self._make_helper(self, SBData.GetFloat, 4) + + def _make_helper_double(self): + return self._make_helper(self, SBData.GetDouble, 8) + + def _read_all_uint8(self): + return self._make_helper_uint8().all() + + def _read_all_uint16(self): + return self._make_helper_uint16().all() + + def _read_all_uint32(self): + return self._make_helper_uint32().all() + + def _read_all_uint64(self): + return self._make_helper_uint64().all() + + def _read_all_sint8(self): + return self._make_helper_sint8().all() + + def _read_all_sint16(self): + return self._make_helper_sint16().all() + + def _read_all_sint32(self): + return self._make_helper_sint32().all() + + def _read_all_sint64(self): + return self._make_helper_sint64().all() + + def _read_all_float(self): + return self._make_helper_float().all() + + def _read_all_double(self): + return self._make_helper_double().all() + + __swig_getmethods__["uint8"] = _make_helper_uint8 + if _newclass: uint8 = property(_make_helper_uint8, None, doc='''A read only property that returns an array-like object out of which you can read uint8 values.''') + + __swig_getmethods__["uint16"] = _make_helper_uint16 + if _newclass: uint16 = property(_make_helper_uint16, None, doc='''A read only property that returns an array-like object out of which you can read uint16 values.''') + + __swig_getmethods__["uint32"] = _make_helper_uint32 + if _newclass: uint32 = property(_make_helper_uint32, None, doc='''A read only property that returns an array-like object out of which you can read uint32 values.''') + + __swig_getmethods__["uint64"] = _make_helper_uint64 + if _newclass: uint64 = property(_make_helper_uint64, None, doc='''A read only property that returns an array-like object out of which you can read uint64 values.''') + + __swig_getmethods__["sint8"] = _make_helper_sint8 + if _newclass: sint8 = property(_make_helper_sint8, None, doc='''A read only property that returns an array-like object out of which you can read sint8 values.''') + + __swig_getmethods__["sint16"] = _make_helper_sint16 + if _newclass: sint16 = property(_make_helper_sint16, None, doc='''A read only property that returns an array-like object out of which you can read sint16 values.''') + + __swig_getmethods__["sint32"] = _make_helper_sint32 + if _newclass: sint32 = property(_make_helper_sint32, None, doc='''A read only property that returns an array-like object out of which you can read sint32 values.''') + + __swig_getmethods__["sint64"] = _make_helper_sint64 + if _newclass: sint64 = property(_make_helper_sint64, None, doc='''A read only property that returns an array-like object out of which you can read sint64 values.''') + + __swig_getmethods__["float"] = _make_helper_float + if _newclass: float = property(_make_helper_float, None, doc='''A read only property that returns an array-like object out of which you can read float values.''') + + __swig_getmethods__["double"] = _make_helper_double + if _newclass: double = property(_make_helper_double, None, doc='''A read only property that returns an array-like object out of which you can read double values.''') + + __swig_getmethods__["uint8s"] = _read_all_uint8 + if _newclass: uint8s = property(_read_all_uint8, None, doc='''A read only property that returns an array with all the contents of this SBData represented as uint8 values.''') + + __swig_getmethods__["uint16s"] = _read_all_uint16 + if _newclass: uint16s = property(_read_all_uint16, None, doc='''A read only property that returns an array with all the contents of this SBData represented as uint16 values.''') + + __swig_getmethods__["uint32s"] = _read_all_uint32 + if _newclass: uint32s = property(_read_all_uint32, None, doc='''A read only property that returns an array with all the contents of this SBData represented as uint32 values.''') + + __swig_getmethods__["uint64s"] = _read_all_uint64 + if _newclass: uint64s = property(_read_all_uint64, None, doc='''A read only property that returns an array with all the contents of this SBData represented as uint64 values.''') + + __swig_getmethods__["sint8s"] = _read_all_sint8 + if _newclass: sint8s = property(_read_all_sint8, None, doc='''A read only property that returns an array with all the contents of this SBData represented as sint8 values.''') + + __swig_getmethods__["sint16s"] = _read_all_sint16 + if _newclass: sint16s = property(_read_all_sint16, None, doc='''A read only property that returns an array with all the contents of this SBData represented as sint16 values.''') + + __swig_getmethods__["sint32s"] = _read_all_sint32 + if _newclass: sint32s = property(_read_all_sint32, None, doc='''A read only property that returns an array with all the contents of this SBData represented as sint32 values.''') + + __swig_getmethods__["sint64s"] = _read_all_sint64 + if _newclass: sint64s = property(_read_all_sint64, None, doc='''A read only property that returns an array with all the contents of this SBData represented as sint64 values.''') + + __swig_getmethods__["floats"] = _read_all_float + if _newclass: floats = property(_read_all_float, None, doc='''A read only property that returns an array with all the contents of this SBData represented as float values.''') + + __swig_getmethods__["doubles"] = _read_all_double + if _newclass: doubles = property(_read_all_double, None, doc='''A read only property that returns an array with all the contents of this SBData represented as double values.''') + + %} + + %pythoncode %{ + __swig_getmethods__["byte_order"] = GetByteOrder + __swig_setmethods__["byte_order"] = SetByteOrder + if _newclass: byte_order = property(GetByteOrder, SetByteOrder, doc='''A read/write property getting and setting the endianness of this SBData (data.byte_order = lldb.eByteOrderLittle).''') + + __swig_getmethods__["size"] = GetByteSize + if _newclass: size = property(GetByteSize, None, doc='''A read only property that returns the size the same result as GetByteSize().''') + + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBDebugger.i b/scripts/interface/SBDebugger.i new file mode 100644 index 000000000000..89b2882aeb91 --- /dev/null +++ b/scripts/interface/SBDebugger.i @@ -0,0 +1,388 @@ +//===-- SWIG Interface for SBDebugger ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"SBDebugger is the primordial object that creates SBTargets and provides +access to them. It also manages the overall debugging experiences. + +For example (from example/disasm.py), + +import lldb +import os +import sys + +def disassemble_instructions (insts): + for i in insts: + print i + +... + +# Create a new debugger instance +debugger = lldb.SBDebugger.Create() + +# When we step or continue, don't return from the function until the process +# stops. We do this by setting the async mode to false. +debugger.SetAsync (False) + +# Create a target from a file and arch +print('Creating a target for \'%s\'' % exe) + +target = debugger.CreateTargetWithFileAndArch (exe, lldb.LLDB_ARCH_DEFAULT) + +if target: + # If the target is valid set a breakpoint at main + main_bp = target.BreakpointCreateByName (fname, target.GetExecutable().GetFilename()); + + print main_bp + + # Launch the process. Since we specified synchronous mode, we won't return + # from this function until we hit the breakpoint at main + process = target.LaunchSimple (None, None, os.getcwd()) + + # Make sure the launch went ok + if process: + # Print some simple process info + state = process.GetState () + print process + if state == lldb.eStateStopped: + # Get the first thread + thread = process.GetThreadAtIndex (0) + if thread: + # Print some simple thread info + print thread + # Get the first frame + frame = thread.GetFrameAtIndex (0) + if frame: + # Print some simple frame info + print frame + function = frame.GetFunction() + # See if we have debug info (a function) + if function: + # We do have a function, print some info for the function + print function + # Now get all instructions for this function and print them + insts = function.GetInstructions(target) + disassemble_instructions (insts) + else: + # See if we have a symbol in the symbol table for where we stopped + symbol = frame.GetSymbol(); + if symbol: + # We do have a symbol, print some info for the symbol + print symbol + # Now get all instructions for this symbol and print them + insts = symbol.GetInstructions(target) + disassemble_instructions (insts) + + registerList = frame.GetRegisters() + print('Frame registers (size of register set = %d):' % registerList.GetSize()) + for value in registerList: + #print value + print('%s (number of children = %d):' % (value.GetName(), value.GetNumChildren())) + for child in value: + print('Name: ', child.GetName(), ' Value: ', child.GetValue()) + + print('Hit the breakpoint at main, enter to continue and wait for program to exit or \'Ctrl-D\'/\'quit\' to terminate the program') + next = sys.stdin.readline() + if not next or next.rstrip('\n') == 'quit': + print('Terminating the inferior process...') + process.Kill() + else: + # Now continue to the program exit + process.Continue() + # When we return from the above function we will hopefully be at the + # program exit. Print out some process info + print process + elif state == lldb.eStateExited: + print('Didn\'t hit the breakpoint at main, program has exited...') + else: + print('Unexpected process state: %s, killing process...' % debugger.StateAsCString (state)) + process.Kill() +") SBDebugger; +class SBDebugger +{ +public: + + static void + Initialize(); + + static void + Terminate(); + + static lldb::SBDebugger + Create(); + + static lldb::SBDebugger + Create(bool source_init_files); + + static lldb::SBDebugger + Create(bool source_init_files, lldb::LogOutputCallback log_callback, void *baton); + + static void + Destroy (lldb::SBDebugger &debugger); + + static void + MemoryPressureDetected(); + + SBDebugger(); + + SBDebugger(const lldb::SBDebugger &rhs); + + ~SBDebugger(); + + bool + IsValid() const; + + void + Clear (); + + void + SetAsync (bool b); + + bool + GetAsync (); + + void + SkipLLDBInitFiles (bool b); + + void + SetInputFileHandle (FILE *f, bool transfer_ownership); + + void + SetOutputFileHandle (FILE *f, bool transfer_ownership); + + void + SetErrorFileHandle (FILE *f, bool transfer_ownership); + + FILE * + GetInputFileHandle (); + + FILE * + GetOutputFileHandle (); + + FILE * + GetErrorFileHandle (); + + lldb::SBCommandInterpreter + GetCommandInterpreter (); + + void + HandleCommand (const char *command); + + lldb::SBListener + GetListener (); + + void + HandleProcessEvent (const lldb::SBProcess &process, + const lldb::SBEvent &event, + FILE *out, + FILE *err); + + lldb::SBTarget + CreateTarget (const char *filename, + const char *target_triple, + const char *platform_name, + bool add_dependent_modules, + lldb::SBError& sb_error); + + lldb::SBTarget + CreateTargetWithFileAndTargetTriple (const char *filename, + const char *target_triple); + + lldb::SBTarget + CreateTargetWithFileAndArch (const char *filename, + const char *archname); + + lldb::SBTarget + CreateTarget (const char *filename); + + %feature("docstring", + "Return true if target is deleted from the target list of the debugger." + ) DeleteTarget; + bool + DeleteTarget (lldb::SBTarget &target); + + lldb::SBTarget + GetTargetAtIndex (uint32_t idx); + + uint32_t + GetIndexOfTarget (lldb::SBTarget target); + + lldb::SBTarget + FindTargetWithProcessID (pid_t pid); + + lldb::SBTarget + FindTargetWithFileAndArch (const char *filename, + const char *arch); + + uint32_t + GetNumTargets (); + + lldb::SBTarget + GetSelectedTarget (); + + void + SetSelectedTarget (lldb::SBTarget &target); + + lldb::SBPlatform + GetSelectedPlatform(); + + void + SetSelectedPlatform(lldb::SBPlatform &platform); + + lldb::SBSourceManager + GetSourceManager (); + + // REMOVE: just for a quick fix, need to expose platforms through + // SBPlatform from this class. + lldb::SBError + SetCurrentPlatform (const char *platform_name); + + bool + SetCurrentPlatformSDKRoot (const char *sysroot); + + // FIXME: Once we get the set show stuff in place, the driver won't need + // an interface to the Set/Get UseExternalEditor. + bool + SetUseExternalEditor (bool input); + + bool + GetUseExternalEditor (); + + bool + SetUseColor (bool use_color); + + bool + GetUseColor () const; + + static bool + GetDefaultArchitecture (char *arch_name, size_t arch_name_len); + + static bool + SetDefaultArchitecture (const char *arch_name); + + lldb::ScriptLanguage + GetScriptingLanguage (const char *script_language_name); + + static const char * + GetVersionString (); + + static const char * + StateAsCString (lldb::StateType state); + + static bool + StateIsRunningState (lldb::StateType state); + + static bool + StateIsStoppedState (lldb::StateType state); + + bool + EnableLog (const char *channel, const char ** types); + + void + SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton); + + void + DispatchInput (const void *data, size_t data_len); + + void + DispatchInputInterrupt (); + + void + DispatchInputEndOfFile (); + + const char * + GetInstanceName (); + + static SBDebugger + FindDebuggerWithID (int id); + + static lldb::SBError + SetInternalVariable (const char *var_name, const char *value, const char *debugger_instance_name); + + static lldb::SBStringList + GetInternalVariableValue (const char *var_name, const char *debugger_instance_name); + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetTerminalWidth () const; + + void + SetTerminalWidth (uint32_t term_width); + + lldb::user_id_t + GetID (); + + const char * + GetPrompt() const; + + void + SetPrompt (const char *prompt); + + lldb::ScriptLanguage + GetScriptLanguage() const; + + void + SetScriptLanguage (lldb::ScriptLanguage script_lang); + + bool + GetCloseInputOnEOF () const; + + void + SetCloseInputOnEOF (bool b); + + lldb::SBTypeCategory + GetCategory (const char* category_name); + + SBTypeCategory + GetCategory (lldb::LanguageType lang_type); + + lldb::SBTypeCategory + CreateCategory (const char* category_name); + + bool + DeleteCategory (const char* category_name); + + uint32_t + GetNumCategories (); + + lldb::SBTypeCategory + GetCategoryAtIndex (uint32_t); + + lldb::SBTypeCategory + GetDefaultCategory(); + + lldb::SBTypeFormat + GetFormatForType (lldb::SBTypeNameSpecifier); + + lldb::SBTypeSummary + GetSummaryForType (lldb::SBTypeNameSpecifier); + + lldb::SBTypeFilter + GetFilterForType (lldb::SBTypeNameSpecifier); + + lldb::SBTypeSynthetic + GetSyntheticForType (lldb::SBTypeNameSpecifier); + + void + RunCommandInterpreter (bool auto_handle_events, + bool spawn_thread, + SBCommandInterpreterRunOptions &options, + int &num_errors, + bool &quit_requested, + bool &stopped_for_crash); + + lldb::SBError + RunREPL (lldb::LanguageType language, const char *repl_options); +}; // class SBDebugger + +} // namespace lldb diff --git a/scripts/interface/SBDeclaration.i b/scripts/interface/SBDeclaration.i new file mode 100644 index 000000000000..e7a6b6d44b73 --- /dev/null +++ b/scripts/interface/SBDeclaration.i @@ -0,0 +1,68 @@ +//===-- SWIG Interface for SBDeclaration --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + %feature("docstring", + "Specifies an association with a line and column for a variable." + ) SBDeclaration; + class SBDeclaration + { + public: + + SBDeclaration (); + + SBDeclaration (const lldb::SBDeclaration &rhs); + + ~SBDeclaration (); + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetLine () const; + + uint32_t + GetColumn () const; + + bool + GetDescription (lldb::SBStream &description); + + void + SetFileSpec (lldb::SBFileSpec filespec); + + void + SetLine (uint32_t line); + + void + SetColumn (uint32_t column); + + bool + operator == (const lldb::SBDeclaration &rhs) const; + + bool + operator != (const lldb::SBDeclaration &rhs) const; + + %pythoncode %{ + __swig_getmethods__["file"] = GetFileSpec + if _newclass: file = property(GetFileSpec, None, doc='''A read only property that returns an lldb object that represents the file (lldb.SBFileSpec) for this line entry.''') + + __swig_getmethods__["line"] = GetLine + if _newclass: line = property(GetLine, None, doc='''A read only property that returns the 1 based line number for this line entry, a return value of zero indicates that no line information is available.''') + + __swig_getmethods__["column"] = GetColumn + if _newclass: column = property(GetColumn, None, doc='''A read only property that returns the 1 based column number for this line entry, a return value of zero indicates that no column information is available.''') + %} + + }; + +} // namespace lldb diff --git a/scripts/interface/SBError.i b/scripts/interface/SBError.i new file mode 100644 index 000000000000..bebf2d7d72a5 --- /dev/null +++ b/scripts/interface/SBError.i @@ -0,0 +1,128 @@ +//===-- SWIG Interface for SBError ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a container for holding any error code. + +For example (from test/python_api/hello_world/TestHelloWorld.py), + + def hello_world_attach_with_id_api(self): + '''Create target, spawn a process, and attach to it by id.''' + + target = self.dbg.CreateTarget(self.exe) + + # Spawn a new process and don't display the stdout if not in TraceOn() mode. + import subprocess + popen = subprocess.Popen([self.exe, 'abc', 'xyz'], + stdout = open(os.devnull, 'w') if not self.TraceOn() else None) + + listener = lldb.SBListener('my.attach.listener') + error = lldb.SBError() + process = target.AttachToProcessWithID(listener, popen.pid, error) + + self.assertTrue(error.Success() and process, PROCESS_IS_VALID) + + # Let's check the stack traces of the attached process. + import lldbutil + stacktraces = lldbutil.print_stacktraces(process, string_buffer=True) + self.expect(stacktraces, exe=False, + substrs = ['main.c:%d' % self.line2, + '(int)argc=3']) + + listener = lldb.SBListener('my.attach.listener') + error = lldb.SBError() + process = target.AttachToProcessWithID(listener, popen.pid, error) + + self.assertTrue(error.Success() and process, PROCESS_IS_VALID) + +checks that after the attach, there is no error condition by asserting +that error.Success() is True and we get back a valid process object. + +And (from test/python_api/event/TestEvent.py), + + # Now launch the process, and do not stop at entry point. + error = lldb.SBError() + process = target.Launch(listener, None, None, None, None, None, None, 0, False, error) + self.assertTrue(error.Success() and process, PROCESS_IS_VALID) + +checks that after calling the target.Launch() method there's no error +condition and we get back a void process object. +") SBError; + +class SBError { +public: + SBError (); + + SBError (const lldb::SBError &rhs); + + ~SBError(); + + 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); + + %varargs(3, char *str = NULL) SetErrorStringWithFormat; + int + SetErrorStringWithFormat (const char *format, ...); + + bool + IsValid () const; + + bool + GetDescription (lldb::SBStream &description); + + %pythoncode %{ + __swig_getmethods__["value"] = GetError + if _newclass: value = property(GetError, None, doc='''A read only property that returns the same result as GetError().''') + + __swig_getmethods__["fail"] = Fail + if _newclass: fail = property(Fail, None, doc='''A read only property that returns the same result as Fail().''') + + __swig_getmethods__["success"] = Success + if _newclass: success = property(Success, None, doc='''A read only property that returns the same result as Success().''') + + __swig_getmethods__["description"] = GetCString + if _newclass: description = property(GetCString, None, doc='''A read only property that returns the same result as GetCString().''') + + __swig_getmethods__["type"] = GetType + if _newclass: type = property(GetType, None, doc='''A read only property that returns the same result as GetType().''') + + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBEvent.i b/scripts/interface/SBEvent.i new file mode 100644 index 000000000000..ddbd5135b9e3 --- /dev/null +++ b/scripts/interface/SBEvent.i @@ -0,0 +1,153 @@ +//===-- SWIG Interface for SBEvent ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBBroadcaster; + +%feature("docstring", +"API clients can register to receive events. + +For example, check out the following output: + +Try wait for event... +Event description: 0x103d0bb70 Event: broadcaster = 0x1009c8410, type = 0x00000001, data = { process = 0x1009c8400 (pid = 21528), state = running} +Event data flavor: Process::ProcessEventData +Process state: running + +Try wait for event... +Event description: 0x103a700a0 Event: broadcaster = 0x1009c8410, type = 0x00000001, data = { process = 0x1009c8400 (pid = 21528), state = stopped} +Event data flavor: Process::ProcessEventData +Process state: stopped + +Try wait for event... +Event description: 0x103d0d4a0 Event: broadcaster = 0x1009c8410, type = 0x00000001, data = { process = 0x1009c8400 (pid = 21528), state = exited} +Event data flavor: Process::ProcessEventData +Process state: exited + +Try wait for event... +timeout occurred waiting for event... + +from test/python_api/event/TestEventspy: + + def do_listen_for_and_print_event(self): + '''Create a listener and use SBEvent API to print the events received.''' + exe = os.path.join(os.getcwd(), 'a.out') + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple(None, None, os.getcwd()) + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + # Get a handle on the process's broadcaster. + broadcaster = process.GetBroadcaster() + + # Create an empty event object. + event = lldb.SBEvent() + + # Create a listener object and register with the broadcaster. + listener = lldb.SBListener('my listener') + rc = broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged) + self.assertTrue(rc, 'AddListener successfully retruns') + + traceOn = self.TraceOn() + if traceOn: + lldbutil.print_stacktraces(process) + + # Create MyListeningThread class to wait for any kind of event. + import threading + class MyListeningThread(threading.Thread): + def run(self): + count = 0 + # Let's only try at most 4 times to retrieve any kind of event. + # After that, the thread exits. + while not count > 3: + if traceOn: + print('Try wait for event...') + if listener.WaitForEventForBroadcasterWithType(5, + broadcaster, + lldb.SBProcess.eBroadcastBitStateChanged, + event): + if traceOn: + desc = lldbutil.get_description(event)) + print('Event description:', desc) + print('Event data flavor:', event.GetDataFlavor()) + print('Process state:', lldbutil.state_type_to_str(process.GetState())) + print() + else: + if traceOn: + print 'timeout occurred waiting for event...' + count = count + 1 + return + + # Let's start the listening thread to retrieve the events. + my_thread = MyListeningThread() + my_thread.start() + + # Use Python API to continue the process. The listening thread should be + # able to receive the state changed events. + process.Continue() + + # Use Python API to kill the process. The listening thread should be + # able to receive the state changed event, too. + process.Kill() + + # Wait until the 'MyListeningThread' terminates. + my_thread.join() +") SBEvent; +class SBEvent +{ +public: + SBEvent(); + + SBEvent (const lldb::SBEvent &rhs); + + %feature("autodoc", + "__init__(self, int type, str data) -> SBEvent (make an event that contains a C string)" + ) SBEvent; + SBEvent (uint32_t event, const char *cstr, uint32_t cstr_len); + + ~SBEvent(); + + bool + IsValid() const; + + const char * + GetDataFlavor (); + + uint32_t + GetType () const; + + lldb::SBBroadcaster + GetBroadcaster () const; + + const char * + GetBroadcasterClass () const; + + bool + BroadcasterMatchesRef (const lldb::SBBroadcaster &broadcaster); + + void + Clear(); + + static const char * + GetCStringFromEvent (const lldb::SBEvent &event); + + bool + GetDescription (lldb::SBStream &description) const; +}; + +} // namespace lldb diff --git a/scripts/interface/SBExecutionContext.i b/scripts/interface/SBExecutionContext.i new file mode 100644 index 000000000000..cd9d9287bd22 --- /dev/null +++ b/scripts/interface/SBExecutionContext.i @@ -0,0 +1,57 @@ +//===-- SWIG Interface for SBExecutionContext ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBExecutionContext +{ +public: + SBExecutionContext(); + + SBExecutionContext (const lldb::SBExecutionContext &rhs); + + SBExecutionContext (const lldb::SBTarget &target); + + SBExecutionContext (const lldb::SBProcess &process); + + SBExecutionContext (lldb::SBThread thread); // can't be a const& because SBThread::get() isn't itself a const function + + SBExecutionContext (const lldb::SBFrame &frame); + + ~SBExecutionContext(); + + SBTarget + GetTarget () const; + + SBProcess + GetProcess () const; + + SBThread + GetThread () const; + + SBFrame + GetFrame () const; + + %pythoncode %{ + __swig_getmethods__["target"] = GetTarget + if _newclass: target = property(GetTarget, None, doc='''A read only property that returns the same result as GetTarget().''') + + __swig_getmethods__["process"] = GetProcess + if _newclass: process = property(GetProcess, None, doc='''A read only property that returns the same result as GetProcess().''') + + __swig_getmethods__["thread"] = GetThread + if _newclass: thread = property(GetThread, None, doc='''A read only property that returns the same result as GetThread().''') + + __swig_getmethods__["frame"] = GetFrame + if _newclass: frame = property(GetFrame, None, doc='''A read only property that returns the same result as GetFrame().''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBExpressionOptions.i b/scripts/interface/SBExpressionOptions.i new file mode 100644 index 000000000000..1f423cf47e42 --- /dev/null +++ b/scripts/interface/SBExpressionOptions.i @@ -0,0 +1,137 @@ +//===-- SWIG interface for SBExpressionOptions -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"A container for options to use when evaluating expressions." +) SBExpressionOptions; + +class SBExpressionOptions +{ +friend class SBFrame; +friend class SBValue; + +public: + SBExpressionOptions(); + + SBExpressionOptions (const lldb::SBExpressionOptions &rhs); + + ~SBExpressionOptions(); + + bool + GetCoerceResultToId () const; + + %feature("docstring", "Sets whether to coerce the expression result to ObjC id type after evaluation.") SetCoerceResultToId; + + void + SetCoerceResultToId (bool coerce = true); + + bool + GetUnwindOnError () const; + + %feature("docstring", "Sets whether to unwind the expression stack on error.") SetUnwindOnError; + + void + SetUnwindOnError (bool unwind = true); + + bool + GetIgnoreBreakpoints () const; + + %feature("docstring", "Sets whether to ignore breakpoint hits while running expressions.") SetUnwindOnError; + + void + SetIgnoreBreakpoints (bool ignore = true); + + lldb::DynamicValueType + GetFetchDynamicValue () const; + + %feature("docstring", "Sets whether to cast the expression result to its dynamic type.") SetFetchDynamicValue; + + void + SetFetchDynamicValue (lldb::DynamicValueType dynamic = lldb::eDynamicCanRunTarget); + + uint32_t + GetTimeoutInMicroSeconds () const; + + %feature("docstring", "Sets the timeout in microseconds to run the expression for. If try all threads is set to true and the expression doesn't complete within the specified timeout, all threads will be resumed for the same timeout to see if the expresson will finish.") SetTimeoutInMicroSeconds; + void + SetTimeoutInMicroSeconds (uint32_t timeout = 0); + + uint32_t + GetOneThreadTimeoutInMicroSeconds () const; + + %feature("docstring", "Sets the timeout in microseconds to run the expression on one thread before either timing out or trying all threads.") SetTimeoutInMicroSeconds; + void + SetOneThreadTimeoutInMicroSeconds (uint32_t timeout = 0); + + bool + GetTryAllThreads () const; + + %feature("docstring", "Sets whether to run all threads if the expression does not complete on one thread.") SetTryAllThreads; + void + SetTryAllThreads (bool run_others = true); + + bool + GetStopOthers () const; + + %feature("docstring", "Sets whether to stop other threads at all while running expressins. If false, TryAllThreads does nothing.") SetTryAllThreads; + void + SetStopOthers (bool stop_others = true); + + bool + GetTrapExceptions () const; + + %feature("docstring", "Sets whether to abort expression evaluation if an exception is thrown while executing. Don't set this to false unless you know the function you are calling traps all exceptions itself.") SetTryAllThreads; + void + SetTrapExceptions (bool trap_exceptions = true); + + %feature ("docstring", "Sets the language that LLDB should assume the expression is written in") SetLanguage; + void + SetLanguage (lldb::LanguageType language); + + bool + GetGenerateDebugInfo (); + + %feature("docstring", "Sets whether to generate debug information for the expression and also controls if a SBModule is generated.") SetGenerateDebugInfo; + void + SetGenerateDebugInfo (bool b = true); + + bool + GetSuppressPersistentResult (); + + %feature("docstring", "Sets whether to produce a persistent result that can be used in future expressions.") SetSuppressPersistentResult; + void + SetSuppressPersistentResult (bool b = false); + + + %feature("docstring", "Gets the prefix to use for this expression.") GetPrefix; + const char * + GetPrefix () const; + + %feature("docstring", "Sets the prefix to use for this expression. This prefix gets inserted after the 'target.expr-prefix' prefix contents, but before the wrapped expression function body.") SetPrefix; + void + SetPrefix (const char *prefix); + +protected: + + SBExpressionOptions (lldb_private::EvaluateExpressionOptions &expression_options); + + lldb_private::EvaluateExpressionOptions * + get () const; + + lldb_private::EvaluateExpressionOptions & + ref () const; + +private: + // This auto_pointer is made in the constructor and is always valid. + mutable std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb diff --git a/scripts/interface/SBFileSpec.i b/scripts/interface/SBFileSpec.i new file mode 100644 index 000000000000..c153f2bd86f6 --- /dev/null +++ b/scripts/interface/SBFileSpec.i @@ -0,0 +1,103 @@ +//===-- SWIG Interface for SBFileSpec ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a file specification that divides the path into a directory and +basename. The string values of the paths are put into uniqued string pools +for fast comparisons and efficient memory usage. + +For example, the following code + + lineEntry = context.GetLineEntry() + self.expect(lineEntry.GetFileSpec().GetDirectory(), 'The line entry should have the correct directory', + exe=False, + substrs = [self.mydir]) + self.expect(lineEntry.GetFileSpec().GetFilename(), 'The line entry should have the correct filename', + exe=False, + substrs = ['main.c']) + self.assertTrue(lineEntry.GetLine() == self.line, + 'The line entry's line number should match ') + +gets the line entry from the symbol context when a thread is stopped. +It gets the file spec corresponding to the line entry and checks that +the filename and the directory matches what we expect. +") SBFileSpec; +class SBFileSpec +{ +public: + SBFileSpec (); + + SBFileSpec (const lldb::SBFileSpec &rhs); + + SBFileSpec (const char *path);// Deprecated, use SBFileSpec (const char *path, bool resolve) + + SBFileSpec (const char *path, bool resolve); + + ~SBFileSpec (); + + bool + IsValid() const; + + bool + Exists () const; + + bool + ResolveExecutableLocation (); + + const char * + GetFilename() const; + + const char * + GetDirectory() const; + + void + SetFilename(const char *filename); + + void + SetDirectory(const char *directory); + + uint32_t + GetPath (char *dst_path, size_t dst_len) const; + + static int + ResolvePath (const char *src_path, char *dst_path, size_t dst_len); + + bool + GetDescription (lldb::SBStream &description) const; + + %pythoncode %{ + def __get_fullpath__(self): + spec_dir = self.GetDirectory() + spec_file = self.GetFilename() + if spec_dir and spec_file: + return '%s/%s' % (spec_dir, spec_file) + elif spec_dir: + return spec_dir + elif spec_file: + return spec_file + return None + + __swig_getmethods__["fullpath"] = __get_fullpath__ + if _newclass: fullpath = property(__get_fullpath__, None, doc='''A read only property that returns the fullpath as a python string.''') + + __swig_getmethods__["basename"] = GetFilename + if _newclass: basename = property(GetFilename, None, doc='''A read only property that returns the path basename as a python string.''') + + __swig_getmethods__["dirname"] = GetDirectory + if _newclass: dirname = property(GetDirectory, None, doc='''A read only property that returns the path directory name as a python string.''') + + __swig_getmethods__["exists"] = Exists + if _newclass: exists = property(Exists, None, doc='''A read only property that returns a boolean value that indicates if the file exists.''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBFileSpecList.i b/scripts/interface/SBFileSpecList.i new file mode 100644 index 000000000000..38a6f43bdc76 --- /dev/null +++ b/scripts/interface/SBFileSpecList.i @@ -0,0 +1,45 @@ +//===-- SWIG Interface for SBFileSpecList -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBFileSpecList +{ +public: + SBFileSpecList (); + + SBFileSpecList (const lldb::SBFileSpecList &rhs); + + ~SBFileSpecList (); + + uint32_t + GetSize () const; + + bool + GetDescription (SBStream &description) const; + + void + Append (const SBFileSpec &sb_file); + + bool + AppendIfUnique (const SBFileSpec &sb_file); + + void + Clear(); + + uint32_t + FindFileIndex (uint32_t idx, const SBFileSpec &sb_file, bool full); + + const SBFileSpec + GetFileSpecAtIndex (uint32_t idx) const; + +}; + + +} // namespace lldb diff --git a/scripts/interface/SBFrame.i b/scripts/interface/SBFrame.i new file mode 100644 index 000000000000..1c10a9b6e3e8 --- /dev/null +++ b/scripts/interface/SBFrame.i @@ -0,0 +1,407 @@ +//===-- SWIG Interface for SBFrame ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents one of the stack frames associated with a thread. +SBThread contains SBFrame(s). For example (from test/lldbutil.py), + +def print_stacktrace(thread, string_buffer = False): + '''Prints a simple stack trace of this thread.''' + + ... + + for i in range(depth): + frame = thread.GetFrameAtIndex(i) + function = frame.GetFunction() + + load_addr = addrs[i].GetLoadAddress(target) + if not function: + file_addr = addrs[i].GetFileAddress() + start_addr = frame.GetSymbol().GetStartAddress().GetFileAddress() + symbol_offset = file_addr - start_addr + print >> output, ' frame #{num}: {addr:#016x} {mod}`{symbol} + {offset}'.format( + num=i, addr=load_addr, mod=mods[i], symbol=symbols[i], offset=symbol_offset) + else: + print >> output, ' frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line} {args}'.format( + num=i, addr=load_addr, mod=mods[i], + func='%s [inlined]' % funcs[i] if frame.IsInlined() else funcs[i], + file=files[i], line=lines[i], + args=get_args_as_string(frame, showFuncName=False) if not frame.IsInlined() else '()') + + ... + +And, + + for frame in thread: + print frame + +See also SBThread." +) SBFrame; +class SBFrame +{ +public: + SBFrame (); + + SBFrame (const lldb::SBFrame &rhs); + + ~SBFrame(); + + bool + IsEqual (const lldb::SBFrame &rhs) const; + + bool + IsValid() const; + + uint32_t + GetFrameID () const; + + %feature("docstring", " + Get the Canonical Frame Address for this stack frame. + This is the DWARF standard's definition of a CFA, a stack address + that remains constant throughout the lifetime of the function. + Returns an lldb::addr_t stack address, or LLDB_INVALID_ADDRESS if + the CFA cannot be determined.") GetCFA; + lldb::addr_t + GetCFA () const; + + lldb::addr_t + GetPC () const; + + bool + SetPC (lldb::addr_t new_pc); + + lldb::addr_t + GetSP () const; + + lldb::addr_t + GetFP () const; + + lldb::SBAddress + GetPCAddress () const; + + lldb::SBSymbolContext + GetSymbolContext (uint32_t resolve_scope) const; + + lldb::SBModule + GetModule () const; + + lldb::SBCompileUnit + GetCompileUnit () const; + + lldb::SBFunction + GetFunction () const; + + lldb::SBSymbol + GetSymbol () const; + + %feature("docstring", " + /// Gets the deepest block that contains the frame PC. + /// + /// See also GetFrameBlock(). + ") GetBlock; + lldb::SBBlock + GetBlock () const; + + %feature("docstring", " + /// Get the appropriate function name for this frame. Inlined functions in + /// LLDB are represented by Blocks that have inlined function information, so + /// just looking at the SBFunction or SBSymbol for a frame isn't enough. + /// This function will return the appropriate function, symbol or inlined + /// function name for the frame. + /// + /// This function returns: + /// - the name of the inlined function (if there is one) + /// - the name of the concrete function (if there is one) + /// - the name of the symbol (if there is one) + /// - NULL + /// + /// See also IsInlined(). + ") GetFunctionName; + const char * + GetFunctionName(); + + const char * + GetDisplayFunctionName (); + + const char * + GetFunctionName() const; + + %feature("docstring", " + /// Return true if this frame represents an inlined function. + /// + /// See also GetFunctionName(). + ") IsInlined; + bool + IsInlined(); + + bool + IsInlined() const; + + %feature("docstring", " + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + ") EvaluateExpression; + lldb::SBValue + EvaluateExpression (const char *expr); + + lldb::SBValue + EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic); + + lldb::SBValue + EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic, bool unwind_on_error); + + lldb::SBValue + EvaluateExpression (const char *expr, SBExpressionOptions &options); + + %feature("docstring", " + /// Gets the lexical block that defines the stack frame. Another way to think + /// of this is it will return the block that contains all of the variables + /// for a stack frame. Inlined functions are represented as SBBlock objects + /// that have inlined function information: the name of the inlined function, + /// where it was called from. The block that is returned will be the first + /// block at or above the block for the PC (SBFrame::GetBlock()) that defines + /// the scope of the frame. When a function contains no inlined functions, + /// this will be the top most lexical block that defines the function. + /// When a function has inlined functions and the PC is currently + /// in one of those inlined functions, this method will return the inlined + /// block that defines this frame. If the PC isn't currently in an inlined + /// function, the lexical block that defines the function is returned. + ") GetFrameBlock; + lldb::SBBlock + GetFrameBlock () 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 + + %feature("docstring", " + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + ") GetVariables; + lldb::SBValueList + GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only); + + lldb::SBValueList + GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only, + lldb::DynamicValueType use_dynamic); + + lldb::SBValueList + GetVariables (const lldb::SBVariablesOptions& options); + + lldb::SBValueList + GetRegisters (); + + %feature("docstring", " + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + ") FindVariable; + lldb::SBValue + FindVariable (const char *var_name); + + lldb::SBValue + FindVariable (const char *var_name, lldb::DynamicValueType use_dynamic); + + lldb::SBValue + FindRegister (const char *name); + + %feature("docstring", " + /// Get a lldb.SBValue for a variable path. + /// + /// Variable paths can include access to pointer or instance members: + /// rect_ptr->origin.y + /// pt.x + /// Pointer dereferences: + /// *this->foo_ptr + /// **argv + /// Address of: + /// &pt + /// &my_array[3].x + /// Array accesses and treating pointers as arrays: + /// int_array[1] + /// pt_ptr[22].x + /// + /// Unlike EvaluateExpression() which returns lldb.SBValue objects + /// with constant copies of the values at the time of evaluation, + /// the result of this function is a value that will continue to + /// track the current value of the value as execution progresses + /// in the current frame. + ") GetValueForVariablePath; + lldb::SBValue + GetValueForVariablePath (const char *var_path); + + lldb::SBValue + GetValueForVariablePath (const char *var_path, lldb::DynamicValueType use_dynamic); + + %feature("docstring", " + /// Find variables, register sets, registers, or persistent variables using + /// the frame as the scope. + /// + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + ") FindValue; + lldb::SBValue + FindValue (const char *name, ValueType value_type); + + lldb::SBValue + FindValue (const char *name, ValueType value_type, lldb::DynamicValueType use_dynamic); + + bool + GetDescription (lldb::SBStream &description); + + %pythoncode %{ + def get_all_variables(self): + return self.GetVariables(True,True,True,True) + + def get_parent_frame(self): + parent_idx = self.idx + 1 + if parent_idx >= 0 and parent_idx < len(self.thread.frame): + return self.thread.frame[parent_idx] + else: + return SBFrame() + + def get_arguments(self): + return self.GetVariables(True,False,False,False) + + def get_locals(self): + return self.GetVariables(False,True,False,False) + + def get_statics(self): + return self.GetVariables(False,False,True,False) + + def var(self, var_expr_path): + '''Calls through to lldb.SBFrame.GetValueForVariablePath() and returns + a value that represents the variable expression path''' + return self.GetValueForVariablePath(var_expr_path) + + def get_registers_access(self): + class registers_access(object): + '''A helper object that exposes a flattened view of registers, masking away the notion of register sets for easy scripting.''' + def __init__(self, regs): + self.regs = regs + + def __getitem__(self, key): + if type(key) is str: + for i in range(0,len(self.regs)): + rs = self.regs[i] + for j in range (0,rs.num_children): + reg = rs.GetChildAtIndex(j) + if reg.name == key: return reg + else: + return lldb.SBValue() + + return registers_access(self.registers) + + __swig_getmethods__["pc"] = GetPC + __swig_setmethods__["pc"] = SetPC + if _newclass: pc = property(GetPC, SetPC) + + __swig_getmethods__["addr"] = GetPCAddress + if _newclass: addr = property(GetPCAddress, None, doc='''A read only property that returns the program counter (PC) as a section offset address (lldb.SBAddress).''') + + __swig_getmethods__["fp"] = GetFP + if _newclass: fp = property(GetFP, None, doc='''A read only property that returns the frame pointer (FP) as an unsigned integer.''') + + __swig_getmethods__["sp"] = GetSP + if _newclass: sp = property(GetSP, None, doc='''A read only property that returns the stack pointer (SP) as an unsigned integer.''') + + __swig_getmethods__["module"] = GetModule + if _newclass: module = property(GetModule, None, doc='''A read only property that returns an lldb object that represents the module (lldb.SBModule) for this stack frame.''') + + __swig_getmethods__["compile_unit"] = GetCompileUnit + if _newclass: compile_unit = property(GetCompileUnit, None, doc='''A read only property that returns an lldb object that represents the compile unit (lldb.SBCompileUnit) for this stack frame.''') + + __swig_getmethods__["function"] = GetFunction + if _newclass: function = property(GetFunction, None, doc='''A read only property that returns an lldb object that represents the function (lldb.SBFunction) for this stack frame.''') + + __swig_getmethods__["symbol"] = GetSymbol + if _newclass: symbol = property(GetSymbol, None, doc='''A read only property that returns an lldb object that represents the symbol (lldb.SBSymbol) for this stack frame.''') + + __swig_getmethods__["block"] = GetBlock + if _newclass: block = property(GetBlock, None, doc='''A read only property that returns an lldb object that represents the block (lldb.SBBlock) for this stack frame.''') + + __swig_getmethods__["is_inlined"] = IsInlined + if _newclass: is_inlined = property(IsInlined, None, doc='''A read only property that returns an boolean that indicates if the block frame is an inlined function.''') + + __swig_getmethods__["name"] = GetFunctionName + if _newclass: name = property(GetFunctionName, None, doc='''A read only property that retuns the name for the function that this frame represents. Inlined stack frame might have a concrete function that differs from the name of the inlined function (a named lldb.SBBlock).''') + + __swig_getmethods__["line_entry"] = GetLineEntry + if _newclass: line_entry = property(GetLineEntry, None, doc='''A read only property that returns an lldb object that represents the line table entry (lldb.SBLineEntry) for this stack frame.''') + + __swig_getmethods__["thread"] = GetThread + if _newclass: thread = property(GetThread, None, doc='''A read only property that returns an lldb object that represents the thread (lldb.SBThread) for this stack frame.''') + + __swig_getmethods__["disassembly"] = Disassemble + if _newclass: disassembly = property(Disassemble, None, doc='''A read only property that returns the disassembly for this stack frame as a python string.''') + + __swig_getmethods__["idx"] = GetFrameID + if _newclass: idx = property(GetFrameID, None, doc='''A read only property that returns the zero based stack frame index.''') + + __swig_getmethods__["variables"] = get_all_variables + if _newclass: variables = property(get_all_variables, None, doc='''A read only property that returns a list() that contains a collection of lldb.SBValue objects that represent the variables in this stack frame.''') + + __swig_getmethods__["vars"] = get_all_variables + if _newclass: vars = property(get_all_variables, None, doc='''A read only property that returns a list() that contains a collection of lldb.SBValue objects that represent the variables in this stack frame.''') + + __swig_getmethods__["locals"] = get_locals + if _newclass: locals = property(get_locals, None, doc='''A read only property that returns a list() that contains a collection of lldb.SBValue objects that represent the local variables in this stack frame.''') + + __swig_getmethods__["args"] = get_arguments + if _newclass: args = property(get_arguments, None, doc='''A read only property that returns a list() that contains a collection of lldb.SBValue objects that represent the argument variables in this stack frame.''') + + __swig_getmethods__["arguments"] = get_arguments + if _newclass: arguments = property(get_arguments, None, doc='''A read only property that returns a list() that contains a collection of lldb.SBValue objects that represent the argument variables in this stack frame.''') + + __swig_getmethods__["statics"] = get_statics + if _newclass: statics = property(get_statics, None, doc='''A read only property that returns a list() that contains a collection of lldb.SBValue objects that represent the static variables in this stack frame.''') + + __swig_getmethods__["registers"] = GetRegisters + if _newclass: registers = property(GetRegisters, None, doc='''A read only property that returns a list() that contains a collection of lldb.SBValue objects that represent the CPU registers for this stack frame.''') + + __swig_getmethods__["regs"] = GetRegisters + if _newclass: regs = property(GetRegisters, None, doc='''A read only property that returns a list() that contains a collection of lldb.SBValue objects that represent the CPU registers for this stack frame.''') + + __swig_getmethods__["register"] = get_registers_access + if _newclass: register = property(get_registers_access, None, doc='''A read only property that returns an helper object providing a flattened indexable view of the CPU registers for this stack frame.''') + + __swig_getmethods__["reg"] = get_registers_access + if _newclass: reg = property(get_registers_access, None, doc='''A read only property that returns an helper object providing a flattened indexable view of the CPU registers for this stack frame''') + + __swig_getmethods__["parent"] = get_parent_frame + if _newclass: parent = property(get_parent_frame, None, doc='''A read only property that returns the parent (caller) frame of the current frame.''') + + %} +}; + +} // namespace lldb diff --git a/scripts/interface/SBFunction.i b/scripts/interface/SBFunction.i new file mode 100644 index 000000000000..435e3e4bf30b --- /dev/null +++ b/scripts/interface/SBFunction.i @@ -0,0 +1,145 @@ +//===-- SWIG Interface for SBFunction ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a generic function, which can be inlined or not. + +For example (from test/lldbutil.py, but slightly modified for doc purpose), + + ... + + frame = thread.GetFrameAtIndex(i) + addr = frame.GetPCAddress() + load_addr = addr.GetLoadAddress(target) + function = frame.GetFunction() + mod_name = frame.GetModule().GetFileSpec().GetFilename() + + if not function: + # No debug info for 'function'. + symbol = frame.GetSymbol() + file_addr = addr.GetFileAddress() + start_addr = symbol.GetStartAddress().GetFileAddress() + symbol_name = symbol.GetName() + symbol_offset = file_addr - start_addr + print >> output, ' frame #{num}: {addr:#016x} {mod}`{symbol} + {offset}'.format( + num=i, addr=load_addr, mod=mod_name, symbol=symbol_name, offset=symbol_offset) + else: + # Debug info is available for 'function'. + func_name = frame.GetFunctionName() + file_name = frame.GetLineEntry().GetFileSpec().GetFilename() + line_num = frame.GetLineEntry().GetLine() + print >> output, ' frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line} {args}'.format( + num=i, addr=load_addr, mod=mod_name, + func='%s [inlined]' % func_name] if frame.IsInlined() else func_name, + file=file_name, line=line_num, args=get_args_as_string(frame, showFuncName=False)) + + ... +") SBFunction; +class SBFunction +{ +public: + + SBFunction (); + + SBFunction (const lldb::SBFunction &rhs); + + ~SBFunction (); + + bool + IsValid () const; + + const char * + GetName() const; + + const char * + GetDisplayName() const; + + const char * + GetMangledName () const; + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target); + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target, const char *flavor); + + lldb::SBAddress + GetStartAddress (); + + lldb::SBAddress + GetEndAddress (); + + const char * + GetArgumentName (uint32_t arg_idx); + + uint32_t + GetPrologueByteSize (); + + lldb::SBType + GetType (); + + lldb::SBBlock + GetBlock (); + + lldb::LanguageType + GetLanguage (); + + %feature("docstring", " + Returns true if the function was compiled with optimization. + Optimization, in this case, is meant to indicate that the debugger + experience may be confusing for the user -- variables optimized away, + stepping jumping between source lines -- and the driver may want to + provide some guidance to the user about this. + Returns false if unoptimized, or unknown.") GetIsOptimized; + bool + GetIsOptimized(); + + bool + GetDescription (lldb::SBStream &description); + + bool + operator == (const lldb::SBFunction &rhs) const; + + bool + operator != (const lldb::SBFunction &rhs) const; + + %pythoncode %{ + def get_instructions_from_current_target (self): + return self.GetInstructions (target) + + __swig_getmethods__["addr"] = GetStartAddress + if _newclass: addr = property(GetStartAddress, None, doc='''A read only property that returns an lldb object that represents the start address (lldb.SBAddress) for this function.''') + + __swig_getmethods__["end_addr"] = GetEndAddress + if _newclass: end_addr = property(GetEndAddress, None, doc='''A read only property that returns an lldb object that represents the end address (lldb.SBAddress) for this function.''') + + __swig_getmethods__["block"] = GetBlock + if _newclass: block = property(GetBlock, None, doc='''A read only property that returns an lldb object that represents the top level lexical block (lldb.SBBlock) for this function.''') + + __swig_getmethods__["instructions"] = get_instructions_from_current_target + if _newclass: instructions = property(get_instructions_from_current_target, None, doc='''A read only property that returns an lldb object that represents the instructions (lldb.SBInstructionList) for this function.''') + + __swig_getmethods__["mangled"] = GetMangledName + if _newclass: mangled = property(GetMangledName, None, doc='''A read only property that returns the mangled (linkage) name for this function as a string.''') + + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None, doc='''A read only property that returns the name for this function as a string.''') + + __swig_getmethods__["prologue_size"] = GetPrologueByteSize + if _newclass: prologue_size = property(GetPrologueByteSize, None, doc='''A read only property that returns the size in bytes of the prologue instructions as an unsigned integer.''') + + __swig_getmethods__["type"] = GetType + if _newclass: type = property(GetType, None, doc='''A read only property that returns an lldb object that represents the return type (lldb.SBType) for this function.''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBHostOS.i b/scripts/interface/SBHostOS.i new file mode 100644 index 000000000000..d9f42160bf09 --- /dev/null +++ b/scripts/interface/SBHostOS.i @@ -0,0 +1,47 @@ +//===-- SWIG Interface for SBHostOS -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBHostOS +{ +public: + + static lldb::SBFileSpec + GetProgramFileSpec (); + + static lldb::SBFileSpec + GetLLDBPythonPath (); + + static lldb::SBFileSpec + GetLLDBPath (lldb::PathType path_type); + + static void + ThreadCreated (const char *name); + + static lldb::thread_t + ThreadCreate (const char *name, + lldb::thread_func_t, + 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, + lldb::thread_result_t *result, + lldb::SBError *err); +}; + +} // namespace lldb diff --git a/scripts/interface/SBInstruction.i b/scripts/interface/SBInstruction.i new file mode 100644 index 000000000000..421990646a12 --- /dev/null +++ b/scripts/interface/SBInstruction.i @@ -0,0 +1,103 @@ +//===-- SWIG Interface for SBInstruction ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +// There's a lot to be fixed here, but need to wait for underlying insn implementation +// to be revised & settle down first. + +namespace lldb { + +class SBInstruction +{ +public: + + SBInstruction (); + + SBInstruction (const SBInstruction &rhs); + + ~SBInstruction (); + + bool + IsValid(); + + lldb::SBAddress + GetAddress(); + + lldb::AddressClass + GetAddressClass (); + + const char * + GetMnemonic (lldb::SBTarget target); + + const char * + GetOperands (lldb::SBTarget target); + + const char * + GetComment (lldb::SBTarget target); + + lldb::SBData + GetData (lldb::SBTarget target); + + size_t + GetByteSize (); + + bool + DoesBranch (); + + void + Print (FILE *out); + + bool + GetDescription (lldb::SBStream &description); + + bool + EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options); + + bool + DumpEmulation (const char * triple); // triple is to specify the architecture, e.g. 'armv6' or 'armv7-apple-ios' + + bool + TestEmulation (lldb::SBStream &output_stream, const char *test_file); + + %pythoncode %{ + def __mnemonic_property__ (self): + return self.GetMnemonic (target) + def __operands_property__ (self): + return self.GetOperands (target) + def __comment_property__ (self): + return self.GetComment (target) + def __file_addr_property__ (self): + return self.GetAddress ().GetFileAddress() + def __load_adrr_property__ (self): + return self.GetComment (target) + + __swig_getmethods__["mnemonic"] = __mnemonic_property__ + if _newclass: mnemonic = property(__mnemonic_property__, None, doc='''A read only property that returns the mnemonic for this instruction as a string.''') + + __swig_getmethods__["operands"] = __operands_property__ + if _newclass: operands = property(__operands_property__, None, doc='''A read only property that returns the operands for this instruction as a string.''') + + __swig_getmethods__["comment"] = __comment_property__ + if _newclass: comment = property(__comment_property__, None, doc='''A read only property that returns the comment for this instruction as a string.''') + + __swig_getmethods__["addr"] = GetAddress + if _newclass: addr = property(GetAddress, None, doc='''A read only property that returns an lldb object that represents the address (lldb.SBAddress) for this instruction.''') + + __swig_getmethods__["size"] = GetByteSize + if _newclass: size = property(GetByteSize, None, doc='''A read only property that returns the size in bytes for this instruction as an integer.''') + + __swig_getmethods__["is_branch"] = DoesBranch + if _newclass: is_branch = property(DoesBranch, None, doc='''A read only property that returns a boolean value that indicates if this instruction is a branch instruction.''') + %} + + +}; + +} // namespace lldb diff --git a/scripts/interface/SBInstructionList.i b/scripts/interface/SBInstructionList.i new file mode 100644 index 000000000000..32603be5cc1e --- /dev/null +++ b/scripts/interface/SBInstructionList.i @@ -0,0 +1,91 @@ +//===-- SWIG Interface for SBInstructionList --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +namespace lldb { + +%feature("docstring", +"Represents a list of machine instructions. SBFunction and SBSymbol have +GetInstructions() methods which return SBInstructionList instances. + +SBInstructionList supports instruction (SBInstruction instance) iteration. +For example (see also SBDebugger for a more complete example), + +def disassemble_instructions (insts): + for i in insts: + print i + +defines a function which takes an SBInstructionList instance and prints out +the machine instructions in assembly format." +) SBInstructionList; +class SBInstructionList +{ +public: + + SBInstructionList (); + + SBInstructionList (const SBInstructionList &rhs); + + ~SBInstructionList (); + + bool + IsValid () const; + + size_t + GetSize (); + + lldb::SBInstruction + GetInstructionAtIndex (uint32_t idx); + + void + Clear (); + + void + AppendInstruction (lldb::SBInstruction inst); + + void + Print (FILE *out); + + bool + GetDescription (lldb::SBStream &description); + + bool + DumpEmulationForAllInstructions (const char *triple); + + %pythoncode %{ + def __len__(self): + '''Access len of the instruction list.''' + return int(self.GetSize()) + + def __getitem__(self, key): + '''Access instructions by integer index for array access or by lldb.SBAddress to find an instruction that matches a section offset address object.''' + if type(key) is int: + # Find an instruction by index + if key < len(self): + return self.GetInstructionAtIndex(key) + elif type(key) is SBAddress: + # Find an instruction using a lldb.SBAddress object + lookup_file_addr = key.file_addr + closest_inst = None + for idx in range(self.GetSize()): + inst = self.GetInstructionAtIndex(idx) + inst_file_addr = inst.addr.file_addr + if inst_file_addr == lookup_file_addr: + return inst + elif inst_file_addr > lookup_file_addr: + return closest_inst + else: + closest_inst = inst + return None + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBLanguageRuntime.i b/scripts/interface/SBLanguageRuntime.i new file mode 100644 index 000000000000..95153ba0cdcb --- /dev/null +++ b/scripts/interface/SBLanguageRuntime.i @@ -0,0 +1,22 @@ +//===-- SWIG Interface for SBLanguageRuntime --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBLanguageRuntime +{ +public: + static lldb::LanguageType + GetLanguageTypeFromString (const char *string); + + static const char * + GetNameForLanguageType (lldb::LanguageType language); +}; + +} // namespace lldb diff --git a/scripts/interface/SBLaunchInfo.i b/scripts/interface/SBLaunchInfo.i new file mode 100644 index 000000000000..24f3f853f198 --- /dev/null +++ b/scripts/interface/SBLaunchInfo.i @@ -0,0 +1,132 @@ +//===-- SWIG Interface for SBLaunchInfo--------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBLaunchInfo +{ +public: + SBLaunchInfo (const char **argv); + + pid_t + GetProcessID(); + + uint32_t + GetUserID(); + + uint32_t + GetGroupID(); + + bool + UserIDIsValid (); + + bool + GroupIDIsValid (); + + void + SetUserID (uint32_t uid); + + void + SetGroupID (uint32_t gid); + + lldb::SBFileSpec + GetExecutableFile (); + + void + SetExecutableFile (lldb::SBFileSpec exe_file, bool add_as_first_arg); + + lldb::SBListener + GetListener (); + + void + SetListener (lldb::SBListener &listener); + + uint32_t + GetNumArguments (); + + const char * + GetArgumentAtIndex (uint32_t idx); + + void + SetArguments (const char **argv, bool append); + + uint32_t + GetNumEnvironmentEntries (); + + const char * + GetEnvironmentEntryAtIndex (uint32_t idx); + + void + SetEnvironmentEntries (const char **envp, bool append); + + void + Clear (); + + const char * + GetWorkingDirectory () const; + + void + SetWorkingDirectory (const char *working_dir); + + uint32_t + GetLaunchFlags (); + + void + SetLaunchFlags (uint32_t flags); + + const char * + GetProcessPluginName (); + + void + SetProcessPluginName (const char *plugin_name); + + const char * + GetShell (); + + void + SetShell (const char * path); + + bool + GetShellExpandArguments (); + + void + SetShellExpandArguments (bool expand); + + uint32_t + GetResumeCount (); + + void + SetResumeCount (uint32_t c); + + bool + AddCloseFileAction (int fd); + + bool + AddDuplicateFileAction (int fd, int dup_fd); + + bool + AddOpenFileAction (int fd, const char *path, bool read, bool write); + + bool + AddSuppressFileAction (int fd, bool read, bool write); + + void + SetLaunchEventData (const char *data); + + const char * + GetLaunchEventData () const; + + bool + GetDetachOnError() const; + + void + SetDetachOnError(bool enable); +}; + +} // namespace lldb diff --git a/scripts/interface/SBLineEntry.i b/scripts/interface/SBLineEntry.i new file mode 100644 index 000000000000..fbe2100086f1 --- /dev/null +++ b/scripts/interface/SBLineEntry.i @@ -0,0 +1,106 @@ +//===-- SWIG Interface for SBLineEntry --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Specifies an association with a contiguous range of instructions and +a source file location. SBCompileUnit contains SBLineEntry(s). For example, + + for lineEntry in compileUnit: + print('line entry: %s:%d' % (str(lineEntry.GetFileSpec()), + lineEntry.GetLine())) + print('start addr: %s' % str(lineEntry.GetStartAddress())) + print('end addr: %s' % str(lineEntry.GetEndAddress())) + +produces: + +line entry: /Volumes/data/lldb/svn/trunk/test/python_api/symbol-context/main.c:20 +start addr: a.out[0x100000d98] +end addr: a.out[0x100000da3] +line entry: /Volumes/data/lldb/svn/trunk/test/python_api/symbol-context/main.c:21 +start addr: a.out[0x100000da3] +end addr: a.out[0x100000da9] +line entry: /Volumes/data/lldb/svn/trunk/test/python_api/symbol-context/main.c:22 +start addr: a.out[0x100000da9] +end addr: a.out[0x100000db6] +line entry: /Volumes/data/lldb/svn/trunk/test/python_api/symbol-context/main.c:23 +start addr: a.out[0x100000db6] +end addr: a.out[0x100000dbc] +... + +See also SBCompileUnit." +) SBLineEntry; +class SBLineEntry +{ +public: + + SBLineEntry (); + + SBLineEntry (const lldb::SBLineEntry &rhs); + + ~SBLineEntry (); + + lldb::SBAddress + GetStartAddress () const; + + lldb::SBAddress + GetEndAddress () const; + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetLine () const; + + uint32_t + GetColumn () const; + + bool + GetDescription (lldb::SBStream &description); + + void + SetFileSpec (lldb::SBFileSpec filespec); + + void + SetLine (uint32_t line); + + void + SetColumn (uint32_t column); + + bool + operator == (const lldb::SBLineEntry &rhs) const; + + bool + operator != (const lldb::SBLineEntry &rhs) const; + + %pythoncode %{ + __swig_getmethods__["file"] = GetFileSpec + if _newclass: file = property(GetFileSpec, None, doc='''A read only property that returns an lldb object that represents the file (lldb.SBFileSpec) for this line entry.''') + + __swig_getmethods__["line"] = GetLine + if _newclass: line = property(GetLine, None, doc='''A read only property that returns the 1 based line number for this line entry, a return value of zero indicates that no line information is available.''') + + __swig_getmethods__["column"] = GetColumn + if _newclass: column = property(GetColumn, None, doc='''A read only property that returns the 1 based column number for this line entry, a return value of zero indicates that no column information is available.''') + + __swig_getmethods__["addr"] = GetStartAddress + if _newclass: addr = property(GetStartAddress, None, doc='''A read only property that returns an lldb object that represents the start address (lldb.SBAddress) for this line entry.''') + + __swig_getmethods__["end_addr"] = GetEndAddress + if _newclass: end_addr = property(GetEndAddress, None, doc='''A read only property that returns an lldb object that represents the end address (lldb.SBAddress) for this line entry.''') + + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBListener.i b/scripts/interface/SBListener.i new file mode 100644 index 000000000000..3bd376012f04 --- /dev/null +++ b/scripts/interface/SBListener.i @@ -0,0 +1,99 @@ +//===-- SWIG Interface for SBListener ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"API clients can register its own listener to debugger events. + +See aslo SBEvent for example usage of creating and adding a listener." +) SBListener; +class SBListener +{ +public: + SBListener (); + + SBListener (const char *name); + + SBListener (const SBListener &rhs); + + ~SBListener (); + + void + AddEvent (const lldb::SBEvent &event); + + void + Clear (); + + bool + IsValid () const; + + uint32_t + StartListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask); + + uint32_t + StopListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask); + + uint32_t + StartListeningForEvents (const lldb::SBBroadcaster& broadcaster, + uint32_t event_mask); + + bool + StopListeningForEvents (const lldb::SBBroadcaster& broadcaster, + uint32_t event_mask); + + // Returns true if an event was received, 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); +}; + +} // namespace lldb diff --git a/scripts/interface/SBModule.i b/scripts/interface/SBModule.i new file mode 100644 index 000000000000..b1b6d2a7ca3d --- /dev/null +++ b/scripts/interface/SBModule.i @@ -0,0 +1,530 @@ +//===-- SWIG Interface for SBModule -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents 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. + +You can retrieve SBModule from SBSymbolContext, which in turn is available +from SBFrame. + +SBModule supports symbol iteration, for example, + + for symbol in module: + name = symbol.GetName() + saddr = symbol.GetStartAddress() + eaddr = symbol.GetEndAddress() + +and rich comparion methods which allow the API program to use, + + if thisModule == thatModule: + print('This module is the same as that module') + +to test module equality. A module also contains object file sections, namely +SBSection. SBModule supports section iteration through section_iter(), for +example, + + print('Number of sections: %d' % module.GetNumSections()) + for sec in module.section_iter(): + print(sec) + +And to iterate the symbols within a SBSection, use symbol_in_section_iter(), + + # Iterates the text section and prints each symbols within each sub-section. + for subsec in text_sec: + print(INDENT + repr(subsec)) + for sym in exe_module.symbol_in_section_iter(subsec): + print(INDENT2 + repr(sym)) + print(INDENT2 + 'symbol type: %s' % symbol_type_to_str(sym.GetType())) + +produces this following output: + + [0x0000000100001780-0x0000000100001d5c) a.out.__TEXT.__text + id = {0x00000004}, name = 'mask_access(MaskAction, unsigned int)', range = [0x00000001000017c0-0x0000000100001870) + symbol type: code + id = {0x00000008}, name = 'thread_func(void*)', range = [0x0000000100001870-0x00000001000019b0) + symbol type: code + id = {0x0000000c}, name = 'main', range = [0x00000001000019b0-0x0000000100001d5c) + symbol type: code + id = {0x00000023}, name = 'start', address = 0x0000000100001780 + symbol type: code + [0x0000000100001d5c-0x0000000100001da4) a.out.__TEXT.__stubs + id = {0x00000024}, name = '__stack_chk_fail', range = [0x0000000100001d5c-0x0000000100001d62) + symbol type: trampoline + id = {0x00000028}, name = 'exit', range = [0x0000000100001d62-0x0000000100001d68) + symbol type: trampoline + id = {0x00000029}, name = 'fflush', range = [0x0000000100001d68-0x0000000100001d6e) + symbol type: trampoline + id = {0x0000002a}, name = 'fgets', range = [0x0000000100001d6e-0x0000000100001d74) + symbol type: trampoline + id = {0x0000002b}, name = 'printf', range = [0x0000000100001d74-0x0000000100001d7a) + symbol type: trampoline + id = {0x0000002c}, name = 'pthread_create', range = [0x0000000100001d7a-0x0000000100001d80) + symbol type: trampoline + id = {0x0000002d}, name = 'pthread_join', range = [0x0000000100001d80-0x0000000100001d86) + symbol type: trampoline + id = {0x0000002e}, name = 'pthread_mutex_lock', range = [0x0000000100001d86-0x0000000100001d8c) + symbol type: trampoline + id = {0x0000002f}, name = 'pthread_mutex_unlock', range = [0x0000000100001d8c-0x0000000100001d92) + symbol type: trampoline + id = {0x00000030}, name = 'rand', range = [0x0000000100001d92-0x0000000100001d98) + symbol type: trampoline + id = {0x00000031}, name = 'strtoul', range = [0x0000000100001d98-0x0000000100001d9e) + symbol type: trampoline + id = {0x00000032}, name = 'usleep', range = [0x0000000100001d9e-0x0000000100001da4) + symbol type: trampoline + [0x0000000100001da4-0x0000000100001e2c) a.out.__TEXT.__stub_helper + [0x0000000100001e2c-0x0000000100001f10) a.out.__TEXT.__cstring + [0x0000000100001f10-0x0000000100001f68) a.out.__TEXT.__unwind_info + [0x0000000100001f68-0x0000000100001ff8) a.out.__TEXT.__eh_frame +" +) SBModule; +class SBModule +{ +public: + + SBModule (); + + SBModule (const lldb::SBModule &rhs); + + SBModule (const lldb::SBModuleSpec &module_spec); + + SBModule (lldb::SBProcess &process, + lldb::addr_t header_addr); + + ~SBModule (); + + bool + IsValid () const; + + void + Clear(); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get const accessor for the module file specification. + /// + /// This function returns the file for the module on the host system + /// that is running LLDB. This can differ from the path on the + /// platform since we might be doing remote debugging. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + ") GetFileSpec; + lldb::SBFileSpec + GetFileSpec () const; + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get accessor for the module platform file specification. + /// + /// Platform file refers to the path of the module as it is known on + /// the remote system on which it is being debugged. For local + /// debugging this is always the same as Module::GetFileSpec(). But + /// remote debugging might mention a file '/usr/lib/liba.dylib' + /// which might be locally downloaded and cached. In this case the + /// platform file could be something like: + /// '/tmp/lldb/platform-cache/remote.host.computer/usr/lib/liba.dylib' + /// The file could also be cached in a local developer kit directory. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + ") GetPlatformFileSpec; + lldb::SBFileSpec + GetPlatformFileSpec () const; + + bool + SetPlatformFileSpec (const lldb::SBFileSpec &platform_file); + + lldb::SBFileSpec + GetRemoteInstallFileSpec (); + + bool + SetRemoteInstallFileSpec (lldb::SBFileSpec &file); + + %feature("docstring", "Returns the UUID of the module as a Python string." + ) GetUUIDString; + const char * + GetUUIDString () const; + + lldb::SBSection + FindSection (const char *sect_name); + + lldb::SBAddress + ResolveFileAddress (lldb::addr_t vm_addr); + + lldb::SBSymbolContext + ResolveSymbolContextForAddress (const lldb::SBAddress& addr, + uint32_t resolve_scope); + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetNumCompileUnits(); + + lldb::SBCompileUnit + GetCompileUnitAtIndex (uint32_t); + + size_t + GetNumSymbols (); + + lldb::SBSymbol + GetSymbolAtIndex (size_t idx); + + lldb::SBSymbol + FindSymbol (const char *name, + lldb::SymbolType type = eSymbolTypeAny); + + lldb::SBSymbolContextList + FindSymbols (const char *name, + lldb::SymbolType type = eSymbolTypeAny); + + + size_t + GetNumSections (); + + lldb::SBSection + GetSectionAtIndex (size_t idx); + + + %feature("docstring", " + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// @param[in] name + /// The name of the function we are looking for. + /// + /// @param[in] name_type_mask + /// A logical OR of one or more FunctionNameType enum bits that + /// indicate what kind of names should be used when doing the + /// lookup. Bits include fully qualified names, base names, + /// C++ methods, or ObjC selectors. + /// See FunctionNameType for more details. + /// + /// @return + /// A symbol context list that gets filled in with all of the + /// matches. + //------------------------------------------------------------------ + ") FindFunctions; + lldb::SBSymbolContextList + FindFunctions (const char *name, + uint32_t name_type_mask = lldb::eFunctionNameTypeAny); + + lldb::SBType + FindFirstType (const char* name); + + lldb::SBTypeList + FindTypes (const char* type); + + lldb::SBType + GetTypeByID (lldb::user_id_t uid); + + lldb::SBType + GetBasicType(lldb::BasicType type); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get all types matching \a type_mask from debug info in this + /// module. + /// + /// @param[in] type_mask + /// A bitfield that consists of one or more bits logically OR'ed + /// together from the lldb::TypeClass enumeration. This allows + /// you to request only structure types, or only class, struct + /// and union types. Passing in lldb::eTypeClassAny will return + /// all types found in the debug information for this module. + /// + /// @return + /// A list of types in this module that match \a type_mask + //------------------------------------------------------------------ + ") GetTypes; + lldb::SBTypeList + GetTypes (uint32_t type_mask = lldb::eTypeClassAny); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] target + /// A valid SBTarget instance representing the debuggee. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a max_matches. + /// + /// @return + /// A list of matched variables in an SBValueList. + //------------------------------------------------------------------ + ") FindGlobalVariables; + lldb::SBValueList + FindGlobalVariables (lldb::SBTarget &target, + const char *name, + uint32_t max_matches); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Find the first global (or static) variable by name. + /// + /// @param[in] target + /// A valid SBTarget instance representing the debuggee. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @return + /// An SBValue that gets filled in with the found variable (if any). + //------------------------------------------------------------------ + ") FindFirstGlobalVariable; + lldb::SBValue + FindFirstGlobalVariable (lldb::SBTarget &target, const char *name); + + lldb::ByteOrder + GetByteOrder (); + + uint32_t + GetAddressByteSize(); + + const char * + GetTriple (); + + uint32_t + GetVersion (uint32_t *versions, + uint32_t num_versions); + + lldb::SBFileSpec + GetSymbolFileSpec() const; + + lldb::SBAddress + GetObjectFileHeaderAddress() const; + + bool + operator == (const lldb::SBModule &rhs) const; + + bool + operator != (const lldb::SBModule &rhs) const; + + %pythoncode %{ + class symbols_access(object): + re_compile_type = type(re.compile('.')) + '''A helper object that will lazily hand out lldb.SBSymbol objects for a module when supplied an index, name, or regular expression.''' + def __init__(self, sbmodule): + self.sbmodule = sbmodule + + def __len__(self): + if self.sbmodule: + return int(self.sbmodule.GetNumSymbols()) + return 0 + + def __getitem__(self, key): + count = len(self) + if type(key) is int: + if key < count: + return self.sbmodule.GetSymbolAtIndex(key) + elif type(key) is str: + matches = [] + sc_list = self.sbmodule.FindSymbols(key) + for sc in sc_list: + symbol = sc.symbol + if symbol: + matches.append(symbol) + return matches + elif isinstance(key, self.re_compile_type): + matches = [] + for idx in range(count): + symbol = self.sbmodule.GetSymbolAtIndex(idx) + added = False + name = symbol.name + if name: + re_match = key.search(name) + if re_match: + matches.append(symbol) + added = True + if not added: + mangled = symbol.mangled + if mangled: + re_match = key.search(mangled) + if re_match: + matches.append(symbol) + return matches + else: + print("error: unsupported item type: %s" % type(key)) + return None + + def get_symbols_access_object(self): + '''An accessor function that returns a symbols_access() object which allows lazy symbol access from a lldb.SBModule object.''' + return self.symbols_access (self) + + def get_compile_units_access_object (self): + '''An accessor function that returns a compile_units_access() object which allows lazy compile unit access from a lldb.SBModule object.''' + return self.compile_units_access (self) + + def get_symbols_array(self): + '''An accessor function that returns a list() that contains all symbols in a lldb.SBModule object.''' + symbols = [] + for idx in range(self.num_symbols): + symbols.append(self.GetSymbolAtIndex(idx)) + return symbols + + class sections_access(object): + re_compile_type = type(re.compile('.')) + '''A helper object that will lazily hand out lldb.SBSection objects for a module when supplied an index, name, or regular expression.''' + def __init__(self, sbmodule): + self.sbmodule = sbmodule + + def __len__(self): + if self.sbmodule: + return int(self.sbmodule.GetNumSections()) + return 0 + + def __getitem__(self, key): + count = len(self) + if type(key) is int: + if key < count: + return self.sbmodule.GetSectionAtIndex(key) + elif type(key) is str: + for idx in range(count): + section = self.sbmodule.GetSectionAtIndex(idx) + if section.name == key: + return section + elif isinstance(key, self.re_compile_type): + matches = [] + for idx in range(count): + section = self.sbmodule.GetSectionAtIndex(idx) + name = section.name + if name: + re_match = key.search(name) + if re_match: + matches.append(section) + return matches + else: + print("error: unsupported item type: %s" % type(key)) + return None + + class compile_units_access(object): + re_compile_type = type(re.compile('.')) + '''A helper object that will lazily hand out lldb.SBCompileUnit objects for a module when supplied an index, full or partial path, or regular expression.''' + def __init__(self, sbmodule): + self.sbmodule = sbmodule + + def __len__(self): + if self.sbmodule: + return int(self.sbmodule.GetNumCompileUnits()) + return 0 + + def __getitem__(self, key): + count = len(self) + if type(key) is int: + if key < count: + return self.sbmodule.GetCompileUnitAtIndex(key) + elif type(key) is str: + is_full_path = key[0] == '/' + for idx in range(count): + comp_unit = self.sbmodule.GetCompileUnitAtIndex(idx) + if is_full_path: + if comp_unit.file.fullpath == key: + return comp_unit + else: + if comp_unit.file.basename == key: + return comp_unit + elif isinstance(key, self.re_compile_type): + matches = [] + for idx in range(count): + comp_unit = self.sbmodule.GetCompileUnitAtIndex(idx) + fullpath = comp_unit.file.fullpath + if fullpath: + re_match = key.search(fullpath) + if re_match: + matches.append(comp_unit) + return matches + else: + print("error: unsupported item type: %s" % type(key)) + return None + + def get_sections_access_object(self): + '''An accessor function that returns a sections_access() object which allows lazy section array access.''' + return self.sections_access (self) + + def get_sections_array(self): + '''An accessor function that returns an array object that contains all sections in this module object.''' + if not hasattr(self, 'sections_array'): + self.sections_array = [] + for idx in range(self.num_sections): + self.sections_array.append(self.GetSectionAtIndex(idx)) + return self.sections_array + + def get_compile_units_array(self): + '''An accessor function that returns an array object that contains all compile_units in this module object.''' + if not hasattr(self, 'compile_units_array'): + self.compile_units_array = [] + for idx in range(self.GetNumCompileUnits()): + self.compile_units_array.append(self.GetCompileUnitAtIndex(idx)) + return self.compile_units_array + + __swig_getmethods__["symbols"] = get_symbols_array + if _newclass: symbols = property(get_symbols_array, None, doc='''A read only property that returns a list() of lldb.SBSymbol objects contained in this module.''') + + __swig_getmethods__["symbol"] = get_symbols_access_object + if _newclass: symbol = property(get_symbols_access_object, None, doc='''A read only property that can be used to access symbols by index ("symbol = module.symbol[0]"), name ("symbols = module.symbol['main']"), or using a regular expression ("symbols = module.symbol[re.compile(...)]"). The return value is a single lldb.SBSymbol object for array access, and a list() of lldb.SBSymbol objects for name and regular expression access''') + + __swig_getmethods__["sections"] = get_sections_array + if _newclass: sections = property(get_sections_array, None, doc='''A read only property that returns a list() of lldb.SBSection objects contained in this module.''') + + __swig_getmethods__["compile_units"] = get_compile_units_array + if _newclass: compile_units = property(get_compile_units_array, None, doc='''A read only property that returns a list() of lldb.SBCompileUnit objects contained in this module.''') + + __swig_getmethods__["section"] = get_sections_access_object + if _newclass: section = property(get_sections_access_object, None, doc='''A read only property that can be used to access symbols by index ("section = module.section[0]"), name ("sections = module.section[\'main\']"), or using a regular expression ("sections = module.section[re.compile(...)]"). The return value is a single lldb.SBSection object for array access, and a list() of lldb.SBSection objects for name and regular expression access''') + + __swig_getmethods__["compile_unit"] = get_compile_units_access_object + if _newclass: section = property(get_sections_access_object, None, doc='''A read only property that can be used to access compile units by index ("compile_unit = module.compile_unit[0]"), name ("compile_unit = module.compile_unit[\'main.cpp\']"), or using a regular expression ("compile_unit = module.compile_unit[re.compile(...)]"). The return value is a single lldb.SBCompileUnit object for array access or by full or partial path, and a list() of lldb.SBCompileUnit objects regular expressions.''') + + def get_uuid(self): + return uuid.UUID (self.GetUUIDString()) + + __swig_getmethods__["uuid"] = get_uuid + if _newclass: uuid = property(get_uuid, None, doc='''A read only property that returns a standard python uuid.UUID object that represents the UUID of this module.''') + + __swig_getmethods__["file"] = GetFileSpec + if _newclass: file = property(GetFileSpec, None, doc='''A read only property that returns an lldb object that represents the file (lldb.SBFileSpec) for this object file for this module as it is represented where it is being debugged.''') + + __swig_getmethods__["platform_file"] = GetPlatformFileSpec + if _newclass: platform_file = property(GetPlatformFileSpec, None, doc='''A read only property that returns an lldb object that represents the file (lldb.SBFileSpec) for this object file for this module as it is represented on the current host system.''') + + __swig_getmethods__["byte_order"] = GetByteOrder + if _newclass: byte_order = property(GetByteOrder, None, doc='''A read only property that returns an lldb enumeration value (lldb.eByteOrderLittle, lldb.eByteOrderBig, lldb.eByteOrderInvalid) that represents the byte order for this module.''') + + __swig_getmethods__["addr_size"] = GetAddressByteSize + if _newclass: addr_size = property(GetAddressByteSize, None, doc='''A read only property that returns the size in bytes of an address for this module.''') + + __swig_getmethods__["triple"] = GetTriple + if _newclass: triple = property(GetTriple, None, doc='''A read only property that returns the target triple (arch-vendor-os) for this module.''') + + __swig_getmethods__["num_symbols"] = GetNumSymbols + if _newclass: num_symbols = property(GetNumSymbols, None, doc='''A read only property that returns number of symbols in the module symbol table as an integer.''') + + __swig_getmethods__["num_sections"] = GetNumSections + if _newclass: num_sections = property(GetNumSections, None, doc='''A read only property that returns number of sections in the module as an integer.''') + + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBModuleSpec.i b/scripts/interface/SBModuleSpec.i new file mode 100644 index 000000000000..55fd9b1a043d --- /dev/null +++ b/scripts/interface/SBModuleSpec.i @@ -0,0 +1,133 @@ +//===-- SWIG Interface for SBModule -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBModuleSpec +{ +public: + + SBModuleSpec (); + + SBModuleSpec (const lldb::SBModuleSpec &rhs); + + ~SBModuleSpec (); + + bool + IsValid () const; + + void + Clear(); + + //------------------------------------------------------------------ + /// Get const accessor for the module file. + /// + /// This function returns the file for the module on the host system + /// that is running LLDB. This can differ from the path on the + /// platform since we might be doing remote debugging. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetFileSpec (); + + void + SetFileSpec (const lldb::SBFileSpec &fspec); + + //------------------------------------------------------------------ + /// Get accessor for the module platform file. + /// + /// Platform file refers to the path of the module as it is known on + /// the remote system on which it is being debugged. For local + /// debugging this is always the same as Module::GetFileSpec(). But + /// remote debugging might mention a file '/usr/lib/liba.dylib' + /// which might be locally downloaded and cached. In this case the + /// platform file could be something like: + /// '/tmp/lldb/platform-cache/remote.host.computer/usr/lib/liba.dylib' + /// The file could also be cached in a local developer kit directory. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetPlatformFileSpec (); + + void + SetPlatformFileSpec (const lldb::SBFileSpec &fspec); + + lldb::SBFileSpec + GetSymbolFileSpec (); + + void + SetSymbolFileSpec (const lldb::SBFileSpec &fspec); + + const char * + GetObjectName (); + + void + SetObjectName (const char *name); + + const char * + GetTriple (); + + void + SetTriple (const char *triple); + + const uint8_t * + GetUUIDBytes (); + + size_t + GetUUIDLength (); + + bool + SetUUIDBytes (const uint8_t *uuid, size_t uuid_len); + + bool + GetDescription (lldb::SBStream &description); + +}; + + +class SBModuleSpecList +{ +public: + SBModuleSpecList(); + + SBModuleSpecList (const SBModuleSpecList &rhs); + + ~SBModuleSpecList(); + + static SBModuleSpecList + GetModuleSpecifications (const char *path); + + void + Append (const lldb::SBModuleSpec &spec); + + void + Append (const lldb::SBModuleSpecList &spec_list); + + lldb::SBModuleSpec + FindFirstMatchingSpec (const lldb::SBModuleSpec &match_spec); + + lldb::SBModuleSpecList + FindMatchingSpecs (const lldb::SBModuleSpec &match_spec); + + size_t + GetSize(); + + lldb::SBModuleSpec + GetSpecAtIndex (size_t i); + + bool + GetDescription (lldb::SBStream &description); + +}; + +} // namespace lldb diff --git a/scripts/interface/SBPlatform.i b/scripts/interface/SBPlatform.i new file mode 100644 index 000000000000..9234a3dbd86f --- /dev/null +++ b/scripts/interface/SBPlatform.i @@ -0,0 +1,196 @@ +//===-- SWIG Interface for SBPlatform ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + +class SBPlatformConnectOptions +{ +public: + SBPlatformConnectOptions (const char *url); + + SBPlatformConnectOptions (const SBPlatformConnectOptions &rhs); + + ~SBPlatformConnectOptions (); + + const char * + GetURL(); + + void + SetURL(const char *url); + + bool + GetRsyncEnabled(); + + void + EnableRsync (const char *options, + const char *remote_path_prefix, + bool omit_remote_hostname); + + void + DisableRsync (); + + const char * + GetLocalCacheDirectory(); + + void + SetLocalCacheDirectory(const char *path); +}; + +class SBPlatformShellCommand +{ +public: + SBPlatformShellCommand (const char *shell_command); + + SBPlatformShellCommand (const SBPlatformShellCommand &rhs); + + ~SBPlatformShellCommand(); + + void + Clear(); + + const char * + GetCommand(); + + void + SetCommand(const char *shell_command); + + const char * + GetWorkingDirectory (); + + void + SetWorkingDirectory (const char *path); + + uint32_t + GetTimeoutSeconds (); + + void + SetTimeoutSeconds (uint32_t sec); + + int + GetSignal (); + + int + GetStatus (); + + const char * + GetOutput (); +}; + +%feature("docstring", +"A class that represents a platform that can represent the current host or a remote host debug platform. + +The SBPlatform class represents the current host, or a remote host. +It can be connected to a remote platform in order to provide ways +to remotely launch and attach to processes, upload/download files, +create directories, run remote shell commands, find locally cached +versions of files from the remote system, and much more. + +SBPlatform objects can be created and then used to connect to a remote +platform which allows the SBPlatform to be used to get a list of the +current processes on the remote host, attach to one of those processes, +install programs on the remote system, attach and launch processes, +and much more. + +Every SBTarget has a corresponding SBPlatform. The platform can be +specified upon target creation, or the currently selected platform +will attempt to be used when creating the target automatically as long +as the currently selected platform matches the target architecture +and executable type. If the architecture or executable type do not match, +a suitable platform will be found automatically." + +) SBPlatform; +class SBPlatform +{ +public: + + SBPlatform (); + + SBPlatform (const char *); + + ~SBPlatform(); + + bool + IsValid () const; + + void + Clear (); + + const char * + GetWorkingDirectory(); + + bool + SetWorkingDirectory(const char *); + + const char * + GetName (); + + SBError + ConnectRemote (lldb::SBPlatformConnectOptions &connect_options); + + void + DisconnectRemote (); + + bool + IsConnected(); + + const char * + GetTriple(); + + const char * + GetHostname (); + + const char * + GetOSBuild (); + + const char * + GetOSDescription (); + + uint32_t + GetOSMajorVersion (); + + uint32_t + GetOSMinorVersion (); + + uint32_t + GetOSUpdateVersion (); + + lldb::SBError + Get (lldb::SBFileSpec &src, lldb::SBFileSpec &dst); + + lldb::SBError + Put (lldb::SBFileSpec &src, lldb::SBFileSpec &dst); + + lldb::SBError + Install (lldb::SBFileSpec &src, lldb::SBFileSpec &dst); + + lldb::SBError + Run (lldb::SBPlatformShellCommand &shell_command); + + lldb::SBError + Launch (lldb::SBLaunchInfo &launch_info); + + lldb::SBError + Kill (const lldb::pid_t pid); + + lldb::SBError + MakeDirectory (const char *path, uint32_t file_permissions = lldb::eFilePermissionsDirectoryDefault); + + uint32_t + GetFilePermissions (const char *path); + + lldb::SBError + SetFilePermissions (const char *path, uint32_t file_permissions); + + lldb::SBUnixSignals + GetUnixSignals(); + +}; + +} // namespace lldb diff --git a/scripts/interface/SBProcess.i b/scripts/interface/SBProcess.i new file mode 100644 index 000000000000..1571ebc4cb68 --- /dev/null +++ b/scripts/interface/SBProcess.i @@ -0,0 +1,502 @@ +//===-- SWIG Interface for SBProcess ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents the process associated with the target program. + +SBProcess supports thread iteration. For example (from test/lldbutil.py), + +# ================================================== +# Utility functions related to Threads and Processes +# ================================================== + +def get_stopped_threads(process, reason): + '''Returns the thread(s) with the specified stop reason in a list. + + The list can be empty if no such thread exists. + ''' + threads = [] + for t in process: + if t.GetStopReason() == reason: + threads.append(t) + return threads + +... +" +) SBProcess; +class SBProcess +{ +public: + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStateChanged = (1 << 0), + eBroadcastBitInterrupt = (1 << 1), + eBroadcastBitSTDOUT = (1 << 2), + eBroadcastBitSTDERR = (1 << 3), + eBroadcastBitProfileData = (1 << 4) + }; + + SBProcess (); + + SBProcess (const lldb::SBProcess& rhs); + + ~SBProcess(); + + static const char * + GetBroadcasterClassName (); + + const char * + GetPluginName (); + + const char * + GetShortPluginName (); + + void + Clear (); + + bool + IsValid() const; + + lldb::SBTarget + GetTarget() const; + + lldb::ByteOrder + GetByteOrder() const; + + %feature("autodoc", " + Writes data into the current process's stdin. API client specifies a Python + string as the only argument. + ") PutSTDIN; + size_t + PutSTDIN (const char *src, size_t src_len); + + %feature("autodoc", " + Reads data from the current process's stdout stream. API client specifies + the size of the buffer to read data into. It returns the byte buffer in a + Python string. + ") GetSTDOUT; + size_t + GetSTDOUT (char *dst, size_t dst_len) const; + + %feature("autodoc", " + Reads data from the current process's stderr stream. API client specifies + the size of the buffer to read data into. It returns the byte buffer in a + Python string. + ") GetSTDERR; + size_t + GetSTDERR (char *dst, size_t dst_len) const; + + size_t + GetAsyncProfileData(char *dst, size_t dst_len) const; + + void + ReportEventState (const lldb::SBEvent &event, FILE *out) const; + + void + AppendEventStateReport (const lldb::SBEvent &event, lldb::SBCommandReturnObject &result); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Remote connection related functions. These will fail if the + /// process is not in eStateConnected. They are intended for use + /// when connecting to an externally managed debugserver instance. + //------------------------------------------------------------------ + ") RemoteAttachToProcessWithID; + bool + RemoteAttachToProcessWithID (lldb::pid_t pid, + lldb::SBError& error); + + %feature("docstring", + "See SBTarget.Launch for argument description and usage." + ) RemoteLaunch; + bool + RemoteLaunch (char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags, + bool stop_at_entry, + lldb::SBError& error); + + //------------------------------------------------------------------ + // Thread related functions + //------------------------------------------------------------------ + uint32_t + GetNumThreads (); + + %feature("autodoc", " + Returns the INDEX'th thread from the list of current threads. The index + of a thread is only valid for the current stop. For a persistent thread + identifier use either the thread ID or the IndexID. See help on SBThread + for more details. + ") GetThreadAtIndex; + lldb::SBThread + GetThreadAtIndex (size_t index); + + %feature("autodoc", " + Returns the thread with the given thread ID. + ") GetThreadByID; + lldb::SBThread + GetThreadByID (lldb::tid_t sb_thread_id); + + %feature("autodoc", " + Returns the thread with the given thread IndexID. + ") GetThreadByIndexID; + lldb::SBThread + GetThreadByIndexID (uint32_t index_id); + + %feature("autodoc", " + Returns the currently selected thread. + ") GetSelectedThread; + lldb::SBThread + GetSelectedThread () const; + + %feature("autodoc", " + Lazily create a thread on demand through the current OperatingSystem plug-in, if the current OperatingSystem plug-in supports it. + ") CreateOSPluginThread; + lldb::SBThread + CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context); + + bool + SetSelectedThread (const lldb::SBThread &thread); + + bool + SetSelectedThreadByID (lldb::tid_t tid); + + bool + SetSelectedThreadByIndexID (uint32_t index_id); + + //------------------------------------------------------------------ + // Queue related functions + //------------------------------------------------------------------ + uint32_t + GetNumQueues (); + + lldb::SBQueue + GetQueueAtIndex (uint32_t index); + + //------------------------------------------------------------------ + // Stepping related functions + //------------------------------------------------------------------ + + lldb::StateType + GetState (); + + int + GetExitStatus (); + + const char * + GetExitDescription (); + + %feature("autodoc", " + Returns the process ID of the process. + ") GetProcessID; + lldb::pid_t + GetProcessID (); + + %feature("autodoc", " + Returns an integer ID that is guaranteed to be unique across all process instances. This is not the process ID, just a unique integer for comparison and caching purposes. + ") GetUniqueID; + uint32_t + GetUniqueID(); + + uint32_t + GetAddressByteSize() const; + + %feature("docstring", " + Kills the process and shuts down all threads that were spawned to + track and monitor process. + ") Destroy; + lldb::SBError + Destroy (); + + lldb::SBError + Continue (); + + lldb::SBError + Stop (); + + %feature("docstring", "Same as Destroy(self).") Destroy; + lldb::SBError + Kill (); + + lldb::SBError + Detach (); + + %feature("docstring", "Sends the process a unix signal.") Signal; + lldb::SBError + Signal (int signal); + + lldb::SBUnixSignals + GetUnixSignals(); + + %feature("docstring", " + Returns a stop id that will increase every time the process executes. If + include_expression_stops is true, then stops caused by expression evaluation + will cause the returned value to increase, otherwise the counter returned will + only increase when execution is continued explicitly by the user. Note, the value + will always increase, but may increase by more than one per stop. + ") GetStopID; + uint32_t + GetStopID(bool include_expression_stops = false); + + void + SendAsyncInterrupt(); + + %feature("autodoc", " + Reads memory from the current process's address space and removes any + traps that may have been inserted into the memory. It returns the byte + buffer in a Python string. Example: + + # Read 4 bytes from address 'addr' and assume error.Success() is True. + content = process.ReadMemory(addr, 4, error) + new_bytes = bytearray(content) + ") ReadMemory; + size_t + ReadMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error); + + %feature("autodoc", " + Writes memory to the current process's address space and maintains any + traps that might be present due to software breakpoints. Example: + + # Create a Python string from the byte array. + new_value = str(bytes) + result = process.WriteMemory(addr, new_value, error) + if not error.Success() or result != len(bytes): + print('SBProcess.WriteMemory() failed!') + ") WriteMemory; + size_t + WriteMemory (addr_t addr, const void *buf, size_t size, lldb::SBError &error); + + %feature("autodoc", " + Reads a NULL terminated C string from the current process's address space. + It returns a python string of the exact length, or truncates the string if + the maximum character limit is reached. Example: + + # Read a C string of at most 256 bytes from address '0x1000' + error = lldb.SBError() + cstring = process.ReadCStringFromMemory(0x1000, 256, error) + if error.Success(): + print('cstring: ', cstring) + else + print('error: ', error) + ") ReadCStringFromMemory; + + size_t + ReadCStringFromMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error); + + %feature("autodoc", " + Reads an unsigned integer from memory given a byte size and an address. + Returns the unsigned integer that was read. Example: + + # Read a 4 byte unsigned integer from address 0x1000 + error = lldb.SBError() + uint = ReadUnsignedFromMemory(0x1000, 4, error) + if error.Success(): + print('integer: %u' % uint) + else + print('error: ', error) + + ") ReadUnsignedFromMemory; + + uint64_t + ReadUnsignedFromMemory (addr_t addr, uint32_t byte_size, lldb::SBError &error); + + %feature("autodoc", " + Reads a pointer from memory from an address and returns the value. Example: + + # Read a pointer from address 0x1000 + error = lldb.SBError() + ptr = ReadPointerFromMemory(0x1000, error) + if error.Success(): + print('pointer: 0x%x' % ptr) + else + print('error: ', error) + + ") ReadPointerFromMemory; + + lldb::addr_t + ReadPointerFromMemory (addr_t addr, lldb::SBError &error); + + + // Events + static lldb::StateType + GetStateFromEvent (const lldb::SBEvent &event); + + static bool + GetRestartedFromEvent (const lldb::SBEvent &event); + + static size_t + GetNumRestartedReasonsFromEvent (const lldb::SBEvent &event); + + static const char * + GetRestartedReasonAtIndexFromEvent (const lldb::SBEvent &event, size_t idx); + + static lldb::SBProcess + GetProcessFromEvent (const lldb::SBEvent &event); + + static bool + GetInterruptedFromEvent (const lldb::SBEvent &event); + + static bool + EventIsProcessEvent (const lldb::SBEvent &event); + + lldb::SBBroadcaster + GetBroadcaster () const; + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetNumSupportedHardwareWatchpoints (lldb::SBError &error) const; + + uint32_t + LoadImage (lldb::SBFileSpec &image_spec, lldb::SBError &error); + + lldb::SBError + UnloadImage (uint32_t image_token); + + lldb::SBError + SendEventData (const char *event_data); + + %feature("autodoc", " + Return the number of different thread-origin extended backtraces + this process can support as a uint32_t. + When the process is stopped and you have an SBThread, lldb may be + able to show a backtrace of when that thread was originally created, + or the work item was enqueued to it (in the case of a libdispatch + queue). + ") GetNumExtendedBacktraceTypes; + + uint32_t + GetNumExtendedBacktraceTypes (); + + %feature("autodoc", " + Takes an index argument, returns the name of one of the thread-origin + extended backtrace methods as a str. + ") GetExtendedBacktraceTypeAtIndex; + + const char * + GetExtendedBacktraceTypeAtIndex (uint32_t idx); + + lldb::SBThreadCollection + GetHistoryThreads (addr_t addr); + + bool + IsInstrumentationRuntimePresent(lldb::InstrumentationRuntimeType type); + + lldb::SBError + SaveCore(const char *file_name); + + %pythoncode %{ + def __get_is_alive__(self): + '''Returns "True" if the process is currently alive, "False" otherwise''' + s = self.GetState() + if (s == eStateAttaching or + s == eStateLaunching or + s == eStateStopped or + s == eStateRunning or + s == eStateStepping or + s == eStateCrashed or + s == eStateSuspended): + return True + return False + + def __get_is_running__(self): + '''Returns "True" if the process is currently running, "False" otherwise''' + state = self.GetState() + if state == eStateRunning or state == eStateStepping: + return True + return False + + def __get_is_running__(self): + '''Returns "True" if the process is currently stopped, "False" otherwise''' + state = self.GetState() + if state == eStateStopped or state == eStateCrashed or state == eStateSuspended: + return True + return False + + class threads_access(object): + '''A helper object that will lazily hand out thread for a process when supplied an index.''' + def __init__(self, sbprocess): + self.sbprocess = sbprocess + + def __len__(self): + if self.sbprocess: + return int(self.sbprocess.GetNumThreads()) + return 0 + + def __getitem__(self, key): + if type(key) is int and key < len(self): + return self.sbprocess.GetThreadAtIndex(key) + return None + + def get_threads_access_object(self): + '''An accessor function that returns a modules_access() object which allows lazy thread access from a lldb.SBProcess object.''' + return self.threads_access (self) + + def get_process_thread_list(self): + '''An accessor function that returns a list() that contains all threads in a lldb.SBProcess object.''' + threads = [] + accessor = self.get_threads_access_object() + for idx in range(len(accessor)): + threads.append(accessor[idx]) + return threads + + __swig_getmethods__["threads"] = get_process_thread_list + if _newclass: threads = property(get_process_thread_list, None, doc='''A read only property that returns a list() of lldb.SBThread objects for this process.''') + + __swig_getmethods__["thread"] = get_threads_access_object + if _newclass: thread = property(get_threads_access_object, None, doc='''A read only property that returns an object that can access threads by thread index (thread = lldb.process.thread[12]).''') + + __swig_getmethods__["is_alive"] = __get_is_alive__ + if _newclass: is_alive = property(__get_is_alive__, None, doc='''A read only property that returns a boolean value that indicates if this process is currently alive.''') + + __swig_getmethods__["is_running"] = __get_is_running__ + if _newclass: is_running = property(__get_is_running__, None, doc='''A read only property that returns a boolean value that indicates if this process is currently running.''') + + __swig_getmethods__["is_stopped"] = __get_is_running__ + if _newclass: is_stopped = property(__get_is_running__, None, doc='''A read only property that returns a boolean value that indicates if this process is currently stopped.''') + + __swig_getmethods__["id"] = GetProcessID + if _newclass: id = property(GetProcessID, None, doc='''A read only property that returns the process ID as an integer.''') + + __swig_getmethods__["target"] = GetTarget + if _newclass: target = property(GetTarget, None, doc='''A read only property that an lldb object that represents the target (lldb.SBTarget) that owns this process.''') + + __swig_getmethods__["num_threads"] = GetNumThreads + if _newclass: num_threads = property(GetNumThreads, None, doc='''A read only property that returns the number of threads in this process as an integer.''') + + __swig_getmethods__["selected_thread"] = GetSelectedThread + __swig_setmethods__["selected_thread"] = SetSelectedThread + if _newclass: selected_thread = property(GetSelectedThread, SetSelectedThread, doc='''A read/write property that gets/sets the currently selected thread in this process. The getter returns a lldb.SBThread object and the setter takes an lldb.SBThread object.''') + + __swig_getmethods__["state"] = GetState + if _newclass: state = property(GetState, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eState") that represents the current state of this process (running, stopped, exited, etc.).''') + + __swig_getmethods__["exit_state"] = GetExitStatus + if _newclass: exit_state = property(GetExitStatus, None, doc='''A read only property that returns an exit status as an integer of this process when the process state is lldb.eStateExited.''') + + __swig_getmethods__["exit_description"] = GetExitDescription + if _newclass: exit_description = property(GetExitDescription, None, doc='''A read only property that returns an exit description as a string of this process when the process state is lldb.eStateExited.''') + + __swig_getmethods__["broadcaster"] = GetBroadcaster + if _newclass: broadcaster = property(GetBroadcaster, None, doc='''A read only property that an lldb object that represents the broadcaster (lldb.SBBroadcaster) for this process.''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBQueue.i b/scripts/interface/SBQueue.i new file mode 100644 index 000000000000..52724032a876 --- /dev/null +++ b/scripts/interface/SBQueue.i @@ -0,0 +1,75 @@ +//===-- SWIG Interface for SBQueue.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBQueue +{ +public: + SBQueue (); + + SBQueue (const lldb::QueueSP& queue_sp); + + ~SBQueue(); + + bool + IsValid() const; + + void + Clear (); + + lldb::SBProcess + GetProcess (); + + %feature("autodoc", " + Returns an lldb::queue_id_t type unique identifier number for this + queue that will not be used by any other queue during this process' + execution. These ID numbers often start at 1 with the first + system-created queues and increment from there. + ") + GetQueueID; + + lldb::queue_id_t + GetQueueID () const; + + const char * + GetName () const; + + %feature("autodoc", " + Returns an lldb::QueueKind enumerated value (e.g. eQueueKindUnknown, + eQueueKindSerial, eQueueKindConcurrent) describing the type of this + queue. + ") + GetKind(); + + lldb::QueueKind + GetKind(); + + uint32_t + GetIndexID () const; + + uint32_t + GetNumThreads (); + + lldb::SBThread + GetThreadAtIndex (uint32_t); + + uint32_t + GetNumPendingItems (); + + lldb::SBQueueItem + GetPendingItemAtIndex (uint32_t); + + uint32_t + GetNumRunningItems (); + +}; + +} // namespace lldb + diff --git a/scripts/interface/SBQueueItem.i b/scripts/interface/SBQueueItem.i new file mode 100644 index 000000000000..ffa24150b678 --- /dev/null +++ b/scripts/interface/SBQueueItem.i @@ -0,0 +1,46 @@ +//===-- SWIG Interface for SBQueueItem.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBQueueItem +{ +public: + SBQueueItem (); + + SBQueueItem (const lldb::QueueItemSP& queue_item_sp); + + ~SBQueueItem(); + + bool + IsValid() const; + + void + Clear (); + + lldb::QueueItemKind + GetKind () const; + + void + SetKind (lldb::QueueItemKind kind); + + lldb::SBAddress + GetAddress () const; + + void + SetAddress (lldb::SBAddress addr); + + void + SetQueueItem (const lldb::QueueItemSP& queue_item_sp); + + lldb::SBThread + GetExtendedBacktraceThread (const char *type); +}; + +} // namespace lldb diff --git a/scripts/interface/SBSection.i b/scripts/interface/SBSection.i new file mode 100644 index 000000000000..c94161e412b0 --- /dev/null +++ b/scripts/interface/SBSection.i @@ -0,0 +1,154 @@ +//===-- SWIG Interface for SBSection ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents an executable image section. + +SBSection supports iteration through its subsection, represented as SBSection +as well. For example, + + for sec in exe_module: + if sec.GetName() == '__TEXT': + print sec + break + print INDENT + 'Number of subsections: %d' % sec.GetNumSubSections() + for subsec in sec: + print INDENT + repr(subsec) + +produces: + +[0x0000000100000000-0x0000000100002000) a.out.__TEXT + Number of subsections: 6 + [0x0000000100001780-0x0000000100001d5c) a.out.__TEXT.__text + [0x0000000100001d5c-0x0000000100001da4) a.out.__TEXT.__stubs + [0x0000000100001da4-0x0000000100001e2c) a.out.__TEXT.__stub_helper + [0x0000000100001e2c-0x0000000100001f10) a.out.__TEXT.__cstring + [0x0000000100001f10-0x0000000100001f68) a.out.__TEXT.__unwind_info + [0x0000000100001f68-0x0000000100001ff8) a.out.__TEXT.__eh_frame + +See also SBModule." +) SBSection; + +class SBSection +{ +public: + + SBSection (); + + SBSection (const lldb::SBSection &rhs); + + ~SBSection (); + + bool + IsValid () const; + + const char * + GetName (); + + lldb::SBSection + GetParent(); + + lldb::SBSection + FindSubSection (const char *sect_name); + + size_t + GetNumSubSections (); + + lldb::SBSection + GetSubSectionAtIndex (size_t idx); + + lldb::addr_t + GetFileAddress (); + + lldb::addr_t + GetLoadAddress (lldb::SBTarget &target); + + lldb::addr_t + GetByteSize (); + + uint64_t + GetFileOffset (); + + uint64_t + GetFileByteSize (); + + lldb::SBData + GetSectionData (); + + lldb::SBData + GetSectionData (uint64_t offset, + uint64_t size); + + SectionType + GetSectionType (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Return the size of a target's byte represented by this section + /// in numbers of host bytes. Note that certain architectures have + /// varying minimum addressable unit (i.e. byte) size for their + /// CODE or DATA buses. + /// + /// @return + /// The number of host (8-bit) bytes needed to hold a target byte + //------------------------------------------------------------------ + ") GetTargetByteSize; + uint32_t + GetTargetByteSize (); + + bool + GetDescription (lldb::SBStream &description); + + bool + operator == (const lldb::SBSection &rhs); + + bool + operator != (const lldb::SBSection &rhs); + + %pythoncode %{ + def get_addr(self): + return SBAddress(self, 0) + + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None, doc='''A read only property that returns the name of this section as a string.''') + + __swig_getmethods__["addr"] = get_addr + if _newclass: addr = property(get_addr, None, doc='''A read only property that returns an lldb object that represents the start address (lldb.SBAddress) for this section.''') + + __swig_getmethods__["file_addr"] = GetFileAddress + if _newclass: file_addr = property(GetFileAddress, None, doc='''A read only property that returns an integer that represents the starting "file" address for this section, or the address of the section in the object file in which it is defined.''') + + __swig_getmethods__["size"] = GetByteSize + if _newclass: size = property(GetByteSize, None, doc='''A read only property that returns the size in bytes of this section as an integer.''') + + __swig_getmethods__["file_offset"] = GetFileOffset + if _newclass: file_offset = property(GetFileOffset, None, doc='''A read only property that returns the file offset in bytes of this section as an integer.''') + + __swig_getmethods__["file_size"] = GetFileByteSize + if _newclass: file_size = property(GetFileByteSize, None, doc='''A read only property that returns the file size in bytes of this section as an integer.''') + + __swig_getmethods__["data"] = GetSectionData + if _newclass: data = property(GetSectionData, None, doc='''A read only property that returns an lldb object that represents the bytes for this section (lldb.SBData) for this section.''') + + __swig_getmethods__["type"] = GetSectionType + if _newclass: type = property(GetSectionType, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eSectionType") that represents the type of this section (code, data, etc.).''') + + __swig_getmethods__["target_byte_size"] = GetTargetByteSize + if _newclass: target_byte_size = property(GetTargetByteSize, None, doc='''A read only property that returns the size of a target byte represented by this section as a number of host bytes.''') + + %} + +private: + + std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb diff --git a/scripts/interface/SBSourceManager.i b/scripts/interface/SBSourceManager.i new file mode 100644 index 000000000000..09cd449149d9 --- /dev/null +++ b/scripts/interface/SBSourceManager.i @@ -0,0 +1,54 @@ +//===-- SWIG Interface for SBSourceManager ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a central authority for displaying source code. + +For example (from test/source-manager/TestSourceManager.py), + + # Create the filespec for 'main.c'. + filespec = lldb.SBFileSpec('main.c', False) + source_mgr = self.dbg.GetSourceManager() + # Use a string stream as the destination. + stream = lldb.SBStream() + source_mgr.DisplaySourceLinesWithLineNumbers(filespec, + self.line, + 2, # context before + 2, # context after + '=>', # prefix for current line + stream) + + # 2 + # 3 int main(int argc, char const *argv[]) { + # => 4 printf('Hello world.\\n'); // Set break point at this line. + # 5 return 0; + # 6 } + self.expect(stream.GetData(), 'Source code displayed correctly', + exe=False, + patterns = ['=> %d.*Hello world' % self.line]) +") SBSourceManager; +class SBSourceManager +{ +public: + SBSourceManager (const lldb::SBSourceManager &rhs); + + ~SBSourceManager(); + + size_t + DisplaySourceLinesWithLineNumbers (const lldb::SBFileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + lldb::SBStream &s); +}; + +} // namespace lldb diff --git a/scripts/interface/SBStream.i b/scripts/interface/SBStream.i new file mode 100644 index 000000000000..f634def72b16 --- /dev/null +++ b/scripts/interface/SBStream.i @@ -0,0 +1,100 @@ +//===-- SWIG Interface for SBStream -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +namespace lldb { + +%feature("docstring", +"Represents a destination for streaming data output to. By default, a string +stream is created. + +For example (from test/source-manager/TestSourceManager.py), + + # Create the filespec for 'main.c'. + filespec = lldb.SBFileSpec('main.c', False) + source_mgr = self.dbg.GetSourceManager() + # Use a string stream as the destination. + stream = lldb.SBStream() + source_mgr.DisplaySourceLinesWithLineNumbers(filespec, + self.line, + 2, # context before + 2, # context after + '=>', # prefix for current line + stream) + + # 2 + # 3 int main(int argc, char const *argv[]) { + # => 4 printf('Hello world.\\n'); // Set break point at this line. + # 5 return 0; + # 6 } + self.expect(stream.GetData(), 'Source code displayed correctly', + exe=False, + patterns = ['=> %d.*Hello world' % self.line]) +") SBStream; +class SBStream +{ +public: + + SBStream (); + + ~SBStream (); + + bool + IsValid() const; + + %feature("docstring", " + //-------------------------------------------------------------------------- + /// If this stream is not redirected to a file, it will maintain a local + /// cache for the stream data which can be accessed using this accessor. + //-------------------------------------------------------------------------- + ") GetData; + const char * + GetData (); + + %feature("docstring", " + //-------------------------------------------------------------------------- + /// If this stream is not redirected to a file, it will maintain a local + /// cache for the stream output whose length can be accessed using this + /// accessor. + //-------------------------------------------------------------------------- + ") GetSize; + size_t + GetSize(); + + // wrapping the variadic Printf() with a plain Print() + // because it is hard to support varargs in SWIG bridgings + %extend { + void Print (const char* str) + { + self->Printf("%s", str); + } + } + + void + RedirectToFile (const char *path, bool append); + + void + RedirectToFileHandle (FILE *fh, bool transfer_fh_ownership); + + void + RedirectToFileDescriptor (int fd, bool transfer_fh_ownership); + + %feature("docstring", " + //-------------------------------------------------------------------------- + /// If the stream is redirected to a file, forget about the file and if + /// ownership of the file was transferred to this object, close the file. + /// If the stream is backed by a local cache, clear this cache. + //-------------------------------------------------------------------------- + ") Clear; + void + Clear (); +}; + +} // namespace lldb diff --git a/scripts/interface/SBStringList.i b/scripts/interface/SBStringList.i new file mode 100644 index 000000000000..901aa88b183a --- /dev/null +++ b/scripts/interface/SBStringList.i @@ -0,0 +1,44 @@ +//===-- SWIG Interface for SBStringList -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBStringList +{ +public: + + SBStringList (); + + SBStringList (const lldb::SBStringList &rhs); + + ~SBStringList (); + + bool + IsValid() const; + + void + AppendString (const char *str); + + void + AppendList (const char **strv, int strc); + + void + AppendList (const lldb::SBStringList &strings); + + uint32_t + GetSize () const; + + const char * + GetStringAtIndex (size_t idx); + + void + Clear (); +}; + +} // namespace lldb diff --git a/scripts/interface/SBSymbol.i b/scripts/interface/SBSymbol.i new file mode 100644 index 000000000000..b6717055e486 --- /dev/null +++ b/scripts/interface/SBSymbol.i @@ -0,0 +1,110 @@ +//===-- SWIG Interface for SBSymbol -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents the symbol possibly associated with a stack frame. +SBModule contains SBSymbol(s). SBSymbol can also be retrived from SBFrame. + +See also SBModule and SBFrame." +) SBSymbol; +class SBSymbol +{ +public: + + SBSymbol (); + + ~SBSymbol (); + + SBSymbol (const lldb::SBSymbol &rhs); + + bool + IsValid () const; + + + const char * + GetName() const; + + const char * + GetDisplayName() const; + + const char * + GetMangledName () const; + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target); + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target, const char *flavor_string); + + SBAddress + GetStartAddress (); + + SBAddress + GetEndAddress (); + + uint32_t + GetPrologueByteSize (); + + SymbolType + GetType (); + + bool + GetDescription (lldb::SBStream &description); + + bool + IsExternal(); + + bool + IsSynthetic(); + + bool + operator == (const lldb::SBSymbol &rhs) const; + + bool + operator != (const lldb::SBSymbol &rhs) const; + + %pythoncode %{ + def get_instructions_from_current_target (self): + return self.GetInstructions (target) + + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None, doc='''A read only property that returns the name for this symbol as a string.''') + + __swig_getmethods__["mangled"] = GetMangledName + if _newclass: mangled = property(GetMangledName, None, doc='''A read only property that returns the mangled (linkage) name for this symbol as a string.''') + + __swig_getmethods__["type"] = GetType + if _newclass: type = property(GetType, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eSymbolType") that represents the type of this symbol.''') + + __swig_getmethods__["addr"] = GetStartAddress + if _newclass: addr = property(GetStartAddress, None, doc='''A read only property that returns an lldb object that represents the start address (lldb.SBAddress) for this symbol.''') + + __swig_getmethods__["end_addr"] = GetEndAddress + if _newclass: end_addr = property(GetEndAddress, None, doc='''A read only property that returns an lldb object that represents the end address (lldb.SBAddress) for this symbol.''') + + __swig_getmethods__["prologue_size"] = GetPrologueByteSize + if _newclass: prologue_size = property(GetPrologueByteSize, None, doc='''A read only property that returns the size in bytes of the prologue instructions as an unsigned integer.''') + + __swig_getmethods__["instructions"] = get_instructions_from_current_target + if _newclass: instructions = property(get_instructions_from_current_target, None, doc='''A read only property that returns an lldb object that represents the instructions (lldb.SBInstructionList) for this symbol.''') + + __swig_getmethods__["external"] = IsExternal + if _newclass: external = property(IsExternal, None, doc='''A read only property that returns a boolean value that indicates if this symbol is externally visiable (exported) from the module that contains it.''') + + __swig_getmethods__["synthetic"] = IsSynthetic + if _newclass: synthetic = property(IsSynthetic, None, doc='''A read only property that returns a boolean value that indicates if this symbol was synthetically created from information in module that contains it.''') + + + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBSymbolContext.i b/scripts/interface/SBSymbolContext.i new file mode 100644 index 000000000000..fa405842d6e5 --- /dev/null +++ b/scripts/interface/SBSymbolContext.i @@ -0,0 +1,112 @@ +//===-- SWIG Interface for SBSymbolContext ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"A context object that provides access to core debugger entities. + +Manay 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. + +For example, + + exe = os.path.join(os.getcwd(), 'a.out') + + # Create a target for the debugger. + target = self.dbg.CreateTarget(exe) + + # Now create a breakpoint on main.c by name 'c'. + breakpoint = target.BreakpointCreateByName('c', 'a.out') + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple(None, None, os.getcwd()) + + # The inferior should stop on 'c'. + from lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + # Now get the SBSymbolContext from this frame. We want everything. :-) + context = frame0.GetSymbolContext(lldb.eSymbolContextEverything) + + # Get the module. + module = context.GetModule() + ... + + # And the compile unit associated with the frame. + compileUnit = context.GetCompileUnit() + ... +" +) SBSymbolContext; +class SBSymbolContext +{ +public: + SBSymbolContext (); + + SBSymbolContext (const lldb::SBSymbolContext& rhs); + + ~SBSymbolContext (); + + bool + IsValid () const; + + lldb::SBModule GetModule (); + lldb::SBCompileUnit GetCompileUnit (); + lldb::SBFunction GetFunction (); + lldb::SBBlock GetBlock (); + lldb::SBLineEntry GetLineEntry (); + lldb::SBSymbol GetSymbol (); + + void SetModule (lldb::SBModule module); + void SetCompileUnit (lldb::SBCompileUnit compile_unit); + void SetFunction (lldb::SBFunction function); + void SetBlock (lldb::SBBlock block); + void SetLineEntry (lldb::SBLineEntry line_entry); + void SetSymbol (lldb::SBSymbol symbol); + + lldb::SBSymbolContext + GetParentOfInlinedScope (const lldb::SBAddress &curr_frame_pc, + lldb::SBAddress &parent_frame_addr) const; + + + bool + GetDescription (lldb::SBStream &description); + + + %pythoncode %{ + __swig_getmethods__["module"] = GetModule + __swig_setmethods__["module"] = SetModule + if _newclass: module = property(GetModule, SetModule, doc='''A read/write property that allows the getting/setting of the module (lldb.SBModule) in this symbol context.''') + + __swig_getmethods__["compile_unit"] = GetCompileUnit + __swig_setmethods__["compile_unit"] = SetCompileUnit + if _newclass: compile_unit = property(GetCompileUnit, SetCompileUnit, doc='''A read/write property that allows the getting/setting of the compile unit (lldb.SBCompileUnit) in this symbol context.''') + + __swig_getmethods__["function"] = GetFunction + __swig_setmethods__["function"] = SetFunction + if _newclass: function = property(GetFunction, SetFunction, doc='''A read/write property that allows the getting/setting of the function (lldb.SBFunction) in this symbol context.''') + + __swig_getmethods__["block"] = GetBlock + __swig_setmethods__["block"] = SetBlock + if _newclass: block = property(GetBlock, SetBlock, doc='''A read/write property that allows the getting/setting of the block (lldb.SBBlock) in this symbol context.''') + + __swig_getmethods__["symbol"] = GetSymbol + __swig_setmethods__["symbol"] = SetSymbol + if _newclass: symbol = property(GetSymbol, SetSymbol, doc='''A read/write property that allows the getting/setting of the symbol (lldb.SBSymbol) in this symbol context.''') + + __swig_getmethods__["line_entry"] = GetLineEntry + __swig_setmethods__["line_entry"] = SetLineEntry + if _newclass: line_entry = property(GetLineEntry, SetLineEntry, doc='''A read/write property that allows the getting/setting of the line entry (lldb.SBLineEntry) in this symbol context.''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBSymbolContextList.i b/scripts/interface/SBSymbolContextList.i new file mode 100644 index 000000000000..25e889499c83 --- /dev/null +++ b/scripts/interface/SBSymbolContextList.i @@ -0,0 +1,140 @@ +//===-- SWIG Interface for SBSymbolContextList ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a list of symbol context object. See also SBSymbolContext. + +For example (from test/python_api/target/TestTargetAPI.py), + + def find_functions(self, exe_name): + '''Exercise SBTaget.FindFunctions() API.''' + exe = os.path.join(os.getcwd(), exe_name) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + list = lldb.SBSymbolContextList() + num = target.FindFunctions('c', lldb.eFunctionNameTypeAuto, False, list) + self.assertTrue(num == 1 and list.GetSize() == 1) + + for sc in list: + self.assertTrue(sc.GetModule().GetFileSpec().GetFilename() == exe_name) + self.assertTrue(sc.GetSymbol().GetName() == 'c') +") SBSymbolContextList; +class SBSymbolContextList +{ +public: + SBSymbolContextList (); + + SBSymbolContextList (const lldb::SBSymbolContextList& rhs); + + ~SBSymbolContextList (); + + bool + IsValid () const; + + uint32_t + GetSize() const; + + SBSymbolContext + GetContextAtIndex (uint32_t idx); + + void + Append (lldb::SBSymbolContext &sc); + + void + Append (lldb::SBSymbolContextList &sc_list); + + bool + GetDescription (lldb::SBStream &description); + + void + Clear(); + + %pythoncode %{ + def __len__(self): + return int(self.GetSize()) + + def __getitem__(self, key): + count = len(self) + if type(key) is int: + if key < count: + return self.GetContextAtIndex(key) + else: + raise IndexError + raise TypeError + + def get_module_array(self): + a = [] + for i in range(len(self)): + obj = self.GetContextAtIndex(i).module + if obj: + a.append(obj) + return a + + def get_compile_unit_array(self): + a = [] + for i in range(len(self)): + obj = self.GetContextAtIndex(i).compile_unit + if obj: + a.append(obj) + return a + def get_function_array(self): + a = [] + for i in range(len(self)): + obj = self.GetContextAtIndex(i).function + if obj: + a.append(obj) + return a + def get_block_array(self): + a = [] + for i in range(len(self)): + obj = self.GetContextAtIndex(i).block + if obj: + a.append(obj) + return a + def get_symbol_array(self): + a = [] + for i in range(len(self)): + obj = self.GetContextAtIndex(i).symbol + if obj: + a.append(obj) + return a + def get_line_entry_array(self): + a = [] + for i in range(len(self)): + obj = self.GetContextAtIndex(i).line_entry + if obj: + a.append(obj) + return a + __swig_getmethods__["modules"] = get_module_array + if _newclass: modules = property(get_module_array, None, doc='''Returns a list() of lldb.SBModule objects, one for each module in each SBSymbolContext object in this list.''') + + __swig_getmethods__["compile_units"] = get_compile_unit_array + if _newclass: compile_units = property(get_compile_unit_array, None, doc='''Returns a list() of lldb.SBCompileUnit objects, one for each compile unit in each SBSymbolContext object in this list.''') + + __swig_getmethods__["functions"] = get_function_array + if _newclass: functions = property(get_function_array, None, doc='''Returns a list() of lldb.SBFunction objects, one for each function in each SBSymbolContext object in this list.''') + + __swig_getmethods__["blocks"] = get_block_array + if _newclass: blocks = property(get_block_array, None, doc='''Returns a list() of lldb.SBBlock objects, one for each block in each SBSymbolContext object in this list.''') + + __swig_getmethods__["line_entries"] = get_line_entry_array + if _newclass: line_entries = property(get_line_entry_array, None, doc='''Returns a list() of lldb.SBLineEntry objects, one for each line entry in each SBSymbolContext object in this list.''') + + __swig_getmethods__["symbols"] = get_symbol_array + if _newclass: symbols = property(get_symbol_array, None, doc='''Returns a list() of lldb.SBSymbol objects, one for each symbol in each SBSymbolContext object in this list.''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBTarget.i b/scripts/interface/SBTarget.i new file mode 100644 index 000000000000..74e470d4b3fa --- /dev/null +++ b/scripts/interface/SBTarget.i @@ -0,0 +1,899 @@ +//===-- SWIG Interface for SBTarget -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + +%feature("docstring", +"Represents the target program running under the debugger. + +SBTarget supports module, breakpoint, and watchpoint iterations. For example, + + for m in target.module_iter(): + print m + +produces: + +(x86_64) /Volumes/data/lldb/svn/trunk/test/python_api/lldbutil/iter/a.out +(x86_64) /usr/lib/dyld +(x86_64) /usr/lib/libstdc++.6.dylib +(x86_64) /usr/lib/libSystem.B.dylib +(x86_64) /usr/lib/system/libmathCommon.A.dylib +(x86_64) /usr/lib/libSystem.B.dylib(__commpage) + +and, + + for b in target.breakpoint_iter(): + print b + +produces: + +SBBreakpoint: id = 1, file ='main.cpp', line = 66, locations = 1 +SBBreakpoint: id = 2, file ='main.cpp', line = 85, locations = 1 + +and, + + for wp_loc in target.watchpoint_iter(): + print wp_loc + +produces: + +Watchpoint 1: addr = 0x1034ca048 size = 4 state = enabled type = rw + declare @ '/Volumes/data/lldb/svn/trunk/test/python_api/watchpoint/main.c:12' + hw_index = 0 hit_count = 2 ignore_count = 0" +) SBTarget; +class SBTarget +{ +public: + //------------------------------------------------------------------ + // Broadcaster bits. + //------------------------------------------------------------------ + enum + { + eBroadcastBitBreakpointChanged = (1 << 0), + eBroadcastBitModulesLoaded = (1 << 1), + eBroadcastBitModulesUnloaded = (1 << 2), + eBroadcastBitWatchpointChanged = (1 << 3), + eBroadcastBitSymbolsLoaded = (1 << 4) + }; + + //------------------------------------------------------------------ + // Constructors + //------------------------------------------------------------------ + SBTarget (); + + SBTarget (const lldb::SBTarget& rhs); + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~SBTarget(); + + static const char * + GetBroadcasterClassName (); + + bool + IsValid() const; + + static bool + EventIsTargetEvent (const lldb::SBEvent &event); + + static lldb::SBTarget + GetTargetFromEvent (const lldb::SBEvent &event); + + static uint32_t + GetNumModulesFromEvent (const lldb::SBEvent &event); + + static lldb::SBModule + GetModuleAtIndexFromEvent (const uint32_t idx, const lldb::SBEvent &event); + + lldb::SBProcess + GetProcess (); + + + %feature("docstring", " + //------------------------------------------------------------------ + /// Return the platform object associated with the target. + /// + /// After return, the platform object should be checked for + /// validity. + /// + /// @return + /// A platform object. + //------------------------------------------------------------------ + ") GetPlatform; + lldb::SBPlatform + GetPlatform (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Install any binaries that need to be installed. + /// + /// This function does nothing when debugging on the host system. + /// When connected to remote platforms, the target's main executable + /// and any modules that have their install path set will be + /// installed on the remote platform. If the main executable doesn't + /// have an install location set, it will be installed in the remote + /// platform's working directory. + /// + /// @return + /// An error describing anything that went wrong during + /// installation. + //------------------------------------------------------------------ + ") Install; + lldb::SBError + Install(); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Launch a new process. + /// + /// Launch a new process by spawning a new process using the + /// target object's executable module's file as the file to launch. + /// Arguments are given in \a argv, and the environment variables + /// are in \a envp. Standard input and output files can be + /// optionally re-directed to \a stdin_path, \a stdout_path, and + /// \a stderr_path. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] launch_flags + /// Flags to modify the launch (@see lldb::LaunchFlags) + /// + /// @param[in] stdin_path + /// The path to use when re-directing the STDIN of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stdout_path + /// The path to use when re-directing the STDOUT of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stderr_path + /// The path to use when re-directing the STDERR of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// @param[in] launch_flags + /// Some launch options specified by logical OR'ing + /// lldb::LaunchFlags enumeration values together. + /// + /// @param[in] stop_at_endtry + /// If false do not stop the inferior at the entry point. + /// + /// @param[out] + /// An error object. Contains the reason if there is some failure. + /// + /// @return + /// A process object for the newly created process. + //------------------------------------------------------------------ + + For example, + + process = target.Launch(self.dbg.GetListener(), None, None, + None, '/tmp/stdout.txt', None, + None, 0, False, error) + + launches a new process by passing nothing for both the args and the envs + and redirect the standard output of the inferior to the /tmp/stdout.txt + file. It does not specify a working directory so that the debug server + will use its idea of what the current working directory is for the + inferior. Also, we ask the debugger not to stop the inferior at the + entry point. If no breakpoint is specified for the inferior, it should + run to completion if no user interaction is required. + ") Launch; + lldb::SBProcess + Launch (SBListener &listener, + char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags, // See LaunchFlags + bool stop_at_entry, + lldb::SBError& error); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Launch a new process with sensible defaults. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// Default: listener + /// Set to the target's debugger (SBTarget::GetDebugger()) + /// + /// Default: launch_flags + /// Empty launch flags + /// + /// Default: stdin_path + /// Default: stdout_path + /// Default: stderr_path + /// A pseudo terminal will be used. + /// + /// @return + /// A process object for the newly created process. + //------------------------------------------------------------------ + + For example, + + process = target.LaunchSimple(['X', 'Y', 'Z'], None, os.getcwd()) + + launches a new process by passing 'X', 'Y', 'Z' as the args to the + executable. + ") LaunchSimple; + lldb::SBProcess + LaunchSimple (const char **argv, + const char **envp, + const char *working_directory); + + lldb::SBProcess + Launch (lldb::SBLaunchInfo &launch_info, lldb::SBError& error); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Load a core file + /// + /// @param[in] core_file + /// File path of the core dump. + /// + /// @return + /// A process object for the newly created core file. + //------------------------------------------------------------------ + + For example, + + process = target.LoadCore('./a.out.core') + + loads a new core file and returns the process object. + ") LoadCore; + lldb::SBProcess + LoadCore(const char *core_file); + + lldb::SBProcess + Attach (lldb::SBAttachInfo &attach_info, lldb::SBError& error); + + + %feature("docstring", " + //------------------------------------------------------------------ + /// Attach to process with pid. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] pid + /// The process ID to attach to. + /// + /// @param[out] + /// An error explaining what went wrong if attach fails. + /// + /// @return + /// A process object for the attached process. + //------------------------------------------------------------------ + ") AttachToProcessWithID; + lldb::SBProcess + AttachToProcessWithID (SBListener &listener, + lldb::pid_t pid, + lldb::SBError& error); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Attach to process with name. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] name + /// Basename of process to attach to. + /// + /// @param[in] wait_for + /// If true wait for a new instance of 'name' to be launched. + /// + /// @param[out] + /// An error explaining what went wrong if attach fails. + /// + /// @return + /// A process object for the attached process. + //------------------------------------------------------------------ + ") AttachToProcessWithName; + lldb::SBProcess + AttachToProcessWithName (SBListener &listener, + const char *name, + bool wait_for, + lldb::SBError& error); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Connect to a remote debug server with url. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] url + /// The url to connect to, e.g., 'connect://localhost:12345'. + /// + /// @param[in] plugin_name + /// The plugin name to be used; can be NULL. + /// + /// @param[out] + /// An error explaining what went wrong if the connect fails. + /// + /// @return + /// A process object for the connected process. + //------------------------------------------------------------------ + ") ConnectRemote; + lldb::SBProcess + ConnectRemote (SBListener &listener, + const char *url, + const char *plugin_name, + SBError& error); + + lldb::SBFileSpec + GetExecutable (); + + bool + AddModule (lldb::SBModule &module); + + lldb::SBModule + AddModule (const char *path, + const char *triple, + const char *uuid); + + lldb::SBModule + AddModule (const char *path, + const char *triple, + const char *uuid_cstr, + const char *symfile); + + lldb::SBModule + AddModule (const SBModuleSpec &module_spec); + + uint32_t + GetNumModules () const; + + lldb::SBModule + GetModuleAtIndex (uint32_t idx); + + bool + RemoveModule (lldb::SBModule module); + + lldb::SBDebugger + GetDebugger() const; + + lldb::SBModule + FindModule (const lldb::SBFileSpec &file_spec); + + lldb::ByteOrder + GetByteOrder (); + + uint32_t + GetAddressByteSize(); + + const char * + GetTriple (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Architecture data byte width accessor + /// + /// @return + /// The size in 8-bit (host) bytes of a minimum addressable + /// unit from the Architecture's data bus + //------------------------------------------------------------------ + ") GetDataByteSize; + uint32_t + GetDataByteSize (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Architecture code byte width accessor + /// + /// @return + /// The size in 8-bit (host) bytes of a minimum addressable + /// unit from the Architecture's code bus + //------------------------------------------------------------------ + ") GetCodeByteSize; + uint32_t + GetCodeByteSize (); + + lldb::SBError + SetSectionLoadAddress (lldb::SBSection section, + lldb::addr_t section_base_addr); + + lldb::SBError + ClearSectionLoadAddress (lldb::SBSection section); + + lldb::SBError + SetModuleLoadAddress (lldb::SBModule module, + int64_t sections_offset); + + lldb::SBError + ClearModuleLoadAddress (lldb::SBModule module); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// @param[in] name + /// The name of the function we are looking for. + /// + /// @param[in] name_type_mask + /// A logical OR of one or more FunctionNameType enum bits that + /// indicate what kind of names should be used when doing the + /// lookup. Bits include fully qualified names, base names, + /// C++ methods, or ObjC selectors. + /// See FunctionNameType for more details. + /// + /// @return + /// A lldb::SBSymbolContextList that gets filled in with all of + /// the symbol contexts for all the matches. + //------------------------------------------------------------------ + ") FindFunctions; + lldb::SBSymbolContextList + FindFunctions (const char *name, + uint32_t name_type_mask = lldb::eFunctionNameTypeAny); + + lldb::SBType + FindFirstType (const char* type); + + lldb::SBTypeList + FindTypes (const char* type); + + lldb::SBType + GetBasicType(lldb::BasicType type); + + lldb::SBSourceManager + GetSourceManager (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a max_matches. + /// + /// @return + /// A list of matched variables in an SBValueList. + //------------------------------------------------------------------ + ") FindGlobalVariables; + lldb::SBValueList + FindGlobalVariables (const char *name, + uint32_t max_matches); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Find the first global (or static) variable by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @return + /// An SBValue that gets filled in with the found variable (if any). + //------------------------------------------------------------------ + ") FindFirstGlobalVariable; + lldb::SBValue + FindFirstGlobalVariable (const char* name); + + + lldb::SBValueList + FindGlobalVariables(const char *name, + uint32_t max_matches, + MatchType matchtype); + + lldb::SBSymbolContextList + FindGlobalFunctions(const char *name, + uint32_t max_matches, + MatchType matchtype); + + void + Clear (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Resolve a current file address into a section offset address. + /// + /// @param[in] file_addr + /// + /// @return + /// An SBAddress which will be valid if... + //------------------------------------------------------------------ + ") ResolveFileAddress; + lldb::SBAddress + ResolveFileAddress (lldb::addr_t file_addr); + + lldb::SBAddress + ResolveLoadAddress (lldb::addr_t vm_addr); + + lldb::SBAddress + ResolvePastLoadAddress (uint32_t stop_id, lldb::addr_t vm_addr); + + SBSymbolContext + ResolveSymbolContextForAddress (const SBAddress& addr, + uint32_t resolve_scope); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Read target memory. If a target process is running then memory + /// is read from here. Otherwise the memory is read from the object + /// files. For a target whose bytes are sized as a multiple of host + /// bytes, the data read back will preserve the target's byte order. + /// + /// @param[in] addr + /// A target address to read from. + /// + /// @param[out] buf + /// The buffer to read memory into. + /// + /// @param[in] size + /// The maximum number of host bytes to read in the buffer passed + /// into this call + /// + /// @param[out] error + /// Error information is written here if the memory read fails. + /// + /// @return + /// The amount of data read in host bytes. + //------------------------------------------------------------------ + ") ReadMemory; + size_t + ReadMemory (const SBAddress addr, void *buf, size_t size, lldb::SBError &error); + + 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 + BreakpointCreateByName (const char *symbol_name, + uint32_t func_name_type, // Logical OR one or more FunctionNameType enum bits + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByName (const char *symbol_name, + uint32_t func_name_type, // Logical OR one or more FunctionNameType enum bits + lldb::LanguageType symbol_language, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByNames (const char *symbol_name[], + uint32_t num_names, + uint32_t name_type_mask, // Logical OR one or more FunctionNameType enum bits + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByNames (const char *symbol_name[], + uint32_t num_names, + uint32_t name_type_mask, // Logical OR one or more FunctionNameType enum bits + lldb::LanguageType symbol_language, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByRegex (const char *symbol_name_regex, const char *module_name = NULL); + + lldb::SBBreakpoint + BreakpointCreateByRegex (const char *symbol_name_regex, + lldb::LanguageType symbol_language, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateBySourceRegex (const char *source_regex, const lldb::SBFileSpec &source_file, const char *module_name = NULL); + + lldb::SBBreakpoint + BreakpointCreateBySourceRegex (const char *source_regex, const lldb::SBFileSpecList &module_list, const lldb::SBFileSpecList &file_list); + + lldb::SBBreakpoint + BreakpointCreateForException (lldb::LanguageType language, + bool catch_bp, + bool throw_bp); + + lldb::SBBreakpoint + BreakpointCreateByAddress (addr_t address); + + lldb::SBBreakpoint + BreakpointCreateBySBAddress (SBAddress &sb_address); + + uint32_t + GetNumBreakpoints () const; + + lldb::SBBreakpoint + GetBreakpointAtIndex (uint32_t idx) const; + + bool + BreakpointDelete (break_id_t break_id); + + lldb::SBBreakpoint + FindBreakpointByID (break_id_t break_id); + + bool + EnableAllBreakpoints (); + + bool + DisableAllBreakpoints (); + + bool + DeleteAllBreakpoints (); + + uint32_t + GetNumWatchpoints () const; + + lldb::SBWatchpoint + GetWatchpointAtIndex (uint32_t idx) const; + + bool + DeleteWatchpoint (lldb::watch_id_t watch_id); + + lldb::SBWatchpoint + FindWatchpointByID (lldb::watch_id_t watch_id); + + bool + EnableAllWatchpoints (); + + bool + DisableAllWatchpoints (); + + bool + DeleteAllWatchpoints (); + + lldb::SBWatchpoint + WatchAddress (lldb::addr_t addr, + size_t size, + bool read, + bool write, + SBError &error); + + + lldb::SBBroadcaster + GetBroadcaster () const; + + %feature("docstring", " + //------------------------------------------------------------------ + /// Create an SBValue with the given name by treating the memory starting at addr as an entity of type. + /// + /// @param[in] name + /// The name of the resultant SBValue + /// + /// @param[in] addr + /// The address of the start of the memory region to be used. + /// + /// @param[in] type + /// The type to use to interpret the memory starting at addr. + /// + /// @return + /// An SBValue of the given type, may be invalid if there was an error reading + /// the underlying memory. + //------------------------------------------------------------------ + ") CreateValueFromAddress; + lldb::SBValue + CreateValueFromAddress (const char *name, lldb::SBAddress addr, lldb::SBType type); + + lldb::SBValue + CreateValueFromData (const char *name, lldb::SBData data, lldb::SBType type); + + lldb::SBValue + CreateValueFromExpression (const char *name, const char* expr); + + %feature("docstring", " + Disassemble a specified number of instructions starting at an address. + Parameters: + base_addr -- the address to start disassembly from + count -- the number of instructions to disassemble + flavor_string -- may be 'intel' or 'att' on x86 targets to specify that style of disassembly + Returns an SBInstructionList.") + ReadInstructions; + lldb::SBInstructionList + ReadInstructions (lldb::SBAddress base_addr, uint32_t count); + + lldb::SBInstructionList + ReadInstructions (lldb::SBAddress base_addr, uint32_t count, const char *flavor_string); + + %feature("docstring", " + Disassemble the bytes in a buffer and return them in an SBInstructionList. + Parameters: + base_addr -- used for symbolicating the offsets in the byte stream when disassembling + buf -- bytes to be disassembled + size -- (C++) size of the buffer + Returns an SBInstructionList.") + GetInstructions; + lldb::SBInstructionList + GetInstructions (lldb::SBAddress base_addr, const void *buf, size_t size); + + %feature("docstring", " + Disassemble the bytes in a buffer and return them in an SBInstructionList, with a supplied flavor. + Parameters: + base_addr -- used for symbolicating the offsets in the byte stream when disassembling + flavor -- may be 'intel' or 'att' on x86 targets to specify that style of disassembly + buf -- bytes to be disassembled + size -- (C++) size of the buffer + Returns an SBInstructionList.") + GetInstructionsWithFlavor; + lldb::SBInstructionList + GetInstructionsWithFlavor (lldb::SBAddress base_addr, const char *flavor_string, const void *buf, size_t size); + + lldb::SBSymbolContextList + FindSymbols (const char *name, lldb::SymbolType type = eSymbolTypeAny); + + bool + GetDescription (lldb::SBStream &description, lldb::DescriptionLevel description_level); + + lldb::addr_t + GetStackRedZoneSize(); + + lldb::SBLaunchInfo + GetLaunchInfo () const; + + void + SetLaunchInfo (const lldb::SBLaunchInfo &launch_info); + + bool + operator == (const lldb::SBTarget &rhs) const; + + bool + operator != (const lldb::SBTarget &rhs) const; + + lldb::SBValue + EvaluateExpression (const char *expr); + + lldb::SBValue + EvaluateExpression (const char *expr, const lldb::SBExpressionOptions &options); + + %pythoncode %{ + class modules_access(object): + '''A helper object that will lazily hand out lldb.SBModule objects for a target when supplied an index, or by full or partial path.''' + def __init__(self, sbtarget): + self.sbtarget = sbtarget + + def __len__(self): + if self.sbtarget: + return int(self.sbtarget.GetNumModules()) + return 0 + + def __getitem__(self, key): + num_modules = self.sbtarget.GetNumModules() + if type(key) is int: + if key < num_modules: + return self.sbtarget.GetModuleAtIndex(key) + elif type(key) is str: + if key.find('/') == -1: + for idx in range(num_modules): + module = self.sbtarget.GetModuleAtIndex(idx) + if module.file.basename == key: + return module + else: + for idx in range(num_modules): + module = self.sbtarget.GetModuleAtIndex(idx) + if module.file.fullpath == key: + return module + # See if the string is a UUID + try: + the_uuid = uuid.UUID(key) + if the_uuid: + for idx in range(num_modules): + module = self.sbtarget.GetModuleAtIndex(idx) + if module.uuid == the_uuid: + return module + except: + return None + elif type(key) is uuid.UUID: + for idx in range(num_modules): + module = self.sbtarget.GetModuleAtIndex(idx) + if module.uuid == key: + return module + elif type(key) is re.SRE_Pattern: + matching_modules = [] + for idx in range(num_modules): + module = self.sbtarget.GetModuleAtIndex(idx) + re_match = key.search(module.path.fullpath) + if re_match: + matching_modules.append(module) + return matching_modules + else: + print("error: unsupported item type: %s" % type(key)) + return None + + def get_modules_access_object(self): + '''An accessor function that returns a modules_access() object which allows lazy module access from a lldb.SBTarget object.''' + return self.modules_access (self) + + def get_modules_array(self): + '''An accessor function that returns a list() that contains all modules in a lldb.SBTarget object.''' + modules = [] + for idx in range(self.GetNumModules()): + modules.append(self.GetModuleAtIndex(idx)) + return modules + + __swig_getmethods__["modules"] = get_modules_array + if _newclass: modules = property(get_modules_array, None, doc='''A read only property that returns a list() of lldb.SBModule objects contained in this target. This list is a list all modules that the target currently is tracking (the main executable and all dependent shared libraries).''') + + __swig_getmethods__["module"] = get_modules_access_object + if _newclass: module = property(get_modules_access_object, None, doc=r'''A read only property that returns an object that implements python operator overloading with the square brackets().\n target.module[] allows array access to any modules.\n target.module[] allows access to modules by basename, full path, or uuid string value.\n target.module[uuid.UUID()] allows module access by UUID.\n target.module[re] allows module access using a regular expression that matches the module full path.''') + + __swig_getmethods__["process"] = GetProcess + if _newclass: process = property(GetProcess, None, doc='''A read only property that returns an lldb object that represents the process (lldb.SBProcess) that this target owns.''') + + __swig_getmethods__["executable"] = GetExecutable + if _newclass: executable = property(GetExecutable, None, doc='''A read only property that returns an lldb object that represents the main executable module (lldb.SBModule) for this target.''') + + __swig_getmethods__["debugger"] = GetDebugger + if _newclass: debugger = property(GetDebugger, None, doc='''A read only property that returns an lldb object that represents the debugger (lldb.SBDebugger) that owns this target.''') + + __swig_getmethods__["num_breakpoints"] = GetNumBreakpoints + if _newclass: num_breakpoints = property(GetNumBreakpoints, None, doc='''A read only property that returns the number of breakpoints that this target has as an integer.''') + + __swig_getmethods__["num_watchpoints"] = GetNumWatchpoints + if _newclass: num_watchpoints = property(GetNumWatchpoints, None, doc='''A read only property that returns the number of watchpoints that this target has as an integer.''') + + __swig_getmethods__["broadcaster"] = GetBroadcaster + if _newclass: broadcaster = property(GetBroadcaster, None, doc='''A read only property that an lldb object that represents the broadcaster (lldb.SBBroadcaster) for this target.''') + + __swig_getmethods__["byte_order"] = GetByteOrder + if _newclass: byte_order = property(GetByteOrder, None, doc='''A read only property that returns an lldb enumeration value (lldb.eByteOrderLittle, lldb.eByteOrderBig, lldb.eByteOrderInvalid) that represents the byte order for this target.''') + + __swig_getmethods__["addr_size"] = GetAddressByteSize + if _newclass: addr_size = property(GetAddressByteSize, None, doc='''A read only property that returns the size in bytes of an address for this target.''') + + __swig_getmethods__["triple"] = GetTriple + if _newclass: triple = property(GetTriple, None, doc='''A read only property that returns the target triple (arch-vendor-os) for this target as a string.''') + + __swig_getmethods__["data_byte_size"] = GetDataByteSize + if _newclass: data_byte_size = property(GetDataByteSize, None, doc='''A read only property that returns the size in host bytes of a byte in the data address space for this target.''') + + __swig_getmethods__["code_byte_size"] = GetCodeByteSize + if _newclass: code_byte_size = property(GetCodeByteSize, None, doc='''A read only property that returns the size in host bytes of a byte in the code address space for this target.''') + + __swig_getmethods__["platform"] = GetPlatform + if _newclass: platform = property(GetPlatform, None, doc='''A read only property that returns the platform associated with with this target.''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBThread.i b/scripts/interface/SBThread.i new file mode 100644 index 000000000000..f2b27565d489 --- /dev/null +++ b/scripts/interface/SBThread.i @@ -0,0 +1,408 @@ +//===-- SWIG Interface for SBThread -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a thread of execution. SBProcess contains SBThread(s). + +SBThreads can be referred to by their ID, which maps to the system specific thread +identifier, or by IndexID. The ID may or may not be unique depending on whether the +system reuses its thread identifiers. The IndexID is a monotonically increasing identifier +that will always uniquely reference a particular thread, and when that thread goes +away it will not be reused. + +SBThread supports frame iteration. For example (from test/python_api/ +lldbutil/iter/TestLLDBIterator.py), + + from lldbutil import print_stacktrace + stopped_due_to_breakpoint = False + for thread in process: + if self.TraceOn(): + print_stacktrace(thread) + ID = thread.GetThreadID() + if thread.GetStopReason() == lldb.eStopReasonBreakpoint: + stopped_due_to_breakpoint = True + for frame in thread: + self.assertTrue(frame.GetThread().GetThreadID() == ID) + if self.TraceOn(): + print frame + + self.assertTrue(stopped_due_to_breakpoint) + +See also SBProcess and SBFrame." +) SBThread; +class SBThread +{ +public: + //------------------------------------------------------------------ + // Broadcaster bits. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStackChanged = (1 << 0), + eBroadcastBitThreadSuspended = (1 << 1), + eBroadcastBitThreadResumed = (1 << 2), + eBroadcastBitSelectedFrameChanged = (1 << 3), + eBroadcastBitThreadSelected = (1 << 4) + }; + + + SBThread (); + + SBThread (const lldb::SBThread &thread); + + ~SBThread(); + + static const char * + GetBroadcasterClassName (); + + static bool + EventIsThreadEvent (const SBEvent &event); + + static SBFrame + GetStackFrameFromEvent (const SBEvent &event); + + static SBThread + GetThreadFromEvent (const SBEvent &event); + + bool + IsValid() const; + + void + Clear (); + + lldb::StopReason + GetStopReason(); + + %feature("docstring", " + /// Get the number of words associated with the stop reason. + /// See also GetStopReasonDataAtIndex(). + ") GetStopReasonDataCount; + size_t + GetStopReasonDataCount(); + + %feature("docstring", " + //-------------------------------------------------------------------------- + /// Get information associated with a stop reason. + /// + /// Breakpoint stop reasons will have data that consists of pairs of + /// breakpoint IDs followed by the breakpoint location IDs (they always come + /// in pairs). + /// + /// Stop Reason Count Data Type + /// ======================== ===== ========================================= + /// eStopReasonNone 0 + /// eStopReasonTrace 0 + /// eStopReasonBreakpoint N duple: {breakpoint id, location id} + /// eStopReasonWatchpoint 1 watchpoint id + /// eStopReasonSignal 1 unix signal number + /// eStopReasonException N exception data + /// eStopReasonExec 0 + /// eStopReasonPlanComplete 0 + //-------------------------------------------------------------------------- + ") GetStopReasonDataAtIndex; + uint64_t + GetStopReasonDataAtIndex(uint32_t idx); + + %feature("autodoc", " + Collects a thread's stop reason extended information dictionary and prints it + into the SBStream in a JSON format. The format of this JSON dictionary depends + on the stop reason and is currently used only for instrumentation plugins. + ") GetStopReasonExtendedInfoAsJSON; + bool + GetStopReasonExtendedInfoAsJSON (lldb::SBStream &stream); + + %feature("autodoc", " + Pass only an (int)length and expect to get a Python string describing the + stop reason. + ") GetStopDescription; + size_t + GetStopDescription (char *dst, size_t dst_len); + + SBValue + GetStopReturnValue (); + + %feature("autodoc", " + Returns a unique thread identifier (type lldb::tid_t, typically a 64-bit type) + for the current SBThread that will remain constant throughout the thread's + lifetime in this process and will not be reused by another thread during this + process lifetime. On Mac OS X systems, this is a system-wide unique thread + identifier; this identifier is also used by other tools like sample which helps + to associate data from those tools with lldb. See related GetIndexID. + ") + GetThreadID; + lldb::tid_t + GetThreadID () const; + + %feature("autodoc", " + Return the index number for this SBThread. The index number is the same thing + that a user gives as an argument to 'thread select' in the command line lldb. + These numbers start at 1 (for the first thread lldb sees in a debug session) + and increments up throughout the process lifetime. An index number will not be + reused for a different thread later in a process - thread 1 will always be + associated with the same thread. See related GetThreadID. + This method returns a uint32_t index number, takes no arguments. + ") + GetIndexID; + uint32_t + GetIndexID () const; + + const char * + GetName () const; + + %feature("autodoc", " + Return the queue name associated with this thread, if any, as a str. + For example, with a libdispatch (aka Grand Central Dispatch) queue. + ") GetQueueName; + + const char * + GetQueueName() const; + + %feature("autodoc", " + Return the dispatch_queue_id for this thread, if any, as a lldb::queue_id_t. + For example, with a libdispatch (aka Grand Central Dispatch) queue. + ") GetQueueID; + + lldb::queue_id_t + GetQueueID() const; + + %feature("autodoc", " + Takes a path string and a SBStream reference as parameters, returns a bool. + Collects the thread's 'info' dictionary from the remote system, uses the path + argument to descend into the dictionary to an item of interest, and prints + it into the SBStream in a natural format. Return bool is to indicate if + anything was printed into the stream (true) or not (false). + ") GetInfoItemByPathAsString; + + bool + GetInfoItemByPathAsString (const char *path, lldb::SBStream &strm); + + %feature("autodoc", " + Return the SBQueue for this thread. If this thread is not currently associated + with a libdispatch queue, the SBQueue object's IsValid() method will return false. + If this SBThread is actually a HistoryThread, we may be able to provide QueueID + and QueueName, but not provide an SBQueue. Those individual attributes may have + been saved for the HistoryThread without enough information to reconstitute the + entire SBQueue at that time. + This method takes no arguments, returns an SBQueue. + ") GetQueue; + + lldb::SBQueue + GetQueue () const; + + void + StepOver (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepInto (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepInto (const char *target_name, lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepOut (); + + void + StepOutOfFrame (lldb::SBFrame &frame); + + void + StepInstruction(bool step_over); + + SBError + StepOverUntil (lldb::SBFrame &frame, + lldb::SBFileSpec &file_spec, + uint32_t line); + + SBError + StepUsingScriptedThreadPlan (const char *script_class_name); + + SBError + JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line); + + void + RunToAddress (lldb::addr_t addr); + + %feature("autodoc", " + Force a return from the frame passed in (and any frames younger than it) + without executing any more code in those frames. If return_value contains + a valid SBValue, that will be set as the return value from frame. Note, at + present only scalar return values are supported. + ") ReturnFromFrame; + + SBError + ReturnFromFrame (SBFrame &frame, SBValue &return_value); + + %feature("docstring", " + //-------------------------------------------------------------------------- + /// LLDB currently supports process centric debugging which means when any + /// thread in a process stops, all other threads are stopped. The Suspend() + /// call here tells our process to suspend a thread and not let it run when + /// the other threads in a process are allowed to run. So when + /// SBProcess::Continue() is called, any threads that aren't suspended will + /// be allowed to run. If any of the SBThread functions for stepping are + /// called (StepOver, StepInto, StepOut, StepInstruction, RunToAddres), the + /// thread will now be allowed to run and these functions will simply return. + /// + /// Eventually we plan to add support for thread centric debugging where + /// each thread is controlled individually and each thread would broadcast + /// its state, but we haven't implemented this yet. + /// + /// Likewise the SBThread::Resume() call will again allow the thread to run + /// when the process is continued. + /// + /// Suspend() and Resume() functions are not currently reference counted, if + /// anyone has the need for them to be reference counted, please let us + /// know. + //-------------------------------------------------------------------------- + ") Suspend; + bool + Suspend(); + + bool + Resume (); + + bool + IsSuspended(); + + bool + IsStopped(); + + uint32_t + GetNumFrames (); + + lldb::SBFrame + GetFrameAtIndex (uint32_t idx); + + lldb::SBFrame + GetSelectedFrame (); + + lldb::SBFrame + SetSelectedFrame (uint32_t frame_idx); + + lldb::SBProcess + GetProcess (); + + bool + GetDescription (lldb::SBStream &description) const; + + bool + GetStatus (lldb::SBStream &status) const; + + bool + operator == (const lldb::SBThread &rhs) const; + + bool + operator != (const lldb::SBThread &rhs) const; + + %feature("autodoc"," + Given an argument of str to specify the type of thread-origin extended + backtrace to retrieve, query whether the origin of this thread is + available. An SBThread is retured; SBThread.IsValid will return true + if an extended backtrace was available. The returned SBThread is not + a part of the SBProcess' thread list and it cannot be manipulated like + normal threads -- you cannot step or resume it, for instance -- it is + intended to used primarily for generating a backtrace. You may request + the returned thread's own thread origin in turn. + ") GetExtendedBacktraceThread; + lldb::SBThread + GetExtendedBacktraceThread (const char *type); + + %feature("autodoc"," + Takes no arguments, returns a uint32_t. + If this SBThread is an ExtendedBacktrace thread, get the IndexID of the + original thread that this ExtendedBacktrace thread represents, if + available. The thread that was running this backtrace in the past may + not have been registered with lldb's thread index (if it was created, + did its work, and was destroyed without lldb ever stopping execution). + In that case, this ExtendedBacktrace thread's IndexID will be returned. + ") GetExtendedBacktraceOriginatingIndexID; + uint32_t + GetExtendedBacktraceOriginatingIndexID(); + + %feature("autodoc"," + Takes no arguments, returns a bool. + lldb may be able to detect that function calls should not be executed + on a given thread at a particular point in time. It is recommended that + this is checked before performing an inferior function call on a given + thread. + ") SafeToCallFunctions; + bool + SafeToCallFunctions (); + + %pythoncode %{ + class frames_access(object): + '''A helper object that will lazily hand out frames for a thread when supplied an index.''' + def __init__(self, sbthread): + self.sbthread = sbthread + + def __len__(self): + if self.sbthread: + return int(self.sbthread.GetNumFrames()) + return 0 + + def __getitem__(self, key): + if type(key) is int and key < self.sbthread.GetNumFrames(): + return self.sbthread.GetFrameAtIndex(key) + return None + + def get_frames_access_object(self): + '''An accessor function that returns a frames_access() object which allows lazy frame access from a lldb.SBThread object.''' + return self.frames_access (self) + + def get_thread_frames(self): + '''An accessor function that returns a list() that contains all frames in a lldb.SBThread object.''' + frames = [] + for frame in self: + frames.append(frame) + return frames + + __swig_getmethods__["id"] = GetThreadID + if _newclass: id = property(GetThreadID, None, doc='''A read only property that returns the thread ID as an integer.''') + + __swig_getmethods__["idx"] = GetIndexID + if _newclass: idx = property(GetIndexID, None, doc='''A read only property that returns the thread index ID as an integer. Thread index ID values start at 1 and increment as threads come and go and can be used to uniquely identify threads.''') + + __swig_getmethods__["return_value"] = GetStopReturnValue + if _newclass: return_value = property(GetStopReturnValue, None, doc='''A read only property that returns an lldb object that represents the return value from the last stop (lldb.SBValue) if we just stopped due to stepping out of a function.''') + + __swig_getmethods__["process"] = GetProcess + if _newclass: process = property(GetProcess, None, doc='''A read only property that returns an lldb object that represents the process (lldb.SBProcess) that owns this thread.''') + + __swig_getmethods__["num_frames"] = GetNumFrames + if _newclass: num_frames = property(GetNumFrames, None, doc='''A read only property that returns the number of stack frames in this thread as an integer.''') + + __swig_getmethods__["frames"] = get_thread_frames + if _newclass: frames = property(get_thread_frames, None, doc='''A read only property that returns a list() of lldb.SBFrame objects for all frames in this thread.''') + + __swig_getmethods__["frame"] = get_frames_access_object + if _newclass: frame = property(get_frames_access_object, None, doc='''A read only property that returns an object that can be used to access frames as an array ("frame_12 = lldb.thread.frame[12]").''') + + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None, doc='''A read only property that returns the name of this thread as a string.''') + + __swig_getmethods__["queue"] = GetQueueName + if _newclass: queue = property(GetQueueName, None, doc='''A read only property that returns the dispatch queue name of this thread as a string.''') + + __swig_getmethods__["queue_id"] = GetQueueID + if _newclass: queue_id = property(GetQueueID, None, doc='''A read only property that returns the dispatch queue id of this thread as an integer.''') + + __swig_getmethods__["stop_reason"] = GetStopReason + if _newclass: stop_reason = property(GetStopReason, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eStopReason") that represents the reason this thread stopped.''') + + __swig_getmethods__["is_suspended"] = IsSuspended + if _newclass: is_suspended = property(IsSuspended, None, doc='''A read only property that returns a boolean value that indicates if this thread is suspended.''') + + __swig_getmethods__["is_stopped"] = IsStopped + if _newclass: is_stopped = property(IsStopped, None, doc='''A read only property that returns a boolean value that indicates if this thread is stopped but not exited.''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBThreadCollection.i b/scripts/interface/SBThreadCollection.i new file mode 100644 index 000000000000..824f6923acce --- /dev/null +++ b/scripts/interface/SBThreadCollection.i @@ -0,0 +1,38 @@ +//===-- SWIG Interface for SBThreadCollection -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +namespace lldb { + +%feature("docstring", +"Represents a collection of SBThread objects." +) SBThreadCollection; +class SBThreadCollection +{ +public: + + SBThreadCollection (); + + SBThreadCollection (const SBThreadCollection &rhs); + + ~SBThreadCollection (); + + bool + IsValid () const; + + size_t + GetSize (); + + lldb::SBThread + GetThreadAtIndex (size_t idx); + +}; + +} // namespace lldb diff --git a/scripts/interface/SBThreadPlan.i b/scripts/interface/SBThreadPlan.i new file mode 100644 index 000000000000..785855ec5b94 --- /dev/null +++ b/scripts/interface/SBThreadPlan.i @@ -0,0 +1,123 @@ +//===-- 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_SBThreadPlan_h_ +#define LLDB_SBThreadPlan_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + +%feature("docstring", +"Represents a plan for the execution control of a given thread. + +See also SBThread and SBFrame." +) SBThread; + +class SBThreadPlan +{ + +friend class lldb_private::ThreadPlan; + +public: + SBThreadPlan (); + + SBThreadPlan (const lldb::SBThreadPlan &threadPlan); + + SBThreadPlan (const lldb::ThreadPlanSP& lldb_object_sp); + + SBThreadPlan (lldb::SBThread &thread, const char *class_name); + + ~SBThreadPlan (); + + bool + IsValid() const; + + void + Clear (); + + lldb::StopReason + GetStopReason(); + + /// Get the number of words associated with the stop reason. + /// See also GetStopReasonDataAtIndex(). + size_t + GetStopReasonDataCount(); + + //-------------------------------------------------------------------------- + /// Get information associated with a stop reason. + /// + /// Breakpoint stop reasons will have data that consists of pairs of + /// breakpoint IDs followed by the breakpoint location IDs (they always come + /// in pairs). + /// + /// Stop Reason Count Data Type + /// ======================== ===== ========================================= + /// eStopReasonNone 0 + /// eStopReasonTrace 0 + /// eStopReasonBreakpoint N duple: {breakpoint id, location id} + /// eStopReasonWatchpoint 1 watchpoint id + /// eStopReasonSignal 1 unix signal number + /// eStopReasonException N exception data + /// eStopReasonExec 0 + /// eStopReasonPlanComplete 0 + //-------------------------------------------------------------------------- + uint64_t + GetStopReasonDataAtIndex(uint32_t idx); + + SBThread + GetThread () const; + + bool + GetDescription (lldb::SBStream &description) const; + + void + SetPlanComplete (bool success); + + bool + IsPlanComplete(); + + bool + IsValid(); + + // This section allows an SBThreadPlan to push another of the common types of plans... + SBThreadPlan + QueueThreadPlanForStepOverRange (SBAddress &start_address, + lldb::addr_t range_size); + + SBThreadPlan + QueueThreadPlanForStepInRange (SBAddress &start_address, + lldb::addr_t range_size); + + SBThreadPlan + QueueThreadPlanForStepOut (uint32_t frame_idx_to_step_to, bool first_insn = false); + + SBThreadPlan + QueueThreadPlanForRunToAddress (SBAddress address); + + +protected: + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBFrame; + friend class SBProcess; + friend class SBDebugger; + friend class SBValue; + friend class lldb_private::QueueImpl; + friend class SBQueueItem; + +private: + lldb::ThreadPlanSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBThreadPlan_h_ diff --git a/scripts/interface/SBType.i b/scripts/interface/SBType.i new file mode 100644 index 000000000000..76bd3f0352c0 --- /dev/null +++ b/scripts/interface/SBType.i @@ -0,0 +1,500 @@ +//===-- SWIG Interface for SBType -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + %feature("docstring", +"Represents a member of a type in lldb. +") SBTypeMember; + +class SBTypeMember +{ +public: + SBTypeMember (); + + SBTypeMember (const lldb::SBTypeMember& rhs); + + ~SBTypeMember(); + + bool + IsValid() const; + + const char * + GetName (); + + lldb::SBType + GetType (); + + uint64_t + GetOffsetInBytes(); + + uint64_t + GetOffsetInBits(); + + bool + IsBitfield(); + + uint32_t + GetBitfieldSizeInBits(); + + %pythoncode %{ + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None, doc='''A read only property that returns the name for this member as a string.''') + + __swig_getmethods__["type"] = GetType + if _newclass: type = property(GetType, None, doc='''A read only property that returns an lldb object that represents the type (lldb.SBType) for this member.''') + + __swig_getmethods__["byte_offset"] = GetOffsetInBytes + if _newclass: byte_offset = property(GetOffsetInBytes, None, doc='''A read only property that returns offset in bytes for this member as an integer.''') + + __swig_getmethods__["bit_offset"] = GetOffsetInBits + if _newclass: bit_offset = property(GetOffsetInBits, None, doc='''A read only property that returns offset in bits for this member as an integer.''') + + __swig_getmethods__["is_bitfield"] = IsBitfield + if _newclass: is_bitfield = property(IsBitfield, None, doc='''A read only property that returns true if this member is a bitfield.''') + + __swig_getmethods__["bitfield_bit_size"] = GetBitfieldSizeInBits + if _newclass: bitfield_bit_size = property(GetBitfieldSizeInBits, None, doc='''A read only property that returns the bitfield size in bits for this member as an integer, or zero if this member is not a bitfield.''') + + %} + +protected: + std::unique_ptr m_opaque_ap; +}; + +class SBTypeMemberFunction +{ +public: + SBTypeMemberFunction (); + + SBTypeMemberFunction (const lldb::SBTypeMemberFunction& rhs); + + ~SBTypeMemberFunction(); + + bool + IsValid() const; + + const char * + GetName (); + + const char * + GetDemangledName (); + + const char * + GetMangledName (); + + lldb::SBType + GetType (); + + lldb::SBType + GetReturnType (); + + uint32_t + GetNumberOfArguments (); + + lldb::SBType + GetArgumentTypeAtIndex (uint32_t); + + lldb::MemberFunctionKind + GetKind(); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + +protected: + lldb::TypeMemberFunctionImplSP m_opaque_sp; +}; + +%feature("docstring", +"Represents a data type in lldb. The FindFirstType() method of SBTarget/SBModule +returns a SBType. + +SBType supports the eq/ne operator. For example, + +main.cpp: + +class Task { +public: + int id; + Task *next; + Task(int i, Task *n): + id(i), + next(n) + {} +}; + +int main (int argc, char const *argv[]) +{ + Task *task_head = new Task(-1, NULL); + Task *task1 = new Task(1, NULL); + Task *task2 = new Task(2, NULL); + Task *task3 = new Task(3, NULL); // Orphaned. + Task *task4 = new Task(4, NULL); + Task *task5 = new Task(5, NULL); + + task_head->next = task1; + task1->next = task2; + task2->next = task4; + task4->next = task5; + + int total = 0; + Task *t = task_head; + while (t != NULL) { + if (t->id >= 0) + ++total; + t = t->next; + } + printf('We have a total number of %d tasks\\n', total); + + // This corresponds to an empty task list. + Task *empty_task_head = new Task(-1, NULL); + + return 0; // Break at this line +} + +find_type.py: + + # Get the type 'Task'. + task_type = target.FindFirstType('Task') + self.assertTrue(task_type) + + # Get the variable 'task_head'. + frame0.FindVariable('task_head') + task_head_type = task_head.GetType() + self.assertTrue(task_head_type.IsPointerType()) + + # task_head_type is 'Task *'. + task_pointer_type = task_type.GetPointerType() + self.assertTrue(task_head_type == task_pointer_type) + + # Get the child mmember 'id' from 'task_head'. + id = task_head.GetChildMemberWithName('id') + id_type = id.GetType() + + # SBType.GetBasicType() takes an enum 'BasicType' (lldb-enumerations.h). + int_type = id_type.GetBasicType(lldb.eBasicTypeInt) + # id_type and int_type should be the same type! + self.assertTrue(id_type == int_type) + +... +") SBType; +class SBType +{ +public: + SBType (); + + SBType (const lldb::SBType &rhs); + + ~SBType (); + + bool + IsValid(); + + uint64_t + GetByteSize(); + + bool + IsPointerType(); + + bool + IsReferenceType(); + + bool + IsFunctionType (); + + bool + IsPolymorphicClass (); + + bool + IsArrayType (); + + bool + IsVectorType (); + + bool + IsTypedefType (); + + bool + IsAnonymousType (); + + lldb::SBType + GetPointerType(); + + lldb::SBType + GetPointeeType(); + + lldb::SBType + GetReferenceType(); + + lldb::SBType + SBType::GetTypedefedType(); + + lldb::SBType + GetDereferencedType(); + + lldb::SBType + GetUnqualifiedType(); + + lldb::SBType + GetCanonicalType(); + + lldb::SBType + GetArrayElementType (); + + lldb::SBType + GetVectorElementType (); + + lldb::BasicType + GetBasicType(); + + lldb::SBType + GetBasicType (lldb::BasicType type); + + uint32_t + GetNumberOfFields (); + + uint32_t + GetNumberOfDirectBaseClasses (); + + uint32_t + GetNumberOfVirtualBaseClasses (); + + lldb::SBTypeMember + GetFieldAtIndex (uint32_t idx); + + lldb::SBTypeMember + GetDirectBaseClassAtIndex (uint32_t idx); + + lldb::SBTypeMember + GetVirtualBaseClassAtIndex (uint32_t idx); + + lldb::SBTypeEnumMemberList + GetEnumMembers(); + + const char* + GetName(); + + const char * + GetDisplayTypeName (); + + lldb::TypeClass + GetTypeClass (); + + uint32_t + GetNumberOfTemplateArguments (); + + lldb::SBType + GetTemplateArgumentType (uint32_t idx); + + lldb::TemplateArgumentKind + GetTemplateArgumentKind (uint32_t idx); + + lldb::SBType + GetFunctionReturnType (); + + lldb::SBTypeList + GetFunctionArgumentTypes (); + + uint32_t + GetNumberOfMemberFunctions (); + + lldb::SBTypeMemberFunction + GetMemberFunctionAtIndex (uint32_t idx); + + bool + IsTypeComplete (); + + uint32_t + GetTypeFlags (); + + %pythoncode %{ + def template_arg_array(self): + num_args = self.num_template_args + if num_args: + template_args = [] + for i in range(num_args): + template_args.append(self.GetTemplateArgumentType(i)) + return template_args + return None + + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None, doc='''A read only property that returns the name for this type as a string.''') + + __swig_getmethods__["size"] = GetByteSize + if _newclass: size = property(GetByteSize, None, doc='''A read only property that returns size in bytes for this type as an integer.''') + + __swig_getmethods__["is_pointer"] = IsPointerType + if _newclass: is_pointer = property(IsPointerType, None, doc='''A read only property that returns a boolean value that indicates if this type is a pointer type.''') + + __swig_getmethods__["is_reference"] = IsReferenceType + if _newclass: is_reference = property(IsReferenceType, None, doc='''A read only property that returns a boolean value that indicates if this type is a reference type.''') + + __swig_getmethods__["is_function"] = IsFunctionType + if _newclass: is_reference = property(IsReferenceType, None, doc='''A read only property that returns a boolean value that indicates if this type is a function type.''') + + __swig_getmethods__["num_fields"] = GetNumberOfFields + if _newclass: num_fields = property(GetNumberOfFields, None, doc='''A read only property that returns number of fields in this type as an integer.''') + + __swig_getmethods__["num_bases"] = GetNumberOfDirectBaseClasses + if _newclass: num_bases = property(GetNumberOfDirectBaseClasses, None, doc='''A read only property that returns number of direct base classes in this type as an integer.''') + + __swig_getmethods__["num_vbases"] = GetNumberOfVirtualBaseClasses + if _newclass: num_vbases = property(GetNumberOfVirtualBaseClasses, None, doc='''A read only property that returns number of virtual base classes in this type as an integer.''') + + __swig_getmethods__["num_template_args"] = GetNumberOfTemplateArguments + if _newclass: num_template_args = property(GetNumberOfTemplateArguments, None, doc='''A read only property that returns number of template arguments in this type as an integer.''') + + __swig_getmethods__["template_args"] = template_arg_array + if _newclass: template_args = property(template_arg_array, None, doc='''A read only property that returns a list() of lldb.SBType objects that represent all template arguments in this type.''') + + __swig_getmethods__["type"] = GetTypeClass + if _newclass: type = property(GetTypeClass, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eTypeClass") that represents a classification for this type.''') + + __swig_getmethods__["is_complete"] = IsTypeComplete + if _newclass: is_complete = property(IsTypeComplete, None, doc='''A read only property that returns a boolean value that indicates if this type is a complete type (True) or a forward declaration (False).''') + + def get_bases_array(self): + '''An accessor function that returns a list() that contains all direct base classes in a lldb.SBType object.''' + bases = [] + for idx in range(self.GetNumberOfDirectBaseClasses()): + bases.append(self.GetDirectBaseClassAtIndex(idx)) + return bases + + def get_vbases_array(self): + '''An accessor function that returns a list() that contains all fields in a lldb.SBType object.''' + vbases = [] + for idx in range(self.GetNumberOfVirtualBaseClasses()): + vbases.append(self.GetVirtualBaseClassAtIndex(idx)) + return vbases + + def get_fields_array(self): + '''An accessor function that returns a list() that contains all fields in a lldb.SBType object.''' + fields = [] + for idx in range(self.GetNumberOfFields()): + fields.append(self.GetFieldAtIndex(idx)) + return fields + + def get_members_array(self): + '''An accessor function that returns a list() that contains all members (base classes and fields) in a lldb.SBType object in ascending bit offset order.''' + members = [] + bases = self.get_bases_array() + fields = self.get_fields_array() + vbases = self.get_vbases_array() + for base in bases: + bit_offset = base.bit_offset + added = False + for idx, member in enumerate(members): + if member.bit_offset > bit_offset: + members.insert(idx, base) + added = True + break + if not added: + members.append(base) + for vbase in vbases: + bit_offset = vbase.bit_offset + added = False + for idx, member in enumerate(members): + if member.bit_offset > bit_offset: + members.insert(idx, vbase) + added = True + break + if not added: + members.append(vbase) + for field in fields: + bit_offset = field.bit_offset + added = False + for idx, member in enumerate(members): + if member.bit_offset > bit_offset: + members.insert(idx, field) + added = True + break + if not added: + members.append(field) + return members + + def get_enum_members_array(self): + '''An accessor function that returns a list() that contains all enum members in an lldb.SBType object.''' + enum_members_list = [] + sb_enum_members = self.GetEnumMembers() + for idx in range(sb_enum_members.GetSize()): + enum_members_list.append(sb_enum_members.GetTypeEnumMemberAtIndex(idx)) + return enum_members_list + + __swig_getmethods__["bases"] = get_bases_array + if _newclass: bases = property(get_bases_array, None, doc='''A read only property that returns a list() of lldb.SBTypeMember objects that represent all of the direct base classes for this type.''') + + __swig_getmethods__["vbases"] = get_vbases_array + if _newclass: vbases = property(get_vbases_array, None, doc='''A read only property that returns a list() of lldb.SBTypeMember objects that represent all of the virtual base classes for this type.''') + + __swig_getmethods__["fields"] = get_fields_array + if _newclass: fields = property(get_fields_array, None, doc='''A read only property that returns a list() of lldb.SBTypeMember objects that represent all of the fields for this type.''') + + __swig_getmethods__["members"] = get_members_array + if _newclass: members = property(get_members_array, None, doc='''A read only property that returns a list() of all lldb.SBTypeMember objects that represent all of the base classes, virtual base classes and fields for this type in ascending bit offset order.''') + + __swig_getmethods__["enum_members"] = get_enum_members_array + if _newclass: enum_members = property(get_enum_members_array, None, doc='''A read only property that returns a list() of all lldb.SBTypeEnumMember objects that represent the enum members for this type.''') + + %} + +}; + +%feature("docstring", +"Represents a list of SBTypes. The FindTypes() method of SBTarget/SBModule +returns a SBTypeList. + +SBTypeList supports SBType iteration. For example, + +main.cpp: + +class Task { +public: + int id; + Task *next; + Task(int i, Task *n): + id(i), + next(n) + {} +}; + +... + +find_type.py: + + # Get the type 'Task'. + type_list = target.FindTypes('Task') + self.assertTrue(len(type_list) == 1) + # To illustrate the SBType iteration. + for type in type_list: + # do something with type + +... +") SBTypeList; +class SBTypeList +{ +public: + SBTypeList(); + + bool + IsValid(); + + void + Append (lldb::SBType type); + + lldb::SBType + GetTypeAtIndex (uint32_t index); + + uint32_t + GetSize(); + + ~SBTypeList(); +}; + +} // namespace lldb diff --git a/scripts/interface/SBTypeCategory.i b/scripts/interface/SBTypeCategory.i new file mode 100644 index 000000000000..924c7f6976db --- /dev/null +++ b/scripts/interface/SBTypeCategory.i @@ -0,0 +1,246 @@ +//===-- SWIG Interface for SBTypeCategory---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + %feature("docstring", + "Represents a category that can contain formatters for types. + ") SBTypeCategory; + + class SBTypeCategory + { + public: + + SBTypeCategory(); + + SBTypeCategory (const lldb::SBTypeCategory &rhs); + + ~SBTypeCategory (); + + bool + IsValid() const; + + bool + GetEnabled (); + + void + SetEnabled (bool); + + const char* + GetName(); + + lldb::LanguageType + GetLanguageAtIndex (uint32_t idx); + + uint32_t + GetNumLanguages (); + + void + AddLanguage (lldb::LanguageType language); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + uint32_t + GetNumFormats (); + + uint32_t + GetNumSummaries (); + + uint32_t + GetNumFilters (); + + uint32_t + GetNumSynthetics (); + + lldb::SBTypeNameSpecifier + GetTypeNameSpecifierForFilterAtIndex (uint32_t); + + lldb::SBTypeNameSpecifier + GetTypeNameSpecifierForFormatAtIndex (uint32_t); + + lldb::SBTypeNameSpecifier + GetTypeNameSpecifierForSummaryAtIndex (uint32_t); + + lldb::SBTypeNameSpecifier + GetTypeNameSpecifierForSyntheticAtIndex (uint32_t); + + lldb::SBTypeFilter + GetFilterForType (lldb::SBTypeNameSpecifier); + + lldb::SBTypeFormat + GetFormatForType (lldb::SBTypeNameSpecifier); + + lldb::SBTypeSummary + GetSummaryForType (lldb::SBTypeNameSpecifier); + + lldb::SBTypeSynthetic + GetSyntheticForType (lldb::SBTypeNameSpecifier); + + lldb::SBTypeFilter + GetFilterAtIndex (uint32_t); + + lldb::SBTypeFormat + GetFormatAtIndex (uint32_t); + + lldb::SBTypeSummary + GetSummaryAtIndex (uint32_t); + + lldb::SBTypeSynthetic + GetSyntheticAtIndex (uint32_t); + + bool + AddTypeFormat (lldb::SBTypeNameSpecifier, + lldb::SBTypeFormat); + + bool + DeleteTypeFormat (lldb::SBTypeNameSpecifier); + + bool + AddTypeSummary (lldb::SBTypeNameSpecifier, + lldb::SBTypeSummary); + + bool + DeleteTypeSummary (lldb::SBTypeNameSpecifier); + + bool + AddTypeFilter (lldb::SBTypeNameSpecifier, + lldb::SBTypeFilter); + + bool + DeleteTypeFilter (lldb::SBTypeNameSpecifier); + + bool + AddTypeSynthetic (lldb::SBTypeNameSpecifier, + lldb::SBTypeSynthetic); + + bool + DeleteTypeSynthetic (lldb::SBTypeNameSpecifier); + + %pythoncode %{ + + class formatters_access_class(object): + '''A helper object that will lazily hand out formatters for a specific category.''' + def __init__(self, sbcategory, get_count_function, get_at_index_function, get_by_name_function): + self.sbcategory = sbcategory + self.get_count_function = get_count_function + self.get_at_index_function = get_at_index_function + self.get_by_name_function = get_by_name_function + self.regex_type = type(re.compile('.')) + + + def __len__(self): + if self.sbcategory and self.get_count_function: + return int(self.get_count_function(self.sbcategory)) + return 0 + + def __getitem__(self, key): + num_items = len(self) + if type(key) is int: + if key < num_items: + return self.get_at_index_function(self.sbcategory,key) + elif type(key) is str: + return self.get_by_name_function(self.sbcategory,SBTypeNameSpecifier(key)) + elif isinstance(key,self.regex_type): + return self.get_by_name_function(self.sbcategory,SBTypeNameSpecifier(key.pattern,True)) + else: + print("error: unsupported item type: %s" % type(key)) + return None + + def get_formats_access_object(self): + '''An accessor function that returns an accessor object which allows lazy format access from a lldb.SBTypeCategory object.''' + return self.formatters_access_class (self,self.__class__.GetNumFormats,self.__class__.GetFormatAtIndex,self.__class__.GetFormatForType) + + def get_formats_array(self): + '''An accessor function that returns a list() that contains all formats in a lldb.SBCategory object.''' + formats = [] + for idx in range(self.GetNumFormats()): + formats.append(self.GetFormatAtIndex(idx)) + return formats + + def get_summaries_access_object(self): + '''An accessor function that returns an accessor object which allows lazy summary access from a lldb.SBTypeCategory object.''' + return self.formatters_access_class (self,self.__class__.GetNumSummaries,self.__class__.GetSummaryAtIndex,self.__class__.GetSummaryForType) + + def get_summaries_array(self): + '''An accessor function that returns a list() that contains all summaries in a lldb.SBCategory object.''' + summaries = [] + for idx in range(self.GetNumSummaries()): + summaries.append(self.GetSummaryAtIndex(idx)) + return summaries + + def get_synthetics_access_object(self): + '''An accessor function that returns an accessor object which allows lazy synthetic children provider access from a lldb.SBTypeCategory object.''' + return self.formatters_access_class (self,self.__class__.GetNumSynthetics,self.__class__.GetSyntheticAtIndex,self.__class__.GetSyntheticForType) + + def get_synthetics_array(self): + '''An accessor function that returns a list() that contains all synthetic children providers in a lldb.SBCategory object.''' + synthetics = [] + for idx in range(self.GetNumSynthetics()): + synthetics.append(self.GetSyntheticAtIndex(idx)) + return synthetics + + def get_filters_access_object(self): + '''An accessor function that returns an accessor object which allows lazy filter access from a lldb.SBTypeCategory object.''' + return self.formatters_access_class (self,self.__class__.GetNumFilters,self.__class__.GetFilterAtIndex,self.__class__.GetFilterForType) + + def get_filters_array(self): + '''An accessor function that returns a list() that contains all filters in a lldb.SBCategory object.''' + filters = [] + for idx in range(self.GetNumFilters()): + filters.append(self.GetFilterAtIndex(idx)) + return filters + + __swig_getmethods__["formats"] = get_formats_array + if _newclass: formats = property(get_formats_array, None, doc='''A read only property that returns a list() of lldb.SBTypeFormat objects contained in this category''') + + __swig_getmethods__["format"] = get_formats_access_object + if _newclass: format = property(get_formats_access_object, None, doc=r'''A read only property that returns an object that you can use to look for formats by index or type name.''') + + __swig_getmethods__["summaries"] = get_summaries_array + if _newclass: summaries = property(get_summaries_array, None, doc='''A read only property that returns a list() of lldb.SBTypeSummary objects contained in this category''') + + __swig_getmethods__["summary"] = get_summaries_access_object + if _newclass: summary = property(get_summaries_access_object, None, doc=r'''A read only property that returns an object that you can use to look for summaries by index or type name or regular expression.''') + + __swig_getmethods__["filters"] = get_filters_array + if _newclass: filters = property(get_filters_array, None, doc='''A read only property that returns a list() of lldb.SBTypeFilter objects contained in this category''') + + __swig_getmethods__["filter"] = get_filters_access_object + if _newclass: filter = property(get_filters_access_object, None, doc=r'''A read only property that returns an object that you can use to look for filters by index or type name or regular expression.''') + + __swig_getmethods__["synthetics"] = get_synthetics_array + if _newclass: synthetics = property(get_synthetics_array, None, doc='''A read only property that returns a list() of lldb.SBTypeSynthetic objects contained in this category''') + + __swig_getmethods__["synthetic"] = get_synthetics_access_object + if _newclass: synthetic = property(get_synthetics_access_object, None, doc=r'''A read only property that returns an object that you can use to look for synthetic children provider by index or type name or regular expression.''') + + __swig_getmethods__["num_formats"] = GetNumFormats + if _newclass: num_formats = property(GetNumFormats, None) + __swig_getmethods__["num_summaries"] = GetNumSummaries + if _newclass: num_summaries = property(GetNumSummaries, None) + __swig_getmethods__["num_filters"] = GetNumFilters + if _newclass: num_filters = property(GetNumFilters, None) + __swig_getmethods__["num_synthetics"] = GetNumSynthetics + if _newclass: num_synthetics = property(GetNumSynthetics, None) + + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None) + + __swig_getmethods__["enabled"] = GetEnabled + __swig_setmethods__["enabled"] = SetEnabled + if _newclass: enabled = property(GetEnabled, SetEnabled) + %} + + }; + + +} // namespace lldb + diff --git a/scripts/interface/SBTypeEnumMember.i b/scripts/interface/SBTypeEnumMember.i new file mode 100644 index 000000000000..02d89f17a28b --- /dev/null +++ b/scripts/interface/SBTypeEnumMember.i @@ -0,0 +1,108 @@ +//===-- SWIG Interface for SBTypeEnumMember ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature( + "docstring", + "Represents a member of an enum in lldb." +) SBTypeEnumMember; + +class SBTypeEnumMember +{ +public: + SBTypeEnumMember (); + + SBTypeEnumMember (const SBTypeEnumMember& rhs); + + ~SBTypeEnumMember(); + + bool + IsValid() const; + + int64_t + GetValueAsSigned(); + + uint64_t + GetValueAsUnsigned(); + + const char * + GetName (); + + lldb::SBType + GetType (); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + %pythoncode %{ + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None, doc='''A read only property that returns the name for this enum member as a string.''') + + __swig_getmethods__["type"] = GetType + if _newclass: type = property(GetType, None, doc='''A read only property that returns an lldb object that represents the type (lldb.SBType) for this enum member.''') + + __swig_getmethods__["signed"] = GetValueAsSigned + if _newclass: signed = property(GetValueAsSigned, None, doc='''A read only property that returns the value of this enum member as a signed integer.''') + + __swig_getmethods__["unsigned"] = GetValueAsUnsigned + if _newclass: unsigned = property(GetValueAsUnsigned, None, doc='''A read only property that returns the value of this enum member as a unsigned integer.''') + %} + +protected: + friend class SBType; + friend class SBTypeEnumMemberList; + + void + reset (lldb_private::TypeEnumMemberImpl *); + + lldb_private::TypeEnumMemberImpl & + ref (); + + const lldb_private::TypeEnumMemberImpl & + ref () const; + + lldb::TypeEnumMemberImplSP m_opaque_sp; + + SBTypeEnumMember (const lldb::TypeEnumMemberImplSP &); +}; + +%feature( + "docstring", + "Represents a list of SBTypeEnumMembers." +) SBTypeEnumMemberList; + +class SBTypeEnumMemberList +{ +public: + SBTypeEnumMemberList(); + + SBTypeEnumMemberList(const SBTypeEnumMemberList& rhs); + + ~SBTypeEnumMemberList(); + + bool + IsValid(); + + void + Append (SBTypeEnumMember entry); + + SBTypeEnumMember + GetTypeEnumMemberAtIndex (uint32_t index); + + uint32_t + GetSize(); + + +private: + std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb diff --git a/scripts/interface/SBTypeFilter.i b/scripts/interface/SBTypeFilter.i new file mode 100644 index 000000000000..083dfa8433af --- /dev/null +++ b/scripts/interface/SBTypeFilter.i @@ -0,0 +1,75 @@ +//===-- SWIG Interface for SBTypeFilter----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + %feature("docstring", + "Represents a filter that can be associated to one or more types. + ") SBTypeFilter; + + class SBTypeFilter + { + public: + + SBTypeFilter(); + + SBTypeFilter (uint32_t options); + + SBTypeFilter (const lldb::SBTypeFilter &rhs); + + ~SBTypeFilter (); + + bool + IsValid() const; + + bool + IsEqualTo (lldb::SBTypeFilter &rhs); + + uint32_t + GetNumberOfExpressionPaths (); + + const char* + GetExpressionPathAtIndex (uint32_t i); + + bool + ReplaceExpressionPathAtIndex (uint32_t i, const char* item); + + void + AppendExpressionPath (const char* item); + + void + Clear(); + + uint32_t + GetOptions(); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, lldb::DescriptionLevel description_level); + + bool + operator == (lldb::SBTypeFilter &rhs); + + bool + operator != (lldb::SBTypeFilter &rhs); + + %pythoncode %{ + __swig_getmethods__["options"] = GetOptions + __swig_setmethods__["options"] = SetOptions + if _newclass: options = property(GetOptions, SetOptions) + + __swig_getmethods__["count"] = GetNumberOfExpressionPaths + if _newclass: count = property(GetNumberOfExpressionPaths, None) + %} + + }; + +} // namespace lldb diff --git a/scripts/interface/SBTypeFormat.i b/scripts/interface/SBTypeFormat.i new file mode 100644 index 000000000000..4e4b69c3f1fc --- /dev/null +++ b/scripts/interface/SBTypeFormat.i @@ -0,0 +1,78 @@ +//===-- SWIG Interface for SBTypeFormat----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + %feature("docstring", + "Represents a format that can be associated to one or more types. + ") SBTypeFormat; + + class SBTypeFormat + { + public: + + SBTypeFormat(); + + SBTypeFormat (lldb::Format format, uint32_t options = 0); + + SBTypeFormat (const char* type, uint32_t options = 0); + + SBTypeFormat (const lldb::SBTypeFormat &rhs); + + ~SBTypeFormat (); + + bool + IsValid() const; + + bool + IsEqualTo (lldb::SBTypeFormat &rhs); + + lldb::Format + GetFormat (); + + const char* + GetTypeName (); + + uint32_t + GetOptions(); + + void + SetFormat (lldb::Format); + + void + SetTypeName (const char*); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + bool + operator == (lldb::SBTypeFormat &rhs); + + bool + operator != (lldb::SBTypeFormat &rhs); + + %pythoncode %{ + __swig_getmethods__["format"] = GetFormat + __swig_setmethods__["format"] = SetFormat + if _newclass: format = property(GetFormat, SetFormat) + + __swig_getmethods__["options"] = GetOptions + __swig_setmethods__["options"] = SetOptions + if _newclass: options = property(GetOptions, SetOptions) + %} + + }; + + +} // namespace lldb + diff --git a/scripts/interface/SBTypeNameSpecifier.i b/scripts/interface/SBTypeNameSpecifier.i new file mode 100644 index 000000000000..97d23ca172ef --- /dev/null +++ b/scripts/interface/SBTypeNameSpecifier.i @@ -0,0 +1,68 @@ +//===-- SWIG Interface for SBTypeNameSpecifier---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + %feature("docstring", + "Represents a general way to provide a type name to LLDB APIs. + ") SBTypeNameSpecifier; + + class SBTypeNameSpecifier + { + public: + + SBTypeNameSpecifier(); + + SBTypeNameSpecifier (const char* name, + bool is_regex = false); + + SBTypeNameSpecifier (SBType type); + + SBTypeNameSpecifier (const lldb::SBTypeNameSpecifier &rhs); + + ~SBTypeNameSpecifier (); + + bool + IsValid() const; + + bool + IsEqualTo (lldb::SBTypeNameSpecifier &rhs); + + const char* + GetName(); + + lldb::SBType + GetType (); + + bool + IsRegex(); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + bool + operator == (lldb::SBTypeNameSpecifier &rhs); + + bool + operator != (lldb::SBTypeNameSpecifier &rhs); + + %pythoncode %{ + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None) + + __swig_getmethods__["is_regex"] = IsRegex + if _newclass: is_regex = property(IsRegex, None) + %} + + + }; + +} // namespace lldb + diff --git a/scripts/interface/SBTypeSummary.i b/scripts/interface/SBTypeSummary.i new file mode 100644 index 000000000000..924256111aee --- /dev/null +++ b/scripts/interface/SBTypeSummary.i @@ -0,0 +1,123 @@ +//===-- SWIG Interface for SBTypeSummary---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + class SBTypeSummaryOptions + { + public: + SBTypeSummaryOptions(); + + SBTypeSummaryOptions (const lldb::SBTypeSummaryOptions &rhs); + + ~SBTypeSummaryOptions (); + + bool + IsValid (); + + lldb::LanguageType + GetLanguage (); + + lldb::TypeSummaryCapping + GetCapping (); + + void + SetLanguage (lldb::LanguageType); + + void + SetCapping (lldb::TypeSummaryCapping); + }; + + %feature("docstring", + "Represents a summary that can be associated to one or more types. + ") SBTypeSummary; + + class SBTypeSummary + { + public: + + SBTypeSummary(); + + static SBTypeSummary + CreateWithSummaryString (const char* data, uint32_t options = 0); + + static SBTypeSummary + CreateWithFunctionName (const char* data, uint32_t options = 0); + + static SBTypeSummary + CreateWithScriptCode (const char* data, uint32_t options = 0); + + SBTypeSummary (const lldb::SBTypeSummary &rhs); + + ~SBTypeSummary (); + + bool + IsValid() const; + + bool + IsEqualTo (lldb::SBTypeSummary &rhs); + + bool + IsFunctionCode(); + + bool + IsFunctionName(); + + bool + IsSummaryString(); + + const char* + GetData (); + + void + SetSummaryString (const char* data); + + void + SetFunctionName (const char* data); + + void + SetFunctionCode (const char* data); + + uint32_t + GetOptions (); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + bool + operator == (lldb::SBTypeSummary &rhs); + + bool + operator != (lldb::SBTypeSummary &rhs); + + %pythoncode %{ + __swig_getmethods__["options"] = GetOptions + __swig_setmethods__["options"] = SetOptions + if _newclass: options = property(GetOptions, SetOptions) + + __swig_getmethods__["is_summary_string"] = IsSummaryString + if _newclass: is_summary_string = property(IsSummaryString, None) + + __swig_getmethods__["is_function_name"] = IsFunctionName + if _newclass: is_function_name = property(IsFunctionName, None) + + __swig_getmethods__["is_function_name"] = IsFunctionCode + if _newclass: is_function_name = property(IsFunctionCode, None) + + __swig_getmethods__["summary_data"] = GetData + if _newclass: summary_data = property(GetData, None) + %} + + }; + +} // namespace lldb + diff --git a/scripts/interface/SBTypeSynthetic.i b/scripts/interface/SBTypeSynthetic.i new file mode 100644 index 000000000000..e040cd55c975 --- /dev/null +++ b/scripts/interface/SBTypeSynthetic.i @@ -0,0 +1,80 @@ +//===-- SWIG Interface for SBTypeSynthetic-------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + + %feature("docstring", + "Represents a summary that can be associated to one or more types. + ") SBTypeSynthetic; + + class SBTypeSynthetic + { + public: + + SBTypeSynthetic(); + + static lldb::SBTypeSynthetic + CreateWithClassName (const char* data, uint32_t options = 0); + + static lldb::SBTypeSynthetic + CreateWithScriptCode (const char* data, uint32_t options = 0); + + SBTypeSynthetic (const lldb::SBTypeSynthetic &rhs); + + ~SBTypeSynthetic (); + + bool + IsValid() const; + + bool + IsEqualTo (lldb::SBTypeSynthetic &rhs); + + bool + IsClassCode(); + + const char* + GetData (); + + void + SetClassName (const char* data); + + void + SetClassCode (const char* data); + + uint32_t + GetOptions (); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + bool + operator == (lldb::SBTypeSynthetic &rhs); + + bool + operator != (lldb::SBTypeSynthetic &rhs); + + %pythoncode %{ + __swig_getmethods__["options"] = GetOptions + __swig_setmethods__["options"] = SetOptions + if _newclass: options = property(GetOptions, SetOptions) + + __swig_getmethods__["contains_code"] = IsClassCode + if _newclass: contains_code = property(IsClassCode, None) + + __swig_getmethods__["synthetic_data"] = GetData + if _newclass: synthetic_data = property(GetData, None) + %} + + }; + +} // namespace lldb diff --git a/scripts/interface/SBUnixSignals.i b/scripts/interface/SBUnixSignals.i new file mode 100644 index 000000000000..2cb45371070b --- /dev/null +++ b/scripts/interface/SBUnixSignals.i @@ -0,0 +1,74 @@ +//===-- SWIG Interface for SBUnixSignals ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Allows you to manipulate LLDB's signal disposition" +) SBUnixSignals; +class SBUnixSignals +{ +public: + SBUnixSignals (); + + SBUnixSignals (const lldb::SBUnixSignals &rhs); + + ~SBUnixSignals(); + + void + Clear (); + + bool + IsValid () const; + + const char * + GetSignalAsCString (int32_t signo) const; + + int32_t + GetSignalNumberFromName (const char *name) const; + + bool + GetShouldSuppress (int32_t signo) const; + + bool + SetShouldSuppress (int32_t signo, + bool value); + + bool + GetShouldStop (int32_t signo) const; + + bool + SetShouldStop (int32_t signo, + bool value); + + bool + GetShouldNotify (int32_t signo) const; + + bool + SetShouldNotify (int32_t signo, bool value); + + int32_t + GetNumSignals () const; + + int32_t + GetSignalAtIndex (int32_t index) const; + + %pythoncode %{ + def get_unix_signals_list(self): + signals = [] + for idx in range(0, self.GetNumSignals()): + signals.append(self.GetSignalAtIndex(sig)) + return signals + + __swig_getmethods__["signals"] = get_unix_signals_list + if _newclass: threads = property(get_unix_signals_list, None, doc='''A read only property that returns a list() of valid signal numbers for this platform.''') + %} +}; + +} // namespace lldb diff --git a/scripts/interface/SBValue.i b/scripts/interface/SBValue.i new file mode 100644 index 000000000000..5049fd05794d --- /dev/null +++ b/scripts/interface/SBValue.i @@ -0,0 +1,540 @@ +//===-- SWIG Interface for SBValue ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents the value of a variable, a register, or an expression. + +SBValue supports iteration through its child, which in turn is represented +as an SBValue. For example, we can get the general purpose registers of a +frame as an SBValue, and iterate through all the registers, + + registerSet = frame.GetRegisters() # Returns an SBValueList. + for regs in registerSet: + if 'general purpose registers' in regs.getName().lower(): + GPRs = regs + break + + print('%s (number of children = %d):' % (GPRs.GetName(), GPRs.GetNumChildren())) + for reg in GPRs: + print('Name: ', reg.GetName(), ' Value: ', reg.GetValue()) + +produces the output: + +General Purpose Registers (number of children = 21): +Name: rax Value: 0x0000000100000c5c +Name: rbx Value: 0x0000000000000000 +Name: rcx Value: 0x00007fff5fbffec0 +Name: rdx Value: 0x00007fff5fbffeb8 +Name: rdi Value: 0x0000000000000001 +Name: rsi Value: 0x00007fff5fbffea8 +Name: rbp Value: 0x00007fff5fbffe80 +Name: rsp Value: 0x00007fff5fbffe60 +Name: r8 Value: 0x0000000008668682 +Name: r9 Value: 0x0000000000000000 +Name: r10 Value: 0x0000000000001200 +Name: r11 Value: 0x0000000000000206 +Name: r12 Value: 0x0000000000000000 +Name: r13 Value: 0x0000000000000000 +Name: r14 Value: 0x0000000000000000 +Name: r15 Value: 0x0000000000000000 +Name: rip Value: 0x0000000100000dae +Name: rflags Value: 0x0000000000000206 +Name: cs Value: 0x0000000000000027 +Name: fs Value: 0x0000000000000010 +Name: gs Value: 0x0000000000000048 + +See also linked_list_iter() for another perspective on how to iterate through an +SBValue instance which interprets the value object as representing the head of a +linked list." +) SBValue; +class SBValue +{ +public: + SBValue (); + + SBValue (const SBValue &rhs); + + ~SBValue (); + + bool + IsValid(); + + void + Clear(); + + SBError + GetError(); + + lldb::user_id_t + GetID (); + + const char * + GetName(); + + const char * + GetTypeName (); + + const char * + GetDisplayTypeName (); + + size_t + GetByteSize (); + + bool + IsInScope (); + + lldb::Format + GetFormat (); + + void + SetFormat (lldb::Format format); + + const char * + GetValue (); + + int64_t + GetValueAsSigned(SBError& error, int64_t fail_value=0); + + uint64_t + GetValueAsUnsigned(SBError& error, uint64_t fail_value=0); + + int64_t + GetValueAsSigned(int64_t fail_value=0); + + uint64_t + GetValueAsUnsigned(uint64_t fail_value=0); + + ValueType + GetValueType (); + + bool + GetValueDidChange (); + + const char * + GetSummary (); + + const char * + GetSummary (lldb::SBStream& stream, + lldb::SBTypeSummaryOptions& options); + + const char * + GetObjectDescription (); + + const char * + GetTypeValidatorResult (); + + lldb::SBValue + GetDynamicValue (lldb::DynamicValueType use_dynamic); + + lldb::SBValue + GetStaticValue (); + + lldb::SBValue + GetNonSyntheticValue (); + + lldb::DynamicValueType + GetPreferDynamicValue (); + + void + SetPreferDynamicValue (lldb::DynamicValueType use_dynamic); + + bool + GetPreferSyntheticValue (); + + void + SetPreferSyntheticValue (bool use_synthetic); + + bool + IsDynamic(); + + bool + IsSynthetic (); + + const char * + GetLocation (); + + bool + SetValueFromCString (const char *value_str); + + bool + SetValueFromCString (const char *value_str, lldb::SBError& error); + + lldb::SBTypeFormat + GetTypeFormat (); + + lldb::SBTypeSummary + GetTypeSummary (); + + lldb::SBTypeFilter + GetTypeFilter (); + + lldb::SBTypeSynthetic + GetTypeSynthetic (); + + lldb::SBValue + GetChildAtIndex (uint32_t idx); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get a child value by index from a value. + /// + /// Structs, unions, classes, arrays and pointers have child + /// values that can be access by index. + /// + /// Structs and unions access child members using a zero based index + /// for each child member. For + /// + /// Classes reserve the first indexes for base classes that have + /// members (empty base classes are omitted), and all members of the + /// current class will then follow the base classes. + /// + /// Pointers differ depending on what they point to. If the pointer + /// points to a simple type, the child at index zero + /// is the only child value available, unless \a synthetic_allowed + /// is \b true, in which case the pointer will be used as an array + /// and can create 'synthetic' child values using positive or + /// negative indexes. If the pointer points to an aggregate type + /// (an array, class, union, struct), then the pointee is + /// transparently skipped and any children are going to be the indexes + /// of the child values within the aggregate type. For example if + /// we have a 'Point' type and we have a SBValue that contains a + /// pointer to a 'Point' type, then the child at index zero will be + /// the 'x' member, and the child at index 1 will be the 'y' member + /// (the child at index zero won't be a 'Point' instance). + /// + /// If you actually need an SBValue that represents the type pointed + /// to by a SBValue for which GetType().IsPointeeType() returns true, + /// regardless of the pointee type, you can do that with the SBValue.Dereference + /// method (or the equivalent deref property). + /// + /// Arrays have a preset number of children that can be accessed by + /// index and will returns invalid child values for indexes that are + /// out of bounds unless the \a synthetic_allowed is \b true. In this + /// case the array can create 'synthetic' child values for indexes + /// that aren't in the array bounds using positive or negative + /// indexes. + /// + /// @param[in] idx + /// The index of the child value to get + /// + /// @param[in] use_dynamic + /// An enumeration that specifies whether to get dynamic values, + /// and also if the target can be run to figure out the dynamic + /// type of the child value. + /// + /// @param[in] synthetic_allowed + /// If \b true, then allow child values to be created by index + /// for pointers and arrays for indexes that normally wouldn't + /// be allowed. + /// + /// @return + /// A new SBValue object that represents the child member value. + //------------------------------------------------------------------ + ") GetChildAtIndex; + lldb::SBValue + GetChildAtIndex (uint32_t idx, + lldb::DynamicValueType use_dynamic, + bool can_create_synthetic); + + lldb::SBValue + CreateChildAtOffset (const char *name, uint32_t offset, lldb::SBType type); + + lldb::SBValue + SBValue::Cast (lldb::SBType type); + + lldb::SBValue + CreateValueFromExpression (const char *name, const char* expression); + + lldb::SBValue + CreateValueFromExpression (const char *name, const char* expression, SBExpressionOptions &options); + + lldb::SBValue + CreateValueFromAddress(const char* name, lldb::addr_t address, lldb::SBType type); + + lldb::SBValue + CreateValueFromData (const char* name, + lldb::SBData data, + lldb::SBType type); + + lldb::SBType + GetType(); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Returns the child member index. + /// + /// Matches children of this object only and will match base classes and + /// member names if this is a clang typed object. + /// + /// @param[in] name + /// The name of the child value to get + /// + /// @return + /// An index to the child member value. + //------------------------------------------------------------------ + ") GetIndexOfChildWithName; + uint32_t + GetIndexOfChildWithName (const char *name); + + lldb::SBValue + GetChildMemberWithName (const char *name); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Returns the child member value. + /// + /// Matches child members of this object and child members of any base + /// classes. + /// + /// @param[in] name + /// The name of the child value to get + /// + /// @param[in] use_dynamic + /// An enumeration that specifies whether to get dynamic values, + /// and also if the target can be run to figure out the dynamic + /// type of the child value. + /// + /// @return + /// A new SBValue object that represents the child member value. + //------------------------------------------------------------------ + ") GetChildMemberWithName; + lldb::SBValue + GetChildMemberWithName (const char *name, lldb::DynamicValueType use_dynamic); + + %feature("docstring", "Expands nested expressions like .a->b[0].c[1]->d." + ) GetValueForExpressionPath; + lldb::SBValue + GetValueForExpressionPath(const char* expr_path); + + lldb::SBDeclaration + GetDeclaration (); + + bool + MightHaveChildren (); + + bool + IsRuntimeSupportValue (); + + uint32_t + GetNumChildren (); + + %feature("doctstring", " + //------------------------------------------------------------------ + /// Returns the number for children. + /// + /// @param[in] max + /// If max is less the lldb.UINT32_MAX, then the returned value is + /// capped to max. + /// + /// @return + /// An integer value capped to the argument max. + //------------------------------------------------------------------ + ") GetNumChildren; + uint32_t + GetNumChildren (uint32_t max); + + void * + GetOpaqueType(); + + lldb::SBValue + Dereference (); + + lldb::SBValue + AddressOf(); + + bool + TypeIsPointerType (); + + lldb::SBTarget + GetTarget(); + + lldb::SBProcess + GetProcess(); + + lldb::SBThread + GetThread(); + + lldb::SBFrame + GetFrame(); + + %feature("docstring", " + /// Find and watch a variable. + /// It returns an SBWatchpoint, which may be invalid. + ") Watch; + lldb::SBWatchpoint + Watch (bool resolve_location, bool read, bool write, SBError &error); + + %feature("docstring", " + /// Find and watch the location pointed to by a variable. + /// It returns an SBWatchpoint, which may be invalid. + ") WatchPointee; + lldb::SBWatchpoint + WatchPointee (bool resolve_location, bool read, bool write, SBError &error); + + bool + GetDescription (lldb::SBStream &description); + + bool + GetExpressionPath (lldb::SBStream &description); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get an SBData wrapping what this SBValue points to. + /// + /// This method will dereference the current SBValue, if its + /// data type is a T* or T[], and extract item_count elements + /// of type T from it, copying their contents in an SBData. + /// + /// @param[in] item_idx + /// The index of the first item to retrieve. For an array + /// this is equivalent to array[item_idx], for a pointer + /// to *(pointer + item_idx). In either case, the measurement + /// unit for item_idx is the sizeof(T) rather than the byte + /// + /// @param[in] item_count + /// How many items should be copied into the output. By default + /// only one item is copied, but more can be asked for. + /// + /// @return + /// An SBData with the contents of the copied items, on success. + /// An empty SBData otherwise. + //------------------------------------------------------------------ + ") GetPointeeData; + lldb::SBData + GetPointeeData (uint32_t item_idx = 0, + uint32_t item_count = 1); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get an SBData wrapping the contents of this SBValue. + /// + /// This method will read the contents of this object in memory + /// and copy them into an SBData for future use. + /// + /// @return + /// An SBData with the contents of this SBValue, on success. + /// An empty SBData otherwise. + //------------------------------------------------------------------ + ") GetData; + lldb::SBData + GetData (); + + bool + SetData (lldb::SBData &data, lldb::SBError& error); + + lldb::addr_t + GetLoadAddress(); + + lldb::SBAddress + GetAddress(); + + lldb::SBValue + Persist (); + + %feature("docstring", "Returns an expression path for this value." + ) GetExpressionPath; + bool + GetExpressionPath (lldb::SBStream &description, bool qualify_cxx_base_classes); + + %pythoncode %{ + def __get_dynamic__ (self): + '''Helper function for the "SBValue.dynamic" property.''' + return self.GetDynamicValue (eDynamicCanRunTarget) + + __swig_getmethods__["name"] = GetName + if _newclass: name = property(GetName, None, doc='''A read only property that returns the name of this value as a string.''') + + __swig_getmethods__["type"] = GetType + if _newclass: type = property(GetType, None, doc='''A read only property that returns a lldb.SBType object that represents the type for this value.''') + + __swig_getmethods__["size"] = GetByteSize + if _newclass: size = property(GetByteSize, None, doc='''A read only property that returns the size in bytes of this value.''') + + __swig_getmethods__["is_in_scope"] = IsInScope + if _newclass: is_in_scope = property(IsInScope, None, doc='''A read only property that returns a boolean value that indicates whether this value is currently lexically in scope.''') + + __swig_getmethods__["format"] = GetFormat + __swig_setmethods__["format"] = SetFormat + if _newclass: format = property(GetName, SetFormat, doc='''A read/write property that gets/sets the format used for lldb.SBValue().GetValue() for this value. See enumerations that start with "lldb.eFormat".''') + + __swig_getmethods__["value"] = GetValue + __swig_setmethods__["value"] = SetValueFromCString + if _newclass: value = property(GetValue, SetValueFromCString, doc='''A read/write property that gets/sets value from a string.''') + + __swig_getmethods__["value_type"] = GetValueType + if _newclass: value_type = property(GetValueType, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eValueType") that represents the type of this value (local, argument, global, register, etc.).''') + + __swig_getmethods__["changed"] = GetValueDidChange + if _newclass: changed = property(GetValueDidChange, None, doc='''A read only property that returns a boolean value that indicates if this value has changed since it was last updated.''') + + __swig_getmethods__["data"] = GetData + if _newclass: data = property(GetData, None, doc='''A read only property that returns an lldb object (lldb.SBData) that represents the bytes that make up the value for this object.''') + + __swig_getmethods__["load_addr"] = GetLoadAddress + if _newclass: load_addr = property(GetLoadAddress, None, doc='''A read only property that returns the load address of this value as an integer.''') + + __swig_getmethods__["addr"] = GetAddress + if _newclass: addr = property(GetAddress, None, doc='''A read only property that returns an lldb.SBAddress that represents the address of this value if it is in memory.''') + + __swig_getmethods__["deref"] = Dereference + if _newclass: deref = property(Dereference, None, doc='''A read only property that returns an lldb.SBValue that is created by dereferencing this value.''') + + __swig_getmethods__["address_of"] = AddressOf + if _newclass: address_of = property(AddressOf, None, doc='''A read only property that returns an lldb.SBValue that represents the address-of this value.''') + + __swig_getmethods__["error"] = GetError + if _newclass: error = property(GetError, None, doc='''A read only property that returns the lldb.SBError that represents the error from the last time the variable value was calculated.''') + + __swig_getmethods__["summary"] = GetSummary + if _newclass: summary = property(GetSummary, None, doc='''A read only property that returns the summary for this value as a string''') + + __swig_getmethods__["description"] = GetObjectDescription + if _newclass: description = property(GetObjectDescription, None, doc='''A read only property that returns the language-specific description of this value as a string''') + + __swig_getmethods__["dynamic"] = __get_dynamic__ + if _newclass: dynamic = property(__get_dynamic__, None, doc='''A read only property that returns an lldb.SBValue that is created by finding the dynamic type of this value.''') + + __swig_getmethods__["location"] = GetLocation + if _newclass: location = property(GetLocation, None, doc='''A read only property that returns the location of this value as a string.''') + + __swig_getmethods__["target"] = GetTarget + if _newclass: target = property(GetTarget, None, doc='''A read only property that returns the lldb.SBTarget that this value is associated with.''') + + __swig_getmethods__["process"] = GetProcess + if _newclass: process = property(GetProcess, None, doc='''A read only property that returns the lldb.SBProcess that this value is associated with, the returned value might be invalid and should be tested.''') + + __swig_getmethods__["thread"] = GetThread + if _newclass: thread = property(GetThread, None, doc='''A read only property that returns the lldb.SBThread that this value is associated with, the returned value might be invalid and should be tested.''') + + __swig_getmethods__["frame"] = GetFrame + if _newclass: frame = property(GetFrame, None, doc='''A read only property that returns the lldb.SBFrame that this value is associated with, the returned value might be invalid and should be tested.''') + + __swig_getmethods__["num_children"] = GetNumChildren + if _newclass: num_children = property(GetNumChildren, None, doc='''A read only property that returns the number of child lldb.SBValues that this value has.''') + + __swig_getmethods__["unsigned"] = GetValueAsUnsigned + if _newclass: unsigned = property(GetValueAsUnsigned, None, doc='''A read only property that returns the value of this SBValue as an usigned integer.''') + + __swig_getmethods__["signed"] = GetValueAsSigned + if _newclass: signed = property(GetValueAsSigned, None, doc='''A read only property that returns the value of this SBValue as a signed integer.''') + + def get_expr_path(self): + s = SBStream() + self.GetExpressionPath (s) + return s.GetData() + + __swig_getmethods__["path"] = get_expr_path + if _newclass: path = property(get_expr_path, None, doc='''A read only property that returns the expression path that one can use to reach this value in an expression.''') + %} + +}; + +} // namespace lldb diff --git a/scripts/interface/SBValueList.i b/scripts/interface/SBValueList.i new file mode 100644 index 000000000000..80e8f0ec999e --- /dev/null +++ b/scripts/interface/SBValueList.i @@ -0,0 +1,142 @@ +//===-- SWIG Interface for SBValueList --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a collection of SBValues. Both SBFrame's GetVariables() and +GetRegisters() return a SBValueList. + +SBValueList supports SBValue iteration. For example (from test/lldbutil.py), + +def get_registers(frame, kind): + '''Returns the registers given the frame and the kind of registers desired. + + Returns None if there's no such kind. + ''' + registerSet = frame.GetRegisters() # Return type of SBValueList. + for value in registerSet: + if kind.lower() in value.GetName().lower(): + return value + + return None + +def get_GPRs(frame): + '''Returns the general purpose registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_GPRs + regs = get_GPRs(frame) + for reg in regs: + print('%s => %s' % (reg.GetName(), reg.GetValue())) + ... + ''' + return get_registers(frame, 'general purpose') + +def get_FPRs(frame): + '''Returns the floating point registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_FPRs + regs = get_FPRs(frame) + for reg in regs: + print('%s => %s' % (reg.GetName(), reg.GetValue())) + ... + ''' + return get_registers(frame, 'floating point') + +def get_ESRs(frame): + '''Returns the exception state registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_ESRs + regs = get_ESRs(frame) + for reg in regs: + print('%s => %s' % (reg.GetName(), reg.GetValue())) + ... + ''' + return get_registers(frame, 'exception state')" +) SBValueList; +class SBValueList +{ +public: + + SBValueList (); + + SBValueList (const lldb::SBValueList &rhs); + + ~SBValueList(); + + bool + IsValid() const; + + void + Clear(); + + void + Append (const lldb::SBValue &val_obj); + + void + Append (const lldb::SBValueList& value_list); + + uint32_t + GetSize() const; + + lldb::SBValue + GetValueAtIndex (uint32_t idx) const; + + lldb::SBValue + FindValueObjectByUID (lldb::user_id_t uid); + + lldb::SBValue + GetFirstValueByName (const char* name) const; + + %pythoncode %{ + def __len__(self): + return int(self.GetSize()) + + def __getitem__(self, key): + count = len(self) + #------------------------------------------------------------ + # Access with "int" to get Nth item in the list + #------------------------------------------------------------ + if type(key) is int: + if key < count: + return self.GetValueAtIndex(key) + #------------------------------------------------------------ + # Access with "str" to get values by name + #------------------------------------------------------------ + elif type(key) is str: + matches = [] + for idx in range(count): + value = self.GetValueAtIndex(idx) + if value.name == key: + matches.append(value) + return matches + #------------------------------------------------------------ + # Match with regex + #------------------------------------------------------------ + elif isinstance(key, type(re.compile('.'))): + matches = [] + for idx in range(count): + value = self.GetValueAtIndex(idx) + re_match = key.search(value.name) + if re_match: + matches.append(value) + return matches + + %} + + +}; + +} // namespace lldb diff --git a/scripts/interface/SBVariablesOptions.i b/scripts/interface/SBVariablesOptions.i new file mode 100644 index 000000000000..3941a58d7bc9 --- /dev/null +++ b/scripts/interface/SBVariablesOptions.i @@ -0,0 +1,61 @@ +//===-- SWIG Interface for SBVariablesOptions ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +class SBVariablesOptions +{ +public: + SBVariablesOptions (); + + SBVariablesOptions (const SBVariablesOptions& options); + + ~SBVariablesOptions (); + + bool + IsValid () const; + + bool + GetIncludeArguments () const; + + void + SetIncludeArguments (bool); + + bool + GetIncludeLocals () const; + + void + SetIncludeLocals (bool); + + bool + GetIncludeStatics () const; + + void + SetIncludeStatics (bool); + + bool + GetInScopeOnly () const; + + void + SetInScopeOnly (bool); + + bool + GetIncludeRuntimeSupportValues () const; + + void + SetIncludeRuntimeSupportValues (bool); + + lldb::DynamicValueType + GetUseDynamic () const; + + void + SetUseDynamic (lldb::DynamicValueType); +}; + +} // namespace lldb diff --git a/scripts/interface/SBWatchpoint.i b/scripts/interface/SBWatchpoint.i new file mode 100644 index 000000000000..9a40131f5186 --- /dev/null +++ b/scripts/interface/SBWatchpoint.i @@ -0,0 +1,99 @@ +//===-- SWIG Interface for SBWatchpoint -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents an instance of watchpoint for a specific target program. + +A watchpoint is determined by the address and the byte size that resulted in +this particular instantiation. Each watchpoint has its settable options. + +See also SBTarget.watchpoint_iter() for example usage of iterating through the +watchpoints of the target." +) SBWatchpoint; +class SBWatchpoint +{ +public: + + SBWatchpoint (); + + SBWatchpoint (const lldb::SBWatchpoint &rhs); + + ~SBWatchpoint (); + + bool + IsValid(); + + SBError + GetError(); + + watch_id_t + GetID (); + + %feature("docstring", " + //------------------------------------------------------------------ + /// With -1 representing an invalid hardware index. + //------------------------------------------------------------------ + ") GetHardwareIndex; + int32_t + GetHardwareIndex (); + + lldb::addr_t + GetWatchAddress (); + + size_t + GetWatchSize(); + + void + SetEnabled(bool enabled); + + bool + IsEnabled (); + + uint32_t + GetHitCount (); + + uint32_t + GetIgnoreCount (); + + void + SetIgnoreCount (uint32_t n); + + %feature("docstring", " + //------------------------------------------------------------------ + /// Get the condition expression for the watchpoint. + //------------------------------------------------------------------ + ") GetCondition; + const char * + GetCondition (); + + %feature("docstring", " + //-------------------------------------------------------------------------- + /// The watchpoint stops only if the condition expression evaluates to true. + //-------------------------------------------------------------------------- + ") SetCondition; + void + SetCondition (const char *condition); + + bool + GetDescription (lldb::SBStream &description, DescriptionLevel level); + + static bool + EventIsWatchpointEvent (const lldb::SBEvent &event); + + static lldb::WatchpointEventType + GetWatchpointEventTypeFromEvent (const lldb::SBEvent& event); + + static lldb::SBWatchpoint + GetWatchpointFromEvent (const lldb::SBEvent& event); + +}; + +} // namespace lldb diff --git a/scripts/lldb.swig b/scripts/lldb.swig new file mode 100644 index 000000000000..1d333540728c --- /dev/null +++ b/scripts/lldb.swig @@ -0,0 +1,199 @@ +/* + lldb.swig + + 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. +*/ + +/* Define our module docstring. */ +%define DOCSTRING +"The lldb module contains the public APIs for Python binding. + +Some of the important classes are described here: + +o SBTarget: Represents the target program running under the debugger. +o SBProcess: Represents the process associated with the target program. +o SBThread: Represents a thread of execution. SBProcess contains SBThread(s). +o SBFrame: Represents one of the stack frames associated with a thread. SBThread + contains SBFrame(s). +o SBSymbolContext: A container that stores various debugger related info. +o SBValue: Represents the value of a variable, a register, or an expression. +o SBModule: Represents an executable image and its associated object and symbol + files. SBTarget contains SBModule(s). +o SBBreakpoint: Represents a logical breakpoint and its associated settings. + SBTarget contains SBBreakpoint(s). +o SBSymbol: Represents the symbol possibly associated with a stack frame. +o SBCompileUnit: Represents a compilation unit, or compiled source file. +o SBFunction: Represents a generic function, which can be inlined or not. +o SBBlock: Represents a lexical block. SBFunction contains SBBlock(s). +o SBLineEntry: Specifies an association with a contiguous range of instructions + and a source file location. SBCompileUnit contains SBLineEntry(s)." +%enddef + +// The name of the module to be created. +%module(docstring=DOCSTRING) lldb + +// Parameter types will be used in the autodoc string. +%feature("autodoc", "1"); + +%pythoncode%{ +import uuid +import re +import os + +import six +%} +%include "./Python/python-typemaps.swig" + +/* C++ headers to be included. */ +%{ +#include +#include +%} + +/* The liblldb header files to be included. */ +%{ +#include "lldb/lldb-public.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBAttachInfo.h" +#include "lldb/API/SBBlock.h" +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBCommunication.h" +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBData.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBExecutionContext.h" +#include "lldb/API/SBExpressionOptions.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFileSpecList.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBFunction.h" +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBLanguageRuntime.h" +#include "lldb/API/SBLaunchInfo.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBModuleSpec.h" +#include "lldb/API/SBPlatform.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBQueue.h" +#include "lldb/API/SBQueueItem.h" +#include "lldb/API/SBSection.h" +#include "lldb/API/SBSourceManager.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBSymbol.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBSymbolContextList.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBThreadCollection.h" +#include "lldb/API/SBThreadPlan.h" +#include "lldb/API/SBType.h" +#include "lldb/API/SBTypeCategory.h" +#include "lldb/API/SBTypeEnumMember.h" +#include "lldb/API/SBTypeFilter.h" +#include "lldb/API/SBTypeFormat.h" +#include "lldb/API/SBTypeNameSpecifier.h" +#include "lldb/API/SBTypeSummary.h" +#include "lldb/API/SBTypeSynthetic.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBValueList.h" +#include "lldb/API/SBVariablesOptions.h" +#include "lldb/API/SBWatchpoint.h" +#include "lldb/API/SBUnixSignals.h" + +#include "../source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h" + +#include "../scripts/Python/python-swigsafecast.swig" +%} + +/* Various liblldb typedefs that SWIG needs to know about. */ +#define __extension__ /* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h. */ +/* The ISO C99 standard specifies that in C++ implementations limit macros such + as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */ +#define __STDC_LIMIT_MACROS +%include "stdint.i" + +%include "lldb/lldb-defines.h" +%include "lldb/lldb-enumerations.h" +%include "lldb/lldb-forward.h" +%include "lldb/lldb-types.h" + +/* Forward declaration of SB classes. */ +%include "lldb/API/SBDefines.h" + +/* Python interface files with docstrings. */ +%include "./interface/SBAddress.i" +%include "./interface/SBAttachInfo.i" +%include "./interface/SBBlock.i" +%include "./interface/SBBreakpoint.i" +%include "./interface/SBBreakpointLocation.i" +%include "./interface/SBBroadcaster.i" +%include "./interface/SBCommandInterpreter.i" +%include "./interface/SBCommandReturnObject.i" +%include "./interface/SBCommunication.i" +%include "./interface/SBCompileUnit.i" +%include "./interface/SBData.i" +%include "./interface/SBDebugger.i" +%include "./interface/SBDeclaration.i" +%include "./interface/SBError.i" +%include "./interface/SBEvent.i" +%include "./interface/SBExecutionContext.i" +%include "./interface/SBExpressionOptions.i" +%include "./interface/SBFileSpec.i" +%include "./interface/SBFileSpecList.i" +%include "./interface/SBFrame.i" +%include "./interface/SBFunction.i" +%include "./interface/SBHostOS.i" +%include "./interface/SBInstruction.i" +%include "./interface/SBInstructionList.i" +%include "./interface/SBLanguageRuntime.i" +%include "./interface/SBLaunchInfo.i" +%include "./interface/SBLineEntry.i" +%include "./interface/SBListener.i" +%include "./interface/SBModule.i" +%include "./interface/SBModuleSpec.i" +%include "./interface/SBPlatform.i" +%include "./interface/SBProcess.i" +%include "./interface/SBQueue.i" +%include "./interface/SBQueueItem.i" +%include "./interface/SBSection.i" +%include "./interface/SBSourceManager.i" +%include "./interface/SBStream.i" +%include "./interface/SBStringList.i" +%include "./interface/SBSymbol.i" +%include "./interface/SBSymbolContext.i" +%include "./interface/SBSymbolContextList.i" +%include "./interface/SBTarget.i" +%include "./interface/SBThread.i" +%include "./interface/SBThreadCollection.i" +%include "./interface/SBThreadPlan.i" +%include "./interface/SBType.i" +%include "./interface/SBTypeCategory.i" +%include "./interface/SBTypeEnumMember.i" +%include "./interface/SBTypeFilter.i" +%include "./interface/SBTypeFormat.i" +%include "./interface/SBTypeNameSpecifier.i" +%include "./interface/SBTypeSummary.i" +%include "./interface/SBTypeSynthetic.i" +%include "./interface/SBValue.i" +%include "./interface/SBValueList.i" +%include "./interface/SBVariablesOptions.i" +%include "./interface/SBWatchpoint.i" +%include "./interface/SBUnixSignals.i" + +%include "./Python/python-extensions.swig" + +%include "./Python/python-wrapper.swig" diff --git a/scripts/package-clang-headers.py b/scripts/package-clang-headers.py new file mode 100644 index 000000000000..b28ad0d343ec --- /dev/null +++ b/scripts/package-clang-headers.py @@ -0,0 +1,80 @@ +#! /usr/bin/env python + +# package-clang-headers.py +# +# The Clang module loader depends on built-in headers for the Clang compiler. +# We grab these from the Clang build and move them into the LLDB module. + +# TARGET_DIR is where the lldb framework/shared library gets put. +# LLVM_BUILD_DIR is where LLVM and Clang got built +# LLVM_BUILD_DIR/lib/clang should exist and contain headers + +import os +import re +import shutil +import sys + +if len(sys.argv) != 3: + print "usage: " + sys.argv[0] + " TARGET_DIR LLVM_BUILD_DIR" + sys.exit(1) + +target_dir = sys.argv[1] +llvm_build_dir = sys.argv[2] + +if not os.path.isdir(target_dir): + print target_dir + " doesn't exist" + sys.exit(1) + +if not os.path.isdir(llvm_build_dir): + llvm_build_dir = re.sub ("-macosx-", "-iphoneos-", llvm_build_dir) + +if not os.path.isdir(llvm_build_dir): + llvm_build_dir = re.sub ("-iphoneos-", "-appletvos-", llvm_build_dir) + +if not os.path.isdir(llvm_build_dir): + llvm_build_dir = re.sub ("-appletvos-", "-watchos-", llvm_build_dir) + +if not os.path.isdir(llvm_build_dir): + print llvm_build_dir + " doesn't exist" + sys.exit(1) + +resources = os.path.join(target_dir, "LLDB.framework", "Resources") + +if not os.path.isdir(resources): + print resources + " must exist" + sys.exit(1) + +clang_dir = os.path.join(llvm_build_dir, "lib", "clang") + +if not os.path.isdir(clang_dir): + print clang_dir + " must exist" + sys.exit(1) + +version_dir = None + +for subdir in os.listdir(clang_dir): + if (re.match("^[0-9]+(\.[0-9]+)*$", subdir)): + version_dir = os.path.join(clang_dir, subdir) + break + +if version_dir == None: + print "Couldn't find a subdirectory of the form #(.#)... in " + clang_dir + sys.exit(1) + +if not os.path.isdir(version_dir): + print version_dir + " is not a directory" + sys.exit(1) + +# Just checking... we're actually going to copy all of version_dir +include_dir = os.path.join(version_dir, "include") + +if not os.path.isdir(include_dir): + print version_dir + " is not a directory" + sys.exit(1) + +clang_resources = os.path.join(resources, "Clang") + +if os.path.isdir(clang_resources): + shutil.rmtree(clang_resources) + +shutil.copytree(version_dir, clang_resources) diff --git a/scripts/prepare_bindings.py b/scripts/prepare_bindings.py new file mode 100755 index 000000000000..3165f232e5ec --- /dev/null +++ b/scripts/prepare_bindings.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Prepares language bindings for LLDB build process. Run with --help +to see a description of the supported command line arguments. +""" + +# Python modules: +import argparse +import logging +import os +import platform +import sys + +# LLDB modules: +import use_lldb_suite +from lldbsuite.support import fs + + +def prepare_binding_for_language(scripts_dir, script_lang, options): + """Prepares the binding for a specific language. + + @param scripts_dir the full path to the scripts source directory. + @param script_lang the name of the script language. Should be a child + directory within the scripts dir, and should contain a + prepare_scripts_{script_lang}.py script file in it. + @param options the dictionary of parsed command line options. + + There is no return value. If it returns, the process succeeded; otherwise, + the process will exit where it fails. + """ + # Ensure the language-specific prepare module exists. + script_name = "prepare_binding_{}.py".format(script_lang) + lang_path = os.path.join(scripts_dir, script_lang) + script_path = os.path.join(lang_path, script_name) + if not os.path.exists(script_path): + logging.error( + "failed to find prepare script for language '%s' at '%s'", + script_lang, + script_path) + sys.exit(-9) + + # Include this language-specific directory in the Python search + # path. + sys.path.append(os.path.normcase(lang_path)) + + # Execute the specific language script + module_name = os.path.splitext(script_name)[0] + module = __import__(module_name) + module.main(options) + + # Remove the language-specific directory from the Python search path. + sys.path.remove(os.path.normcase(lang_path)) + + +def prepare_all_bindings(options): + """Prepares bindings for each of the languages supported. + + @param options the parsed arguments from the command line + + @return the exit value for the program. 0 is success, all othes + indicate some kind of failure. + """ + # Check for the existence of the SWIG scripts folder + scripts_dir = os.path.join(options.src_root, "scripts") + if not os.path.exists(scripts_dir): + logging.error("failed to find scripts dir: '%s'", scripts_dir) + sys.exit(-8) + + child_dirs = ["Python"] + + # Iterate script directory find any script language directories + for script_lang in child_dirs: + logging.info("executing language script for: '%s'", script_lang) + prepare_binding_for_language(scripts_dir, script_lang, options) + + +def process_args(args): + """Returns options processed from the provided command line. + + @param args the command line to process. + """ + + # Setup the parser arguments that are accepted. + parser = argparse.ArgumentParser( + description="Prepare language bindings for LLDB build.") + + # Arguments to control logging verbosity. + parser.add_argument( + "--debug", "-d", + action="store_true", + help="Set program logging level to DEBUG.") + parser.add_argument( + "--verbose", "-v", + action="count", + default=0, + help=( + "Increase logging verbosity level. Default: only error and " + "higher are displayed. Each -v increases level of verbosity.")) + + # Arguments to control whether we're building an OS X-style + # framework. This is the opposite of the older "-m" (makefile) + # option. + parser.add_argument( + "--config-build-dir", + "--cfgBldDir", + help=( + "Configuration build dir, will use python module path " + "if unspecified.")) + parser.add_argument( + "--find-swig", + action="store_true", + help=( + "Indicates the swig executable should be searched for " + "if not eplicitly provided. Either this or the explicit " + "swig executable option must be provided.")) + parser.add_argument( + "--framework", + action="store_true", + help="Prepare as OS X-style framework.") + parser.add_argument( + "--generate-dependency-file", + "-M", + action="store_true", + help="Make the dependency (.d) file for the wrappers.") + parser.add_argument( + "--prefix", + help="Override path where the LLDB module is placed.") + parser.add_argument( + "--src-root", + "--srcRoot", + "-s", + # Default to the parent directory of this script's directory. + default=os.path.abspath( + os.path.join( + os.path.dirname(os.path.realpath(__file__)), + os.path.pardir)), + help="Specifies the LLDB source root directory.") + parser.add_argument( + "--swig-executable", + "--swigExecutable", + help="Path to the swig executable.") + parser.add_argument( + "--target-dir", + "--targetDir", + required=True, + help=( + "Specifies the build dir where the language binding " + "should be placed")) + + # Process args. + options = parser.parse_args(args) + + # Set logging level based on verbosity count. + if options.debug: + log_level = logging.DEBUG + else: + # See logging documentation for error levels. We'll default + # to showing ERROR or higher error messages. For each -v + # specified, we'll shift to the next lower-priority log level. + log_level = logging.ERROR - 10 * options.verbose + if log_level < logging.NOTSET: + # Displays all logged messages. + log_level = logging.NOTSET + logging.basicConfig(level=log_level) + logging.info("logging is using level: %d", log_level) + + return options + + +def main(args): + """Drives the main script preparation steps. + + @param args list of command line arguments. + """ + # Process command line arguments. + options = process_args(args) + logging.debug("Processed args: options=%s", options) + + # Ensure we have a swig executable. + if not options.swig_executable or len(options.swig_executable) == 0: + if options.find_swig: + try: + options.swig_executable = fs.find_executable("swig") + except Exception as e: + logging.error("Unable to find swig executable: %s" % e.message) + sys.exit(-6) + else: + logging.error( + "The --find-swig option must be specified " + "when the swig executable location is not " + "explicitly provided.") + sys.exit(-12) + + # Check if the swig file exists. + swig_path = os.path.normcase( + os.path.join(options.src_root, "scripts", "lldb.swig")) + if not os.path.isfile(swig_path): + logging.error("swig file not found at '%s'", swig_path) + sys.exit(-3) + + # Prepare bindings for each supported language binding. + # This will error out if it doesn't succeed. + prepare_all_bindings(options) + sys.exit(0) + +if __name__ == "__main__": + # Run the main driver loop. + main(sys.argv[1:]) diff --git a/scripts/sed-sources b/scripts/sed-sources new file mode 100755 index 000000000000..c67fb3319adb --- /dev/null +++ b/scripts/sed-sources @@ -0,0 +1,251 @@ +#!/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 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 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 +); + + +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(); diff --git a/scripts/shush b/scripts/shush new file mode 100755 index 000000000000..a2dd6c8c58d0 --- /dev/null +++ b/scripts/shush @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import sys +import subprocess +import tempfile +import time +import os + +class Printer(object): + def __init__(self): + pass + + @classmethod + def write(self, message): + sys.stdout.write("%s\n" % message) + sys.stdout.flush() + +def command(): + return ' '.join(sys.argv[1:]) + +def tmpfile(suffix=None): + if suffix is None: suffix = "" + return tempfile.NamedTemporaryFile(prefix='shush', suffix=suffix, delete=False) + +def launch(cmd="/bin/ls", sin=None, sout=None): + class Process(object): + def __init__(self, p, i, o): + self.p = p + self.stdin = i + self.stdout = o + + def poll(self): + self.returncode = self.p.poll() + return self.returncode + + return Process(subprocess.Popen(cmd, shell=True, stdin=sin, stdout=sout, stderr=subprocess.STDOUT), sin, sout) + +def wait(p): + while p.poll() is None: + time.sleep(5) + Printer.write("still running @ %s..." % time.strftime("%Y%m%d%H%M%S")) # fool Xcode into thinking that I am doing something... + return p + +def exit(p): + code = p.returncode + if code != 0: + Printer.write("error: sucks to be you") + Printer.write("error: shushed process failed - go check %s for details" % (p.stdout.name)) + else: + Printer.write("shush: success - output is going away") + try: + os.remove(p.stdout.name) + finally: + pass + sys.exit(code) + +def main(): + out = tmpfile() + cmd = command() + Printer.write("shush: launching '%s' - std{out|err}=%s" % (cmd, out.name)) + p = wait(launch(cmd=cmd, sout=out)) + exit(p) + +main() diff --git a/scripts/swig_bot.py b/scripts/swig_bot.py new file mode 100644 index 000000000000..95f4eb8d71b1 --- /dev/null +++ b/scripts/swig_bot.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +""" +SWIG generation top-level script. Supports both local and remote generation +of SWIG bindings for multiple languages. +""" + +# Python modules +import argparse +import logging +import sys +import traceback + +# LLDB modules +import use_lldb_suite + +# swig_bot modules +from swig_bot_lib import client +from swig_bot_lib import server + +def process_args(args): + parser = argparse.ArgumentParser( + description='Run swig-bot client or server.') + + # Create and populate subparser arguments for when swig_bot is + # run in client or server mode + subparsers = parser.add_subparsers( + help="Pass --help to a sub-command to print detailed usage") + client_parser = subparsers.add_parser("client", + help="Run SWIG generation client") + client.add_subparser_args(client_parser) + client_parser.set_defaults(func=run_client) + + server_parser = subparsers.add_parser("server", + help="Run SWIG generation server") + server.add_subparser_args(server_parser) + server_parser.set_defaults(func=run_server) + + # Arguments to control logging verbosity. + parser.add_argument( + "--verbose", "-v", + action="store_true", + default=False, + help="Increase logging verbosity level.") + + options = parser.parse_args(args) + # Set logging level. + if options.verbose: + log_level = logging.DEBUG + else: + log_level = logging.NOTSET + logging.basicConfig(level=log_level) + logging.info("logging is using level: %d", log_level) + + return options + +def run_client(options): + logging.info("Running swig_bot in client mode") + client.finalize_subparser_options(options) + client.run(options) + +def run_server(options): + logging.info("Running swig_bot in server mode") + server.finalize_subparser_options(options) + server.run(options) + +if __name__ == "__main__": + options = process_args(sys.argv[1:]) + try: + if options.func is None: + logging.error("Unknown mode specified. Expected client or server.") + sys.exit(-1) + else: + options.func(options) + except KeyboardInterrupt as e: + logging.info("Ctrl+C received. Shutting down...") + sys.exit(-1) + except Exception as e: + error = traceback.format_exc() + logging.error("An error occurred running swig-bot.") + logging.error(error) diff --git a/scripts/swig_bot_lib/__init__.py b/scripts/swig_bot_lib/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scripts/swig_bot_lib/client.py b/scripts/swig_bot_lib/client.py new file mode 100644 index 000000000000..9bf55b42b666 --- /dev/null +++ b/scripts/swig_bot_lib/client.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python + +""" +SWIG generation client. Supports both local and remote generation of SWIG +bindings for multiple languages. +""" + +# Future imports +from __future__ import absolute_import +from __future__ import print_function + +# Python modules +import argparse +import io +import logging +import os +import socket +import struct +import sys + +# LLDB modules +import use_lldb_suite +from lldbsuite.support import fs +from lldbsuite.support import sockutil + +# package imports +from . import local +from . import remote + +default_ip = "127.0.0.1" +default_port = 8537 + +def add_subparser_args(parser): + """Returns options processed from the provided command line. + + @param args the command line to process. + """ + + # A custom action used by the --local command line option. It can be + # used with either 0 or 1 argument. If used with 0 arguments, it + # searches for a copy of swig located on the physical machine. If + # used with 1 argument, the argument is the path to a swig executable. + class FindLocalSwigAction(argparse.Action): + def __init__(self, option_strings, dest, **kwargs): + super(FindLocalSwigAction, self).__init__( + option_strings, dest, nargs='?', **kwargs) + def __call__(self, parser, namespace, values, option_string=None): + swig_exe = None + if values is None: + swig_exe = fs.find_executable('swig') + else: + swig_exe = values + setattr(namespace, self.dest, os.path.normpath(swig_exe)) + + # A custom action used by the --remote command line option. It can be + # used with either 0 or 1 arguments. If used with 0 arguments it chooses + # a default connection string. If used with one argument it is a string + # of the form `ip_address[:port]`. If the port is unspecified, the + # default port is used. + class RemoteIpAction(argparse.Action): + def __init__(self, option_strings, dest, **kwargs): + super(RemoteIpAction, self).__init__( + option_strings, dest, nargs='?', **kwargs) + def __call__(self, parser, namespace, values, option_string=None): + ip_port = None + if values is None: + ip_port = (default_ip, default_port) + else: + result = values.split(':') + if len(result)==1: + ip_port = (result[0], default_port) + elif len(result)==2: + ip_port = (result[0], int(result[1])) + else: + raise ValueError("Invalid connection string") + setattr(namespace, self.dest, ip_port) + + parser.add_argument( + "--local", + action=FindLocalSwigAction, + dest="swig_executable", + help=( + "Run the copy of swig at the specified location, or search PATH" + "if the location is omitted")) + + parser.add_argument( + "--remote", + action=RemoteIpAction, + help=( + "Use the given connection string to connect to a remote " + "generation service")) + + parser.add_argument( + "--src-root", + required=True, + help="The root folder of the LLDB source tree.") + + parser.add_argument( + "--target-dir", + default=os.getcwd(), + help=( + "Specifies the build dir where the language binding " + "should be placed")) + + parser.add_argument( + "--language", + dest="languages", + action="append", + help="Specifies the language to generate bindings for") + +def finalize_subparser_options(options): + if options.languages is None: + options.languages = ['python'] + + if options.remote is None and options.swig_executable is None: + logging.error("Must specify either --local or --remote") + sys.exit(-3) + + return options + +def establish_remote_connection(ip_port): + logging.debug("Creating socket...") + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + logging.info("Connecting to server {} on port {}" + .format(ip_port[0], ip_port[1])) + s.connect(ip_port) + logging.info("Connection established...") + return s + +def transmit_request(connection, packed_input): + logging.info("Sending {} bytes of compressed data." + .format(len(packed_input))) + connection.sendall(struct.pack("!I", len(packed_input))) + connection.sendall(packed_input) + logging.info("Awaiting response.") + response_len = struct.unpack("!I", sockutil.recvall(connection, 4))[0] + logging.debug("Expecting {} byte response".format(response_len)) + response = sockutil.recvall(connection, response_len) + return response + +def handle_response(options, connection, response): + logging.debug("Received {} byte response.".format(len(response))) + logging.debug("Creating output directory {}" + .format(options.target_dir)) + os.makedirs(options.target_dir, exist_ok=True) + + logging.info("Unpacking response archive into {}" + .format(options.target_dir)) + local.unpack_archive(options.target_dir, response) + response_file_path = os.path.normpath( + os.path.join(options.target_dir, "swig_output.json")) + if not os.path.isfile(response_file_path): + logging.error("Response file '{}' does not exist." + .format(response_file_path)) + return + try: + response = remote.deserialize_response_status( + io.open(response_file_path)) + if response[0] != 0: + logging.error("An error occurred during generation. Status={}" + .format(response[0])) + logging.error(response[1]) + else: + logging.info("SWIG generation successful.") + if len(response[1]) > 0: + logging.info(response[1]) + finally: + os.unlink(response_file_path) + +def run(options): + if options.remote is None: + logging.info("swig bot client using local swig installation at '{}'" + .format(options.swig_executable)) + if not os.path.isfile(options.swig_executable): + logging.error("Swig executable '{}' does not exist." + .format(options.swig_executable)) + config = local.LocalConfig() + config.languages = options.languages + config.src_root = options.src_root + config.target_dir = options.target_dir + config.swig_executable = options.swig_executable + local.generate(config) + else: + logging.info("swig bot client using remote generation with server '{}'" + .format(options.remote)) + connection = None + try: + config = remote.generate_config(options.languages) + logging.debug("Generated config json {}".format(config)) + inputs = [("include/lldb", ".h"), + ("include/lldb/API", ".h"), + ("scripts", ".swig"), + ("scripts/Python", ".swig"), + ("scripts/interface", ".i")] + zip_data = io.BytesIO() + packed_input = local.pack_archive(zip_data, options.src_root, inputs) + logging.info("(null) -> config.json") + packed_input.writestr("config.json", config) + packed_input.close() + connection = establish_remote_connection(options.remote) + response = transmit_request(connection, zip_data.getvalue()) + handle_response(options, connection, response) + finally: + if connection is not None: + connection.close() \ No newline at end of file diff --git a/scripts/swig_bot_lib/local.py b/scripts/swig_bot_lib/local.py new file mode 100644 index 000000000000..7cca0b3cabbb --- /dev/null +++ b/scripts/swig_bot_lib/local.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +""" +Shared functionality used by `client` and `server` when generating or preparing +to generate SWIG on the local machine. +""" + +# Future imports +from __future__ import absolute_import +from __future__ import print_function + +# Python modules +import argparse +import imp +import io +import logging +import os +import subprocess +import sys +import tempfile +import zipfile + +# LLDB modules +import use_lldb_suite + +# Package imports +from lldbsuite.support import fs + +class LocalConfig(object): + src_root = None + target_dir = None + languages = None + swig_executable = None + +def pack_archive(bytes_io, src_root, filters): + logging.info("Creating input file package...") + zip_file = None + try: + # It's possible that a custom-built interpreter will not have the + # standard zlib module. If so, we can only store, not compress. By + # try to compress since we usually have a standard Python distribution. + zip_file = zipfile.ZipFile(bytes_io, mode='w', + compression=zipfile.ZIP_DEFLATED) + except RuntimeError: + zip_file = zipfile.ZipFile(bytes_io, mode='w', + compression=zipfile.ZIP_STORED) + archive_entries = [] + if filters is not None: + def filter_func(t): + subfolder = t[0] + ext = t[1] + full_path = os.path.normpath(os.path.join(src_root, subfolder)) + candidates = [os.path.normpath(os.path.join(full_path, f)) + for f in os.listdir(full_path)] + actual = filter( + lambda f : os.path.isfile(f) and os.path.splitext(f)[1] == ext, + candidates) + return (subfolder, map(lambda f : os.path.basename(f), actual)) + archive_entries = map(filter_func, filters) + else: + for (root, dirs, files) in os.walk(src_root): + logging.debug("Adding files {} from directory {} to output package" + .format(files, root)) + if len(files) > 0: + rel_root = os.path.relpath(root, src_root) + archive_entries.append((rel_root, files)) + + archive_entries = list(archive_entries) + for entry in archive_entries: + subfolder = entry[0] + files = list(entry[1]) + for file in files: + rel_path = os.path.normpath(os.path.join(subfolder, file)) + full_path = os.path.join(src_root, rel_path) + logging.info("{} -> {}".format(full_path, rel_path)) + zip_file.write(full_path, rel_path) + + return zip_file + +def unpack_archive(folder, archive_bytes): + zip_data = io.BytesIO(archive_bytes) + logging.debug("Opening zip archive...") + zip_file = zipfile.ZipFile(zip_data, mode='r') + zip_file.extractall(folder) + zip_file.close() + +def generate(options): + include_folder = os.path.join(options.src_root, "include") + in_file = os.path.join(options.src_root, "scripts", "lldb.swig") + include_folder = os.path.normcase(include_folder) + + for lang in options.languages: + lang = lang.lower() + out_dir = os.path.join(options.target_dir, lang.title()) + if not os.path.exists(out_dir): + os.makedirs(out_dir) + out_file = os.path.join(out_dir, "LLDBWrap{}.cpp".format(lang.title())) + swig_command = [ + options.swig_executable, + "-c++", + ] + swig_command.append("-" + lang) + if lang == "python": + swig_command.append("-threads") + + swig_command.extend([ + "-I" + include_folder, + "-D__STDC_LIMIT_MACROS", + "-D__STDC_CONSTANT_MACROS", + "-outdir", out_dir, + "-o", out_file, + in_file + ]) + + logging.info("generating swig {} bindings into {}" + .format(lang, out_dir)) + logging.debug("swig command line: {}".format(swig_command)) + try: + # Execute swig + swig_output = subprocess.check_output( + swig_command, stderr=subprocess.STDOUT, universal_newlines=True) + + logging.info("swig generation succeeded") + if swig_output is not None and len(swig_output) > 0: + logging.info("swig output: %s", swig_output) + return (0, swig_output) + except subprocess.CalledProcessError as e: + logging.error("An error occurred executing swig. returncode={}" + .format(e.returncode)) + logging.error(e.output) + return (e.returncode, e.output) \ No newline at end of file diff --git a/scripts/swig_bot_lib/remote.py b/scripts/swig_bot_lib/remote.py new file mode 100644 index 000000000000..590a873d6270 --- /dev/null +++ b/scripts/swig_bot_lib/remote.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +""" +Shared functionality used by `client` and `server` when dealing with +remote transmission +""" + +# Future imports +from __future__ import absolute_import +from __future__ import print_function + +# Python modules +import json +import logging +import os +import socket +import struct +import sys + +# LLDB modules +import use_lldb_suite + +def generate_config(languages): + config = {"languages": languages} + return json.dumps(config) + +def parse_config(json_reader): + json_data = json_reader.read() + options_dict = json.loads(json_data) + return options_dict + +def serialize_response_status(status): + status = {"retcode": status[0], "output": status[1]} + return json.dumps(status) + +def deserialize_response_status(json_reader): + json_data = json_reader.read() + response_dict = json.loads(json_data) + return (response_dict["retcode"], response_dict["output"]) diff --git a/scripts/swig_bot_lib/server.py b/scripts/swig_bot_lib/server.py new file mode 100644 index 000000000000..cc25cee4d4b1 --- /dev/null +++ b/scripts/swig_bot_lib/server.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +""" +SWIG generation server. Listens for connections from swig generation clients +and runs swig in the requested fashion, sending back the results. +""" + +# Future imports +from __future__ import absolute_import +from __future__ import print_function + +# Python modules +import argparse +import io +import logging +import os +import select +import shutil +import socket +import struct +import sys +import tempfile +import traceback + +# LLDB modules +import use_lldb_suite +from lldbsuite.support import fs +from lldbsuite.support import sockutil + +# package imports +from . import local +from . import remote + +default_port = 8537 + +def add_subparser_args(parser): + parser.add_argument( + "--port", + action="store", + default=default_port, + help=("The local port to bind to")) + + parser.add_argument( + "--swig-executable", + action="store", + default=fs.find_executable("swig"), + dest="swig_executable") + +def finalize_subparser_options(options): + pass + +def initialize_listening_socket(options): + logging.debug("Creating socket...") + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + logging.info("Binding to ip address '', port {}".format(options.port)) + s.bind(('', options.port)) + + logging.debug("Putting socket in listen mode...") + s.listen() + return s + +def accept_once(sock, options): + logging.debug("Waiting for connection...") + while True: + rlist, wlist, xlist = select.select([sock], [], [], 0.5) + if not rlist: + continue + + client, addr = sock.accept() + logging.info("Received connection from {}".format(addr)) + data_size = struct.unpack("!I", sockutil.recvall(client, 4))[0] + logging.debug("Expecting {} bytes of data from client" + .format(data_size)) + data = sockutil.recvall(client, data_size) + logging.info("Received {} bytes of data from client" + .format(len(data))) + + pack_location = None + try: + tempfolder = os.path.join(tempfile.gettempdir(), "swig-bot") + os.makedirs(tempfolder, exist_ok=True) + + pack_location = tempfile.mkdtemp(dir=tempfolder) + logging.debug("Extracting archive to {}".format(pack_location)) + + local.unpack_archive(pack_location, data) + logging.debug("Successfully unpacked archive...") + + config_file = os.path.normpath(os.path.join(pack_location, + "config.json")) + parsed_config = remote.parse_config(io.open(config_file)) + config = local.LocalConfig() + config.languages = parsed_config["languages"] + config.swig_executable = options.swig_executable + config.src_root = pack_location + config.target_dir = os.path.normpath( + os.path.join(config.src_root, "output")) + logging.info( + "Running swig. languages={}, swig={}, src_root={}, target={}" + .format(config.languages, config.swig_executable, + config.src_root, config.target_dir)) + + status = local.generate(config) + logging.debug("Finished running swig. Packaging up files {}" + .format(os.listdir(config.target_dir))) + zip_data = io.BytesIO() + zip_file = local.pack_archive(zip_data, config.target_dir, None) + response_status = remote.serialize_response_status(status) + logging.debug("Sending response status {}".format(response_status)) + logging.info("(swig output) -> swig_output.json") + zip_file.writestr("swig_output.json", response_status) + + zip_file.close() + response_data = zip_data.getvalue() + logging.info("Sending {} byte response".format(len(response_data))) + client.sendall(struct.pack("!I", len(response_data))) + client.sendall(response_data) + finally: + if pack_location is not None: + logging.debug("Removing temporary folder {}" + .format(pack_location)) + shutil.rmtree(pack_location) + +def accept_loop(sock, options): + while True: + try: + accept_once(sock, options) + except Exception as e: + error = traceback.format_exc() + logging.error("An error occurred while processing the connection.") + logging.error(error) + +def run(options): + print(options) + sock = initialize_listening_socket(options) + accept_loop(sock, options) + return options diff --git a/scripts/use_lldb_suite.py b/scripts/use_lldb_suite.py new file mode 100644 index 000000000000..63a098cea220 --- /dev/null +++ b/scripts/use_lldb_suite.py @@ -0,0 +1,22 @@ +import inspect +import os +import sys + +def find_lldb_root(): + lldb_root = os.path.dirname(inspect.getfile(inspect.currentframe())) + while True: + lldb_root = os.path.dirname(lldb_root) + if lldb_root is None: + return None + + test_path = os.path.join(lldb_root, "use_lldb_suite_root.py") + if os.path.isfile(test_path): + return lldb_root + return None + +lldb_root = find_lldb_root() +if lldb_root is not None: + import imp + module = imp.find_module("use_lldb_suite_root", [lldb_root]) + if module is not None: + imp.load_module("use_lldb_suite_root", *module) diff --git a/scripts/utilsArgsParse.py b/scripts/utilsArgsParse.py new file mode 100644 index 000000000000..e762edccc30d --- /dev/null +++ b/scripts/utilsArgsParse.py @@ -0,0 +1,139 @@ +""" Utility module handle program args and give help + + -------------------------------------------------------------------------- + File: utilsArgsParse.py + + Overview: Python module to parse and validate program parameters + against those required by the program whether mandatory + or optional. + Also give help information on arguments required by the + program. + + Gotchas: None. + + Copyright: None. + -------------------------------------------------------------------------- + +""" + +# Python modules: +import getopt # Parse command line arguments + +# Third party modules: + +# In-house modules: + +# Instantiations: + +# User facing text: +strMsgErrorInvalidParameters = "Invalid parameters entered, -h for help. \nYou entered:\n" +strMsgErrorInvalidNoParams = "No parameters entered, -h for help\n" +strMsgErrorNumberParameters = "Number of parameters entered incorrect, %d parameters required. You entered:\n" +strMsgArgFileNotImplemented = "Sorry the --argFile is not implemented" + +#++--------------------------------------------------------------------------- +# Details: Validate the arguments passed in against the mandatory and +# optional arguments specified. The argument format for the parameters +# is required to work with the module getopt function getopt(). +# Parameter vDictArgReq specifies which parameters are mandatory and +# which are optional. The format is for example: +# dictArgReq = {"-h": "o", # o = optional, m = mandatory +# "-m": "m", +# "--targetDir": "m", +# "--cfgBldDir": "o" } +# Args: vArgv - (R) List of arguments and values. +# vstrListArgs - (R) List of small arguments. +# vListLongArgs - (R) List of long arguments. +# vDictArgReq - (R) Map of arguments required. +# vstrHelpInfo - (R) Formatted help text. +# Returns: Int - 0 success. +# 1 success display information, do nothing else. +# -1 error invalid parameters. +# -2 error incorrect number of mandatory parameters. +# Dict - Map of arguments names to argument values +# Str - Error message. +# Throws: None. +#-- +def parse(vArgv, vstrListArgs, vListLongArgs, vDictArgReq, vstrHelpInfo): + dictArgs = {} + dictDummy = {} + strDummy = "" + + # Validate parameters above and error on not recognised + try: + dictOptsNeeded, dictArgsLeftOver = getopt.getopt(vArgv, + vstrListArgs, + vListLongArgs) + except getopt.GetoptError: + strMsg = strMsgErrorInvalidParameters + strMsg += str(vArgv) + return (-1, dictDummy, strMsg) + + if len(dictOptsNeeded) == 0: + strMsg = strMsgErrorInvalidNoParams + return (-1, dictDummy, strMsg) + + # Look for help -h before anything else + for opt, arg in dictOptsNeeded: + if opt == '-h': + return (1, dictDummy, vstrHelpInfo) + + # Look for the --argFile if found ignore other command line arguments + for opt, arg in dictOptsNeeded: + if opt == '--argsFile': + return (1, dictDummy, strMsgArgFileNotImplemented) + + # Count the number of mandatory args required (if any one found) + countMandatory = 0 + for opt, man in list(vDictArgReq.items()): + if man == "m": + countMandatory = countMandatory + 1 + + # Extract short args + listArgs = [] + for arg in vstrListArgs: + if (arg == '-h') or (arg == ':'): + continue + listArgs.append(arg) + + # Append to arg dictionary the option and its value + bFoundNoInputValue = False + countMandatoryOpts = 0 + for opt, val in dictOptsNeeded: + match = 0 + for arg in listArgs: + argg = "-" + arg + if opt == argg: + if "m" == vDictArgReq[opt]: + countMandatoryOpts = countMandatoryOpts + 1 + dictArgs[opt] = val + match = 1 + break + if match == 0: + for arg in vListLongArgs: + argg = "--" + arg[:arg.__len__() - 1] + if opt == argg: + if "m" == vDictArgReq[opt]: + countMandatoryOpts = countMandatoryOpts + 1 + dictArgs[opt] = val + if val.__len__() == 0: + bFoundNoInputValue = True + break + + # Do any of the long arguments not have a value attached + if bFoundNoInputValue: + strMsg = strMsgErrorInvalidParameters + strMsg += str(vArgv) + return (-1, dictDummy, strMsg) + + # Debug only + #print countMandatoryOpts + #print countMandatory + + # Do we have the exact number of mandatory arguments + if (countMandatoryOpts > 0) and (countMandatory != countMandatoryOpts): + strMsg = strMsgErrorNumberParameters % countMandatory + strMsg += str(vArgv) + return (-2, dictDummy, strMsg) + + return (0, dictArgs, strDummy) diff --git a/scripts/utilsDebug.py b/scripts/utilsDebug.py new file mode 100644 index 000000000000..4b5eb7fa3e70 --- /dev/null +++ b/scripts/utilsDebug.py @@ -0,0 +1,120 @@ +""" Utility module to help debug Python scripts + + -------------------------------------------------------------------------- + File: utilsDebug.py + + Overview: Python module to supply functions to help debug Python + scripts. + Gotchas: None. + Copyright: None. + -------------------------------------------------------------------------- +""" + +# Python modules: +import sys + +# Third party modules: + +# In-house modules: + +# Instantiations: + +#----------------------------------------------------------------------------- +# Details: Class to implement simple stack function trace. Instantiation the +# class as the first function you want to trace. Example: +# obj = utilsDebug.CDebugFnVerbose("validate_arguments()") +# Gotchas: This class will not work in properly in a multi-threaded +# environment. +# Authors: Illya Rudkin 28/11/2013. +# Changes: None. +#-- +class CDebugFnVerbose(object): + # Public static properties: + bVerboseOn = False # True = turn on function tracing, False = turn off. + + # Public: + #++------------------------------------------------------------------------ + # Details: CDebugFnVerbose constructor. + # Type: Method. + # Args: vstrFnName - (R) Text description i.e. a function name. + # Return: None. + # Throws: None. + #-- + # CDebugFnVerbose(vstrFnName) + + #++------------------------------------------------------------------------ + # Details: Print out information on the object specified. + # Type: Method. + # Args: vstrText - (R) Some helper text description. + # vObject - (R) Some Python type object. + # Return: None. + # Throws: None. + #-- + def dump_object(self, vstrText, vObject): + if CDebugFnVerbose.bVerboseOn == False: + return + sys.stdout.write("%d%s> Dp: %s" % (CDebugFnVerbose.__nLevel, self.__get_dots(), + vstrText)) + print(vObject) + + #++------------------------------------------------------------------------ + # Details: Print out some progress text given by the client. + # Type: Method. + # Args: vstrText - (R) Some helper text description. + # Return: None. + # Throws: None. + #-- + def dump_text(self, vstrText): + if CDebugFnVerbose.bVerboseOn == False: + return + print(("%d%s> Dp: %s" % (CDebugFnVerbose.__nLevel, self.__get_dots(), + vstrText))) + + # Private methods: + def __init__(self, vstrFnName): + self.__indent_out(vstrFnName) + + #++------------------------------------------------------------------------ + # Details: Build an indentation string of dots based on the __nLevel. + # Type: Method. + # Args: None. + # Return: Str - variable length string. + # Throws: None. + #-- + def __get_dots(self): + return "".join("." for i in range(0, CDebugFnVerbose.__nLevel)) + + #++------------------------------------------------------------------------ + # Details: Build and print out debug verbosity text indicating the function + # just exited from. + # Type: Method. + # Args: None. + # Return: None. + # Throws: None. + #-- + def __indent_back(self): + if CDebugFnVerbose.bVerboseOn: + print(("%d%s< fn: %s" % (CDebugFnVerbose.__nLevel, self.__get_dots(), + self.__strFnName))) + CDebugFnVerbose.__nLevel -= 1 + + #++------------------------------------------------------------------------ + # Details: Build and print out debug verbosity text indicating the function + # just entered. + # Type: Method. + # Args: vstrFnName - (R) Name of the function entered. + # Return: None. + # Throws: None. + #-- + def __indent_out(self, vstrFnName): + CDebugFnVerbose.__nLevel += 1 + self.__strFnName = vstrFnName + if CDebugFnVerbose.bVerboseOn: + print(("%d%s> fn: %s" % (CDebugFnVerbose.__nLevel, self.__get_dots(), + self.__strFnName))) + + # Private statics attributes: + __nLevel = 0 # Indentation level counter + + # Private attributes: + __strFnName = "" diff --git a/scripts/utilsOsType.py b/scripts/utilsOsType.py new file mode 100644 index 000000000000..a2f0563bf895 --- /dev/null +++ b/scripts/utilsOsType.py @@ -0,0 +1,90 @@ +""" Utility module to determine the OS Python running on + + -------------------------------------------------------------------------- + File: utilsOsType.py + + Overview: Python module to supply functions and an enumeration to + help determine the platform type, bit size and OS currently + being used. + -------------------------------------------------------------------------- + +""" + +# Python modules: +import sys # Provide system information + +# Third party modules: + +# In-house modules: + +# Instantiations: + +# Enumerations: +#----------------------------------------------------------------------------- +# Details: Class to implement a 'C' style enumeration type. +# Gotchas: None. +# Authors: Illya Rudkin 28/11/2013. +# Changes: None. +#-- +if sys.version_info.major >= 3: + from enum import Enum + class EnumOsType(Enum): + Unknown = 0 + Darwin = 1 + FreeBSD = 2 + Linux = 3 + NetBSD = 4 + Windows = 5 +else: + class EnumOsType(object): + values = ["Unknown", + "Darwin", + "FreeBSD", + "Linux", + "NetBSD", + "Windows"] + class __metaclass__(type): +#++--------------------------------------------------------------------------- +# Details: Fn acts as an enumeration. +# Args: vName - (R) Enumeration to match. +# Returns: Int - Matching enumeration/index. +# Throws: None. +#-- + def __getattr__(cls, vName): + return cls.values.index(vName) + +#++--------------------------------------------------------------------------- +# Details: Reverse fast lookup of the values list. +# Args: vI - (R) Index / enumeration. +# Returns: Str - text description matching enumeration. +# Throws: None. +#-- + def name_of(cls, vI): + return EnumOsType.values[vI] + +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- + +#++--------------------------------------------------------------------------- +# Details: Determine what operating system is currently running on. +# Args: None. +# Returns: EnumOsType - The OS type being used ATM. +# Throws: None. +#-- +def determine_os_type(): + eOSType = EnumOsType.Unknown + + strOS = sys.platform + if strOS == "darwin": + eOSType = EnumOsType.Darwin + elif strOS.startswith("freebsd"): + eOSType = EnumOsType.FreeBSD + elif strOS.startswith("linux"): + eOSType = EnumOsType.Linux + elif strOS.startswith("netbsd"): + eOSType = EnumOsType.NetBSD + elif strOS == "win32": + eOSType = EnumOsType.Windows + + return eOSType diff --git a/scripts/verify_api.py b/scripts/verify_api.py new file mode 100755 index 000000000000..e636cdda6498 --- /dev/null +++ b/scripts/verify_api.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +import commands +import optparse +import os +import os.path +import re +import sys + +def extract_exe_symbol_names (arch, exe_path, match_str): + command = 'dsymutil --arch %s -s "%s" | grep "%s" | colrm 1 69' % (arch, exe_path, match_str) + (command_exit_status, command_output) = commands.getstatusoutput(command) + if command_exit_status == 0: + if command_output: + return command_output[0:-1].split("'\n") + else: + print 'error: command returned no output' + else: + print 'error: command failed with exit status %i\n command: %s' % (command_exit_status, command) + return list() + +def verify_api(all_args): + '''Verify the API in the specified library is valid given one or more binaries.''' + usage = "usage: verify_api --library [ --library ...] executable1 [executable2 ...]" + description='''Verify the API in the specified library is valid given one or more binaries. + + Example: + + verify_api.py --library ~/Documents/src/lldb/build/Debug/LLDB.framework/LLDB --arch x86_64 /Applications/Xcode.app/Contents/PlugIns/DebuggerLLDB.ideplugin/Contents/MacOS/DebuggerLLDB --api-regex lldb + ''' + parser = optparse.OptionParser(description=description, prog='verify_api',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-a', '--arch', type='string', action='append', dest='archs', help='architecure to use when checking the api') + parser.add_option('-r', '--api-regex', type='string', dest='api_regex_str', help='Exclude any undefined symbols that do not match this regular expression when searching for missing APIs.') + parser.add_option('-l', '--library', type='string', action='append', dest='libraries', help='Specify one or more libraries that will contain all needed APIs for the executables.') + (options, args) = parser.parse_args(all_args) + + api_external_symbols = list() + if options.archs: + for arch in options.archs: + for library in options.libraries: + external_symbols = extract_exe_symbol_names(arch, library, "( SECT EXT)"); + if external_symbols: + for external_symbol in external_symbols: + api_external_symbols.append(external_symbol) + else: + sys.exit(1) + else: + print 'error: must specify one or more architectures with the --arch option' + sys.exit(4) + if options.verbose: + print "API symbols:" + for (i, external_symbol) in enumerate(api_external_symbols): + print "[%u] %s" % (i, external_symbol) + + api_regex = None + if options.api_regex_str: + api_regex = re.compile(options.api_regex_str) + + for arch in options.archs: + for exe_path in args: + print 'Verifying (%s) "%s"...' % (arch, exe_path) + exe_errors = 0 + undefined_symbols = extract_exe_symbol_names(arch, exe_path, "( UNDF EXT)"); + for undefined_symbol in undefined_symbols: + if api_regex: + match = api_regex.search(undefined_symbol) + if not match: + if options.verbose: + print 'ignoring symbol: %s' % (undefined_symbol) + continue + if undefined_symbol in api_external_symbols: + if options.verbose: + print 'verified symbol: %s' % (undefined_symbol) + else: + print 'missing symbol: %s' % (undefined_symbol) + exe_errors += 1 + if exe_errors: + print 'error: missing %u API symbols from %s' % (exe_errors, options.libraries) + else: + print 'success' + +if __name__ == '__main__': + verify_api(sys.argv[1:]) \ No newline at end of file diff --git a/source/API/CMakeLists.txt b/source/API/CMakeLists.txt new file mode 100644 index 000000000000..06a26e17c929 --- /dev/null +++ b/source/API/CMakeLists.txt @@ -0,0 +1,119 @@ +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + add_definitions( -DEXPORT_LIBLLDB ) +endif() + +# Include this so that add_lldb_library() has the list of dependencies +# for liblldb to link against +include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) + +add_lldb_library(liblldb SHARED + SBAddress.cpp + SBAttachInfo.cpp + SBBlock.cpp + SBBreakpoint.cpp + SBBreakpointLocation.cpp + SBBroadcaster.cpp + SBCommandInterpreter.cpp + SBCommandReturnObject.cpp + SBCommunication.cpp + SBCompileUnit.cpp + SBData.cpp + SBDebugger.cpp + SBDeclaration.cpp + SBError.cpp + SBEvent.cpp + SBExecutionContext.cpp + SBExpressionOptions.cpp + SBFileSpec.cpp + SBFileSpecList.cpp + SBFrame.cpp + SBFunction.cpp + SBHostOS.cpp + SBInstruction.cpp + SBInstructionList.cpp + SBLanguageRuntime.cpp + SBLaunchInfo.cpp + SBLineEntry.cpp + SBListener.cpp + SBModule.cpp + SBModuleSpec.cpp + SBPlatform.cpp + SBProcess.cpp + SBQueue.cpp + SBQueueItem.cpp + SBSection.cpp + SBSourceManager.cpp + SBStream.cpp + SBStringList.cpp + SBSymbol.cpp + SBSymbolContext.cpp + SBSymbolContextList.cpp + SBTarget.cpp + SBThread.cpp + SBThreadCollection.cpp + SBThreadPlan.cpp + SBType.cpp + SBTypeCategory.cpp + SBTypeEnumMember.cpp + SBTypeFilter.cpp + SBTypeFormat.cpp + SBTypeNameSpecifier.cpp + SBTypeSummary.cpp + SBTypeSynthetic.cpp + SBValue.cpp + SBValueList.cpp + SBVariablesOptions.cpp + SBWatchpoint.cpp + SBUnixSignals.cpp + SystemInitializerFull.cpp + ${LLDB_WRAP_PYTHON} + ) + +# This should not be part of LLDBDependencies.cmake, because we don't +# want every single library taking a dependency on the script interpreters. +target_link_libraries(liblldb PRIVATE + lldbPluginScriptInterpreterNone + lldbPluginScriptInterpreterPython + ) + +set_target_properties(liblldb + PROPERTIES + VERSION ${LLDB_VERSION} + ) + +if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") + if (NOT LLDB_EXPORT_ALL_SYMBOLS) + # If we're not exporting all symbols, we'll want to explicitly set + # the exported symbols here. This prevents 'log enable --stack ...' + # from working on some systems but limits the liblldb size. + MESSAGE("-- Symbols (liblldb): only exporting liblldb.exports symbols") + add_llvm_symbol_exports(liblldb ${CMAKE_CURRENT_SOURCE_DIR}/liblldb.exports) + else() + # Don't use an explicit export. Instead, tell the linker to + # export all symbols. + MESSAGE("-- Symbols (liblldb): exporting all symbols") + # Darwin linker doesn't need this extra step. + if (NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") + lldb_append_link_flags(liblldb "-Wl,--export-dynamic") + endif() + endif() +endif() + +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + # Only MSVC has the ABI compatibility problem and avoids using FindPythonLibs, + # so only it needs to explicitly link against ${PYTHON_LIBRARY} + if (MSVC AND NOT LLDB_DISABLE_PYTHON) + target_link_libraries(liblldb PRIVATE ${PYTHON_LIBRARY}) + endif() +else() + set_target_properties(liblldb + PROPERTIES + OUTPUT_NAME lldb + ) +endif() + +if (LLDB_WRAP_PYTHON) + add_dependencies(liblldb swig_wrapper) +endif() +target_link_libraries(liblldb ${cmake_2_8_12_PRIVATE} ${LLDB_SYSTEM_LIBS}) + diff --git a/source/API/Makefile b/source/API/Makefile new file mode 100644 index 000000000000..e35b2c37735e --- /dev/null +++ b/source/API/Makefile @@ -0,0 +1,18 @@ +##===- source/API/Makefile ---------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbAPI +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile + +ifeq ($(HOST_OS),MingW) +CXXFLAGS += -DEXPORT_LIBLLDB +endif diff --git a/source/API/SBProcess.cpp b/source/API/SBProcess.cpp index dceadeca69e5..42554726c018 100644 --- a/source/API/SBProcess.cpp +++ b/source/API/SBProcess.cpp @@ -995,7 +995,14 @@ SBProcess::GetStateFromEvent (const SBEvent &event) bool SBProcess::GetRestartedFromEvent (const SBEvent &event) { - return Process::ProcessEventData::GetRestartedFromEvent (event.get()); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_val = Process::ProcessEventData::GetRestartedFromEvent (event.get()); + + if (log) + log->Printf ("SBProcess::%s (event.sp=%p) => %d", __FUNCTION__, event.get(), ret_val); + + return ret_val; } size_t diff --git a/source/API/SystemInitializerFull.cpp b/source/API/SystemInitializerFull.cpp index f223357824e8..cbc01a6cff9a 100644 --- a/source/API/SystemInitializerFull.cpp +++ b/source/API/SystemInitializerFull.cpp @@ -102,7 +102,7 @@ PyInit__lldb(void); #define LLDBSwigPyInit PyInit__lldb #else -extern "C" void +extern "C" void init_lldb(void); #define LLDBSwigPyInit init_lldb @@ -308,7 +308,7 @@ SystemInitializerFull::Initialize() SystemRuntimeMacOSX::Initialize(); RenderScriptRuntime::Initialize(); GoLanguageRuntime::Initialize(); - + CPlusPlusLanguage::Initialize(); GoLanguage::Initialize(); ObjCLanguage::Initialize(); @@ -430,7 +430,7 @@ SystemInitializerFull::Terminate() GoLanguage::Terminate(); ObjCLanguage::Terminate(); ObjCPlusPlusLanguage::Terminate(); - + #if defined(__APPLE__) ProcessMachCore::Terminate(); ProcessKDP::Terminate(); diff --git a/source/Breakpoint/CMakeLists.txt b/source/Breakpoint/CMakeLists.txt new file mode 100644 index 000000000000..85494b15aa08 --- /dev/null +++ b/source/Breakpoint/CMakeLists.txt @@ -0,0 +1,23 @@ +add_lldb_library(lldbBreakpoint + Breakpoint.cpp + BreakpointID.cpp + BreakpointIDList.cpp + BreakpointList.cpp + BreakpointLocation.cpp + BreakpointLocationCollection.cpp + BreakpointLocationList.cpp + BreakpointOptions.cpp + BreakpointResolver.cpp + BreakpointResolverAddress.cpp + BreakpointResolverFileLine.cpp + BreakpointResolverFileRegex.cpp + BreakpointResolverName.cpp + BreakpointSite.cpp + BreakpointSiteList.cpp + Stoppoint.cpp + StoppointCallbackContext.cpp + StoppointLocation.cpp + Watchpoint.cpp + WatchpointList.cpp + WatchpointOptions.cpp + ) diff --git a/source/Breakpoint/Makefile b/source/Breakpoint/Makefile new file mode 100644 index 000000000000..223e4c24465f --- /dev/null +++ b/source/Breakpoint/Makefile @@ -0,0 +1,14 @@ +##===- source/Breakpoint/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbBreakpoint +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 000000000000..f23af477bf89 --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,92 @@ +include_directories(.) + +if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) +include_directories( + Plugins/Process/Linux + Plugins/Process/POSIX + ) +endif () + +if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" ) +include_directories( + Plugins/Process/FreeBSD + Plugins/Process/POSIX + ) +endif () + +if ( CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) +include_directories( + Plugins/Process/POSIX + ) +endif () + + +set(lldbBase_SOURCES + lldb.cpp + ) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + set(LLDB_VERS_GENERATED_FILE ${LLDB_BINARY_DIR}/source/LLDB_vers.c) + add_custom_command(OUTPUT ${LLDB_VERS_GENERATED_FILE} + COMMAND ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl + ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj liblldb_core + > ${LLDB_VERS_GENERATED_FILE} + DEPENDS ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl + ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj) + set_source_files_properties(${LLDB_VERS_GENERATED_FILE} PROPERTIES GENERATED 1) + # Add this to lldbBase since lldb.cpp uses the symbol defined here. + list(APPEND lldbBase_SOURCES ${LLDB_VERS_GENERATED_FILE}) + add_custom_target(lldbGeneratedVersion + DEPENDS ${LLDB_VERS_GENERATED_FILE}) +endif() + +add_lldb_library(lldbBase + ${lldbBase_SOURCES} + ) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_dependencies(lldbBase lldbGeneratedVersion) +endif() + +add_subdirectory(Breakpoint) +add_subdirectory(Commands) +add_subdirectory(Core) +add_subdirectory(DataFormatters) +add_subdirectory(Expression) +add_subdirectory(Host) +add_subdirectory(Initialization) +add_subdirectory(Interpreter) +add_subdirectory(Plugins) +add_subdirectory(Symbol) +add_subdirectory(Target) +add_subdirectory(Utility) + +# Build API last. Since liblldb needs to link against every other target, it needs +# those targets to have already been created. +add_subdirectory(API) + +# Determine LLDB revision and repository. GetSourceVersion and GetRepositoryPath are shell-scripts, and as +# such will not work on Windows. +if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" ) + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLDB_SOURCE_DIR} + OUTPUT_VARIABLE LLDB_REVISION) + if ( LLDB_REVISION ) + string(REGEX REPLACE "(\r?\n)+$" "" LLDB_REVISION ${LLDB_REVISION}) + endif() + + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLDB_SOURCE_DIR} + OUTPUT_VARIABLE LLDB_REPOSITORY) + if ( LLDB_REPOSITORY ) + # Replace newline characters with spaces + string(REGEX REPLACE "(\r?\n)+" " " LLDB_REPOSITORY ${LLDB_REPOSITORY}) + + # Remove trailing spaces + string(REGEX REPLACE "(\ )+$" "" LLDB_REPOSITORY ${LLDB_REPOSITORY}) + endif() + + set_property( + SOURCE lldb.cpp + PROPERTY COMPILE_DEFINITIONS "LLDB_REVISION=\"${LLDB_REVISION}\"" "LLDB_REPOSITORY=\"${LLDB_REPOSITORY}\"") +endif () +# FIXME: implement svn/git revision and repository parsing solution on Windows. There is an SVN-only +# revision parsing solution in tools/clang/lib/Basic/CMakelists.txt. diff --git a/source/Commands/CMakeLists.txt b/source/Commands/CMakeLists.txt new file mode 100644 index 000000000000..8805bbf6dc94 --- /dev/null +++ b/source/Commands/CMakeLists.txt @@ -0,0 +1,32 @@ +add_lldb_library(lldbCommands + CommandCompletions.cpp + CommandObjectApropos.cpp + CommandObjectArgs.cpp + CommandObjectBreakpoint.cpp + CommandObjectBreakpointCommand.cpp + CommandObjectBugreport.cpp + CommandObjectCommands.cpp + CommandObjectDisassemble.cpp + CommandObjectExpression.cpp + CommandObjectFrame.cpp + CommandObjectGUI.cpp + CommandObjectHelp.cpp + CommandObjectLog.cpp + CommandObjectMemory.cpp + CommandObjectMultiword.cpp + CommandObjectPlatform.cpp + CommandObjectPlugin.cpp + CommandObjectProcess.cpp + CommandObjectQuit.cpp + CommandObjectRegister.cpp + CommandObjectSettings.cpp + CommandObjectSource.cpp + CommandObjectSyntax.cpp + CommandObjectTarget.cpp + CommandObjectThread.cpp + CommandObjectType.cpp + CommandObjectVersion.cpp + CommandObjectWatchpoint.cpp + CommandObjectWatchpointCommand.cpp + CommandObjectLanguage.cpp + ) diff --git a/source/Commands/CommandObjectSource.cpp b/source/Commands/CommandObjectSource.cpp index 08c8d46d1ee0..a9e52d1a76f3 100644 --- a/source/Commands/CommandObjectSource.cpp +++ b/source/Commands/CommandObjectSource.cpp @@ -27,6 +27,7 @@ #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StackFrame.h" #include "lldb/Target/TargetList.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/Options.h" @@ -34,9 +35,11 @@ using namespace lldb; using namespace lldb_private; -//------------------------------------------------------------------------- -// CommandObjectSourceInfo -//------------------------------------------------------------------------- + +#pragma mark CommandObjectSourceInfo +//---------------------------------------------------------------------- +// CommandObjectSourceInfo - debug line entries dumping command +//---------------------------------------------------------------------- class CommandObjectSourceInfo : public CommandObjectParsed { @@ -44,14 +47,9 @@ class CommandObjectSourceInfo : public CommandObjectParsed class CommandOptions : public Options { public: - CommandOptions (CommandInterpreter &interpreter) : - Options(interpreter) - { - } + CommandOptions (CommandInterpreter &interpreter) : Options(interpreter) {} - ~CommandOptions () override - { - } + ~CommandOptions () override {} Error SetOptionValue (uint32_t option_idx, const char *option_arg) override @@ -60,19 +58,44 @@ class CommandObjectSourceInfo : public CommandObjectParsed const int short_option = g_option_table[option_idx].short_option; switch (short_option) { - case 'l': - start_line = StringConvert::ToUInt32 (option_arg, 0); - if (start_line == 0) - error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); - break; + case 'l': + start_line = StringConvert::ToUInt32(option_arg, 0); + if (start_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; - case 'f': - file_name = option_arg; - break; + case 'e': + end_line = StringConvert::ToUInt32(option_arg, 0); + if (end_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; - default: - error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + case 'c': + num_lines = StringConvert::ToUInt32(option_arg, 0); + if (num_lines == 0) + error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg); + break; + + case 'f': + file_name = option_arg; + break; + + case 'n': + symbol_name = option_arg; + break; + + case 'a': + { + ExecutionContext exe_ctx(m_interpreter.GetExecutionContext()); + address = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } break; + case 's': + modules.push_back(std::string(option_arg)); + break; + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; } return error; @@ -83,10 +106,15 @@ class CommandObjectSourceInfo : public CommandObjectParsed { file_spec.Clear(); file_name.clear(); + symbol_name.clear(); + address = LLDB_INVALID_ADDRESS; start_line = 0; + end_line = 0; + num_lines = 0; + modules.clear(); } - const OptionDefinition* + const OptionDefinition * GetDefinitions () override { return g_option_table; @@ -96,24 +124,24 @@ class CommandObjectSourceInfo : public CommandObjectParsed // Instance variables to hold the values for command options. FileSpec file_spec; std::string file_name; + std::string symbol_name; + lldb::addr_t address; uint32_t start_line; - + uint32_t end_line; + uint32_t num_lines; + STLStringArray modules; }; - -public: - CommandObjectSourceInfo(CommandInterpreter &interpreter) : - CommandObjectParsed (interpreter, - "source info", - "Display information about the source lines from the current executable's debug info.", - "source info []"), - m_options (interpreter) - { - } - - ~CommandObjectSourceInfo () override + +public: + CommandObjectSourceInfo (CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "source info", "Display source line information (as specified) based " + "on the current executable's debug info.", + NULL, eCommandRequiresTarget), + m_options(interpreter) { } + ~CommandObjectSourceInfo () override {} Options * GetOptions () override @@ -122,25 +150,576 @@ class CommandObjectSourceInfo : public CommandObjectParsed } protected: - bool - DoExecute (Args& command, CommandReturnObject &result) override + + // Dump the line entries in each symbol context. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If file_spec is set, only dump lines in the file. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpLinesInSymbolContexts (Stream &strm, const SymbolContextList &sc_list, + const ModuleList &module_list, const FileSpec &file_spec) { - result.AppendError ("Not yet implemented"); - result.SetStatus (eReturnStatusFailed); - return false; + uint32_t start_line = m_options.start_line; + uint32_t end_line = m_options.end_line; + uint32_t num_lines = m_options.num_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + + uint32_t num_matches = 0; + bool has_path = false; + if (file_spec) + { + assert(file_spec.GetFilename().AsCString()); + has_path = (file_spec.GetDirectory().AsCString() != 0); + } + + // Dump all the line entries for the file in the list. + ConstString last_module_file_name; + uint32_t num_scs = sc_list.GetSize(); + for (uint32_t i = 0; i < num_scs; ++i) + { + SymbolContext sc; + sc_list.GetContextAtIndex(i, sc); + if (sc.comp_unit) + { + Module *module = sc.module_sp.get(); + CompileUnit *cu = sc.comp_unit; + const LineEntry &line_entry = sc.line_entry; + assert(module && cu); + + // Are we looking for specific modules, files or lines? + if (module_list.GetSize() && module_list.GetIndexForModule(module) == LLDB_INVALID_INDEX32) + continue; + if (file_spec && !lldb_private::FileSpec::Equal(file_spec, line_entry.file, has_path)) + continue; + if (start_line > 0 && line_entry.line < start_line) + continue; + if (end_line > 0 && line_entry.line > end_line) + continue; + if (num_lines > 0 && num_matches > num_lines) + continue; + + // Print a new header if the module changed. + const ConstString &module_file_name = module->GetFileSpec().GetFilename(); + assert(module_file_name); + if (module_file_name != last_module_file_name) + { + if (num_matches > 0) + strm << "\n\n"; + strm << "Lines found in module `" << module_file_name << "\n"; + } + // Dump the line entry. + line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, + target, /*show_address_only=*/false); + strm << "\n"; + last_module_file_name = module_file_name; + num_matches++; + } + } + return num_matches; + } + + // Dump the requested line entries for the file in the compilation unit. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpFileLinesInCompUnit (Stream &strm, Module *module, CompileUnit *cu, const FileSpec &file_spec) + { + uint32_t start_line = m_options.start_line; + uint32_t end_line = m_options.end_line; + uint32_t num_lines = m_options.num_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + + uint32_t num_matches = 0; + assert(module); + if (cu) + { + assert(file_spec.GetFilename().AsCString()); + bool has_path = (file_spec.GetDirectory().AsCString() != 0); + const FileSpecList &cu_file_list = cu->GetSupportFiles(); + size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path); + if (file_idx != UINT32_MAX) + { + // Update the file to how it appears in the CU. + const FileSpec &cu_file_spec = cu_file_list.GetFileSpecAtIndex(file_idx); + + // Dump all matching lines at or above start_line for the file in the CU. + const ConstString &file_spec_name = file_spec.GetFilename(); + const ConstString &module_file_name = module->GetFileSpec().GetFilename(); + bool cu_header_printed = false; + uint32_t line = start_line; + while (true) + { + LineEntry line_entry; + + // Find the lowest index of a line entry with a line equal to + // or higher than 'line'. + uint32_t start_idx = 0; + start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, + /*exact=*/false, &line_entry); + if (start_idx == UINT32_MAX) + // No more line entries for our file in this CU. + break; + + if (end_line > 0 && line_entry.line > end_line) + break; + + // Loop through to find any other entries for this line, dumping each. + line = line_entry.line; + do + { + num_matches++; + if (num_lines > 0 && num_matches > num_lines) + break; + assert(lldb_private::FileSpec::Equal(cu_file_spec, line_entry.file, has_path)); + if (!cu_header_printed) + { + if (num_matches > 0) + strm << "\n\n"; + strm << "Lines found for file " << file_spec_name + << " in compilation unit " << cu->GetFilename() + << " in `" << module_file_name << "\n"; + cu_header_printed = true; + } + line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, + target, /*show_address_only=*/false); + strm << "\n"; + + // Anymore after this one? + start_idx++; + start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, + /*exact=*/true, &line_entry); + } while (start_idx != UINT32_MAX); + + // Try the next higher line, starting over at start_idx 0. + line++; + } + } + } + return num_matches; + } + + // Dump the requested line entries for the file in the module. + // Return the number of entries found. + // If module_list is set, only dump lines contained in one of the modules. + // If the start_line option was specified, don't print lines less than start_line. + // If the end_line option was specified, don't print lines greater than end_line. + // If the num_lines option was specified, dont print more than num_lines entries. + uint32_t + DumpFileLinesInModule (Stream &strm, Module *module, const FileSpec &file_spec) + { + uint32_t num_matches = 0; + if (module) + { + // Look through all the compilation units (CUs) in this module for ones that + // contain lines of code from this source file. + for (size_t i = 0; i < module->GetNumCompileUnits(); i++) + { + // Look for a matching source file in this CU. + CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i)); + if (cu_sp) + { + num_matches += DumpFileLinesInCompUnit(strm, module, cu_sp.get(), file_spec); + } + } + } + return num_matches; + } + + // Given an address and a list of modules, append the symbol contexts of all line entries + // containing the address found in the modules and return the count of matches. If none + // is found, return an error in 'error_strm'. + size_t + GetSymbolContextsForAddress (const ModuleList &module_list, lldb::addr_t addr, + SymbolContextList &sc_list, StreamString &error_strm) + { + Address so_addr; + size_t num_matches = 0; + assert(module_list.GetSize() > 0); + Target *target = m_exe_ctx.GetTargetPtr(); + if (target->GetSectionLoadList().IsEmpty()) + { + // The target isn't loaded yet, we need to lookup the file address in + // all modules. Note: the module list option does not apply to addresses. + const size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; ++i) + { + ModuleSP module_sp(module_list.GetModuleAtIndex(i)); + if (!module_sp) + continue; + if (module_sp->ResolveFileAddress(addr, so_addr)) + { + SymbolContext sc; + sc.Clear(true); + if (module_sp->ResolveSymbolContextForAddress(so_addr, eSymbolContextEverything, sc) & + eSymbolContextLineEntry) + { + sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); + ++num_matches; + } + } + } + if (num_matches == 0) + error_strm.Printf("Source information for file address 0x%" PRIx64 + " not found in any modules.\n", addr); + } + else + { + // The target has some things loaded, resolve this address to a + // compile unit + file + line and display + if (target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) + { + ModuleSP module_sp(so_addr.GetModule()); + // Check to make sure this module is in our list. + if (module_sp && + module_list.GetIndexForModule(module_sp.get()) != LLDB_INVALID_INDEX32) + { + SymbolContext sc; + sc.Clear(true); + if (module_sp->ResolveSymbolContextForAddress(so_addr, eSymbolContextEverything, sc) & + eSymbolContextLineEntry) + { + sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); + ++num_matches; + } + else + { + StreamString addr_strm; + so_addr.Dump(&addr_strm, NULL, Address::DumpStyleModuleWithFileAddress); + error_strm.Printf("Address 0x%" PRIx64 " resolves to %s, but there is" + " no source information available for this address.\n", + addr, addr_strm.GetData()); + } + } + else + { + StreamString addr_strm; + so_addr.Dump(&addr_strm, NULL, Address::DumpStyleModuleWithFileAddress); + error_strm.Printf("Address 0x%" PRIx64 " resolves to %s, but it cannot" + " be found in any modules.\n", + addr, addr_strm.GetData()); + } + } + else + error_strm.Printf("Unable to resolve address 0x%" PRIx64 ".\n", addr); + } + return num_matches; + } + + // Dump the line entries found in functions matching the name specified in the option. + bool + DumpLinesInFunctions (CommandReturnObject &result) + { + SymbolContextList sc_list_funcs; + ConstString name(m_options.symbol_name.c_str()); + SymbolContextList sc_list_lines; + Target *target = m_exe_ctx.GetTargetPtr(); + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + + // Note: module_list can't be const& because FindFunctionSymbols isn't const. + ModuleList module_list = (m_module_list.GetSize() > 0) ? + m_module_list : target->GetImages(); + size_t num_matches = module_list.FindFunctions(name, + eFunctionNameTypeAuto, + /*include_symbols=*/false, + /*include_inlines=*/true, + /*append=*/true, + sc_list_funcs); + if (!num_matches) + { + // If we didn't find any functions with that name, try searching for + // symbols that line up exactly with function addresses. + SymbolContextList sc_list_symbols; + size_t num_symbol_matches = module_list.FindFunctionSymbols(name, + eFunctionNameTypeAuto, + sc_list_symbols); + for (size_t i = 0; i < num_symbol_matches; i++) + { + SymbolContext sc; + sc_list_symbols.GetContextAtIndex(i, sc); + if (sc.symbol && sc.symbol->ValueIsAddress()) + { + const Address &base_address = sc.symbol->GetAddressRef(); + Function *function = base_address.CalculateSymbolContextFunction(); + if (function) + { + sc_list_funcs.Append(SymbolContext(function)); + num_matches++; + } + } + } + } + if (num_matches == 0) + { + result.AppendErrorWithFormat("Could not find function named \'%s\'.\n", + m_options.symbol_name.c_str()); + return false; + } + for (size_t i = 0; i < num_matches; i++) + { + SymbolContext sc; + sc_list_funcs.GetContextAtIndex(i, sc); + bool context_found_for_symbol = false; + // Loop through all the ranges in the function. + AddressRange range; + for (uint32_t r = 0; + sc.GetAddressRange(eSymbolContextEverything, + r, + /*use_inline_block_range=*/true, + range); + ++r) + { + // Append the symbol contexts for each address in the range to sc_list_lines. + const Address &base_address = range.GetBaseAddress(); + const addr_t size = range.GetByteSize(); + lldb::addr_t start_addr = base_address.GetLoadAddress(target); + if (start_addr == LLDB_INVALID_ADDRESS) + start_addr = base_address.GetFileAddress(); + lldb::addr_t end_addr = start_addr + size; + for (lldb::addr_t addr = start_addr; addr < end_addr; addr += addr_byte_size) + { + StreamString error_strm; + if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines, error_strm)) + result.AppendWarningWithFormat("in symbol '%s': %s", + sc.GetFunctionName().AsCString(), + error_strm.GetData()); + else + context_found_for_symbol = true; + } + } + if (!context_found_for_symbol) + result.AppendWarningWithFormat("Unable to find line information" + " for matching symbol '%s'.\n", + sc.GetFunctionName().AsCString()); + } + if (sc_list_lines.GetSize() == 0) + { + result.AppendErrorWithFormat("No line information could be found" + " for any symbols matching '%s'.\n", + name.AsCString()); + return false; + } + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), + sc_list_lines, module_list, file_spec)) + { + result.AppendErrorWithFormat("Unable to dump line information for symbol '%s'.\n", + name.AsCString()); + return false; + } + return true; + } + + // Dump the line entries found for the address specified in the option. + bool + DumpLinesForAddress (CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + SymbolContextList sc_list; + + StreamString error_strm; + if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address, sc_list, error_strm)) + { + result.AppendErrorWithFormat("%s.\n", error_strm.GetData()); + return false; + } + ModuleList module_list; + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), + sc_list, module_list, file_spec)) + { + result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64 ".\n", + m_options.address); + return false; + } + return true; + } + + // Dump the line entries found in the file specified in the option. + bool + DumpLinesForFile (CommandReturnObject &result) + { + FileSpec file_spec(m_options.file_name, false); + const char *filename = m_options.file_name.c_str(); + Target *target = m_exe_ctx.GetTargetPtr(); + const ModuleList &module_list = (m_module_list.GetSize() > 0) ? + m_module_list : target->GetImages(); + + bool displayed_something = false; + const size_t num_modules = module_list.GetSize(); + for (uint32_t i = 0; i < num_modules; ++i) + { + // Dump lines for this module. + Module *module = module_list.GetModulePointerAtIndex(i); + assert(module); + if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec)) + displayed_something = true; + } + if (!displayed_something) + { + result.AppendErrorWithFormat("No source filenames matched '%s'.\n", filename); + return false; + } + return true; + } + + // Dump the line entries for the current frame. + bool + DumpLinesForFrame (CommandReturnObject &result) + { + StackFrame *cur_frame = m_exe_ctx.GetFramePtr(); + if (cur_frame == NULL) + { + result.AppendError("No selected frame to use to find the default source."); + return false; + } + else if (!cur_frame->HasDebugInformation()) + { + result.AppendError("No debug info for the selected frame."); + return false; + } + else + { + const SymbolContext &sc = cur_frame->GetSymbolContext(eSymbolContextLineEntry); + SymbolContextList sc_list; + sc_list.Append(sc); + ModuleList module_list; + FileSpec file_spec; + if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list, module_list, file_spec)) + { + result.AppendError("No source line info available for the selected frame."); + return false; + } + } + return true; + } + + bool + DoExecute (Args &command, CommandReturnObject &result) override + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 0) + { + result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", + GetCommandName()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Target *target = m_exe_ctx.GetTargetPtr(); + if (target == NULL) + { + target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError("invalid target, create a debug target using the " + "'target create' command."); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + result.GetOutputStream().SetAddressByteSize(addr_byte_size); + result.GetErrorStream().SetAddressByteSize(addr_byte_size); + + // Collect the list of modules to search. + m_module_list.Clear(); + if (m_options.modules.size() > 0) + { + for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) + { + FileSpec module_file_spec(m_options.modules[i].c_str(), false); + if (module_file_spec) + { + ModuleSpec module_spec(module_file_spec); + if (target->GetImages().FindModules(module_spec, m_module_list) == 0) + result.AppendWarningWithFormat("No module found for '%s'.\n", + m_options.modules[i].c_str()); + } + } + if (!m_module_list.GetSize()) + { + result.AppendError("No modules match the input."); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else if (target->GetImages().GetSize() == 0) + { + result.AppendError("The target has no associated executable images."); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // Check the arguments to see what lines we should dump. + if (!m_options.symbol_name.empty()) + { + // Print lines for symbol. + if (DumpLinesInFunctions(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else if (m_options.address != LLDB_INVALID_ADDRESS) + { + // Print lines for an address. + if (DumpLinesForAddress(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else if (!m_options.file_name.empty()) + { + // Dump lines for a file. + if (DumpLinesForFile(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + else + { + // Dump the line for the current frame. + if (DumpLinesForFrame(result)) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); } CommandOptions m_options; + ModuleList m_module_list; }; -OptionDefinition -CommandObjectSourceInfo::CommandOptions::g_option_table[] = -{ -{ LLDB_OPT_SET_1, false, "line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, -{ LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, -{ 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } +OptionDefinition CommandObjectSourceInfo::CommandOptions::g_option_table[] = { + {LLDB_OPT_SET_ALL, false, "count", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCount, + "The number of line entries to display."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "shlib", 's', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eModuleCompletion, eArgTypeShlibName, + "Look up the source in the given module or shared library (can be " + "specified more than once)."}, + {LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, + {LLDB_OPT_SET_1, false, "line", 'l', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, + "The line number at which to start the displaying lines."}, + {LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeLineNum, + "The line number at which to stop displaying lines."}, + {LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, + CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display."}, + {LLDB_OPT_SET_3, false, "address", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeAddressOrExpression, + "Lookup the address and display the source information for the " + "corresponding file and line."}, + {0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL} }; + #pragma mark CommandObjectSourceList //------------------------------------------------------------------------- // CommandObjectSourceList @@ -906,7 +1485,6 @@ CommandObjectSourceList::CommandOptions::g_option_table[] = }; #pragma mark CommandObjectMultiwordSource - //------------------------------------------------------------------------- // CommandObjectMultiwordSource //------------------------------------------------------------------------- @@ -917,8 +1495,7 @@ CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter & "A set of commands for accessing source file information", "source []") { - // "source info" isn't implemented yet... - //LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); + LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectSourceList (interpreter))); } diff --git a/source/Commands/CommandObjectTarget.cpp b/source/Commands/CommandObjectTarget.cpp index 026ae3570854..57ec1c953fcf 100644 --- a/source/Commands/CommandObjectTarget.cpp +++ b/source/Commands/CommandObjectTarget.cpp @@ -2577,8 +2577,9 @@ class CommandObjectTargetModulesDumpLineTable : public CommandObjectTargetModule if (command.GetArgumentCount() == 0) { - result.AppendErrorWithFormat ("\nSyntax: %s\n", m_cmd_syntax.c_str()); + result.AppendError ("file option must be specified."); result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); } else { diff --git a/source/Commands/CommandObjectType.cpp b/source/Commands/CommandObjectType.cpp index b57ac70f8f1a..2b803e7eb0d3 100644 --- a/source/Commands/CommandObjectType.cpp +++ b/source/Commands/CommandObjectType.cpp @@ -1396,11 +1396,6 @@ class CommandObjectTypeFormatterList : public CommandObjectParsed auto category_closure = [&result, &formatter_regex] (const lldb::TypeCategoryImplSP& category) -> void { result.GetOutputStream().Printf("-----------------------\nCategory: %s\n-----------------------\n", category->GetName()); - - typedef const std::shared_ptr Bar; - typedef std::function Func1Type; - typedef std::function Func2Type; - TypeCategoryImpl::ForEachCallbacks foreach; foreach.SetExact([&result, &formatter_regex] (ConstString name, const FormatterSharedPointer& format_sp) -> bool { if (formatter_regex) diff --git a/source/Commands/Makefile b/source/Commands/Makefile new file mode 100644 index 000000000000..1a66485a0c21 --- /dev/null +++ b/source/Commands/Makefile @@ -0,0 +1,16 @@ +##===- source/Commands/Makefile ----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbCommands +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile + +EXTRA_OPTIONS += -Wno-four-char-constants \ No newline at end of file diff --git a/source/Core/CMakeLists.txt b/source/Core/CMakeLists.txt new file mode 100644 index 000000000000..b975fb88e784 --- /dev/null +++ b/source/Core/CMakeLists.txt @@ -0,0 +1,76 @@ +add_lldb_library(lldbCore + Address.cpp + AddressRange.cpp + AddressResolver.cpp + AddressResolverFileLine.cpp + AddressResolverName.cpp + ArchSpec.cpp + Baton.cpp + Broadcaster.cpp + Communication.cpp + Connection.cpp + ConnectionMachPort.cpp + ConnectionSharedMemory.cpp + ConstString.cpp + CxaDemangle.cpp + DataBufferHeap.cpp + DataBufferMemoryMap.cpp + DataEncoder.cpp + DataExtractor.cpp + Debugger.cpp + Disassembler.cpp + DynamicLoader.cpp + EmulateInstruction.cpp + Error.cpp + Event.cpp + FastDemangle.cpp + FileLineResolver.cpp + FileSpecList.cpp + FormatEntity.cpp + History.cpp + IOHandler.cpp + Listener.cpp + Log.cpp + Logging.cpp + Mangled.cpp + Module.cpp + ModuleChild.cpp + ModuleList.cpp + Opcode.cpp + PluginManager.cpp + RegisterValue.cpp + RegularExpression.cpp + Scalar.cpp + SearchFilter.cpp + Section.cpp + SourceManager.cpp + State.cpp + Stream.cpp + StreamAsynchronousIO.cpp + StreamCallback.cpp + StreamFile.cpp + StreamGDBRemote.cpp + StreamString.cpp + StringList.cpp + StructuredData.cpp + Timer.cpp + UserID.cpp + UserSettingsController.cpp + UUID.cpp + Value.cpp + ValueObject.cpp + ValueObjectCast.cpp + ValueObjectChild.cpp + ValueObjectConstResult.cpp + ValueObjectConstResultCast.cpp + ValueObjectConstResultChild.cpp + ValueObjectConstResultImpl.cpp + ValueObjectDynamicValue.cpp + ValueObjectList.cpp + ValueObjectMemory.cpp + ValueObjectRegister.cpp + ValueObjectSyntheticFilter.cpp + ValueObjectVariable.cpp + VMRange.cpp + ) + diff --git a/source/Core/Makefile b/source/Core/Makefile new file mode 100644 index 000000000000..b7773e3f571d --- /dev/null +++ b/source/Core/Makefile @@ -0,0 +1,14 @@ +##===- source/Core/Makefile --------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbCore +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Core/StringList.cpp b/source/Core/StringList.cpp index 4e07ba4a4579..ce197ac7bd48 100644 --- a/source/Core/StringList.cpp +++ b/source/Core/StringList.cpp @@ -11,6 +11,8 @@ #include "lldb/Core/StreamString.h" #include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" #include @@ -304,6 +306,13 @@ StringList::operator << (const char* str) return *this; } +StringList& +StringList::operator << (const std::string& str) +{ + AppendString(str); + return *this; +} + StringList& StringList::operator << (StringList strings) { @@ -311,6 +320,16 @@ StringList::operator << (StringList strings) return *this; } +StringList& +StringList::operator = (const std::vector &rhs) +{ + Clear(); + for (const auto &s : rhs) + m_strings.push_back(s); + + return *this; +} + size_t StringList::AutoComplete (const char *s, StringList &matches, size_t &exact_idx) const { @@ -339,3 +358,21 @@ StringList::AutoComplete (const char *s, StringList &matches, size_t &exact_idx) return matches.GetSize(); } +void +StringList::LogDump(Log *log, const char *name) +{ + if (!log) + return; + + StreamString strm; + if (name) + strm.Printf("Begin %s:\n", name); + for (const auto &s : m_strings) { + strm.Indent(); + strm.Printf("%s\n", s.c_str()); + } + if (name) + strm.Printf("End %s.\n", name); + + log->Debug("%s", strm.GetData()); +} diff --git a/source/DataFormatters/CMakeLists.txt b/source/DataFormatters/CMakeLists.txt new file mode 100644 index 000000000000..bfb5c8b9f68b --- /dev/null +++ b/source/DataFormatters/CMakeLists.txt @@ -0,0 +1,19 @@ +add_lldb_library(lldbDataFormatters + CXXFunctionPointer.cpp + DataVisualization.cpp + DumpValueObjectOptions.cpp + FormatCache.cpp + FormatClasses.cpp + FormatManager.cpp + FormattersHelpers.cpp + LanguageCategory.cpp + StringPrinter.cpp + TypeCategory.cpp + TypeCategoryMap.cpp + TypeFormat.cpp + TypeSummary.cpp + TypeSynthetic.cpp + TypeValidator.cpp + ValueObjectPrinter.cpp + VectorType.cpp + ) diff --git a/source/DataFormatters/Makefile b/source/DataFormatters/Makefile new file mode 100644 index 000000000000..4eb3249e5a58 --- /dev/null +++ b/source/DataFormatters/Makefile @@ -0,0 +1,14 @@ +##===- source/DataFormatters/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbDataFormatters +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Expression/CMakeLists.txt b/source/Expression/CMakeLists.txt new file mode 100644 index 000000000000..52392f13319a --- /dev/null +++ b/source/Expression/CMakeLists.txt @@ -0,0 +1,16 @@ +add_lldb_library(lldbExpression + DWARFExpression.cpp + Expression.cpp + ExpressionSourceCode.cpp + ExpressionVariable.cpp + FunctionCaller.cpp + IRDynamicChecks.cpp + IRExecutionUnit.cpp + IRInterpreter.cpp + IRMemoryMap.cpp + LLVMUserExpression.cpp + Materializer.cpp + REPL.cpp + UserExpression.cpp + UtilityFunction.cpp + ) diff --git a/source/Expression/Makefile b/source/Expression/Makefile new file mode 100644 index 000000000000..495f094d3900 --- /dev/null +++ b/source/Expression/Makefile @@ -0,0 +1,14 @@ +##===- source/Expression/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbExpression +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Host/CMakeLists.txt b/source/Host/CMakeLists.txt new file mode 100644 index 000000000000..776fcfb44ffa --- /dev/null +++ b/source/Host/CMakeLists.txt @@ -0,0 +1,190 @@ +macro(add_host_subdirectory group) + list(APPEND HOST_SOURCES ${ARGN}) + source_group(${group} FILES ${ARGN}) +endmacro() + +add_host_subdirectory(common + common/Condition.cpp + common/File.cpp + common/FileCache.cpp + common/FileSpec.cpp + common/FileSystem.cpp + common/GetOptInc.cpp + common/Host.cpp + common/HostInfoBase.cpp + common/HostNativeThreadBase.cpp + common/HostProcess.cpp + common/HostThread.cpp + common/IOObject.cpp + common/LockFileBase.cpp + common/Mutex.cpp + common/MonitoringProcessLauncher.cpp + common/NativeBreakpoint.cpp + common/NativeBreakpointList.cpp + common/NativeWatchpointList.cpp + common/NativeProcessProtocol.cpp + common/NativeRegisterContext.cpp + common/NativeRegisterContextRegisterInfo.cpp + common/NativeThreadProtocol.cpp + common/OptionParser.cpp + common/PipeBase.cpp + common/ProcessRunLock.cpp + common/Socket.cpp + common/SocketAddress.cpp + common/SoftwareBreakpoint.cpp + common/StringConvert.cpp + common/Symbols.cpp + common/TCPSocket.cpp + common/Terminal.cpp + common/ThisThread.cpp + common/ThreadLauncher.cpp + common/TimeValue.cpp + common/XML.cpp + common/UDPSocket.cpp + ) + +# Keep track of whether we want to provide a define for the +# Python's architecture-specific lib path (i.e. where a +# Python lldb module would go). +set (get_python_libdir 0) + +if (NOT LLDB_DISABLE_LIBEDIT) + add_host_subdirectory(common + common/Editline.cpp + ) +endif() + +add_host_subdirectory(posix + posix/ConnectionFileDescriptorPosix.cpp + ) + +if (CMAKE_SYSTEM_NAME MATCHES "Windows") + add_host_subdirectory(windows + windows/Condition.cpp + windows/ConnectionGenericFileWindows.cpp + windows/EditLineWin.cpp + windows/FileSystem.cpp + windows/Host.cpp + windows/HostInfoWindows.cpp + windows/HostProcessWindows.cpp + windows/HostThreadWindows.cpp + windows/LockFileWindows.cpp + windows/Mutex.cpp + windows/PipeWindows.cpp + windows/ProcessLauncherWindows.cpp + windows/ProcessRunLock.cpp + windows/ThisThread.cpp + windows/Windows.cpp + ) +else() + if (NOT LLDB_DISABLE_PYTHON) + # We'll grab the arch-specific python libdir on POSIX systems. + set (get_python_libdir 1) + endif() + + add_host_subdirectory(posix + posix/DomainSocket.cpp + posix/FileSystem.cpp + posix/HostInfoPosix.cpp + posix/HostProcessPosix.cpp + posix/HostThreadPosix.cpp + posix/LockFilePosix.cpp + posix/MainLoopPosix.cpp + posix/PipePosix.cpp + ) + + if (NOT __ANDROID_NDK__) + add_host_subdirectory(posix + posix/ProcessLauncherPosix.cpp + ) + endif() + + if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) + add_host_subdirectory(macosx + macosx/Host.mm + macosx/HostInfoMacOSX.mm + macosx/HostThreadMacOSX.mm + macosx/Symbols.cpp + macosx/ThisThread.cpp + macosx/cfcpp/CFCBundle.cpp + macosx/cfcpp/CFCData.cpp + macosx/cfcpp/CFCMutableArray.cpp + macosx/cfcpp/CFCMutableDictionary.cpp + macosx/cfcpp/CFCMutableSet.cpp + macosx/cfcpp/CFCString.cpp + ) + + elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") + if (__ANDROID_NDK__) + add_host_subdirectory(android + android/HostInfoAndroid.cpp + android/LibcGlue.cpp + android/ProcessLauncherAndroid.cpp + linux/AbstractSocket.cpp + linux/Host.cpp + linux/HostInfoLinux.cpp + linux/HostThreadLinux.cpp + linux/LibcGlue.cpp + linux/ThisThread.cpp + ) + else() + add_host_subdirectory(linux + linux/AbstractSocket.cpp + linux/Host.cpp + linux/HostInfoLinux.cpp + linux/HostThreadLinux.cpp + linux/LibcGlue.cpp + linux/ThisThread.cpp + ) + endif() + + elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_host_subdirectory(freebsd + freebsd/Host.cpp + freebsd/HostInfoFreeBSD.cpp + freebsd/HostThreadFreeBSD.cpp + freebsd/ThisThread.cpp + ) + + elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + add_host_subdirectory(netbsd + netbsd/Host.cpp + netbsd/HostInfoNetBSD.cpp + netbsd/HostThreadNetBSD.cpp + netbsd/ThisThread.cpp + ) + endif() +endif() + +if (${get_python_libdir}) + # Call a python script to gather the arch-specific libdir for + # modules like the lldb module. + execute_process( + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/get_relative_lib_dir.py + RESULT_VARIABLE get_libdir_status + OUTPUT_VARIABLE relative_libdir + ) + if (get_libdir_status EQUAL 0) + add_definitions(-DLLDB_PYTHON_RELATIVE_LIBDIR="${relative_libdir}") + endif() +endif() + +if (${get_python_libdir}) + # Call a python script to gather the arch-specific libdir for + # modules like the lldb module. + execute_process( + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/get_relative_lib_dir.py + RESULT_VARIABLE get_libdir_status + OUTPUT_VARIABLE relative_libdir + ) + if (get_libdir_status EQUAL 0) + add_definitions(-DLLDB_PYTHON_RELATIVE_LIBDIR="${relative_libdir}") + endif() +endif() + +add_lldb_library(lldbHost ${HOST_SOURCES}) + +if (CMAKE_SYSTEM_NAME MATCHES "NetBSD") +target_link_libraries(lldbHost kvm) +endif () diff --git a/source/Host/Makefile b/source/Host/Makefile new file mode 100644 index 000000000000..da90c8c364a3 --- /dev/null +++ b/source/Host/Makefile @@ -0,0 +1,65 @@ +##===- source/Host/Makefile --------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LEVEL := $(LLDB_LEVEL)/../.. + +include $(LEVEL)/Makefile.config + +define DIR_SOURCES +SOURCES += $$(addprefix $(1)/,$$(notdir $$(wildcard $$(PROJ_SRC_DIR)/$(1)/*.cpp \ + $$(PROJ_SRC_DIR)/*.cc $$(PROJ_SRC_DIR)/$(1)/*.c $$(PROJ_SRC_DIR)/$(1)/*.mm))) +endef + +$(eval $(call DIR_SOURCES,common)) + +ifeq ($(HOST_OS),Darwin) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,macosx)) +CFCPP_SOURCES = \ + $(addprefix macosx/cfcpp/,$(notdir $(wildcard $(PROJ_SRC_DIR)/macosx/cfcpp/*.cpp))) +SOURCES += $(CFCPP_SOURCES) + +CFCPP_BaseNameSources := $(sort $(basename $(CFCPP_SOURCES))) +CFCPP_OBJECTS := $(CFCPP_BaseNameSources:%=$(ObjDir)/%.o) + +# Make sure the cfcpp output directory exists +$(CFCPP_OBJECTS): $(ObjDir)/cfcpp/.dir +endif + +ifeq ($(HOST_OS),Linux) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,linux)) +endif + +ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD)) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,freebsd)) +endif + +ifeq ($(HOST_OS),NetBSD) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,netbsd)) +endif + +ifeq ($(HOST_OS),MingW) +$(eval $(call DIR_SOURCES,windows)) +SOURCES += posix/ConnectionFileDescriptorPosix.cpp +endif + +ifeq ($(HOST_OS),Android) +$(eval $(call DIR_SOURCES,posix)) +$(eval $(call DIR_SOURCES,linux)) +$(eval $(call DIR_SOURCES,android)) +endif + +LIBRARYNAME := lldbHost +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Host/android/HostInfoAndroid.cpp b/source/Host/android/HostInfoAndroid.cpp new file mode 100644 index 000000000000..3fa50ec8ddfd --- /dev/null +++ b/source/Host/android/HostInfoAndroid.cpp @@ -0,0 +1,104 @@ +//===-- HostInfoAndroid.cpp -------------------------------------*- 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/android/HostInfoAndroid.h" +#include "lldb/Host/linux/HostInfoLinux.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb_private; +using namespace llvm; + +void +HostInfoAndroid::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ + HostInfoLinux::ComputeHostArchitectureSupport(arch_32, arch_64); + + if (arch_32.IsValid()) + { + arch_32.GetTriple().setEnvironment(llvm::Triple::Android); + } + if (arch_64.IsValid()) + { + arch_64.GetTriple().setEnvironment(llvm::Triple::Android); + } +} + +FileSpec +HostInfoAndroid::GetDefaultShell() +{ + return FileSpec("/system/bin/sh", false); +} + +FileSpec +HostInfoAndroid::ResolveLibraryPath(const std::string& module_path, const ArchSpec& arch) +{ + static const char* const ld_library_path_separator = ":"; + static const char* const default_lib32_path[] = { + "/vendor/lib", + "/system/lib", + nullptr + }; + static const char* const default_lib64_path[] = { + "/vendor/lib64", + "/system/lib64", + nullptr + }; + + if (module_path.empty() || module_path[0] == '/') + return FileSpec(module_path.c_str(), true); + + SmallVector ld_paths; + + if (const char* ld_library_path = ::getenv("LD_LIBRARY_PATH")) + StringRef(ld_library_path).split(ld_paths, StringRef(ld_library_path_separator), -1, false); + + const char* const* default_lib_path = nullptr; + switch (arch.GetAddressByteSize()) + { + case 4: + default_lib_path = default_lib32_path; + break; + case 8: + default_lib_path = default_lib64_path; + break; + default: + assert(false && "Unknown address byte size"); + return FileSpec(); + } + + for(const char* const* it = default_lib_path; *it; ++it) + ld_paths.push_back(StringRef(*it)); + + for (const StringRef& path : ld_paths) + { + FileSpec file_candidate(path.str().c_str(), true); + file_candidate.AppendPathComponent(module_path.c_str()); + + if (file_candidate.Exists()) + return file_candidate; + } + + return FileSpec(); +} + +bool +HostInfoAndroid::ComputeTempFileBaseDirectory(FileSpec &file_spec) +{ + bool success = HostInfoLinux::ComputeTempFileBaseDirectory(file_spec); + + // On Android, there is no path which is guaranteed to be writable. If the user has not + // provided a path via an environment variable, the generic algorithm will deduce /tmp, which + // is plain wrong. In that case we have an invalid directory, we substitute the path with + // /data/local/tmp, which is correct at least in some cases (i.e., when running as shell user). + if (!success || !file_spec.Exists()) + file_spec = FileSpec("/data/local/tmp", false); + + return file_spec.Exists(); +} diff --git a/source/Host/android/LibcGlue.cpp b/source/Host/android/LibcGlue.cpp new file mode 100644 index 000000000000..3842fb6c2a8e --- /dev/null +++ b/source/Host/android/LibcGlue.cpp @@ -0,0 +1,40 @@ +//===-- LibcGlue.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This files adds functions missing from libc on earlier versions of Android + +#include + +#include + +#if __ANDROID_API__ < 21 + +#include +#include +#include +#include + +#include "lldb/Host/Time.h" + +time_t timegm(struct tm* t) +{ + return (time_t) timegm64(t); +} + +int signalfd (int fd, const sigset_t *mask, int flags) +{ + return syscall(__NR_signalfd4, fd, mask, _NSIG / 8, flags); +} + +int posix_openpt(int flags) +{ + return open("/dev/ptmx", flags); +} + +#endif diff --git a/source/Host/android/ProcessLauncherAndroid.cpp b/source/Host/android/ProcessLauncherAndroid.cpp new file mode 100644 index 000000000000..24eebc8c030f --- /dev/null +++ b/source/Host/android/ProcessLauncherAndroid.cpp @@ -0,0 +1,108 @@ +//===-- ProcessLauncherAndroid.cpp ------------------------------*- 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/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/android/ProcessLauncherAndroid.h" + +#include "lldb/Target/ProcessLaunchInfo.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +static bool +DupDescriptor(const FileSpec &file_spec, int fd, int flags) +{ + int target_fd = ::open(file_spec.GetCString(), flags, 0666); + + if (target_fd == -1) + return false; + + if (::dup2(target_fd, fd) == -1) + return false; + + return (::close(target_fd) == -1) ? false : true; +} + +// If there is no PATH variable specified inside the environment then set the path to /system/bin. +// It is required because the default path used by execve() is wrong on android. +static void +FixupEnvironment(Args& env) +{ + static const char* path = "PATH="; + static const int path_len = ::strlen(path); + for (const char** args = env.GetConstArgumentVector(); *args; ++args) + if (::strncmp(path, *args, path_len) == 0) + return; + env.AppendArgument("PATH=/system/bin"); +} + +HostProcess +ProcessLauncherAndroid::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + // TODO: Handle other launch parameters specified in launc_info + + char exe_path[PATH_MAX]; + launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + + lldb::pid_t pid = ::fork(); + if (pid == static_cast(-1)) + { + // Fork failed + error.SetErrorStringWithFormat("Fork failed with error message: %s", strerror(errno)); + return HostProcess(LLDB_INVALID_PROCESS_ID); + } + else if (pid == 0) + { + if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO)) { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) + if (!DupDescriptor(file_spec, STDIN_FILENO, O_RDONLY)) + exit(-1); + } + + if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDOUT_FILENO)) { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) + if (!DupDescriptor(file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + exit(-1); + } + + if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDERR_FILENO)) { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) + if (!DupDescriptor(file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + exit(-1); + } + + // Child process + const char **argv = launch_info.GetArguments().GetConstArgumentVector(); + + Args env = launch_info.GetEnvironmentEntries(); + FixupEnvironment(env); + const char **envp = env.GetConstArgumentVector(); + + FileSpec working_dir = launch_info.GetWorkingDirectory(); + if (working_dir) + { + if (::chdir(working_dir.GetCString()) != 0) + exit(-1); + } + + execve(argv[0], + const_cast(argv), + const_cast(envp)); + exit(-1); + } + + return HostProcess(pid); +} diff --git a/source/Host/linux/AbstractSocket.cpp b/source/Host/linux/AbstractSocket.cpp new file mode 100644 index 000000000000..8ac0107123bb --- /dev/null +++ b/source/Host/linux/AbstractSocket.cpp @@ -0,0 +1,31 @@ +//===-- AbstractSocket.cpp --------------------------------------*- 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/linux/AbstractSocket.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +AbstractSocket::AbstractSocket(bool child_processes_inherit, Error &error) + : DomainSocket(ProtocolUnixAbstract, child_processes_inherit, error) +{ +} + +size_t +AbstractSocket::GetNameOffset() const +{ + return 1; +} + +void +AbstractSocket::DeleteSocketFile(llvm::StringRef name) +{ +} diff --git a/source/Host/linux/Host.cpp b/source/Host/linux/Host.cpp new file mode 100644 index 000000000000..cb7369fe7aec --- /dev/null +++ b/source/Host/linux/Host.cpp @@ -0,0 +1,393 @@ +//===-- source/Host/linux/Host.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#include +#include +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" + +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#ifdef __ANDROID_NDK__ +#include "lldb/Host/android/Android.h" +#endif +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" + +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Symbol/ObjectFile.h" +#include "Plugins/Process/Linux/ProcFileReader.h" + +using namespace lldb; +using namespace lldb_private; + +typedef enum ProcessStateFlags +{ + eProcessStateRunning = (1u << 0), // Running + eProcessStateSleeping = (1u << 1), // Sleeping in an interruptible wait + eProcessStateWaiting = (1u << 2), // Waiting in an uninterruptible disk sleep + eProcessStateZombie = (1u << 3), // Zombie + eProcessStateTracedOrStopped = (1u << 4), // Traced or stopped (on a signal) + eProcessStatePaging = (1u << 5) // Paging +} ProcessStateFlags; + +typedef struct ProcessStatInfo +{ + lldb::pid_t ppid; // Parent Process ID + uint32_t fProcessState; // ProcessStateFlags +} ProcessStatInfo; + +// Get the process info with additional information from /proc/$PID/stat (like process state, and tracer pid). +static bool GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid); + +static bool +ReadProcPseudoFileStat (lldb::pid_t pid, ProcessStatInfo& stat_info) +{ + // Read the /proc/$PID/stat file. + lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "stat"); + + // The filename of the executable is stored in parenthesis right after the pid. We look for the closing + // parenthesis for the filename and work from there in case the name has something funky like ')' in it. + const char *filename_end = strrchr ((const char *)buf_sp->GetBytes(), ')'); + if (filename_end) + { + char state = '\0'; + int ppid = LLDB_INVALID_PROCESS_ID; + + // Read state and ppid. + sscanf (filename_end + 1, " %c %d", &state, &ppid); + + stat_info.ppid = ppid; + + switch (state) + { + case 'R': + stat_info.fProcessState |= eProcessStateRunning; + break; + case 'S': + stat_info.fProcessState |= eProcessStateSleeping; + break; + case 'D': + stat_info.fProcessState |= eProcessStateWaiting; + break; + case 'Z': + stat_info.fProcessState |= eProcessStateZombie; + break; + case 'T': + stat_info.fProcessState |= eProcessStateTracedOrStopped; + break; + case 'W': + stat_info.fProcessState |= eProcessStatePaging; + break; + } + + return true; + } + + return false; +} + +static void +GetLinuxProcessUserAndGroup (lldb::pid_t pid, ProcessInstanceInfo &process_info, lldb::pid_t &tracerpid) +{ + tracerpid = 0; + uint32_t rUid = UINT32_MAX; // Real User ID + uint32_t eUid = UINT32_MAX; // Effective User ID + uint32_t rGid = UINT32_MAX; // Real Group ID + uint32_t eGid = UINT32_MAX; // Effective Group ID + + // Read the /proc/$PID/status file and parse the Uid:, Gid:, and TracerPid: fields. + lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "status"); + + static const char uid_token[] = "Uid:"; + char *buf_uid = strstr ((char *)buf_sp->GetBytes(), uid_token); + if (buf_uid) + { + // Real, effective, saved set, and file system UIDs. Read the first two. + buf_uid += sizeof(uid_token); + rUid = strtol (buf_uid, &buf_uid, 10); + eUid = strtol (buf_uid, &buf_uid, 10); + } + + static const char gid_token[] = "Gid:"; + char *buf_gid = strstr ((char *)buf_sp->GetBytes(), gid_token); + if (buf_gid) + { + // Real, effective, saved set, and file system GIDs. Read the first two. + buf_gid += sizeof(gid_token); + rGid = strtol (buf_gid, &buf_gid, 10); + eGid = strtol (buf_gid, &buf_gid, 10); + } + + static const char tracerpid_token[] = "TracerPid:"; + char *buf_tracerpid = strstr((char *)buf_sp->GetBytes(), tracerpid_token); + if (buf_tracerpid) + { + // Tracer PID. 0 if we're not being debugged. + buf_tracerpid += sizeof(tracerpid_token); + tracerpid = strtol (buf_tracerpid, &buf_tracerpid, 10); + } + + process_info.SetUserID (rUid); + process_info.SetEffectiveUserID (eUid); + process_info.SetGroupID (rGid); + process_info.SetEffectiveGroupID (eGid); +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + return process_linux::ProcFileReader::ReadIntoDataBuffer (process->GetID(), "auxv"); +} + +lldb::DataBufferSP +Host::GetAuxvData (lldb::pid_t pid) +{ + return process_linux::ProcFileReader::ReadIntoDataBuffer (pid, "auxv"); +} + +static bool +IsDirNumeric(const char *dname) +{ + for (; *dname; dname++) + { + if (!isdigit (*dname)) + return false; + } + return true; +} + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + static const char procdir[] = "/proc/"; + + DIR *dirproc = opendir (procdir); + if (dirproc) + { + struct dirent *direntry = NULL; + const uid_t our_uid = getuid(); + const lldb::pid_t our_pid = getpid(); + bool all_users = match_info.GetMatchAllUsers(); + + while ((direntry = readdir (dirproc)) != NULL) + { + if (direntry->d_type != DT_DIR || !IsDirNumeric (direntry->d_name)) + continue; + + lldb::pid_t pid = atoi (direntry->d_name); + + // Skip this process. + if (pid == our_pid) + continue; + + lldb::pid_t tracerpid; + ProcessStatInfo stat_info; + ProcessInstanceInfo process_info; + + if (!GetProcessAndStatInfo (pid, process_info, stat_info, tracerpid)) + continue; + + // Skip if process is being debugged. + if (tracerpid != 0) + continue; + + // Skip zombies. + if (stat_info.fProcessState & eProcessStateZombie) + continue; + + // Check for user match if we're not matching all users and not running as root. + if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) + continue; + + if (match_info.Matches (process_info)) + { + process_infos.Append (process_info); + } + } + + closedir (dirproc); + } + + return process_infos.GetSize(); +} + +bool +Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach) +{ + bool tids_changed = false; + static const char procdir[] = "/proc/"; + static const char taskdir[] = "/task/"; + std::string process_task_dir = procdir + std::to_string(pid) + taskdir; + DIR *dirproc = opendir (process_task_dir.c_str()); + + if (dirproc) + { + struct dirent *direntry = NULL; + while ((direntry = readdir (dirproc)) != NULL) + { + if (direntry->d_type != DT_DIR || !IsDirNumeric (direntry->d_name)) + continue; + + lldb::tid_t tid = atoi(direntry->d_name); + TidMap::iterator it = tids_to_attach.find(tid); + if (it == tids_to_attach.end()) + { + tids_to_attach.insert(TidPair(tid, false)); + tids_changed = true; + } + } + closedir (dirproc); + } + + return tids_changed; +} + +static bool +GetELFProcessCPUType (const char *exe_path, ProcessInstanceInfo &process_info) +{ + // Clear the architecture. + process_info.GetArchitecture().Clear(); + + ModuleSpecList specs; + FileSpec filespec (exe_path, false); + const size_t num_specs = ObjectFile::GetModuleSpecifications (filespec, 0, 0, specs); + // GetModuleSpecifications() could fail if the executable has been deleted or is locked. + // But it shouldn't return more than 1 architecture. + assert(num_specs <= 1 && "Linux plugin supports only a single architecture"); + if (num_specs == 1) + { + ModuleSpec module_spec; + if (specs.GetModuleSpecAtIndex (0, module_spec) && module_spec.GetArchitecture().IsValid()) + { + process_info.GetArchitecture () = module_spec.GetArchitecture(); + return true; + } + } + return false; +} + +static bool +GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid) +{ + tracerpid = 0; + process_info.Clear(); + ::memset (&stat_info, 0, sizeof(stat_info)); + stat_info.ppid = LLDB_INVALID_PROCESS_ID; + + // Use special code here because proc/[pid]/exe is a symbolic link. + char link_path[PATH_MAX]; + char exe_path[PATH_MAX] = ""; + if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", pid) <= 0) + return false; + + ssize_t len = readlink (link_path, exe_path, sizeof(exe_path) - 1); + if (len <= 0) + return false; + + // readlink does not append a null byte. + exe_path[len] = 0; + + // If the binary has been deleted, the link name has " (deleted)" appended. + // Remove if there. + static const ssize_t deleted_len = strlen(" (deleted)"); + if (len > deleted_len && + !strcmp(exe_path + len - deleted_len, " (deleted)")) + { + exe_path[len - deleted_len] = 0; + } + else + { + GetELFProcessCPUType (exe_path, process_info); + } + + process_info.SetProcessID(pid); + process_info.GetExecutableFile().SetFile(exe_path, false); + process_info.GetArchitecture().MergeFrom(HostInfo::GetArchitecture()); + + lldb::DataBufferSP buf_sp; + + // Get the process environment. + buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(pid, "environ"); + Args &info_env = process_info.GetEnvironmentEntries(); + char *next_var = (char *)buf_sp->GetBytes(); + char *end_buf = next_var + buf_sp->GetByteSize(); + while (next_var < end_buf && 0 != *next_var) + { + info_env.AppendArgument(next_var); + next_var += strlen(next_var) + 1; + } + + // Get the command line used to start the process. + buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(pid, "cmdline"); + + // Grab Arg0 first, if there is one. + char *cmd = (char *)buf_sp->GetBytes(); + if (cmd) + { + process_info.SetArg0(cmd); + + // Now process any remaining arguments. + Args &info_args = process_info.GetArguments(); + char *next_arg = cmd + strlen(cmd) + 1; + end_buf = cmd + buf_sp->GetByteSize(); + while (next_arg < end_buf && 0 != *next_arg) + { + info_args.AppendArgument(next_arg); + next_arg += strlen(next_arg) + 1; + } + } + + // Read /proc/$PID/stat to get our parent pid. + if (ReadProcPseudoFileStat (pid, stat_info)) + { + process_info.SetParentProcessID (stat_info.ppid); + } + + // Get User and Group IDs and get tracer pid. + GetLinuxProcessUserAndGroup (pid, process_info, tracerpid); + + return true; +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + lldb::pid_t tracerpid; + ProcessStatInfo stat_info; + + return GetProcessAndStatInfo (pid, process_info, stat_info, tracerpid); +} + +size_t +Host::GetEnvironment (StringList &env) +{ + char **host_env = environ; + char *env_entry; + size_t i; + for (i=0; (env_entry = host_env[i]) != NULL; ++i) + env.AppendString(env_entry); + return i; +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + return Error("unimplemented"); +} diff --git a/source/Host/linux/HostInfoLinux.cpp b/source/Host/linux/HostInfoLinux.cpp new file mode 100644 index 000000000000..4732a2a571b6 --- /dev/null +++ b/source/Host/linux/HostInfoLinux.cpp @@ -0,0 +1,282 @@ +//===-- HostInfoLinux.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Host/linux/HostInfoLinux.h" + +#include +#include +#include +#include + +#include +#include // std::once + +using namespace lldb_private; + +namespace +{ +struct HostInfoLinuxFields +{ + HostInfoLinuxFields() + : m_os_major(0) + , m_os_minor(0) + , m_os_update(0) + { + } + + std::string m_distribution_id; + uint32_t m_os_major; + uint32_t m_os_minor; + uint32_t m_os_update; +}; + +HostInfoLinuxFields *g_fields = nullptr; +} + +void +HostInfoLinux::Initialize() +{ + HostInfoPosix::Initialize(); + + g_fields = new HostInfoLinuxFields(); +} + +uint32_t +HostInfoLinux::GetMaxThreadNameLength() +{ + return 16; +} + +bool +HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + static bool success = false; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + + struct utsname un; + if (uname(&un) == 0) + { + int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update); + if (status == 3) + success = true; + else + { + // Some kernels omit the update version, so try looking for just "X.Y" and + // set update to 0. + g_fields->m_os_update = 0; + status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor); + if (status == 2) + success = true; + } + } + }); + + + major = g_fields->m_os_major; + minor = g_fields->m_os_minor; + update = g_fields->m_os_update; + return success; +} + +bool +HostInfoLinux::GetOSBuildString(std::string &s) +{ + struct utsname un; + ::memset(&un, 0, sizeof(utsname)); + s.clear(); + + if (uname(&un) < 0) + return false; + + s.assign(un.release); + return true; +} + +bool +HostInfoLinux::GetOSKernelDescription(std::string &s) +{ + struct utsname un; + + ::memset(&un, 0, sizeof(utsname)); + s.clear(); + + if (uname(&un) < 0) + return false; + + s.assign(un.version); + return true; +} + +llvm::StringRef +HostInfoLinux::GetDistributionId() +{ + // Try to run 'lbs_release -i', and use that response + // for the distribution id. + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST)); + if (log) + log->Printf("attempting to determine Linux distribution..."); + + // check if the lsb_release command exists at one of the + // following paths + const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"}; + + for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) + { + const char *const get_distribution_info_exe = exe_paths[exe_index]; + if (access(get_distribution_info_exe, F_OK)) + { + // this exe doesn't exist, move on to next exe + if (log) + log->Printf("executable doesn't exist: %s", get_distribution_info_exe); + continue; + } + + // execute the distribution-retrieval command, read output + std::string get_distribution_id_command(get_distribution_info_exe); + get_distribution_id_command += " -i"; + + FILE *file = popen(get_distribution_id_command.c_str(), "r"); + if (!file) + { + if (log) + log->Printf("failed to run command: \"%s\", cannot retrieve " + "platform information", + get_distribution_id_command.c_str()); + break; + } + + // retrieve the distribution id string. + char distribution_id[256] = {'\0'}; + if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL) + { + if (log) + log->Printf("distribution id command returned \"%s\"", distribution_id); + + const char *const distributor_id_key = "Distributor ID:\t"; + if (strstr(distribution_id, distributor_id_key)) + { + // strip newlines + std::string id_string(distribution_id + strlen(distributor_id_key)); + id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end()); + + // lower case it and convert whitespace to underscores + std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch) + { + return tolower(isspace(ch) ? '_' : ch); + }); + + g_fields->m_distribution_id = id_string; + if (log) + log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str()); + } + else + { + if (log) + log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id); + } + } + else + { + if (log) + log->Printf("failed to retrieve distribution id, \"%s\" returned no" + " lines", + get_distribution_id_command.c_str()); + } + + // clean up the file + pclose(file); + } + }); + + return g_fields->m_distribution_id.c_str(); +} + +FileSpec +HostInfoLinux::GetProgramFileSpec() +{ + static FileSpec g_program_filespec; + + if (!g_program_filespec) + { + char exe_path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len > 0) + { + exe_path[len] = 0; + g_program_filespec.SetFile(exe_path, false); + } + } + + return g_program_filespec; +} + +bool +HostInfoLinux::ComputeSupportExeDirectory(FileSpec &file_spec) +{ + if (HostInfoPosix::ComputeSupportExeDirectory(file_spec) && + file_spec.IsAbsolute() && + file_spec.Exists()) + return true; + file_spec.GetDirectory() = GetProgramFileSpec().GetDirectory(); + return !file_spec.GetDirectory().IsEmpty(); +} + +bool +HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec) +{ + FileSpec temp_file("/usr/lib/lldb", true); + file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); + return true; +} + +bool +HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec) +{ + // XDG Base Directory Specification + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. + const char *xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home && xdg_data_home[0]) + { + std::string user_plugin_dir(xdg_data_home); + user_plugin_dir += "/lldb"; + file_spec.GetDirectory().SetCString(user_plugin_dir.c_str()); + } + else + file_spec.GetDirectory().SetCString("~/.local/share/lldb"); + return true; +} + +void +HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ + HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); + + const char *distribution_id = GetDistributionId().data(); + + // On Linux, "unknown" in the vendor slot isn't what we want for the default + // triple. It's probably an artifact of config.guess. + if (arch_32.IsValid()) + { + arch_32.SetDistributionId(distribution_id); + if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) + arch_32.GetTriple().setVendorName(llvm::StringRef()); + } + if (arch_64.IsValid()) + { + arch_64.SetDistributionId(distribution_id); + if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) + arch_64.GetTriple().setVendorName(llvm::StringRef()); + } +} diff --git a/source/Host/linux/HostThreadLinux.cpp b/source/Host/linux/HostThreadLinux.cpp new file mode 100644 index 000000000000..2312ced0107b --- /dev/null +++ b/source/Host/linux/HostThreadLinux.cpp @@ -0,0 +1,52 @@ +//===-- HostThreadLinux.cpp -------------------------------------*- 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/DataBuffer.h" +#include "lldb/Host/linux/HostThreadLinux.h" +#include "Plugins/Process/Linux/ProcFileReader.h" + +#include "llvm/ADT/SmallVector.h" + +#include + +using namespace lldb_private; + +HostThreadLinux::HostThreadLinux() + : HostThreadPosix() +{ +} + +HostThreadLinux::HostThreadLinux(lldb::thread_t thread) + : HostThreadPosix(thread) +{ +} + +void +HostThreadLinux::SetName(lldb::thread_t thread, llvm::StringRef name) +{ +#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__) + ::pthread_setname_np(thread, name.data()); +#else + (void) thread; + (void) name; +#endif +} + +void +HostThreadLinux::GetName(lldb::thread_t thread, llvm::SmallVectorImpl &name) +{ + // Read /proc/$TID/comm file. + lldb::DataBufferSP buf_sp = process_linux::ProcFileReader::ReadIntoDataBuffer(thread, "comm"); + const char *comm_str = (const char *)buf_sp->GetBytes(); + const char *cr_str = ::strchr(comm_str, '\n'); + size_t length = cr_str ? (cr_str - comm_str) : strlen(comm_str); + + name.clear(); + name.append(comm_str, comm_str + length); +} diff --git a/source/Host/linux/LibcGlue.cpp b/source/Host/linux/LibcGlue.cpp new file mode 100644 index 000000000000..63d026f76c62 --- /dev/null +++ b/source/Host/linux/LibcGlue.cpp @@ -0,0 +1,30 @@ +//===-- LibcGlue.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file adds functions missing from libc on older versions of linux + +#include +#include +#include +#include + +#ifndef HAVE_PROCESS_VM_READV // If the syscall wrapper is not available, provide one. +ssize_t process_vm_readv(::pid_t pid, + const struct iovec *local_iov, unsigned long liovcnt, + const struct iovec *remote_iov, unsigned long riovcnt, + unsigned long flags) +{ +#ifdef HAVE_NR_PROCESS_VM_READV // If we have the syscall number, we can issue the syscall ourselves. + return syscall(__NR_process_vm_readv, pid, local_iov, liovcnt, remote_iov, riovcnt, flags); +#else // If not, let's pretend the syscall is not present. + errno = ENOSYS; + return -1; +#endif +} +#endif diff --git a/source/Host/linux/ThisThread.cpp b/source/Host/linux/ThisThread.cpp new file mode 100644 index 000000000000..1c68c8ba16d7 --- /dev/null +++ b/source/Host/linux/ThisThread.cpp @@ -0,0 +1,29 @@ +//===-- ThisThread.cpp ------------------------------------------*- 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/HostNativeThread.h" +#include "lldb/Host/ThisThread.h" + +#include "llvm/ADT/SmallVector.h" + +#include + +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name) +{ + HostNativeThread::SetName(::pthread_self(), name); +} + +void +ThisThread::GetName(llvm::SmallVectorImpl &name) +{ + HostNativeThread::GetName(::pthread_self(), name); +} diff --git a/source/Host/macosx/Host.mm b/source/Host/macosx/Host.mm new file mode 100644 index 000000000000..2db27a276e4a --- /dev/null +++ b/source/Host/macosx/Host.mm @@ -0,0 +1,1587 @@ +//===-- 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 "lldb/Host/Host.h" + +#include + +#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +#define NO_XPC_SERVICES 1 +#endif + +#if !defined(NO_XPC_SERVICES) +#define __XPC_PRIVATE_H__ +#include + +#define LaunchUsingXPCRightName "com.apple.dt.Xcode.RootDebuggingXPCService" + +// These XPC messaging keys are used for communication between Host.mm and the XPC service. +#define LauncherXPCServiceAuthKey "auth-key" +#define LauncherXPCServiceArgPrefxKey "arg" +#define LauncherXPCServiceEnvPrefxKey "env" +#define LauncherXPCServiceCPUTypeKey "cpuType" +#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags" +#define LauncherXPCServiceStdInPathKeyKey "stdInPath" +#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath" +#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath" +#define LauncherXPCServiceChildPIDKey "childPID" +#define LauncherXPCServiceErrorTypeKey "errorType" +#define LauncherXPCServiceCodeTypeKey "errorCode" + +#endif + +#include "llvm/Support/Host.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/NameMatches.h" + +#include "cfcpp/CFCBundle.h" +#include "cfcpp/CFCMutableArray.h" +#include "cfcpp/CFCMutableDictionary.h" +#include "cfcpp/CFCReleaser.h" +#include "cfcpp/CFCString.h" + + +#include + +#include +#include + +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +#endif + +extern "C" +{ + int __pthread_chdir(const char *path); + int __pthread_fchdir (int fildes); +} + +using namespace lldb; +using namespace lldb_private; + +bool +Host::GetBundleDirectory (const FileSpec &file, FileSpec &bundle_directory) +{ +#if defined (__APPLE__) + if (file.GetFileType () == FileSpec::eFileTypeDirectory) + { + char path[PATH_MAX]; + if (file.GetPath(path, sizeof(path))) + { + CFCBundle bundle (path); + if (bundle.GetPath (path, sizeof(path))) + { + bundle_directory.SetFile (path, false); + return true; + } + } + } +#endif + bundle_directory.Clear(); + return false; +} + + +bool +Host::ResolveExecutableInBundle (FileSpec &file) +{ +#if defined (__APPLE__) + 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, false); + return true; + } + } + } + } +#endif + return false; +} + +static void * +AcceptPIDFromInferior (void *arg) +{ + const char *connect_url = (const char *)arg; + ConnectionFileDescriptor file_conn; + Error error; + if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess) + { + char pid_str[256]; + ::memset (pid_str, 0, sizeof(pid_str)); + ConnectionStatus status; + const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL); + if (pid_str_len > 0) + { + int pid = atoi (pid_str); + return (void *)(intptr_t)pid; + } + } + return NULL; +} + +static bool +WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) +{ + const int time_delta_usecs = 100000; + const int num_retries = timeout_in_seconds/time_delta_usecs; + for (int i=0; iIsValid()) +// { +// command_file.Printf("--arch=%s ", arch_spec->GetArchitectureName()); +// } +// +// if (disable_aslr) +// { +// command_file.PutCString("--disable-aslr "); +// } +// +// command_file.PutCString("-- "); +// +// if (argv) +// { +// for (size_t i=0; argv[i] != NULL; ++i) +// { +// command_file.Printf("\"%s\" ", argv[i]); +// } +// } +// command_file.PutCString("\necho Process exited with status $?\n"); +// command_file.GetFile().Close(); +// if (::chmod (temp_file_path, S_IRWXU | S_IRWXG) != 0) +// return LLDB_INVALID_PROCESS_ID; +// +// CFCMutableDictionary cf_env_dict; +// +// const bool can_create = true; +// if (envp) +// { +// for (size_t i=0; envp[i] != NULL; ++i) +// { +// const char *env_entry = envp[i]; +// const char *equal_pos = strchr(env_entry, '='); +// if (equal_pos) +// { +// std::string env_key (env_entry, equal_pos); +// std::string env_val (equal_pos + 1); +// CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8); +// CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8); +// cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create); +// } +// } +// } +// +// LSApplicationParameters app_params; +// ::memset (&app_params, 0, sizeof (app_params)); +// app_params.flags = kLSLaunchDontAddToRecents | kLSLaunchAsync; +// app_params.argv = NULL; +// app_params.environment = (CFDictionaryRef)cf_env_dict.get(); +// +// CFCReleaser command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL, +// (const UInt8 *)temp_file_path, +// strlen(temp_file_path), +// false)); +// +// CFCMutableArray urls; +// +// // Terminal.app will open the ".command" file we have created +// // and run our process inside it which will wait at the entry point +// // for us to attach. +// urls.AppendValue(command_file_url.get()); +// +// +// lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +// +// Error lldb_error; +// // Sleep and wait a bit for debugserver to start to listen... +// char connect_url[128]; +// ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str()); +// +// // Spawn a new thread to accept incoming connection on the connect_url +// // so we can grab the pid from the inferior +// lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(), +// AcceptPIDFromInferior, +// connect_url, +// &lldb_error); +// +// ProcessSerialNumber psn; +// error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1); +// if (error == noErr) +// { +// thread_result_t accept_thread_result = NULL; +// if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error)) +// { +// if (accept_thread_result) +// { +// pid = (intptr_t)accept_thread_result; +// +// // Wait for process to be stopped the entry point by watching +// // for the process status to be set to SSTOP which indicates it it +// // SIGSTOP'ed at the entry point +// WaitForProcessToSIGSTOP (pid, 5); +// } +// } +// } +// else +// { +// Host::ThreadCancel (accept_thread, &lldb_error); +// } +// +// return pid; +//} + +const char *applscript_in_new_tty = +"tell application \"Terminal\"\n" +" do script \"%s\"\n" +"end tell\n"; + + +const char *applscript_in_existing_tty = "\ +set the_shell_script to \"%s\"\n\ +tell application \"Terminal\"\n\ + repeat with the_window in (get windows)\n\ + repeat with the_tab in tabs of the_window\n\ + set the_tty to tty in the_tab\n\ + if the_tty contains \"%s\" then\n\ + if the_tab is not busy then\n\ + set selected of the_tab to true\n\ + set frontmost of the_window to true\n\ + do script the_shell_script in the_tab\n\ + return\n\ + end if\n\ + end if\n\ + end repeat\n\ + end repeat\n\ + do script the_shell_script\n\ +end tell\n"; + + +static Error +LaunchInNewTerminalWithAppleScript (const char *exe_path, ProcessLaunchInfo &launch_info) +{ + Error error; + char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; + if (::mktemp (unix_socket_name) == NULL) + { + error.SetErrorString ("failed to make temporary path for a unix socket"); + return error; + } + + StreamString command; + FileSpec darwin_debug_file_spec; + if (!HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, darwin_debug_file_spec)) + { + error.SetErrorString ("can't locate the 'darwin-debug' executable"); + return error; + } + + darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); + + if (!darwin_debug_file_spec.Exists()) + { + error.SetErrorStringWithFormat ("the 'darwin-debug' executable doesn't exists at '%s'", + darwin_debug_file_spec.GetPath().c_str()); + return error; + } + + char launcher_path[PATH_MAX]; + darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); + + const ArchSpec &arch_spec = launch_info.GetArchitecture(); + // Only set the architecture if it is valid and if it isn't Haswell (x86_64h). + if (arch_spec.IsValid() && arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h) + command.Printf("arch -arch %s ", arch_spec.GetArchitectureName()); + + command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name); + + if (arch_spec.IsValid()) + command.Printf(" --arch=%s", arch_spec.GetArchitectureName()); + + FileSpec working_dir{launch_info.GetWorkingDirectory()}; + if (working_dir) + command.Printf(" --working-dir '%s'", working_dir.GetCString()); + else + { + char cwd[PATH_MAX]; + if (getcwd(cwd, PATH_MAX)) + command.Printf(" --working-dir '%s'", cwd); + } + + if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) + command.PutCString(" --disable-aslr"); + + // We are launching on this host in a terminal. So compare the environment on the host + // to what is supplied in the launch_info. Any items that aren't in the host environment + // need to be sent to darwin-debug. If we send all environment entries, we might blow the + // max command line length, so we only send user modified entries. + const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector (); + + StringList host_env; + const size_t host_env_count = Host::GetEnvironment (host_env); + + if (envp && envp[0]) + { + const char *env_entry; + for (size_t env_idx = 0; (env_entry = envp[env_idx]) != NULL; ++env_idx) + { + bool add_entry = true; + for (size_t i=0; i file_URL (::CFURLCreateWithFileSystemPath (NULL, + file_cfstr.get(), + kCFURLPOSIXPathStyle, + false)); + + if (log) + log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no); + + long error; + BabelAESelInfo file_and_line_info = + { + 0, // reserved0 + (int16_t)(line_no - 1), // fLineNumber (zero based line number) + 1, // fSelStart + 1024, // fSelEnd + 0, // reserved1 + 0 // reserved2 + }; + + AEKeyDesc file_and_line_desc; + + error = ::AECreateDesc (typeUTF8Text, + &file_and_line_info, + sizeof (file_and_line_info), + &(file_and_line_desc.descContent)); + + if (error != noErr) + { + if (log) + log->Printf("Error creating AEDesc: %ld.\n", error); + return false; + } + + file_and_line_desc.descKey = keyAEPosition; + + static std::string g_app_name; + static FSRef g_app_fsref; + + LSApplicationParameters app_params; + ::memset (&app_params, 0, sizeof (app_params)); + app_params.flags = kLSLaunchDefaults | + kLSLaunchDontAddToRecents | + kLSLaunchDontSwitch; + + char *external_editor = ::getenv ("LLDB_EXTERNAL_EDITOR"); + + if (external_editor) + { + if (log) + log->Printf("Looking for external editor \"%s\".\n", external_editor); + + if (g_app_name.empty() || strcmp (g_app_name.c_str(), external_editor) != 0) + { + CFCString editor_name (external_editor, kCFStringEncodingUTF8); + error = ::LSFindApplicationForInfo (kLSUnknownCreator, + NULL, + editor_name.get(), + &g_app_fsref, + NULL); + + // If we found the app, then store away the name so we don't have to re-look it up. + if (error != noErr) + { + if (log) + log->Printf("Could not find External Editor application, error: %ld.\n", error); + return false; + } + + } + app_params.application = &g_app_fsref; + } + + ProcessSerialNumber psn; + CFCReleaser file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL)); + error = ::LSOpenURLsWithRole (file_array.get(), + kLSRolesAll, + &file_and_line_desc, + &app_params, + &psn, + 1); + + AEDisposeDesc (&(file_and_line_desc.descContent)); + + if (error != noErr) + { + if (log) + log->Printf("LSOpenURLsWithRole failed, error: %ld.\n", error); + + return false; + } + + return true; +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) +} + +size_t +Host::GetEnvironment (StringList &env) +{ + char **host_env = *_NSGetEnviron(); + char *env_entry; + size_t i; + for (i=0; (env_entry = host_env[i]) != NULL; ++i) + env.AppendString(env_entry); + return i; + +} + +static bool +GetMacOSXProcessCPUType (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + // Make a new mib to stay thread safe + int mib[CTL_MAXNAME]={0,}; + size_t mib_len = CTL_MAXNAME; + if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len)) + return false; + + mib[mib_len] = process_info.GetProcessID(); + mib_len++; + + cpu_type_t cpu, sub = 0; + size_t len = sizeof(cpu); + if (::sysctl (mib, mib_len, &cpu, &len, 0, 0) == 0) + { + switch (cpu) + { + case CPU_TYPE_I386: sub = CPU_SUBTYPE_I386_ALL; break; + case CPU_TYPE_X86_64: sub = CPU_SUBTYPE_X86_64_ALL; break; + +#if defined (CPU_TYPE_ARM64) && defined (CPU_SUBTYPE_ARM64_ALL) + case CPU_TYPE_ARM64: sub = CPU_SUBTYPE_ARM64_ALL; break; +#endif + + case CPU_TYPE_ARM: + { + // Note that we fetched the cpu type from the PROCESS but we can't get a cpusubtype of the + // process -- we can only get the host's cpu subtype. + uint32_t cpusubtype = 0; + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + sub = cpusubtype; + + bool host_cpu_is_64bit; + uint32_t is64bit_capable; + size_t is64bit_capable_len = sizeof (is64bit_capable); + if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, &is64bit_capable_len, NULL, 0) == 0) + host_cpu_is_64bit = true; + else + host_cpu_is_64bit = false; + + // if the host is an armv8 device, its cpusubtype will be in CPU_SUBTYPE_ARM64 numbering + // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value instead. + + if (host_cpu_is_64bit) + { + sub = CPU_SUBTYPE_ARM_V7; + } + } + break; + + default: + break; + } + process_info.GetArchitecture ().SetArchitecture (eArchTypeMachO, cpu, sub); + return true; + } + } + process_info.GetArchitecture().Clear(); + return false; +} + +static bool +GetMacOSXProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, + ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + int proc_args_mib[3] = { CTL_KERN, KERN_PROCARGS2, (int)process_info.GetProcessID() }; + + size_t arg_data_size = 0; + if (::sysctl (proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) || arg_data_size == 0) + arg_data_size = 8192; + + // Add a few bytes to the calculated length, I know we need to add at least one byte + // to this number otherwise we get junk back, so add 128 just in case... + DataBufferHeap arg_data(arg_data_size+128, 0); + arg_data_size = arg_data.GetByteSize(); + if (::sysctl (proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size , NULL, 0) == 0) + { + DataExtractor data (arg_data.GetBytes(), arg_data_size, endian::InlHostByteOrder(), sizeof(void *)); + lldb::offset_t offset = 0; + uint32_t argc = data.GetU32 (&offset); + llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); + const llvm::Triple::ArchType triple_arch = triple.getArch(); + const bool check_for_ios_simulator = (triple_arch == llvm::Triple::x86 || triple_arch == llvm::Triple::x86_64); + const char *cstr = data.GetCStr (&offset); + if (cstr) + { + process_info.GetExecutableFile().SetFile(cstr, false); + + if (match_info_ptr == NULL || + NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(), + match_info_ptr->GetNameMatchType(), + match_info_ptr->GetProcessInfo().GetName())) + { + // Skip NULLs + while (1) + { + const uint8_t *p = data.PeekData(offset, 1); + if ((p == NULL) || (*p != '\0')) + break; + ++offset; + } + // Now extract all arguments + Args &proc_args = process_info.GetArguments(); + for (int i=0; i(argc); ++i) + { + cstr = data.GetCStr(&offset); + if (cstr) + proc_args.AppendArgument(cstr); + } + + Args &proc_env = process_info.GetEnvironmentEntries (); + while ((cstr = data.GetCStr(&offset))) + { + if (cstr[0] == '\0') + break; + + if (check_for_ios_simulator) + { + if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0) + process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::IOS); + else + process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::MacOSX); + } + + proc_env.AppendArgument(cstr); + } + return true; + } + } + } + } + return false; +} + +static bool +GetMacOSXProcessUserAndGroup (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = process_info.GetProcessID(); + struct kinfo_proc proc_kinfo; + size_t proc_kinfo_size = sizeof(struct kinfo_proc); + + if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) + { + if (proc_kinfo_size > 0) + { + process_info.SetParentProcessID (proc_kinfo.kp_eproc.e_ppid); + process_info.SetUserID (proc_kinfo.kp_eproc.e_pcred.p_ruid); + process_info.SetGroupID (proc_kinfo.kp_eproc.e_pcred.p_rgid); + process_info.SetEffectiveUserID (proc_kinfo.kp_eproc.e_ucred.cr_uid); + if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + process_info.SetEffectiveGroupID (proc_kinfo.kp_eproc.e_ucred.cr_groups[0]); + else + process_info.SetEffectiveGroupID (UINT32_MAX); + return true; + } + } + } + process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID); + process_info.SetUserID (UINT32_MAX); + process_info.SetGroupID (UINT32_MAX); + process_info.SetEffectiveUserID (UINT32_MAX); + process_info.SetEffectiveGroupID (UINT32_MAX); + return false; +} + + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + std::vector kinfos; + + int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + + size_t pid_data_size = 0; + if (::sysctl (mib, 4, NULL, &pid_data_size, NULL, 0) != 0) + return 0; + + // Add a few extra in case a few more show up + const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10; + + kinfos.resize (estimated_pid_count); + pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); + + if (::sysctl (mib, 4, &kinfos[0], &pid_data_size, NULL, 0) != 0) + return 0; + + const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); + + bool all_users = match_info.GetMatchAllUsers(); + const lldb::pid_t our_pid = getpid(); + const uid_t our_uid = getuid(); + for (size_t i = 0; i < actual_pid_count; i++) + { + const struct kinfo_proc &kinfo = kinfos[i]; + + bool kinfo_user_matches = false; + if (all_users) + kinfo_user_matches = true; + else + kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid; + + // Special case, if lldb is being run as root we can attach to anything. + if (our_uid == 0) + kinfo_user_matches = true; + + if (kinfo_user_matches == false || // Make sure the user is acceptable + static_cast(kinfo.kp_proc.p_pid) == our_pid || // Skip this process + kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero) + kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains... + kinfo.kp_proc.p_flag & P_TRACED || // Being debugged? + kinfo.kp_proc.p_flag & P_WEXIT || // Working on exiting? + kinfo.kp_proc.p_flag & P_TRANSLATED) // Skip translated ppc (Rosetta) + continue; + + ProcessInstanceInfo process_info; + process_info.SetProcessID (kinfo.kp_proc.p_pid); + process_info.SetParentProcessID (kinfo.kp_eproc.e_ppid); + process_info.SetUserID (kinfo.kp_eproc.e_pcred.p_ruid); + process_info.SetGroupID (kinfo.kp_eproc.e_pcred.p_rgid); + process_info.SetEffectiveUserID (kinfo.kp_eproc.e_ucred.cr_uid); + if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + process_info.SetEffectiveGroupID (kinfo.kp_eproc.e_ucred.cr_groups[0]); + else + process_info.SetEffectiveGroupID (UINT32_MAX); + + // Make sure our info matches before we go fetch the name and cpu type + if (match_info.Matches (process_info)) + { + // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 + if (GetMacOSXProcessCPUType (process_info)) + { + if (GetMacOSXProcessArgs (&match_info, process_info)) + { + if (match_info.Matches (process_info)) + process_infos.Append (process_info); + } + } + } + } + return process_infos.GetSize(); +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.SetProcessID(pid); + bool success = false; + + // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 + if (GetMacOSXProcessCPUType (process_info)) + success = true; + + if (GetMacOSXProcessArgs (NULL, process_info)) + success = true; + + if (GetMacOSXProcessUserAndGroup (process_info)) + success = true; + + if (success) + return true; + + process_info.Clear(); + return false; +} + +#if !NO_XPC_SERVICES +static void +PackageXPCArguments (xpc_object_t message, const char *prefix, const Args& args) +{ + size_t count = args.GetArgumentCount(); + char buf[50]; // long enough for 'argXXX' + memset(buf, 0, 50); + sprintf(buf, "%sCount", prefix); + xpc_dictionary_set_int64(message, buf, count); + for (size_t i=0; iGetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, file_action->GetPath()); + } + file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); + if (file_action && file_action->GetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, file_action->GetPath()); + } + file_action = launch_info.GetFileActionForFD(STDERR_FILENO); + if (file_action && file_action->GetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, file_action->GetPath()); + } + + xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message); + xpc_type_t returnType = xpc_get_type(reply); + if (returnType == XPC_TYPE_DICTIONARY) + { + pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey); + if (pid == 0) + { + int errorType = xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey); + int errorCode = xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey); + + error.SetError(errorCode, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Problems with launching via XPC. Error type : %i, code : %i", errorType, errorCode); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + + if (authorizationRef) + { + AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + authorizationRef = NULL; + } + } + } + else if (returnType == XPC_TYPE_ERROR) + { + error.SetError(5, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Problems with launching via XPC. XPC error : %s", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + } + + return error; +#else + Error error; + return error; +#endif +} + +static bool +ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) +{ + bool result = false; + +#if !NO_XPC_SERVICES + bool launchingAsRoot = launch_info.GetUserID() == 0; + bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0; + + if (launchingAsRoot && !currentUserIsRoot) + { + // If current user is already root, we don't need XPC's help. + result = true; + } +#endif + + return result; +} + +Error +Host::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + char exe_path[PATH_MAX]; + PlatformSP host_platform_sp (Platform::GetHostPlatform ()); + + ModuleSpec exe_module_spec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()); + + FileSpec::FileType file_type = exe_module_spec.GetFileSpec().GetFileType(); + if (file_type != FileSpec::eFileTypeRegular) + { + lldb::ModuleSP exe_module_sp; + error = host_platform_sp->ResolveExecutable (exe_module_spec, + exe_module_sp, + NULL); + + if (error.Fail()) + return error; + + if (exe_module_sp) + exe_module_spec.GetFileSpec() = exe_module_sp->GetFileSpec(); + } + + if (exe_module_spec.GetFileSpec().Exists()) + { + exe_module_spec.GetFileSpec().GetPath (exe_path, sizeof(exe_path)); + } + else + { + launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path); + return error; + } + + if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) + { +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + return LaunchInNewTerminalWithAppleScript (exe_path, launch_info); +#else + error.SetErrorString ("launching a process in a new terminal is not supported on iOS devices"); + return error; +#endif + } + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + if (ShouldLaunchUsingXPC(launch_info)) + { + error = LaunchProcessXPC(exe_path, launch_info, pid); + } + else + { + error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); + } + + if (pid != LLDB_INVALID_PROCESS_ID) + { + // If all went well, then set the process ID into the launch info + launch_info.SetProcessID(pid); + + // Make sure we reap any processes we spawn or we will have zombies. + if (!launch_info.MonitorProcess()) + { + const bool monitor_signals = false; + Host::MonitorChildProcessCallback callback = nullptr; + + if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus)) + callback = Process::SetProcessExitStatus; + + StartMonitoringChildProcess (callback, + NULL, + pid, + monitor_signals); + } + } + else + { + // Invalid process ID, something didn't go well + if (error.Success()) + error.SetErrorString ("process launch failed for unknown reasons"); + } + return error; +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + Error error; + if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) + { + FileSpec expand_tool_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec)) + { + error.SetErrorString("could not get support executable directory for lldb-argdumper tool"); + return error; + } + expand_tool_spec.AppendPathComponent("lldb-argdumper"); + if (!expand_tool_spec.Exists()) + { + error.SetErrorStringWithFormat("could not find the lldb-argdumper tool: %s", expand_tool_spec.GetPath().c_str()); + return error; + } + + StreamString expand_tool_spec_stream; + expand_tool_spec_stream.Printf("\"%s\"",expand_tool_spec.GetPath().c_str()); + + Args expand_command(expand_tool_spec_stream.GetData()); + expand_command.AppendArguments (launch_info.GetArguments()); + + int status; + std::string output; + FileSpec cwd(launch_info.GetWorkingDirectory()); + if (!cwd.Exists()) + { + char *wd = getcwd(nullptr, 0); + if (wd == nullptr) + { + error.SetErrorStringWithFormat("cwd does not exist; cannot launch with shell argument expansion"); + return error; + } + else + { + FileSpec working_dir(wd, false); + free(wd); + launch_info.SetWorkingDirectory(working_dir); + + } + } + RunShellCommand(expand_command, cwd, &status, nullptr, &output, 10); + + if (status != 0) + { + error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status); + return error; + } + + auto data_sp = StructuredData::ParseJSON(output); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto dict_sp = data_sp->GetAsDictionary(); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); + if (!args_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_array_sp = args_sp->GetAsArray(); + if (!args_array_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + launch_info.GetArguments().Clear(); + + for (size_t i = 0; + i < args_array_sp->GetSize(); + i++) + { + auto item_sp = args_array_sp->GetItemAtIndex(i); + if (!item_sp) + continue; + auto str_sp = item_sp->GetAsString(); + if (!str_sp) + continue; + + launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); + } + } + + return error; +} + +HostThread +Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) +{ + unsigned long mask = DISPATCH_PROC_EXIT; + if (monitor_signals) + mask |= DISPATCH_PROC_SIGNAL; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + + dispatch_source_t source = ::dispatch_source_create (DISPATCH_SOURCE_TYPE_PROC, + pid, + mask, + ::dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT,0)); + + if (log) + log->Printf ("Host::StartMonitoringChildProcess (callback=%p, baton=%p, pid=%i, monitor_signals=%i) source = %p\n", + callback, + callback_baton, + (int)pid, + monitor_signals, + source); + + if (source) + { + ::dispatch_source_set_cancel_handler (source, ^{ + ::dispatch_release (source); + }); + ::dispatch_source_set_event_handler (source, ^{ + + int status= 0; + int wait_pid = 0; + bool cancel = false; + bool exited = false; + do + { + wait_pid = ::waitpid (pid, &status, 0); + } while (wait_pid < 0 && errno == EINTR); + + if (wait_pid >= 0) + { + 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 ("::waitpid (pid = %llu, &status, 0) => pid = %i, status = 0x%8.8x (%s), signal = %i, exit_status = %i", + pid, + wait_pid, + status, + status_cstr, + signal, + exit_status); + + if (callback) + cancel = callback (callback_baton, pid, exited, signal, exit_status); + + if (exited || cancel) + { + ::dispatch_source_cancel(source); + } + } + }); + + ::dispatch_resume (source); + } + return HostThread(); +} + +//---------------------------------------------------------------------- +// Log to both stderr and to ASL Logging when running on MacOSX. +//---------------------------------------------------------------------- +void +Host::SystemLog (SystemLogType type, const char *format, va_list args) +{ + if (format && format[0]) + { + 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.LLDB.framework"); + ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); + } + + // Copy the va_list so we can log this message twice + va_list copy_args; + va_copy (copy_args, args); + // Log to stderr + ::vfprintf (stderr, format, copy_args); + va_end (copy_args); + + int asl_level; + switch (type) + { + case eSystemLogError: + asl_level = ASL_LEVEL_ERR; + break; + + case eSystemLogWarning: + asl_level = ASL_LEVEL_WARNING; + break; + } + + // Log to ASL + ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); + } +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + return lldb::DataBufferSP(); +} diff --git a/source/Host/macosx/HostInfoMacOSX.mm b/source/Host/macosx/HostInfoMacOSX.mm new file mode 100644 index 000000000000..f5a0540e8774 --- /dev/null +++ b/source/Host/macosx/HostInfoMacOSX.mm @@ -0,0 +1,372 @@ +//===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if !defined(LLDB_DISABLE_PYTHON) +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#endif + +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/macosx/HostInfoMacOSX.h" +#include "lldb/Core/Log.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/SafeMachO.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +// C++ Includes +#include + +// C inclues +#include +#include +#include +#include + +// Objective C/C++ includes +#include +#include +#include +#include + +// These are needed when compiling on systems +// that do not yet have these definitions +#include +#ifndef CPU_SUBTYPE_X86_64_H +#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) +#endif +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM|CPU_ARCH_ABI64) +#endif + +#include // for TARGET_OS_TV, TARGET_OS_WATCH + +using namespace lldb_private; + +bool +HostInfoMacOSX::GetOSBuildString(std::string &s) +{ + int mib[2] = {CTL_KERN, KERN_OSVERSION}; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign(cstr, cstr_len); + return true; + } + + s.clear(); + return false; +} + +bool +HostInfoMacOSX::GetOSKernelDescription(std::string &s) +{ + int mib[2] = {CTL_KERN, KERN_VERSION}; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign(cstr, cstr_len); + return true; + } + s.clear(); + return false; +} + +bool +HostInfoMacOSX::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + static uint32_t g_major = 0; + static uint32_t g_minor = 0; + static uint32_t g_update = 0; + + if (g_major == 0) + { + @autoreleasepool + { + NSDictionary *version_info = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *version_value = [version_info objectForKey:@"ProductVersion"]; + const char *version_str = [version_value UTF8String]; + if (version_str) + Args::StringToVersion(version_str, g_major, g_minor, g_update); + } + } + + if (g_major != 0) + { + major = g_major; + minor = g_minor; + update = g_update; + return true; + } + return false; +} + +FileSpec +HostInfoMacOSX::GetProgramFileSpec() +{ + static FileSpec g_program_filespec; + if (!g_program_filespec) + { + char program_fullpath[PATH_MAX]; + // If DST is NULL, then return the number of bytes needed. + uint32_t len = sizeof(program_fullpath); + int err = _NSGetExecutablePath(program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile(program_fullpath, false); + else if (err == -1) + { + char *large_program_fullpath = (char *)::malloc(len + 1); + + err = _NSGetExecutablePath(large_program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile(large_program_fullpath, false); + + ::free(large_program_fullpath); + } + } + return g_program_filespec; +} + +bool +HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + // Shallow bundle + raw_path.resize(framework_pos); +#else + // Normal bundle + raw_path.resize(framework_pos); + raw_path.append("/Resources"); +#endif + } + else + { + // Find the bin path relative to the lib path where the cmake-based + // OS X .dylib lives. This is not going to work if the bin and lib + // dir are not both in the same dir. + // + // It is not going to work to do it by the executable path either, + // as in the case of a python script, the executable is python, not + // the lldb driver. + raw_path.append("/../bin"); + FileSpec support_dir_spec(raw_path, true); + if (!support_dir_spec.Exists() || !support_dir_spec.IsDirectory()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoMacOSX::%s(): failed to find support directory", + __FUNCTION__); + return false; + } + + // Get normalization from support_dir_spec. Note the FileSpec resolve + // does not remove '..' in the path. + char *const dir_realpath = realpath(support_dir_spec.GetPath().c_str(), NULL); + if (dir_realpath) + { + raw_path = dir_realpath; + free(dir_realpath); + } + else + { + raw_path = support_dir_spec.GetPath(); + } + } + + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return (bool)file_spec.GetDirectory(); +} + +bool +HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Headers"); + } + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +} + +bool +HostInfoMacOSX::ComputePythonDirectory(FileSpec &file_spec) +{ +#ifndef LLDB_DISABLE_PYTHON + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/Python"); + } + else + { + llvm::SmallString<256> python_version_dir; + llvm::raw_svector_ostream os(python_version_dir); + os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages"; + + // We may get our string truncated. Should we protect this with an assert? + raw_path.append(python_version_dir.c_str()); + } + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +#else + return false; +#endif +} + +bool +HostInfoMacOSX::ComputeClangDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath (lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/Clang"); + } + file_spec.SetFile (raw_path.c_str(), true); + return true; +} + +bool +HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos == std::string::npos) + return false; + + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/PlugIns"); + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +} + +bool +HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) +{ + FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns", true); + file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); + return true; +} + +void +HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ + // All apple systems support 32 bit execution. + uint32_t cputype, cpusubtype; + uint32_t is_64_bit_capable = false; + size_t len = sizeof(cputype); + ArchSpec host_arch; + // These will tell us about the kernel architecture, which even on a 64 + // bit machine can be 32 bit... + if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) + { + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) + cpusubtype = CPU_TYPE_ANY; + + len = sizeof(is_64_bit_capable); + ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); + + if (is_64_bit_capable) + { + if (cputype & CPU_ARCH_ABI64) + { + // We have a 64 bit kernel on a 64 bit system + arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); + } + else + { + // We have a 64 bit kernel that is returning a 32 bit cputype, the + // cpusubtype will be correct as if it were for a 64 bit architecture + arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, cpusubtype); + } + + // Now we need modify the cpusubtype for the 32 bit slices. + uint32_t cpusubtype32 = cpusubtype; +#if defined(__i386__) || defined(__x86_64__) + if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) + cpusubtype32 = CPU_SUBTYPE_I386_ALL; +#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + cpusubtype32 = CPU_SUBTYPE_ARM_V7S; +#endif + arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), cpusubtype32); + + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + { + // When running on a watch or tv, report the host os correctly +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 + arch_32.GetTriple().setOS(llvm::Triple::TvOS); + arch_64.GetTriple().setOS(llvm::Triple::TvOS); +#else + arch_32.GetTriple().setOS(llvm::Triple::IOS); + arch_64.GetTriple().setOS(llvm::Triple::IOS); +#endif + } + else + { + arch_32.GetTriple().setOS(llvm::Triple::MacOSX); + arch_64.GetTriple().setOS(llvm::Triple::MacOSX); + } + } + else + { + // We have a 32 bit kernel on a 32 bit system + arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + arch_32.GetTriple().setOS(llvm::Triple::WatchOS); +#else + arch_32.GetTriple().setOS(llvm::Triple::IOS); +#endif + arch_64.Clear(); + } + } +} diff --git a/source/Host/macosx/HostThreadMacOSX.mm b/source/Host/macosx/HostThreadMacOSX.mm new file mode 100644 index 000000000000..c84a78efd9e6 --- /dev/null +++ b/source/Host/macosx/HostThreadMacOSX.mm @@ -0,0 +1,102 @@ +//===-- HostThreadMacOSX.cpp ------------------------------------*- 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/macosx/HostThreadMacOSX.h" +#include "lldb/Host/Host.h" + +#include +#include + +#include +#include + +using namespace lldb_private; + +namespace +{ + +pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; +pthread_key_t g_thread_create_key = 0; + +class MacOSXDarwinThread +{ + public: + MacOSXDarwinThread() + : 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 SnowLeopard and later we just call the thread registration function. + objc_registerThreadWithCollector(); +#endif + } + else + { + m_pool = [[NSAutoreleasePool alloc] init]; + } + } + + ~MacOSXDarwinThread() + { + if (m_pool) + { + [m_pool drain]; + m_pool = nil; + } + } + + static void + PThreadDestructor(void *v) + { + if (v) + delete static_cast(v); + ::pthread_setspecific(g_thread_create_key, NULL); + } + + protected: + NSAutoreleasePool *m_pool; + + private: + DISALLOW_COPY_AND_ASSIGN(MacOSXDarwinThread); +}; + +void +InitThreadCreated() +{ + ::pthread_key_create(&g_thread_create_key, MacOSXDarwinThread::PThreadDestructor); +} +} // namespace + +HostThreadMacOSX::HostThreadMacOSX() + : HostThreadPosix() +{ +} + +HostThreadMacOSX::HostThreadMacOSX(lldb::thread_t thread) + : HostThreadPosix(thread) +{ +} + +lldb::thread_result_t +HostThreadMacOSX::ThreadCreateTrampoline(lldb::thread_arg_t arg) +{ + ::pthread_once(&g_thread_create_once, InitThreadCreated); + if (g_thread_create_key) + { + ::pthread_setspecific(g_thread_create_key, new MacOSXDarwinThread()); + } + + return HostThreadPosix::ThreadCreateTrampoline(arg); +} diff --git a/source/Host/macosx/Symbols.cpp b/source/Host/macosx/Symbols.cpp new file mode 100644 index 000000000000..f6a18febe6da --- /dev/null +++ b/source/Host/macosx/Symbols.cpp @@ -0,0 +1,559 @@ +//===-- 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 "lldb/Utility/SafeMachO.h" + +// C++ Includes +// Other libraries and framework includes +#include + +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/CleanUp.h" +#include "Host/macosx/cfcpp/CFCBundle.h" +#include "Host/macosx/cfcpp/CFCData.h" +#include "Host/macosx/cfcpp/CFCReleaser.h" +#include "Host/macosx/cfcpp/CFCString.h" +#include "mach/machine.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices +extern "C" { + +CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); +CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); + +} +#endif + +int +LocateMacOSXFilesUsingDebugSymbols +( + const ModuleSpec &module_spec, + ModuleSpec &return_module_spec +) +{ + return_module_spec = module_spec; + return_module_spec.GetFileSpec().Clear(); + return_module_spec.GetSymbolFileSpec().Clear(); + + int items_found = 0; + +#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices + + const UUID *uuid = module_spec.GetUUIDPtr(); + const ArchSpec *arch = module_spec.GetArchitecturePtr(); + + if (uuid && uuid->IsValid()) + { + // Try and locate the dSYM file using DebugSymbols first + 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; + const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + 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 (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + if (log) + { + log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for the dSYM", path, uuid->GetAsString().c_str()); + } + FileSpec dsym_filespec(path, path[0] == '~'); + + if (dsym_filespec.GetFileType () == FileSpec::eFileTypeDirectory) + { + dsym_filespec = Symbols::FindSymbolFileInBundle (dsym_filespec, uuid, arch); + ++items_found; + } + else + { + ++items_found; + } + return_module_spec.GetSymbolFileSpec() = dsym_filespec; + } + + bool success = false; + if (log) + { + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for an exec file", path, uuid->GetAsString().c_str()); + } + + } + + CFCReleaser dict(::DBGCopyDSYMPropertyLists (dsym_url.get())); + CFDictionaryRef uuid_dict = NULL; + if (dict.get()) + { + CFCString uuid_cfstr (uuid->GetAsString().c_str()); + uuid_dict = static_cast(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get())); + } + if (uuid_dict) + { + CFStringRef exec_cf_path = static_cast(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable"))); + if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) + { + if (log) + { + log->Printf ("plist bundle has exec path of %s for UUID %s", path, uuid->GetAsString().c_str()); + } + ++items_found; + FileSpec exec_filespec (path, path[0] == '~'); + if (exec_filespec.Exists()) + { + success = true; + return_module_spec.GetFileSpec() = exec_filespec; + } + } + } + + if (!success) + { + // No dictionary, check near the dSYM bundle for an executable that matches... + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + char *dsym_extension_pos = ::strstr (path, ".dSYM"); + if (dsym_extension_pos) + { + *dsym_extension_pos = '\0'; + if (log) + { + log->Printf ("Looking for executable binary next to dSYM bundle with name with name %s", path); + } + FileSpec file_spec (path, true); + ModuleSpecList module_specs; + ModuleSpec matched_module_spec; + switch (file_spec.GetFileType()) + { + case FileSpec::eFileTypeDirectory: // Bundle directory? + { + CFCBundle bundle (path); + CFCReleaser bundle_exe_url (bundle.CopyExecutableURL ()); + if (bundle_exe_url.get()) + { + if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + FileSpec bundle_exe_file_spec (path, true); + if (ObjectFile::GetModuleSpecifications(bundle_exe_file_spec, 0, 0, module_specs) && + module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + + { + ++items_found; + return_module_spec.GetFileSpec() = bundle_exe_file_spec; + if (log) + { + log->Printf ("Executable binary %s next to dSYM is compatible; using", path); + } + } + } + } + } + break; + + case FileSpec::eFileTypePipe: // Forget pipes + case FileSpec::eFileTypeSocket: // We can't process socket files + case FileSpec::eFileTypeInvalid: // File doesn't exist... + break; + + case FileSpec::eFileTypeUnknown: + case FileSpec::eFileTypeRegular: + case FileSpec::eFileTypeSymbolicLink: + case FileSpec::eFileTypeOther: + if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, module_specs) && + module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + + { + ++items_found; + return_module_spec.GetFileSpec() = file_spec; + if (log) + { + log->Printf ("Executable binary %s next to dSYM is compatible; using", path); + } + } + break; + } + } + } + } + } + } + } + } +#endif // #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) + + return items_found; +} + +FileSpec +Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec, + const lldb_private::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); + + lldb_utility::CleanUp dirp (opendir(path), NULL, closedir); + if (dirp.is_valid()) + { + dsym_fspec.GetDirectory().SetCString(path); + struct dirent* dp; + while ((dp = readdir(dirp.get())) != NULL) + { + // Only search directories + if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) + { + if (dp->d_namlen == 1 && dp->d_name[0] == '.') + continue; + + if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') + continue; + } + + if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) + { + dsym_fspec.GetFilename().SetCString(dp->d_name); + ModuleSpecList module_specs; + if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) + { + ModuleSpec spec; + for (size_t i = 0; i < module_specs.GetSize(); ++i) + { + assert(module_specs.GetModuleSpecAtIndex(i, spec)); + if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && + (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch)))) + { + return dsym_fspec; + } + } + } + } + } + } + } + dsym_fspec.Clear(); + return dsym_fspec; +} + +static bool +GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + bool success = false; + if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ()) + { + std::string str; + CFStringRef cf_str; + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + { + module_spec.GetFileSpec().SetFile (str.c_str(), true); + if (log) + { + log->Printf ("From dsymForUUID plist: Symbol rich executable is at '%s'", str.c_str()); + } + } + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + { + module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true); + success = true; + if (log) + { + log->Printf ("From dsymForUUID plist: dSYM is at '%s'", str.c_str()); + } + } + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + module_spec.GetArchitecture().SetTriple(str.c_str()); + } + + std::string DBGBuildSourcePath; + std::string DBGSourcePath; + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); + } + + if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) + { + module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true); + } + } + return success; +} + + +bool +Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup) +{ + bool success = false; + const UUID *uuid_ptr = module_spec.GetUUIDPtr(); + const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); + + // It's expensive to check for the DBGShellCommands defaults setting, only do it once per + // lldb run and cache the result. + static bool g_have_checked_for_dbgshell_command = false; + static const char *g_dbgshell_command = NULL; + if (g_have_checked_for_dbgshell_command == false) + { + g_have_checked_for_dbgshell_command = true; + CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols")); + if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID()) + { + char cstr_buf[PATH_MAX]; + if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8)) + { + g_dbgshell_command = strdup (cstr_buf); // this malloc'ed memory will never be freed + } + } + if (defaults_setting) + { + CFRelease (defaults_setting); + } + } + + // When g_dbgshell_command is NULL, the user has not enabled the use of an external program + // to find the symbols, don't run it for them. + if (force_lookup == false && g_dbgshell_command == NULL) + { + return false; + } + + if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists())) + { + static bool g_located_dsym_for_uuid_exe = false; + static bool g_dsym_for_uuid_exe_exists = false; + static char g_dsym_for_uuid_exe_path[PATH_MAX]; + if (!g_located_dsym_for_uuid_exe) + { + g_located_dsym_for_uuid_exe = true; + const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE"); + FileSpec dsym_for_uuid_exe_spec; + if (dsym_for_uuid_exe_path_cstr) + { + dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + + if (!g_dsym_for_uuid_exe_exists) + { + dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + if (!g_dsym_for_uuid_exe_exists) + { + long bufsize; + if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) + { + char buffer[bufsize]; + struct passwd pwd; + struct passwd *tilde_rc = NULL; + // we are a library so we need to use the reentrant version of getpwnam() + if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 + && tilde_rc + && tilde_rc->pw_dir) + { + std::string dsymforuuid_path(tilde_rc->pw_dir); + dsymforuuid_path += "/bin/dsymForUUID"; + dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + } + } + } + if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) + { + dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + + if (g_dsym_for_uuid_exe_exists) + dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path)); + } + if (g_dsym_for_uuid_exe_exists) + { + std::string uuid_str; + char file_path[PATH_MAX]; + file_path[0] = '\0'; + + if (uuid_ptr) + uuid_str = uuid_ptr->GetAsString(); + + if (file_spec_ptr) + file_spec_ptr->GetPath(file_path, sizeof(file_path)); + + StreamString command; + if (!uuid_str.empty()) + command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path); + + if (!command.GetString().empty()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + int exit_status = -1; + int signo = -1; + std::string command_output; + if (log) + { + if (!uuid_str.empty()) + log->Printf("Calling %s with UUID %s to find dSYM", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + log->Printf("Calling %s with file %s to find dSYM", g_dsym_for_uuid_exe_path, file_path); + } + Error error = Host::RunShellCommand (command.GetData(), + NULL, // current working directory + &exit_status, // Exit status + &signo, // Signal int * + &command_output, // Command output + 30, // Large timeout to allow for long dsym download times + false); // Don't run in a shell (we don't need shell expansion) + if (error.Success() && exit_status == 0 && !command_output.empty()) + { + CFCData data (CFDataCreateWithBytesNoCopy (NULL, + (const UInt8 *)command_output.data(), + command_output.size(), + kCFAllocatorNull)); + + CFCReleaser plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL)); + + if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ()) + { + if (!uuid_str.empty()) + { + CFCString uuid_cfstr(uuid_str.c_str()); + CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get()); + success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec); + } + else + { + const CFIndex num_values = ::CFDictionaryGetCount(plist.get()); + if (num_values > 0) + { + std::vector keys (num_values, NULL); + std::vector values (num_values, NULL); + ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]); + if (num_values == 1) + { + return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec); + } + else + { + for (CFIndex i=0; iPrintf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, file_path); + } + } + } + } + } + return success; +} + diff --git a/source/Host/macosx/ThisThread.cpp b/source/Host/macosx/ThisThread.cpp new file mode 100644 index 000000000000..95c7f2bf1e38 --- /dev/null +++ b/source/Host/macosx/ThisThread.cpp @@ -0,0 +1,39 @@ +//===-- ThisThread.cpp ------------------------------------------*- 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/ThisThread.h" + +#include + +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + ::pthread_setname_np(name); +#endif +} + +void +ThisThread::GetName(llvm::SmallVectorImpl &name) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + char pthread_name[1024]; + dispatch_queue_t current_queue = ::dispatch_get_current_queue(); + if (current_queue != NULL) + { + const char *queue_name = dispatch_queue_get_label(current_queue); + if (queue_name && queue_name[0]) + { + name = queue_name; + } + } +#endif +} diff --git a/source/Host/macosx/cfcpp/CFCBundle.cpp b/source/Host/macosx/cfcpp/CFCBundle.cpp new file mode 100644 index 000000000000..71b074993661 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCBundle.cpp @@ -0,0 +1,99 @@ +//===-- 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; +} + +bool +CFCBundle::GetPath (char *dst, size_t dst_len) +{ + CFBundleRef bundle = get(); + if (bundle) + { + CFCReleaser bundle_url (CFBundleCopyBundleURL (bundle)); + if (bundle_url.get()) + { + Boolean resolveAgainstBase = 0; + return ::CFURLGetFileSystemRepresentation (bundle_url.get(), resolveAgainstBase, (UInt8 *)dst, dst_len) != 0; + } + } + return false; +} + +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/source/Host/macosx/cfcpp/CFCBundle.h b/source/Host/macosx/cfcpp/CFCBundle.h new file mode 100644 index 000000000000..1cd1b681af84 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCBundle.h @@ -0,0 +1,50 @@ +//===-- 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 + GetPath (char *dst, size_t dst_len); + + 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/source/Host/macosx/cfcpp/CFCData.cpp b/source/Host/macosx/cfcpp/CFCData.cpp new file mode 100644 index 000000000000..4f49368ad8ad --- /dev/null +++ b/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/source/Host/macosx/cfcpp/CFCData.h b/source/Host/macosx/cfcpp/CFCData.h new file mode 100644 index 000000000000..6a718f54c055 --- /dev/null +++ b/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/source/Host/macosx/cfcpp/CFCMutableArray.cpp b/source/Host/macosx/cfcpp/CFCMutableArray.cpp new file mode 100644 index 000000000000..c3c0a11193a7 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableArray.cpp @@ -0,0 +1,166 @@ +//===-- 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" +#include "CFCString.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; +} + + +bool +CFCMutableArray::AppendCStringAsCFString (const char *s, CFStringEncoding encoding, 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) + { + CFCString cf_str (s, encoding); + ::CFArrayAppendValue (array, cf_str.get()); + return true; + } + return false; +} + +bool +CFCMutableArray::AppendFileSystemRepresentationAsCFString (const char *s, 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) + { + CFCString cf_path; + cf_path.SetFileSystemRepresentation(s); + ::CFArrayAppendValue (array, cf_path.get()); + return true; + } + return false; +} diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.h b/source/Host/macosx/cfcpp/CFCMutableArray.h new file mode 100644 index 000000000000..f78cd92ffab1 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableArray.h @@ -0,0 +1,39 @@ +//===-- 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 + bool AppendCStringAsCFString (const char *cstr, + CFStringEncoding encoding = kCFStringEncodingUTF8, + bool can_create = true); + bool AppendFileSystemRepresentationAsCFString (const char *s, + bool can_create = true); +}; + +#endif // #ifndef CoreFoundationCPP_CFMutableArray_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp new file mode 100644 index 000000000000..bce023bfd616 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp @@ -0,0 +1,529 @@ +//===-- 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::AddValueDouble(CFStringRef key, double 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, kCFNumberDoubleType, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueDouble(CFStringRef key, double 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, kCFNumberDoubleType, &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/source/Host/macosx/cfcpp/CFCMutableDictionary.h b/source/Host/macosx/cfcpp/CFCMutableDictionary.h new file mode 100644 index 000000000000..a1cfb68f569e --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.h @@ -0,0 +1,79 @@ +//===-- 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 AddValueDouble(CFStringRef key, double value, bool can_create = false); + bool SetValueDouble(CFStringRef key, double 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/source/Host/macosx/cfcpp/CFCMutableSet.cpp b/source/Host/macosx/cfcpp/CFCMutableSet.cpp new file mode 100644 index 000000000000..afc09e180b6b --- /dev/null +++ b/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 NULL; + 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/source/Host/macosx/cfcpp/CFCMutableSet.h b/source/Host/macosx/cfcpp/CFCMutableSet.h new file mode 100644 index 000000000000..78f7a8be81d2 --- /dev/null +++ b/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/source/Host/macosx/cfcpp/CFCReleaser.h b/source/Host/macosx/cfcpp/CFCReleaser.h new file mode 100644 index 000000000000..67dd2ead5799 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCReleaser.h @@ -0,0 +1,158 @@ +//===-- 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 ownership 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. + // I had to add the "enforce_null" bool here because some + // API's require the pointer address even though they don't change it. + //---------------------------------------------------------- + T* + ptr_address(bool enforce_null = true) + { + if (enforce_null) + 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/source/Host/macosx/cfcpp/CFCString.cpp b/source/Host/macosx/cfcpp/CFCString.cpp new file mode 100644 index 000000000000..81a96b824999 --- /dev/null +++ b/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/source/Host/macosx/cfcpp/CFCString.h b/source/Host/macosx/cfcpp/CFCString.h new file mode 100644 index 000000000000..27c090313ece --- /dev/null +++ b/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 = kCFStringEncodingUTF8); + 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/source/Host/macosx/cfcpp/CoreFoundationCPP.h b/source/Host/macosx/cfcpp/CoreFoundationCPP.h new file mode 100644 index 000000000000..6843e2649cda --- /dev/null +++ b/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/source/Host/netbsd/Makefile b/source/Host/netbsd/Makefile new file mode 100644 index 000000000000..2502cc49c15b --- /dev/null +++ b/source/Host/netbsd/Makefile @@ -0,0 +1,14 @@ +##===- source/Host/netbsd/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../.. +LIBRARYNAME := lldbHostNetBSD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Host/windows/Condition.cpp b/source/Host/windows/Condition.cpp new file mode 100644 index 000000000000..2f16ad77d7a3 --- /dev/null +++ b/source/Host/windows/Condition.cpp @@ -0,0 +1,98 @@ +//===-- 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" +#include "lldb/Host/windows/windows.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() +{ + m_condition = static_cast(malloc(sizeof(CONDITION_VARIABLE))); + InitializeConditionVariable(static_cast(m_condition)); +} + +//---------------------------------------------------------------------- +// Destructor +// +// Destroys the pthread condition that the object owns. +//---------------------------------------------------------------------- +Condition::~Condition () +{ + free(m_condition); +} + +//---------------------------------------------------------------------- +// Unblock all threads waiting for a condition variable +//---------------------------------------------------------------------- +int +Condition::Broadcast () +{ + WakeAllConditionVariable(static_cast(m_condition)); + return 0; +} + +//---------------------------------------------------------------------- +// Unblocks one thread waiting for the condition variable +//---------------------------------------------------------------------- +int +Condition::Signal () +{ + WakeConditionVariable(static_cast(m_condition)); + return 0; +} + +//---------------------------------------------------------------------- +// The Wait() function atomically blocks the current thread +// waiting on the owned condition variable, and unblocks the mutex +// specified by "mutex". The waiting thread unblocks only after +// another thread calls Signal(), or Broadcast() with the same +// condition variable, or if "abstime" is valid (non-NULL) this +// function will return when the system time reaches the time +// specified in "abstime". If "abstime" is NULL this function will +// wait for an infinite amount of time for the condition variable +// to be signaled or broadcasted. +// +// The current thread re-acquires the lock on "mutex". +//---------------------------------------------------------------------- +int +Condition::Wait (Mutex &mutex, const TimeValue *abstime, bool *timed_out) +{ + DWORD wait = INFINITE; + if (abstime != NULL) { + int wval = (*abstime - TimeValue::Now()) / 1000000; + if (wval < 0) wval = 0; + + wait = wval; + } + + int err = SleepConditionVariableCS(static_cast(m_condition), static_cast(mutex.m_mutex), wait); + + if (timed_out != NULL) + { + if ((err == 0) && GetLastError() == ERROR_TIMEOUT) + *timed_out = true; + else + *timed_out = false; + } + + return err == 0; +} + diff --git a/source/Host/windows/ConnectionGenericFileWindows.cpp b/source/Host/windows/ConnectionGenericFileWindows.cpp new file mode 100644 index 000000000000..eebf3d4f633c --- /dev/null +++ b/source/Host/windows/ConnectionGenericFileWindows.cpp @@ -0,0 +1,354 @@ +//===-- ConnectionGenericFileWindows.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Host/windows/ConnectionGenericFileWindows.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +// This is a simple helper class to package up the information needed to return from a Read/Write +// operation function. Since there is a lot of code to be run before exit regardless of whether the +// operation succeeded or failed, combined with many possible return paths, this is the cleanest +// way to represent it. +class ReturnInfo +{ + public: + void + Set(size_t bytes, ConnectionStatus status, DWORD error_code) + { + m_error.SetError(error_code, eErrorTypeWin32); + m_bytes = bytes; + m_status = status; + } + + void + Set(size_t bytes, ConnectionStatus status, llvm::StringRef error_msg) + { + m_error.SetErrorString(error_msg.data()); + m_bytes = bytes; + m_status = status; + } + + size_t + GetBytes() const + { + return m_bytes; + } + ConnectionStatus + GetStatus() const + { + return m_status; + } + const Error & + GetError() const + { + return m_error; + } + + private: + Error m_error; + size_t m_bytes; + ConnectionStatus m_status; +}; +} + +ConnectionGenericFile::ConnectionGenericFile() + : m_file(INVALID_HANDLE_VALUE) + , m_owns_file(false) +{ + ::ZeroMemory(&m_overlapped, sizeof(m_overlapped)); + ::ZeroMemory(&m_file_position, sizeof(m_file_position)); + InitializeEventHandles(); +} + +ConnectionGenericFile::ConnectionGenericFile(lldb::file_t file, bool owns_file) + : m_file(file) + , m_owns_file(owns_file) +{ + ::ZeroMemory(&m_overlapped, sizeof(m_overlapped)); + ::ZeroMemory(&m_file_position, sizeof(m_file_position)); + InitializeEventHandles(); +} + +ConnectionGenericFile::~ConnectionGenericFile() +{ + if (m_owns_file && IsConnected()) + ::CloseHandle(m_file); + + ::CloseHandle(m_event_handles[kBytesAvailableEvent]); + ::CloseHandle(m_event_handles[kInterruptEvent]); +} + +void +ConnectionGenericFile::InitializeEventHandles() +{ + m_event_handles[kInterruptEvent] = CreateEvent(NULL, FALSE, FALSE, NULL); + + // Note, we should use a manual reset event for the hEvent argument of the OVERLAPPED. This + // is because both WaitForMultipleObjects and GetOverlappedResult (if you set the bWait + // argument to TRUE) will wait for the event to be signalled. If we use an auto-reset event, + // WaitForMultipleObjects will reset the event, return successfully, and then + // GetOverlappedResult will block since the event is no longer signalled. + m_event_handles[kBytesAvailableEvent] = ::CreateEvent(NULL, TRUE, FALSE, NULL); +} + +bool +ConnectionGenericFile::IsConnected() const +{ + return m_file && (m_file != INVALID_HANDLE_VALUE); +} + +lldb::ConnectionStatus +ConnectionGenericFile::Connect(const char *s, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionGenericFile::Connect (url = '%s')", static_cast(this), s); + + if (strstr(s, "file://") != s) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s); + return eConnectionStatusError; + } + + if (IsConnected()) + { + ConnectionStatus status = Disconnect(error_ptr); + if (status != eConnectionStatusSuccess) + return status; + } + + // file://PATH + const char *path = s + strlen("file://"); + // Open the file for overlapped access. If it does not exist, create it. We open it overlapped + // so that we can issue asynchronous reads and then use WaitForMultipleObjects to allow the read + // to be interrupted by an event object. + m_file = ::CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL); + if (m_file == INVALID_HANDLE_VALUE) + { + if (error_ptr) + error_ptr->SetError(::GetLastError(), eErrorTypeWin32); + return eConnectionStatusError; + } + + m_owns_file = true; + m_uri.assign(s); + return eConnectionStatusSuccess; +} + +lldb::ConnectionStatus +ConnectionGenericFile::Disconnect(Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionGenericFile::Disconnect ()", static_cast(this)); + + if (!IsConnected()) + return eConnectionStatusSuccess; + + // Reset the handle so that after we unblock any pending reads, subsequent calls to Read() will + // see a disconnected state. + HANDLE old_file = m_file; + m_file = INVALID_HANDLE_VALUE; + + // Set the disconnect event so that any blocking reads unblock, then cancel any pending IO operations. + ::CancelIoEx(old_file, &m_overlapped); + + // Close the file handle if we owned it, but don't close the event handles. We could always + // reconnect with the same Connection instance. + if (m_owns_file) + ::CloseHandle(old_file); + + ::ZeroMemory(&m_file_position, sizeof(m_file_position)); + m_owns_file = false; + m_uri.clear(); + return eConnectionStatusSuccess; +} + +size_t +ConnectionGenericFile::Read(void *dst, size_t dst_len, uint32_t timeout_usec, lldb::ConnectionStatus &status, Error *error_ptr) +{ + ReturnInfo return_info; + BOOL result = 0; + DWORD bytes_read = 0; + + if (error_ptr) + error_ptr->Clear(); + + if (!IsConnected()) + { + return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE); + goto finish; + } + + m_overlapped.hEvent = m_event_handles[kBytesAvailableEvent]; + + result = ::ReadFile(m_file, dst, dst_len, NULL, &m_overlapped); + if (result || ::GetLastError() == ERROR_IO_PENDING) + { + if (!result) + { + // The expected return path. The operation is pending. Wait for the operation to complete + // or be interrupted. + TimeValue time_value; + time_value.OffsetWithMicroSeconds(timeout_usec); + DWORD milliseconds = time_value.milliseconds(); + DWORD wait_result = ::WaitForMultipleObjects(llvm::array_lengthof(m_event_handles), m_event_handles, FALSE, milliseconds); + // All of the events are manual reset events, so make sure we reset them to non-signalled. + switch (wait_result) + { + case WAIT_OBJECT_0 + kBytesAvailableEvent: + break; + case WAIT_OBJECT_0 + kInterruptEvent: + return_info.Set(0, eConnectionStatusInterrupted, 0); + goto finish; + case WAIT_TIMEOUT: + return_info.Set(0, eConnectionStatusTimedOut, 0); + goto finish; + case WAIT_FAILED: + return_info.Set(0, eConnectionStatusError, ::GetLastError()); + goto finish; + } + } + // The data is ready. Figure out how much was read and return; + if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_read, FALSE)) + { + DWORD result_error = ::GetLastError(); + // ERROR_OPERATION_ABORTED occurs when someone calls Disconnect() during a blocking read. + // This triggers a call to CancelIoEx, which causes the operation to complete and the + // result to be ERROR_OPERATION_ABORTED. + if (result_error == ERROR_HANDLE_EOF || result_error == ERROR_OPERATION_ABORTED || result_error == ERROR_BROKEN_PIPE) + return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0); + else + return_info.Set(bytes_read, eConnectionStatusError, result_error); + } + else if (bytes_read == 0) + return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0); + else + return_info.Set(bytes_read, eConnectionStatusSuccess, 0); + + goto finish; + } + else if (::GetLastError() == ERROR_BROKEN_PIPE) + { + // The write end of a pipe was closed. This is equivalent to EOF. + return_info.Set(0, eConnectionStatusEndOfFile, 0); + } + else + { + // An unknown error occurred. Fail out. + return_info.Set(0, eConnectionStatusError, ::GetLastError()); + } + goto finish; + +finish: + status = return_info.GetStatus(); + if (error_ptr) + *error_ptr = return_info.GetError(); + + // kBytesAvailableEvent is a manual reset event. Make sure it gets reset here so that any + // subsequent operations don't immediately see bytes available. + ResetEvent(m_event_handles[kBytesAvailableEvent]); + + IncrementFilePointer(return_info.GetBytes()); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + { + log->Printf("%" PRIxPTR " ConnectionGenericFile::Read() handle = %" PRIxPTR ", dst = %" PRIxPTR ", dst_len = %" PRIu64 + ") => %" PRIu64 ", error = %s", + this, m_file, dst, static_cast(dst_len), static_cast(return_info.GetBytes()), + return_info.GetError().AsCString()); + } + + return return_info.GetBytes(); +} + +size_t +ConnectionGenericFile::Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr) +{ + ReturnInfo return_info; + DWORD bytes_written = 0; + BOOL result = 0; + + if (error_ptr) + error_ptr->Clear(); + + if (!IsConnected()) + { + return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE); + goto finish; + } + + m_overlapped.hEvent = NULL; + + // Writes are not interruptible like reads are, so just block until it's done. + result = ::WriteFile(m_file, src, src_len, NULL, &m_overlapped); + if (!result && ::GetLastError() != ERROR_IO_PENDING) + { + return_info.Set(0, eConnectionStatusError, ::GetLastError()); + goto finish; + } + + if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_written, TRUE)) + { + return_info.Set(bytes_written, eConnectionStatusError, ::GetLastError()); + goto finish; + } + + return_info.Set(bytes_written, eConnectionStatusSuccess, 0); + goto finish; + +finish: + status = return_info.GetStatus(); + if (error_ptr) + *error_ptr = return_info.GetError(); + + IncrementFilePointer(return_info.GetBytes()); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + { + log->Printf("%" PRIxPTR " ConnectionGenericFile::Write() handle = %" PRIxPTR ", src = %" PRIxPTR ", src_len = %" PRIu64 + ") => %" PRIu64 ", error = %s", + this, m_file, src, static_cast(src_len), static_cast(return_info.GetBytes()), + return_info.GetError().AsCString()); + } + return return_info.GetBytes(); +} + +std::string +ConnectionGenericFile::GetURI() +{ + return m_uri; +} + +bool +ConnectionGenericFile::InterruptRead() +{ + return ::SetEvent(m_event_handles[kInterruptEvent]); +} + +void +ConnectionGenericFile::IncrementFilePointer(DWORD amount) +{ + LARGE_INTEGER old_pos; + old_pos.HighPart = m_overlapped.OffsetHigh; + old_pos.LowPart = m_overlapped.Offset; + old_pos.QuadPart += amount; + m_overlapped.Offset = old_pos.LowPart; + m_overlapped.OffsetHigh = old_pos.HighPart; +} diff --git a/source/Host/windows/EditLineWin.cpp b/source/Host/windows/EditLineWin.cpp new file mode 100644 index 000000000000..55fe52dc8ccd --- /dev/null +++ b/source/Host/windows/EditLineWin.cpp @@ -0,0 +1,435 @@ +//===-- EditLineWin.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// this file is only relevant for Visual C++ +#if defined( _WIN32 ) + +#include "lldb/Host/windows/windows.h" + +#include "lldb/Host/windows/editlinewin.h" +#include +#include + +// edit line EL_ADDFN function pointer type +typedef unsigned char(*el_addfn_func)(EditLine *e, int ch); +typedef const char* (*el_prompt_func)(EditLine *); + +// edit line wrapper binding container +struct el_binding +{ + // + const char *name; + const char *help; + // function pointer to callback routine + el_addfn_func func; + // ascii key this function is bound to + const char *key; +}; + +// stored key bindings +static std::vector _bindings; + +//TODO: this should in fact be related to the exact edit line context we create +static void *clientData = NULL; + +// store the current prompt string +// default to what we expect to receive anyway +static const char *_prompt = "(lldb) "; + +#if !defined( _WIP_INPUT_METHOD ) + +static char * +el_get_s (char *buffer, int chars) +{ + return gets_s(buffer, chars); +} +#else + +static void +con_output (char _in) +{ + HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE ); + DWORD written = 0; + // get the cursor position + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo( hout, &info ); + // output this char + WriteConsoleOutputCharacterA( hout, &_in, 1, info.dwCursorPosition, &written ); + // advance cursor position + info.dwCursorPosition.X++; + SetConsoleCursorPosition( hout, info.dwCursorPosition ); +} + +static void +con_backspace (void) +{ + HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE ); + DWORD written = 0; + // get cursor position + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo( hout, &info ); + // nudge cursor backwards + info.dwCursorPosition.X--; + SetConsoleCursorPosition( hout, info.dwCursorPosition ); + // blank out the last character + WriteConsoleOutputCharacterA( hout, " ", 1, info.dwCursorPosition, &written ); +} + +static void +con_return (void) +{ + HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE ); + DWORD written = 0; + // get cursor position + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo( hout, &info ); + // move onto the new line + info.dwCursorPosition.X = 0; + info.dwCursorPosition.Y++; + SetConsoleCursorPosition( hout, info.dwCursorPosition ); +} + +static bool +runBind (char _key) +{ + for ( int i=0; i<_bindings.size(); i++ ) + { + el_binding *bind = _bindings[i]; + if ( bind->key[0] == _key ) + { + bind->func( (EditLine*) -1, _key ); + return true; + } + } + return false; +} + +// replacement get_s which is EL_BIND aware +static char * +el_get_s (char *buffer, int chars) +{ + // + char *head = buffer; + // + for ( ;; Sleep( 10 ) ) + { + // + INPUT_RECORD _record; + // + DWORD _read = 0; + if ( ReadConsoleInputA( GetStdHandle( STD_INPUT_HANDLE ), &_record, 1, &_read ) == FALSE ) + break; + // if we didn't read a key + if ( _read == 0 ) + continue; + // only interested in key events + if ( _record.EventType != KEY_EVENT ) + continue; + // is the key down + if (! _record.Event.KeyEvent.bKeyDown ) + continue; + // read the ascii key character + char _key = _record.Event.KeyEvent.uChar.AsciiChar; + // non ascii conformant key press + if ( _key == 0 ) + { + // check the scan code + // if VK_UP scroll back through history + // if VK_DOWN scroll forward through history + continue; + } + // try to execute any bind this key may have + if ( runBind( _key ) ) + continue; + // if we read a return key + if ( _key == '\n' || _key == '\r' ) + { + con_return( ); + break; + } + // key is backspace + if ( _key == 0x8 ) + { + // avoid deleting past beginning + if ( head > buffer ) + { + con_backspace( ); + head--; + } + continue; + } + + // add this key to the input buffer + if ( (head-buffer) < (chars-1) ) + { + con_output( _key ); + *(head++) = _key; + } + } + // insert end of line character + *head = '\0'; + + return buffer; +} +#endif + +// edit line initialize +EditLine * +el_init (const char *, FILE *, FILE *, FILE *) +{ + // + SetConsoleTitleA( "lldb" ); + // return dummy handle + return (EditLine*) -1; +} + +const char * +el_gets (EditLine *el, int *length) +{ + // print the prompt if we have one + if ( _prompt != NULL ) + printf("%s", _prompt); + // create a buffer for the user input + char *buffer = new char[ MAX_PATH ]; + // try to get user input string + if ( el_get_s( buffer, MAX_PATH ) ) + { + // get the string length in 'length' + while ( buffer[ *length ] != '\0' ) + (*length)++; + // return the input buffer + // remember that this memory has the be free'd somewhere + return buffer; + } + else + { + // on error + delete [] buffer; + return NULL; + } +} + +int +el_set (EditLine *el, int code, ...) +{ + va_list vl; + va_start(vl, code); + // + switch ( code ) + { + // edit line set prompt message + case ( EL_PROMPT ): + { + // EL_PROMPT, char *(*f)( EditLine *) + // define a prompt printing function as 'f', which is to return a string that + // contains the prompt. + + // get the function pointer from the arg list + void *func_vp = (void*)va_arg(vl, el_prompt_func); + // cast to suitable prototype + el_prompt_func func_fp = (el_prompt_func)func_vp; + // call to get the prompt as a string + _prompt = func_fp( el ); + } + break; + + case (EL_PROMPT_ESC) : + { + // EL_PROMPT, char *(*f)( EditLine *) + // define a prompt printing function as 'f', which is to return a string that + // contains the prompt. + + // get the function pointer from the arg list + void *func_vp = (void*)va_arg(vl, el_prompt_func); + va_arg(vl, int); + // call to get the prompt as a string + el_prompt_func func_fp = (el_prompt_func)func_vp; + _prompt = func_fp(el); + } + break; + + case ( EL_EDITOR ): + { + // EL_EDITOR, const char *mode + // set editing mode to "emacs" or "vi" + } + break; + case ( EL_HIST ): + { + // EL_HIST, History *(*fun)(History *, int op, ... ), const char *ptr + // defines which history function to use, which is usually history(). Ptr should be the + // value returned by history_init(). + } + break; + case ( EL_ADDFN ): + { + // EL_ADDFN, const char *name, const char *help, unsigned char (*func)(EditLine *e, int ch) + // add a user defined function, func), referred to as 'name' which is invoked when a key which is bound to 'name' is + // entered. 'help' is a description of 'name'. at invocation time, 'ch' is the key which caused the invocation. the + // return value of 'func()' should be one of: + // CC_NORM add a normal character + // CC_NEWLINE end of line was entered + // CC_EOF EOF was entered + // CC_ARGHACK expecting further command input as arguments, do nothing visually. + // CC_REFRESH refresh display. + // CC_REFRESH_BEEP refresh display and beep. + // CC_CURSOR cursor moved so update and perform CC_REFRESH + // CC_REDISPLAY redisplay entire input line. this is useful if a key binding outputs extra information. + // CC_ERROR an error occurred. beep and flush tty. + // CC_FATAL fatal error, reset tty to known state. + + el_binding *binding = new el_binding; + binding->name = va_arg( vl, const char *); + binding->help = va_arg( vl, const char *); + binding->func = va_arg( vl, el_addfn_func ); + binding->key = 0; + // add this to the bindings list + _bindings.push_back( binding ); + } + break; + case ( EL_BIND ): + { + // EL_BIND, const char *, ..., NULL + // perform the BIND built-in command. Refer to editrc(5) for more information. + + const char *name = va_arg( vl, const char* ); + + for ( int i=0; i<_bindings.size(); i++ ) + { + el_binding *bind = _bindings[i]; + if ( strcmp( bind->name, name ) == 0 ) + { + bind->key = va_arg( vl, const char * ); + break; + } + } + + } + break; + case ( EL_CLIENTDATA ): + { + clientData = va_arg(vl, void*); + } + break; + } + return 0; +} + +void +el_end (EditLine *el) +{ + //assert( !"Not implemented!" ); +} + +void +el_reset (EditLine *) +{ + assert( !"Not implemented!" ); +} + +int +el_getc (EditLine *, char *) +{ + assert( !"Not implemented!" ); + return 0; +} + +void +el_push (EditLine *, const char *) +{ +} + +void +el_beep (EditLine *) +{ + Beep( 1000, 500 ); +} + +int +el_parse (EditLine *, int, const char **) +{ + assert( !"Not implemented!" ); + return 0; +} + +int +el_get (EditLine *el, int code, ...) +{ + va_list vl; + va_start( vl, code ); + + switch ( code ) + { + case ( EL_CLIENTDATA ): + { + void **dout = va_arg( vl, void** ); + *dout = clientData; + } + break; + default: + assert( !"Not implemented!" ); + } + return 0; +} + +int +el_source (EditLine *el, const char *file) +{ + // init edit line by reading the contents of 'file' + // nothing to do here on windows... + return 0; +} + +void +el_resize (EditLine *) +{ + assert( !"Not implemented!" ); +} + +const LineInfo * +el_line (EditLine *el) +{ + return 0; +} + +int +el_insertstr (EditLine *, const char *) +{ +// assert( !"Not implemented!" ); + return 0; +} + +void +el_deletestr (EditLine *, int) +{ + assert( !"Not implemented!" ); +} + +History * +history_init (void) +{ + // return dummy handle + return (History*) -1; +} + +void +history_end (History *) +{ +// assert( !"Not implemented!" ); +} + +int +history (History *, HistEvent *, int op, ...) +{ + // perform operation 'op' on the history list with + // optional arguments as needed by the operation. + return 0; +} + +#endif diff --git a/source/Host/windows/FileSystem.cpp b/source/Host/windows/FileSystem.cpp new file mode 100644 index 000000000000..2cca12b92711 --- /dev/null +++ b/source/Host/windows/FileSystem.cpp @@ -0,0 +1,221 @@ +//===-- FileSystem.cpp ------------------------------------------*- 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/windows/windows.h" + +#include +#include +#include + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/windows/AutoHandle.h" +#include "llvm/Support/FileSystem.h" + +using namespace lldb_private; + +const char * +FileSystem::DEV_NULL = "nul"; + +FileSpec::PathSyntax +FileSystem::GetNativePathSyntax() +{ + return FileSpec::ePathSyntaxWindows; +} + +Error +FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) +{ + // On Win32, the mode parameter is ignored, as Windows files and directories support a + // different permission model than POSIX. + Error error; + const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true); + if (err_code) + { + error.SetErrorString(err_code.message().c_str()); + } + + return error; +} + +Error +FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) +{ + Error error; + if (!recurse) + { + BOOL result = ::RemoveDirectory(file_spec.GetCString()); + if (!result) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + } + else + { + // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to + // indicate the end of the list. + std::string path_buffer{file_spec.GetPath()}; + path_buffer.push_back(0); + + SHFILEOPSTRUCT shfos = {0}; + shfos.wFunc = FO_DELETE; + shfos.pFrom = path_buffer.c_str(); + shfos.fFlags = FOF_NO_UI; + + int result = ::SHFileOperation(&shfos); + // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values. + if (result != 0) + error.SetErrorStringWithFormat("SHFileOperation failed"); + } + return error; +} + +Error +FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) +{ + Error error; + // Beware that Windows's permission model is different from Unix's, and it's + // not clear if this API is supposed to check ACLs. To match the caller's + // expectations as closely as possible, we'll use Microsoft's _stat, which + // attempts to emulate POSIX stat. This should be good enough for basic + // checks like FileSpec::Readable. + struct _stat file_stats; + if (::_stat(file_spec.GetCString(), &file_stats) == 0) + { + // The owner permission bits in "st_mode" currently match the definitions + // for the owner file mode bits. + file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC); + } + else + { + error.SetErrorToErrno(); + } + + return error; +} + +Error +FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) +{ + Error error; + error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__); + return error; +} + +lldb::user_id_t +FileSystem::GetFileSize(const FileSpec &file_spec) +{ + return file_spec.GetByteSize(); +} + +bool +FileSystem::GetFileExists(const FileSpec &file_spec) +{ + return file_spec.Exists(); +} + +Error +FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) +{ + Error error; + if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr)) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; +} + +int +FileSystem::GetHardlinkCount(const FileSpec &file_spec) +{ + HANDLE file_handle = ::CreateFile(file_spec.GetCString(), + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if (file_handle == INVALID_HANDLE_VALUE) + return -1; + + AutoHandle auto_file_handle(file_handle); + BY_HANDLE_FILE_INFORMATION file_info; + if (::GetFileInformationByHandle(file_handle, &file_info)) + return file_info.nNumberOfLinks; + + return -1; +} + +Error +FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) +{ + Error error; + DWORD attrib = ::GetFileAttributes(dst.GetCString()); + if (attrib == INVALID_FILE_ATTRIBUTES) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY); + DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; + BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag); + if (!result) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; +} + +Error +FileSystem::Unlink(const FileSpec &file_spec) +{ + Error error; + BOOL result = ::DeleteFile(file_spec.GetCString()); + if (!result) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; +} + +Error +FileSystem::Readlink(const FileSpec &src, FileSpec &dst) +{ + Error error; + HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT, NULL); + if (h == INVALID_HANDLE_VALUE) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + char buf[PATH_MAX]; + // Subtract 1 from the path length since this function does not add a null terminator. + DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1, + FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + if (result == 0) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + else + dst.SetFile(buf, false); + + ::CloseHandle(h); + return error; +} + +Error +FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) +{ + return Error("ResolveSymbolicLink() isn't implemented on Windows"); +} + +bool +FileSystem::IsLocal(const FileSpec &spec) +{ + if (spec) + { + // TODO: return true if the file is on a locally mounted file system + return true; + } + + return false; +} diff --git a/source/Host/windows/Host.cpp b/source/Host/windows/Host.cpp new file mode 100644 index 000000000000..2c9a139df256 --- /dev/null +++ b/source/Host/windows/Host.cpp @@ -0,0 +1,326 @@ +//===-- source/Host/windows/Host.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/windows/AutoHandle.h" + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" + +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredData.h" + +// Windows includes +#include + +using namespace lldb; +using namespace lldb_private; + +namespace +{ + bool GetTripleForProcess(const FileSpec &executable, llvm::Triple &triple) + { + // Open the PE File as a binary file, and parse just enough information to determine the + // machine type. + File imageBinary( + executable.GetPath().c_str(), + File::eOpenOptionRead, + lldb::eFilePermissionsUserRead); + imageBinary.SeekFromStart(0x3c); + int32_t peOffset = 0; + uint32_t peHead = 0; + uint16_t machineType = 0; + size_t readSize = sizeof(peOffset); + imageBinary.Read(&peOffset, readSize); + imageBinary.SeekFromStart(peOffset); + imageBinary.Read(&peHead, readSize); + if (peHead != 0x00004550) // "PE\0\0", little-endian + return false; // Error: Can't find PE header + readSize = 2; + imageBinary.Read(&machineType, readSize); + triple.setVendor(llvm::Triple::PC); + triple.setOS(llvm::Triple::Win32); + triple.setArch(llvm::Triple::UnknownArch); + if (machineType == 0x8664) + triple.setArch(llvm::Triple::x86_64); + else if (machineType == 0x14c) + triple.setArch(llvm::Triple::x86); + + return true; + } + + bool GetExecutableForProcess(const AutoHandle &handle, std::string &path) + { + // Get the process image path. MAX_PATH isn't long enough, paths can actually be up to 32KB. + std::vector buffer(32768); + DWORD dwSize = buffer.size(); + if (!::QueryFullProcessImageNameA(handle.get(), 0, &buffer[0], &dwSize)) + return false; + path.assign(&buffer[0]); + return true; + } + + void GetProcessExecutableAndTriple(const AutoHandle &handle, ProcessInstanceInfo &process) + { + // We may not have permissions to read the path from the process. So start off by + // setting the executable file to whatever Toolhelp32 gives us, and then try to + // enhance this with more detailed information, but fail gracefully. + std::string executable; + llvm::Triple triple; + triple.setVendor(llvm::Triple::PC); + triple.setOS(llvm::Triple::Win32); + triple.setArch(llvm::Triple::UnknownArch); + if (GetExecutableForProcess(handle, executable)) + { + FileSpec executableFile(executable.c_str(), false); + process.SetExecutableFile(executableFile, true); + GetTripleForProcess(executableFile, triple); + } + process.SetArchitecture(ArchSpec(triple)); + + // TODO(zturner): Add the ability to get the process user name. + } +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + return 0; +} + +lldb::tid_t +Host::GetCurrentThreadID() +{ + return lldb::tid_t(::GetCurrentThreadId()); +} + +lldb::thread_t +Host::GetCurrentThread () +{ + return lldb::thread_t(::GetCurrentThread()); +} + +lldb::thread_key_t +Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback) +{ + return TlsAlloc(); +} + +void* +Host::ThreadLocalStorageGet(lldb::thread_key_t key) +{ + return ::TlsGetValue (key); +} + +void +Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value) +{ + ::TlsSetValue (key, value); +} + +void +Host::Kill(lldb::pid_t pid, int signo) +{ + TerminateProcess((HANDLE) pid, 1); +} + + +const char * +Host::GetSignalAsCString(int signo) +{ + return NULL; +} + +FileSpec +Host::GetModuleFileSpecForHostAddress (const void *host_addr) +{ + FileSpec module_filespec; + + HMODULE hmodule = NULL; + if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)host_addr, &hmodule)) + return module_filespec; + + std::vector buffer(MAX_PATH); + DWORD chars_copied = 0; + do { + chars_copied = ::GetModuleFileName(hmodule, &buffer[0], buffer.size()); + if (chars_copied == buffer.size() && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) + buffer.resize(buffer.size() * 2); + } while (chars_copied >= buffer.size()); + + module_filespec.SetFile(&buffer[0], false); + return module_filespec; +} + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + process_infos.Clear(); + + AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); + if (!snapshot.IsValid()) + return 0; + + PROCESSENTRY32 pe = {0}; + pe.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(snapshot.get(), &pe)) + { + do + { + AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID), nullptr); + + ProcessInstanceInfo process; + process.SetExecutableFile(FileSpec(pe.szExeFile, false), true); + process.SetProcessID(pe.th32ProcessID); + process.SetParentProcessID(pe.th32ParentProcessID); + GetProcessExecutableAndTriple(handle, process); + + if (match_info.MatchAllProcesses() || match_info.Matches(process)) + process_infos.Append(process); + } while (Process32Next(snapshot.get(), &pe)); + } + return process_infos.GetSize(); +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.Clear(); + + AutoHandle handle(::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid), + nullptr); + if (!handle.IsValid()) + return false; + + process_info.SetProcessID(pid); + GetProcessExecutableAndTriple(handle, process_info); + + // Need to read the PEB to get parent process and command line arguments. + return true; +} + +HostThread +Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) +{ + return HostThread(); +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + Error error; + if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) + { + FileSpec expand_tool_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec)) + { + error.SetErrorString("could not find support executable directory for the lldb-argdumper tool"); + return error; + } + expand_tool_spec.AppendPathComponent("lldb-argdumper.exe"); + if (!expand_tool_spec.Exists()) + { + error.SetErrorString("could not find the lldb-argdumper tool"); + return error; + } + + std::string quoted_cmd_string; + launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string); + std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/'); + StreamString expand_command; + + expand_command.Printf("\"%s\" %s", + expand_tool_spec.GetPath().c_str(), + quoted_cmd_string.c_str()); + + int status; + std::string output; + RunShellCommand(expand_command.GetData(), launch_info.GetWorkingDirectory(), &status, nullptr, &output, 10); + + if (status != 0) + { + error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status); + return error; + } + + auto data_sp = StructuredData::ParseJSON(output); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto dict_sp = data_sp->GetAsDictionary(); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); + if (!args_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_array_sp = args_sp->GetAsArray(); + if (!args_array_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + launch_info.GetArguments().Clear(); + + for (size_t i = 0; + i < args_array_sp->GetSize(); + i++) + { + auto item_sp = args_array_sp->GetItemAtIndex(i); + if (!item_sp) + continue; + auto str_sp = item_sp->GetAsString(); + if (!str_sp) + continue; + + launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); + } + } + + return error; +} + +size_t +Host::GetEnvironment(StringList &env) +{ + // The environment block on Windows is a contiguous buffer of NULL terminated strings, + // where the end of the environment block is indicated by two consecutive NULLs. + LPCH environment_block = ::GetEnvironmentStrings(); + env.Clear(); + while (*environment_block != '\0') + { + llvm::StringRef current_var(environment_block); + if (current_var[0] != '=') + env.AppendString(current_var); + + environment_block += current_var.size()+1; + } + return env.GetSize(); +} diff --git a/source/Host/windows/HostInfoWindows.cpp b/source/Host/windows/HostInfoWindows.cpp new file mode 100644 index 000000000000..6dce71d9172a --- /dev/null +++ b/source/Host/windows/HostInfoWindows.cpp @@ -0,0 +1,118 @@ +//===-- HostInfoWindows.cpp -------------------------------------*- 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/windows/windows.h" + +#include // std::once + +#include "lldb/Host/windows/HostInfoWindows.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" + +using namespace lldb_private; + +FileSpec HostInfoWindows::m_program_filespec; + +size_t +HostInfoWindows::GetPageSize() +{ + SYSTEM_INFO systemInfo; + GetNativeSystemInfo(&systemInfo); + return systemInfo.dwPageSize; +} + +bool +HostInfoWindows::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + OSVERSIONINFOEX info; + + ZeroMemory(&info, sizeof(OSVERSIONINFOEX)); + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); +#pragma warning(push) +#pragma warning(disable : 4996) + // Starting with Microsoft SDK for Windows 8.1, this function is deprecated in favor of the + // new Windows Version Helper APIs. Since we don't specify a minimum SDK version, it's easier + // to simply disable the warning rather than try to support both APIs. + if (GetVersionEx((LPOSVERSIONINFO)&info) == 0) + { + return false; + } +#pragma warning(pop) + + major = info.dwMajorVersion; + minor = info.dwMinorVersion; + update = info.wServicePackMajor; + + return true; +} + +bool +HostInfoWindows::GetOSBuildString(std::string &s) +{ + s.clear(); + uint32_t major, minor, update; + if (!GetOSVersion(major, minor, update)) + return false; + + llvm::raw_string_ostream stream(s); + stream << "Windows NT " << major << "." << minor << "." << update; + return true; +} + +bool +HostInfoWindows::GetOSKernelDescription(std::string &s) +{ + return GetOSBuildString(s); +} + +bool +HostInfoWindows::GetHostname(std::string &s) +{ + char buffer[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1; + if (!::GetComputerName(buffer, &dwSize)) + return false; + + s.assign(buffer, buffer + dwSize); + return true; +} + +FileSpec +HostInfoWindows::GetProgramFileSpec() +{ + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + char buffer[PATH_MAX]; + ::GetModuleFileName(NULL, buffer, sizeof(buffer)); + m_program_filespec.SetFile(buffer, false); + }); + return m_program_filespec; +} + +FileSpec +HostInfoWindows::GetDefaultShell() +{ + return FileSpec(::getenv("ComSpec"), false); +} + +bool +HostInfoWindows::ComputePythonDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + llvm::SmallString<64> path(lldb_file_spec.GetDirectory().AsCString()); + llvm::sys::path::remove_filename(path); + llvm::sys::path::append(path, "lib", "site-packages"); + std::replace(path.begin(), path.end(), '\\', '/'); + file_spec.GetDirectory().SetString(path.c_str()); + return true; +} diff --git a/source/Host/windows/HostProcessWindows.cpp b/source/Host/windows/HostProcessWindows.cpp new file mode 100644 index 000000000000..0f81c18d34af --- /dev/null +++ b/source/Host/windows/HostProcessWindows.cpp @@ -0,0 +1,137 @@ +//===-- HostProcessWindows.cpp ----------------------------------*- 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/FileSpec.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/windows/HostProcessWindows.h" + +#include "llvm/ADT/STLExtras.h" + +#include + +using namespace lldb_private; + +namespace +{ +struct MonitorInfo +{ + HostProcess::MonitorCallback callback; + void *baton; + HANDLE process_handle; +}; +} + +HostProcessWindows::HostProcessWindows() + : HostNativeProcessBase() + , m_owns_handle(true) +{ +} + +HostProcessWindows::HostProcessWindows(lldb::process_t process) + : HostNativeProcessBase(process) + , m_owns_handle(true) +{ +} + +HostProcessWindows::~HostProcessWindows() +{ + Close(); +} + +void +HostProcessWindows::SetOwnsHandle(bool owns) +{ + m_owns_handle = owns; +} + +Error HostProcessWindows::Terminate() +{ + Error error; + if (m_process == nullptr) + error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32); + + if (!::TerminateProcess(m_process, 0)) + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + + return error; +} + +Error HostProcessWindows::GetMainModule(FileSpec &file_spec) const +{ + Error error; + if (m_process == nullptr) + error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32); + + char path[MAX_PATH] = { 0 }; + if (::GetProcessImageFileName(m_process, path, llvm::array_lengthof(path))) + file_spec.SetFile(path, false); + else + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + + return error; +} + +lldb::pid_t HostProcessWindows::GetProcessId() const +{ + return (m_process == LLDB_INVALID_PROCESS) ? -1 : ::GetProcessId(m_process); +} + +bool HostProcessWindows::IsRunning() const +{ + if (m_process == nullptr) + return false; + + DWORD code = 0; + if (!::GetExitCodeProcess(m_process, &code)) + return false; + + return (code == STILL_ACTIVE); +} + +HostThread +HostProcessWindows::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals) +{ + HostThread monitor_thread; + MonitorInfo *info = new MonitorInfo; + info->callback = callback; + info->baton = callback_baton; + + // Since the life of this HostProcessWindows instance and the life of the process may be different, duplicate the handle so that + // the monitor thread can have ownership over its own copy of the handle. + HostThread result; + if (::DuplicateHandle(GetCurrentProcess(), m_process, GetCurrentProcess(), &info->process_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + result = ThreadLauncher::LaunchThread("ChildProcessMonitor", HostProcessWindows::MonitorThread, info, nullptr); + return result; +} + +lldb::thread_result_t +HostProcessWindows::MonitorThread(void *thread_arg) +{ + DWORD exit_code; + + MonitorInfo *info = static_cast(thread_arg); + if (info) + { + ::WaitForSingleObject(info->process_handle, INFINITE); + ::GetExitCodeProcess(info->process_handle, &exit_code); + info->callback(info->baton, ::GetProcessId(info->process_handle), true, 0, exit_code); + ::CloseHandle(info->process_handle); + delete (info); + } + return 0; +} + +void HostProcessWindows::Close() +{ + if (m_owns_handle && m_process != LLDB_INVALID_PROCESS) + ::CloseHandle(m_process); + m_process = nullptr; +} diff --git a/source/Host/windows/HostThreadWindows.cpp b/source/Host/windows/HostThreadWindows.cpp new file mode 100644 index 000000000000..d59064fb1c15 --- /dev/null +++ b/source/Host/windows/HostThreadWindows.cpp @@ -0,0 +1,98 @@ +//===-- HostThreadWindows.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" + +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/windows/HostThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +void __stdcall ExitThreadProxy(ULONG_PTR dwExitCode) +{ + ::ExitThread(dwExitCode); +} +} + +HostThreadWindows::HostThreadWindows() + : HostNativeThreadBase() + , m_owns_handle(true) +{ +} + +HostThreadWindows::HostThreadWindows(lldb::thread_t thread) + : HostNativeThreadBase(thread) + , m_owns_handle(true) +{ +} + +HostThreadWindows::~HostThreadWindows() +{ + Reset(); +} + +void +HostThreadWindows::SetOwnsHandle(bool owns) +{ + m_owns_handle = owns; +} + +Error +HostThreadWindows::Join(lldb::thread_result_t *result) +{ + Error error; + if (IsJoinable()) + { + DWORD wait_result = ::WaitForSingleObject(m_thread, INFINITE); + if (WAIT_OBJECT_0 == wait_result && result) + { + DWORD exit_code = 0; + if (!::GetExitCodeThread(m_thread, &exit_code)) + *result = 0; + *result = exit_code; + } + else if (WAIT_OBJECT_0 != wait_result) + error.SetError(::GetLastError(), eErrorTypeWin32); + } + else + error.SetError(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + Reset (); + return error; +} + +Error +HostThreadWindows::Cancel() +{ + Error error; + + DWORD result = ::QueueUserAPC(::ExitThreadProxy, m_thread, 0); + error.SetError(result, eErrorTypeWin32); + return error; +} + +lldb::tid_t +HostThreadWindows::GetThreadId() const +{ + return ::GetThreadId(m_thread); +} + +void +HostThreadWindows::Reset() +{ + if (m_owns_handle && m_thread != LLDB_INVALID_HOST_THREAD) + ::CloseHandle(m_thread); + + HostNativeThreadBase::Reset(); +} diff --git a/source/Host/windows/LockFileWindows.cpp b/source/Host/windows/LockFileWindows.cpp new file mode 100644 index 000000000000..161d3dbd8260 --- /dev/null +++ b/source/Host/windows/LockFileWindows.cpp @@ -0,0 +1,93 @@ +//===-- LockFileWindows.cpp -------------------------------------*- 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/windows/LockFileWindows.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +namespace +{ + +Error fileLock (HANDLE file_handle, DWORD flags, const uint64_t start, const uint64_t len) +{ + if (start != 0) + return Error ("Non-zero start lock regions are not supported"); + + OVERLAPPED overlapped = {0}; + + if (!::LockFileEx (file_handle, flags, 0, len, 0, &overlapped) && ::GetLastError () != ERROR_IO_PENDING) + return Error (::GetLastError (), eErrorTypeWin32); + + DWORD bytes; + if (!::GetOverlappedResult (file_handle, &overlapped, &bytes, TRUE)) + return Error (::GetLastError (), eErrorTypeWin32); + + return Error (); +} + +} // namespace + +LockFileWindows::LockFileWindows (int fd) + : LockFileBase (fd), + m_file (reinterpret_cast (_get_osfhandle (fd))) +{ +} + +LockFileWindows::~LockFileWindows () +{ + Unlock (); +} + +bool +LockFileWindows::IsValidFile () const +{ + return LockFileBase::IsValidFile() && m_file != INVALID_HANDLE_VALUE; +} + +Error +LockFileWindows::DoWriteLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_file, LOCKFILE_EXCLUSIVE_LOCK, start, len); +} + +Error +LockFileWindows::DoTryWriteLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, start, len); +} + +Error +LockFileWindows::DoReadLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_file, 0, start, len); +} + +Error +LockFileWindows::DoTryReadLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_file, LOCKFILE_FAIL_IMMEDIATELY, start, len); +} + +Error +LockFileWindows::DoUnlock () +{ + OVERLAPPED overlapped = {0}; + + if (!::UnlockFileEx (m_file, 0, m_len, 0, &overlapped) && ::GetLastError () != ERROR_IO_PENDING) + return Error (::GetLastError (), eErrorTypeWin32); + + DWORD bytes; + if (!::GetOverlappedResult (m_file, &overlapped, &bytes, TRUE)) + return Error (::GetLastError (), eErrorTypeWin32); + + return Error (); +} diff --git a/source/Host/windows/Mutex.cpp b/source/Host/windows/Mutex.cpp new file mode 100644 index 000000000000..ed90f46eb54b --- /dev/null +++ b/source/Host/windows/Mutex.cpp @@ -0,0 +1,109 @@ +//===-- Mutex.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/windows/windows.h" + +#include +#include + +#if 0 +// This logging is way too verbose to enable even for a log channel. +// This logging can be enabled by changing the "#if 0", but should be +// reverted prior to checking in. +#include +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#endif + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with no attributes. +//---------------------------------------------------------------------- +Mutex::Mutex () : + m_mutex() +{ + m_mutex = static_cast(malloc(sizeof(CRITICAL_SECTION))); + InitializeCriticalSection(static_cast(m_mutex)); +} + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with "type" as the mutex type. +//---------------------------------------------------------------------- +Mutex::Mutex (Mutex::Type type) : + m_mutex() +{ + m_mutex = static_cast(malloc(sizeof(CRITICAL_SECTION))); + InitializeCriticalSection(static_cast(m_mutex)); +} + +//---------------------------------------------------------------------- +// Destructor. +// +// Destroys the mutex owned by this object. +//---------------------------------------------------------------------- +Mutex::~Mutex() +{ + DeleteCriticalSection(static_cast(m_mutex)); + free(m_mutex); +} + +//---------------------------------------------------------------------- +// Locks the mutex owned by this object, if the mutex is already +// locked, the calling thread will block until the mutex becomes +// available. +// +// RETURNS +// The error code from the pthread_mutex_lock() function call. +//---------------------------------------------------------------------- +int +Mutex::Lock() +{ + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p)...\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), m_mutex); + + EnterCriticalSection(static_cast(m_mutex)); + return 0; +} + +//---------------------------------------------------------------------- +// Attempts to lock the mutex owned by this object without blocking. +// If the mutex is already locked, TryLock() will not block waiting +// for the mutex, but will return an error condition. +// +// RETURNS +// The error code from the pthread_mutex_trylock() function call. +//---------------------------------------------------------------------- +int +Mutex::TryLock(const char *failure_message) +{ + return TryEnterCriticalSection(static_cast(m_mutex)) == 0; +} + +//---------------------------------------------------------------------- +// 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() +{ + LeaveCriticalSection(static_cast(m_mutex)); + return 0; +} diff --git a/source/Host/windows/PipeWindows.cpp b/source/Host/windows/PipeWindows.cpp new file mode 100644 index 000000000000..d4afd6e74943 --- /dev/null +++ b/source/Host/windows/PipeWindows.cpp @@ -0,0 +1,336 @@ +//===-- PipeWindows.cpp -----------------------------------------*- 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/windows/PipeWindows.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +#include +#include + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +std::atomic g_pipe_serial(0); +} + +PipeWindows::PipeWindows() +{ + m_read = INVALID_HANDLE_VALUE; + m_write = INVALID_HANDLE_VALUE; + + m_read_fd = -1; + m_write_fd = -1; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); +} + +PipeWindows::~PipeWindows() +{ + Close(); +} + +Error +PipeWindows::CreateNew(bool child_process_inherit) +{ + // Even for anonymous pipes, we open a named pipe. This is because you cannot get + // overlapped i/o on Windows without using a named pipe. So we synthesize a unique + // name. + uint32_t serial = g_pipe_serial.fetch_add(1); + std::string pipe_name; + llvm::raw_string_ostream pipe_name_stream(pipe_name); + pipe_name_stream << "lldb.pipe." << ::GetCurrentProcessId() << "." << serial; + pipe_name_stream.flush(); + + return CreateNew(pipe_name.c_str(), child_process_inherit); +} + +Error +PipeWindows::CreateNew(llvm::StringRef name, bool child_process_inherit) +{ + if (name.empty()) + return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32); + + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + std::string pipe_path = "\\\\.\\Pipe\\"; + pipe_path.append(name); + + // Always open for overlapped i/o. We implement blocking manually in Read and Write. + DWORD read_mode = FILE_FLAG_OVERLAPPED; + m_read = + ::CreateNamedPipe(pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL); + if (INVALID_HANDLE_VALUE == m_read) + return Error(::GetLastError(), eErrorTypeWin32); + m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + + // Open the write end of the pipe. + Error result = OpenNamedPipe(name, child_process_inherit, false); + if (!result.Success()) + { + CloseReadFileDescriptor(); + return result; + } + + return result; +} + +Error +PipeWindows::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl& name) +{ + llvm::SmallString<128> pipe_name; + Error error; + ::UUID unique_id; + RPC_CSTR unique_string; + RPC_STATUS status = ::UuidCreate(&unique_id); + if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) + status = ::UuidToStringA(&unique_id, &unique_string); + if (status == RPC_S_OK) + { + pipe_name = prefix; + pipe_name += "-"; + pipe_name += reinterpret_cast(unique_string); + ::RpcStringFreeA(&unique_string); + error = CreateNew(pipe_name, child_process_inherit); + } + else + { + error.SetError(status, eErrorTypeWin32); + } + if (error.Success()) + name = pipe_name; + return error; +} + +Error +PipeWindows::OpenAsReader(llvm::StringRef name, bool child_process_inherit) +{ + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + return OpenNamedPipe(name, child_process_inherit, true); +} + +Error +PipeWindows::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout) +{ + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + return OpenNamedPipe(name, child_process_inherit, false); +} + +Error +PipeWindows::OpenNamedPipe(llvm::StringRef name, bool child_process_inherit, bool is_read) +{ + if (name.empty()) + return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32); + + assert(is_read ? !CanRead() : !CanWrite()); + + SECURITY_ATTRIBUTES attributes = {0}; + attributes.bInheritHandle = child_process_inherit; + + std::string pipe_path = "\\\\.\\Pipe\\"; + pipe_path.append(name); + + if (is_read) + { + m_read = ::CreateFile(pipe_path.c_str(), GENERIC_READ, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE == m_read) + return Error(::GetLastError(), eErrorTypeWin32); + + m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); + + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + } + else + { + m_write = ::CreateFile(pipe_path.c_str(), GENERIC_WRITE, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE == m_write) + return Error(::GetLastError(), eErrorTypeWin32); + + m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); + + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); + } + + return Error(); +} + +int +PipeWindows::GetReadFileDescriptor() const +{ + return m_read_fd; +} + +int +PipeWindows::GetWriteFileDescriptor() const +{ + return m_write_fd; +} + +int +PipeWindows::ReleaseReadFileDescriptor() +{ + if (!CanRead()) + return -1; + int result = m_read_fd; + m_read_fd = -1; + if (m_read_overlapped.hEvent) + ::CloseHandle(m_read_overlapped.hEvent); + m_read = INVALID_HANDLE_VALUE; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + return result; +} + +int +PipeWindows::ReleaseWriteFileDescriptor() +{ + if (!CanWrite()) + return -1; + int result = m_write_fd; + m_write_fd = -1; + m_write = INVALID_HANDLE_VALUE; + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); + return result; +} + +void +PipeWindows::CloseReadFileDescriptor() +{ + if (!CanRead()) + return; + + if (m_read_overlapped.hEvent) + ::CloseHandle(m_read_overlapped.hEvent); + _close(m_read_fd); + m_read = INVALID_HANDLE_VALUE; + m_read_fd = -1; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); +} + +void +PipeWindows::CloseWriteFileDescriptor() +{ + if (!CanWrite()) + return; + + _close(m_write_fd); + m_write = INVALID_HANDLE_VALUE; + m_write_fd = -1; + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); +} + +void +PipeWindows::Close() +{ + CloseReadFileDescriptor(); + CloseWriteFileDescriptor(); +} + +Error +PipeWindows::Delete(llvm::StringRef name) +{ + return Error(); +} + +bool +PipeWindows::CanRead() const +{ + return (m_read != INVALID_HANDLE_VALUE); +} + +bool +PipeWindows::CanWrite() const +{ + return (m_write != INVALID_HANDLE_VALUE); +} + +HANDLE +PipeWindows::GetReadNativeHandle() +{ + return m_read; +} + +HANDLE +PipeWindows::GetWriteNativeHandle() +{ + return m_write; +} + +Error +PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &duration, size_t &bytes_read) +{ + if (!CanRead()) + return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + bytes_read = 0; + DWORD sys_bytes_read = size; + BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read, &m_read_overlapped); + if (!result && GetLastError() != ERROR_IO_PENDING) + return Error(::GetLastError(), eErrorTypeWin32); + + DWORD timeout = (duration == std::chrono::microseconds::zero()) ? INFINITE : duration.count() * 1000; + DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout); + if (wait_result != WAIT_OBJECT_0) + { + // The operation probably failed. However, if it timed out, we need to cancel the I/O. + // Between the time we returned from WaitForSingleObject and the time we call CancelIoEx, + // the operation may complete. If that hapens, CancelIoEx will fail and return ERROR_NOT_FOUND. + // If that happens, the original operation should be considered to have been successful. + bool failed = true; + DWORD failure_error = ::GetLastError(); + if (wait_result == WAIT_TIMEOUT) + { + BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped); + if (!cancel_result && GetLastError() == ERROR_NOT_FOUND) + failed = false; + } + if (failed) + return Error(failure_error, eErrorTypeWin32); + } + + // Now we call GetOverlappedResult setting bWait to false, since we've already waited + // as long as we're willing to. + if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE)) + return Error(::GetLastError(), eErrorTypeWin32); + + bytes_read = sys_bytes_read; + return Error(); +} + +Error +PipeWindows::Write(const void *buf, size_t num_bytes, size_t &bytes_written) +{ + if (!CanWrite()) + return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + DWORD sys_bytes_written = 0; + BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written, &m_write_overlapped); + if (!write_result && GetLastError() != ERROR_IO_PENDING) + return Error(::GetLastError(), eErrorTypeWin32); + + BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_written, TRUE); + if (!result) + return Error(::GetLastError(), eErrorTypeWin32); + return Error(); +} diff --git a/source/Host/windows/ProcessLauncherWindows.cpp b/source/Host/windows/ProcessLauncherWindows.cpp new file mode 100644 index 000000000000..355dd40544f9 --- /dev/null +++ b/source/Host/windows/ProcessLauncherWindows.cpp @@ -0,0 +1,105 @@ +//===-- ProcessLauncherWindows.cpp ------------------------------*- 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/HostProcess.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/ProcessLaunchInfo.h" + +#include +#include + +using namespace lldb; +using namespace lldb_private; + +HostProcess +ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + error.Clear(); + + std::string executable; + std::string commandLine; + std::vector environment; + STARTUPINFO startupinfo = {0}; + PROCESS_INFORMATION pi = {0}; + + HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); + HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); + HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO); + + startupinfo.cb = sizeof(startupinfo); + startupinfo.dwFlags |= STARTF_USESTDHANDLES; + startupinfo.hStdError = stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); + startupinfo.hStdInput = stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE); + startupinfo.hStdOutput = stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); + + const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"); + if (hide_console_var && llvm::StringRef(hide_console_var).equals_lower("true")) + { + startupinfo.dwFlags |= STARTF_USESHOWWINDOW; + startupinfo.wShowWindow = SW_HIDE; + } + + DWORD flags = CREATE_NEW_CONSOLE; + if (launch_info.GetFlags().Test(eLaunchFlagDebug)) + flags |= DEBUG_ONLY_THIS_PROCESS; + + executable = launch_info.GetExecutableFile().GetPath(); + launch_info.GetArguments().GetQuotedCommandString(commandLine); + BOOL result = ::CreateProcessA(executable.c_str(), const_cast(commandLine.c_str()), NULL, NULL, TRUE, flags, NULL, + launch_info.GetWorkingDirectory().GetCString(), &startupinfo, &pi); + if (result) + { + // Do not call CloseHandle on pi.hProcess, since we want to pass that back through the HostProcess. + ::CloseHandle(pi.hThread); + } + + if (stdin_handle) + ::CloseHandle(stdin_handle); + if (stdout_handle) + ::CloseHandle(stdout_handle); + if (stderr_handle) + ::CloseHandle(stderr_handle); + + if (!result) + error.SetError(::GetLastError(), eErrorTypeWin32); + return HostProcess(pi.hProcess); +} + +HANDLE +ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd) +{ + const FileAction *action = launch_info.GetFileActionForFD(fd); + if (action == nullptr) + return NULL; + SECURITY_ATTRIBUTES secattr = {0}; + secattr.nLength = sizeof(SECURITY_ATTRIBUTES); + secattr.bInheritHandle = TRUE; + + const char *path = action->GetPath(); + DWORD access = 0; + DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + DWORD create = 0; + DWORD flags = 0; + if (fd == STDIN_FILENO) + { + access = GENERIC_READ; + create = OPEN_EXISTING; + flags = FILE_ATTRIBUTE_READONLY; + } + if (fd == STDOUT_FILENO || fd == STDERR_FILENO) + { + access = GENERIC_WRITE; + create = CREATE_ALWAYS; + if (fd == STDERR_FILENO) + flags = FILE_FLAG_WRITE_THROUGH; + } + + HANDLE result = ::CreateFile(path, access, share, &secattr, create, flags, NULL); + return (result == INVALID_HANDLE_VALUE) ? NULL : result; +} diff --git a/source/Host/windows/ProcessRunLock.cpp b/source/Host/windows/ProcessRunLock.cpp new file mode 100644 index 000000000000..1f21552a3062 --- /dev/null +++ b/source/Host/windows/ProcessRunLock.cpp @@ -0,0 +1,106 @@ +#include "lldb/Host/ProcessRunLock.h" +#include "lldb/Host/windows/windows.h" + +namespace +{ +#if defined(__MINGW32__) +// Taken from WinNT.h +typedef struct _RTL_SRWLOCK { + PVOID Ptr; +} RTL_SRWLOCK, *PRTL_SRWLOCK; + +// Taken from WinBase.h +typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; +#endif +} + + +static PSRWLOCK GetLock(lldb::rwlock_t lock) +{ + return static_cast(lock); +} + +static bool ReadLock(lldb::rwlock_t rwlock) +{ + ::AcquireSRWLockShared(GetLock(rwlock)); + return true; +} + +static bool ReadUnlock(lldb::rwlock_t rwlock) +{ + ::ReleaseSRWLockShared(GetLock(rwlock)); + return true; +} + +static bool WriteLock(lldb::rwlock_t rwlock) +{ + ::AcquireSRWLockExclusive(GetLock(rwlock)); + return true; +} + +static bool WriteTryLock(lldb::rwlock_t rwlock) +{ + return !!::TryAcquireSRWLockExclusive(GetLock(rwlock)); +} + +static bool WriteUnlock(lldb::rwlock_t rwlock) +{ + ::ReleaseSRWLockExclusive(GetLock(rwlock)); + return true; +} + +using namespace lldb_private; + +ProcessRunLock::ProcessRunLock() + : m_running(false) +{ + m_rwlock = new SRWLOCK; + InitializeSRWLock(GetLock(m_rwlock)); +} + +ProcessRunLock::~ProcessRunLock() +{ + delete m_rwlock; +} + +bool ProcessRunLock::ReadTryLock() +{ + ::ReadLock(m_rwlock); + if (m_running == false) + return true; + ::ReadUnlock(m_rwlock); + return false; +} + +bool ProcessRunLock::ReadUnlock() +{ + return ::ReadUnlock(m_rwlock); +} + +bool ProcessRunLock::SetRunning () +{ + WriteLock(m_rwlock); + m_running = true; + WriteUnlock(m_rwlock); + return true; +} + +bool ProcessRunLock::TrySetRunning () +{ + if (WriteTryLock(m_rwlock)) + { + bool was_running = m_running; + m_running = true; + WriteUnlock(m_rwlock); + return !was_running; + } + return false; +} + +bool ProcessRunLock::SetStopped () +{ + WriteLock(m_rwlock); + m_running = false; + WriteUnlock(m_rwlock); + return true; +} diff --git a/source/Host/windows/ThisThread.cpp b/source/Host/windows/ThisThread.cpp new file mode 100644 index 000000000000..bcd5b8d1c1d0 --- /dev/null +++ b/source/Host/windows/ThisThread.cpp @@ -0,0 +1,66 @@ +//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" + +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/ThisThread.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" + +using namespace lldb; +using namespace lldb_private; + +#if defined(_MSC_VER) && !defined(__clang__) + +namespace +{ +static const DWORD MS_VC_EXCEPTION = 0x406D1388; + +#pragma pack(push, 8) +struct THREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to thread name + DWORD dwThreadId; // Thread ID (-1 == current thread) + DWORD dwFlags; // Reserved. Do not use. +}; +#pragma pack(pop) +} + +#endif + +void +ThisThread::SetName(llvm::StringRef name) +{ +// Other compilers don't yet support SEH, so we can only set the thread if compiling with MSVC. +// TODO(zturner): Once clang-cl supports SEH, relax this conditional. +#if defined(_MSC_VER) && !defined(__clang__) + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name.data(); + info.dwThreadId = ::GetCurrentThreadId(); + info.dwFlags = 0; + + __try { + ::RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) {} +#endif +} + +void +ThisThread::GetName(llvm::SmallVectorImpl &name) +{ + // Getting the thread name is not supported on Windows. + // TODO(zturner): In SetName(), make a TLS entry that contains the thread's name, and in this function + // try to extract that TLS entry. + name.clear(); +} diff --git a/source/Host/windows/Windows.cpp b/source/Host/windows/Windows.cpp new file mode 100644 index 000000000000..71bff7a9d2e2 --- /dev/null +++ b/source/Host/windows/Windows.cpp @@ -0,0 +1,231 @@ +//===-- Windows.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file provides Windows support functions + +#include "lldb/Host/windows/windows.h" +#include "lldb/Host/windows/win32.h" + +#include +#include +#include +#include +#include +#include +#include + +// These prototypes are defined in , but it also defines chdir() and getcwd(), giving multiply defined errors +extern "C" +{ + char *_getcwd(char *buffer, int maxlen); + int _chdir(const char *path); +} + +int vasprintf(char **ret, const char *fmt, va_list ap) +{ + char *buf; + int len; + size_t buflen; + va_list ap2; + +#if defined(_MSC_VER) || defined(__MINGW64) + ap2 = ap; + len = _vscprintf(fmt, ap2); +#else + va_copy(ap2, ap); + len = vsnprintf(NULL, 0, fmt, ap2); +#endif + + if (len >= 0 && (buf = (char*) malloc ((buflen = (size_t) (len + 1)))) != NULL) { + len = vsnprintf(buf, buflen, fmt, ap); + *ret = buf; + } else { + *ret = NULL; + len = -1; + } + + va_end(ap2); + return len; +} + +char* strcasestr(const char *s, const char* find) +{ + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + c = tolower((unsigned char) c); + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return 0; + } while ((char) tolower((unsigned char) sc) != c); + } while (strncasecmp(s, find, len) != 0); + s--; + } + return ((char *) s); +} + +char* realpath(const char * name, char * resolved) +{ + char *retname = NULL; /* we will return this, if we fail */ + + /* SUSv3 says we must set `errno = EINVAL', and return NULL, + * if `name' is passed as a NULL pointer. + */ + + if (name == NULL) + errno = EINVAL; + + /* Otherwise, `name' must refer to a readable filesystem object, + * if we are going to resolve its absolute path name. + */ + + else if (access(name, 4) == 0) + { + /* If `name' didn't point to an existing entity, + * then we don't get to here; we simply fall past this block, + * returning NULL, with `errno' appropriately set by `access'. + * + * When we _do_ get to here, then we can use `_fullpath' to + * resolve the full path for `name' into `resolved', but first, + * check that we have a suitable buffer, in which to return it. + */ + + if ((retname = resolved) == NULL) + { + /* Caller didn't give us a buffer, so we'll exercise the + * option granted by SUSv3, and allocate one. + * + * `_fullpath' would do this for us, but it uses `malloc', and + * Microsoft's implementation doesn't set `errno' on failure. + * If we don't do this explicitly ourselves, then we will not + * know if `_fullpath' fails on `malloc' failure, or for some + * other reason, and we want to set `errno = ENOMEM' for the + * `malloc' failure case. + */ + + retname = (char*) malloc(_MAX_PATH); + } + + /* By now, we should have a valid buffer. + * If we don't, then we know that `malloc' failed, + * so we can set `errno = ENOMEM' appropriately. + */ + + if (retname == NULL) + errno = ENOMEM; + + /* Otherwise, when we do have a valid buffer, + * `_fullpath' should only fail if the path name is too long. + */ + + else if ((retname = _fullpath(retname, name, _MAX_PATH)) == NULL) + errno = ENAMETOOLONG; + } + + /* By the time we get to here, + * `retname' either points to the required resolved path name, + * or it is NULL, with `errno' set appropriately, either of which + * is our required return condition. + */ + + if (retname != NULL) + { + // Do a LongPath<->ShortPath roundtrip so that case is resolved by OS + int initialLength = strlen(retname); + TCHAR buffer[MAX_PATH]; + GetShortPathName(retname, buffer, MAX_PATH); + GetLongPathName(buffer, retname, initialLength + 1); + + // Force drive to be upper case + if (retname[1] == ':') + retname[0] = toupper(retname[0]); + } + + return retname; +} + +#ifdef _MSC_VER + +char* basename(char *path) +{ + char* l1 = strrchr(path, '\\'); + char* l2 = strrchr(path, '/'); + if (l2 > l1) l1 = l2; + if (!l1) return path; // no base name + return &l1[1]; +} + +// use _getcwd() instead of GetCurrentDirectory() because it updates errno +char* getcwd(char* path, int max) +{ + return _getcwd(path, max); +} + +// use _chdir() instead of SetCurrentDirectory() because it updates errno +int chdir(const char* path) +{ + return _chdir(path); +} + +char *dirname(char *path) +{ + char* l1 = strrchr(path, '\\'); + char* l2 = strrchr(path, '/'); + if (l2 > l1) l1 = l2; + if (!l1) return NULL; // no dir name + *l1 = 0; + return path; +} + +int strcasecmp(const char* s1, const char* s2) +{ + return stricmp(s1, s2); +} + +int strncasecmp(const char* s1, const char* s2, size_t n) +{ + return strnicmp(s1, s2, n); +} + +int usleep(uint32_t useconds) +{ + Sleep(useconds / 1000); + return 0; +} + +#if _MSC_VER < 1900 +namespace lldb_private { +int vsnprintf(char *buffer, size_t count, const char *format, va_list argptr) +{ + int old_errno = errno; + int r = ::vsnprintf(buffer, count, format, argptr); + int new_errno = errno; + buffer[count-1] = '\0'; + if (r == -1 || r == count) + { + FILE *nul = fopen("nul", "w"); + int bytes_written = ::vfprintf(nul, format, argptr); + fclose(nul); + if (bytes_written < count) + errno = new_errno; + else + { + errno = old_errno; + r = bytes_written; + } + } + return r; +} +} // namespace lldb_private +#endif + +#endif // _MSC_VER diff --git a/source/Initialization/CMakeLists.txt b/source/Initialization/CMakeLists.txt new file mode 100644 index 000000000000..f23a2b4339b8 --- /dev/null +++ b/source/Initialization/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbInitialization + SystemInitializerCommon.cpp + SystemInitializer.cpp + SystemLifetimeManager.cpp + ) diff --git a/source/Initialization/Makefile b/source/Initialization/Makefile new file mode 100644 index 000000000000..a63f4733bb6e --- /dev/null +++ b/source/Initialization/Makefile @@ -0,0 +1,14 @@ +##===- source/Initialize/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbInitialization +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Interpreter/CMakeLists.txt b/source/Interpreter/CMakeLists.txt new file mode 100644 index 000000000000..b92128cd3011 --- /dev/null +++ b/source/Interpreter/CMakeLists.txt @@ -0,0 +1,45 @@ +add_lldb_library(lldbInterpreter + Args.cpp + CommandHistory.cpp + CommandInterpreter.cpp + CommandObject.cpp + CommandObjectRegexCommand.cpp + CommandObjectScript.cpp + CommandOptionValidators.cpp + CommandReturnObject.cpp + OptionGroupArchitecture.cpp + OptionGroupBoolean.cpp + OptionGroupFile.cpp + OptionGroupFormat.cpp + OptionGroupOutputFile.cpp + OptionGroupPlatform.cpp + OptionGroupString.cpp + OptionGroupUInt64.cpp + OptionGroupUUID.cpp + OptionGroupValueObjectDisplay.cpp + OptionValue.cpp + OptionValueArch.cpp + OptionValueArgs.cpp + OptionValueArray.cpp + OptionValueBoolean.cpp + OptionValueChar.cpp + OptionValueDictionary.cpp + OptionValueEnumeration.cpp + OptionValueFileSpec.cpp + OptionValueFileSpecLIst.cpp + OptionValueFormat.cpp + OptionValueFormatEntity.cpp + OptionValueLanguage.cpp + OptionValuePathMappings.cpp + OptionValueProperties.cpp + OptionValueRegex.cpp + OptionValueSInt64.cpp + OptionValueString.cpp + OptionValueUInt64.cpp + OptionValueUUID.cpp + OptionGroupVariable.cpp + OptionGroupWatchpoint.cpp + Options.cpp + Property.cpp + ScriptInterpreter.cpp + ) diff --git a/source/Interpreter/CommandHistory.cpp b/source/Interpreter/CommandHistory.cpp index bbe64b446acc..9d3c814697b0 100644 --- a/source/Interpreter/CommandHistory.cpp +++ b/source/Interpreter/CommandHistory.cpp @@ -130,9 +130,9 @@ CommandHistory::Dump (Stream& stream, size_t stop_idx) const { Mutex::Locker locker(m_mutex); - stop_idx = std::min(stop_idx, m_history.size() - 1); + stop_idx = std::min(stop_idx + 1, m_history.size()); for (size_t counter = start_idx; - counter <= stop_idx; + counter < stop_idx; counter++) { const std::string hist_item = m_history[counter]; diff --git a/source/Interpreter/Makefile b/source/Interpreter/Makefile new file mode 100644 index 000000000000..2f25e6796604 --- /dev/null +++ b/source/Interpreter/Makefile @@ -0,0 +1,51 @@ +##===- source/Interpreter/Makefile ------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbInterpreter +BUILD_ARCHIVE = 1 +include $(LLDB_LEVEL)/../../Makefile.config + +ifneq ($(HOST_OS),MingW) +ifeq (,$(findstring -DLLDB_DISABLE_PYTHON,$(CXXFLAGS))) +DO_BUILD_LLDBWrapPython = 1 +BUILT_SOURCES := LLDBWrapPython.cpp +endif +endif + +include $(LLDB_LEVEL)/Makefile +-include $(PROJ_OBJ_DIR)/LLDBWrapPython.cpp.d + +ifeq ($(DO_BUILD_LLDBWrapPython),1) +# Drop -Wfour-char-constants, which we are not currently clean with. +EXTRA_OPTIONS += -Wno-four-char-constants + +# Drop -Wself-assign, -Wmissing-field-initializers, -Wsometimes-uninitialized, +# -Wcast-qual, and -Wdeprecated-register which we are not clean with due to SWIG +# generated cpp source. +EXTRA_OPTIONS += -Wno-missing-field-initializers -Wno-self-assign -Wno-sometimes-uninitialized -Wno-cast-qual -Wno-deprecated-register + +PYTHON_DIR := $(PROJ_OBJ_ROOT)/$(BuildMode) + +SWIG_SOURCES := $(shell find $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts -type f -name '*.swig' -print) + +LLDBWrapPython.cpp lldb.py: $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/Python/modify-python-lldb.py \ + $(wildcard $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/interface/*.i) \ + ${SWIG_SOURCES} + $(Echo) Generating LLDBWrapPython.cpp + $(Verb) "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/prepare_bindings.py" "--src-root=$(PROJ_SRC_DIR)/$(LLDB_LEVEL)" "--target-dir=$(PROJ_OBJ_DIR)" "--config-build-dir=$(PROJ_OBJ_DIR)" "--prefix=$(PYTHON_DIR)" $(if $(DISABLE_AUTO_DEPENDENCIES),,-M) --find-swig + $(Verb) "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/finish-swig-wrapper-classes.sh" "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)" "$(PROJ_OBJ_DIR)" "$(PROJ_OBJ_DIR)" "$(PYTHON_DIR)" -m + +install-local:: lldb.py + $(Echo) Installing $(BuildMode) LLDB python modules + $(Verb) "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/prepare_bindings.py" "--src-root=$(PROJ_SRC_DIR)/$(LLDB_LEVEL)" "--target-dir=$(PROJ_OBJ_DIR)" "--config-build-dir=$(PROJ_OBJ_DIR)" "--prefix=$(DESTDIR)$(prefix)" --find-swig + +clean-local:: + $(Verb) $(RM) -f LLDBWrapPython.cpp lldb.py +endif diff --git a/source/Makefile b/source/Makefile new file mode 100644 index 000000000000..0ad56ffa5e0c --- /dev/null +++ b/source/Makefile @@ -0,0 +1,37 @@ +##===- source/Makefile -------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := .. +PARALLEL_DIRS := API Initialization Breakpoint Commands Core DataFormatters Expression Host Interpreter Plugins Symbol Target Utility +LIBRARYNAME := lldbBase +BUILD_ARCHIVE = 1 + +# Although LLVM makefiles provide $(HOST_OS), we cannot use that here because it is defined by including the $(LLDB_LEVEL)/Makefile +# below. Instead, we use uname -s to detect the HOST_OS and generate LLDB_vers.c only on Mac. On Linux, the version number is +# calculated in the same way as Clang's version. +ifeq (Darwin,$(shell uname -s)) +BUILT_SOURCES = LLDB_vers.c +endif + +SOURCES := lldb.cpp + +include $(LLDB_LEVEL)/Makefile + +ifeq ($(HOST_OS),Darwin) +LLDB_vers.c: $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/generate-vers.pl $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/lldb.xcodeproj/project.pbxproj + "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/generate-vers.pl" "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/lldb.xcodeproj/project.pbxproj" liblldb_core > LLDB_vers.c +else +LLDB_REVISION := $(strip \ + $(shell $(LLVM_SRC_ROOT)/utils/GetSourceVersion $(LLVM_SRC_ROOT)/tools/lldb)) + +LLDB_REPOSITORY := $(strip \ + $(shell $(LLVM_SRC_ROOT)/utils/GetRepositoryPath $(LLVM_SRC_ROOT)/tools/lldb)) + +CPP.Defines += -DLLDB_REVISION='"$(LLDB_REVISION)"' -DLLDB_REPOSITORY='"$(LLDB_REPOSITORY)"' +endif diff --git a/source/Plugins/ABI/CMakeLists.txt b/source/Plugins/ABI/CMakeLists.txt new file mode 100644 index 000000000000..08452c5dad62 --- /dev/null +++ b/source/Plugins/ABI/CMakeLists.txt @@ -0,0 +1,12 @@ +add_subdirectory(SysV-arm) +add_subdirectory(SysV-arm64) +add_subdirectory(SysV-hexagon) +add_subdirectory(SysV-ppc) +add_subdirectory(SysV-ppc64) +add_subdirectory(SysV-mips) +add_subdirectory(SysV-mips64) +add_subdirectory(SysV-i386) +add_subdirectory(SysV-x86_64) +add_subdirectory(MacOSX-i386) +add_subdirectory(MacOSX-arm) +add_subdirectory(MacOSX-arm64) diff --git a/source/Plugins/ABI/MacOSX-arm/CMakeLists.txt b/source/Plugins/ABI/MacOSX-arm/CMakeLists.txt new file mode 100644 index 000000000000..ea8b011b1984 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABIMacOSX_arm + ABIMacOSX_arm.cpp + ) diff --git a/source/Plugins/ABI/MacOSX-arm/Makefile b/source/Plugins/ABI/MacOSX-arm/Makefile new file mode 100644 index 000000000000..18073266d34d --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/MacOSX-arm/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===--------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABIMacOSX_arm +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/MacOSX-arm64/CMakeLists.txt b/source/Plugins/ABI/MacOSX-arm64/CMakeLists.txt new file mode 100644 index 000000000000..498bb7218637 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABIMacOSX_arm64 + ABIMacOSX_arm64.cpp + ) diff --git a/source/Plugins/ABI/MacOSX-arm64/Makefile b/source/Plugins/ABI/MacOSX-arm64/Makefile new file mode 100644 index 000000000000..7fc6909e43cc --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/MacOSX-arm64/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABIMacOSX_arm64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/MacOSX-i386/CMakeLists.txt b/source/Plugins/ABI/MacOSX-i386/CMakeLists.txt new file mode 100644 index 000000000000..f6a543a66da7 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-i386/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABIMacOSX_i386 + ABIMacOSX_i386.cpp + ) diff --git a/source/Plugins/ABI/MacOSX-i386/Makefile b/source/Plugins/ABI/MacOSX-i386/Makefile new file mode 100644 index 000000000000..d9bc73922563 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-i386/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/MacOSX-i386/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABIMacOSX_i386 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-arm/CMakeLists.txt b/source/Plugins/ABI/SysV-arm/CMakeLists.txt new file mode 100644 index 000000000000..c25ce5a1c633 --- /dev/null +++ b/source/Plugins/ABI/SysV-arm/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_arm + ABISysV_arm.cpp + ) diff --git a/source/Plugins/ABI/SysV-arm/Makefile b/source/Plugins/ABI/SysV-arm/Makefile new file mode 100644 index 000000000000..a1d95dc17320 --- /dev/null +++ b/source/Plugins/ABI/SysV-arm/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-arm/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===--------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_arm +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-arm64/CMakeLists.txt b/source/Plugins/ABI/SysV-arm64/CMakeLists.txt new file mode 100644 index 000000000000..0ddb37043ace --- /dev/null +++ b/source/Plugins/ABI/SysV-arm64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_arm64 + ABISysV_arm64.cpp + ) diff --git a/source/Plugins/ABI/SysV-arm64/Makefile b/source/Plugins/ABI/SysV-arm64/Makefile new file mode 100644 index 000000000000..a72ecd83404d --- /dev/null +++ b/source/Plugins/ABI/SysV-arm64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-arm64/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_arm64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-hexagon/CMakeLists.txt b/source/Plugins/ABI/SysV-hexagon/CMakeLists.txt new file mode 100644 index 000000000000..b8fbb0744719 --- /dev/null +++ b/source/Plugins/ABI/SysV-hexagon/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_hexagon + ABISysV_hexagon.cpp + ) diff --git a/source/Plugins/ABI/SysV-hexagon/Makefile b/source/Plugins/ABI/SysV-hexagon/Makefile new file mode 100644 index 000000000000..23733c7e551e --- /dev/null +++ b/source/Plugins/ABI/SysV-hexagon/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-hexagon/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_hexagon +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-i386/CMakeLists.txt b/source/Plugins/ABI/SysV-i386/CMakeLists.txt new file mode 100644 index 000000000000..3528f01ad75e --- /dev/null +++ b/source/Plugins/ABI/SysV-i386/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_i386 + ABISysV_i386.cpp + ) diff --git a/source/Plugins/ABI/SysV-i386/Makefile b/source/Plugins/ABI/SysV-i386/Makefile new file mode 100644 index 000000000000..0ac3cbee2d54 --- /dev/null +++ b/source/Plugins/ABI/SysV-i386/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-i386/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_i386 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-mips/CMakeLists.txt b/source/Plugins/ABI/SysV-mips/CMakeLists.txt new file mode 100644 index 000000000000..0db3e5c1c7e0 --- /dev/null +++ b/source/Plugins/ABI/SysV-mips/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_mips + ABISysV_mips.cpp + ) diff --git a/source/Plugins/ABI/SysV-mips/Makefile b/source/Plugins/ABI/SysV-mips/Makefile new file mode 100644 index 000000000000..c61130628433 --- /dev/null +++ b/source/Plugins/ABI/SysV-mips/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-mips/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_mips +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-mips64/CMakeLists.txt b/source/Plugins/ABI/SysV-mips64/CMakeLists.txt new file mode 100644 index 000000000000..099464821edd --- /dev/null +++ b/source/Plugins/ABI/SysV-mips64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_mips64 + ABISysV_mips64.cpp + ) diff --git a/source/Plugins/ABI/SysV-mips64/Makefile b/source/Plugins/ABI/SysV-mips64/Makefile new file mode 100644 index 000000000000..b7e6dc615cb0 --- /dev/null +++ b/source/Plugins/ABI/SysV-mips64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-mips64/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_mips64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-ppc/CMakeLists.txt b/source/Plugins/ABI/SysV-ppc/CMakeLists.txt new file mode 100644 index 000000000000..a7784195f771 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_ppc + ABISysV_ppc.cpp + ) diff --git a/source/Plugins/ABI/SysV-ppc/Makefile b/source/Plugins/ABI/SysV-ppc/Makefile new file mode 100644 index 000000000000..7a6d38d68337 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-hexagon/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_ppc +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-ppc64/CMakeLists.txt b/source/Plugins/ABI/SysV-ppc64/CMakeLists.txt new file mode 100644 index 000000000000..7a33d2922047 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_ppc64 + ABISysV_ppc64.cpp + ) diff --git a/source/Plugins/ABI/SysV-ppc64/Makefile b/source/Plugins/ABI/SysV-ppc64/Makefile new file mode 100644 index 000000000000..43fc41d42713 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-hexagon/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_ppc64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ABI/SysV-x86_64/CMakeLists.txt b/source/Plugins/ABI/SysV-x86_64/CMakeLists.txt new file mode 100644 index 000000000000..a2b62d7240fd --- /dev/null +++ b/source/Plugins/ABI/SysV-x86_64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_x86_64 + ABISysV_x86_64.cpp + ) diff --git a/source/Plugins/ABI/SysV-x86_64/Makefile b/source/Plugins/ABI/SysV-x86_64/Makefile new file mode 100644 index 000000000000..32990a64f956 --- /dev/null +++ b/source/Plugins/ABI/SysV-x86_64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ABI/SysV-x86_64/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginABISysV_x86_64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/CMakeLists.txt b/source/Plugins/CMakeLists.txt new file mode 100644 index 000000000000..928332fb6ba4 --- /dev/null +++ b/source/Plugins/CMakeLists.txt @@ -0,0 +1,20 @@ +add_subdirectory(ABI) +add_subdirectory(Disassembler) +add_subdirectory(DynamicLoader) +add_subdirectory(ExpressionParser) +add_subdirectory(Instruction) +add_subdirectory(InstrumentationRuntime) +add_subdirectory(JITLoader) +add_subdirectory(Language) +add_subdirectory(LanguageRuntime) +add_subdirectory(MemoryHistory) +add_subdirectory(ObjectContainer) +add_subdirectory(ObjectFile) +add_subdirectory(OperatingSystem) +add_subdirectory(Platform) +add_subdirectory(Process) +add_subdirectory(ScriptInterpreter) +add_subdirectory(SymbolFile) +add_subdirectory(SystemRuntime) +add_subdirectory(SymbolVendor) +add_subdirectory(UnwindAssembly) diff --git a/source/Plugins/Disassembler/CMakeLists.txt b/source/Plugins/Disassembler/CMakeLists.txt new file mode 100644 index 000000000000..6e3c904d5a60 --- /dev/null +++ b/source/Plugins/Disassembler/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(llvm) diff --git a/source/Plugins/Disassembler/llvm/CMakeLists.txt b/source/Plugins/Disassembler/llvm/CMakeLists.txt new file mode 100644 index 000000000000..ada81dc55e97 --- /dev/null +++ b/source/Plugins/Disassembler/llvm/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDisassemblerLLVM + DisassemblerLLVMC.cpp + ) diff --git a/source/Plugins/Disassembler/llvm/Makefile b/source/Plugins/Disassembler/llvm/Makefile new file mode 100644 index 000000000000..a1309cdd081f --- /dev/null +++ b/source/Plugins/Disassembler/llvm/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Disassembler/llvm/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDisassemblerLLVM +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/CMakeLists.txt b/source/Plugins/DynamicLoader/CMakeLists.txt new file mode 100644 index 000000000000..8e1b316a3c25 --- /dev/null +++ b/source/Plugins/DynamicLoader/CMakeLists.txt @@ -0,0 +1,9 @@ +add_subdirectory(MacOSX-DYLD) +add_subdirectory(POSIX-DYLD) +add_subdirectory(Static) +add_subdirectory(Hexagon-DYLD) +add_subdirectory(Windows-DYLD) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(Darwin-Kernel) +endif() diff --git a/source/Plugins/DynamicLoader/Darwin-Kernel/CMakeLists.txt b/source/Plugins/DynamicLoader/Darwin-Kernel/CMakeLists.txt new file mode 100644 index 000000000000..02752b78a4c7 --- /dev/null +++ b/source/Plugins/DynamicLoader/Darwin-Kernel/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDynamicLoaderDarwinKernel + DynamicLoaderDarwinKernel.cpp + ) diff --git a/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp new file mode 100644 index 000000000000..218b0b7a1eee --- /dev/null +++ b/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -0,0 +1,1698 @@ +//===-- DynamicLoaderDarwinKernel.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/SafeMachO.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "Plugins/Platform/MacOSX/PlatformDarwinKernel.h" + +#include "DynamicLoaderDarwinKernel.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; + +// Progressively greater amounts of scanning we will allow +// For some targets very early in startup, we can't do any random reads of memory or we can crash the device +// so a setting is needed that can completely disable the KASLR scans. + +enum KASLRScanType +{ + eKASLRScanNone = 0, // No reading into the inferior at all + eKASLRScanLowgloAddresses, // Check one word of memory for a possible kernel addr, then see if a kernel is there + eKASLRScanNearPC, // Scan backwards from the current $pc looking for kernel; checking at 96 locations total + eKASLRScanExhaustiveScan // Scan through the entire possible kernel address range looking for a kernel +}; + +OptionEnumValueElement +g_kaslr_kernel_scan_enum_values[] = +{ + { eKASLRScanNone, "none", "Do not read memory looking for a Darwin kernel when attaching." }, + { eKASLRScanLowgloAddresses, "basic", "Check for the Darwin kernel's load addr in the lowglo page (boot-args=debug) only." }, + { eKASLRScanNearPC, "fast-scan", "Scan near the pc value on attach to find the Darwin kernel's load address."}, + { eKASLRScanExhaustiveScan, "exhaustive-scan", "Scan through the entire potential address range of Darwin kernel (only on 32-bit targets)."}, + { 0, NULL, NULL } +}; + +static PropertyDefinition +g_properties[] = +{ + { "load-kexts" , OptionValue::eTypeBoolean, true, true, NULL, NULL, "Automatically loads kext images when attaching to a kernel." }, + { "scan-type", OptionValue::eTypeEnum, true, eKASLRScanNearPC, NULL, g_kaslr_kernel_scan_enum_values, "Control how many reads lldb will make while searching for a Darwin kernel on attach." }, + { NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL } +}; + +enum { + ePropertyLoadKexts, + ePropertyScanType +}; + +class DynamicLoaderDarwinKernelProperties : public Properties +{ +public: + + static ConstString & + GetSettingName () + { + static ConstString g_setting_name("darwin-kernel"); + return g_setting_name; + } + + DynamicLoaderDarwinKernelProperties() : + Properties () + { + m_collection_sp.reset (new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual + ~DynamicLoaderDarwinKernelProperties() + { + } + + bool + GetLoadKexts() const + { + const uint32_t idx = ePropertyLoadKexts; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); + } + + KASLRScanType + GetScanType() const + { + const uint32_t idx = ePropertyScanType; + return (KASLRScanType) m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); + } + + +}; + +typedef std::shared_ptr DynamicLoaderDarwinKernelPropertiesSP; + +static const DynamicLoaderDarwinKernelPropertiesSP & +GetGlobalProperties() +{ + static DynamicLoaderDarwinKernelPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new DynamicLoaderDarwinKernelProperties ()); + return g_settings_sp; +} + +//---------------------------------------------------------------------- +// 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 * +DynamicLoaderDarwinKernel::CreateInstance (Process* process, bool force) +{ + if (!force) + { + // If the user provided an executable binary and it is not a kernel, + // this plugin should not create an instance. + Module* exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) + { + if (object_file->GetStrata() != ObjectFile::eStrataKernel) + { + return NULL; + } + } + } + + // If the target's architecture does not look like an Apple environment, + // this plugin should not create an instance. + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + if (triple_ref.getVendor() != llvm::Triple::Apple) + { + return NULL; + } + break; + // If we have triple like armv7-unknown-unknown, we should try looking for a Darwin kernel. + case llvm::Triple::UnknownOS: + break; + default: + return NULL; + break; + } + } + + // At this point if there is an ExecutableModule, it is a kernel and the Target is some variant of an Apple system. + // If the Process hasn't provided the kernel load address, we need to look around in memory to find it. + + addr_t kernel_load_address = SearchForDarwinKernel (process); + if (kernel_load_address != LLDB_INVALID_ADDRESS) + { + process->SetCanRunCode(false); + return new DynamicLoaderDarwinKernel (process, kernel_load_address); + } + return NULL; +} + +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForDarwinKernel (Process *process) +{ + addr_t kernel_load_address = process->GetImageInfoAddress(); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + { + kernel_load_address = SearchForKernelAtSameLoadAddr (process); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + { + kernel_load_address = SearchForKernelWithDebugHints (process); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + { + kernel_load_address = SearchForKernelNearPC (process); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + { + kernel_load_address = SearchForKernelViaExhaustiveSearch (process); + } + } + } + } + return kernel_load_address; +} + +//---------------------------------------------------------------------- +// Check if the kernel binary is loaded in memory without a slide. +// First verify that the ExecutableModule is a kernel before we proceed. +// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS. +//---------------------------------------------------------------------- +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForKernelAtSameLoadAddr (Process *process) +{ + Module *exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module == NULL) + return LLDB_INVALID_ADDRESS; + + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + if (exe_objfile == NULL) + return LLDB_INVALID_ADDRESS; + + if (exe_objfile->GetType() != ObjectFile::eTypeExecutable || exe_objfile->GetStrata() != ObjectFile::eStrataKernel) + return LLDB_INVALID_ADDRESS; + + if (!exe_objfile->GetHeaderAddress().IsValid()) + return LLDB_INVALID_ADDRESS; + + if (CheckForKernelImageAtAddress (exe_objfile->GetHeaderAddress().GetFileAddress(), process) == exe_module->GetUUID()) + return exe_objfile->GetHeaderAddress().GetFileAddress(); + + return LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// If the debug flag is included in the boot-args nvram setting, the kernel's load address +// will be noted in the lowglo page at a fixed address +// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS. +//---------------------------------------------------------------------- +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForKernelWithDebugHints (Process *process) +{ + if (GetGlobalProperties()->GetScanType() == eKASLRScanNone) + return LLDB_INVALID_ADDRESS; + + Error read_err; + addr_t addr = LLDB_INVALID_ADDRESS; + if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8) + { + addr = process->ReadUnsignedIntegerFromMemory (0xffffff8000002010ULL, 8, LLDB_INVALID_ADDRESS, read_err); + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + { + return addr; + } + addr = process->ReadUnsignedIntegerFromMemory (0xffffff8000004010ULL, 8, LLDB_INVALID_ADDRESS, read_err); + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + { + return addr; + } + } + else + { + addr = process->ReadUnsignedIntegerFromMemory (0xffff0110, 4, LLDB_INVALID_ADDRESS, read_err); + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + { + return addr; + } + } + return LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// If the kernel is currently executing when lldb attaches, and we don't have +// a better way of finding the kernel's load address, try searching backwards +// from the current pc value looking for the kernel's Mach header in memory. +// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS. +//---------------------------------------------------------------------- +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForKernelNearPC (Process *process) +{ + if (GetGlobalProperties()->GetScanType() == eKASLRScanNone + || GetGlobalProperties()->GetScanType() == eKASLRScanLowgloAddresses) + { + return LLDB_INVALID_ADDRESS; + } + + ThreadSP thread = process->GetThreadList().GetSelectedThread (); + if (thread.get() == NULL) + return LLDB_INVALID_ADDRESS; + addr_t pc = thread->GetRegisterContext ()->GetPC(LLDB_INVALID_ADDRESS); + + if (pc == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + addr_t kernel_range_low; + if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8) + { + kernel_range_low = 1ULL << 63; + } + else + { + kernel_range_low = 1ULL << 31; + } + + // Outside the normal kernel address range, this is probably userland code running right now + if (pc < kernel_range_low) + return LLDB_INVALID_ADDRESS; + + // The kernel will load at at one megabyte boundary (0x100000), or at that boundary plus + // an offset of one page (0x1000) or two, depending on the device. + + // Round the current pc down to the nearest one megabyte boundary - the place where we will start searching. + addr_t addr = pc & ~0xfffff; + + int i = 0; + while (i < 32 && pc >= kernel_range_low) + { + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + return addr; + if (CheckForKernelImageAtAddress (addr + 0x1000, process).IsValid()) + return addr + 0x1000; + if (CheckForKernelImageAtAddress (addr + 0x2000, process).IsValid()) + return addr + 0x2000; + if (CheckForKernelImageAtAddress (addr + 0x4000, process).IsValid()) + return addr + 0x4000; + i++; + addr -= 0x100000; + } + + return LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// Scan through the valid address range for a kernel binary. +// This is uselessly slow in 64-bit environments so we don't even try it. +// This scan is not enabled by default even for 32-bit targets. +// Returns the address of the kernel if one was found, else LLDB_INVALID_ADDRESS. +//---------------------------------------------------------------------- +lldb::addr_t +DynamicLoaderDarwinKernel::SearchForKernelViaExhaustiveSearch (Process *process) +{ + if (GetGlobalProperties()->GetScanType() != eKASLRScanExhaustiveScan) + { + return LLDB_INVALID_ADDRESS; + } + + addr_t kernel_range_low, kernel_range_high; + if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8) + { + kernel_range_low = 1ULL << 63; + kernel_range_high = UINT64_MAX; + } + else + { + kernel_range_low = 1ULL << 31; + kernel_range_high = UINT32_MAX; + } + + // Stepping through memory at one-megabyte resolution looking for a kernel + // rarely works (fast enough) with a 64-bit address space -- for now, let's + // not even bother. We may be attaching to something which *isn't* a kernel + // and we don't want to spin for minutes on-end looking for a kernel. + if (process->GetTarget().GetArchitecture().GetAddressByteSize() == 8) + return LLDB_INVALID_ADDRESS; + + addr_t addr = kernel_range_low; + + while (addr >= kernel_range_low && addr < kernel_range_high) + { + if (CheckForKernelImageAtAddress (addr, process).IsValid()) + return addr; + if (CheckForKernelImageAtAddress (addr + 0x1000, process).IsValid()) + return addr + 0x1000; + if (CheckForKernelImageAtAddress (addr + 0x2000, process).IsValid()) + return addr + 0x2000; + if (CheckForKernelImageAtAddress (addr + 0x4000, process).IsValid()) + return addr + 0x4000; + addr += 0x100000; + } + return LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// Given an address in memory, look to see if there is a kernel image at that +// address. +// Returns a UUID; if a kernel was not found at that address, UUID.IsValid() will be false. +//---------------------------------------------------------------------- +lldb_private::UUID +DynamicLoaderDarwinKernel::CheckForKernelImageAtAddress (lldb::addr_t addr, Process *process) +{ + if (addr == LLDB_INVALID_ADDRESS) + return UUID(); + + // First try a quick test -- read the first 4 bytes and see if there is a valid Mach-O magic field there + // (the first field of the mach_header/mach_header_64 struct). + + Error read_error; + uint64_t result = process->ReadUnsignedIntegerFromMemory (addr, 4, LLDB_INVALID_ADDRESS, read_error); + if (result != llvm::MachO::MH_MAGIC_64 + && result != llvm::MachO::MH_MAGIC + && result != llvm::MachO::MH_CIGAM + && result != llvm::MachO::MH_CIGAM_64) + { + return UUID(); + } + + // Read the mach header and see whether it looks like a kernel + llvm::MachO::mach_header header; + if (process->DoReadMemory (addr, &header, sizeof(header), read_error) != sizeof(header)) + return UUID(); + + if (header.magic == llvm::MachO::MH_CIGAM || + header.magic == llvm::MachO::MH_CIGAM_64) + { + header.magic = llvm::ByteSwap_32(header.magic); + header.cputype = llvm::ByteSwap_32(header.cputype); + header.cpusubtype = llvm::ByteSwap_32(header.cpusubtype); + header.filetype = llvm::ByteSwap_32(header.filetype); + header.ncmds = llvm::ByteSwap_32(header.ncmds); + header.sizeofcmds = llvm::ByteSwap_32(header.sizeofcmds); + header.flags = llvm::ByteSwap_32(header.flags); + } + + // A kernel is an executable which does not have the dynamic link object flag set. + if (header.filetype == llvm::MachO::MH_EXECUTE + && (header.flags & llvm::MachO::MH_DYLDLINK) == 0) + { + // Create a full module to get the UUID + ModuleSP memory_module_sp = process->ReadModuleFromMemory (FileSpec ("temp_mach_kernel", false), addr); + if (!memory_module_sp.get()) + return UUID(); + + ObjectFile *exe_objfile = memory_module_sp->GetObjectFile(); + if (exe_objfile == NULL) + return UUID(); + + if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && exe_objfile->GetStrata() == ObjectFile::eStrataKernel) + { + ArchSpec kernel_arch (eArchTypeMachO, header.cputype, header.cpusubtype); + if (!process->GetTarget().GetArchitecture().IsCompatibleMatch(kernel_arch)) + { + process->GetTarget().SetArchitecture (kernel_arch); + } + return memory_module_sp->GetUUID(); + } + } + + return UUID(); +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderDarwinKernel::DynamicLoaderDarwinKernel (Process* process, lldb::addr_t kernel_addr) : + DynamicLoader(process), + m_kernel_load_address (kernel_addr), + m_kernel(), + m_kext_summary_header_ptr_addr (), + m_kext_summary_header_addr (), + m_kext_summary_header (), + m_known_kexts (), + m_mutex(Mutex::eMutexTypeRecursive), + m_break_id (LLDB_INVALID_BREAK_ID) +{ + Error error; + PlatformSP platform_sp(Platform::Create(PlatformDarwinKernel::GetPluginNameStatic(), error)); + // Only select the darwin-kernel Platform if we've been asked to load kexts. + // It can take some time to scan over all of the kext info.plists and that + // shouldn't be done if kext loading is explicitly disabled. + if (platform_sp.get() && GetGlobalProperties()->GetLoadKexts()) + { + process->GetTarget().SetPlatform (platform_sp); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoaderDarwinKernel::~DynamicLoaderDarwinKernel() +{ + Clear(true); +} + +void +DynamicLoaderDarwinKernel::UpdateIfNeeded() +{ + LoadKernelModuleIfNeeded(); + SetNotificationBreakpointIfNeeded (); +} +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderDarwinKernel::DidAttach () +{ + PrivateInitialize(m_process); + UpdateIfNeeded(); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderDarwinKernel::DidLaunch () +{ + PrivateInitialize(m_process); + UpdateIfNeeded(); +} + + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::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_kernel.Clear(); + m_known_kexts.clear(); + m_kext_summary_header_ptr_addr.Clear(); + m_kext_summary_header_addr.Clear(); + m_break_id = LLDB_INVALID_BREAK_ID; +} + + +bool +DynamicLoaderDarwinKernel::KextImageInfo::LoadImageAtFileAddress (Process *process) +{ + if (IsLoaded()) + return true; + + if (m_module_sp) + { + bool changed = false; + if (m_module_sp->SetLoadAddress (process->GetTarget(), 0, true, changed)) + m_load_process_stop_id = process->GetStopID(); + } + return false; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetModule (ModuleSP module_sp) +{ + m_module_sp = module_sp; + if (module_sp.get() && module_sp->GetObjectFile()) + { + if (module_sp->GetObjectFile()->GetType() == ObjectFile::eTypeExecutable + && module_sp->GetObjectFile()->GetStrata() == ObjectFile::eStrataKernel) + { + m_kernel_image = true; + } + else + { + m_kernel_image = false; + } + } +} + +ModuleSP +DynamicLoaderDarwinKernel::KextImageInfo::GetModule () +{ + return m_module_sp; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetLoadAddress (addr_t load_addr) +{ + m_load_address = load_addr; +} + +addr_t +DynamicLoaderDarwinKernel::KextImageInfo::GetLoadAddress () const +{ + return m_load_address; +} + +uint64_t +DynamicLoaderDarwinKernel::KextImageInfo::GetSize () const +{ + return m_size; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetSize (uint64_t size) +{ + m_size = size; +} + +uint32_t +DynamicLoaderDarwinKernel::KextImageInfo::GetProcessStopId () const +{ + return m_load_process_stop_id; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetProcessStopId (uint32_t stop_id) +{ + m_load_process_stop_id = stop_id; +} + +bool +DynamicLoaderDarwinKernel::KextImageInfo::operator== (const KextImageInfo &rhs) +{ + if (m_uuid.IsValid() || rhs.GetUUID().IsValid()) + { + if (m_uuid == rhs.GetUUID()) + { + return true; + } + return false; + } + + if (m_name == rhs.GetName() && m_load_address == rhs.GetLoadAddress()) + return true; + + return false; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetName (const char *name) +{ + m_name = name; +} + +std::string +DynamicLoaderDarwinKernel::KextImageInfo::GetName () const +{ + return m_name; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetUUID (const UUID &uuid) +{ + m_uuid = uuid; +} + +UUID +DynamicLoaderDarwinKernel::KextImageInfo::GetUUID () const +{ + return m_uuid; +} + +// Given the m_load_address from the kext summaries, and a UUID, try to create an in-memory +// Module at that address. Require that the MemoryModule have a matching UUID and detect +// if this MemoryModule is a kernel or a kext. +// +// Returns true if m_memory_module_sp is now set to a valid Module. + +bool +DynamicLoaderDarwinKernel::KextImageInfo::ReadMemoryModule (Process *process) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (m_memory_module_sp.get() != NULL) + return true; + if (m_load_address == LLDB_INVALID_ADDRESS) + return false; + + FileSpec file_spec; + file_spec.SetFile (m_name.c_str(), false); + + ModuleSP memory_module_sp = process->ReadModuleFromMemory (file_spec, m_load_address); + + if (memory_module_sp.get() == NULL) + return false; + + bool is_kernel = false; + if (memory_module_sp->GetObjectFile()) + { + if (memory_module_sp->GetObjectFile()->GetType() == ObjectFile::eTypeExecutable + && memory_module_sp->GetObjectFile()->GetStrata() == ObjectFile::eStrataKernel) + { + is_kernel = true; + } + else if (memory_module_sp->GetObjectFile()->GetType() == ObjectFile::eTypeSharedLibrary) + { + is_kernel = false; + } + } + + // If this is a kext, and the kernel specified what UUID we should find at this + // load address, require that the memory module have a matching UUID or something + // has gone wrong and we should discard it. + if (m_uuid.IsValid()) + { + if (m_uuid != memory_module_sp->GetUUID()) + { + if (log) + { + log->Printf ("KextImageInfo::ReadMemoryModule the kernel said to find uuid %s at 0x%" PRIx64 " but instead we found uuid %s, throwing it away", m_uuid.GetAsString().c_str(), m_load_address, memory_module_sp->GetUUID().GetAsString().c_str()); + } + return false; + } + } + + // If the in-memory Module has a UUID, let's use that. + if (!m_uuid.IsValid() && memory_module_sp->GetUUID().IsValid()) + { + m_uuid = memory_module_sp->GetUUID(); + } + + m_memory_module_sp = memory_module_sp; + m_kernel_image = is_kernel; + if (is_kernel) + { + if (log) + { + // This is unusual and probably not intended + log->Printf ("KextImageInfo::ReadMemoryModule read the kernel binary out of memory"); + } + if (memory_module_sp->GetArchitecture().IsValid()) + { + process->GetTarget().SetArchitecture(memory_module_sp->GetArchitecture()); + } + if (m_uuid.IsValid()) + { + ModuleSP exe_module_sp = process->GetTarget().GetExecutableModule(); + if (exe_module_sp.get() && exe_module_sp->GetUUID().IsValid()) + { + if (m_uuid != exe_module_sp->GetUUID()) + { + // The user specified a kernel binary that has a different UUID than + // the kernel actually running in memory. This never ends well; + // clear the user specified kernel binary from the Target. + + m_module_sp.reset(); + + ModuleList user_specified_kernel_list; + user_specified_kernel_list.Append (exe_module_sp); + process->GetTarget().GetImages().Remove (user_specified_kernel_list); + } + } + } + } + + return true; +} + +bool +DynamicLoaderDarwinKernel::KextImageInfo::IsKernel () const +{ + return m_kernel_image == true; +} + +void +DynamicLoaderDarwinKernel::KextImageInfo::SetIsKernel (bool is_kernel) +{ + m_kernel_image = is_kernel; +} + +bool +DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule (Process *process) +{ + if (IsLoaded()) + return true; + + + Target &target = process->GetTarget(); + + // If we don't have / can't create a memory module for this kext, don't try to load it - we won't + // have the correct segment load addresses. + if (!ReadMemoryModule (process)) + { + return false; + } + + bool uuid_is_valid = m_uuid.IsValid(); + + if (IsKernel() && uuid_is_valid && m_memory_module_sp.get()) + { + Stream *s = target.GetDebugger().GetOutputFile().get(); + if (s) + { + s->Printf ("Kernel UUID: %s\n", m_memory_module_sp->GetUUID().GetAsString().c_str()); + s->Printf ("Load Address: 0x%" PRIx64 "\n", m_load_address); + } + } + + if (!m_module_sp) + { + // See if the kext has already been loaded into the target, probably by the user doing target modules add. + const ModuleList &target_images = target.GetImages(); + m_module_sp = target_images.FindModule(m_uuid); + + // Search for the kext on the local filesystem via the UUID + if (!m_module_sp && uuid_is_valid) + { + ModuleSpec module_spec; + module_spec.GetUUID() = m_uuid; + module_spec.GetArchitecture() = target.GetArchitecture(); + + // For the kernel, we really do need an on-disk file copy of the binary to do anything useful. + // This will force a clal to + if (IsKernel()) + { + if (Symbols::DownloadObjectAndSymbolFile (module_spec, true)) + { + if (module_spec.GetFileSpec().Exists()) + { + m_module_sp.reset(new Module (module_spec.GetFileSpec(), target.GetArchitecture())); + if (m_module_sp.get() && m_module_sp->MatchesModuleSpec (module_spec)) + { + ModuleList loaded_module_list; + loaded_module_list.Append (m_module_sp); + target.ModulesDidLoad (loaded_module_list); + } + } + } + } + + // If the current platform is PlatformDarwinKernel, create a ModuleSpec with the filename set + // to be the bundle ID for this kext, e.g. "com.apple.filesystems.msdosfs", and ask the platform + // to find it. + PlatformSP platform_sp (target.GetPlatform()); + if (!m_module_sp && platform_sp) + { + ConstString platform_name (platform_sp->GetPluginName()); + static ConstString g_platform_name (PlatformDarwinKernel::GetPluginNameStatic()); + if (platform_name == g_platform_name) + { + ModuleSpec kext_bundle_module_spec(module_spec); + FileSpec kext_filespec(m_name.c_str(), false); + kext_bundle_module_spec.GetFileSpec() = kext_filespec; + platform_sp->GetSharedModule (kext_bundle_module_spec, process, m_module_sp, &target.GetExecutableSearchPaths(), NULL, NULL); + } + } + + // Ask the Target to find this file on the local system, if possible. + // This will search in the list of currently-loaded files, look in the + // standard search paths on the system, and on a Mac it will try calling + // the DebugSymbols framework with the UUID to find the binary via its + // search methods. + if (!m_module_sp) + { + m_module_sp = target.GetSharedModule (module_spec); + } + + if (IsKernel() && !m_module_sp) + { + Stream *s = target.GetDebugger().GetOutputFile().get(); + if (s) + { + s->Printf ("WARNING: Unable to locate kernel binary on the debugger system.\n"); + } + } + } + + // If we managed to find a module, append it to the target's list of images. + // If we also have a memory module, require that they have matching UUIDs + if (m_module_sp) + { + bool uuid_match_ok = true; + if (m_memory_module_sp) + { + if (m_module_sp->GetUUID() != m_memory_module_sp->GetUUID()) + { + uuid_match_ok = false; + } + } + if (uuid_match_ok) + { + target.GetImages().AppendIfNeeded(m_module_sp); + if (IsKernel() && target.GetExecutableModulePointer() != m_module_sp.get()) + { + target.SetExecutableModule (m_module_sp, false); + } + } + } + } + + if (!m_module_sp && !IsKernel() && m_uuid.IsValid() && !m_name.empty()) + { + Stream *s = target.GetDebugger().GetOutputFile().get(); + if (s) + { + s->Printf ("warning: Can't find binary/dSYM for %s (%s)\n", + m_name.c_str(), m_uuid.GetAsString().c_str()); + } + } + + static ConstString g_section_name_LINKEDIT ("__LINKEDIT"); + + if (m_memory_module_sp && m_module_sp) + { + if (m_module_sp->GetUUID() == m_memory_module_sp->GetUUID()) + { + ObjectFile *ondisk_object_file = m_module_sp->GetObjectFile(); + ObjectFile *memory_object_file = m_memory_module_sp->GetObjectFile(); + + if (memory_object_file && ondisk_object_file) + { + // The memory_module for kexts may have an invalid __LINKEDIT seg; skip it. + const bool ignore_linkedit = !IsKernel (); + + SectionList *ondisk_section_list = ondisk_object_file->GetSectionList (); + SectionList *memory_section_list = memory_object_file->GetSectionList (); + if (memory_section_list && ondisk_section_list) + { + const uint32_t num_ondisk_sections = ondisk_section_list->GetSize(); + // There may be CTF sections in the memory image so we can't + // always just compare the number of sections (which are actually + // segments in mach-o parlance) + uint32_t sect_idx = 0; + + // Use the memory_module's addresses for each section to set the + // file module's load address as appropriate. We don't want to use + // a single slide value for the entire kext - different segments may + // be slid different amounts by the kext loader. + + uint32_t num_sections_loaded = 0; + for (sect_idx=0; sect_idxGetSectionAtIndex(sect_idx)); + if (ondisk_section_sp) + { + // Don't ever load __LINKEDIT as it may or may not be actually + // mapped into memory and there is no current way to tell. + // I filed rdar://problem/12851706 to track being able to tell + // if the __LINKEDIT is actually mapped, but until then, we need + // to not load the __LINKEDIT + if (ignore_linkedit && ondisk_section_sp->GetName() == g_section_name_LINKEDIT) + continue; + + const Section *memory_section = memory_section_list->FindSectionByName(ondisk_section_sp->GetName()).get(); + if (memory_section) + { + target.SetSectionLoadAddress (ondisk_section_sp, memory_section->GetFileAddress()); + ++num_sections_loaded; + } + } + } + if (num_sections_loaded > 0) + m_load_process_stop_id = process->GetStopID(); + else + m_module_sp.reset(); // No sections were loaded + } + else + m_module_sp.reset(); // One or both section lists + } + else + m_module_sp.reset(); // One or both object files missing + } + else + m_module_sp.reset(); // UUID mismatch + } + + bool is_loaded = IsLoaded(); + + if (is_loaded && m_module_sp && IsKernel()) + { + Stream *s = target.GetDebugger().GetOutputFile().get(); + if (s) + { + ObjectFile *kernel_object_file = m_module_sp->GetObjectFile(); + if (kernel_object_file) + { + addr_t file_address = kernel_object_file->GetHeaderAddress().GetFileAddress(); + if (m_load_address != LLDB_INVALID_ADDRESS && file_address != LLDB_INVALID_ADDRESS) + { + s->Printf ("Kernel slid 0x%" PRIx64 " in memory.\n", m_load_address - file_address); + } + } + { + s->Printf ("Loaded kernel file %s\n", + m_module_sp->GetFileSpec().GetPath().c_str()); + } + s->Flush (); + } + } + return is_loaded; +} + +uint32_t +DynamicLoaderDarwinKernel::KextImageInfo::GetAddressByteSize () +{ + if (m_memory_module_sp) + return m_memory_module_sp->GetArchitecture().GetAddressByteSize(); + if (m_module_sp) + return m_module_sp->GetArchitecture().GetAddressByteSize(); + return 0; +} + +lldb::ByteOrder +DynamicLoaderDarwinKernel::KextImageInfo::GetByteOrder() +{ + if (m_memory_module_sp) + return m_memory_module_sp->GetArchitecture().GetByteOrder(); + if (m_module_sp) + return m_module_sp->GetArchitecture().GetByteOrder(); + return endian::InlHostByteOrder(); +} + +lldb_private::ArchSpec +DynamicLoaderDarwinKernel::KextImageInfo::GetArchitecture () const +{ + if (m_memory_module_sp) + return m_memory_module_sp->GetArchitecture(); + if (m_module_sp) + return m_module_sp->GetArchitecture(); + return lldb_private::ArchSpec (); +} + + +//---------------------------------------------------------------------- +// Load the kernel module and initialize the "m_kernel" member. Return +// true _only_ if the kernel is loaded the first time through (subsequent +// calls to this function should return false after the kernel has been +// already loaded). +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::LoadKernelModuleIfNeeded() +{ + if (!m_kext_summary_header_ptr_addr.IsValid()) + { + m_kernel.Clear(); + m_kernel.SetModule (m_process->GetTarget().GetExecutableModule()); + m_kernel.SetIsKernel(true); + + ConstString kernel_name("mach_kernel"); + if (m_kernel.GetModule().get() + && m_kernel.GetModule()->GetObjectFile() + && !m_kernel.GetModule()->GetObjectFile()->GetFileSpec().GetFilename().IsEmpty()) + { + kernel_name = m_kernel.GetModule()->GetObjectFile()->GetFileSpec().GetFilename(); + } + m_kernel.SetName (kernel_name.AsCString()); + + if (m_kernel.GetLoadAddress() == LLDB_INVALID_ADDRESS) + { + m_kernel.SetLoadAddress(m_kernel_load_address); + if (m_kernel.GetLoadAddress() == LLDB_INVALID_ADDRESS && m_kernel.GetModule()) + { + // We didn't get a hint from the process, so we will + // try the kernel at the address that it exists at in + // the file if we have one + ObjectFile *kernel_object_file = m_kernel.GetModule()->GetObjectFile(); + if (kernel_object_file) + { + addr_t load_address = kernel_object_file->GetHeaderAddress().GetLoadAddress(&m_process->GetTarget()); + addr_t file_address = kernel_object_file->GetHeaderAddress().GetFileAddress(); + if (load_address != LLDB_INVALID_ADDRESS && load_address != 0) + { + m_kernel.SetLoadAddress (load_address); + if (load_address != file_address) + { + // Don't accidentally relocate the kernel to the File address -- + // the Load address has already been set to its actual in-memory address. + // Mark it as IsLoaded. + m_kernel.SetProcessStopId (m_process->GetStopID()); + } + } + else + { + m_kernel.SetLoadAddress(file_address); + } + } + } + } + + if (m_kernel.GetLoadAddress() != LLDB_INVALID_ADDRESS) + { + if (!m_kernel.LoadImageUsingMemoryModule (m_process)) + { + m_kernel.LoadImageAtFileAddress (m_process); + } + } + + if (m_kernel.IsLoaded() && m_kernel.GetModule()) + { + static ConstString kext_summary_symbol ("gLoadedKextSummaries"); + const Symbol *symbol = m_kernel.GetModule()->FindFirstSymbolWithNameAndType (kext_summary_symbol, eSymbolTypeData); + if (symbol) + { + m_kext_summary_header_ptr_addr = symbol->GetAddress(); + // Update all image infos + ReadAllKextSummaries (); + } + } + else + { + m_kernel.Clear(); + } + } +} + +//---------------------------------------------------------------------- +// 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 +DynamicLoaderDarwinKernel::BreakpointHitCallback (void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + return static_cast(baton)->BreakpointHit (context, break_id, break_loc_id); +} + +bool +DynamicLoaderDarwinKernel::BreakpointHit (StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + Log *log(GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf ("DynamicLoaderDarwinKernel::BreakpointHit (...)\n"); + + ReadAllKextSummaries (); + + if (log) + PutToLog(log); + + return GetStopWhenImagesChange(); +} + + +bool +DynamicLoaderDarwinKernel::ReadKextSummaryHeader () +{ + Mutex::Locker locker(m_mutex); + + // the all image infos is already valid for this process stop ID + + if (m_kext_summary_header_ptr_addr.IsValid()) + { + const uint32_t addr_size = m_kernel.GetAddressByteSize (); + const ByteOrder byte_order = m_kernel.GetByteOrder(); + Error error; + // Read enough bytes for a "OSKextLoadedKextSummaryHeader" structure + // which is currently 4 uint32_t and a pointer. + uint8_t buf[24]; + DataExtractor data (buf, sizeof(buf), byte_order, addr_size); + const size_t count = 4 * sizeof(uint32_t) + addr_size; + const bool prefer_file_cache = false; + if (m_process->GetTarget().ReadPointerFromMemory (m_kext_summary_header_ptr_addr, + prefer_file_cache, + error, + m_kext_summary_header_addr)) + { + // We got a valid address for our kext summary header and make sure it isn't NULL + if (m_kext_summary_header_addr.IsValid() && + m_kext_summary_header_addr.GetFileAddress() != 0) + { + const size_t bytes_read = m_process->GetTarget().ReadMemory (m_kext_summary_header_addr, prefer_file_cache, buf, count, error); + if (bytes_read == count) + { + lldb::offset_t offset = 0; + m_kext_summary_header.version = data.GetU32(&offset); + if (m_kext_summary_header.version > 128) + { + Stream *s = m_process->GetTarget().GetDebugger().GetOutputFile().get(); + s->Printf ("WARNING: Unable to read kext summary header, got improbable version number %u\n", m_kext_summary_header.version); + // If we get an improbably large version number, we're probably getting bad memory. + m_kext_summary_header_addr.Clear(); + return false; + } + if (m_kext_summary_header.version >= 2) + { + m_kext_summary_header.entry_size = data.GetU32(&offset); + if (m_kext_summary_header.entry_size > 4096) + { + // If we get an improbably large entry_size, we're probably getting bad memory. + Stream *s = m_process->GetTarget().GetDebugger().GetOutputFile().get(); + s->Printf ("WARNING: Unable to read kext summary header, got improbable entry_size %u\n", m_kext_summary_header.entry_size); + m_kext_summary_header_addr.Clear(); + return false; + } + } + else + { + // Versions less than 2 didn't have an entry size, it was hard coded + m_kext_summary_header.entry_size = KERNEL_MODULE_ENTRY_SIZE_VERSION_1; + } + m_kext_summary_header.entry_count = data.GetU32(&offset); + if (m_kext_summary_header.entry_count > 10000) + { + // If we get an improbably large number of kexts, we're probably getting bad memory. + Stream *s = m_process->GetTarget().GetDebugger().GetOutputFile().get(); + s->Printf ("WARNING: Unable to read kext summary header, got improbable number of kexts %u\n", m_kext_summary_header.entry_count); + m_kext_summary_header_addr.Clear(); + return false; + } + return true; + } + } + } + } + m_kext_summary_header_addr.Clear(); + return false; +} + +// We've either (a) just attached to a new kernel, or (b) the kexts-changed breakpoint was hit +// and we need to figure out what kexts have been added or removed. +// Read the kext summaries from the inferior kernel memory, compare them against the +// m_known_kexts vector and update the m_known_kexts vector as needed to keep in sync with the +// inferior. + +bool +DynamicLoaderDarwinKernel::ParseKextSummaries (const Address &kext_summary_addr, uint32_t count) +{ + KextImageInfo::collection kext_summaries; + Log *log(GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf ("Kexts-changed breakpoint hit, there are %d kexts currently.\n", count); + + Mutex::Locker locker(m_mutex); + + if (!ReadKextSummaries (kext_summary_addr, count, kext_summaries)) + return false; + + // read the plugin.dynamic-loader.darwin-kernel.load-kexts setting -- if the user requested no + // kext loading, don't print any messages about kexts & don't try to read them. + const bool load_kexts = GetGlobalProperties()->GetLoadKexts(); + + // By default, all kexts we've loaded in the past are marked as "remove" and all of the kexts + // we just found out about from ReadKextSummaries are marked as "add". + std::vector to_be_removed(m_known_kexts.size(), true); + std::vector to_be_added(count, true); + + int number_of_new_kexts_being_added = 0; + int number_of_old_kexts_being_removed = m_known_kexts.size(); + + const uint32_t new_kexts_size = kext_summaries.size(); + const uint32_t old_kexts_size = m_known_kexts.size(); + + // The m_known_kexts vector may have entries that have been Cleared, + // or are a kernel. + for (uint32_t old_kext = 0; old_kext < old_kexts_size; old_kext++) + { + bool ignore = false; + KextImageInfo &image_info = m_known_kexts[old_kext]; + if (image_info.IsKernel()) + { + ignore = true; + } + else if (image_info.GetLoadAddress() == LLDB_INVALID_ADDRESS && !image_info.GetModule()) + { + ignore = true; + } + + if (ignore) + { + number_of_old_kexts_being_removed--; + to_be_removed[old_kext] = false; + } + } + + // Scan over the list of kexts we just read from the kernel, note those that + // need to be added and those already loaded. + for (uint32_t new_kext = 0; new_kext < new_kexts_size; new_kext++) + { + bool add_this_one = true; + for (uint32_t old_kext = 0; old_kext < old_kexts_size; old_kext++) + { + if (m_known_kexts[old_kext] == kext_summaries[new_kext]) + { + // We already have this kext, don't re-load it. + to_be_added[new_kext] = false; + // This kext is still present, do not remove it. + to_be_removed[old_kext] = false; + + number_of_old_kexts_being_removed--; + add_this_one = false; + break; + } + } + if (add_this_one) + { + number_of_new_kexts_being_added++; + } + } + + if (number_of_new_kexts_being_added == 0 && number_of_old_kexts_being_removed == 0) + return true; + + Stream *s = m_process->GetTarget().GetDebugger().GetOutputFile().get(); + if (s && load_kexts) + { + if (number_of_new_kexts_being_added > 0 && number_of_old_kexts_being_removed > 0) + { + s->Printf ("Loading %d kext modules and unloading %d kext modules ", number_of_new_kexts_being_added, number_of_old_kexts_being_removed); + } + else if (number_of_new_kexts_being_added > 0) + { + s->Printf ("Loading %d kext modules ", number_of_new_kexts_being_added); + } + else if (number_of_old_kexts_being_removed > 0) + { + s->Printf ("Unloading %d kext modules ", number_of_old_kexts_being_removed); + } + } + + if (log) + { + if (load_kexts) + { + log->Printf ("DynamicLoaderDarwinKernel::ParseKextSummaries: %d kexts added, %d kexts removed", number_of_new_kexts_being_added, number_of_old_kexts_being_removed); + } + else + { + log->Printf ("DynamicLoaderDarwinKernel::ParseKextSummaries kext loading is disabled, else would have %d kexts added, %d kexts removed", number_of_new_kexts_being_added, number_of_old_kexts_being_removed); + } + } + + + if (number_of_new_kexts_being_added > 0) + { + ModuleList loaded_module_list; + + const uint32_t num_of_new_kexts = kext_summaries.size(); + for (uint32_t new_kext = 0; new_kext < num_of_new_kexts; new_kext++) + { + if (to_be_added[new_kext] == true) + { + KextImageInfo &image_info = kext_summaries[new_kext]; + if (load_kexts) + { + if (!image_info.LoadImageUsingMemoryModule (m_process)) + { + image_info.LoadImageAtFileAddress (m_process); + } + } + + m_known_kexts.push_back(image_info); + + if (image_info.GetModule() && m_process->GetStopID() == image_info.GetProcessStopId()) + loaded_module_list.AppendIfNeeded (image_info.GetModule()); + + if (s && load_kexts) + s->Printf ("."); + + if (log) + kext_summaries[new_kext].PutToLog (log); + } + } + m_process->GetTarget().ModulesDidLoad (loaded_module_list); + } + + if (number_of_old_kexts_being_removed > 0) + { + ModuleList loaded_module_list; + const uint32_t num_of_old_kexts = m_known_kexts.size(); + for (uint32_t old_kext = 0; old_kext < num_of_old_kexts; old_kext++) + { + ModuleList unloaded_module_list; + if (to_be_removed[old_kext]) + { + KextImageInfo &image_info = m_known_kexts[old_kext]; + // You can't unload the kernel. + if (!image_info.IsKernel()) + { + if (image_info.GetModule()) + { + unloaded_module_list.AppendIfNeeded (image_info.GetModule()); + } + if (s) + s->Printf ("."); + image_info.Clear(); + // should pull it out of the KextImageInfos vector but that would mutate the list and invalidate + // the to_be_removed bool vector; leaving it in place once Cleared() is relatively harmless. + } + } + m_process->GetTarget().ModulesDidUnload (unloaded_module_list, false); + } + } + + if (s && load_kexts) + { + s->Printf (" done.\n"); + s->Flush (); + } + + return true; +} + +uint32_t +DynamicLoaderDarwinKernel::ReadKextSummaries (const Address &kext_summary_addr, + uint32_t image_infos_count, + KextImageInfo::collection &image_infos) +{ + const ByteOrder endian = m_kernel.GetByteOrder(); + const uint32_t addr_size = m_kernel.GetAddressByteSize(); + + image_infos.resize(image_infos_count); + const size_t count = image_infos.size() * m_kext_summary_header.entry_size; + DataBufferHeap data(count, 0); + Error error; + + const bool prefer_file_cache = false; + const size_t bytes_read = m_process->GetTarget().ReadMemory (kext_summary_addr, + prefer_file_cache, + data.GetBytes(), + data.GetByteSize(), + error); + if (bytes_read == count) + { + + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), endian, addr_size); + uint32_t i=0; + for (uint32_t kext_summary_offset = 0; + i < image_infos.size() && extractor.ValidOffsetForDataOfSize(kext_summary_offset, m_kext_summary_header.entry_size); + ++i, kext_summary_offset += m_kext_summary_header.entry_size) + { + lldb::offset_t offset = kext_summary_offset; + const void *name_data = extractor.GetData(&offset, KERNEL_MODULE_MAX_NAME); + if (name_data == NULL) + break; + image_infos[i].SetName ((const char *) name_data); + UUID uuid (extractor.GetData (&offset, 16), 16); + image_infos[i].SetUUID (uuid); + image_infos[i].SetLoadAddress (extractor.GetU64(&offset)); + image_infos[i].SetSize (extractor.GetU64(&offset)); + } + if (i < image_infos.size()) + image_infos.resize(i); + } + else + { + image_infos.clear(); + } + return image_infos.size(); +} + +bool +DynamicLoaderDarwinKernel::ReadAllKextSummaries () +{ + Mutex::Locker locker(m_mutex); + + if (ReadKextSummaryHeader ()) + { + if (m_kext_summary_header.entry_count > 0 && m_kext_summary_header_addr.IsValid()) + { + Address summary_addr (m_kext_summary_header_addr); + summary_addr.Slide(m_kext_summary_header.GetSize()); + if (!ParseKextSummaries (summary_addr, m_kext_summary_header.entry_count)) + { + m_known_kexts.clear(); + } + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// Dump an image info structure to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::KextImageInfo::PutToLog (Log *log) const +{ + if (log == NULL) + return; + const uint8_t *u = (uint8_t *) m_uuid.GetBytes(); + + if (m_load_address == LLDB_INVALID_ADDRESS) + { + if (u) + { + log->Printf("\tuuid=%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 name=\"%s\" (UNLOADED)", + 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], + m_name.c_str()); + } + else + log->Printf("\tname=\"%s\" (UNLOADED)", m_name.c_str()); + } + else + { + if (u) + { + log->Printf("\taddr=0x%16.16" PRIx64 " size=0x%16.16" PRIx64 " 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 name=\"%s\"", + m_load_address, m_size, + 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], + m_name.c_str()); + } + else + { + log->Printf("\t[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") name=\"%s\"", + m_load_address, m_load_address+m_size, m_name.c_str()); + } + } +} + +//---------------------------------------------------------------------- +// Dump the _dyld_all_image_infos members and all current image infos +// that we have parsed to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::PutToLog(Log *log) const +{ + if (log == NULL) + return; + + Mutex::Locker locker(m_mutex); + log->Printf("gLoadedKextSummaries = 0x%16.16" PRIx64 " { version=%u, entry_size=%u, entry_count=%u }", + m_kext_summary_header_addr.GetFileAddress(), + m_kext_summary_header.version, + m_kext_summary_header.entry_size, + m_kext_summary_header.entry_count); + + size_t i; + const size_t count = m_known_kexts.size(); + if (count > 0) + { + log->PutCString("Loaded:"); + for (i = 0; iGetState())); + Clear(true); + m_process = process; +} + +void +DynamicLoaderDarwinKernel::SetNotificationBreakpointIfNeeded () +{ + if (m_break_id == LLDB_INVALID_BREAK_ID && m_kernel.GetModule()) + { + DEBUG_PRINTF("DynamicLoaderDarwinKernel::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + + + const bool internal_bp = true; + const bool hardware = false; + const LazyBool skip_prologue = eLazyBoolNo; + FileSpecList module_spec_list; + module_spec_list.Append (m_kernel.GetModule()->GetFileSpec()); + Breakpoint *bp = m_process->GetTarget().CreateBreakpoint (&module_spec_list, + NULL, + "OSKextLoadedKextSummariesUpdated", + eFunctionNameTypeFull, + eLanguageTypeUnknown, + skip_prologue, + internal_bp, + hardware).get(); + + bp->SetCallback (DynamicLoaderDarwinKernel::BreakpointHitCallback, this, true); + m_break_id = bp->GetID(); + } +} + +//---------------------------------------------------------------------- +// Member function that gets called when the process state changes. +//---------------------------------------------------------------------- +void +DynamicLoaderDarwinKernel::PrivateProcessStateChanged (Process *process, StateType state) +{ + DEBUG_PRINTF("DynamicLoaderDarwinKernel::%s(%s)\n", __FUNCTION__, StateAsCString(state)); + switch (state) + { + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + Clear(false); + break; + + case eStateStopped: + UpdateIfNeeded(); + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + } +} + +ThreadPlanSP +DynamicLoaderDarwinKernel::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP thread_plan_sp; + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->Printf ("Could not find symbol for step through."); + return thread_plan_sp; +} + +Error +DynamicLoaderDarwinKernel::CanLoadImage () +{ + Error error; + error.SetErrorString("always unsafe to load or unload shared libraries in the darwin kernel"); + return error; +} + +void +DynamicLoaderDarwinKernel::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + DebuggerInitialize); +} + +void +DynamicLoaderDarwinKernel::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +void +DynamicLoaderDarwinKernel::DebuggerInitialize (lldb_private::Debugger &debugger) +{ + if (!PluginManager::GetSettingForDynamicLoaderPlugin (debugger, DynamicLoaderDarwinKernelProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForDynamicLoaderPlugin (debugger, + GetGlobalProperties()->GetValueProperties(), + ConstString ("Properties for the DynamicLoaderDarwinKernel plug-in."), + is_global_setting); + } +} + +lldb_private::ConstString +DynamicLoaderDarwinKernel::GetPluginNameStatic() +{ + static ConstString g_name("darwin-kernel"); + return g_name; +} + +const char * +DynamicLoaderDarwinKernel::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library loads/unloads in the MacOSX kernel."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +DynamicLoaderDarwinKernel::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderDarwinKernel::GetPluginVersion() +{ + return 1; +} + +lldb::ByteOrder +DynamicLoaderDarwinKernel::GetByteOrderFromMagic (uint32_t magic) +{ + switch (magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_MAGIC_64: + return endian::InlHostByteOrder(); + + case llvm::MachO::MH_CIGAM: + case llvm::MachO::MH_CIGAM_64: + if (endian::InlHostByteOrder() == lldb::eByteOrderBig) + return lldb::eByteOrderLittle; + else + return lldb::eByteOrderBig; + + default: + break; + } + return lldb::eByteOrderInvalid; +} + diff --git a/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h b/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h new file mode 100644 index 000000000000..7ebda48cec93 --- /dev/null +++ b/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h @@ -0,0 +1,371 @@ +//===-- DynamicLoaderDarwinKernel.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderDarwinKernel_h_ +#define liblldb_DynamicLoaderDarwinKernel_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" + +class DynamicLoaderDarwinKernel : public lldb_private::DynamicLoader +{ +public: + DynamicLoaderDarwinKernel(lldb_private::Process *process, lldb::addr_t kernel_addr); + + ~DynamicLoaderDarwinKernel() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process, bool force); + + static void + DebuggerInitialize (lldb_private::Debugger &debugger); + + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + void + DidAttach() override; + + void + DidLaunch() override; + + lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others) override; + + lldb_private::Error + CanLoadImage() override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + void + PrivateInitialize (lldb_private::Process *process); + + void + PrivateProcessStateChanged (lldb_private::Process *process, + lldb::StateType state); + + void + UpdateIfNeeded(); + + void + LoadKernelModuleIfNeeded (); + + void + Clear (bool clear_process); + + void + PutToLog (lldb_private::Log *log) const; + + static bool + BreakpointHitCallback (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + bool + BreakpointHit (lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + uint32_t + GetAddrByteSize() + { + return m_kernel.GetAddressByteSize(); + } + + static lldb::ByteOrder + GetByteOrderFromMagic (uint32_t magic); + + enum + { + KERNEL_MODULE_MAX_NAME = 64u, + // Versions less than 2 didn't have an entry size, + // they had a 64 bit name, 16 byte UUID, 8 byte addr, + // 8 byte size, 8 byte version, 4 byte load tag, and + // 4 byte flags + KERNEL_MODULE_ENTRY_SIZE_VERSION_1 = 64u + 16u + 8u + 8u + 8u + 4u + 4u + }; + + // class KextImageInfo represents a single kext or kernel binary image. + // The class was designed to hold the information from the OSKextLoadedKextSummary + // structure (in libkern/libkern/OSKextLibPrivate.h from xnu). The kernel maintains + // a list of loded kexts in memory (the OSKextLoadedKextSummaryHeader structure, + // which points to an array of OSKextLoadedKextSummary's). + // + // A KextImageInfos may have - + // + // 1. The load address, name, UUID, and size of a kext/kernel binary in memory + // (read straight out of the kernel's list-of-kexts loaded) + // 2. A ModuleSP based on a MemoryModule read out of the kernel's memory + // (very unlikely to have any symbolic information) + // 3. A ModuleSP for an on-disk copy of the kext binary, possibly with debug info + // or a dSYM + // + // For performance reasons, the developer may prefer that lldb not load the kexts out + // of memory at the start of a kernel session. But we should build up / maintain a + // list of kexts that the kernel has told us about so we can relocate a kext module + // later if the user explicitly adds it to the target. + + class KextImageInfo + { + public: + KextImageInfo () : + m_name (), + m_module_sp (), + m_memory_module_sp (), + m_load_process_stop_id (UINT32_MAX), + m_uuid (), + m_load_address (LLDB_INVALID_ADDRESS), + m_size (0), + m_kernel_image (false) + { } + + void + Clear () + { + m_load_address = LLDB_INVALID_ADDRESS; + m_size = 0; + m_name.clear (); + m_uuid.Clear(); + m_module_sp.reset(); + m_memory_module_sp.reset(); + m_load_process_stop_id = UINT32_MAX; + } + + bool + LoadImageAtFileAddress (lldb_private::Process *process); + + bool + LoadImageUsingMemoryModule (lldb_private::Process *process); + + bool + IsLoaded () + { + return m_load_process_stop_id != UINT32_MAX; + } + + void + SetLoadAddress (lldb::addr_t load_addr); // Address of the Mach-O header for this binary + + lldb::addr_t + GetLoadAddress () const; // Address of the Mach-O header for this binary + + lldb_private::UUID + GetUUID () const; + + void + SetUUID (const lldb_private::UUID &uuid); + + void + SetName (const char *); + + std::string + GetName () const; + + void + SetModule (lldb::ModuleSP module); + + lldb::ModuleSP + GetModule (); + + // try to fill in m_memory_module_sp from memory based on the m_load_address + bool + ReadMemoryModule (lldb_private::Process *process); + + bool + IsKernel () const; // true if this is the mach_kernel; false if this is a kext + + void + SetIsKernel (bool is_kernel); + + uint64_t + GetSize () const; + + void + SetSize (uint64_t size); + + uint32_t + GetProcessStopId () const; // the stop-id when this binary was first noticed + + void + SetProcessStopId (uint32_t stop_id); + + bool + operator== (const KextImageInfo &rhs); + + uint32_t + GetAddressByteSize (); // as determined by Mach-O header + + lldb::ByteOrder + GetByteOrder(); // as determined by Mach-O header + + lldb_private::ArchSpec + GetArchitecture () const; // as determined by Mach-O header + + void + PutToLog (lldb_private::Log *log) const; + + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + private: + std::string m_name; + lldb::ModuleSP m_module_sp; + lldb::ModuleSP m_memory_module_sp; + uint32_t m_load_process_stop_id; // the stop-id when this module was added to the Target + lldb_private::UUID m_uuid; // UUID for this dylib if it has one, else all zeros + lldb::addr_t m_load_address; + uint64_t m_size; + bool m_kernel_image; // true if this is the kernel, false if this is a kext + }; + + struct OSKextLoadedKextSummaryHeader + { + uint32_t version; + uint32_t entry_size; + uint32_t entry_count; + lldb::addr_t image_infos_addr; + + OSKextLoadedKextSummaryHeader() : + version (0), + entry_size (0), + entry_count (0), + image_infos_addr (LLDB_INVALID_ADDRESS) + { + } + + uint32_t + GetSize() + { + switch (version) + { + case 0: return 0; // Can't know the size without a valid version + case 1: return 8; // Version 1 only had a version + entry_count + default: break; + } + // Version 2 and above has version, entry_size, entry_count, and reserved + return 16; + } + + void + Clear() + { + version = 0; + entry_size = 0; + entry_count = 0; + image_infos_addr = LLDB_INVALID_ADDRESS; + } + + bool + IsValid() const + { + return version >= 1 || version <= 2; + } + }; + + void + RegisterNotificationCallbacks(); + + void + UnregisterNotificationCallbacks(); + + void + SetNotificationBreakpointIfNeeded (); + + bool + ReadAllKextSummaries (); + + bool + ReadKextSummaryHeader (); + + bool + ParseKextSummaries (const lldb_private::Address &kext_summary_addr, + uint32_t count); + + void + UpdateImageInfosHeaderAndLoadCommands(KextImageInfo::collection &image_infos, + uint32_t infos_count, + bool update_executable); + + uint32_t + ReadKextSummaries (const lldb_private::Address &kext_summary_addr, + uint32_t image_infos_count, + KextImageInfo::collection &image_infos); + + static lldb::addr_t + SearchForDarwinKernel (lldb_private::Process *process); + + static lldb::addr_t + SearchForKernelAtSameLoadAddr (lldb_private::Process *process); + + static lldb::addr_t + SearchForKernelWithDebugHints (lldb_private::Process *process); + + static lldb::addr_t + SearchForKernelNearPC (lldb_private::Process *process); + + static lldb::addr_t + SearchForKernelViaExhaustiveSearch (lldb_private::Process *process); + + static lldb_private::UUID + CheckForKernelImageAtAddress (lldb::addr_t addr, lldb_private::Process *process); + + lldb::addr_t m_kernel_load_address; + KextImageInfo m_kernel; // Info about the current kernel image being used + + lldb_private::Address m_kext_summary_header_ptr_addr; + lldb_private::Address m_kext_summary_header_addr; + OSKextLoadedKextSummaryHeader m_kext_summary_header; + KextImageInfo::collection m_known_kexts; + mutable lldb_private::Mutex m_mutex; + lldb::user_id_t m_break_id; + +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderDarwinKernel); +}; + +#endif // liblldb_DynamicLoaderDarwinKernel_h_ diff --git a/source/Plugins/DynamicLoader/Darwin-Kernel/Makefile b/source/Plugins/DynamicLoader/Darwin-Kernel/Makefile new file mode 100644 index 000000000000..d2342fd06772 --- /dev/null +++ b/source/Plugins/DynamicLoader/Darwin-Kernel/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Disassembler/llvm/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDynamicLoaderDarwinKernel +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/Hexagon-DYLD/CMakeLists.txt b/source/Plugins/DynamicLoader/Hexagon-DYLD/CMakeLists.txt new file mode 100644 index 000000000000..af15f284118f --- /dev/null +++ b/source/Plugins/DynamicLoader/Hexagon-DYLD/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginDynamicLoaderHexagonDYLD + HexagonDYLDRendezvous.cpp + DynamicLoaderHexagonDYLD.cpp + ) diff --git a/source/Plugins/DynamicLoader/Hexagon-DYLD/Makefile b/source/Plugins/DynamicLoader/Hexagon-DYLD/Makefile new file mode 100644 index 000000000000..43334562ebb1 --- /dev/null +++ b/source/Plugins/DynamicLoader/Hexagon-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/Hexagon-DYLD/Makefile ----*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDynamicLoaderHexagon +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/CMakeLists.txt b/source/Plugins/DynamicLoader/MacOSX-DYLD/CMakeLists.txt new file mode 100644 index 000000000000..2c44877662f5 --- /dev/null +++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDynamicLoaderMacOSXDYLD + DynamicLoaderMacOSXDYLD.cpp + ) diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp new file mode 100644 index 000000000000..fba11f6aea85 --- /dev/null +++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp @@ -0,0 +1,2075 @@ +//===-- 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/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/ABI.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" + +//#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 + +#ifndef __APPLE__ +#include "Utility/UuidCompatibility.h" +#else +#include +#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. + + +DynamicLoaderMacOSXDYLD::DYLDImageInfo * +DynamicLoaderMacOSXDYLD::GetImageInfo (Module *module) +{ + const UUID &module_uuid = module->GetUUID(); + DYLDImageInfo::collection::iterator pos, end = m_dyld_image_infos.end(); + + // First try just by UUID as it is the safest. + if (module_uuid.IsValid()) + { + for (pos = m_dyld_image_infos.begin(); pos != end; ++pos) + { + if (pos->uuid == module_uuid) + return &(*pos); + } + + if (m_dyld.uuid == module_uuid) + return &m_dyld; + } + + // Next try by platform path only for things that don't have a valid UUID + // since if a file has a valid UUID in real life it should also in the + // dyld info. This is the next safest because the paths in the dyld info + // are platform paths, not local paths. For local debugging platform == local + // paths. + const FileSpec &platform_file_spec = module->GetPlatformFileSpec(); + for (pos = m_dyld_image_infos.begin(); pos != end; ++pos) + { + if (pos->file_spec == platform_file_spec && pos->uuid.IsValid() == false) + return &(*pos); + } + + if (m_dyld.file_spec == platform_file_spec && m_dyld.uuid.IsValid() == false) + return &m_dyld; + + return NULL; +} + +//---------------------------------------------------------------------- +// 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, bool force) +{ + bool create = force; + if (!create) + { + create = true; + Module* exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) + { + create = (object_file->GetStrata() == ObjectFile::eStrataUser); + } + } + + if (create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + create = triple_ref.getVendor() == llvm::Triple::Apple; + break; + default: + create = false; + break; + } + } + } + + if (create) + return new DynamicLoaderMacOSXDYLD (process); + return NULL; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::DynamicLoaderMacOSXDYLD (Process* process) : + DynamicLoader(process), + m_dyld(), + m_dyld_module_wp(), + m_dyld_all_image_infos_addr(LLDB_INVALID_ADDRESS), + m_dyld_all_image_infos(), + m_dyld_all_image_infos_stop_id (UINT32_MAX), + m_break_id(LLDB_INVALID_BREAK_ID), + m_dyld_image_infos(), + m_dyld_image_infos_stop_id (UINT32_MAX), + m_mutex(Mutex::eMutexTypeRecursive), + m_process_image_addr_is_all_images_infos (false) +{ +} + +//---------------------------------------------------------------------- +// 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); + LocateDYLD (); + SetNotificationBreakpoint (); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidLaunch () +{ + PrivateInitialize(m_process); + LocateDYLD (); + SetNotificationBreakpoint (); +} + +bool +DynamicLoaderMacOSXDYLD::ProcessDidExec () +{ + if (m_process) + { + // If we are stopped after an exec, we will have only one thread... + if (m_process->GetThreadList().GetSize() == 1) + { + // We know if a process has exec'ed if our "m_dyld_all_image_infos_addr" + // value differs from the Process' image info address. When a process + // execs itself it might cause a change if ASLR is enabled. + const addr_t shlib_addr = m_process->GetImageInfoAddress (); + if (m_process_image_addr_is_all_images_infos == true && shlib_addr != m_dyld_all_image_infos_addr) + { + // The image info address from the process is the 'dyld_all_image_infos' + // address and it has changed. + return true; + } + + if (m_process_image_addr_is_all_images_infos == false && shlib_addr == m_dyld.address) + { + // The image info address from the process is the mach_header + // address for dyld and it has changed. + return true; + } + + // ASLR might be disabled and dyld could have ended up in the same + // location. We should try and detect if we are stopped at '_dyld_start' + ThreadSP thread_sp (m_process->GetThreadList().GetThreadAtIndex(0)); + if (thread_sp) + { + lldb::StackFrameSP frame_sp (thread_sp->GetStackFrameAtIndex(0)); + if (frame_sp) + { + const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol; + if (symbol) + { + if (symbol->GetName() == ConstString("_dyld_start")) + return true; + } + } + } + } + } + return false; +} + + + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Clear (bool clear_process) +{ + Mutex::Locker locker(m_mutex); + + if (LLDB_BREAK_ID_IS_VALID(m_break_id)) + m_process->GetTarget().RemoveBreakpointByID (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 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) + { + // Check the image info addr as it might point to the + // mach header for dyld, or it might point to the + // dyld_all_image_infos struct + const addr_t shlib_addr = m_process->GetImageInfoAddress (); + if (shlib_addr != LLDB_INVALID_ADDRESS) + { + ByteOrder byte_order = m_process->GetTarget().GetArchitecture().GetByteOrder(); + uint8_t buf[4]; + DataExtractor data (buf, sizeof(buf), byte_order, 4); + Error error; + if (m_process->ReadMemory (shlib_addr, buf, 4, error) == 4) + { + lldb::offset_t offset = 0; + uint32_t magic = data.GetU32 (&offset); + switch (magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_MAGIC_64: + case llvm::MachO::MH_CIGAM: + case llvm::MachO::MH_CIGAM_64: + m_process_image_addr_is_all_images_infos = false; + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(shlib_addr); + + default: + break; + } + } + // Maybe it points to the all image infos? + m_dyld_all_image_infos_addr = shlib_addr; + m_process_image_addr_is_all_images_infos = true; + } + } + + 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().GetExecutableModulePointer(); + + if (executable) + { + const ArchSpec &exe_arch = executable->GetArchitecture(); + if (exe_arch.GetAddressByteSize() == 8) + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x7fff5fc00000ull); + } + else if (exe_arch.GetMachine() == llvm::Triple::arm || exe_arch.GetMachine() == llvm::Triple::thumb || exe_arch.GetMachine() == llvm::Triple::aarch64) + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x2fe00000); + } + else + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x8fe00000); + } + } + return false; +} + +ModuleSP +DynamicLoaderMacOSXDYLD::FindTargetModuleForDYLDImageInfo (DYLDImageInfo &image_info, bool can_create, bool *did_create_ptr) +{ + if (did_create_ptr) + *did_create_ptr = false; + + Target &target = m_process->GetTarget(); + const ModuleList &target_images = target.GetImages(); + ModuleSpec module_spec (image_info.file_spec); + module_spec.GetUUID() = image_info.uuid; + ModuleSP module_sp (target_images.FindFirstModule (module_spec)); + + if (module_sp && !module_spec.GetUUID().IsValid() && !module_sp->GetUUID().IsValid()) + { + // No UUID, we must rely upon the cached module modification + // time and the modification time of the file on disk + if (module_sp->GetModificationTime() != module_sp->GetFileSpec().GetModificationTime()) + module_sp.reset(); + } + + if (!module_sp) + { + if (can_create) + { + module_sp = target.GetSharedModule (module_spec); + if (!module_sp || module_sp->GetObjectFile() == NULL) + module_sp = m_process->ReadModuleFromMemory (image_info.file_spec, image_info.address); + + if (did_create_ptr) + *did_create_ptr = (bool) module_sp; + } + } + return module_sp; +} + +//---------------------------------------------------------------------- +// 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 == llvm::MachO::MH_DYLINKER) + { + m_dyld.address = addr; + ModuleSP dyld_module_sp; + if (ParseLoadCommands (data, m_dyld, &m_dyld.file_spec)) + { + if (m_dyld.file_spec) + { + dyld_module_sp = FindTargetModuleForDYLDImageInfo (m_dyld, true, NULL); + + if (dyld_module_sp) + UpdateImageLoadAddress (dyld_module_sp.get(), m_dyld); + } + } + + Target &target = m_process->GetTarget(); + + 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->GetLoadAddress(&target); + } + + // Update all image infos + InitializeFromAllImageInfos (); + + // 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 (dyld_module_sp) + { + target.GetImages().AppendIfNeeded (dyld_module_sp); + + // At this point we should have read in dyld's module, and so we should set breakpoints in it: + ModuleList modules; + modules.Append(dyld_module_sp); + target.ModulesDidLoad(modules); + m_dyld_module_wp = dyld_module_sp; + } + return true; + } + } + return false; +} + +bool +DynamicLoaderMacOSXDYLD::NeedToLocateDYLD () const +{ + return m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UpdateImageLoadAddress (Module *module, 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) + { + std::vector inaccessible_segment_indexes; + // We now know the slide amount, so go through all sections + // and update the load addresses with the correct values. + const size_t num_segments = info.segments.size(); + for (size_t i=0; iFindSectionByName(info.segments[i].name)); + + if (info.segments[i].maxprot == 0) + { + inaccessible_segment_indexes.push_back(i); + } + else + { + const addr_t new_section_load_addr = info.segments[i].vmaddr + info.slide; + static ConstString g_section_name_LINKEDIT ("__LINKEDIT"); + + if (section_sp) + { + // __LINKEDIT sections from files in the shared cache + // can overlap so check to see what the segment name is + // and pass "false" so we don't warn of overlapping + // "Section" objects, and "true" for all other sections. + const bool warn_multiple = section_sp->GetName() != g_section_name_LINKEDIT; + + changed = m_process->GetTarget().SetSectionLoadAddress (section_sp, new_section_load_addr, warn_multiple); + } + else + { + Host::SystemLog (Host::eSystemLogWarning, + "warning: unable to find and load segment named '%s' at 0x%" PRIx64 " in '%s' in macosx dynamic loader plug-in.\n", + info.segments[i].name.AsCString(""), + (uint64_t)new_section_load_addr, + image_object_file->GetFileSpec().GetPath().c_str()); + } + } + } + + // If the loaded the file (it changed) and we have segments that + // are not readable or writeable, add them to the invalid memory + // region cache for the process. This will typically only be + // the __PAGEZERO segment in the main executable. We might be able + // to apply this more generally to more sections that have no + // protections in the future, but for now we are going to just + // do __PAGEZERO. + if (changed && !inaccessible_segment_indexes.empty()) + { + for (uint32_t i=0; iFindSectionByName(info.segments[seg_idx].name)); + + if (section_sp) + { + static ConstString g_pagezero_section_name("__PAGEZERO"); + if (g_pagezero_section_name == section_sp->GetName()) + { + // __PAGEZERO never slides... + const lldb::addr_t vmaddr = info.segments[seg_idx].vmaddr; + const lldb::addr_t vmsize = info.segments[seg_idx].vmsize; + Process::LoadRange pagezero_range (vmaddr, vmsize); + m_process->AddInvalidMemoryRegion(pagezero_range); + } + } + } + } + } + } + } + // We might have an in memory image that was loaded as soon as it was created + if (info.load_stop_id == m_process->GetStopID()) + changed = true; + else if (changed) + { + // Update the stop ID when this library was updated + info.load_stop_id = m_process->GetStopID(); + } + return changed; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UnloadImageLoadAddress (Module *module, 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) + { + const size_t num_segments = info.segments.size(); + for (size_t i=0; iFindSectionByName(info.segments[i].name)); + if (section_sp) + { + const addr_t old_section_load_addr = info.segments[i].vmaddr + info.slide; + if (m_process->GetTarget().SetSectionUnloaded (section_sp, old_section_load_addr)) + changed = true; + } + else + { + Host::SystemLog (Host::eSystemLogWarning, + "warning: unable to find and unload segment named '%s' in '%s' in macosx dynamic loader plug-in.\n", + info.segments[i].name.AsCString(""), + image_object_file->GetFileSpec().GetPath().c_str()); + } + } + } + } + } + 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 + // DYLD passes three arguments to the notification breakpoint. + // Arg1: enum dyld_image_mode mode - 0 = adding, 1 = removing + // Arg2: uint32_t infoCount - Number of shared libraries added + // Arg3: dyld_image_info info[] - Array of structs of the form: + // const struct mach_header *imageLoadAddress + // const char *imageFilePath + // uintptr_t imageFileModDate (a time_t) + + DynamicLoaderMacOSXDYLD* dyld_instance = (DynamicLoaderMacOSXDYLD*) baton; + + // First step is to see if we've already initialized the all image infos. If we haven't then this function + // will do so and return true. In the course of initializing the all_image_infos it will read the complete + // current state, so we don't need to figure out what has changed from the data passed in to us. + + ExecutionContext exe_ctx (context->exe_ctx_ref); + Process *process = exe_ctx.GetProcessPtr(); + + // This is a sanity check just in case this dyld_instance is an old dyld plugin's breakpoint still lying around. + if (process != dyld_instance->m_process) + return false; + + if (dyld_instance->InitializeFromAllImageInfos()) + return dyld_instance->GetStopWhenImagesChange(); + + const lldb::ABISP &abi = process->GetABI(); + if (abi) + { + // Build up the value array to store the three arguments given above, then get the values from the ABI: + + ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); + ValueList argument_values; + Value input_value; + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + CompilerType clang_uint32_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint, 32); + input_value.SetValueType (Value::eValueTypeScalar); + input_value.SetCompilerType (clang_uint32_type); +// input_value.SetContext (Value::eContextTypeClangType, clang_uint32_type); + argument_values.PushValue (input_value); + argument_values.PushValue (input_value); + input_value.SetCompilerType (clang_void_ptr_type); + // input_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type); + argument_values.PushValue (input_value); + + if (abi->GetArgumentValues (exe_ctx.GetThreadRef(), argument_values)) + { + uint32_t dyld_mode = argument_values.GetValueAtIndex(0)->GetScalar().UInt (-1); + if (dyld_mode != static_cast(-1)) + { + // Okay the mode was right, now get the number of elements, and the array of new elements... + uint32_t image_infos_count = argument_values.GetValueAtIndex(1)->GetScalar().UInt (-1); + if (image_infos_count != static_cast(-1)) + { + // Got the number added, now go through the array of added elements, putting out the mach header + // address, and adding the image. + // Note, I'm not putting in logging here, since the AddModules & RemoveModules functions do + // all the logging internally. + + lldb::addr_t image_infos_addr = argument_values.GetValueAtIndex(2)->GetScalar().ULongLong(); + if (dyld_mode == 0) + { + // This is add: + dyld_instance->AddModulesUsingImageInfosAddress (image_infos_addr, image_infos_count); + } + else + { + // This is remove: + dyld_instance->RemoveModulesUsingImageInfosAddress (image_infos_addr, image_infos_count); + } + + } + } + } + } + else + { + process->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf("No ABI plugin located for triple %s -- shared libraries will not be registered!\n", process->GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); + } + + // 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); + + // the all image infos is already valid for this process stop ID + if (m_process->GetStopID() == m_dyld_all_image_infos_stop_id) + return true; + + m_dyld_all_image_infos.Clear(); + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + ByteOrder byte_order = m_process->GetTarget().GetArchitecture().GetByteOrder(); + uint32_t addr_size = 4; + if (m_dyld_all_image_infos_addr > UINT32_MAX) + addr_size = 8; + + uint8_t buf[256]; + DataExtractor data (buf, sizeof(buf), byte_order, addr_size); + lldb::offset_t offset = 0; + + const size_t count_v2 = sizeof (uint32_t) + // version + sizeof (uint32_t) + // infoArrayCount + addr_size + // infoArray + addr_size + // notification + addr_size + // processDetachedFromSharedRegion + libSystemInitialized + pad + addr_size; // dyldImageLoadAddress + const size_t count_v11 = count_v2 + + addr_size + // jitInfo + addr_size + // dyldVersion + addr_size + // errorMessage + addr_size + // terminationFlags + addr_size + // coreSymbolicationShmPage + addr_size + // systemOrderFlag + addr_size + // uuidArrayCount + addr_size + // uuidArray + addr_size + // dyldAllImageInfosAddress + addr_size + // initialImageCount + addr_size + // errorKind + addr_size + // errorClientOfDylibPath + addr_size + // errorTargetDylibPath + addr_size; // errorSymbol + const size_t count_v13 = count_v11 + + addr_size + // sharedCacheSlide + sizeof (uuid_t); // sharedCacheUUID + UNUSED_IF_ASSERT_DISABLED(count_v13); + assert (sizeof (buf) >= count_v13); + + Error error; + if (m_process->ReadMemory (m_dyld_all_image_infos_addr, buf, 4, error) == 4) + { + m_dyld_all_image_infos.version = data.GetU32(&offset); + // If anything in the high byte is set, we probably got the byte + // order incorrect (the process might not have it set correctly + // yet due to attaching to a program without a specified file). + if (m_dyld_all_image_infos.version & 0xff000000) + { + // We have guessed the wrong byte order. Swap it and try + // reading the version again. + if (byte_order == eByteOrderLittle) + byte_order = eByteOrderBig; + else + byte_order = eByteOrderLittle; + + data.SetByteOrder (byte_order); + offset = 0; + m_dyld_all_image_infos.version = data.GetU32(&offset); + } + } + else + { + return false; + } + + const size_t count = (m_dyld_all_image_infos.version >= 11) ? count_v11 : count_v2; + + const size_t bytes_read = m_process->ReadMemory (m_dyld_all_image_infos_addr, buf, count, error); + if (bytes_read == count) + { + 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); + 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); + if (m_dyld_all_image_infos.version >= 11) + { + offset += addr_size * 8; + uint64_t dyld_all_image_infos_addr = data.GetPointer(&offset); + + // When we started, we were given the actual address of the all_image_infos + // struct (probably via TASK_DYLD_INFO) in memory - this address is stored in + // m_dyld_all_image_infos_addr and is the most accurate address we have. + + // We read the dyld_all_image_infos struct from memory; it contains its own address. + // If the address in the struct does not match the actual address, + // the dyld we're looking at has been loaded at a different location (slid) from + // where it intended to load. The addresses in the dyld_all_image_infos struct + // are the original, non-slid addresses, and need to be adjusted. Most importantly + // the address of dyld and the notification address need to be adjusted. + + if (dyld_all_image_infos_addr != m_dyld_all_image_infos_addr) + { + uint64_t image_infos_offset = dyld_all_image_infos_addr - m_dyld_all_image_infos.dyldImageLoadAddress; + uint64_t notification_offset = m_dyld_all_image_infos.notification - m_dyld_all_image_infos.dyldImageLoadAddress; + m_dyld_all_image_infos.dyldImageLoadAddress = m_dyld_all_image_infos_addr - image_infos_offset; + m_dyld_all_image_infos.notification = m_dyld_all_image_infos.dyldImageLoadAddress + notification_offset; + } + } + m_dyld_all_image_infos_stop_id = m_process->GetStopID(); + return true; + } + } + return false; +} + + +// This method is an amalgamation of code from +// ReadMachHeader() +// ParseLoadCommands() +// UpdateImageInfosHeaderAndLoadCommands() +// but written to extract everything from the JSON packet from debugserver, instead of using memory reads. + +bool +DynamicLoaderMacOSXDYLD::AddModulesUsingInfosFromDebugserver (StructuredData::ObjectSP image_details, DYLDImageInfo::collection &image_infos) +{ + StructuredData::ObjectSP images_sp = image_details->GetAsDictionary()->GetValueForKey("images"); + if (images_sp.get() == nullptr) + return false; + + image_infos.resize (images_sp->GetAsArray()->GetSize()); + + uint32_t exe_idx = UINT32_MAX; + + for (size_t i = 0; i < image_infos.size(); i++) + { + StructuredData::ObjectSP image_sp = images_sp->GetAsArray()->GetItemAtIndex(i); + if (image_sp.get() == nullptr || image_sp->GetAsDictionary() == nullptr) + return false; + StructuredData::Dictionary *image = image_sp->GetAsDictionary(); + if (image->HasKey("load_address") == false + || image->HasKey("pathname") == false + || image->HasKey("mod_date") == false + || image->HasKey("mach_header") == false + || image->GetValueForKey("mach_header")->GetAsDictionary() == nullptr + || image->HasKey("segments") == false + || image->GetValueForKey("segments")->GetAsArray() == nullptr + || image->HasKey("uuid") == false ) + { + return false; + } + image_infos[i].address = image->GetValueForKey("load_address")->GetAsInteger()->GetValue(); + image_infos[i].mod_date = image->GetValueForKey("mod_date")->GetAsInteger()->GetValue(); + image_infos[i].file_spec.SetFile(image->GetValueForKey("pathname")->GetAsString()->GetValue().c_str(), false); + + StructuredData::Dictionary *mh = image->GetValueForKey("mach_header")->GetAsDictionary(); + image_infos[i].header.magic = mh->GetValueForKey("magic")->GetAsInteger()->GetValue(); + image_infos[i].header.cputype = mh->GetValueForKey("cputype")->GetAsInteger()->GetValue(); + image_infos[i].header.cpusubtype = mh->GetValueForKey("cpusubtype")->GetAsInteger()->GetValue(); + image_infos[i].header.filetype = mh->GetValueForKey("filetype")->GetAsInteger()->GetValue(); + + // Fields that aren't used by DynamicLoaderMacOSXDYLD so debugserver doesn't currently send them + // in the reply. + + if (mh->HasKey("flags")) + image_infos[i].header.flags = mh->GetValueForKey("flags")->GetAsInteger()->GetValue(); + else + image_infos[i].header.flags = 0; + + if (mh->HasKey("ncmds")) + image_infos[i].header.ncmds = mh->GetValueForKey("ncmds")->GetAsInteger()->GetValue(); + else + image_infos[i].header.ncmds = 0; + + if (mh->HasKey("sizeofcmds")) + image_infos[i].header.sizeofcmds = mh->GetValueForKey("sizeofcmds")->GetAsInteger()->GetValue(); + else + image_infos[i].header.sizeofcmds = 0; + + if (image_infos[i].header.filetype == llvm::MachO::MH_EXECUTE) + exe_idx = i; + + StructuredData::Array *segments = image->GetValueForKey("segments")->GetAsArray(); + uint32_t segcount = segments->GetSize(); + for (size_t j = 0; j < segcount; j++) + { + Segment segment; + StructuredData::Dictionary *seg = segments->GetItemAtIndex(j)->GetAsDictionary(); + segment.name = ConstString(seg->GetValueForKey("name")->GetAsString()->GetValue().c_str()); + segment.vmaddr = seg->GetValueForKey("vmaddr")->GetAsInteger()->GetValue(); + segment.vmsize = seg->GetValueForKey("vmsize")->GetAsInteger()->GetValue(); + segment.fileoff = seg->GetValueForKey("fileoff")->GetAsInteger()->GetValue(); + segment.filesize = seg->GetValueForKey("filesize")->GetAsInteger()->GetValue(); + segment.maxprot = seg->GetValueForKey("maxprot")->GetAsInteger()->GetValue(); + + // Fields that aren't used by DynamicLoaderMacOSXDYLD so debugserver doesn't currently send them + // in the reply. + + if (seg->HasKey("initprot")) + segment.initprot = seg->GetValueForKey("initprot")->GetAsInteger()->GetValue(); + else + segment.initprot = 0; + + if (seg->HasKey("flags")) + segment.flags = seg->GetValueForKey("flags")->GetAsInteger()->GetValue(); + else + segment.flags = 0; + + if (seg->HasKey("nsects")) + segment.nsects = seg->GetValueForKey("nsects")->GetAsInteger()->GetValue(); + else + segment.nsects = 0; + + image_infos[i].segments.push_back (segment); + } + + image_infos[i].uuid.SetFromCString (image->GetValueForKey("uuid")->GetAsString()->GetValue().c_str()); + + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + const size_t num_sections = image_infos[i].segments.size(); + for (size_t k = 0; k < num_sections; ++k) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + if ((image_infos[i].segments[k].fileoff == 0 && image_infos[i].segments[k].filesize > 0) + || (image_infos[i].segments[k].name == ConstString("__TEXT"))) + { + image_infos[i].slide = image_infos[i].address - image_infos[i].segments[k].vmaddr; + // We have found the slide amount, so we can exit + // this for loop. + break; + } + } + } + + Target &target = m_process->GetTarget(); + + if (exe_idx < image_infos.size()) + { + const bool can_create = true; + ModuleSP exe_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[exe_idx], can_create, NULL)); + + if (exe_module_sp) + { + UpdateImageLoadAddress (exe_module_sp.get(), image_infos[exe_idx]); + + if (exe_module_sp.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded. Also when setting the + // executable module, it will clear the targets module list, and if we + // have an in memory dyld module, it will get removed from the list + // so we will need to add it back after setting the executable module, + // so we first try and see if we already have a weak pointer to the + // dyld module, make it into a shared pointer, then add the executable, + // then re-add it back to make sure it is always in the list. + ModuleSP dyld_module_sp(m_dyld_module_wp.lock()); + + const bool get_dependent_images = false; + m_process->GetTarget().SetExecutableModule (exe_module_sp, + get_dependent_images); + + if (dyld_module_sp) + { + if(target.GetImages().AppendIfNeeded (dyld_module_sp)) + { + // Also add it to the section list. + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + } + } + } + } + } + return true; +} + +bool +DynamicLoaderMacOSXDYLD::AddModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count) +{ + DYLDImageInfo::collection image_infos; + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf ("Adding %d modules.\n", image_infos_count); + + Mutex::Locker locker(m_mutex); + if (m_process->GetStopID() == m_dyld_image_infos_stop_id) + return true; + + StructuredData::ObjectSP image_infos_json_sp = m_process->GetLoadedDynamicLibrariesInfos (image_infos_addr, image_infos_count); + if (image_infos_json_sp.get() + && image_infos_json_sp->GetAsDictionary() + && image_infos_json_sp->GetAsDictionary()->HasKey("images") + && image_infos_json_sp->GetAsDictionary()->GetValueForKey("images")->GetAsArray() + && image_infos_json_sp->GetAsDictionary()->GetValueForKey("images")->GetAsArray()->GetSize() == image_infos_count) + { + bool return_value = false; + if (AddModulesUsingInfosFromDebugserver (image_infos_json_sp, image_infos)) + { + return_value = AddModulesUsingImageInfos (image_infos); + } + m_dyld_image_infos_stop_id = m_process->GetStopID(); + return return_value; + } + + if (!ReadImageInfos (image_infos_addr, image_infos_count, image_infos)) + return false; + + UpdateImageInfosHeaderAndLoadCommands (image_infos, image_infos_count, false); + bool return_value = AddModulesUsingImageInfos (image_infos); + m_dyld_image_infos_stop_id = m_process->GetStopID(); + return return_value; +} + +// Adds the modules in image_infos to m_dyld_image_infos. +// NB don't call this passing in m_dyld_image_infos. + +bool +DynamicLoaderMacOSXDYLD::AddModulesUsingImageInfos (DYLDImageInfo::collection &image_infos) +{ + // Now add these images to the main list. + ModuleList loaded_module_list; + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + Target &target = m_process->GetTarget(); + ModuleList& target_images = target.GetImages(); + + for (uint32_t idx = 0; idx < image_infos.size(); ++idx) + { + if (log) + { + log->Printf ("Adding new image at address=0x%16.16" PRIx64 ".", image_infos[idx].address); + image_infos[idx].PutToLog (log); + } + + m_dyld_image_infos.push_back(image_infos[idx]); + + ModuleSP image_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[idx], true, NULL)); + + 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) + { + ModuleSpec module_spec (objfile->GetFileSpec(), image_infos[idx].GetArchitecture ()); + module_spec.GetObjectName() = commpage_dbstr; + ModuleSP commpage_image_module_sp(target_images.FindFirstModule (module_spec)); + if (!commpage_image_module_sp) + { + module_spec.SetObjectOffset (objfile->GetFileOffset() + commpage_section->GetFileOffset()); + module_spec.SetObjectSize (objfile->GetByteSize()); + commpage_image_module_sp = target.GetSharedModule (module_spec); + if (!commpage_image_module_sp || commpage_image_module_sp->GetObjectFile() == NULL) + { + commpage_image_module_sp = m_process->ReadModuleFromMemory (image_infos[idx].file_spec, + image_infos[idx].address); + // Always load a memory image right away in the target in case + // we end up trying to read the symbol table from memory... The + // __LINKEDIT will need to be mapped so we can figure out where + // the symbol table bits are... + bool changed = false; + UpdateImageLoadAddress (commpage_image_module_sp.get(), image_infos[idx]); + target.GetImages().Append(commpage_image_module_sp); + if (changed) + { + image_infos[idx].load_stop_id = m_process->GetStopID(); + loaded_module_list.AppendIfNeeded (commpage_image_module_sp); + } + } + } + } + } + } + + // 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(), image_infos[idx])) + { + target_images.AppendIfNeeded(image_module_sp); + loaded_module_list.AppendIfNeeded (image_module_sp); + } + } + } + + if (loaded_module_list.GetSize() > 0) + { + if (log) + loaded_module_list.LogUUIDAndPaths (log, "DynamicLoaderMacOSXDYLD::ModulesDidLoad"); + m_process->GetTarget().ModulesDidLoad (loaded_module_list); + } + return true; +} + +bool +DynamicLoaderMacOSXDYLD::RemoveModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count) +{ + DYLDImageInfo::collection image_infos; + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + + Mutex::Locker locker(m_mutex); + if (m_process->GetStopID() == m_dyld_image_infos_stop_id) + return true; + + // First read in the image_infos for the removed modules, and their headers & load commands. + if (!ReadImageInfos (image_infos_addr, image_infos_count, image_infos)) + { + if (log) + log->PutCString ("Failed reading image infos array."); + return false; + } + + if (log) + log->Printf ("Removing %d modules.", image_infos_count); + + ModuleList unloaded_module_list; + for (uint32_t idx = 0; idx < image_infos.size(); ++idx) + { + if (log) + { + log->Printf ("Removing module at address=0x%16.16" PRIx64 ".", image_infos[idx].address); + image_infos[idx].PutToLog (log); + } + + // Remove this image_infos from the m_all_image_infos. We do the comparison by address + // rather than by file spec because we can have many modules with the same "file spec" in the + // case that they are modules loaded from memory. + // + // Also copy over the uuid from the old entry to the removed entry so we can + // use it to lookup the module in the module list. + + DYLDImageInfo::collection::iterator pos, end = m_dyld_image_infos.end(); + for (pos = m_dyld_image_infos.begin(); pos != end; pos++) + { + if (image_infos[idx].address == (*pos).address) + { + image_infos[idx].uuid = (*pos).uuid; + + // Add the module from this image_info to the "unloaded_module_list". We'll remove them all at + // one go later on. + + ModuleSP unload_image_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[idx], false, NULL)); + if (unload_image_module_sp.get()) + { + // When we unload, be sure to use the image info from the old list, + // since that has sections correctly filled in. + UnloadImageLoadAddress (unload_image_module_sp.get(), *pos); + unloaded_module_list.AppendIfNeeded (unload_image_module_sp); + } + else + { + if (log) + { + log->Printf ("Could not find module for unloading info entry:"); + image_infos[idx].PutToLog(log); + } + } + + // Then remove it from the m_dyld_image_infos: + + m_dyld_image_infos.erase(pos); + break; + } + } + + if (pos == end) + { + if (log) + { + log->Printf ("Could not find image_info entry for unloading image:"); + image_infos[idx].PutToLog(log); + } + } + } + if (unloaded_module_list.GetSize() > 0) + { + if (log) + { + log->PutCString("Unloaded:"); + unloaded_module_list.LogUUIDAndPaths (log, "DynamicLoaderMacOSXDYLD::ModulesDidUnload"); + } + m_process->GetTarget().GetImages().Remove (unloaded_module_list); + } + m_dyld_image_infos_stop_id = m_process->GetStopID(); + return true; +} + +bool +DynamicLoaderMacOSXDYLD::ReadImageInfos (lldb::addr_t image_infos_addr, + uint32_t image_infos_count, + DYLDImageInfo::collection &image_infos) +{ + const ByteOrder endian = m_dyld.GetByteOrder(); + const uint32_t addr_size = m_dyld.GetAddressByteSize(); + + image_infos.resize(image_infos_count); + const size_t count = image_infos.size() * 3 * addr_size; + DataBufferHeap info_data(count, 0); + Error error; + const size_t bytes_read = m_process->ReadMemory (image_infos_addr, + info_data.GetBytes(), + info_data.GetByteSize(), + error); + if (bytes_read == count) + { + lldb::offset_t info_data_offset = 0; + DataExtractor info_data_ref(info_data.GetBytes(), info_data.GetByteSize(), endian, addr_size); + for (size_t i = 0; i < image_infos.size() && info_data_ref.ValidOffset(info_data_offset); i++) + { + image_infos[i].address = info_data_ref.GetPointer(&info_data_offset); + lldb::addr_t path_addr = info_data_ref.GetPointer(&info_data_offset); + image_infos[i].mod_date = info_data_ref.GetPointer(&info_data_offset); + + char raw_path[PATH_MAX]; + m_process->ReadCStringFromMemory (path_addr, raw_path, sizeof(raw_path), error); + // don't resolve the path + if (error.Success()) + { + const bool resolve_path = false; + image_infos[i].file_spec.SetFile(raw_path, resolve_path); + } + } + return true; + } + else + { + 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). Only do this if this is the first time +// we're reading the dyld infos. Return true if we actually read anything, +// and false otherwise. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::InitializeFromAllImageInfos () +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER)); + + Mutex::Locker locker(m_mutex); + if (m_process->GetStopID() == m_dyld_image_infos_stop_id + || m_dyld_image_infos.size() != 0) + return false; + + if (ReadAllImageInfosStructure ()) + { + // Nothing to load or unload? + if (m_dyld_all_image_infos.dylib_info_count == 0) + return true; + + if (m_dyld_all_image_infos.dylib_info_addr == 0) + { + // DYLD is updating the images now. So we should say we have no images, and then we'll + // figure it out when we hit the added breakpoint. + return false; + } + else + { + if (!AddModulesUsingImageInfosAddress (m_dyld_all_image_infos.dylib_info_addr, + m_dyld_all_image_infos.dylib_info_count)) + { + DEBUG_PRINTF("%s", "unable to read all data for all_dylib_infos."); + m_dyld_image_infos.clear(); + } + } + + // Now we have one more bit of business. If there is a library left in the images for our target that + // doesn't have a load address, then it must be something that we were expecting to load (for instance we + // read a load command for it) but it didn't in fact load - probably because DYLD_*_PATH pointed + // to an equivalent version. We don't want it to stay in the target's module list or it will confuse + // us, so unload it here. + Target &target = m_process->GetTarget(); + const ModuleList &target_modules = target.GetImages(); + ModuleList not_loaded_modules; + Mutex::Locker modules_locker(target_modules.GetMutex()); + + size_t num_modules = target_modules.GetSize(); + for (size_t i = 0; i < num_modules; i++) + { + ModuleSP module_sp = target_modules.GetModuleAtIndexUnlocked (i); + if (!module_sp->IsLoadedInTarget (&target)) + { + if (log) + { + StreamString s; + module_sp->GetDescription (&s); + log->Printf ("Unloading pre-run module: %s.", s.GetData ()); + } + not_loaded_modules.Append (module_sp); + } + } + + if (not_loaded_modules.GetSize() != 0) + { + target.GetImages().Remove(not_loaded_modules); + } + + return true; + } + else + return false; +} + +//---------------------------------------------------------------------- +// 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, llvm::MachO::mach_header *header, DataExtractor *load_command_data) +{ + DataBufferHeap header_bytes(sizeof(llvm::MachO::mach_header), 0); + Error error; + size_t bytes_read = m_process->ReadMemory (addr, + header_bytes.GetBytes(), + header_bytes.GetByteSize(), + error); + if (bytes_read == sizeof(llvm::MachO::mach_header)) + { + lldb::offset_t offset = 0; + ::memset (header, 0, sizeof(llvm::MachO::mach_header)); + + // Get the magic byte unswapped so we can figure out what we are dealing with + DataExtractor data(header_bytes.GetBytes(), header_bytes.GetByteSize(), endian::InlHostByteOrder(), 4); + header->magic = data.GetU32(&offset); + lldb::addr_t load_cmd_addr = addr; + data.SetByteOrder(DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic(header->magic)); + switch (header->magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_CIGAM: + data.SetAddressByteSize(4); + load_cmd_addr += sizeof(llvm::MachO::mach_header); + break; + + case llvm::MachO::MH_MAGIC_64: + case llvm::MachO::MH_CIGAM_64: + data.SetAddressByteSize(8); + load_cmd_addr += sizeof(llvm::MachO::mach_header_64); + break; + + default: + return false; + } + + // Read the rest of dyld's mach header + if (data.GetU32(&offset, &header->cputype, (sizeof(llvm::MachO::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, DYLDImageInfo& dylib_info, FileSpec *lc_id_dylinker) +{ + lldb::offset_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(llvm::MachO::load_command))) + { + llvm::MachO::load_command load_cmd; + lldb::offset_t load_cmd_offset = offset; + load_cmd.cmd = data.GetU32 (&offset); + load_cmd.cmdsize = data.GetU32 (&offset); + switch (load_cmd.cmd) + { + case llvm::MachO::LC_SEGMENT: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + // We are putting 4 uint32_t values 4 uint64_t values so + // we have to use multiple 32 bit gets below. + segment.vmaddr = data.GetU32 (&offset); + segment.vmsize = data.GetU32 (&offset); + segment.fileoff = data.GetU32 (&offset); + segment.filesize = data.GetU32 (&offset); + // Extract maxprot, initprot, nsects and flags all at once + data.GetU32(&offset, &segment.maxprot, 4); + dylib_info.segments.push_back (segment); + } + break; + + case llvm::MachO::LC_SEGMENT_64: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + // Extract vmaddr, vmsize, fileoff, and filesize all at once + data.GetU64(&offset, &segment.vmaddr, 4); + // Extract maxprot, initprot, nsects and flags all at once + data.GetU32(&offset, &segment.maxprot, 4); + dylib_info.segments.push_back (segment); + } + break; + + case llvm::MachO::LC_ID_DYLINKER: + if (lc_id_dylinker) + { + const lldb::offset_t name_offset = load_cmd_offset + data.GetU32 (&offset); + const char *path = data.PeekCStr (name_offset); + lc_id_dylinker->SetFile (path, true); + } + break; + + case llvm::MachO::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; + } + } + + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + const size_t num_sections = dylib_info.segments.size(); + for (size_t i = 0; i < num_sections; ++i) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + if ((dylib_info.segments[i].fileoff == 0 && dylib_info.segments[i].filesize > 0) || (dylib_info.segments[i].name == ConstString("__TEXT"))) + { + dylib_info.slide = dylib_info.address - dylib_info.segments[i].vmaddr; + // We have found the slide amount, so we can exit + // this for loop. + break; + } + } + 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::UpdateImageInfosHeaderAndLoadCommands(DYLDImageInfo::collection &image_infos, + uint32_t infos_count, + bool update_executable) +{ + uint32_t exe_idx = UINT32_MAX; + // Read any UUID values that we can get + for (uint32_t i = 0; i < infos_count; i++) + { + if (!image_infos[i].UUIDValid()) + { + DataExtractor data; // Load command data + if (!ReadMachHeader (image_infos[i].address, &image_infos[i].header, &data)) + continue; + + ParseLoadCommands (data, image_infos[i], NULL); + + if (image_infos[i].header.filetype == llvm::MachO::MH_EXECUTE) + exe_idx = i; + + } + } + + Target &target = m_process->GetTarget(); + + if (exe_idx < image_infos.size()) + { + const bool can_create = true; + ModuleSP exe_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[exe_idx], can_create, NULL)); + + if (exe_module_sp) + { + UpdateImageLoadAddress (exe_module_sp.get(), image_infos[exe_idx]); + + if (exe_module_sp.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded. Also when setting the + // executable module, it will clear the targets module list, and if we + // have an in memory dyld module, it will get removed from the list + // so we will need to add it back after setting the executable module, + // so we first try and see if we already have a weak pointer to the + // dyld module, make it into a shared pointer, then add the executable, + // then re-add it back to make sure it is always in the list. + ModuleSP dyld_module_sp(m_dyld_module_wp.lock()); + + const bool get_dependent_images = false; + m_process->GetTarget().SetExecutableModule (exe_module_sp, + get_dependent_images); + + if (dyld_module_sp) + { + if(target.GetImages().AppendIfNeeded (dyld_module_sp)) + { + // Also add it to the section list. + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + } + } + } + } + } +} + +//---------------------------------------------------------------------- +// On Mac OS X libobjc (the Objective-C runtime) has several critical dispatch +// functions written in hand-written assembly, and also have hand-written unwind +// information in the eh_frame section. Normally we prefer analyzing the +// assembly instructions of a currently executing frame to unwind from that frame -- +// but on hand-written functions this profiling can fail. We should use the +// eh_frame instructions for these functions all the time. +// +// As an aside, it would be better if the eh_frame entries had a flag (or were +// extensible so they could have an Apple-specific flag) which indicates that +// the instructions are asynchronous -- accurate at every instruction, instead +// of our normal default assumption that they are not. +//---------------------------------------------------------------------- + +bool +DynamicLoaderMacOSXDYLD::AlwaysRelyOnEHUnwindInfo (SymbolContext &sym_ctx) +{ + ModuleSP module_sp; + if (sym_ctx.symbol) + { + module_sp = sym_ctx.symbol->GetAddressRef().GetModule(); + } + if (module_sp.get() == NULL && sym_ctx.function) + { + module_sp = sym_ctx.function->GetAddressRange().GetBaseAddress().GetModule(); + } + if (module_sp.get() == NULL) + return false; + + ObjCLanguageRuntime *objc_runtime = m_process->GetObjCLanguageRuntime(); + if (objc_runtime != NULL && objc_runtime->IsModuleObjCLibrary (module_sp)) + { + return true; + } + + return false; +} + + + +//---------------------------------------------------------------------- +// Dump a Segment to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Segment::PutToLog (Log *log, lldb::addr_t slide) const +{ + if (log) + { + if (slide == 0) + log->Printf ("\t\t%16s [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")", + name.AsCString(""), + vmaddr + slide, + vmaddr + slide + vmsize); + else + log->Printf ("\t\t%16s [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") slide = 0x%" PRIx64, + name.AsCString(""), + vmaddr + slide, + vmaddr + slide + vmsize, + slide); + } +} + +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.8" PRIx64 " 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' (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.GetPath().c_str()); + } + else + log->Printf("\t modtime=0x%8.8" PRIx64 " path='%s' (UNLOADED)", + mod_date, + file_spec.GetPath().c_str()); + } + else + { + if (u) + { + log->Printf("\taddress=0x%16.16" PRIx64 " modtime=0x%8.8" PRIx64 " 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'", + 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.GetPath().c_str()); + } + else + { + log->Printf("\taddress=0x%16.16" PRIx64 " modtime=0x%8.8" PRIx64 " path='%s'", + address, + mod_date, + file_spec.GetPath().c_str()); + + } + for (uint32_t i=0; iPrintf("dyld_all_image_infos = { version=%d, count=%d, addr=0x%8.8" PRIx64 ", notify=0x%8.8" PRIx64 " }", + 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->PutCString("Loaded:"); + for (i = 0; iGetState())); + Clear(true); + m_process = process; + m_process->GetTarget().ClearAllLoadedSections(); +} + +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. + bool resolved = m_process->GetTarget().ResolveLoadAddress(m_dyld_all_image_infos.notification, so_addr); + if (!resolved) + { + ModuleSP dyld_module_sp = m_dyld_module_wp.lock(); + if (dyld_module_sp) + { + UpdateImageLoadAddress (dyld_module_sp.get(), m_dyld); + resolved = m_process->GetTarget().ResolveLoadAddress(m_dyld_all_image_infos.notification, so_addr); + } + } + + if (resolved) + { + Breakpoint *dyld_break = m_process->GetTarget().CreateBreakpoint (so_addr, true, false).get(); + dyld_break->SetCallback (DynamicLoaderMacOSXDYLD::NotifyBreakpointHit, this, true); + dyld_break->SetBreakpointKind ("shared-library-event"); + m_break_id = dyld_break->GetID(); + } + } + } + return m_break_id != LLDB_INVALID_BREAK_ID; +} + +//---------------------------------------------------------------------- +// 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 eStateConnected: + 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; + } +} + +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; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + TargetSP target_sp (thread.CalculateTarget()); + + if (current_symbol != NULL) + { + std::vector
addresses; + + if (current_symbol->IsTrampoline()) + { + const ConstString &trampoline_name = current_symbol->GetMangled().GetName(current_symbol->GetLanguage(), Mangled::ePreferMangled); + + if (trampoline_name) + { + const ModuleList &images = target_sp->GetImages(); + + SymbolContextList code_symbols; + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, code_symbols); + size_t num_code_symbols = code_symbols.GetSize(); + + if (num_code_symbols > 0) + { + for (uint32_t i = 0; i < num_code_symbols; i++) + { + SymbolContext context; + AddressRange addr_range; + if (code_symbols.GetContextAtIndex(i, context)) + { + context.GetAddressRange (eSymbolContextEverything, 0, false, addr_range); + addresses.push_back(addr_range.GetBaseAddress()); + if (log) + { + addr_t load_addr = addr_range.GetBaseAddress().GetLoadAddress(target_sp.get()); + + log->Printf ("Found a trampoline target symbol at 0x%" PRIx64 ".", load_addr); + } + } + } + } + + SymbolContextList reexported_symbols; + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeReExported, reexported_symbols); + size_t num_reexported_symbols = reexported_symbols.GetSize(); + if (num_reexported_symbols > 0) + { + for (uint32_t i = 0; i < num_reexported_symbols; i++) + { + SymbolContext context; + if (reexported_symbols.GetContextAtIndex(i, context)) + { + if (context.symbol) + { + Symbol *actual_symbol = context.symbol->ResolveReExportedSymbol(*target_sp.get()); + if (actual_symbol) + { + const Address actual_symbol_addr = actual_symbol->GetAddress(); + if (actual_symbol_addr.IsValid()) + { + addresses.push_back(actual_symbol_addr); + if (log) + { + lldb::addr_t load_addr = actual_symbol_addr.GetLoadAddress(target_sp.get()); + log->Printf ("Found a re-exported symbol: %s at 0x%" PRIx64 ".", + actual_symbol->GetName().GetCString(), load_addr); + } + } + } + } + } + } + } + + SymbolContextList indirect_symbols; + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver, indirect_symbols); + size_t num_indirect_symbols = indirect_symbols.GetSize(); + if (num_indirect_symbols > 0) + { + for (uint32_t i = 0; i < num_indirect_symbols; i++) + { + SymbolContext context; + AddressRange addr_range; + if (indirect_symbols.GetContextAtIndex(i, context)) + { + context.GetAddressRange (eSymbolContextEverything, 0, false, addr_range); + addresses.push_back(addr_range.GetBaseAddress()); + if (log) + { + addr_t load_addr = addr_range.GetBaseAddress().GetLoadAddress(target_sp.get()); + + log->Printf ("Found an indirect target symbol at 0x%" PRIx64 ".", load_addr); + } + } + } + } + } + } + else if (current_symbol->GetType() == eSymbolTypeReExported) + { + // I am not sure we could ever end up stopped AT a re-exported symbol. But just in case: + + const Symbol *actual_symbol = current_symbol->ResolveReExportedSymbol(*(target_sp.get())); + if (actual_symbol) + { + Address target_addr(actual_symbol->GetAddress()); + if (target_addr.IsValid()) + { + if (log) + log->Printf ("Found a re-exported symbol: %s pointing to: %s at 0x%" PRIx64 ".", + current_symbol->GetName().GetCString(), + actual_symbol->GetName().GetCString(), + target_addr.GetLoadAddress(target_sp.get())); + addresses.push_back (target_addr.GetLoadAddress(target_sp.get())); + + } + } + } + + if (addresses.size() > 0) + { + // First check whether any of the addresses point to Indirect symbols, and if they do, resolve them: + std::vector load_addrs; + for (Address address : addresses) + { + Symbol *symbol = address.CalculateSymbolContextSymbol(); + if (symbol && symbol->IsIndirect()) + { + Error error; + Address symbol_address = symbol->GetAddress(); + addr_t resolved_addr = thread.GetProcess()->ResolveIndirectFunction(&symbol_address, error); + if (error.Success()) + { + load_addrs.push_back(resolved_addr); + if (log) + log->Printf("ResolveIndirectFunction found resolved target for %s at 0x%" PRIx64 ".", + symbol->GetName().GetCString(), resolved_addr); + } + } + else + { + load_addrs.push_back(address.GetLoadAddress(target_sp.get())); + } + + } + thread_plan_sp.reset (new ThreadPlanRunToAddress (thread, load_addrs, stop_others)); + } + } + else + { + if (log) + log->Printf ("Could not find symbol for step through."); + } + + return thread_plan_sp; +} + +size_t +DynamicLoaderMacOSXDYLD::FindEquivalentSymbols (lldb_private::Symbol *original_symbol, + lldb_private::ModuleList &images, + lldb_private::SymbolContextList &equivalent_symbols) +{ + const ConstString &trampoline_name = original_symbol->GetMangled().GetName(original_symbol->GetLanguage(), Mangled::ePreferMangled); + if (!trampoline_name) + return 0; + + size_t initial_size = equivalent_symbols.GetSize(); + + static const char *resolver_name_regex = "(_gc|_non_gc|\\$[A-Za-z0-9\\$]+)$"; + std::string equivalent_regex_buf("^"); + equivalent_regex_buf.append (trampoline_name.GetCString()); + equivalent_regex_buf.append (resolver_name_regex); + + RegularExpression equivalent_name_regex (equivalent_regex_buf.c_str()); + const bool append = true; + images.FindSymbolsMatchingRegExAndType (equivalent_name_regex, eSymbolTypeCode, equivalent_symbols, append); + + return equivalent_symbols.GetSize() - initial_size; +} + +Error +DynamicLoaderMacOSXDYLD::CanLoadImage () +{ + Error error; + // In order for us to tell if we can load a shared library we verify that + // the dylib_info_addr isn't zero (which means no shared libraries have + // been set yet, or dyld is currently mucking with the shared library list). + if (ReadAllImageInfosStructure ()) + { + // TODO: also check the _dyld_global_lock_held variable in libSystem.B.dylib? + // TODO: check the malloc lock? + // TODO: check the objective C lock? + if (m_dyld_all_image_infos.dylib_info_addr != 0) + return error; // Success + } + + error.SetErrorString("unsafe to load or unload shared libraries"); + return error; +} + +void +DynamicLoaderMacOSXDYLD::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderMacOSXDYLD::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +DynamicLoaderMacOSXDYLD::GetPluginNameStatic() +{ + static ConstString g_name("macosx-dyld"); + return g_name; +} + +const char * +DynamicLoaderMacOSXDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library loads/unloads in MacOSX user processes."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +DynamicLoaderMacOSXDYLD::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderMacOSXDYLD::GetPluginVersion() +{ + return 1; +} + +uint32_t +DynamicLoaderMacOSXDYLD::AddrByteSize() +{ + switch (m_dyld.header.magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_CIGAM: + return 4; + + case llvm::MachO::MH_MAGIC_64: + case llvm::MachO::MH_CIGAM_64: + return 8; + + default: + break; + } + return 0; +} + +lldb::ByteOrder +DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic (uint32_t magic) +{ + switch (magic) + { + case llvm::MachO::MH_MAGIC: + case llvm::MachO::MH_MAGIC_64: + return endian::InlHostByteOrder(); + + case llvm::MachO::MH_CIGAM: + case llvm::MachO::MH_CIGAM_64: + if (endian::InlHostByteOrder() == lldb::eByteOrderBig) + return lldb::eByteOrderLittle; + else + return lldb::eByteOrderBig; + + default: + break; + } + return lldb::eByteOrderInvalid; +} + +lldb::ByteOrder +DynamicLoaderMacOSXDYLD::DYLDImageInfo::GetByteOrder() +{ + return DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic(header.magic); +} + diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h new file mode 100644 index 000000000000..8fd60d0b6ac7 --- /dev/null +++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h @@ -0,0 +1,385 @@ +//===-- 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 +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/SafeMachO.h" + +class DynamicLoaderMacOSXDYLD : public lldb_private::DynamicLoader +{ +public: + DynamicLoaderMacOSXDYLD(lldb_private::Process *process); + + ~DynamicLoaderMacOSXDYLD() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process, bool force); + + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + void + DidAttach() override; + + void + DidLaunch() override; + + bool + ProcessDidExec() override; + + lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others) override; + + size_t + FindEquivalentSymbols(lldb_private::Symbol *original_symbol, + lldb_private::ModuleList &module_list, + lldb_private::SymbolContextList &equivalent_symbols) override; + + lldb_private::Error + CanLoadImage() override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + + bool + AlwaysRelyOnEHUnwindInfo(lldb_private::SymbolContext &sym_ctx) override; + +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); + + static bool + NotifyBreakpointHit (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + uint32_t + AddrByteSize(); + + static lldb::ByteOrder + GetByteOrderFromMagic (uint32_t magic); + + bool + ReadMachHeader (lldb::addr_t addr, + llvm::MachO::mach_header *header, + lldb_private::DataExtractor *load_command_data); + + class Segment + { + public: + Segment() : + name(), + vmaddr(LLDB_INVALID_ADDRESS), + vmsize(0), + fileoff(0), + filesize(0), + maxprot(0), + initprot(0), + nsects(0), + flags(0) + { + } + + lldb_private::ConstString name; + lldb::addr_t vmaddr; + lldb::addr_t vmsize; + lldb::addr_t fileoff; + lldb::addr_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; + + bool + operator==(const Segment& rhs) const + { + return name == rhs.name && vmaddr == rhs.vmaddr && vmsize == rhs.vmsize; + } + + 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 + llvm::MachO::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) + uint32_t load_stop_id; // The process stop ID that the sections for this image were loaded + + DYLDImageInfo() : + address(LLDB_INVALID_ADDRESS), + slide(0), + mod_date(0), + file_spec(), + uuid(), + header(), + segments(), + load_stop_id(0) + { + } + + void + Clear(bool load_cmd_data_only) + { + if (!load_cmd_data_only) + { + address = LLDB_INVALID_ADDRESS; + slide = 0; + mod_date = 0; + file_spec.Clear(); + ::memset (&header, 0, sizeof(header)); + } + uuid.Clear(); + segments.clear(); + load_stop_id = 0; + } + + 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(); + } + + uint32_t + GetAddressByteSize () + { + if (header.cputype) + { + if (header.cputype & llvm::MachO::CPU_ARCH_ABI64) + return 8; + else + return 4; + } + return 0; + } + + lldb::ByteOrder + GetByteOrder(); + + lldb_private::ArchSpec + GetArchitecture () const + { + return lldb_private::ArchSpec (lldb_private::eArchTypeMachO, header.cputype, header.cpusubtype); + } + + 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, + DYLDImageInfo& dylib_info, + lldb_private::FileSpec *lc_id_dylinker); + + bool + UpdateImageLoadAddress(lldb_private::Module *module, + DYLDImageInfo& info); + + bool + UnloadImageLoadAddress (lldb_private::Module *module, + DYLDImageInfo& info); + + lldb::ModuleSP + FindTargetModuleForDYLDImageInfo (DYLDImageInfo &image_info, + bool can_create, + bool *did_create_ptr); + + DYLDImageInfo * + GetImageInfo (lldb_private::Module *module); + + bool + NeedToLocateDYLD () const; + + bool + SetNotificationBreakpoint (); + + // There is a little tricky bit where you might initially attach while dyld is updating + // the all_image_infos, and you can't read the infos, so you have to continue and pick it + // up when you hit the update breakpoint. At that point, you need to run this initialize + // function, but when you do it that way you DON'T need to do the extra work you would at + // the breakpoint. + // So this function will only do actual work if the image infos haven't been read yet. + // If it does do any work, then it will return true, and false otherwise. That way you can + // call it in the breakpoint action, and if it returns true you're done. + bool + InitializeFromAllImageInfos (); + + bool + ReadAllImageInfosStructure (); + + bool + AddModulesUsingInfosFromDebugserver (lldb_private::StructuredData::ObjectSP image_details, DYLDImageInfo::collection &image_infos); + + bool + AddModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count); + + bool + AddModulesUsingImageInfos (DYLDImageInfo::collection &image_infos); + + bool + RemoveModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count); + + void + UpdateImageInfosHeaderAndLoadCommands(DYLDImageInfo::collection &image_infos, + uint32_t infos_count, + bool update_executable); + + bool + ReadImageInfos (lldb::addr_t image_infos_addr, + uint32_t image_infos_count, + DYLDImageInfo::collection &image_infos); + + + DYLDImageInfo m_dyld; // Info about the current dyld being used + lldb::ModuleWP m_dyld_module_wp; + lldb::addr_t m_dyld_all_image_infos_addr; + DYLDAllImageInfos m_dyld_all_image_infos; + uint32_t m_dyld_all_image_infos_stop_id; + lldb::user_id_t m_break_id; + DYLDImageInfo::collection m_dyld_image_infos; // Current shared libraries information + uint32_t m_dyld_image_infos_stop_id; // The process stop ID that "m_dyld_image_infos" is valid for + mutable lldb_private::Mutex m_mutex; + lldb_private::Process::Notifications m_notification_callbacks; + bool m_process_image_addr_is_all_images_infos; + +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderMacOSXDYLD); +}; + +#endif // liblldb_DynamicLoaderMacOSXDYLD_h_ diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/Makefile b/source/Plugins/DynamicLoader/MacOSX-DYLD/Makefile new file mode 100644 index 000000000000..ffac3b457179 --- /dev/null +++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Disassembler/llvm/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDynamicLoaderMacOSXDYLD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/CMakeLists.txt b/source/Plugins/DynamicLoader/POSIX-DYLD/CMakeLists.txt new file mode 100644 index 000000000000..b302997794a0 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginDynamicLoaderPosixDYLD + AuxVector.cpp + DYLDRendezvous.cpp + DynamicLoaderPOSIXDYLD.cpp + ) diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile b/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile new file mode 100644 index 000000000000..1c56366015d6 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/POSIX-DYLD/Makefile ----*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDynamicLoaderPosixDYLD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/Static/CMakeLists.txt b/source/Plugins/DynamicLoader/Static/CMakeLists.txt new file mode 100644 index 000000000000..274f6bac3697 --- /dev/null +++ b/source/Plugins/DynamicLoader/Static/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDynamicLoaderStatic + DynamicLoaderStatic.cpp + ) diff --git a/source/Plugins/DynamicLoader/Static/Makefile b/source/Plugins/DynamicLoader/Static/Makefile new file mode 100644 index 000000000000..63972dfc5512 --- /dev/null +++ b/source/Plugins/DynamicLoader/Static/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/Static/Makefile --------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDynamicLoaderStatic +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/DynamicLoader/Windows-DYLD/CMakeLists.txt b/source/Plugins/DynamicLoader/Windows-DYLD/CMakeLists.txt new file mode 100644 index 000000000000..ee768057bcd5 --- /dev/null +++ b/source/Plugins/DynamicLoader/Windows-DYLD/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginDynamicLoaderWindowsDYLD + DynamicLoaderWindowsDYLD.cpp + ) diff --git a/source/Plugins/DynamicLoader/Windows-DYLD/Makefile b/source/Plugins/DynamicLoader/Windows-DYLD/Makefile new file mode 100644 index 000000000000..bf62aee30b2b --- /dev/null +++ b/source/Plugins/DynamicLoader/Windows-DYLD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/Windows-DYLD/Makefile --*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDynamicLoaderWindowsDYLD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ExpressionParser/CMakeLists.txt b/source/Plugins/ExpressionParser/CMakeLists.txt new file mode 100644 index 000000000000..dc0540ad30a1 --- /dev/null +++ b/source/Plugins/ExpressionParser/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(Clang) +add_subdirectory(Go) diff --git a/source/Plugins/ExpressionParser/Clang/CMakeLists.txt b/source/Plugins/ExpressionParser/Clang/CMakeLists.txt new file mode 100644 index 000000000000..e18dde6b700e --- /dev/null +++ b/source/Plugins/ExpressionParser/Clang/CMakeLists.txt @@ -0,0 +1,15 @@ +add_lldb_library(lldbPluginExpressionParserClang + ASTDumper.cpp + ASTResultSynthesizer.cpp + ASTStructExtractor.cpp + ClangASTSource.cpp + ClangExpressionDeclMap.cpp + ClangExpressionParser.cpp + ClangExpressionVariable.cpp + ClangFunctionCaller.cpp + ClangModulesDeclVendor.cpp + ClangPersistentVariables.cpp + ClangUserExpression.cpp + ClangUtilityFunction.cpp + IRForTarget.cpp + ) diff --git a/source/Plugins/ExpressionParser/Clang/Makefile b/source/Plugins/ExpressionParser/Clang/Makefile new file mode 100644 index 000000000000..eb592daabb48 --- /dev/null +++ b/source/Plugins/ExpressionParser/Clang/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ExpressionParser/Clang ---------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginExpressionParserClang +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ExpressionParser/Go/CMakeLists.txt b/source/Plugins/ExpressionParser/Go/CMakeLists.txt new file mode 100644 index 000000000000..f59f51e76489 --- /dev/null +++ b/source/Plugins/ExpressionParser/Go/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginExpressionParserGo + GoLexer.cpp + GoParser.cpp + GoUserExpression.cpp + ) diff --git a/source/Plugins/ExpressionParser/Go/Makefile b/source/Plugins/ExpressionParser/Go/Makefile new file mode 100644 index 000000000000..c5bd7fb2857e --- /dev/null +++ b/source/Plugins/ExpressionParser/Go/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ExpressionParser/Clang ---------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginExpressionParserGo +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Instruction/ARM/CMakeLists.txt b/source/Plugins/Instruction/ARM/CMakeLists.txt new file mode 100644 index 000000000000..dc547a57306b --- /dev/null +++ b/source/Plugins/Instruction/ARM/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginInstructionARM + EmulateInstructionARM.cpp + EmulationStateARM.cpp + ) diff --git a/source/Plugins/Instruction/ARM/Makefile b/source/Plugins/Instruction/ARM/Makefile new file mode 100644 index 000000000000..31a233b0b374 --- /dev/null +++ b/source/Plugins/Instruction/ARM/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Instruction/ARM/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginInstructionARM +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Instruction/ARM64/CMakeLists.txt b/source/Plugins/Instruction/ARM64/CMakeLists.txt new file mode 100644 index 000000000000..9f8ee0848984 --- /dev/null +++ b/source/Plugins/Instruction/ARM64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginInstructionARM64 + EmulateInstructionARM64.cpp + ) diff --git a/source/Plugins/Instruction/ARM64/Makefile b/source/Plugins/Instruction/ARM64/Makefile new file mode 100644 index 000000000000..8f60ce6dcd47 --- /dev/null +++ b/source/Plugins/Instruction/ARM64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Instruction/ARM/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginInstructionARM64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Instruction/CMakeLists.txt b/source/Plugins/Instruction/CMakeLists.txt new file mode 100644 index 000000000000..78f2f64cf1ab --- /dev/null +++ b/source/Plugins/Instruction/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(ARM) +add_subdirectory(ARM64) +add_subdirectory(MIPS) +add_subdirectory(MIPS64) diff --git a/source/Plugins/Instruction/MIPS/CMakeLists.txt b/source/Plugins/Instruction/MIPS/CMakeLists.txt new file mode 100644 index 000000000000..dc6707336939 --- /dev/null +++ b/source/Plugins/Instruction/MIPS/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginInstructionMIPS + EmulateInstructionMIPS.cpp + ) diff --git a/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp b/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp index d6485f686e2c..a71fca7c5c3a 100644 --- a/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp +++ b/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp @@ -591,45 +591,45 @@ EmulateInstructionMIPS::GetOpcodeForInstruction (const char *op_name) //---------------------------------------------------------------------- // Branch instructions //---------------------------------------------------------------------- - { "BEQ", &EmulateInstructionMIPS::Emulate_BEQ, "BEQ rs,rt,offset" }, - { "BNE", &EmulateInstructionMIPS::Emulate_BNE, "BNE rs,rt,offset" }, - { "BEQL", &EmulateInstructionMIPS::Emulate_BEQL, "BEQL rs,rt,offset" }, - { "BNEL", &EmulateInstructionMIPS::Emulate_BNEL, "BNEL rs,rt,offset" }, - { "BGEZALL", &EmulateInstructionMIPS::Emulate_BGEZALL, "BGEZALL rt,offset" }, + { "BEQ", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQ rs,rt,offset" }, + { "BNE", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNE rs,rt,offset" }, + { "BEQL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQL rs,rt,offset" }, + { "BNEL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNEL rs,rt,offset" }, + { "BGEZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BGEZALL rt,offset" }, { "BAL", &EmulateInstructionMIPS::Emulate_BAL, "BAL offset" }, - { "BGEZAL", &EmulateInstructionMIPS::Emulate_BGEZAL, "BGEZAL rs,offset" }, + { "BGEZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BGEZAL rs,offset" }, { "BALC", &EmulateInstructionMIPS::Emulate_BALC, "BALC offset" }, { "BC", &EmulateInstructionMIPS::Emulate_BC, "BC offset" }, - { "BGEZ", &EmulateInstructionMIPS::Emulate_BGEZ, "BGEZ rs,offset" }, - { "BLEZALC", &EmulateInstructionMIPS::Emulate_BLEZALC, "BLEZALC rs,offset" }, - { "BGEZALC", &EmulateInstructionMIPS::Emulate_BGEZALC, "BGEZALC rs,offset" }, - { "BLTZALC", &EmulateInstructionMIPS::Emulate_BLTZALC, "BLTZALC rs,offset" }, - { "BGTZALC", &EmulateInstructionMIPS::Emulate_BGTZALC, "BGTZALC rs,offset" }, - { "BEQZALC", &EmulateInstructionMIPS::Emulate_BEQZALC, "BEQZALC rs,offset" }, - { "BNEZALC", &EmulateInstructionMIPS::Emulate_BNEZALC, "BNEZALC rs,offset" }, - { "BEQC", &EmulateInstructionMIPS::Emulate_BEQC, "BEQC rs,rt,offset" }, - { "BNEC", &EmulateInstructionMIPS::Emulate_BNEC, "BNEC rs,rt,offset" }, - { "BLTC", &EmulateInstructionMIPS::Emulate_BLTC, "BLTC rs,rt,offset" }, - { "BGEC", &EmulateInstructionMIPS::Emulate_BGEC, "BGEC rs,rt,offset" }, - { "BLTUC", &EmulateInstructionMIPS::Emulate_BLTUC, "BLTUC rs,rt,offset" }, - { "BGEUC", &EmulateInstructionMIPS::Emulate_BGEUC, "BGEUC rs,rt,offset" }, - { "BLTZC", &EmulateInstructionMIPS::Emulate_BLTZC, "BLTZC rt,offset" }, - { "BLEZC", &EmulateInstructionMIPS::Emulate_BLEZC, "BLEZC rt,offset" }, - { "BGEZC", &EmulateInstructionMIPS::Emulate_BGEZC, "BGEZC rt,offset" }, - { "BGTZC", &EmulateInstructionMIPS::Emulate_BGTZC, "BGTZC rt,offset" }, - { "BEQZC", &EmulateInstructionMIPS::Emulate_BEQZC, "BEQZC rt,offset" }, - { "BNEZC", &EmulateInstructionMIPS::Emulate_BNEZC, "BNEZC rt,offset" }, - { "BGEZL", &EmulateInstructionMIPS::Emulate_BGEZL, "BGEZL rt,offset" }, - { "BGTZ", &EmulateInstructionMIPS::Emulate_BGTZ, "BGTZ rt,offset" }, - { "BGTZL", &EmulateInstructionMIPS::Emulate_BGTZL, "BGTZL rt,offset" }, - { "BLEZ", &EmulateInstructionMIPS::Emulate_BLEZ, "BLEZ rt,offset" }, - { "BLEZL", &EmulateInstructionMIPS::Emulate_BLEZL, "BLEZL rt,offset" }, - { "BLTZ", &EmulateInstructionMIPS::Emulate_BLTZ, "BLTZ rt,offset" }, - { "BLTZAL", &EmulateInstructionMIPS::Emulate_BLTZAL, "BLTZAL rt,offset" }, - { "BLTZALL", &EmulateInstructionMIPS::Emulate_BLTZALL, "BLTZALL rt,offset" }, - { "BLTZL", &EmulateInstructionMIPS::Emulate_BLTZL, "BLTZL rt,offset" }, - { "BOVC", &EmulateInstructionMIPS::Emulate_BOVC, "BOVC rs,rt,offset" }, - { "BNVC", &EmulateInstructionMIPS::Emulate_BNVC, "BNVC rs,rt,offset" }, + { "BGEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZ rs,offset" }, + { "BLEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BLEZALC rs,offset" }, + { "BGEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BGEZALC rs,offset" }, + { "BLTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BLTZALC rs,offset" }, + { "BGTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BGTZALC rs,offset" }, + { "BEQZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BEQZALC rs,offset" }, + { "BNEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,"BNEZALC rs,offset" }, + { "BEQC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BEQC rs,rt,offset" }, + { "BNEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BNEC rs,rt,offset" }, + { "BLTC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BLTC rs,rt,offset" }, + { "BGEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BGEC rs,rt,offset" }, + { "BLTUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BLTUC rs,rt,offset" }, + { "BGEUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BGEUC rs,rt,offset" }, + { "BLTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLTZC rt,offset" }, + { "BLEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLEZC rt,offset" }, + { "BGEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGEZC rt,offset" }, + { "BGTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGTZC rt,offset" }, + { "BEQZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BEQZC rt,offset" }, + { "BNEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BNEZC rt,offset" }, + { "BGEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZL rt,offset" }, + { "BGTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZ rt,offset" }, + { "BGTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZL rt,offset" }, + { "BLEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZ rt,offset" }, + { "BLEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZL rt,offset" }, + { "BLTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZ rt,offset" }, + { "BLTZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BLTZAL rt,offset" }, + { "BLTZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link, "BLTZALL rt,offset" }, + { "BLTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZL rt,offset" }, + { "BOVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BOVC rs,rt,offset" }, + { "BNVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C, "BNVC rs,rt,offset" }, { "J", &EmulateInstructionMIPS::Emulate_J, "J target" }, { "JAL", &EmulateInstructionMIPS::Emulate_JAL, "JAL target" }, { "JALX", &EmulateInstructionMIPS::Emulate_JAL, "JALX target" }, @@ -639,16 +639,16 @@ EmulateInstructionMIPS::GetOpcodeForInstruction (const char *op_name) { "JIC", &EmulateInstructionMIPS::Emulate_JIC, "JIC rt,offset" }, { "JR", &EmulateInstructionMIPS::Emulate_JR, "JR target" }, { "JR_HB", &EmulateInstructionMIPS::Emulate_JR, "JR.HB target" }, - { "BC1F", &EmulateInstructionMIPS::Emulate_BC1F, "BC1F cc, offset" }, - { "BC1T", &EmulateInstructionMIPS::Emulate_BC1T, "BC1T cc, offset" }, - { "BC1FL", &EmulateInstructionMIPS::Emulate_BC1FL, "BC1FL cc, offset" }, - { "BC1TL", &EmulateInstructionMIPS::Emulate_BC1TL, "BC1TL cc, offset" }, + { "BC1F", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1F cc, offset" }, + { "BC1T", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1T cc, offset" }, + { "BC1FL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1FL cc, offset" }, + { "BC1TL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1TL cc, offset" }, { "BC1EQZ", &EmulateInstructionMIPS::Emulate_BC1EQZ, "BC1EQZ ft, offset" }, { "BC1NEZ", &EmulateInstructionMIPS::Emulate_BC1NEZ, "BC1NEZ ft, offset" }, - { "BC1ANY2F", &EmulateInstructionMIPS::Emulate_BC1ANY2F, "BC1ANY2F cc, offset" }, - { "BC1ANY2T", &EmulateInstructionMIPS::Emulate_BC1ANY2T, "BC1ANY2T cc, offset" }, - { "BC1ANY4F", &EmulateInstructionMIPS::Emulate_BC1ANY4F, "BC1ANY4F cc, offset" }, - { "BC1ANY4T", &EmulateInstructionMIPS::Emulate_BC1ANY4T, "BC1ANY4T cc, offset" }, + { "BC1ANY2F", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY2F cc, offset" }, + { "BC1ANY2T", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY2T cc, offset" }, + { "BC1ANY4F", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY4F cc, offset" }, + { "BC1ANY4T", &EmulateInstructionMIPS::Emulate_3D_branch, "BC1ANY4T cc, offset" }, { "BNZ_B", &EmulateInstructionMIPS::Emulate_BNZB, "BNZ.b wt,s16" }, { "BNZ_H", &EmulateInstructionMIPS::Emulate_BNZH, "BNZ.h wt,s16" }, { "BNZ_W", &EmulateInstructionMIPS::Emulate_BNZW, "BNZ.w wt,s16" }, @@ -1387,19 +1387,26 @@ EmulateInstructionMIPS::Emulate_JRADDIUSP (llvm::MCInst& insn) return true; } +static int +IsAdd64bitOverflow (int32_t a, int32_t b) +{ + int32_t r = (uint32_t) a + (uint32_t) b; + return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); +} + +/* + Emulate below MIPS branch instructions. + BEQ, BNE : Branch on condition + BEQL, BNEL : Branch likely +*/ bool -EmulateInstructionMIPS::Emulate_BEQ (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_BXX_3ops (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; + int32_t offset, pc, target = 0, rs_val, rt_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); - /* - * BEQ rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); @@ -1416,13 +1423,399 @@ EmulateInstructionMIPS::Emulate_BEQ (llvm::MCInst& insn) if (!success) return false; - if (rs_val == rt_val) - target = pc + offset; - else - target = pc + 8; + if (!strcasecmp (op_name, "BEQ") || + !strcasecmp (op_name, "BEQL")) + { + if (rs_val == rt_val) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BNE") || + !strcasecmp (op_name, "BNEL")) + { + if (rs_val != rt_val) + target = pc + offset; + else + target = pc + 8; + } Context context; context.type = eContextRelativeBranchImmediate; + context.SetImmediate (offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BEQC, BNEC, BLTC, BGEC, BLTUC, BGEUC, BOVC, BNVC: Compact branch instructions with no delay slot +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_3ops_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs, rt; + int32_t offset, pc, target = 0, rs_val, rt_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); + offset = insn.getOperand(2).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BEQC")) + { + if (rs_val == rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEC")) + { + if (rs_val != rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTC")) + { + if (rs_val < rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEC")) + { + if (rs_val >= rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTUC")) + { + if (rs_val < rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEUC")) + { + if ((uint32_t)rs_val >= (uint32_t)rt_val) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BOVC")) + { + if (IsAdd64bitOverflow (rs_val, rt_val)) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNVC")) + { + if (!IsAdd64bitOverflow (rs_val, rt_val)) + target = pc + 4 + offset; + else + target = pc + 4; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (current_inst_size + offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +/* + Emulate below MIPS conditional branch and link instructions. + BLEZALC, BGEZALC, BLTZALC, BGTZALC, BEQZALC, BNEZALC : Compact branches +*/ +bool +EmulateInstructionMIPS::Emulate_Bcond_Link_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLEZALC")) + { + if (rs_val <= 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEZALC")) + { + if (rs_val >= 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLTZALC")) + { + if (rs_val < 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGTZALC")) + { + if (rs_val > 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BEQZALC")) + { + if (rs_val == 0) + target = pc + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEZALC")) + { + if (rs_val != 0) + target = pc + offset; + else + target = pc + 4; + } + + Context context; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) + return false; + + return true; +} + +/* + Emulate below MIPS Non-Compact conditional branch and link instructions. + BLTZAL, BGEZAL : + BLTZALL, BGEZALL : Branch likely +*/ +bool +EmulateInstructionMIPS::Emulate_Bcond_Link (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZAL") || + !strcasecmp (op_name, "BLTZALL")) + { + if ((int32_t) rs_val < 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGEZAL") || + !strcasecmp (op_name, "BGEZALL")) + { + if ((int32_t) rs_val >= 0) + target = pc + offset; + else + target = pc + 8; + } + + Context context; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BLTZL, BGEZL, BGTZL, BLEZL : Branch likely + BLTZ, BGEZ, BGTZ, BLEZ : Non-compact branches +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_2ops (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZL") || + !strcasecmp (op_name, "BLTZ")) + { + if (rs_val < 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGEZL") || + !strcasecmp (op_name, "BGEZ")) + { + if (rs_val >= 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BGTZL") || + !strcasecmp (op_name, "BGTZ")) + { + if (rs_val > 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BLEZL") || + !strcasecmp (op_name, "BLEZ")) + { + if (rs_val <= 0) + target = pc + offset; + else + target = pc + 8; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) + return false; + + return true; +} + +/* + Emulate below MIPS branch instructions. + BLTZC, BLEZC, BGEZC, BGTZC, BEQZC, BNEZC : Compact Branches +*/ +bool +EmulateInstructionMIPS::Emulate_BXX_2ops_C (llvm::MCInst& insn) +{ + bool success = false; + uint32_t rs; + int32_t offset, pc, target = 0; + int32_t rs_val; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize(); + + rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); + offset = insn.getOperand(1).getImm(); + + pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); + if (!success) + return false; + + rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); + if (!success) + return false; + + if (!strcasecmp (op_name, "BLTZC")) + { + if (rs_val < 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BLEZC")) + { + if (rs_val <= 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGEZC")) + { + if (rs_val >= 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BGTZC")) + { + if (rs_val > 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BEQZC")) + { + if (rs_val == 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + else if (!strcasecmp (op_name, "BNEZC")) + { + if (rs_val != 0) + target = pc + 4 + offset; + else + target = pc + 4; + } + + Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (current_inst_size + offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) return false; @@ -1687,450 +2080,6 @@ EmulateInstructionMIPS::Emulate_JALRS (llvm::MCInst& insn) return true; } -bool -EmulateInstructionMIPS::Emulate_BNE (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNE rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BEQL rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val == rt_val) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNEL rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZL rs, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZL rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZL rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZL rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZ rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZ rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZ rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZALL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZALL rt, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - bool EmulateInstructionMIPS::Emulate_BAL (llvm::MCInst& insn) { @@ -2193,430 +2142,6 @@ EmulateInstructionMIPS::Emulate_BALC (llvm::MCInst& insn) return true; } -bool -EmulateInstructionMIPS::Emulate_BGEZAL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZAL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if ((int32_t) rs_val >= 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZAL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZAL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if ((int32_t) rs_val < 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZALL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZALL rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 8 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 8)) - return false; - - return true; -} - - -bool -EmulateInstructionMIPS::Emulate_BLEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] <= 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val <= 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] < 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val < 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] > 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val > 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BEQZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] == 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val == 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEZALC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BNEZALC rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] != 0) - * if condition then - * RA = PC + 4 - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val != 0) - target = pc + offset; - else - target = pc + 4; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_ra_mips, pc + 4)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZ (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target, rs_val; - - /* - * BGEZ rs,offset - * offset = sign_ext (offset << 2) - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + offset - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - Context context; - - if (rs_val >= 0) - target = pc + offset; - else - target = pc + 8; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - bool EmulateInstructionMIPS::Emulate_BC (llvm::MCInst& insn) { @@ -2644,597 +2169,6 @@ EmulateInstructionMIPS::Emulate_BC (llvm::MCInst& insn) return true; } -bool -EmulateInstructionMIPS::Emulate_BEQC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BEQC rs, rt, offset - * condition <- (GPR[rs] = GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val == rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target, rs_val, rt_val; - - /* - * BNEC rs, rt, offset - * condition <- (GPR[rs] != GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val != rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BLTC rs, rt, offset - * condition <- (GPR[rs] < GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val < rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BGEC rs, rt, offset - * condition <- (GPR[rs] > GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val > rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTUC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - uint32_t rs_val, rt_val; - - /* - * BLTUC rs, rt, offset - * condition <- (GPR[rs] < GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val < rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEUC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - uint32_t rs_val, rt_val; - - /* - * BGEUC rs, rt, offset - * condition <- (GPR[rs] > GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (rs_val > rt_val) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLTZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLTZC rs, offset - * condition <- (GPR[rs] < 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val < 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BLEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BLEZC rs, offset - * condition <- (GPR[rs] <= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val <= 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGEZC rs, offset - * condition <- (GPR[rs] >= 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val >= 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BGTZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - int32_t rs_val; - - /* - * BGTZC rs, offset - * condition <- (GPR[rs] > 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val > 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BEQZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - uint32_t rs_val; - - /* - * BEQZC rs, offset - * condition <- (GPR[rs] = 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val == 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNEZC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs; - int32_t offset, pc, target; - uint32_t rs_val; - - /* - * BNEZC rs, offset - * condition <- (GPR[rs] != 0) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - if (rs_val != 0) - target = pc + 4 + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -static int -IsAdd64bitOverflow (int32_t a, int32_t b) -{ - int32_t r = (uint32_t) a + (uint32_t) b; - return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); -} - -bool -EmulateInstructionMIPS::Emulate_BOVC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BOVC rs, rt, offset - * condition <- overflow(GPR[rs] + GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (IsAdd64bitOverflow (rs_val, rt_val)) - target = pc + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BNVC (llvm::MCInst& insn) -{ - bool success = false; - uint32_t rs, rt; - int32_t offset, pc, target; - int32_t rs_val, rt_val; - - /* - * BNVC rs, rt, offset - * condition <- overflow(GPR[rs] + GPR[rt]) - * if condition then - * PC = PC + sign_ext (offset << 2) - */ - rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - offset = insn.getOperand(2).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success); - if (!success) - return false; - - rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success); - if (!success) - return false; - - if (! IsAdd64bitOverflow (rs_val, rt_val)) - target = pc + offset; - else - target = pc + 4; - - Context context; - context.type = eContextRelativeBranchImmediate; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - bool EmulateInstructionMIPS::Emulate_J (llvm::MCInst& insn) { @@ -3419,20 +2353,19 @@ EmulateInstructionMIPS::Emulate_JR (llvm::MCInst& insn) return true; } +/* + Emulate Branch on FP True/False + BC1F, BC1FL : Branch on FP False (L stands for branch likely) + BC1T, BC1TL : Branch on FP True (L stands for branch likely) +*/ bool -EmulateInstructionMIPS::Emulate_BC1F (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_FP_branch (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1F cc, offset - * condition <- (FPConditionCode(cc) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ + int32_t pc, offset, target = 0; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); @@ -3446,135 +2379,23 @@ EmulateInstructionMIPS::Emulate_BC1F (llvm::MCInst& insn) /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) == 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1T (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1T cc, offset - * condition <- (FPConditionCode(cc) != 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) != 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1FL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1F cc, offset - * condition <- (FPConditionCode(cc) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) == 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1TL (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1T cc, offset - * condition <- (FPConditionCode(cc) != 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - if ((fcsr & (1 << cc)) != 0) - target = pc + offset; - else - target = pc + 8; /* skip delay slot */ - + if (!strcasecmp (op_name, "BC1F") || + !strcasecmp (op_name, "BC1FL")) + { + if ((fcsr & (1 << cc)) == 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1T") || + !strcasecmp (op_name, "BC1TL")) + { + if ((fcsr & (1 << cc)) != 0) + target = pc + offset; + else + target = pc + 8; + } Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) @@ -3661,21 +2482,19 @@ EmulateInstructionMIPS::Emulate_BC1NEZ (llvm::MCInst& insn) return true; } +/* + Emulate MIPS-3D Branch instructions + BC1ANY2F, BC1ANY2T : Branch on Any of Two Floating Point Condition Codes False/True + BC1ANY4F, BC1ANY4T : Branch on Any of Four Floating Point Condition Codes False/True +*/ bool -EmulateInstructionMIPS::Emulate_BC1ANY2F (llvm::MCInst& insn) +EmulateInstructionMIPS::Emulate_3D_branch (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY2F cc, offset - * condition <- (FPConditionCode(cc) == 0 - * || FPConditionCode(cc+1) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ + int32_t pc, offset, target = 0; + const char *op_name = m_insn_info->getName (insn.getOpcode ()); + cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); @@ -3690,145 +2509,38 @@ EmulateInstructionMIPS::Emulate_BC1ANY2F (llvm::MCInst& insn) /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - /* if any one bit is 0 */ - if (((fcsr >> cc) & 3) != 3) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1ANY2T (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY2T cc, offset - * condition <- (FPConditionCode(cc) == 1 - * || FPConditionCode(cc+1) == 1) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 1 */ - if (((fcsr >> cc) & 3) != 0) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1ANY4F (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY4F cc, offset - * condition <- (FPConditionCode(cc) == 0 - * || FPConditionCode(cc+1) == 0) - * || FPConditionCode(cc+2) == 0) - * || FPConditionCode(cc+3) == 0) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 0 */ - if (((fcsr >> cc) & 0xf) != 0xf) - target = pc + offset; - else - target = pc + 8; - - Context context; - - if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) - return false; - - return true; -} - -bool -EmulateInstructionMIPS::Emulate_BC1ANY4T (llvm::MCInst& insn) -{ - bool success = false; - uint32_t cc, fcsr; - int32_t target, pc, offset; - - /* - * BC1ANY4T cc, offset - * condition <- (FPConditionCode(cc) == 1 - * || FPConditionCode(cc+1) == 1) - * || FPConditionCode(cc+2) == 1) - * || FPConditionCode(cc+3) == 1) - * if condition then - * offset = sign_ext (offset) - * PC = PC + offset - */ - cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); - offset = insn.getOperand(1).getImm(); - - pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips, 0, &success); - if (!success) - return false; - - fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success); - if (!success) - return false; - - /* fcsr[23], fcsr[25-31] are vaild condition bits */ - fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); - - /* if any one bit is 1 */ - if (((fcsr >> cc) & 0xf) != 0) - target = pc + offset; - else - target = pc + 8; - + if (!strcasecmp (op_name, "BC1ANY2F")) + { + /* if any one bit is 0 */ + if (((fcsr >> cc) & 3) != 3) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY2T")) + { + /* if any one bit is 1 */ + if (((fcsr >> cc) & 3) != 0) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY4F")) + { + /* if any one bit is 0 */ + if (((fcsr >> cc) & 0xf) != 0xf) + target = pc + offset; + else + target = pc + 8; + } + else if (!strcasecmp (op_name, "BC1ANY4T")) + { + /* if any one bit is 1 */ + if (((fcsr >> cc) & 0xf) != 0) + target = pc + offset; + else + target = pc + 8; + } Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc_mips, target)) diff --git a/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h b/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h index e1340f983278..892de054e2ae 100644 --- a/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h +++ b/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h @@ -160,122 +160,38 @@ class EmulateInstructionMIPS : public lldb_private::EmulateInstruction Emulate_LDST_Reg (llvm::MCInst& insn); bool - Emulate_BEQ (llvm::MCInst& insn); + Emulate_BXX_3ops (llvm::MCInst& insn); bool - Emulate_BNE (llvm::MCInst& insn); + Emulate_BXX_3ops_C (llvm::MCInst& insn); bool - Emulate_BEQL (llvm::MCInst& insn); + Emulate_BXX_2ops (llvm::MCInst& insn); bool - Emulate_BNEL (llvm::MCInst& insn); + Emulate_BXX_2ops_C (llvm::MCInst& insn); bool - Emulate_BGEZALL (llvm::MCInst& insn); + Emulate_Bcond_Link_C (llvm::MCInst& insn); + + bool + Emulate_Bcond_Link (llvm::MCInst& insn); + + bool + Emulate_FP_branch (llvm::MCInst& insn); + + bool + Emulate_3D_branch (llvm::MCInst& insn); bool Emulate_BAL (llvm::MCInst& insn); - bool - Emulate_BGEZAL (llvm::MCInst& insn); - bool Emulate_BALC (llvm::MCInst& insn); bool Emulate_BC (llvm::MCInst& insn); - bool - Emulate_BGEZ (llvm::MCInst& insn); - - bool - Emulate_BLEZALC (llvm::MCInst& insn); - - bool - Emulate_BGEZALC (llvm::MCInst& insn); - - bool - Emulate_BLTZALC (llvm::MCInst& insn); - - bool - Emulate_BGTZALC (llvm::MCInst& insn); - - bool - Emulate_BEQZALC (llvm::MCInst& insn); - - bool - Emulate_BNEZALC (llvm::MCInst& insn); - - bool - Emulate_BEQC (llvm::MCInst& insn); - - bool - Emulate_BNEC (llvm::MCInst& insn); - - bool - Emulate_BLTC (llvm::MCInst& insn); - - bool - Emulate_BGEC (llvm::MCInst& insn); - - bool - Emulate_BLTUC (llvm::MCInst& insn); - - bool - Emulate_BGEUC (llvm::MCInst& insn); - - bool - Emulate_BLTZC (llvm::MCInst& insn); - - bool - Emulate_BLEZC (llvm::MCInst& insn); - - bool - Emulate_BGEZC (llvm::MCInst& insn); - - bool - Emulate_BGTZC (llvm::MCInst& insn); - - bool - Emulate_BEQZC (llvm::MCInst& insn); - - bool - Emulate_BNEZC (llvm::MCInst& insn); - - bool - Emulate_BGEZL (llvm::MCInst& insn); - - bool - Emulate_BGTZ (llvm::MCInst& insn); - - bool - Emulate_BGTZL (llvm::MCInst& insn); - - bool - Emulate_BLEZ (llvm::MCInst& insn); - - bool - Emulate_BLEZL (llvm::MCInst& insn); - - bool - Emulate_BLTZ (llvm::MCInst& insn); - - bool - Emulate_BLTZAL (llvm::MCInst& insn); - - bool - Emulate_BLTZALL (llvm::MCInst& insn); - - bool - Emulate_BLTZL (llvm::MCInst& insn); - - bool - Emulate_BOVC (llvm::MCInst& insn); - - bool - Emulate_BNVC (llvm::MCInst& insn); - bool Emulate_J (llvm::MCInst& insn); @@ -294,36 +210,12 @@ class EmulateInstructionMIPS : public lldb_private::EmulateInstruction bool Emulate_JR (llvm::MCInst& insn); - bool - Emulate_BC1F (llvm::MCInst& insn); - - bool - Emulate_BC1T (llvm::MCInst& insn); - - bool - Emulate_BC1FL (llvm::MCInst& insn); - - bool - Emulate_BC1TL (llvm::MCInst& insn); - bool Emulate_BC1EQZ (llvm::MCInst& insn); bool Emulate_BC1NEZ (llvm::MCInst& insn); - bool - Emulate_BC1ANY2F (llvm::MCInst& insn); - - bool - Emulate_BC1ANY2T (llvm::MCInst& insn); - - bool - Emulate_BC1ANY4F (llvm::MCInst& insn); - - bool - Emulate_BC1ANY4T (llvm::MCInst& insn); - bool Emulate_BNZB (llvm::MCInst& insn); diff --git a/source/Plugins/Instruction/MIPS/Makefile b/source/Plugins/Instruction/MIPS/Makefile new file mode 100644 index 000000000000..e9cef4ba0cf7 --- /dev/null +++ b/source/Plugins/Instruction/MIPS/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Instruction/MIPS/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginInstructionMIPS +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Instruction/MIPS64/CMakeLists.txt b/source/Plugins/Instruction/MIPS64/CMakeLists.txt new file mode 100644 index 000000000000..25c919c4edb6 --- /dev/null +++ b/source/Plugins/Instruction/MIPS64/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginInstructionMIPS64 + EmulateInstructionMIPS64.cpp + ) diff --git a/source/Plugins/Instruction/MIPS64/Makefile b/source/Plugins/Instruction/MIPS64/Makefile new file mode 100644 index 000000000000..7e5b339a359d --- /dev/null +++ b/source/Plugins/Instruction/MIPS64/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Instruction/MIPS64/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginInstructionMIPS64 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt new file mode 100644 index 000000000000..77e7d15caa7c --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginInstrumentationRuntimeAddressSanitizer + AddressSanitizerRuntime.cpp + ) diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile b/source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile new file mode 100644 index 000000000000..030aec17b026 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/InstrumentationRuntime/AddressSanitizer Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginInstrumentationRuntimeAddressSanitizer +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/InstrumentationRuntime/CMakeLists.txt b/source/Plugins/InstrumentationRuntime/CMakeLists.txt new file mode 100644 index 000000000000..8ee303b25359 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(AddressSanitizer) diff --git a/source/Plugins/JITLoader/CMakeLists.txt b/source/Plugins/JITLoader/CMakeLists.txt new file mode 100644 index 000000000000..e52230199109 --- /dev/null +++ b/source/Plugins/JITLoader/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(GDB) diff --git a/source/Plugins/JITLoader/GDB/CMakeLists.txt b/source/Plugins/JITLoader/GDB/CMakeLists.txt new file mode 100644 index 000000000000..bcf714dca593 --- /dev/null +++ b/source/Plugins/JITLoader/GDB/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(.) + +add_lldb_library(lldbPluginJITLoaderGDB + JITLoaderGDB.cpp + ) + diff --git a/source/Plugins/JITLoader/GDB/Makefile b/source/Plugins/JITLoader/GDB/Makefile new file mode 100644 index 000000000000..cd5404ffca18 --- /dev/null +++ b/source/Plugins/JITLoader/GDB/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/JITLoader/GDB/Makefile ---------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginJITLoaderGDB +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Language/CMakeLists.txt b/source/Plugins/Language/CMakeLists.txt new file mode 100644 index 000000000000..60b3da2406b6 --- /dev/null +++ b/source/Plugins/Language/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(CPlusPlus) +add_subdirectory(Go) +add_subdirectory(ObjC) +add_subdirectory(ObjCPlusPlus) diff --git a/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/source/Plugins/Language/CPlusPlus/CMakeLists.txt new file mode 100644 index 000000000000..0b0f0f451a8d --- /dev/null +++ b/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginCPlusPlusLanguage + CPlusPlusLanguage.cpp + CxxStringTypes.cpp + LibCxx.cpp + LibCxxInitializerList.cpp + LibCxxList.cpp + LibCxxMap.cpp + LibCxxUnorderedMap.cpp + LibCxxVector.cpp + LibStdcpp.cpp +) diff --git a/source/Plugins/Language/CPlusPlus/Makefile b/source/Plugins/Language/CPlusPlus/Makefile new file mode 100644 index 000000000000..2cb0dcf638b3 --- /dev/null +++ b/source/Plugins/Language/CPlusPlus/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Language/CPlusPlus -------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginCPlusPlusLanguage +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Language/Go/CMakeLists.txt b/source/Plugins/Language/Go/CMakeLists.txt new file mode 100644 index 000000000000..f3a9c12b7523 --- /dev/null +++ b/source/Plugins/Language/Go/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginGoLanguage + GoLanguage.cpp + GoFormatterFunctions.cpp +) diff --git a/source/Plugins/Language/Go/Makefile b/source/Plugins/Language/Go/Makefile new file mode 100644 index 000000000000..3ea09f6c538f --- /dev/null +++ b/source/Plugins/Language/Go/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Language/Go -------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginGoLanguage +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Language/ObjC/CMakeLists.txt b/source/Plugins/Language/ObjC/CMakeLists.txt new file mode 100644 index 000000000000..5c480a1aed11 --- /dev/null +++ b/source/Plugins/Language/ObjC/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginObjCLanguage + ObjCLanguage.cpp + CF.cpp + Cocoa.cpp + CoreMedia.cpp + NSArray.cpp + NSDictionary.cpp + NSError.cpp + NSException.cpp + NSIndexPath.cpp + NSSet.cpp + NSString.cpp +) diff --git a/source/Plugins/Language/ObjC/Makefile b/source/Plugins/Language/ObjC/Makefile new file mode 100644 index 000000000000..58c9e58f2bc6 --- /dev/null +++ b/source/Plugins/Language/ObjC/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Language/ObjC ------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginObjCLanguage +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt b/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt new file mode 100644 index 000000000000..ef80af74107b --- /dev/null +++ b/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjCPlusPlusLanguage + ObjCPlusPlusLanguage.cpp +) diff --git a/source/Plugins/Language/ObjCPlusPlus/Makefile b/source/Plugins/Language/ObjCPlusPlus/Makefile new file mode 100644 index 000000000000..74e1a14bcf4d --- /dev/null +++ b/source/Plugins/Language/ObjCPlusPlus/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Language/ObjCPlusPlus ----------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginObjCPlusPlusLanguage +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/CMakeLists.txt b/source/Plugins/LanguageRuntime/CMakeLists.txt new file mode 100644 index 000000000000..66b17a4af229 --- /dev/null +++ b/source/Plugins/LanguageRuntime/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(CPlusPlus) +add_subdirectory(ObjC) +add_subdirectory(Go) +add_subdirectory(RenderScript) diff --git a/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt b/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt new file mode 100644 index 000000000000..26c68c60b019 --- /dev/null +++ b/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(ItaniumABI) +#add_subdirectory(MicrosoftABI) diff --git a/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt new file mode 100644 index 000000000000..d25215d0a613 --- /dev/null +++ b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginCXXItaniumABI + ItaniumABILanguageRuntime.cpp + ) diff --git a/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/Makefile b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/Makefile new file mode 100644 index 000000000000..ac87437f9d2a --- /dev/null +++ b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/LangRuntime/C++/ItaniumABI/Makefile --*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../../.. +LIBRARYNAME := lldbPluginCXXItaniumABI +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/Go/CMakeLists.txt b/source/Plugins/LanguageRuntime/Go/CMakeLists.txt new file mode 100644 index 000000000000..7c9166a94cbe --- /dev/null +++ b/source/Plugins/LanguageRuntime/Go/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_NO_RTTI 1) + +add_lldb_library(lldbPluginLanguageRuntimeGo + GoLanguageRuntime.cpp + ) diff --git a/source/Plugins/LanguageRuntime/Go/Makefile b/source/Plugins/LanguageRuntime/Go/Makefile new file mode 100644 index 000000000000..1c8114e421c1 --- /dev/null +++ b/source/Plugins/LanguageRuntime/Go/Makefile @@ -0,0 +1,14 @@ +##===- Source/Plugins/LangRuntime/Go/Makefile ----*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginLanguageRuntimeGo +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/CMakeLists.txt b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/CMakeLists.txt new file mode 100644 index 000000000000..13fde3d181fa --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginAppleObjCRuntime + AppleObjCRuntime.cpp + AppleObjCRuntimeV1.cpp + AppleObjCRuntimeV2.cpp + AppleObjCTrampolineHandler.cpp + AppleObjCDeclVendor.cpp + AppleThreadPlanStepThroughObjCTrampoline.cpp + AppleObjCClassDescriptorV2.cpp + AppleObjCTypeEncodingParser.cpp + ) diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/Makefile b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/Makefile new file mode 100644 index 000000000000..485fe75b3f88 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/LangRuntime/ObjC/AppleRT/Makefile ----*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../../.. +LIBRARYNAME := lldbPluginAppleObjCRuntime +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt b/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt new file mode 100644 index 000000000000..af13dc6a144d --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(AppleObjCRuntime) diff --git a/source/Plugins/LanguageRuntime/RenderScript/CMakeLists.txt b/source/Plugins/LanguageRuntime/RenderScript/CMakeLists.txt new file mode 100644 index 000000000000..d944d76c4957 --- /dev/null +++ b/source/Plugins/LanguageRuntime/RenderScript/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(RenderScriptRuntime) diff --git a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/CMakeLists.txt b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/CMakeLists.txt new file mode 100644 index 000000000000..d93e9fa2a291 --- /dev/null +++ b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginRenderScriptRuntime + RenderScriptRuntime.cpp + ) diff --git a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/Makefile b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/Makefile new file mode 100644 index 000000000000..eeb50ae3fcae --- /dev/null +++ b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/Makefile @@ -0,0 +1,14 @@ +##===- Source/Plugins/LangRuntime/RenderScript/RenderScriptRuntime/Makefile ----*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../../.. +LIBRARYNAME := lldbPluginRenderScriptRuntime +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp index 149244df30c2..5d82ded50882 100644 --- a/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp +++ b/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp @@ -452,7 +452,7 @@ RenderScriptRuntime::GetPluginNameStatic() return g_name; } -RenderScriptRuntime::ModuleKind +RenderScriptRuntime::ModuleKind RenderScriptRuntime::GetModuleKind(const lldb::ModuleSP &module_sp) { if (module_sp) @@ -493,7 +493,7 @@ RenderScriptRuntime::IsRenderScriptModule(const lldb::ModuleSP &module_sp) return GetModuleKind(module_sp) != eModuleKindIgnored; } -void +void RenderScriptRuntime::ModulesDidLoad(const ModuleList &module_list ) { Mutex::Locker locker (module_list.GetMutex ()); @@ -640,11 +640,11 @@ RenderScriptRuntime::HookCallback(void *baton, StoppointCallbackContext *ctx, ll RenderScriptRuntime *lang_rt = (RenderScriptRuntime *)context.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); lang_rt->HookCallback(hook_info, context); - + return false; } -void +void RenderScriptRuntime::HookCallback(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); @@ -652,7 +652,7 @@ RenderScriptRuntime::HookCallback(RuntimeHook* hook_info, ExecutionContext& cont if (log) log->Printf ("RenderScriptRuntime::HookCallback - '%s' .", hook_info->defn->name); - if (hook_info->defn->grabber) + if (hook_info->defn->grabber) { (this->*(hook_info->defn->grabber))(hook_info, context); } @@ -706,7 +706,6 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 *data = result; success = true; } - break; } case llvm::Triple::ArchType::x86_64: @@ -741,6 +740,7 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 case llvm::Triple::ArchType::arm: { // arm 32 bit + // first 4 arguments are passed via registers if (arg < 4) { const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg); @@ -760,18 +760,19 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = (arg-4) * sizeof(uint32_t); - process->ReadMemory(sp + offset, &data, sizeof(uint32_t), error); - if (error.Fail()) + uint32_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading ARM stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } case llvm::Triple::ArchType::aarch64: @@ -803,8 +804,8 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 } case llvm::Triple::ArchType::mipsel: { - // read from the registers + // first 4 arguments are passed in registers if (arg < 4){ const RegisterInfo* rArg = reg_ctx->GetRegisterInfoAtIndex(arg + 4); RegisterValue rVal; @@ -818,26 +819,25 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 if (log) log->Printf("RenderScriptRuntime::GetArgSimple() - Mips - Error while reading the argument #%d", arg); } - } - - // read from the stack + // arguments > 4 are read from the stack else { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = arg * sizeof(uint32_t); - process->ReadMemory(sp + offset, &data, sizeof(uint32_t), error); - if (error.Fail()) + uint32_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - error reading Mips stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } case llvm::Triple::ArchType::mips64el: @@ -858,24 +858,24 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 log->Printf("RenderScriptRuntime::GetArgSimple - Mips64 - Error reading the argument #%d", arg); } } - - // read from the stack + // arguments > 8 are read from the stack else { uint64_t sp = reg_ctx->GetSP(); uint32_t offset = (arg - 8) * sizeof(uint64_t); - process->ReadMemory(sp + offset, &data, sizeof(uint64_t), error); - if (error.Fail()) + uint64_t value = 0; + size_t bytes_read = process->ReadMemory(sp + offset, &value, sizeof(value), error); + if (error.Fail() || bytes_read != sizeof(value)) { if (log) log->Printf("RenderScriptRuntime::GetArgSimple - Mips64 - Error reading Mips64 stack: %s.", error.AsCString()); } else { + *data = value; success = true; } } - break; } default: @@ -883,7 +883,6 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 // invalid architecture if (log) log->Printf("RenderScriptRuntime::GetArgSimple - Architecture not supported"); - } } @@ -895,11 +894,11 @@ RenderScriptRuntime::GetArgSimple(ExecutionContext &context, uint32_t arg, uint6 return success; } -void +void RenderScriptRuntime::CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); - + //Context, Script, int, data, length uint64_t rs_context_u64 = 0U; @@ -921,7 +920,7 @@ RenderScriptRuntime::CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionConte log->Printf("RenderScriptRuntime::CaptureSetGlobalVar1 - Error while reading the function parameters"); return; } - + if (log) { log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - 0x%" PRIx64 ",0x%" PRIx64 " slot %" PRIu64 " = 0x%" PRIx64 ":%" PRIu64 "bytes.", @@ -934,18 +933,18 @@ RenderScriptRuntime::CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionConte if (rs_id_u64 < rsm->m_globals.size()) { auto rsg = rsm->m_globals[rs_id_u64]; - log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - Setting of '%s' within '%s' inferred", rsg.m_name.AsCString(), + log->Printf ("RenderScriptRuntime::CaptureSetGlobalVar1 - Setting of '%s' within '%s' inferred", rsg.m_name.AsCString(), rsm->m_module->GetFileSpec().GetFilename().AsCString()); } } } } -void +void RenderScriptRuntime::CaptureAllocationInit1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); - + //Context, Alloc, bool uint64_t rs_context_u64 = 0U; @@ -1009,7 +1008,7 @@ RenderScriptRuntime::CaptureAllocationDestroy(RuntimeHook* hook_info, ExecutionC log->Printf("RenderScriptRuntime::CaptureAllocationDestroy - Couldn't find destroyed allocation"); } -void +void RenderScriptRuntime::CaptureScriptInit1(RuntimeHook* hook_info, ExecutionContext& context) { Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); @@ -1045,16 +1044,16 @@ RenderScriptRuntime::CaptureScriptInit1(RuntimeHook* hook_info, ExecutionContext { if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading resname: %s.", error.AsCString()); - + } process->ReadCStringFromMemory((lldb::addr_t)rs_cachedirptr_u64, cachedir, error); if (error.Fail()) { if (log) - log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading cachedir: %s.", error.AsCString()); + log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - error reading cachedir: %s.", error.AsCString()); } - + if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - 0x%" PRIx64 ",0x%" PRIx64 " => '%s' at '%s' .", rs_context_u64, rs_script_u64, resname.c_str(), cachedir.c_str()); @@ -1077,7 +1076,7 @@ RenderScriptRuntime::CaptureScriptInit1(RuntimeHook* hook_info, ExecutionContext if (log) log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - '%s' tagged with context 0x%" PRIx64 " and script 0x%" PRIx64 ".", strm.GetData(), rs_context_u64, rs_script_u64); - } + } else if (log) { log->Printf ("RenderScriptRuntime::CaptureScriptInit1 - resource name invalid, Script not tagged"); @@ -1134,7 +1133,7 @@ RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind) if (addr == LLDB_INVALID_ADDRESS) { if (log) - log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to resolve the address of hook function '%s' with symbol '%s'.", + log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Unable to resolve the address of hook function '%s' with symbol '%s'.", hook_defn->name, symbol_name); continue; } @@ -1152,7 +1151,7 @@ RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind) m_runtimeHooks[addr] = hook; if (log) { - log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Successfully hooked '%s' in '%s' version %" PRIu64 " at 0x%" PRIx64 ".", + log->Printf ("RenderScriptRuntime::LoadRuntimeHooks - Successfully hooked '%s' in '%s' version %" PRIu64 " at 0x%" PRIx64 ".", hook_defn->name, module->GetFileSpec().GetFilename().AsCString(), (uint64_t)hook_defn->version, (uint64_t)addr); } } @@ -2231,7 +2230,7 @@ RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id, const // Write allocation data to file num_bytes = static_cast(*alloc->size.get()); if (log) - log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, buffer.get()); + log->Printf("RenderScriptRuntime::SaveAllocation - Writing 0x%" PRIx64 " bytes from %p", (uint64_t) num_bytes, (void*) buffer.get()); err = file.Write(buffer.get(), num_bytes); if (!err.Success()) @@ -2299,7 +2298,7 @@ RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp) } case eModuleKindLibRS: { - if (!m_libRS) + if (!m_libRS) { m_libRS = module_sp; static ConstString gDbgPresentStr("gDebuggerPresent"); @@ -2334,7 +2333,7 @@ RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp) break; } if (module_loaded) - Update(); + Update(); return module_loaded; } return false; @@ -2408,7 +2407,7 @@ RSModuleDescriptor::ParseRSInfo() m_kernels.push_back(RSKernelDescriptor(this, name, slot)); } } - } + } else if (sscanf(line.c_str(), "pragmaCount: %u", &numDefns) == 1) { char name[MAXLINE]; @@ -2417,7 +2416,7 @@ RSModuleDescriptor::ParseRSInfo() { name[0] = '\0'; value[0] = '\0'; - if (sscanf(info_lines[++offset].c_str(), "%s - %s", &name[0], &value[0]) != 0 + if (sscanf(info_lines[++offset].c_str(), "%s - %s", &name[0], &value[0]) != 0 && (name[0] != '\0')) { m_pragmas[std::string(name)] = value; @@ -2466,7 +2465,7 @@ RenderScriptRuntime::Status(Stream &strm) const strm.Printf("CPU Reference Implementation discovered."); strm.EOL(); } - + if (m_runtimeHooks.size()) { strm.Printf("Runtime functions hooked:"); @@ -2476,7 +2475,7 @@ RenderScriptRuntime::Status(Stream &strm) const strm.Indent(b.second->defn->name); strm.EOL(); } - } + } else { strm.Printf("Runtime is not hooked."); @@ -2484,7 +2483,7 @@ RenderScriptRuntime::Status(Stream &strm) const } } -void +void RenderScriptRuntime::DumpContexts(Stream &strm) const { strm.Printf("Inferred RenderScript Contexts:"); @@ -2519,7 +2518,7 @@ RenderScriptRuntime::DumpContexts(Stream &strm) const strm.IndentLess(); } -void +void RenderScriptRuntime::DumpKernels(Stream &strm) const { strm.Printf("RenderScript Kernels:"); diff --git a/source/Plugins/Makefile b/source/Plugins/Makefile new file mode 100644 index 000000000000..931f459a26b7 --- /dev/null +++ b/source/Plugins/Makefile @@ -0,0 +1,67 @@ +##===- source/Plugins/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. + +include $(LLDB_LEVEL)/../../Makefile.config + + +PARALLEL_DIRS := ABI/MacOSX-arm ABI/MacOSX-arm64 ABI/MacOSX-i386 ABI/SysV-i386 ABI/SysV-x86_64 \ + ABI/SysV-arm ABI/SysV-arm64 ABI/SysV-hexagon ABI/SysV-ppc ABI/SysV-ppc64 \ + ABI/SysV-mips ABI/SysV-mips64 Disassembler/llvm \ + ObjectContainer/BSD-Archive ObjectFile/ELF ObjectFile/PECOFF \ + ObjectContainer/Universal-Mach-O ObjectFile/Mach-O \ + ObjectFile/JIT SymbolFile/DWARF SymbolFile/Symtab Process/Utility \ + DynamicLoader/Static Platform Process/elf-core Process/gdb-remote \ + Instruction/ARM Instruction/ARM64 Instruction/MIPS Instruction/MIPS64 \ + UnwindAssembly/InstEmulation UnwindAssembly/x86 \ + LanguageRuntime/CPlusPlus/ItaniumABI \ + LanguageRuntime/ObjC/AppleObjCRuntime \ + LanguageRuntime/Go/ \ + LanguageRuntime/RenderScript/RenderScriptRuntime \ + Language/CPlusPlus \ + Language/Go \ + Language/ObjC \ + Language/ObjCPlusPlus \ + DynamicLoader/POSIX-DYLD \ + DynamicLoader/Hexagon-DYLD \ + DynamicLoader/MacOSX-DYLD \ + DynamicLoader/Windows-DYLD \ + JITLoader/GDB \ + ExpressionParser/Clang \ + ExpressionParser/Go \ + OperatingSystem/Go \ + OperatingSystem/Python \ + SystemRuntime/MacOSX \ + SymbolVendor/ELF \ + MemoryHistory/asan \ + InstrumentationRuntime/AddressSanitizer \ + ScriptInterpreter/Python ScriptInterpreter/None + +ifeq ($(HOST_OS),Darwin) +PARALLEL_DIRS += Process/MacOSX-Kernel +PARALLEL_DIRS += DynamicLoader/Darwin-Kernel +PARALLEL_DIRS += SymbolVendor/MacOSX +#PARALLEL_DIRS += Process/MacOSX-User +PARALLEL_DIRS += Process/mach-core +endif + +ifeq ($(HOST_OS),Linux) +PARALLEL_DIRS += Process/Linux Process/POSIX +endif + +ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD)) +PARALLEL_DIRS += Process/FreeBSD Process/POSIX +endif + +ifeq ($(HOST_OS),NetBSD) +PARALLEL_DIRS += Process/POSIX +endif + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/MemoryHistory/CMakeLists.txt b/source/Plugins/MemoryHistory/CMakeLists.txt new file mode 100644 index 000000000000..113f06362578 --- /dev/null +++ b/source/Plugins/MemoryHistory/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(asan) diff --git a/source/Plugins/MemoryHistory/asan/CMakeLists.txt b/source/Plugins/MemoryHistory/asan/CMakeLists.txt new file mode 100644 index 000000000000..8bfe95e3680e --- /dev/null +++ b/source/Plugins/MemoryHistory/asan/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginMemoryHistoryASan + MemoryHistoryASan.cpp + ) diff --git a/source/Plugins/MemoryHistory/asan/Makefile b/source/Plugins/MemoryHistory/asan/Makefile new file mode 100644 index 000000000000..86de6aba3638 --- /dev/null +++ b/source/Plugins/MemoryHistory/asan/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/MemoryHistory/asan/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginMemoryHistoryASan +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectContainer/BSD-Archive/CMakeLists.txt b/source/Plugins/ObjectContainer/BSD-Archive/CMakeLists.txt new file mode 100644 index 000000000000..68ebe885e3e9 --- /dev/null +++ b/source/Plugins/ObjectContainer/BSD-Archive/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjectContainerBSDArchive + ObjectContainerBSDArchive.cpp + ) diff --git a/source/Plugins/ObjectContainer/BSD-Archive/Makefile b/source/Plugins/ObjectContainer/BSD-Archive/Makefile new file mode 100644 index 000000000000..00c5911ea95f --- /dev/null +++ b/source/Plugins/ObjectContainer/BSD-Archive/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectContainer/BSD-Archive/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginObjectContainerBSDArchive +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectContainer/CMakeLists.txt b/source/Plugins/ObjectContainer/CMakeLists.txt new file mode 100644 index 000000000000..5dcef91f07d2 --- /dev/null +++ b/source/Plugins/ObjectContainer/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(BSD-Archive) +add_subdirectory(Universal-Mach-O) diff --git a/source/Plugins/ObjectContainer/Universal-Mach-O/CMakeLists.txt b/source/Plugins/ObjectContainer/Universal-Mach-O/CMakeLists.txt new file mode 100644 index 000000000000..b4553868bf94 --- /dev/null +++ b/source/Plugins/ObjectContainer/Universal-Mach-O/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjectContainerMachOArchive + ObjectContainerUniversalMachO.cpp + ) diff --git a/source/Plugins/ObjectContainer/Universal-Mach-O/Makefile b/source/Plugins/ObjectContainer/Universal-Mach-O/Makefile new file mode 100644 index 000000000000..957753527d24 --- /dev/null +++ b/source/Plugins/ObjectContainer/Universal-Mach-O/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectContainer/Universal-Mach-O/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginObjectContainerMachOArchive +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp b/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp new file mode 100644 index 000000000000..7497b987eba4 --- /dev/null +++ b/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp @@ -0,0 +1,310 @@ +//===-- 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/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +void +ObjectContainerUniversalMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + GetModuleSpecifications); +} + +void +ObjectContainerUniversalMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +ObjectContainerUniversalMachO::GetPluginNameStatic() +{ + static ConstString g_name("mach-o"); + return g_name; +} + +const char * +ObjectContainerUniversalMachO::GetPluginDescriptionStatic() +{ + return "Universal mach-o object container reader."; +} + + +ObjectContainer * +ObjectContainerUniversalMachO::CreateInstance +( + const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length +) +{ + // We get data when we aren't trying to look for cached container information, + // so only try and look for an architecture slice if we get data + if (data_sp) + { + DataExtractor data; + data.SetData (data_sp, data_offset, length); + if (ObjectContainerUniversalMachO::MagicBytesMatch(data)) + { + std::unique_ptr container_ap(new ObjectContainerUniversalMachO (module_sp, data_sp, data_offset, file, file_offset, length)); + if (container_ap->ParseHeader()) + { + return container_ap.release(); + } + } + } + return NULL; +} + +bool +ObjectContainerUniversalMachO::MagicBytesMatch (const DataExtractor &data) +{ + lldb::offset_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return magic == FAT_MAGIC || magic == FAT_CIGAM; +} + +ObjectContainerUniversalMachO::ObjectContainerUniversalMachO +( + const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length +) : + ObjectContainer (module_sp, file, file_offset, length, data_sp, data_offset), + m_header(), + m_fat_archs() +{ + memset(&m_header, 0, sizeof(m_header)); +} + + +ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO() +{ +} + +bool +ObjectContainerUniversalMachO::ParseHeader () +{ + bool success = ParseHeader (m_data, m_header, m_fat_archs); + // We no longer need any data, we parsed all we needed to parse + // and cached it in m_header and m_fat_archs + m_data.Clear(); + return success; +} + +bool +ObjectContainerUniversalMachO::ParseHeader (lldb_private::DataExtractor &data, + llvm::MachO::fat_header &header, + std::vector &fat_archs) +{ + bool success = false; + // 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. + // Universal mach-o files always have their headers in big endian. + lldb::offset_t offset = 0; + data.SetByteOrder (eByteOrderBig); + header.magic = data.GetU32(&offset); + fat_archs.clear(); + + if (header.magic == FAT_MAGIC) + { + + data.SetAddressByteSize(4); + + header.nfat_arch = data.GetU32(&offset); + + // Now we should have enough data for all of the fat headers, so lets index + // them so we know how many architectures that this universal binary contains. + uint32_t arch_idx = 0; + for (arch_idx = 0; arch_idx < header.nfat_arch; ++arch_idx) + { + if (data.ValidOffsetForDataOfSize(offset, sizeof(fat_arch))) + { + fat_arch arch; + if (data.GetU32(&offset, &arch, sizeof(fat_arch)/sizeof(uint32_t))) + fat_archs.push_back(arch); + } + } + success = true; + } + else + { + memset(&header, 0, sizeof(header)); + } + return success; +} + +void +ObjectContainerUniversalMachO::Dump (Stream *s) const +{ + s->Printf("%p: ", static_cast(this)); + s->Indent(); + const size_t num_archs = GetNumArchitectures(); + const size_t num_objects = GetNumObjects(); + s->Printf("ObjectContainerUniversalMachO, num_archs = %lu, num_objects = %lu", num_archs, num_objects); + uint32_t i; + ArchSpec arch; + s->IndentMore(); + for (i=0; iIndent(); + GetArchitectureAtIndex(i, arch); + s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName()); + } + for (i=0; iIndent(); + s->Printf("object[%u] = %s\n", i, 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.SetArchitecture (eArchTypeMachO, m_fat_archs[idx].cputype, m_fat_archs[idx].cpusubtype); + return true; + } + return false; +} + +ObjectFileSP +ObjectContainerUniversalMachO::GetObjectFile (const FileSpec *file) +{ + uint32_t arch_idx = 0; + ArchSpec arch; + // If the module hasn't specified an architecture yet, set it to the default + // architecture: + ModuleSP module_sp (GetModule()); + if (module_sp) + { + if (!module_sp->GetArchitecture().IsValid()) + { + arch = Target::GetDefaultArchitecture (); + if (!arch.IsValid()) + arch.SetTriple (LLDB_ARCH_DEFAULT); + } + else + arch = module_sp->GetArchitecture(); + + ArchSpec curr_arch; + // First, try to find an exact match for the Arch of the Target. + for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) + { + if (GetArchitectureAtIndex (arch_idx, curr_arch) && arch.IsExactMatch(curr_arch)) + break; + } + + // Failing an exact match, try to find a compatible Arch of the Target. + if (arch_idx >= m_header.nfat_arch) + { + for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) + { + if (GetArchitectureAtIndex (arch_idx, curr_arch) && arch.IsCompatibleMatch(curr_arch)) + break; + } + } + + if (arch_idx < m_header.nfat_arch) + { + DataBufferSP data_sp; + lldb::offset_t data_offset = 0; + return ObjectFile::FindPlugin (module_sp, + file, + m_offset + m_fat_archs[arch_idx].offset, + m_fat_archs[arch_idx].size, + data_sp, + data_offset); + } + } + return ObjectFileSP(); +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ObjectContainerUniversalMachO::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectContainerUniversalMachO::GetPluginVersion() +{ + return 1; +} + + +size_t +ObjectContainerUniversalMachO::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t file_size, + lldb_private::ModuleSpecList &specs) +{ + const size_t initial_count = specs.GetSize(); + + DataExtractor data; + data.SetData (data_sp, data_offset, data_sp->GetByteSize()); + + if (ObjectContainerUniversalMachO::MagicBytesMatch(data)) + { + llvm::MachO::fat_header header; + std::vector fat_archs; + if (ParseHeader (data, header, fat_archs)) + { + for (const llvm::MachO::fat_arch &fat_arch : fat_archs) + { + const lldb::offset_t slice_file_offset = fat_arch.offset + file_offset; + if (fat_arch.offset < file_size && file_size > slice_file_offset) + { + ObjectFile::GetModuleSpecifications (file, + slice_file_offset, + file_size - slice_file_offset, + specs); + } + } + } + } + return specs.GetSize() - initial_count; +} + diff --git a/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h b/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h new file mode 100644 index 000000000000..162402e4b2ba --- /dev/null +++ b/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h @@ -0,0 +1,105 @@ +//===-- 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_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Utility/SafeMachO.h" + +class ObjectContainerUniversalMachO : + public lldb_private::ObjectContainer +{ +public: + ObjectContainerUniversalMachO(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, + lldb::offset_t length); + + ~ObjectContainerUniversalMachO() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectContainer * + CreateInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, + lldb::offset_t length); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool + MagicBytesMatch (const lldb_private::DataExtractor &data); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + bool + ParseHeader() override; + + void + Dump(lldb_private::Stream *s) const override; + + size_t + GetNumArchitectures() const override; + + bool + GetArchitectureAtIndex(uint32_t cpu_idx, lldb_private::ArchSpec& arch) const override; + + lldb::ObjectFileSP + GetObjectFile(const lldb_private::FileSpec *file) override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + llvm::MachO::fat_header m_header; + std::vector m_fat_archs; + + static bool + ParseHeader (lldb_private::DataExtractor &data, + llvm::MachO::fat_header &header, + std::vector &fat_archs); +}; + +#endif // liblldb_ObjectContainerUniversalMachO_h_ diff --git a/source/Plugins/ObjectFile/CMakeLists.txt b/source/Plugins/ObjectFile/CMakeLists.txt new file mode 100644 index 000000000000..06aa01c4f004 --- /dev/null +++ b/source/Plugins/ObjectFile/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(ELF) +add_subdirectory(Mach-O) +add_subdirectory(PECOFF) +add_subdirectory(JIT) \ No newline at end of file diff --git a/source/Plugins/ObjectFile/ELF/CMakeLists.txt b/source/Plugins/ObjectFile/ELF/CMakeLists.txt new file mode 100644 index 000000000000..69ec80c62bf0 --- /dev/null +++ b/source/Plugins/ObjectFile/ELF/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginObjectFileELF + ELFHeader.cpp + ObjectFileELF.cpp + ) diff --git a/source/Plugins/ObjectFile/ELF/Makefile b/source/Plugins/ObjectFile/ELF/Makefile new file mode 100644 index 000000000000..470660bb7861 --- /dev/null +++ b/source/Plugins/ObjectFile/ELF/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectFile/ELF/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginObjectFileELF +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 44fe6615a361..1d63ced787e0 100644 --- a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -119,7 +119,7 @@ class ELFRelocation /// /// @param type Either DT_REL or DT_RELA. Any other value is invalid. ELFRelocation(unsigned type); - + ~ELFRelocation(); bool @@ -156,7 +156,7 @@ class ELFRelocation }; ELFRelocation::ELFRelocation(unsigned type) -{ +{ if (type == DT_REL || type == SHT_REL) reloc = new ELFRel(); else if (type == DT_RELA || type == SHT_RELA) @@ -172,7 +172,7 @@ ELFRelocation::~ELFRelocation() if (reloc.is()) delete reloc.get(); else - delete reloc.get(); + delete reloc.get(); } bool @@ -315,7 +315,7 @@ kalimbaVariantFromElfFlags(const elf::elf_word e_flags) kal_arch_variant = llvm::Triple::KalimbaSubArch_v5; break; default: - break; + break; } return kal_arch_variant; } @@ -470,9 +470,9 @@ ObjectFileELF::CreateInstance (const lldb::ModuleSP &module_sp, ObjectFile* -ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, - DataBufferSP& data_sp, - const lldb::ProcessSP &process_sp, +ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT)) @@ -561,7 +561,7 @@ calc_crc32(uint32_t crc, const void *buf, size_t size) 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d - }; + }; const uint8_t *p = (const uint8_t *)buf; crc = crc ^ ~0U; @@ -826,12 +826,12 @@ ObjectFileELF::GetPluginVersion() // ObjectFile protocol //------------------------------------------------------------------ -ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, +ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, DataBufferSP& data_sp, lldb::offset_t data_offset, - const FileSpec* file, + const FileSpec* file, lldb::offset_t file_offset, - lldb::offset_t length) : + lldb::offset_t length) : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), m_header(), m_uuid(), @@ -901,7 +901,7 @@ ObjectFileELF::SetLoadAddress (Target &target, if (header->p_type != PT_LOAD || header->p_offset != 0) continue; - + value = value - header->p_vaddr; found_offset = true; break; @@ -1176,7 +1176,7 @@ ObjectFileELF::GetImageInfoAddress(Target *target) } lldb_private::Address -ObjectFileELF::GetEntryPointAddress () +ObjectFileELF::GetEntryPointAddress () { if (m_entry_point_address.IsValid()) return m_entry_point_address; @@ -1187,7 +1187,7 @@ ObjectFileELF::GetEntryPointAddress () SectionList *section_list = GetSectionList(); addr_t offset = m_header.e_entry; - if (!section_list) + if (!section_list) m_entry_point_address.SetOffset(offset); else m_entry_point_address.ResolveAddressUsingFileSections(offset, section_list); @@ -1545,16 +1545,16 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, { switch (header.e_flags & llvm::ELF::EF_MIPS_ARCH_ASE) { - case llvm::ELF::EF_MIPS_MICROMIPS: - arch_spec.SetFlags (ArchSpec::eMIPSAse_micromips); + case llvm::ELF::EF_MIPS_MICROMIPS: + arch_spec.SetFlags (ArchSpec::eMIPSAse_micromips); break; - case llvm::ELF::EF_MIPS_ARCH_ASE_M16: - arch_spec.SetFlags (ArchSpec::eMIPSAse_mips16); + case llvm::ELF::EF_MIPS_ARCH_ASE_M16: + arch_spec.SetFlags (ArchSpec::eMIPSAse_mips16); break; - case llvm::ELF::EF_MIPS_ARCH_ASE_MDMX: - arch_spec.SetFlags (ArchSpec::eMIPSAse_mdmx); + case llvm::ELF::EF_MIPS_ARCH_ASE_MDMX: + arch_spec.SetFlags (ArchSpec::eMIPSAse_mdmx); break; - default: + default: break; } } @@ -1612,7 +1612,7 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, DataExtractor data; if (sheader.sh_type == SHT_MIPS_ABIFLAGS) { - + if (section_size && (data.SetData (object_data, sheader.sh_offset, section_size) == section_size)) { lldb::offset_t ase_offset = 12; // MIPS ABI Flags Version: 0 @@ -1621,12 +1621,12 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, } // Settings appropriate ArchSpec ABI Flags if (header.e_flags & llvm::ELF::EF_MIPS_ABI2) - { + { arch_flags |= lldb_private::ArchSpec::eMIPSABI_N32; } else if (header.e_flags & llvm::ELF::EF_MIPS_ABI_O32) { - arch_flags |= lldb_private::ArchSpec::eMIPSABI_O32; + arch_flags |= lldb_private::ArchSpec::eMIPSABI_O32; } arch_spec.SetFlags (arch_flags); } @@ -1700,7 +1700,7 @@ ObjectFileELF::GetProgramHeaderByIndex(lldb::user_id_t id) return NULL; } -DataExtractor +DataExtractor ObjectFileELF::GetSegmentDataByIndex(lldb::user_id_t id) { const elf::ELFProgramHeader *segment_header = GetProgramHeaderByIndex(id); @@ -1805,12 +1805,12 @@ ObjectFileELF::CreateSections(SectionList &unified_section_list) else if (name == g_sect_name_tdata) { sect_type = eSectionTypeData; - is_thread_specific = true; + is_thread_specific = true; } else if (name == g_sect_name_tbss) { - sect_type = eSectionTypeZeroFill; - is_thread_specific = true; + sect_type = eSectionTypeZeroFill; + is_thread_specific = true; } // .debug_abbrev – Abbreviations used in the .debug_info section // .debug_aranges – Lookup table for mapping addresses to compilation units @@ -1877,12 +1877,12 @@ ObjectFileELF::CreateSections(SectionList &unified_section_list) { // the kalimba toolchain assumes that ELF section names are free-form. It does // support linkscripts which (can) give rise to various arbitrarily named - // sections being "Code" or "Data". + // sections being "Code" or "Data". sect_type = kalimbaSectionType(m_header, header); } const uint32_t target_bytes_size = - (eSectionTypeData == sect_type || eSectionTypeZeroFill == sect_type) ? + (eSectionTypeData == sect_type || eSectionTypeZeroFill == sect_type) ? m_arch_spec.GetDataByteSize() : eSectionTypeCode == sect_type ? m_arch_spec.GetCodeByteSize() : 1; @@ -2022,7 +2022,7 @@ ObjectFileELF::ParseSymbols (Symtab *symtab, { if (symbol.Parse(symtab_data, &offset) == false) break; - + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); // No need to add non-section symbols that have no names @@ -2325,7 +2325,7 @@ ObjectFileELF::ParseSymbolTable(Symtab *symbol_table, user_id_t start_id, lldb_p user_id_t symtab_id = symtab->GetID(); const ELFSectionHeaderInfo *symtab_hdr = GetSectionHeaderByIndex(symtab_id); - assert(symtab_hdr->sh_type == SHT_SYMTAB || + assert(symtab_hdr->sh_type == SHT_SYMTAB || symtab_hdr->sh_type == SHT_DYNSYM); // sh_link: section header index of associated string table. @@ -2601,16 +2601,16 @@ ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, if (!rel_type) return 0; - return ParsePLTRelocations (symbol_table, - start_id, + return ParsePLTRelocations (symbol_table, + start_id, rel_type, - &m_header, - rel_hdr, - plt_hdr, + &m_header, + rel_hdr, + plt_hdr, sym_hdr, - plt_section_sp, - rel_data, - symtab_data, + plt_section_sp, + rel_data, + symtab_data, strtab_data); } @@ -2797,24 +2797,24 @@ ObjectFileELF::GetSymtab() // Synthesize trampoline symbols to help navigate the PLT. addr_t addr = symbol->d_ptr; Section *reloc_section = section_list->FindSectionContainingFileAddress(addr).get(); - if (reloc_section) + if (reloc_section) { user_id_t reloc_id = reloc_section->GetID(); const ELFSectionHeaderInfo *reloc_header = GetSectionHeaderByIndex(reloc_id); assert(reloc_header); - + if (m_symtab_ap == nullptr) m_symtab_ap.reset(new Symtab(reloc_section->GetObjectFile())); ParseTrampolineSymbols (m_symtab_ap.get(), symbol_id, reloc_header, reloc_id); } } - + // If we still don't have any symtab then create an empty instance to avoid do the section // lookup next time. if (m_symtab_ap == nullptr) m_symtab_ap.reset(new Symtab(this)); - + m_symtab_ap->CalculateSymbolSizes(); } @@ -3274,7 +3274,7 @@ ObjectFileELF::CalculateStrata() { switch (m_header.e_type) { - case llvm::ELF::ET_NONE: + case llvm::ELF::ET_NONE: // 0 - No file type return eStrataUnknown; @@ -3285,21 +3285,21 @@ ObjectFileELF::CalculateStrata() case llvm::ELF::ET_EXEC: // 2 - Executable file // TODO: is there any way to detect that an executable is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUser; case llvm::ELF::ET_DYN: // 3 - Shared object file // TODO: is there any way to detect that an shared library is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUnknown; case ET_CORE: // 4 - Core file // TODO: is there any way to detect that an core file is a kernel - // related executable by inspecting the program headers, section + // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUnknown; diff --git a/source/Plugins/ObjectFile/JIT/CMakeLists.txt b/source/Plugins/ObjectFile/JIT/CMakeLists.txt new file mode 100644 index 000000000000..979724bac5ad --- /dev/null +++ b/source/Plugins/ObjectFile/JIT/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjectFileJIT + ObjectFileJIT.cpp + ) diff --git a/source/Plugins/ObjectFile/JIT/Makefile b/source/Plugins/ObjectFile/JIT/Makefile new file mode 100644 index 000000000000..2af3521777a1 --- /dev/null +++ b/source/Plugins/ObjectFile/JIT/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectFile/JIT/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginObjectFileJIT +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectFile/Mach-O/CMakeLists.txt b/source/Plugins/ObjectFile/Mach-O/CMakeLists.txt new file mode 100644 index 000000000000..45d45860b9e5 --- /dev/null +++ b/source/Plugins/ObjectFile/Mach-O/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginObjectFileMachO + ObjectFileMachO.cpp + ) diff --git a/source/Plugins/ObjectFile/Mach-O/Makefile b/source/Plugins/ObjectFile/Mach-O/Makefile new file mode 100644 index 000000000000..2fab0238e411 --- /dev/null +++ b/source/Plugins/ObjectFile/Mach-O/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectFile/Mach-O/Makefile -----------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginObjectFileMachO +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp new file mode 100644 index 000000000000..9c1e17782508 --- /dev/null +++ b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -0,0 +1,6096 @@ +//===-- ObjectFileMachO.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/StringRef.h" + +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RangeMap.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/Host/Host.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadList.h" +#include "Plugins/Process/Utility/RegisterContextDarwin_arm.h" +#include "Plugins/Process/Utility/RegisterContextDarwin_arm64.h" +#include "Plugins/Process/Utility/RegisterContextDarwin_i386.h" +#include "Plugins/Process/Utility/RegisterContextDarwin_x86_64.h" + +#include "lldb/Utility/SafeMachO.h" + +#include "ObjectFileMachO.h" + +#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) +// GetLLDBSharedCacheUUID() needs to call dlsym() +#include +#endif + +#ifndef __APPLE__ +#include "Utility/UuidCompatibility.h" +#endif + +#define THUMB_ADDRESS_BIT_MASK 0xfffffffffffffffeull +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +// Some structure definitions needed for parsing the dyld shared cache files +// found on iOS devices. + +struct lldb_copy_dyld_cache_header_v1 +{ + char magic[16]; // e.g. "dyld_v0 i386", "dyld_v1 armv7", etc. + uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info + uint32_t mappingCount; // number of dyld_cache_mapping_info entries + uint32_t imagesOffset; + uint32_t imagesCount; + uint64_t dyldBaseAddress; + uint64_t codeSignatureOffset; + uint64_t codeSignatureSize; + uint64_t slideInfoOffset; + uint64_t slideInfoSize; + uint64_t localSymbolsOffset; + uint64_t localSymbolsSize; + uint8_t uuid[16]; // v1 and above, also recorded in dyld_all_image_infos v13 and later +}; + +struct lldb_copy_dyld_cache_mapping_info +{ + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint32_t maxProt; + uint32_t initProt; +}; + +struct lldb_copy_dyld_cache_local_symbols_info +{ + uint32_t nlistOffset; + uint32_t nlistCount; + uint32_t stringsOffset; + uint32_t stringsSize; + uint32_t entriesOffset; + uint32_t entriesCount; +}; +struct lldb_copy_dyld_cache_local_symbols_entry +{ + uint32_t dylibOffset; + uint32_t nlistStartIndex; + uint32_t nlistCount; +}; + + +class RegisterContextDarwin_x86_64_Mach : public RegisterContextDarwin_x86_64 +{ +public: + RegisterContextDarwin_x86_64_Mach (lldb_private::Thread &thread, const DataExtractor &data) : + RegisterContextDarwin_x86_64 (thread, 0) + { + SetRegisterDataFrom_LC_THREAD (data); + } + + void + InvalidateAllRegisters() override + { + // Do nothing... registers are always valid... + } + + void + SetRegisterDataFrom_LC_THREAD (const DataExtractor &data) + { + lldb::offset_t offset = 0; + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + bool done = false; + + while (!done) + { + int flavor = data.GetU32 (&offset); + if (flavor == 0) + done = true; + else + { + uint32_t i; + uint32_t count = data.GetU32 (&offset); + switch (flavor) + { + case GPRRegSet: + for (i=0; iGetRegisterInfoByName(name); + if (reg_info == NULL) + reg_info = reg_ctx->GetRegisterInfoByName(alt_name); + if (reg_info) + { + lldb_private::RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) + { + if (reg_info->byte_size >= reg_byte_size) + data.Write(reg_value.GetBytes(), reg_byte_size); + else + { + data.Write(reg_value.GetBytes(), reg_info->byte_size); + for (size_t i=0, n = reg_byte_size - reg_info->byte_size; iGetRegisterContext()); + if (reg_ctx_sp) + { + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + data.PutHex32 (GPRRegSet); // Flavor + data.PutHex32 (GPRWordCount); + WriteRegister (reg_ctx, "rax", NULL, 8, data); + WriteRegister (reg_ctx, "rbx", NULL, 8, data); + WriteRegister (reg_ctx, "rcx", NULL, 8, data); + WriteRegister (reg_ctx, "rdx", NULL, 8, data); + WriteRegister (reg_ctx, "rdi", NULL, 8, data); + WriteRegister (reg_ctx, "rsi", NULL, 8, data); + WriteRegister (reg_ctx, "rbp", NULL, 8, data); + WriteRegister (reg_ctx, "rsp", NULL, 8, data); + WriteRegister (reg_ctx, "r8", NULL, 8, data); + WriteRegister (reg_ctx, "r9", NULL, 8, data); + WriteRegister (reg_ctx, "r10", NULL, 8, data); + WriteRegister (reg_ctx, "r11", NULL, 8, data); + WriteRegister (reg_ctx, "r12", NULL, 8, data); + WriteRegister (reg_ctx, "r13", NULL, 8, data); + WriteRegister (reg_ctx, "r14", NULL, 8, data); + WriteRegister (reg_ctx, "r15", NULL, 8, data); + WriteRegister (reg_ctx, "rip", NULL, 8, data); + WriteRegister (reg_ctx, "rflags", NULL, 8, data); + WriteRegister (reg_ctx, "cs", NULL, 8, data); + WriteRegister (reg_ctx, "fs", NULL, 8, data); + WriteRegister (reg_ctx, "gs", NULL, 8, data); + +// // Write out the FPU registers +// const size_t fpu_byte_size = sizeof(FPU); +// size_t bytes_written = 0; +// data.PutHex32 (FPURegSet); +// data.PutHex32 (fpu_byte_size/sizeof(uint64_t)); +// bytes_written += data.PutHex32(0); // uint32_t pad[0] +// bytes_written += data.PutHex32(0); // uint32_t pad[1] +// bytes_written += WriteRegister (reg_ctx, "fcw", "fctrl", 2, data); // uint16_t fcw; // "fctrl" +// bytes_written += WriteRegister (reg_ctx, "fsw" , "fstat", 2, data); // uint16_t fsw; // "fstat" +// bytes_written += WriteRegister (reg_ctx, "ftw" , "ftag", 1, data); // uint8_t ftw; // "ftag" +// bytes_written += data.PutHex8 (0); // uint8_t pad1; +// bytes_written += WriteRegister (reg_ctx, "fop" , NULL, 2, data); // uint16_t fop; // "fop" +// bytes_written += WriteRegister (reg_ctx, "fioff", "ip", 4, data); // uint32_t ip; // "fioff" +// bytes_written += WriteRegister (reg_ctx, "fiseg", NULL, 2, data); // uint16_t cs; // "fiseg" +// bytes_written += data.PutHex16 (0); // uint16_t pad2; +// bytes_written += WriteRegister (reg_ctx, "dp", "fooff" , 4, data); // uint32_t dp; // "fooff" +// bytes_written += WriteRegister (reg_ctx, "foseg", NULL, 2, data); // uint16_t ds; // "foseg" +// bytes_written += data.PutHex16 (0); // uint16_t pad3; +// bytes_written += WriteRegister (reg_ctx, "mxcsr", NULL, 4, data); // uint32_t mxcsr; +// bytes_written += WriteRegister (reg_ctx, "mxcsrmask", NULL, 4, data);// uint32_t mxcsrmask; +// bytes_written += WriteRegister (reg_ctx, "stmm0", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm1", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm2", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm3", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm4", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm5", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm6", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "stmm7", NULL, sizeof(MMSReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm0" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm1" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm2" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm3" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm4" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm5" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm6" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm7" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm8" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm9" , NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm10", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm11", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm12", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm13", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm14", NULL, sizeof(XMMReg), data); +// bytes_written += WriteRegister (reg_ctx, "xmm15", NULL, sizeof(XMMReg), data); +// +// // Fill rest with zeros +// for (size_t i=0, n = fpu_byte_size - bytes_written; iGetRegisterInfoByName(name); + if (reg_info == NULL) + reg_info = reg_ctx->GetRegisterInfoByName(alt_name); + if (reg_info) + { + lldb_private::RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) + { + if (reg_info->byte_size >= reg_byte_size) + data.Write(reg_value.GetBytes(), reg_byte_size); + else + { + data.Write(reg_value.GetBytes(), reg_info->byte_size); + for (size_t i=0, n = reg_byte_size - reg_info->byte_size; iGetRegisterContext()); + if (reg_ctx_sp) + { + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + data.PutHex32 (GPRRegSet); // Flavor + data.PutHex32 (GPRWordCount); + WriteRegister (reg_ctx, "eax", NULL, 4, data); + WriteRegister (reg_ctx, "ebx", NULL, 4, data); + WriteRegister (reg_ctx, "ecx", NULL, 4, data); + WriteRegister (reg_ctx, "edx", NULL, 4, data); + WriteRegister (reg_ctx, "edi", NULL, 4, data); + WriteRegister (reg_ctx, "esi", NULL, 4, data); + WriteRegister (reg_ctx, "ebp", NULL, 4, data); + WriteRegister (reg_ctx, "esp", NULL, 4, data); + WriteRegister (reg_ctx, "ss", NULL, 4, data); + WriteRegister (reg_ctx, "eflags", NULL, 4, data); + WriteRegister (reg_ctx, "eip", NULL, 4, data); + WriteRegister (reg_ctx, "cs", NULL, 4, data); + WriteRegister (reg_ctx, "ds", NULL, 4, data); + WriteRegister (reg_ctx, "es", NULL, 4, data); + WriteRegister (reg_ctx, "fs", NULL, 4, data); + WriteRegister (reg_ctx, "gs", NULL, 4, data); + + // Write out the EXC registers + data.PutHex32 (EXCRegSet); + data.PutHex32 (EXCWordCount); + WriteRegister (reg_ctx, "trapno", NULL, 4, data); + WriteRegister (reg_ctx, "err", NULL, 4, data); + WriteRegister (reg_ctx, "faultvaddr", NULL, 4, data); + return true; + } + return false; + } + +protected: + int + DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override + { + return 0; + } + + int + DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override + { + return 0; + } + + int + DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override + { + return 0; + } + + int + DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override + { + return 0; + } + + int + DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override + { + return 0; + } + + int + DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override + { + return 0; + } +}; + +class RegisterContextDarwin_arm_Mach : public RegisterContextDarwin_arm +{ +public: + RegisterContextDarwin_arm_Mach (lldb_private::Thread &thread, const DataExtractor &data) : + RegisterContextDarwin_arm (thread, 0) + { + SetRegisterDataFrom_LC_THREAD (data); + } + + void + InvalidateAllRegisters() override + { + // Do nothing... registers are always valid... + } + + void + SetRegisterDataFrom_LC_THREAD (const DataExtractor &data) + { + lldb::offset_t offset = 0; + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + bool done = false; + + while (!done) + { + int flavor = data.GetU32 (&offset); + uint32_t count = data.GetU32 (&offset); + lldb::offset_t next_thread_state = offset + (count * 4); + switch (flavor) + { + case GPRRegSet: + for (uint32_t i=0; iGetRegisterInfoByName(name); + if (reg_info == NULL) + reg_info = reg_ctx->GetRegisterInfoByName(alt_name); + if (reg_info) + { + lldb_private::RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) + { + if (reg_info->byte_size >= reg_byte_size) + data.Write(reg_value.GetBytes(), reg_byte_size); + else + { + data.Write(reg_value.GetBytes(), reg_info->byte_size); + for (size_t i=0, n = reg_byte_size - reg_info->byte_size; iGetRegisterContext()); + if (reg_ctx_sp) + { + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + data.PutHex32 (GPRRegSet); // Flavor + data.PutHex32 (GPRWordCount); + WriteRegister (reg_ctx, "r0", NULL, 4, data); + WriteRegister (reg_ctx, "r1", NULL, 4, data); + WriteRegister (reg_ctx, "r2", NULL, 4, data); + WriteRegister (reg_ctx, "r3", NULL, 4, data); + WriteRegister (reg_ctx, "r4", NULL, 4, data); + WriteRegister (reg_ctx, "r5", NULL, 4, data); + WriteRegister (reg_ctx, "r6", NULL, 4, data); + WriteRegister (reg_ctx, "r7", NULL, 4, data); + WriteRegister (reg_ctx, "r8", NULL, 4, data); + WriteRegister (reg_ctx, "r9", NULL, 4, data); + WriteRegister (reg_ctx, "r10", NULL, 4, data); + WriteRegister (reg_ctx, "r11", NULL, 4, data); + WriteRegister (reg_ctx, "r12", NULL, 4, data); + WriteRegister (reg_ctx, "sp", NULL, 4, data); + WriteRegister (reg_ctx, "lr", NULL, 4, data); + WriteRegister (reg_ctx, "pc", NULL, 4, data); + WriteRegister (reg_ctx, "cpsr", NULL, 4, data); + + // Write out the EXC registers +// data.PutHex32 (EXCRegSet); +// data.PutHex32 (EXCWordCount); +// WriteRegister (reg_ctx, "exception", NULL, 4, data); +// WriteRegister (reg_ctx, "fsr", NULL, 4, data); +// WriteRegister (reg_ctx, "far", NULL, 4, data); + return true; + } + return false; + } + +protected: + int + DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override + { + return -1; + } + + int + DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override + { + return -1; + } + + int + DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override + { + return -1; + } + + int + DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) override + { + return -1; + } + + int + DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override + { + return 0; + } + + int + DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override + { + return 0; + } + + int + DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override + { + return 0; + } + + int + DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) override + { + return -1; + } +}; + +class RegisterContextDarwin_arm64_Mach : public RegisterContextDarwin_arm64 +{ +public: + RegisterContextDarwin_arm64_Mach (lldb_private::Thread &thread, const DataExtractor &data) : + RegisterContextDarwin_arm64 (thread, 0) + { + SetRegisterDataFrom_LC_THREAD (data); + } + + void + InvalidateAllRegisters() override + { + // Do nothing... registers are always valid... + } + + void + SetRegisterDataFrom_LC_THREAD (const DataExtractor &data) + { + lldb::offset_t offset = 0; + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + bool done = false; + while (!done) + { + int flavor = data.GetU32 (&offset); + uint32_t count = data.GetU32 (&offset); + lldb::offset_t next_thread_state = offset + (count * 4); + switch (flavor) + { + case GPRRegSet: + // x0-x29 + fp + lr + sp + pc (== 33 64-bit registers) plus cpsr (1 32-bit register) + if (count >= (33 * 2) + 1) + { + for (uint32_t i=0; i<33; ++i) + gpr.x[i] = data.GetU64(&offset); + gpr.cpsr = data.GetU32(&offset); + SetError (GPRRegSet, Read, 0); + } + offset = next_thread_state; + break; + case FPURegSet: + { + uint8_t *fpu_reg_buf = (uint8_t*) &fpu.v[0]; + const int fpu_reg_buf_size = sizeof (fpu); + if (fpu_reg_buf_size == count + && data.ExtractBytes (offset, fpu_reg_buf_size, eByteOrderLittle, fpu_reg_buf) == fpu_reg_buf_size) + { + SetError (FPURegSet, Read, 0); + } + else + { + done = true; + } + } + offset = next_thread_state; + break; + case EXCRegSet: + if (count == 4) + { + exc.far = data.GetU64(&offset); + exc.esr = data.GetU32(&offset); + exc.exception = data.GetU32(&offset); + SetError (EXCRegSet, Read, 0); + } + offset = next_thread_state; + break; + default: + done = true; + break; + } + } + } + + static size_t + WriteRegister (RegisterContext *reg_ctx, const char *name, const char *alt_name, size_t reg_byte_size, Stream &data) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name); + if (reg_info == NULL) + reg_info = reg_ctx->GetRegisterInfoByName(alt_name); + if (reg_info) + { + lldb_private::RegisterValue reg_value; + if (reg_ctx->ReadRegister(reg_info, reg_value)) + { + if (reg_info->byte_size >= reg_byte_size) + data.Write(reg_value.GetBytes(), reg_byte_size); + else + { + data.Write(reg_value.GetBytes(), reg_info->byte_size); + for (size_t i=0, n = reg_byte_size - reg_info->byte_size; iGetRegisterContext()); + if (reg_ctx_sp) + { + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + data.PutHex32 (GPRRegSet); // Flavor + data.PutHex32 (GPRWordCount); + WriteRegister (reg_ctx, "x0", NULL, 8, data); + WriteRegister (reg_ctx, "x1", NULL, 8, data); + WriteRegister (reg_ctx, "x2", NULL, 8, data); + WriteRegister (reg_ctx, "x3", NULL, 8, data); + WriteRegister (reg_ctx, "x4", NULL, 8, data); + WriteRegister (reg_ctx, "x5", NULL, 8, data); + WriteRegister (reg_ctx, "x6", NULL, 8, data); + WriteRegister (reg_ctx, "x7", NULL, 8, data); + WriteRegister (reg_ctx, "x8", NULL, 8, data); + WriteRegister (reg_ctx, "x9", NULL, 8, data); + WriteRegister (reg_ctx, "x10", NULL, 8, data); + WriteRegister (reg_ctx, "x11", NULL, 8, data); + WriteRegister (reg_ctx, "x12", NULL, 8, data); + WriteRegister (reg_ctx, "x13", NULL, 8, data); + WriteRegister (reg_ctx, "x14", NULL, 8, data); + WriteRegister (reg_ctx, "x15", NULL, 8, data); + WriteRegister (reg_ctx, "x16", NULL, 8, data); + WriteRegister (reg_ctx, "x17", NULL, 8, data); + WriteRegister (reg_ctx, "x18", NULL, 8, data); + WriteRegister (reg_ctx, "x19", NULL, 8, data); + WriteRegister (reg_ctx, "x20", NULL, 8, data); + WriteRegister (reg_ctx, "x21", NULL, 8, data); + WriteRegister (reg_ctx, "x22", NULL, 8, data); + WriteRegister (reg_ctx, "x23", NULL, 8, data); + WriteRegister (reg_ctx, "x24", NULL, 8, data); + WriteRegister (reg_ctx, "x25", NULL, 8, data); + WriteRegister (reg_ctx, "x26", NULL, 8, data); + WriteRegister (reg_ctx, "x27", NULL, 8, data); + WriteRegister (reg_ctx, "x28", NULL, 8, data); + WriteRegister (reg_ctx, "fp", NULL, 8, data); + WriteRegister (reg_ctx, "lr", NULL, 8, data); + WriteRegister (reg_ctx, "sp", NULL, 8, data); + WriteRegister (reg_ctx, "pc", NULL, 8, data); + WriteRegister (reg_ctx, "cpsr", NULL, 4, data); + + // Write out the EXC registers +// data.PutHex32 (EXCRegSet); +// data.PutHex32 (EXCWordCount); +// WriteRegister (reg_ctx, "far", NULL, 8, data); +// WriteRegister (reg_ctx, "esr", NULL, 4, data); +// WriteRegister (reg_ctx, "exception", NULL, 4, data); + return true; + } + return false; + } + +protected: + int + DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override + { + return -1; + } + + int + DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override + { + return -1; + } + + int + DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override + { + return -1; + } + + int + DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) override + { + return -1; + } + + int + DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override + { + return 0; + } + + int + DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override + { + return 0; + } + + int + DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override + { + return 0; + } + + int + DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) override + { + return -1; + } +}; + +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; +} + +#define MACHO_NLIST_ARM_SYMBOL_IS_THUMB 0x0008 + +void +ObjectFileMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + CreateMemoryInstance, + GetModuleSpecifications, + SaveCore); +} + +void +ObjectFileMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ObjectFileMachO::GetPluginNameStatic() +{ + static ConstString g_name("mach-o"); + return g_name; +} + +const char * +ObjectFileMachO::GetPluginDescriptionStatic() +{ + return "Mach-o object file reader (32 and 64 bit)"; +} + +ObjectFile * +ObjectFileMachO::CreateInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) +{ + if (!data_sp) + { + data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); + data_offset = 0; + } + + if (ObjectFileMachO::MagicBytesMatch(data_sp, data_offset, length)) + { + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) + { + data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); + data_offset = 0; + } + std::unique_ptr objfile_ap(new ObjectFileMachO (module_sp, data_sp, data_offset, file, file_offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +ObjectFile * +ObjectFileMachO::CreateMemoryInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + const ProcessSP &process_sp, + lldb::addr_t header_addr) +{ + if (ObjectFileMachO::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) + { + std::unique_ptr objfile_ap(new ObjectFileMachO (module_sp, data_sp, process_sp, header_addr)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +size_t +ObjectFileMachO::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs) +{ + const size_t initial_count = specs.GetSize(); + + if (ObjectFileMachO::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) + { + DataExtractor data; + data.SetData(data_sp); + llvm::MachO::mach_header header; + if (ParseHeader (data, &data_offset, header)) + { + size_t header_and_load_cmds = header.sizeofcmds + MachHeaderSizeFromMagic(header.magic); + if (header_and_load_cmds >= data_sp->GetByteSize()) + { + data_sp = file.ReadFileContents(file_offset, header_and_load_cmds); + data.SetData(data_sp); + data_offset = MachHeaderSizeFromMagic(header.magic); + } + if (data_sp) + { + ModuleSpec spec; + spec.GetFileSpec() = file; + spec.SetObjectOffset(file_offset); + spec.SetObjectSize(length); + + if (GetArchitecture (header, data, data_offset, spec.GetArchitecture())) + { + if (spec.GetArchitecture().IsValid()) + { + GetUUID (header, data, data_offset, spec.GetUUID()); + specs.Append(spec); + } + } + } + } + } + return specs.GetSize() - initial_count; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameTEXT() +{ + static ConstString g_segment_name_TEXT ("__TEXT"); + return g_segment_name_TEXT; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameDATA() +{ + static ConstString g_segment_name_DATA ("__DATA"); + return g_segment_name_DATA; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameDATA_DIRTY() +{ + static ConstString g_segment_name ("__DATA_DIRTY"); + return g_segment_name; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameDATA_CONST() +{ + static ConstString g_segment_name ("__DATA_CONST"); + return g_segment_name; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameOBJC() +{ + static ConstString g_segment_name_OBJC ("__OBJC"); + return g_segment_name_OBJC; +} + +const ConstString & +ObjectFileMachO::GetSegmentNameLINKEDIT() +{ + static ConstString g_section_name_LINKEDIT ("__LINKEDIT"); + return g_section_name_LINKEDIT; +} + +const ConstString & +ObjectFileMachO::GetSectionNameEHFrame() +{ + static ConstString g_section_name_eh_frame ("__eh_frame"); + return g_section_name_eh_frame; +} + +bool +ObjectFileMachO::MagicBytesMatch (DataBufferSP& data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) +{ + DataExtractor data; + data.SetData (data_sp, data_offset, data_length); + lldb::offset_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return MachHeaderSizeFromMagic(magic) != 0; +} + +ObjectFileMachO::ObjectFileMachO(const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) : + ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), + m_mach_segments(), + m_mach_sections(), + m_entry_point_address(), + m_thread_context_offsets(), + m_thread_context_offsets_valid(false) +{ + ::memset (&m_header, 0, sizeof(m_header)); + ::memset (&m_dysymtab, 0, sizeof(m_dysymtab)); +} + +ObjectFileMachO::ObjectFileMachO (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& header_data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr) : + ObjectFile(module_sp, process_sp, header_addr, header_data_sp), + m_mach_segments(), + m_mach_sections(), + m_entry_point_address(), + m_thread_context_offsets(), + m_thread_context_offsets_valid(false) +{ + ::memset (&m_header, 0, sizeof(m_header)); + ::memset (&m_dysymtab, 0, sizeof(m_dysymtab)); +} + +bool +ObjectFileMachO::ParseHeader (DataExtractor &data, + lldb::offset_t *data_offset_ptr, + llvm::MachO::mach_header &header) +{ + data.SetByteOrder (endian::InlHostByteOrder()); + // Leave magic in the original byte order + header.magic = data.GetU32(data_offset_ptr); + bool can_parse = false; + bool is_64_bit = false; + switch (header.magic) + { + case MH_MAGIC: + data.SetByteOrder (endian::InlHostByteOrder()); + data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_MAGIC_64: + data.SetByteOrder (endian::InlHostByteOrder()); + data.SetAddressByteSize(8); + can_parse = true; + is_64_bit = true; + break; + + case MH_CIGAM: + data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_CIGAM_64: + data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + data.SetAddressByteSize(8); + is_64_bit = true; + can_parse = true; + break; + + default: + break; + } + + if (can_parse) + { + data.GetU32(data_offset_ptr, &header.cputype, 6); + if (is_64_bit) + *data_offset_ptr += 4; + return true; + } + else + { + memset(&header, 0, sizeof(header)); + } + return false; +} + +bool +ObjectFileMachO::ParseHeader () +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + bool can_parse = false; + lldb::offset_t offset = 0; + m_data.SetByteOrder (endian::InlHostByteOrder()); + // Leave magic in the original byte order + m_header.magic = m_data.GetU32(&offset); + switch (m_header.magic) + { + case MH_MAGIC: + m_data.SetByteOrder (endian::InlHostByteOrder()); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_MAGIC_64: + m_data.SetByteOrder (endian::InlHostByteOrder()); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + case MH_CIGAM: + m_data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_CIGAM_64: + m_data.SetByteOrder(endian::InlHostByteOrder() == 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; + + if (GetArchitecture (mach_arch)) + { + // Check if the module has a required architecture + const ArchSpec &module_arch = module_sp->GetArchitecture(); + if (module_arch.IsValid() && !module_arch.IsCompatibleMatch(mach_arch)) + return false; + + if (SetModulesArchitecture (mach_arch)) + { + const size_t header_and_lc_size = m_header.sizeofcmds + MachHeaderSizeFromMagic(m_header.magic); + if (m_data.GetByteSize() < header_and_lc_size) + { + DataBufferSP data_sp; + ProcessSP process_sp (m_process_wp.lock()); + if (process_sp) + { + data_sp = ReadMemory (process_sp, m_memory_addr, header_and_lc_size); + } + else + { + // Read in all only the load command data from the file on disk + data_sp = m_file.ReadFileContents(m_file_offset, header_and_lc_size); + if (data_sp->GetByteSize() != header_and_lc_size) + return false; + } + if (data_sp) + m_data.SetData (data_sp); + } + } + return true; + } + } + else + { + memset(&m_header, 0, sizeof(struct mach_header)); + } + } + return false; +} + +ByteOrder +ObjectFileMachO::GetByteOrder () const +{ + return m_data.GetByteOrder (); +} + +bool +ObjectFileMachO::IsExecutable() const +{ + return m_header.filetype == MH_EXECUTE; +} + +uint32_t +ObjectFileMachO::GetAddressByteSize () const +{ + return m_data.GetAddressByteSize (); +} + +AddressClass +ObjectFileMachO::GetAddressClass (lldb::addr_t file_addr) +{ + Symtab *symtab = GetSymtab(); + if (symtab) + { + Symbol *symbol = symtab->FindSymbolContainingFileAddress(file_addr); + if (symbol) + { + if (symbol->ValueIsAddress()) + { + SectionSP section_sp (symbol->GetAddressRef().GetSection()); + if (section_sp) + { + const lldb::SectionType section_type = section_sp->GetType(); + switch (section_type) + { + case eSectionTypeInvalid: + return eAddressClassUnknown; + + case eSectionTypeCode: + if (m_header.cputype == llvm::MachO::CPU_TYPE_ARM) + { + // For ARM we have a bit in the n_desc field of the symbol + // that tells us ARM/Thumb which is bit 0x0008. + if (symbol->GetFlags() & MACHO_NLIST_ARM_SYMBOL_IS_THUMB) + return eAddressClassCodeAlternateISA; + } + return eAddressClassCode; + + case eSectionTypeContainer: + return eAddressClassUnknown; + + case eSectionTypeData: + case eSectionTypeDataCString: + case eSectionTypeDataCStringPointers: + case eSectionTypeDataSymbolAddress: + case eSectionTypeData4: + case eSectionTypeData8: + case eSectionTypeData16: + case eSectionTypeDataPointers: + case eSectionTypeZeroFill: + case eSectionTypeDataObjCMessageRefs: + case eSectionTypeDataObjCCFStrings: + case eSectionTypeGoSymtab: + return eAddressClassData; + + case eSectionTypeDebug: + case eSectionTypeDWARFDebugAbbrev: + case eSectionTypeDWARFDebugAddr: + case eSectionTypeDWARFDebugAranges: + case eSectionTypeDWARFDebugFrame: + case eSectionTypeDWARFDebugInfo: + case eSectionTypeDWARFDebugLine: + case eSectionTypeDWARFDebugLoc: + case eSectionTypeDWARFDebugMacInfo: + case eSectionTypeDWARFDebugMacro: + case eSectionTypeDWARFDebugPubNames: + case eSectionTypeDWARFDebugPubTypes: + case eSectionTypeDWARFDebugRanges: + case eSectionTypeDWARFDebugStr: + case eSectionTypeDWARFDebugStrOffsets: + case eSectionTypeDWARFAppleNames: + case eSectionTypeDWARFAppleTypes: + case eSectionTypeDWARFAppleNamespaces: + case eSectionTypeDWARFAppleObjC: + return eAddressClassDebug; + + case eSectionTypeEHFrame: + case eSectionTypeARMexidx: + case eSectionTypeARMextab: + case eSectionTypeCompactUnwind: + return eAddressClassRuntime; + + case eSectionTypeELFSymbolTable: + case eSectionTypeELFDynamicSymbols: + case eSectionTypeELFRelocationEntries: + case eSectionTypeELFDynamicLinkInfo: + case eSectionTypeOther: + return eAddressClassUnknown; + } + } + } + + const SymbolType symbol_type = symbol->GetType(); + switch (symbol_type) + { + case eSymbolTypeAny: return eAddressClassUnknown; + case eSymbolTypeAbsolute: return eAddressClassUnknown; + + case eSymbolTypeCode: + case eSymbolTypeTrampoline: + case eSymbolTypeResolver: + if (m_header.cputype == llvm::MachO::CPU_TYPE_ARM) + { + // For ARM we have a bit in the n_desc field of the symbol + // that tells us ARM/Thumb which is bit 0x0008. + if (symbol->GetFlags() & MACHO_NLIST_ARM_SYMBOL_IS_THUMB) + return eAddressClassCodeAlternateISA; + } + return eAddressClassCode; + + case eSymbolTypeData: return eAddressClassData; + case eSymbolTypeRuntime: return eAddressClassRuntime; + case eSymbolTypeException: return eAddressClassRuntime; + case eSymbolTypeSourceFile: return eAddressClassDebug; + case eSymbolTypeHeaderFile: return eAddressClassDebug; + case eSymbolTypeObjectFile: return eAddressClassDebug; + case eSymbolTypeCommonBlock: return eAddressClassDebug; + case eSymbolTypeBlock: return eAddressClassDebug; + case eSymbolTypeLocal: return eAddressClassData; + case eSymbolTypeParam: return eAddressClassData; + case eSymbolTypeVariable: return eAddressClassData; + case eSymbolTypeVariableType: return eAddressClassDebug; + case eSymbolTypeLineEntry: return eAddressClassDebug; + case eSymbolTypeLineHeader: return eAddressClassDebug; + case eSymbolTypeScopeBegin: return eAddressClassDebug; + case eSymbolTypeScopeEnd: return eAddressClassDebug; + case eSymbolTypeAdditional: return eAddressClassUnknown; + case eSymbolTypeCompiler: return eAddressClassDebug; + case eSymbolTypeInstrumentation:return eAddressClassDebug; + case eSymbolTypeUndefined: return eAddressClassUnknown; + case eSymbolTypeObjCClass: return eAddressClassRuntime; + case eSymbolTypeObjCMetaClass: return eAddressClassRuntime; + case eSymbolTypeObjCIVar: return eAddressClassRuntime; + case eSymbolTypeReExported: return eAddressClassRuntime; + } + } + } + return eAddressClassUnknown; +} + +Symtab * +ObjectFileMachO::GetSymtab() +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_symtab_ap.get() == NULL) + { + m_symtab_ap.reset(new Symtab(this)); + Mutex::Locker symtab_locker (m_symtab_ap->GetMutex()); + ParseSymtab (); + m_symtab_ap->Finalize (); + } + } + return m_symtab_ap.get(); +} + +bool +ObjectFileMachO::IsStripped () +{ + if (m_dysymtab.cmd == 0) + { + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + for (uint32_t i=0; i EncryptedFileRanges; + EncryptedFileRanges encrypted_file_ranges; + encryption_info_command encryption_cmd; + for (i=0; i(strlen(load_cmd.segname), sizeof(load_cmd.segname))); + + SectionSP unified_section_sp(unified_section_list.FindSectionByName(const_segname)); + if (is_dsym && unified_section_sp) + { + if (const_segname == GetSegmentNameLINKEDIT()) + { + // We need to keep the __LINKEDIT segment private to this object file only + add_to_unified = false; + } + else + { + // This is the dSYM file and this section has already been created by + // the object file, no need to create it. + add_section = false; + } + } + load_cmd.vmaddr = m_data.GetAddress(&offset); + load_cmd.vmsize = m_data.GetAddress(&offset); + load_cmd.fileoff = m_data.GetAddress(&offset); + load_cmd.filesize = m_data.GetAddress(&offset); + if (m_length != 0 && load_cmd.filesize != 0) + { + if (load_cmd.fileoff > m_length) + { + // We have a load command that says it extends past the end of the file. This is likely + // a corrupt file. We don't have any way to return an error condition here (this method + // was likely invoked from something like ObjectFile::GetSectionList()) -- all we can do + // is null out the SectionList vector and if a process has been set up, dump a message + // to stdout. The most common case here is core file debugging with a truncated file. + const char *lc_segment_name = load_cmd.cmd == LC_SEGMENT_64 ? "LC_SEGMENT_64" : "LC_SEGMENT"; + module_sp->ReportWarning("load command %u %s has a fileoff (0x%" PRIx64 ") that extends beyond the end of the file (0x%" PRIx64 "), ignoring this section", + i, + lc_segment_name, + load_cmd.fileoff, + m_length); + + load_cmd.fileoff = 0; + load_cmd.filesize = 0; + } + + if (load_cmd.fileoff + load_cmd.filesize > m_length) + { + // We have a load command that says it extends past the end of the file. This is likely + // a corrupt file. We don't have any way to return an error condition here (this method + // was likely invoked from something like ObjectFile::GetSectionList()) -- all we can do + // is null out the SectionList vector and if a process has been set up, dump a message + // to stdout. The most common case here is core file debugging with a truncated file. + const char *lc_segment_name = load_cmd.cmd == LC_SEGMENT_64 ? "LC_SEGMENT_64" : "LC_SEGMENT"; + GetModule()->ReportWarning("load command %u %s has a fileoff + filesize (0x%" PRIx64 ") that extends beyond the end of the file (0x%" PRIx64 "), the segment will be truncated to match", + i, + lc_segment_name, + load_cmd.fileoff + load_cmd.filesize, + m_length); + + // Tuncase the length + load_cmd.filesize = m_length - load_cmd.fileoff; + } + } + if (m_data.GetU32(&offset, &load_cmd.maxprot, 4)) + { + + const bool segment_is_encrypted = (load_cmd.flags & SG_PROTECTED_VERSION_1) != 0; + + // Keep a list of mach segments around in case we need to + // get at data that isn't stored in the abstracted Sections. + m_mach_segments.push_back (load_cmd); + + // 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 (add_section && (const_segname || is_core)) + { + segment_sp.reset(new Section (module_sp, // Module to which this section belongs + this, // Object file to which this sections 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 + const_segname, // 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 file + 0, // Segments have no alignment information + load_cmd.flags)); // Flags for this section + + segment_sp->SetIsEncrypted (segment_is_encrypted); + m_sections_ap->AddSection(segment_sp); + if (add_to_unified) + unified_section_list.AddSection(segment_sp); + } + else if (unified_section_sp) + { + if (is_dsym && unified_section_sp->GetFileAddress() != load_cmd.vmaddr) + { + // Check to see if the module was read from memory? + if (module_sp->GetObjectFile()->GetHeaderAddress().IsValid()) + { + // We have a module that is in memory and needs to have its + // file address adjusted. We need to do this because when we + // load a file from memory, its addresses will be slid already, + // yet the addresses in the new symbol file will still be unslid. + // Since everything is stored as section offset, this shouldn't + // cause any problems. + + // Make sure we've parsed the symbol table from the + // ObjectFile before we go around changing its Sections. + module_sp->GetObjectFile()->GetSymtab(); + // eh_frame would present the same problems but we parse that on + // a per-function basis as-needed so it's more difficult to + // remove its use of the Sections. Realistically, the environments + // where this code path will be taken will not have eh_frame sections. + + unified_section_sp->SetFileAddress(load_cmd.vmaddr); + + // Notify the module that the section addresses have been changed once + // we're done so any file-address caches can be updated. + section_file_addresses_changed = true; + } + } + m_sections_ap->AddSection(unified_section_sp); + } + + struct section_64 sect64; + ::memset (§64, 0, sizeof(sect64)); + // Push a section into our mach sections for the section at + // index zero (NO_SECT) if we don't have any mach sections yet... + if (m_mach_sections.empty()) + 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 (!const_segname) + { + // 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. + const_segname.SetTrimmedCStringWithLength(sect64.segname, sizeof(sect64.segname)); + segment_sp = unified_section_list.FindSectionByName (const_segname); + 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); + } + + // Grow the section size as needed. + if (sect64.offset) + { + const lldb::addr_t segment_min_file_offset = segment->GetFileOffset(); + const lldb::addr_t segment_max_file_offset = segment_min_file_offset + segment->GetFileSize(); + + const lldb::addr_t section_min_file_offset = sect64.offset; + const lldb::addr_t section_max_file_offset = section_min_file_offset + sect64.size; + const lldb::addr_t new_file_offset = std::min (section_min_file_offset, segment_min_file_offset); + const lldb::addr_t new_file_size = std::max (section_max_file_offset, segment_max_file_offset) - new_file_offset; + segment->SetFileOffset (new_file_offset); + segment->SetFileSize (new_file_size); + } + } + else + { + // Create a fake section for the section's named segment + segment_sp.reset(new Section (segment_sp, // Parent section + module_sp, // Module to which this section belongs + this, // Object file 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 + const_segname, // 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 file + sect64.align, + load_cmd.flags)); // Flags for this section + segment_sp->SetIsFake(true); + + m_sections_ap->AddSection(segment_sp); + if (add_to_unified) + unified_section_list.AddSection(segment_sp); + segment_sp->SetIsEncrypted (segment_is_encrypted); + } + } + assert (segment_sp.get()); + + lldb::SectionType sect_type = eSectionTypeOther; + + if (sect64.flags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS)) + sect_type = eSectionTypeCode; + else + { + 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"); + + static ConstString g_sect_name_dwarf_debug_abbrev ("__debug_abbrev"); + static ConstString g_sect_name_dwarf_debug_aranges ("__debug_aranges"); + static ConstString g_sect_name_dwarf_debug_frame ("__debug_frame"); + static ConstString g_sect_name_dwarf_debug_info ("__debug_info"); + static ConstString g_sect_name_dwarf_debug_line ("__debug_line"); + static ConstString g_sect_name_dwarf_debug_loc ("__debug_loc"); + static ConstString g_sect_name_dwarf_debug_macinfo ("__debug_macinfo"); + static ConstString g_sect_name_dwarf_debug_pubnames ("__debug_pubnames"); + static ConstString g_sect_name_dwarf_debug_pubtypes ("__debug_pubtypes"); + static ConstString g_sect_name_dwarf_debug_ranges ("__debug_ranges"); + static ConstString g_sect_name_dwarf_debug_str ("__debug_str"); + static ConstString g_sect_name_dwarf_apple_names ("__apple_names"); + static ConstString g_sect_name_dwarf_apple_types ("__apple_types"); + static ConstString g_sect_name_dwarf_apple_namespaces ("__apple_namespac"); + static ConstString g_sect_name_dwarf_apple_objc ("__apple_objc"); + static ConstString g_sect_name_eh_frame ("__eh_frame"); + static ConstString g_sect_name_compact_unwind ("__unwind_info"); + static ConstString g_sect_name_text ("__text"); + static ConstString g_sect_name_data ("__data"); + static ConstString g_sect_name_go_symtab ("__gosymtab"); + + if (section_name == g_sect_name_dwarf_debug_abbrev) + sect_type = eSectionTypeDWARFDebugAbbrev; + else if (section_name == g_sect_name_dwarf_debug_aranges) + sect_type = eSectionTypeDWARFDebugAranges; + else if (section_name == g_sect_name_dwarf_debug_frame) + sect_type = eSectionTypeDWARFDebugFrame; + else if (section_name == g_sect_name_dwarf_debug_info) + sect_type = eSectionTypeDWARFDebugInfo; + else if (section_name == g_sect_name_dwarf_debug_line) + sect_type = eSectionTypeDWARFDebugLine; + else if (section_name == g_sect_name_dwarf_debug_loc) + sect_type = eSectionTypeDWARFDebugLoc; + else if (section_name == g_sect_name_dwarf_debug_macinfo) + sect_type = eSectionTypeDWARFDebugMacInfo; + else if (section_name == g_sect_name_dwarf_debug_pubnames) + sect_type = eSectionTypeDWARFDebugPubNames; + else if (section_name == g_sect_name_dwarf_debug_pubtypes) + sect_type = eSectionTypeDWARFDebugPubTypes; + else if (section_name == g_sect_name_dwarf_debug_ranges) + sect_type = eSectionTypeDWARFDebugRanges; + else if (section_name == g_sect_name_dwarf_debug_str) + sect_type = eSectionTypeDWARFDebugStr; + else if (section_name == g_sect_name_dwarf_apple_names) + sect_type = eSectionTypeDWARFAppleNames; + else if (section_name == g_sect_name_dwarf_apple_types) + sect_type = eSectionTypeDWARFAppleTypes; + else if (section_name == g_sect_name_dwarf_apple_namespaces) + sect_type = eSectionTypeDWARFAppleNamespaces; + else if (section_name == g_sect_name_dwarf_apple_objc) + sect_type = eSectionTypeDWARFAppleObjC; + else 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_eh_frame) + sect_type = eSectionTypeEHFrame; + else if (section_name == g_sect_name_compact_unwind) + sect_type = eSectionTypeCompactUnwind; + else if (section_name == g_sect_name_cfstring) + sect_type = eSectionTypeDataObjCCFStrings; + else if (section_name == g_sect_name_go_symtab) + sect_type = eSectionTypeGoSymtab; + 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; + } + + if (sect_type == eSectionTypeOther) + { + switch (mach_sect_type) + { + // TODO: categorize sections by other flags for regular sections + case S_REGULAR: + if (section_name == g_sect_name_text) + sect_type = eSectionTypeCode; + else if (section_name == g_sect_name_data) + sect_type = eSectionTypeData; + else + 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, + module_sp, + this, + ++sectID, + section_name, + sect_type, + sect64.addr - segment_sp->GetFileAddress(), + sect64.size, + sect64.offset, + sect64.offset == 0 ? 0 : sect64.size, + sect64.align, + sect64.flags)); + // Set the section to be encrypted to match the segment + + bool section_is_encrypted = false; + if (!segment_is_encrypted && load_cmd.filesize != 0) + section_is_encrypted = encrypted_file_ranges.FindEntryThatContains(sect64.offset) != NULL; + + section_sp->SetIsEncrypted (segment_is_encrypted || section_is_encrypted); + segment_sp->GetChildren().AddSection(section_sp); + + if (segment_sp->IsFake()) + { + segment_sp.reset(); + const_segname.Clear(); + } + } + } + if (segment_sp && is_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 (section_file_addresses_changed && module_sp.get()) + { + module_sp->SectionFileAddressesChanged(); + } + } +} + +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)); + } + + SectionSP + GetSection (uint8_t n_sect, addr_t file_addr) + { + if (n_sect == 0) + return SectionSP(); + if (n_sect < m_section_infos.size()) + { + if (!m_section_infos[n_sect].section_sp) + { + SectionSP section_sp (m_section_list->FindSectionByID (n_sect)); + m_section_infos[n_sect].section_sp = section_sp; + if (section_sp) + { + m_section_infos[n_sect].vm_range.SetBaseAddress (section_sp->GetFileAddress()); + m_section_infos[n_sect].vm_range.SetByteSize (section_sp->GetByteSize()); + } + else + { + Host::SystemLog (Host::eSystemLogError, "error: unable to find section for section %u\n", n_sect); + } + } + if (m_section_infos[n_sect].vm_range.Contains(file_addr)) + { + // Symbol is in section. + return m_section_infos[n_sect].section_sp; + } + else if (m_section_infos[n_sect].vm_range.GetByteSize () == 0 && + m_section_infos[n_sect].vm_range.GetBaseAddress() == file_addr) + { + // Symbol is in section with zero size, but has the same start + // address as the section. This can happen with linker symbols + // (symbols that start with the letter 'l' or 'L'. + return m_section_infos[n_sect].section_sp; + } + } + return m_section_list->FindSectionContainingFileAddress(file_addr); + } + +protected: + struct SectionInfo + { + SectionInfo () : + vm_range(), + section_sp () + { + } + + VMRange vm_range; + SectionSP section_sp; + }; + SectionList *m_section_list; + std::vector m_section_infos; +}; + +struct TrieEntry +{ + TrieEntry () : + name(), + address(LLDB_INVALID_ADDRESS), + flags (0), + other(0), + import_name() + { + } + + void + Clear () + { + name.Clear(); + address = LLDB_INVALID_ADDRESS; + flags = 0; + other = 0; + import_name.Clear(); + } + + void + Dump () const + { + printf ("0x%16.16llx 0x%16.16llx 0x%16.16llx \"%s\"", + static_cast(address), + static_cast(flags), + static_cast(other), name.GetCString()); + if (import_name) + printf (" -> \"%s\"\n", import_name.GetCString()); + else + printf ("\n"); + } + ConstString name; + uint64_t address; + uint64_t flags; + uint64_t other; + ConstString import_name; +}; + +struct TrieEntryWithOffset +{ + lldb::offset_t nodeOffset; + TrieEntry entry; + + TrieEntryWithOffset (lldb::offset_t offset) : + nodeOffset (offset), + entry() + { + } + + void + Dump (uint32_t idx) const + { + printf ("[%3u] 0x%16.16llx: ", idx, + static_cast(nodeOffset)); + entry.Dump(); + } + + bool + operator<(const TrieEntryWithOffset& other) const + { + return ( nodeOffset < other.nodeOffset ); + } +}; + +static bool +ParseTrieEntries (DataExtractor &data, + lldb::offset_t offset, + const bool is_arm, + std::vector &nameSlices, + std::set &resolver_addresses, + std::vector& output) +{ + if (!data.ValidOffset(offset)) + return true; + + const uint64_t terminalSize = data.GetULEB128(&offset); + lldb::offset_t children_offset = offset + terminalSize; + if ( terminalSize != 0 ) { + TrieEntryWithOffset e (offset); + e.entry.flags = data.GetULEB128(&offset); + const char *import_name = NULL; + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + e.entry.address = 0; + e.entry.other = data.GetULEB128(&offset); // dylib ordinal + import_name = data.GetCStr(&offset); + } + else { + e.entry.address = data.GetULEB128(&offset); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + { + e.entry.other = data.GetULEB128(&offset); + uint64_t resolver_addr = e.entry.other; + if (is_arm) + resolver_addr &= THUMB_ADDRESS_BIT_MASK; + resolver_addresses.insert(resolver_addr); + } + else + e.entry.other = 0; + } + // Only add symbols that are reexport symbols with a valid import name + if (EXPORT_SYMBOL_FLAGS_REEXPORT & e.entry.flags && import_name && import_name[0]) + { + std::string name; + if (!nameSlices.empty()) + { + for (auto name_slice: nameSlices) + name.append(name_slice.data(), name_slice.size()); + } + if (name.size() > 1) + { + // Skip the leading '_' + e.entry.name.SetCStringWithLength(name.c_str() + 1,name.size() - 1); + } + if (import_name) + { + // Skip the leading '_' + e.entry.import_name.SetCString(import_name+1); + } + output.push_back(e); + } + } + + const uint8_t childrenCount = data.GetU8(&children_offset); + for (uint8_t i=0; i < childrenCount; ++i) { + const char *cstr = data.GetCStr(&children_offset); + if (cstr) + nameSlices.push_back(llvm::StringRef(cstr)); + else + return false; // Corrupt data + lldb::offset_t childNodeOffset = data.GetULEB128(&children_offset); + if (childNodeOffset) + { + if (!ParseTrieEntries(data, + childNodeOffset, + is_arm, + nameSlices, + resolver_addresses, + output)) + { + return false; + } + } + nameSlices.pop_back(); + } + return true; +} + +// Read the UUID out of a dyld_shared_cache file on-disk. +UUID +ObjectFileMachO::GetSharedCacheUUID (FileSpec dyld_shared_cache, const ByteOrder byte_order, const uint32_t addr_byte_size) +{ + UUID dsc_uuid; + DataBufferSP dsc_data_sp = dyld_shared_cache.MemoryMapFileContentsIfLocal(0, sizeof(struct lldb_copy_dyld_cache_header_v1)); + if (dsc_data_sp) + { + DataExtractor dsc_header_data (dsc_data_sp, byte_order, addr_byte_size); + + char version_str[7]; + lldb::offset_t offset = 0; + memcpy (version_str, dsc_header_data.GetData (&offset, 6), 6); + version_str[6] = '\0'; + if (strcmp (version_str, "dyld_v") == 0) + { + offset = offsetof (struct lldb_copy_dyld_cache_header_v1, uuid); + uint8_t uuid_bytes[sizeof (uuid_t)]; + memcpy (uuid_bytes, dsc_header_data.GetData (&offset, sizeof (uuid_t)), sizeof (uuid_t)); + dsc_uuid.SetBytes (uuid_bytes); + } + } + return dsc_uuid; +} + +size_t +ObjectFileMachO::ParseSymtab () +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "ObjectFileMachO::ParseSymtab () module = %s", + m_file.GetFilename().AsCString("")); + ModuleSP module_sp (GetModule()); + if (!module_sp) + return 0; + + struct symtab_command symtab_load_command = { 0, 0, 0, 0, 0, 0 }; + struct linkedit_data_command function_starts_load_command = { 0, 0, 0, 0 }; + struct dyld_info_command dyld_info = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + typedef AddressDataArray FunctionStarts; + FunctionStarts function_starts; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + FileSpecList dylib_files; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS)); + static const llvm::StringRef g_objc_v2_prefix_class ("_OBJC_CLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_metaclass ("_OBJC_METACLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_ivar ("_OBJC_IVAR_$_"); + + for (i=0; iLogMessage(log, "LC_SYMTAB.symoff == 0"); + return 0; + } + + if (symtab_load_command.stroff == 0) + { + if (log) + module_sp->LogMessage(log, "LC_SYMTAB.stroff == 0"); + return 0; + } + + if (symtab_load_command.nsyms == 0) + { + if (log) + module_sp->LogMessage(log, "LC_SYMTAB.nsyms == 0"); + return 0; + } + + if (symtab_load_command.strsize == 0) + { + if (log) + module_sp->LogMessage(log, "LC_SYMTAB.strsize == 0"); + return 0; + } + break; + + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + if (m_data.GetU32(&offset, &dyld_info.rebase_off, 10)) + { + dyld_info.cmd = lc.cmd; + dyld_info.cmdsize = lc.cmdsize; + } + else + { + memset (&dyld_info, 0, sizeof(dyld_info)); + } + break; + + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOADFVMLIB: + case LC_LOAD_UPWARD_DYLIB: + { + uint32_t name_offset = cmd_offset + m_data.GetU32(&offset); + const char *path = m_data.PeekCStr(name_offset); + if (path) + { + FileSpec file_spec(path, false); + // Strip the path if there is @rpath, @executable, etc so we just use the basename + if (path[0] == '@') + file_spec.GetDirectory().Clear(); + + if (lc.cmd == LC_REEXPORT_DYLIB) + { + m_reexported_dylibs.AppendIfUnique(file_spec); + } + + dylib_files.Append(file_spec); + } + } + break; + + case LC_FUNCTION_STARTS: + function_starts_load_command.cmd = lc.cmd; + function_starts_load_command.cmdsize = lc.cmdsize; + if (m_data.GetU32(&offset, &function_starts_load_command.dataoff, 2) == NULL) // fill in symoff, nsyms, stroff, strsize fields + memset (&function_starts_load_command, 0, sizeof(function_starts_load_command)); + break; + + default: + break; + } + offset = cmd_offset + lc.cmdsize; + } + + if (symtab_load_command.cmd) + { + Symtab *symtab = m_symtab_ap.get(); + SectionList *section_list = GetSectionList(); + if (section_list == NULL) + return 0; + + const uint32_t addr_byte_size = m_data.GetAddressByteSize(); + const ByteOrder byte_order = m_data.GetByteOrder(); + bool bit_width_32 = addr_byte_size == 4; + const size_t nlist_byte_size = bit_width_32 ? sizeof(struct nlist) : sizeof(struct nlist_64); + + DataExtractor nlist_data (NULL, 0, byte_order, addr_byte_size); + DataExtractor strtab_data (NULL, 0, byte_order, addr_byte_size); + DataExtractor function_starts_data (NULL, 0, byte_order, addr_byte_size); + DataExtractor indirect_symbol_index_data (NULL, 0, byte_order, addr_byte_size); + DataExtractor dyld_trie_data (NULL, 0, byte_order, addr_byte_size); + + const addr_t nlist_data_byte_size = symtab_load_command.nsyms * nlist_byte_size; + const addr_t strtab_data_byte_size = symtab_load_command.strsize; + addr_t strtab_addr = LLDB_INVALID_ADDRESS; + + ProcessSP process_sp (m_process_wp.lock()); + Process *process = process_sp.get(); + + uint32_t memory_module_load_level = eMemoryModuleLoadLevelComplete; + + if (process && m_header.filetype != llvm::MachO::MH_OBJECT) + { + Target &target = process->GetTarget(); + + memory_module_load_level = target.GetMemoryModuleLoadLevel(); + + SectionSP linkedit_section_sp(section_list->FindSectionByName(GetSegmentNameLINKEDIT())); + // Reading mach file from memory in a process or core file... + + if (linkedit_section_sp) + { + addr_t linkedit_load_addr = linkedit_section_sp->GetLoadBaseAddress(&target); + if (linkedit_load_addr == LLDB_INVALID_ADDRESS) + { + // We might be trying to access the symbol table before the __LINKEDIT's load + // address has been set in the target. We can't fail to read the symbol table, + // so calculate the right address manually + linkedit_load_addr = CalculateSectionLoadAddressForMemoryImage(m_memory_addr, GetMachHeaderSection(), linkedit_section_sp.get()); + } + + const addr_t linkedit_file_offset = linkedit_section_sp->GetFileOffset(); + const addr_t symoff_addr = linkedit_load_addr + symtab_load_command.symoff - linkedit_file_offset; + strtab_addr = linkedit_load_addr + symtab_load_command.stroff - linkedit_file_offset; + + bool data_was_read = false; + +#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) + if (m_header.flags & 0x80000000u && process->GetAddressByteSize() == sizeof (void*)) + { + // This mach-o memory file is in the dyld shared cache. If this + // program is not remote and this is iOS, then this process will + // share the same shared cache as the process we are debugging and + // we can read the entire __LINKEDIT from the address space in this + // process. This is a needed optimization that is used for local iOS + // debugging only since all shared libraries in the shared cache do + // not have corresponding files that exist in the file system of the + // device. They have been combined into a single file. This means we + // always have to load these files from memory. All of the symbol and + // string tables from all of the __LINKEDIT sections from the shared + // libraries in the shared cache have been merged into a single large + // symbol and string table. Reading all of this symbol and string table + // data across can slow down debug launch times, so we optimize this by + // reading the memory for the __LINKEDIT section from this process. + + UUID lldb_shared_cache(GetLLDBSharedCacheUUID()); + UUID process_shared_cache(GetProcessSharedCacheUUID(process)); + bool use_lldb_cache = true; + if (lldb_shared_cache.IsValid() && process_shared_cache.IsValid() && lldb_shared_cache != process_shared_cache) + { + use_lldb_cache = false; + ModuleSP module_sp (GetModule()); + if (module_sp) + module_sp->ReportWarning ("shared cache in process does not match lldb's own shared cache, startup will be slow."); + + } + + PlatformSP platform_sp (target.GetPlatform()); + if (platform_sp && platform_sp->IsHost() && use_lldb_cache) + { + data_was_read = true; + nlist_data.SetData((void *)symoff_addr, nlist_data_byte_size, eByteOrderLittle); + strtab_data.SetData((void *)strtab_addr, strtab_data_byte_size, eByteOrderLittle); + if (function_starts_load_command.cmd) + { + const addr_t func_start_addr = linkedit_load_addr + function_starts_load_command.dataoff - linkedit_file_offset; + function_starts_data.SetData ((void *)func_start_addr, function_starts_load_command.datasize, eByteOrderLittle); + } + } + } +#endif + + if (!data_was_read) + { + if (memory_module_load_level == eMemoryModuleLoadLevelComplete) + { + DataBufferSP nlist_data_sp (ReadMemory (process_sp, symoff_addr, nlist_data_byte_size)); + if (nlist_data_sp) + nlist_data.SetData (nlist_data_sp, 0, nlist_data_sp->GetByteSize()); + // Load strings individually from memory when loading from memory since shared cache + // string tables contain strings for all symbols from all shared cached libraries + //DataBufferSP strtab_data_sp (ReadMemory (process_sp, strtab_addr, strtab_data_byte_size)); + //if (strtab_data_sp) + // strtab_data.SetData (strtab_data_sp, 0, strtab_data_sp->GetByteSize()); + if (m_dysymtab.nindirectsyms != 0) + { + const addr_t indirect_syms_addr = linkedit_load_addr + m_dysymtab.indirectsymoff - linkedit_file_offset; + DataBufferSP indirect_syms_data_sp (ReadMemory (process_sp, indirect_syms_addr, m_dysymtab.nindirectsyms * 4)); + if (indirect_syms_data_sp) + indirect_symbol_index_data.SetData (indirect_syms_data_sp, 0, indirect_syms_data_sp->GetByteSize()); + } + } + + if (memory_module_load_level >= eMemoryModuleLoadLevelPartial) + { + if (function_starts_load_command.cmd) + { + const addr_t func_start_addr = linkedit_load_addr + function_starts_load_command.dataoff - linkedit_file_offset; + DataBufferSP func_start_data_sp (ReadMemory (process_sp, func_start_addr, function_starts_load_command.datasize)); + if (func_start_data_sp) + function_starts_data.SetData (func_start_data_sp, 0, func_start_data_sp->GetByteSize()); + } + } + } + } + } + else + { + nlist_data.SetData (m_data, + symtab_load_command.symoff, + nlist_data_byte_size); + strtab_data.SetData (m_data, + symtab_load_command.stroff, + strtab_data_byte_size); + + if (dyld_info.export_size > 0) + { + dyld_trie_data.SetData (m_data, + dyld_info.export_off, + dyld_info.export_size); + } + + if (m_dysymtab.nindirectsyms != 0) + { + indirect_symbol_index_data.SetData (m_data, + m_dysymtab.indirectsymoff, + m_dysymtab.nindirectsyms * 4); + } + if (function_starts_load_command.cmd) + { + function_starts_data.SetData (m_data, + function_starts_load_command.dataoff, + function_starts_load_command.datasize); + } + } + + if (nlist_data.GetByteSize() == 0 && memory_module_load_level == eMemoryModuleLoadLevelComplete) + { + if (log) + module_sp->LogMessage(log, "failed to read nlist data"); + return 0; + } + + const bool have_strtab_data = strtab_data.GetByteSize() > 0; + if (!have_strtab_data) + { + if (process) + { + if (strtab_addr == LLDB_INVALID_ADDRESS) + { + if (log) + module_sp->LogMessage(log, "failed to locate the strtab in memory"); + return 0; + } + } + else + { + if (log) + module_sp->LogMessage(log, "failed to read strtab data"); + return 0; + } + } + + const ConstString &g_segment_name_TEXT = GetSegmentNameTEXT(); + const ConstString &g_segment_name_DATA = GetSegmentNameDATA(); + const ConstString &g_segment_name_DATA_DIRTY = GetSegmentNameDATA_DIRTY(); + const ConstString &g_segment_name_DATA_CONST = GetSegmentNameDATA_CONST(); + const ConstString &g_segment_name_OBJC = GetSegmentNameOBJC(); + const ConstString &g_section_name_eh_frame = GetSectionNameEHFrame(); + SectionSP text_section_sp(section_list->FindSectionByName(g_segment_name_TEXT)); + SectionSP data_section_sp(section_list->FindSectionByName(g_segment_name_DATA)); + SectionSP data_dirty_section_sp(section_list->FindSectionByName(g_segment_name_DATA_DIRTY)); + SectionSP data_const_section_sp(section_list->FindSectionByName(g_segment_name_DATA_CONST)); + 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); + + const bool is_arm = (m_header.cputype == llvm::MachO::CPU_TYPE_ARM); + + // lldb works best if it knows the start address of all functions in a module. + // Linker symbols or debug info are normally the best source of information for start addr / size but + // they may be stripped in a released binary. + // Two additional sources of information exist in Mach-O binaries: + // LC_FUNCTION_STARTS - a list of ULEB128 encoded offsets of each function's start address in the + // binary, relative to the text section. + // eh_frame - the eh_frame FDEs have the start addr & size of each function + // LC_FUNCTION_STARTS is the fastest source to read in, and is present on all modern binaries. + // Binaries built to run on older releases may need to use eh_frame information. + + if (text_section_sp && function_starts_data.GetByteSize()) + { + FunctionStarts::Entry function_start_entry; + function_start_entry.data = false; + lldb::offset_t function_start_offset = 0; + function_start_entry.addr = text_section_sp->GetFileAddress(); + uint64_t delta; + while ((delta = function_starts_data.GetULEB128(&function_start_offset)) > 0) + { + // Now append the current entry + function_start_entry.addr += delta; + function_starts.Append(function_start_entry); + } + } + else + { + // If m_type is eTypeDebugInfo, then this is a dSYM - it will have the load command claiming an eh_frame + // but it doesn't actually have the eh_frame content. And if we have a dSYM, we don't need to do any + // of this fill-in-the-missing-symbols works anyway - the debug info should give us all the functions in + // the module. + if (text_section_sp.get() && eh_frame_section_sp.get() && m_type != eTypeDebugInfo) + { + DWARFCallFrameInfo eh_frame(*this, eh_frame_section_sp, eRegisterKindEHFrame, true); + DWARFCallFrameInfo::FunctionAddressAndSizeVector functions; + eh_frame.GetFunctionAddressAndSizeVector (functions); + addr_t text_base_addr = text_section_sp->GetFileAddress(); + size_t count = functions.GetSize(); + for (size_t i = 0; i < count; ++i) + { + const DWARFCallFrameInfo::FunctionAddressAndSizeVector::Entry *func = functions.GetEntryAtIndex (i); + if (func) + { + FunctionStarts::Entry function_start_entry; + function_start_entry.addr = func->base - text_base_addr; + function_starts.Append(function_start_entry); + } + } + } + } + + const size_t function_starts_count = function_starts.GetSize(); + + const user_id_t TEXT_eh_frame_sectID = + eh_frame_section_sp.get() ? eh_frame_section_sp->GetID() + : static_cast(NO_SECT); + + lldb::offset_t nlist_data_offset = 0; + + uint32_t N_SO_index = UINT32_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; + typedef std::multimap ValueToSymbolIndexMap; + typedef std::map NListIndexToSymbolIndexMap; + typedef std::map ConstNameToSymbolIndexMap; + ValueToSymbolIndexMap N_FUN_addr_to_sym_idx; + ValueToSymbolIndexMap N_STSYM_addr_to_sym_idx; + ConstNameToSymbolIndexMap N_GSYM_name_to_sym_idx; + // Any symbols that get merged into another will get an entry + // in this map so we know + NListIndexToSymbolIndexMap m_nlist_idx_to_sym_idx; + uint32_t nlist_idx = 0; + Symbol *symbol_ptr = NULL; + + uint32_t sym_idx = 0; + Symbol *sym = NULL; + size_t num_syms = 0; + std::string memory_symbol_name; + uint32_t unmapped_local_symbols_found = 0; + + std::vector trie_entries; + std::set resolver_addresses; + + if (dyld_trie_data.GetByteSize() > 0) + { + std::vector nameSlices; + ParseTrieEntries (dyld_trie_data, + 0, + is_arm, + nameSlices, + resolver_addresses, + trie_entries); + + ConstString text_segment_name ("__TEXT"); + SectionSP text_segment_sp = GetSectionList()->FindSectionByName(text_segment_name); + if (text_segment_sp) + { + const lldb::addr_t text_segment_file_addr = text_segment_sp->GetFileAddress(); + if (text_segment_file_addr != LLDB_INVALID_ADDRESS) + { + for (auto &e : trie_entries) + e.entry.address += text_segment_file_addr; + } + } + } + + typedef std::set IndirectSymbols; + IndirectSymbols indirect_symbol_names; + +#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) + + // Some recent builds of the dyld_shared_cache (hereafter: DSC) have been optimized by moving LOCAL + // symbols out of the memory mapped portion of the DSC. The symbol information has all been retained, + // but it isn't available in the normal nlist data. However, there *are* duplicate entries of *some* + // LOCAL symbols in the normal nlist data. To handle this situation correctly, we must first attempt + // to parse any DSC unmapped symbol information. If we find any, we set a flag that tells the normal + // nlist parser to ignore all LOCAL symbols. + + if (m_header.flags & 0x80000000u) + { + // Before we can start mapping the DSC, we need to make certain the target process is actually + // using the cache we can find. + + // Next we need to determine the correct path for the dyld shared cache. + + ArchSpec header_arch; + GetArchitecture(header_arch); + char dsc_path[PATH_MAX]; + char dsc_path_development[PATH_MAX]; + + snprintf(dsc_path, sizeof(dsc_path), "%s%s%s", + "/System/Library/Caches/com.apple.dyld/", /* IPHONE_DYLD_SHARED_CACHE_DIR */ + "dyld_shared_cache_", /* DYLD_SHARED_CACHE_BASE_NAME */ + header_arch.GetArchitectureName()); + + snprintf(dsc_path_development, sizeof(dsc_path), "%s%s%s%s", + "/System/Library/Caches/com.apple.dyld/", /* IPHONE_DYLD_SHARED_CACHE_DIR */ + "dyld_shared_cache_", /* DYLD_SHARED_CACHE_BASE_NAME */ + header_arch.GetArchitectureName(), + ".development"); + + FileSpec dsc_nondevelopment_filespec(dsc_path, false); + FileSpec dsc_development_filespec(dsc_path_development, false); + FileSpec dsc_filespec; + + UUID dsc_uuid; + UUID process_shared_cache_uuid; + + if (process) + { + process_shared_cache_uuid = GetProcessSharedCacheUUID(process); + } + + // First see if we can find an exact match for the inferior process shared cache UUID in + // the development or non-development shared caches on disk. + if (process_shared_cache_uuid.IsValid()) + { + if (dsc_development_filespec.Exists()) + { + UUID dsc_development_uuid = GetSharedCacheUUID (dsc_development_filespec, byte_order, addr_byte_size); + if (dsc_development_uuid.IsValid() && dsc_development_uuid == process_shared_cache_uuid) + { + dsc_filespec = dsc_development_filespec; + dsc_uuid = dsc_development_uuid; + } + } + if (!dsc_uuid.IsValid() && dsc_nondevelopment_filespec.Exists()) + { + UUID dsc_nondevelopment_uuid = GetSharedCacheUUID (dsc_nondevelopment_filespec, byte_order, addr_byte_size); + if (dsc_nondevelopment_uuid.IsValid() && dsc_nondevelopment_uuid == process_shared_cache_uuid) + { + dsc_filespec = dsc_nondevelopment_filespec; + dsc_uuid = dsc_nondevelopment_uuid; + } + } + } + + // Failing a UUID match, prefer the development dyld_shared cache if both are present. + if (!dsc_filespec.Exists()) + { + if (dsc_development_filespec.Exists()) + { + dsc_filespec = dsc_development_filespec; + } + else + { + dsc_filespec = dsc_nondevelopment_filespec; + } + } + + /* The dyld_cache_header has a pointer to the dyld_cache_local_symbols_info structure (localSymbolsOffset). + The dyld_cache_local_symbols_info structure gives us three things: + 1. The start and count of the nlist records in the dyld_shared_cache file + 2. The start and size of the strings for these nlist records + 3. The start and count of dyld_cache_local_symbols_entry entries + + There is one dyld_cache_local_symbols_entry per dylib/framework in the dyld shared cache. + The "dylibOffset" field is the Mach-O header of this dylib/framework in the dyld shared cache. + The dyld_cache_local_symbols_entry also lists the start of this dylib/framework's nlist records + and the count of how many nlist records there are for this dylib/framework. + */ + + // Process the dyld shared cache header to find the unmapped symbols + + DataBufferSP dsc_data_sp = dsc_filespec.MemoryMapFileContentsIfLocal(0, sizeof(struct lldb_copy_dyld_cache_header_v1)); + if (!dsc_uuid.IsValid()) + { + dsc_uuid = GetSharedCacheUUID (dsc_filespec, byte_order, addr_byte_size); + } + if (dsc_data_sp) + { + DataExtractor dsc_header_data (dsc_data_sp, byte_order, addr_byte_size); + + bool uuid_match = true; + if (dsc_uuid.IsValid() && process) + { + if (process_shared_cache_uuid.IsValid() && dsc_uuid != process_shared_cache_uuid) + { + // The on-disk dyld_shared_cache file is not the same as the one in this + // process' memory, don't use it. + uuid_match = false; + ModuleSP module_sp (GetModule()); + if (module_sp) + module_sp->ReportWarning ("process shared cache does not match on-disk dyld_shared_cache file, some symbol names will be missing."); + } + } + + offset = offsetof (struct lldb_copy_dyld_cache_header_v1, mappingOffset); + + uint32_t mappingOffset = dsc_header_data.GetU32(&offset); + + // If the mappingOffset points to a location inside the header, we've + // opened an old dyld shared cache, and should not proceed further. + if (uuid_match && mappingOffset >= sizeof(struct lldb_copy_dyld_cache_header_v1)) + { + + DataBufferSP dsc_mapping_info_data_sp = dsc_filespec.MemoryMapFileContentsIfLocal(mappingOffset, sizeof (struct lldb_copy_dyld_cache_mapping_info)); + DataExtractor dsc_mapping_info_data(dsc_mapping_info_data_sp, byte_order, addr_byte_size); + offset = 0; + + // The File addresses (from the in-memory Mach-O load commands) for the shared libraries + // in the shared library cache need to be adjusted by an offset to match up with the + // dylibOffset identifying field in the dyld_cache_local_symbol_entry's. This offset is + // recorded in mapping_offset_value. + const uint64_t mapping_offset_value = dsc_mapping_info_data.GetU64(&offset); + + offset = offsetof (struct lldb_copy_dyld_cache_header_v1, localSymbolsOffset); + uint64_t localSymbolsOffset = dsc_header_data.GetU64(&offset); + uint64_t localSymbolsSize = dsc_header_data.GetU64(&offset); + + if (localSymbolsOffset && localSymbolsSize) + { + // Map the local symbols + if (DataBufferSP dsc_local_symbols_data_sp = dsc_filespec.MemoryMapFileContentsIfLocal(localSymbolsOffset, localSymbolsSize)) + { + DataExtractor dsc_local_symbols_data(dsc_local_symbols_data_sp, byte_order, addr_byte_size); + + offset = 0; + + typedef std::map UndefinedNameToDescMap; + typedef std::map SymbolIndexToName; + UndefinedNameToDescMap undefined_name_to_desc; + SymbolIndexToName reexport_shlib_needs_fixup; + + + // Read the local_symbols_infos struct in one shot + struct lldb_copy_dyld_cache_local_symbols_info local_symbols_info; + dsc_local_symbols_data.GetU32(&offset, &local_symbols_info.nlistOffset, 6); + + SectionSP text_section_sp(section_list->FindSectionByName(GetSegmentNameTEXT())); + + uint32_t header_file_offset = (text_section_sp->GetFileAddress() - mapping_offset_value); + + offset = local_symbols_info.entriesOffset; + for (uint32_t entry_index = 0; entry_index < local_symbols_info.entriesCount; entry_index++) + { + struct lldb_copy_dyld_cache_local_symbols_entry local_symbols_entry; + local_symbols_entry.dylibOffset = dsc_local_symbols_data.GetU32(&offset); + local_symbols_entry.nlistStartIndex = dsc_local_symbols_data.GetU32(&offset); + local_symbols_entry.nlistCount = dsc_local_symbols_data.GetU32(&offset); + + if (header_file_offset == local_symbols_entry.dylibOffset) + { + unmapped_local_symbols_found = local_symbols_entry.nlistCount; + + // The normal nlist code cannot correctly size the Symbols array, we need to allocate it here. + sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms + unmapped_local_symbols_found - m_dysymtab.nlocalsym); + num_syms = symtab->GetNumSymbols(); + + nlist_data_offset = local_symbols_info.nlistOffset + (nlist_byte_size * local_symbols_entry.nlistStartIndex); + uint32_t string_table_offset = local_symbols_info.stringsOffset; + + for (uint32_t nlist_index = 0; nlist_index < local_symbols_entry.nlistCount; nlist_index++) + { + ///////////////////////////// + { + struct nlist_64 nlist; + if (!dsc_local_symbols_data.ValidOffsetForDataOfSize(nlist_data_offset, nlist_byte_size)) + break; + + nlist.n_strx = dsc_local_symbols_data.GetU32_unchecked(&nlist_data_offset); + nlist.n_type = dsc_local_symbols_data.GetU8_unchecked (&nlist_data_offset); + nlist.n_sect = dsc_local_symbols_data.GetU8_unchecked (&nlist_data_offset); + nlist.n_desc = dsc_local_symbols_data.GetU16_unchecked (&nlist_data_offset); + nlist.n_value = dsc_local_symbols_data.GetAddress_unchecked (&nlist_data_offset); + + SymbolType type = eSymbolTypeInvalid; + const char *symbol_name = dsc_local_symbols_data.PeekCStr(string_table_offset + nlist.n_strx); + + if (symbol_name == NULL) + { + // No symbol should be NULL, even the symbols with no + // string values should have an offset zero which points + // to an empty C-string + Host::SystemLog (Host::eSystemLogError, + "error: DSC unmapped local symbol[%u] has invalid string table offset 0x%x in %s, ignoring symbol\n", + entry_index, + nlist.n_strx, + module_sp->GetFileSpec().GetPath().c_str()); + continue; + } + if (symbol_name[0] == '\0') + symbol_name = NULL; + + const char *symbol_name_non_abi_mangled = NULL; + + SectionSP symbol_section; + uint32_t symbol_byte_size = 0; + bool add_nlist = true; + bool is_debug = ((nlist.n_type & N_STAB) != 0); + bool demangled_is_synthesized = false; + bool is_gsym = false; + bool set_value = true; + + 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. + + // FIXME: In the .o files, we have a GSYM and a debug symbol for all the ObjC data. They + // have the same address, but we want to ensure that we always find only the real symbol, + // 'cause we don't currently correctly attribute the GSYM one to the ObjCClass/Ivar/MetaClass + // symbol type. This is a temporary hack to make sure the ObjectiveC symbols get treated + // correctly. To do this right, we should coalesce all the GSYM & global symbols that have the + // same address. + + is_gsym = true; + sym[sym_idx].SetExternal(true); + + if (symbol_name && symbol_name[0] == '_' && symbol_name[1] == 'O') + { + llvm::StringRef symbol_name_ref(symbol_name); + if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size(); + type = eSymbolTypeObjCMetaClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_ivar.size(); + type = eSymbolTypeObjCIVar; + demangled_is_synthesized = true; + } + } + else + { + if (nlist.n_value != 0) + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeData; + } + break; + + case N_FNAME: + // procedure name (f77 kludge): name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_FUN: + // procedure: name,,n_sect,linenumber,address + if (symbol_name) + { + type = eSymbolTypeCode; + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + N_FUN_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx)); + // 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 = eSymbolTypeCompiler; + + 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 don't 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 + add_nlist = false; + } + } + break; + + case N_STSYM: + // static symbol: name,,n_sect,type,address + N_STSYM_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx)); + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if (symbol_name && symbol_name[0]) + { + type = ObjectFile::GetSymbolTypeFromName(symbol_name+1, eSymbolTypeData); + } + 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 + // Skip these if we want minimal symbol tables + add_nlist = false; + 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 + // Skip these if we want minimal symbol tables + add_nlist = false; + 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: + // source file name + type = eSymbolTypeSourceFile; + if (symbol_name == NULL) + { + add_nlist = false; + if (N_SO_index != UINT32_MAX) + { + // 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); + 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 = UINT32_MAX; + } + else + { + // 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 + const bool N_SO_has_full_path = symbol_name[0] == '/'; + if (N_SO_has_full_path) + { + if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) + { + // We have two consecutive N_SO entries where the first contains a directory + // and the second contains a full path. + sym[sym_idx - 1].GetMangled().SetValue(ConstString(symbol_name), false); + m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1; + add_nlist = false; + } + else + { + // This is the first entry in a N_SO that contains a directory or + // a full path to the source file + N_SO_index = sym_idx; + } + } + else if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) + { + // This is usually the second N_SO entry that contains just the filename, + // so here we combine it with the first one if we are minimizing the symbol table + const char *so_path = sym[sym_idx - 1].GetMangled().GetDemangledName().AsCString(); + if (so_path && so_path[0]) + { + std::string full_so_path (so_path); + const size_t double_slash_pos = full_so_path.find("//"); + if (double_slash_pos != std::string::npos) + { + // The linker has been generating bad N_SO entries with doubled up paths + // in the format "%s%s" where the first string in the DW_AT_comp_dir, + // and the second is the directory for the source file so you end up with + // a path that looks like "/tmp/src//tmp/src/" + FileSpec so_dir(so_path, false); + if (!so_dir.Exists()) + { + so_dir.SetFile(&full_so_path[double_slash_pos + 1], false); + if (so_dir.Exists()) + { + // Trim off the incorrect path + full_so_path.erase(0, double_slash_pos + 1); + } + } + } + if (*full_so_path.rbegin() != '/') + full_so_path += '/'; + full_so_path += symbol_name; + sym[sym_idx - 1].GetMangled().SetValue(ConstString(full_so_path.c_str()), false); + add_nlist = false; + m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1; + } + } + else + { + // This could be a relative path to a N_SO + 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; + + // We currently don't use the header files on darwin + add_nlist = false; + 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); + + switch (n_type) + { + case N_INDR: + { + const char *reexport_name_cstr = strtab_data.PeekCStr(nlist.n_value); + if (reexport_name_cstr && reexport_name_cstr[0]) + { + type = eSymbolTypeReExported; + ConstString reexport_name(reexport_name_cstr + ((reexport_name_cstr[0] == '_') ? 1 : 0)); + sym[sym_idx].SetReExportedSymbolName(reexport_name); + set_value = false; + reexport_shlib_needs_fixup[sym_idx] = reexport_name; + indirect_symbol_names.insert(ConstString(symbol_name + ((symbol_name[0] == '_') ? 1 : 0))); + } + else + type = eSymbolTypeUndefined; + } + break; + + case N_UNDF: + if (symbol_name && symbol_name[0]) + { + ConstString undefined_name(symbol_name + ((symbol_name[0] == '_') ? 1 : 0)); + undefined_name_to_desc[undefined_name] = nlist.n_desc; + } + // Fall through + case N_PBUD: + type = eSymbolTypeUndefined; + break; + + case N_ABS: + type = eSymbolTypeAbsolute; + break; + + case N_SECT: + { + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + if (symbol_section == NULL) + { + // TODO: warn about this? + add_nlist = false; + break; + } + + if (TEXT_eh_frame_sectID == nlist.n_sect) + { + type = eSymbolTypeException; + } + else + { + uint32_t section_type = symbol_section->Get() & SECTION_TYPE; + + switch (section_type) + { + 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_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: + switch (symbol_section->GetType()) + { + case lldb::eSectionTypeCode: + type = eSymbolTypeCode; + break; + case eSectionTypeData: + case eSectionTypeDataCString: // Inlined C string data + case eSectionTypeDataCStringPointers: // Pointers to C string data + case eSectionTypeDataSymbolAddress: // Address of a symbol in the symbol table + case eSectionTypeData4: + case eSectionTypeData8: + case eSectionTypeData16: + type = eSymbolTypeData; + break; + default: + break; + } + 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()) || + symbol_section->IsDescendant(data_dirty_section_sp.get()) || + symbol_section->IsDescendant(data_const_section_sp.get())) + { + if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name) + { + type = eSymbolTypeRuntime; + + if (symbol_name) + { + llvm::StringRef symbol_name_ref(symbol_name); + if (symbol_name_ref.startswith("_OBJC_")) + { + static const llvm::StringRef g_objc_v2_prefix_class ("_OBJC_CLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_metaclass ("_OBJC_METACLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_ivar ("_OBJC_IVAR_$_"); + if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size(); + type = eSymbolTypeObjCMetaClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_ivar.size(); + type = eSymbolTypeObjCIVar; + demangled_is_synthesized = true; + } + } + } + } + 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; + if (symbol_name && symbol_name[0] == '.') + { + llvm::StringRef symbol_name_ref(symbol_name); + static const llvm::StringRef g_objc_v1_prefix_class (".objc_class_name_"); + if (symbol_name_ref.startswith(g_objc_v1_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name; + symbol_name = symbol_name + g_objc_v1_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + } + } + } + } + } + } + break; + } + } + + if (add_nlist) + { + uint64_t symbol_value = nlist.n_value; + if (symbol_name_non_abi_mangled) + { + sym[sym_idx].GetMangled().SetMangledName (ConstString(symbol_name_non_abi_mangled)); + sym[sym_idx].GetMangled().SetDemangledName (ConstString(symbol_name)); + } + else + { + 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 + } + + if (symbol_name) + { + ConstString const_symbol_name(symbol_name); + sym[sym_idx].GetMangled().SetValue(const_symbol_name, symbol_name_is_mangled); + if (is_gsym && is_debug) + { + const char *gsym_name = sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled).GetCString(); + if (gsym_name) + N_GSYM_name_to_sym_idx[gsym_name] = sym_idx; + } + } + } + if (symbol_section) + { + const addr_t section_file_addr = symbol_section->GetFileAddress(); + if (symbol_byte_size == 0 && function_starts_count > 0) + { + addr_t symbol_lookup_file_addr = nlist.n_value; + // Do an exact address match for non-ARM addresses, else get the closest since + // the symbol might be a thumb symbol which has an address with bit zero set + FunctionStarts::Entry *func_start_entry = function_starts.FindEntry (symbol_lookup_file_addr, !is_arm); + if (is_arm && func_start_entry) + { + // Verify that the function start address is the symbol address (ARM) + // or the symbol address + 1 (thumb) + if (func_start_entry->addr != symbol_lookup_file_addr && + func_start_entry->addr != (symbol_lookup_file_addr + 1)) + { + // Not the right entry, NULL it out... + func_start_entry = NULL; + } + } + if (func_start_entry) + { + func_start_entry->data = true; + + addr_t symbol_file_addr = func_start_entry->addr; + uint32_t symbol_flags = 0; + if (is_arm) + { + if (symbol_file_addr & 1) + symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB; + symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + } + + const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry); + const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize(); + if (next_func_start_entry) + { + addr_t next_symbol_file_addr = next_func_start_entry->addr; + // Be sure the clear the Thumb address bit when we calculate the size + // from the current and next address + if (is_arm) + next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + symbol_byte_size = std::min(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr); + } + else + { + symbol_byte_size = section_end_file_addr - symbol_file_addr; + } + } + } + symbol_value -= section_file_addr; + } + + if (is_debug == false) + { + if (type == eSymbolTypeCode) + { + // See if we can find a N_FUN entry for any code symbols. + // If we do find a match, and the name matches, then we + // can merge the two into just the function symbol to avoid + // duplicate entries in the symbol table + std::pair range; + range = N_FUN_addr_to_sym_idx.equal_range(nlist.n_value); + if (range.first != range.second) + { + bool found_it = false; + for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos) + { + if (sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(Mangled::ePreferMangled)) + { + m_nlist_idx_to_sym_idx[nlist_idx] = pos->second; + // We just need the flags from the linker symbol, so put these flags + // into the N_FUN flags to avoid duplicate symbols in the symbol table + sym[pos->second].SetExternal(sym[sym_idx].IsExternal()); + sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc); + if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end()) + sym[pos->second].SetType (eSymbolTypeResolver); + sym[sym_idx].Clear(); + found_it = true; + break; + } + } + if (found_it) + continue; + } + else + { + if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end()) + type = eSymbolTypeResolver; + } + } + else if (type == eSymbolTypeData || + type == eSymbolTypeObjCClass || + type == eSymbolTypeObjCMetaClass || + type == eSymbolTypeObjCIVar ) + { + // See if we can find a N_STSYM entry for any data symbols. + // If we do find a match, and the name matches, then we + // can merge the two into just the Static symbol to avoid + // duplicate entries in the symbol table + std::pair range; + range = N_STSYM_addr_to_sym_idx.equal_range(nlist.n_value); + if (range.first != range.second) + { + bool found_it = false; + for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos) + { + if (sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(Mangled::ePreferMangled)) + { + m_nlist_idx_to_sym_idx[nlist_idx] = pos->second; + // We just need the flags from the linker symbol, so put these flags + // into the N_STSYM flags to avoid duplicate symbols in the symbol table + sym[pos->second].SetExternal(sym[sym_idx].IsExternal()); + sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc); + sym[sym_idx].Clear(); + found_it = true; + break; + } + } + if (found_it) + continue; + } + else + { + const char *gsym_name = sym[sym_idx].GetMangled().GetName(Mangled::ePreferMangled).GetCString(); + if (gsym_name) + { + // Combine N_GSYM stab entries with the non stab symbol + ConstNameToSymbolIndexMap::const_iterator pos = N_GSYM_name_to_sym_idx.find(gsym_name); + if (pos != N_GSYM_name_to_sym_idx.end()) + { + const uint32_t GSYM_sym_idx = pos->second; + m_nlist_idx_to_sym_idx[nlist_idx] = GSYM_sym_idx; + // Copy the address, because often the N_GSYM address has an invalid address of zero + // when the global is a common symbol + sym[GSYM_sym_idx].GetAddressRef().SetSection (symbol_section); + sym[GSYM_sym_idx].GetAddressRef().SetOffset (symbol_value); + // We just need the flags from the linker symbol, so put these flags + // into the N_GSYM flags to avoid duplicate symbols in the symbol table + sym[GSYM_sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + sym[sym_idx].Clear(); + continue; + } + } + } + } + } + + sym[sym_idx].SetID (nlist_idx); + sym[sym_idx].SetType (type); + if (set_value) + { + sym[sym_idx].GetAddressRef().SetSection (symbol_section); + sym[sym_idx].GetAddressRef().SetOffset (symbol_value); + } + sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + + if (symbol_byte_size > 0) + sym[sym_idx].SetByteSize(symbol_byte_size); + + if (demangled_is_synthesized) + sym[sym_idx].SetDemangledNameIsSynthesized(true); + ++sym_idx; + } + else + { + sym[sym_idx].Clear(); + } + + } + ///////////////////////////// + } + break; // No more entries to consider + } + } + + for (const auto &pos :reexport_shlib_needs_fixup) + { + const auto undef_pos = undefined_name_to_desc.find(pos.second); + if (undef_pos != undefined_name_to_desc.end()) + { + const uint8_t dylib_ordinal = llvm::MachO::GET_LIBRARY_ORDINAL(undef_pos->second); + if (dylib_ordinal > 0 && dylib_ordinal < dylib_files.GetSize()) + sym[pos.first].SetReExportedSymbolSharedLibrary(dylib_files.GetFileSpecAtIndex(dylib_ordinal-1)); + } + } + } + } + } + } + } + + // Must reset this in case it was mutated above! + nlist_data_offset = 0; +#endif + + if (nlist_data.GetByteSize() > 0) + { + + // If the sym array was not created while parsing the DSC unmapped + // symbols, create it now. + if (sym == NULL) + { + sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms); + num_syms = symtab->GetNumSymbols(); + } + + if (unmapped_local_symbols_found) + { + assert(m_dysymtab.ilocalsym == 0); + nlist_data_offset += (m_dysymtab.nlocalsym * nlist_byte_size); + nlist_idx = m_dysymtab.nlocalsym; + } + else + { + nlist_idx = 0; + } + + typedef std::map UndefinedNameToDescMap; + typedef std::map SymbolIndexToName; + UndefinedNameToDescMap undefined_name_to_desc; + SymbolIndexToName reexport_shlib_needs_fixup; + for (; nlist_idx < symtab_load_command.nsyms; ++nlist_idx) + { + struct nlist_64 nlist; + if (!nlist_data.ValidOffsetForDataOfSize(nlist_data_offset, nlist_byte_size)) + break; + + nlist.n_strx = nlist_data.GetU32_unchecked(&nlist_data_offset); + nlist.n_type = nlist_data.GetU8_unchecked (&nlist_data_offset); + nlist.n_sect = nlist_data.GetU8_unchecked (&nlist_data_offset); + nlist.n_desc = nlist_data.GetU16_unchecked (&nlist_data_offset); + nlist.n_value = nlist_data.GetAddress_unchecked (&nlist_data_offset); + + SymbolType type = eSymbolTypeInvalid; + const char *symbol_name = NULL; + + if (have_strtab_data) + { + symbol_name = strtab_data.PeekCStr(nlist.n_strx); + + if (symbol_name == NULL) + { + // No symbol should be NULL, even the symbols with no + // string values should have an offset zero which points + // to an empty C-string + Host::SystemLog (Host::eSystemLogError, + "error: symbol[%u] has invalid string table offset 0x%x in %s, ignoring symbol\n", + nlist_idx, + nlist.n_strx, + module_sp->GetFileSpec().GetPath().c_str()); + continue; + } + if (symbol_name[0] == '\0') + symbol_name = NULL; + } + else + { + const addr_t str_addr = strtab_addr + nlist.n_strx; + Error str_error; + if (process->ReadCStringFromMemory(str_addr, memory_symbol_name, str_error)) + symbol_name = memory_symbol_name.c_str(); + } + const char *symbol_name_non_abi_mangled = NULL; + + SectionSP symbol_section; + lldb::addr_t symbol_byte_size = 0; + bool add_nlist = true; + bool is_gsym = false; + bool is_debug = ((nlist.n_type & N_STAB) != 0); + bool demangled_is_synthesized = false; + bool set_value = true; + 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. + + // FIXME: In the .o files, we have a GSYM and a debug symbol for all the ObjC data. They + // have the same address, but we want to ensure that we always find only the real symbol, + // 'cause we don't currently correctly attribute the GSYM one to the ObjCClass/Ivar/MetaClass + // symbol type. This is a temporary hack to make sure the ObjectiveC symbols get treated + // correctly. To do this right, we should coalesce all the GSYM & global symbols that have the + // same address. + is_gsym = true; + sym[sym_idx].SetExternal(true); + + if (symbol_name && symbol_name[0] == '_' && symbol_name[1] == 'O') + { + llvm::StringRef symbol_name_ref(symbol_name); + if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size(); + type = eSymbolTypeObjCMetaClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_ivar.size(); + type = eSymbolTypeObjCIVar; + demangled_is_synthesized = true; + } + } + else + { + if (nlist.n_value != 0) + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeData; + } + break; + + case N_FNAME: + // procedure name (f77 kludge): name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_FUN: + // procedure: name,,n_sect,linenumber,address + if (symbol_name) + { + type = eSymbolTypeCode; + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + N_FUN_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx)); + // 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 = eSymbolTypeCompiler; + + 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 don't 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 + add_nlist = false; + } + } + break; + + case N_STSYM: + // static symbol: name,,n_sect,type,address + N_STSYM_addr_to_sym_idx.insert(std::make_pair(nlist.n_value, sym_idx)); + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if (symbol_name && symbol_name[0]) + { + type = ObjectFile::GetSymbolTypeFromName(symbol_name+1, eSymbolTypeData); + } + 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 + // Skip these if we want minimal symbol tables + add_nlist = false; + 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 + // Skip these if we want minimal symbol tables + add_nlist = false; + 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: + // source file name + type = eSymbolTypeSourceFile; + if (symbol_name == NULL) + { + add_nlist = false; + if (N_SO_index != UINT32_MAX) + { + // 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); + 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 = UINT32_MAX; + } + else + { + // 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 + const bool N_SO_has_full_path = symbol_name[0] == '/'; + if (N_SO_has_full_path) + { + if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) + { + // We have two consecutive N_SO entries where the first contains a directory + // and the second contains a full path. + sym[sym_idx - 1].GetMangled().SetValue(ConstString(symbol_name), false); + m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1; + add_nlist = false; + } + else + { + // This is the first entry in a N_SO that contains a directory or + // a full path to the source file + N_SO_index = sym_idx; + } + } + else if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) + { + // This is usually the second N_SO entry that contains just the filename, + // so here we combine it with the first one if we are minimizing the symbol table + const char *so_path = sym[sym_idx - 1].GetMangled().GetDemangledName(lldb::eLanguageTypeUnknown).AsCString(); + if (so_path && so_path[0]) + { + std::string full_so_path (so_path); + const size_t double_slash_pos = full_so_path.find("//"); + if (double_slash_pos != std::string::npos) + { + // The linker has been generating bad N_SO entries with doubled up paths + // in the format "%s%s" where the first string in the DW_AT_comp_dir, + // and the second is the directory for the source file so you end up with + // a path that looks like "/tmp/src//tmp/src/" + FileSpec so_dir(so_path, false); + if (!so_dir.Exists()) + { + so_dir.SetFile(&full_so_path[double_slash_pos + 1], false); + if (so_dir.Exists()) + { + // Trim off the incorrect path + full_so_path.erase(0, double_slash_pos + 1); + } + } + } + if (*full_so_path.rbegin() != '/') + full_so_path += '/'; + full_so_path += symbol_name; + sym[sym_idx - 1].GetMangled().SetValue(ConstString(full_so_path.c_str()), false); + add_nlist = false; + m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1; + } + } + else + { + // This could be a relative path to a N_SO + 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; + + // We currently don't use the header files on darwin + add_nlist = false; + 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); + + switch (n_type) + { + case N_INDR: + { + const char *reexport_name_cstr = strtab_data.PeekCStr(nlist.n_value); + if (reexport_name_cstr && reexport_name_cstr[0]) + { + type = eSymbolTypeReExported; + ConstString reexport_name(reexport_name_cstr + ((reexport_name_cstr[0] == '_') ? 1 : 0)); + sym[sym_idx].SetReExportedSymbolName(reexport_name); + set_value = false; + reexport_shlib_needs_fixup[sym_idx] = reexport_name; + indirect_symbol_names.insert(ConstString(symbol_name + ((symbol_name[0] == '_') ? 1 : 0))); + } + else + type = eSymbolTypeUndefined; + } + break; + + case N_UNDF: + if (symbol_name && symbol_name[0]) + { + ConstString undefined_name(symbol_name + ((symbol_name[0] == '_') ? 1 : 0)); + undefined_name_to_desc[undefined_name] = nlist.n_desc; + } + // Fall through + case N_PBUD: + type = eSymbolTypeUndefined; + break; + + case N_ABS: + type = eSymbolTypeAbsolute; + break; + + case N_SECT: + { + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + if (!symbol_section) + { + // TODO: warn about this? + add_nlist = false; + break; + } + + if (TEXT_eh_frame_sectID == nlist.n_sect) + { + type = eSymbolTypeException; + } + else + { + uint32_t section_type = symbol_section->Get() & SECTION_TYPE; + + switch (section_type) + { + 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_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: + switch (symbol_section->GetType()) + { + case lldb::eSectionTypeCode: + type = eSymbolTypeCode; + break; + case eSectionTypeData: + case eSectionTypeDataCString: // Inlined C string data + case eSectionTypeDataCStringPointers: // Pointers to C string data + case eSectionTypeDataSymbolAddress: // Address of a symbol in the symbol table + case eSectionTypeData4: + case eSectionTypeData8: + case eSectionTypeData16: + type = eSymbolTypeData; + break; + default: + break; + } + 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()) || + symbol_section->IsDescendant(data_dirty_section_sp.get()) || + symbol_section->IsDescendant(data_const_section_sp.get())) + { + if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name) + { + type = eSymbolTypeRuntime; + + if (symbol_name) + { + llvm::StringRef symbol_name_ref(symbol_name); + if (symbol_name_ref.startswith("_OBJC_")) + { + static const llvm::StringRef g_objc_v2_prefix_class ("_OBJC_CLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_metaclass ("_OBJC_METACLASS_$_"); + static const llvm::StringRef g_objc_v2_prefix_ivar ("_OBJC_IVAR_$_"); + if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size(); + type = eSymbolTypeObjCMetaClass; + demangled_is_synthesized = true; + } + else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) + { + symbol_name_non_abi_mangled = symbol_name + 1; + symbol_name = symbol_name + g_objc_v2_prefix_ivar.size(); + type = eSymbolTypeObjCIVar; + demangled_is_synthesized = true; + } + } + } + } + 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; + if (symbol_name && symbol_name[0] == '.') + { + llvm::StringRef symbol_name_ref(symbol_name); + static const llvm::StringRef g_objc_v1_prefix_class (".objc_class_name_"); + if (symbol_name_ref.startswith(g_objc_v1_prefix_class)) + { + symbol_name_non_abi_mangled = symbol_name; + symbol_name = symbol_name + g_objc_v1_prefix_class.size(); + type = eSymbolTypeObjCClass; + demangled_is_synthesized = true; + } + } + } + } + } + } + break; + } + } + + if (add_nlist) + { + uint64_t symbol_value = nlist.n_value; + + if (symbol_name_non_abi_mangled) + { + sym[sym_idx].GetMangled().SetMangledName (ConstString(symbol_name_non_abi_mangled)); + sym[sym_idx].GetMangled().SetDemangledName (ConstString(symbol_name)); + } + else + { + 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 + } + + if (symbol_name) + { + ConstString const_symbol_name(symbol_name); + sym[sym_idx].GetMangled().SetValue(const_symbol_name, symbol_name_is_mangled); + } + } + + if (is_gsym) + { + const char *gsym_name = sym[sym_idx].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled).GetCString(); + if (gsym_name) + N_GSYM_name_to_sym_idx[gsym_name] = sym_idx; + } + + if (symbol_section) + { + const addr_t section_file_addr = symbol_section->GetFileAddress(); + if (symbol_byte_size == 0 && function_starts_count > 0) + { + addr_t symbol_lookup_file_addr = nlist.n_value; + // Do an exact address match for non-ARM addresses, else get the closest since + // the symbol might be a thumb symbol which has an address with bit zero set + FunctionStarts::Entry *func_start_entry = function_starts.FindEntry (symbol_lookup_file_addr, !is_arm); + if (is_arm && func_start_entry) + { + // Verify that the function start address is the symbol address (ARM) + // or the symbol address + 1 (thumb) + if (func_start_entry->addr != symbol_lookup_file_addr && + func_start_entry->addr != (symbol_lookup_file_addr + 1)) + { + // Not the right entry, NULL it out... + func_start_entry = NULL; + } + } + if (func_start_entry) + { + func_start_entry->data = true; + + addr_t symbol_file_addr = func_start_entry->addr; + if (is_arm) + symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + + const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry); + const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize(); + if (next_func_start_entry) + { + addr_t next_symbol_file_addr = next_func_start_entry->addr; + // Be sure the clear the Thumb address bit when we calculate the size + // from the current and next address + if (is_arm) + next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + symbol_byte_size = std::min(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr); + } + else + { + symbol_byte_size = section_end_file_addr - symbol_file_addr; + } + } + } + symbol_value -= section_file_addr; + } + + if (is_debug == false) + { + if (type == eSymbolTypeCode) + { + // See if we can find a N_FUN entry for any code symbols. + // If we do find a match, and the name matches, then we + // can merge the two into just the function symbol to avoid + // duplicate entries in the symbol table + std::pair range; + range = N_FUN_addr_to_sym_idx.equal_range(nlist.n_value); + if (range.first != range.second) + { + bool found_it = false; + for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos) + { + if (sym[sym_idx].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled)) + { + m_nlist_idx_to_sym_idx[nlist_idx] = pos->second; + // We just need the flags from the linker symbol, so put these flags + // into the N_FUN flags to avoid duplicate symbols in the symbol table + sym[pos->second].SetExternal(sym[sym_idx].IsExternal()); + sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc); + if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end()) + sym[pos->second].SetType (eSymbolTypeResolver); + sym[sym_idx].Clear(); + found_it = true; + break; + } + } + if (found_it) + continue; + } + else + { + if (resolver_addresses.find(nlist.n_value) != resolver_addresses.end()) + type = eSymbolTypeResolver; + } + } + else if (type == eSymbolTypeData || + type == eSymbolTypeObjCClass || + type == eSymbolTypeObjCMetaClass || + type == eSymbolTypeObjCIVar ) + { + // See if we can find a N_STSYM entry for any data symbols. + // If we do find a match, and the name matches, then we + // can merge the two into just the Static symbol to avoid + // duplicate entries in the symbol table + std::pair range; + range = N_STSYM_addr_to_sym_idx.equal_range(nlist.n_value); + if (range.first != range.second) + { + bool found_it = false; + for (ValueToSymbolIndexMap::const_iterator pos = range.first; pos != range.second; ++pos) + { + if (sym[sym_idx].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled) == sym[pos->second].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled)) + { + m_nlist_idx_to_sym_idx[nlist_idx] = pos->second; + // We just need the flags from the linker symbol, so put these flags + // into the N_STSYM flags to avoid duplicate symbols in the symbol table + sym[pos->second].SetExternal(sym[sym_idx].IsExternal()); + sym[pos->second].SetFlags (nlist.n_type << 16 | nlist.n_desc); + sym[sym_idx].Clear(); + found_it = true; + break; + } + } + if (found_it) + continue; + } + else + { + // Combine N_GSYM stab entries with the non stab symbol + const char *gsym_name = sym[sym_idx].GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled).GetCString(); + if (gsym_name) + { + ConstNameToSymbolIndexMap::const_iterator pos = N_GSYM_name_to_sym_idx.find(gsym_name); + if (pos != N_GSYM_name_to_sym_idx.end()) + { + const uint32_t GSYM_sym_idx = pos->second; + m_nlist_idx_to_sym_idx[nlist_idx] = GSYM_sym_idx; + // Copy the address, because often the N_GSYM address has an invalid address of zero + // when the global is a common symbol + sym[GSYM_sym_idx].GetAddressRef().SetSection (symbol_section); + sym[GSYM_sym_idx].GetAddressRef().SetOffset (symbol_value); + // We just need the flags from the linker symbol, so put these flags + // into the N_GSYM flags to avoid duplicate symbols in the symbol table + sym[GSYM_sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + sym[sym_idx].Clear(); + continue; + } + } + } + } + } + + sym[sym_idx].SetID (nlist_idx); + sym[sym_idx].SetType (type); + if (set_value) + { + sym[sym_idx].GetAddressRef().SetSection (symbol_section); + sym[sym_idx].GetAddressRef().SetOffset (symbol_value); + } + sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + + if (symbol_byte_size > 0) + sym[sym_idx].SetByteSize(symbol_byte_size); + + if (demangled_is_synthesized) + sym[sym_idx].SetDemangledNameIsSynthesized(true); + + ++sym_idx; + } + else + { + sym[sym_idx].Clear(); + } + } + + for (const auto &pos :reexport_shlib_needs_fixup) + { + const auto undef_pos = undefined_name_to_desc.find(pos.second); + if (undef_pos != undefined_name_to_desc.end()) + { + const uint8_t dylib_ordinal = llvm::MachO::GET_LIBRARY_ORDINAL(undef_pos->second); + if (dylib_ordinal > 0 && dylib_ordinal < dylib_files.GetSize()) + sym[pos.first].SetReExportedSymbolSharedLibrary(dylib_files.GetFileSpecAtIndex(dylib_ordinal-1)); + } + } + } + + uint32_t synthetic_sym_id = symtab_load_command.nsyms; + + if (function_starts_count > 0) + { + char synthetic_function_symbol[PATH_MAX]; + uint32_t num_synthetic_function_symbols = 0; + for (i=0; i 0) + { + if (num_syms < sym_idx + num_synthetic_function_symbols) + { + num_syms = sym_idx + num_synthetic_function_symbols; + sym = symtab->Resize (num_syms); + } + uint32_t synthetic_function_symbol_idx = 0; + for (i=0; idata == false) + { + addr_t symbol_file_addr = func_start_entry->addr; + uint32_t symbol_flags = 0; + if (is_arm) + { + if (symbol_file_addr & 1) + symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB; + symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + } + Address symbol_addr; + if (module_sp->ResolveFileAddress (symbol_file_addr, symbol_addr)) + { + SectionSP symbol_section (symbol_addr.GetSection()); + uint32_t symbol_byte_size = 0; + if (symbol_section) + { + const addr_t section_file_addr = symbol_section->GetFileAddress(); + const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry); + const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize(); + if (next_func_start_entry) + { + addr_t next_symbol_file_addr = next_func_start_entry->addr; + if (is_arm) + next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; + symbol_byte_size = std::min(next_symbol_file_addr - symbol_file_addr, section_end_file_addr - symbol_file_addr); + } + else + { + symbol_byte_size = section_end_file_addr - symbol_file_addr; + } + snprintf (synthetic_function_symbol, + sizeof(synthetic_function_symbol), + "___lldb_unnamed_function%u$$%s", + ++synthetic_function_symbol_idx, + module_sp->GetFileSpec().GetFilename().GetCString()); + sym[sym_idx].SetID (synthetic_sym_id++); + sym[sym_idx].GetMangled().SetDemangledName(ConstString(synthetic_function_symbol)); + sym[sym_idx].SetType (eSymbolTypeCode); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].GetAddressRef() = symbol_addr; + if (symbol_flags) + sym[sym_idx].SetFlags (symbol_flags); + if (symbol_byte_size) + sym[sym_idx].SetByteSize (symbol_byte_size); + ++sym_idx; + } + } + } + } + } + } + + // Trim our symbols down to just what we ended up with after + // removing any symbols. + if (sym_idx < num_syms) + { + num_syms = sym_idx; + sym = symtab->Resize (num_syms); + } + + // Now synthesize indirect symbols + if (m_dysymtab.nindirectsyms != 0) + { + if (indirect_symbol_index_data.GetByteSize()) + { + NListIndexToSymbolIndexMap::const_iterator end_index_pos = m_nlist_idx_to_sym_idx.end(); + + 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; + 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); + lldb::offset_t symbol_stub_offset = symbol_stub_index * 4; + if (indirect_symbol_index_data.ValidOffsetForDataOfSize(symbol_stub_offset, 4)) + { + const uint32_t stub_sym_id = indirect_symbol_index_data.GetU32 (&symbol_stub_offset); + if (stub_sym_id & (INDIRECT_SYMBOL_ABS | INDIRECT_SYMBOL_LOCAL)) + continue; + + NListIndexToSymbolIndexMap::const_iterator index_pos = m_nlist_idx_to_sym_idx.find (stub_sym_id); + Symbol *stub_symbol = NULL; + if (index_pos != end_index_pos) + { + // We have a remapping from the original nlist index to + // a current symbol index, so just look this up by index + stub_symbol = symtab->SymbolAtIndex (index_pos->second); + } + else + { + // We need to lookup a symbol using the original nlist + // symbol index since this index is coming from the + // S_SYMBOL_STUBS + stub_symbol = symtab->FindSymbolByID (stub_sym_id); + } + + if (stub_symbol) + { + Address so_addr(symbol_stub_addr, section_list); + + if (stub_symbol->GetType() == eSymbolTypeUndefined) + { + // 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. + if (resolver_addresses.find(symbol_stub_addr) == resolver_addresses.end()) + stub_symbol->SetType (eSymbolTypeTrampoline); + else + stub_symbol->SetType (eSymbolTypeResolver); + stub_symbol->SetExternal (false); + stub_symbol->GetAddressRef() = so_addr; + stub_symbol->SetByteSize (symbol_stub_byte_size); + } + else + { + // Make a synthetic symbol to describe the trampoline stub + Mangled stub_symbol_mangled_name(stub_symbol->GetMangled()); + if (sym_idx >= num_syms) + { + sym = symtab->Resize (++num_syms); + stub_symbol = NULL; // this pointer no longer valid + } + sym[sym_idx].SetID (synthetic_sym_id++); + sym[sym_idx].GetMangled() = stub_symbol_mangled_name; + if (resolver_addresses.find(symbol_stub_addr) == resolver_addresses.end()) + sym[sym_idx].SetType (eSymbolTypeTrampoline); + else + sym[sym_idx].SetType (eSymbolTypeResolver); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].GetAddressRef() = so_addr; + sym[sym_idx].SetByteSize (symbol_stub_byte_size); + ++sym_idx; + } + } + else + { + if (log) + log->Warning ("symbol stub referencing symbol table symbol %u that isn't in our minimal symbol table, fix this!!!", stub_sym_id); + } + } + } + } + } + } + } + + if (!trie_entries.empty()) + { + for (const auto &e : trie_entries) + { + if (e.entry.import_name) + { + // Only add indirect symbols from the Trie entries if we + // didn't have a N_INDR nlist entry for this already + if (indirect_symbol_names.find(e.entry.name) == indirect_symbol_names.end()) + { + // Make a synthetic symbol to describe re-exported symbol. + if (sym_idx >= num_syms) + sym = symtab->Resize (++num_syms); + sym[sym_idx].SetID (synthetic_sym_id++); + sym[sym_idx].GetMangled() = Mangled(e.entry.name); + sym[sym_idx].SetType (eSymbolTypeReExported); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].SetReExportedSymbolName(e.entry.import_name); + if (e.entry.other > 0 && e.entry.other <= dylib_files.GetSize()) + { + sym[sym_idx].SetReExportedSymbolSharedLibrary(dylib_files.GetFileSpecAtIndex(e.entry.other-1)); + } + ++sym_idx; + } + } + } + } + +// StreamFile s(stdout, false); +// s.Printf ("Symbol table before CalculateSymbolSizes():\n"); +// symtab->Dump(&s, NULL, eSortOrderNone); + // Set symbol byte sizes correctly since mach-o nlist entries don't have sizes + symtab->CalculateSymbolSizes(); + +// s.Printf ("Symbol table after CalculateSymbolSizes():\n"); +// symtab->Dump(&s, NULL, eSortOrderNone); + + return symtab->GetNumSymbols(); + } + return 0; +} + +void +ObjectFileMachO::Dump (Stream *s) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + s->Printf("%p: ", static_cast(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; + GetArchitecture(header_arch); + + *s << ", file = '" << m_file << "', arch = " << header_arch.GetArchitectureName() << "\n"; + + SectionList *sections = GetSectionList(); + if (sections) + sections->Dump(s, NULL, true, UINT32_MAX); + + if (m_symtab_ap.get()) + m_symtab_ap->Dump(s, NULL, eSortOrderNone); + } +} + +bool +ObjectFileMachO::GetUUID (const llvm::MachO::mach_header &header, + const lldb_private::DataExtractor &data, + lldb::offset_t lc_offset, + lldb_private::UUID& uuid) +{ + uint32_t i; + struct uuid_command load_cmd; + + lldb::offset_t offset = lc_offset; + for (i=0; iGetMutex()); + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + return GetUUID (m_header, m_data, offset, *uuid); + } + return false; +} + +uint32_t +ObjectFileMachO::GetDependentModules (FileSpecList& files) +{ + uint32_t count = 0; + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + struct load_command load_cmd; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + std::vector rpath_paths; + std::vector rpath_relative_paths; + const bool resolve_path = false; // Don't resolve the dependent file paths since they may not reside on this system + uint32_t i; + for (i=0; i corresponding to the flavor. + // + // + // So we just keep reading the various register flavors till we find the GPR one, then read the PC out of there. + // FIXME: We will need to have a "RegisterContext data provider" class at some point that can get all the registers + // out of data in this form & attach them to a given thread. That should underlie the MacOS X User process plugin, + // and we'll also need it for the MacOS X Core File process plugin. When we have that we can also use it here. + // + // For now we hard-code the offsets and flavors we need: + // + // + + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + struct load_command load_cmd; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + lldb::addr_t start_address = LLDB_INVALID_ADDRESS; + bool done = false; + + for (i=0; iFindSectionByName(text_segment_name); + if (text_segment_sp) + { + done = true; + start_address = text_segment_sp->GetFileAddress() + entryoffset; + } + } + + default: + break; + } + if (done) + break; + + // Go to the next load command: + offset = cmd_offset + load_cmd.cmdsize; + } + + if (start_address != LLDB_INVALID_ADDRESS) + { + // We got the start address from the load commands, so now resolve that address in the sections + // of this ObjectFile: + if (!m_entry_point_address.ResolveAddressUsingFileSections (start_address, GetSectionList())) + { + m_entry_point_address.Clear(); + } + } + else + { + // We couldn't read the UnixThread load command - maybe it wasn't there. As a fallback look for the + // "start" symbol in the main executable. + + ModuleSP module_sp (GetModule()); + + if (module_sp) + { + SymbolContextList contexts; + SymbolContext context; + if (module_sp->FindSymbolsWithNameAndType(ConstString ("start"), eSymbolTypeCode, contexts)) + { + if (contexts.GetContextAtIndex(0, context)) + m_entry_point_address = context.symbol->GetAddress(); + } + } + } + } + + return m_entry_point_address; +} + +lldb_private::Address +ObjectFileMachO::GetHeaderAddress () +{ + lldb_private::Address header_addr; + SectionList *section_list = GetSectionList(); + if (section_list) + { + SectionSP text_segment_sp (section_list->FindSectionByName (GetSegmentNameTEXT())); + if (text_segment_sp) + { + header_addr.SetSection (text_segment_sp); + header_addr.SetOffset (0); + } + } + return header_addr; +} + +uint32_t +ObjectFileMachO::GetNumThreadContexts () +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (!m_thread_context_offsets_valid) + { + m_thread_context_offsets_valid = true; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + FileRangeArray::Entry file_range; + thread_command thread_cmd; + for (uint32_t i=0; iGetMutex()); + if (!m_thread_context_offsets_valid) + GetNumThreadContexts (); + + const FileRangeArray::Entry *thread_context_file_range = m_thread_context_offsets.GetEntryAtIndex (idx); + if (thread_context_file_range) + { + + DataExtractor data (m_data, + thread_context_file_range->GetRangeBase(), + thread_context_file_range->GetByteSize()); + + switch (m_header.cputype) + { + case llvm::MachO::CPU_TYPE_ARM64: + reg_ctx_sp.reset (new RegisterContextDarwin_arm64_Mach (thread, data)); + break; + + case llvm::MachO::CPU_TYPE_ARM: + reg_ctx_sp.reset (new RegisterContextDarwin_arm_Mach (thread, data)); + break; + + case llvm::MachO::CPU_TYPE_I386: + reg_ctx_sp.reset (new RegisterContextDarwin_i386_Mach (thread, data)); + break; + + case llvm::MachO::CPU_TYPE_X86_64: + reg_ctx_sp.reset (new RegisterContextDarwin_x86_64_Mach (thread, data)); + break; + } + } + } + return reg_ctx_sp; +} + +ObjectFile::Type +ObjectFileMachO::CalculateType() +{ + switch (m_header.filetype) + { + case MH_OBJECT: // 0x1u + if (GetAddressByteSize () == 4) + { + // 32 bit kexts are just object files, but they do have a valid + // UUID load command. + UUID uuid; + if (GetUUID(&uuid)) + { + // this checking for the UUID load command is not enough + // we could eventually look for the symbol named + // "OSKextGetCurrentIdentifier" as this is required of kexts + if (m_strata == eStrataInvalid) + m_strata = eStrataKernel; + return eTypeSharedLibrary; + } + } + return eTypeObjectFile; + + case MH_EXECUTE: return eTypeExecutable; // 0x2u + case MH_FVMLIB: return eTypeSharedLibrary; // 0x3u + case MH_CORE: return eTypeCoreFile; // 0x4u + case MH_PRELOAD: return eTypeSharedLibrary; // 0x5u + case MH_DYLIB: return eTypeSharedLibrary; // 0x6u + case MH_DYLINKER: return eTypeDynamicLinker; // 0x7u + case MH_BUNDLE: return eTypeSharedLibrary; // 0x8u + case MH_DYLIB_STUB: return eTypeStubLibrary; // 0x9u + case MH_DSYM: return eTypeDebugInfo; // 0xAu + case MH_KEXT_BUNDLE: return eTypeSharedLibrary; // 0xBu + default: + break; + } + return eTypeUnknown; +} + +ObjectFile::Strata +ObjectFileMachO::CalculateStrata() +{ + switch (m_header.filetype) + { + case MH_OBJECT: // 0x1u + { + // 32 bit kexts are just object files, but they do have a valid + // UUID load command. + UUID uuid; + if (GetUUID(&uuid)) + { + // this checking for the UUID load command is not enough + // we could eventually look for the symbol named + // "OSKextGetCurrentIdentifier" as this is required of kexts + if (m_type == eTypeInvalid) + m_type = eTypeSharedLibrary; + + return eStrataKernel; + } + } + return eStrataUnknown; + + case MH_EXECUTE: // 0x2u + // Check for the MH_DYLDLINK bit in the flags + if (m_header.flags & MH_DYLDLINK) + { + return eStrataUser; + } + else + { + SectionList *section_list = GetSectionList(); + if (section_list) + { + static ConstString g_kld_section_name ("__KLD"); + if (section_list->FindSectionByName(g_kld_section_name)) + return eStrataKernel; + } + } + return eStrataRawImage; + + case MH_FVMLIB: return eStrataUser; // 0x3u + case MH_CORE: return eStrataUnknown; // 0x4u + case MH_PRELOAD: return eStrataRawImage; // 0x5u + case MH_DYLIB: return eStrataUser; // 0x6u + case MH_DYLINKER: return eStrataUser; // 0x7u + case MH_BUNDLE: return eStrataUser; // 0x8u + case MH_DYLIB_STUB: return eStrataUser; // 0x9u + case MH_DSYM: return eStrataUnknown; // 0xAu + case MH_KEXT_BUNDLE: return eStrataKernel; // 0xBu + default: + break; + } + return eStrataUnknown; +} + +uint32_t +ObjectFileMachO::GetVersion (uint32_t *versions, uint32_t num_versions) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + struct dylib_command load_cmd; + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t version_cmd = 0; + uint64_t version = 0; + uint32_t i; + for (i=0; i 0) + { + if (num_versions > 0) + versions[0] = (version & 0xFFFF0000ull) >> 16; + if (num_versions > 1) + versions[1] = (version & 0x0000FF00ull) >> 8; + if (num_versions > 2) + versions[2] = (version & 0x000000FFull); + // Fill in an remaining version numbers with invalid values + for (i=3; iGetMutex()); + return GetArchitecture (m_header, m_data, MachHeaderSizeFromMagic(m_header.magic), arch); + } + return false; +} + +UUID +ObjectFileMachO::GetProcessSharedCacheUUID (Process *process) +{ + UUID uuid; + if (process) + { + addr_t all_image_infos = process->GetImageInfoAddress(); + + // The address returned by GetImageInfoAddress may be the address of dyld (don't want) + // or it may be the address of the dyld_all_image_infos structure (want). The first four + // bytes will be either the version field (all_image_infos) or a Mach-O file magic constant. + // Version 13 and higher of dyld_all_image_infos is required to get the sharedCacheUUID field. + + Error err; + uint32_t version_or_magic = process->ReadUnsignedIntegerFromMemory (all_image_infos, 4, -1, err); + if (version_or_magic != static_cast(-1) + && version_or_magic != MH_MAGIC + && version_or_magic != MH_CIGAM + && version_or_magic != MH_MAGIC_64 + && version_or_magic != MH_CIGAM_64 + && version_or_magic >= 13) + { + addr_t sharedCacheUUID_address = LLDB_INVALID_ADDRESS; + int wordsize = process->GetAddressByteSize(); + if (wordsize == 8) + { + sharedCacheUUID_address = all_image_infos + 160; // sharedCacheUUID + } + if (wordsize == 4) + { + sharedCacheUUID_address = all_image_infos + 84; // sharedCacheUUID + } + if (sharedCacheUUID_address != LLDB_INVALID_ADDRESS) + { + uuid_t shared_cache_uuid; + if (process->ReadMemory (sharedCacheUUID_address, shared_cache_uuid, sizeof (uuid_t), err) == sizeof (uuid_t)) + { + uuid.SetBytes (shared_cache_uuid); + } + } + } + } + return uuid; +} + +UUID +ObjectFileMachO::GetLLDBSharedCacheUUID () +{ + UUID uuid; +#if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) + uint8_t *(*dyld_get_all_image_infos)(void); + dyld_get_all_image_infos = (uint8_t*(*)()) dlsym (RTLD_DEFAULT, "_dyld_get_all_image_infos"); + if (dyld_get_all_image_infos) + { + uint8_t *dyld_all_image_infos_address = dyld_get_all_image_infos(); + if (dyld_all_image_infos_address) + { + uint32_t *version = (uint32_t*) dyld_all_image_infos_address; // version + if (*version >= 13) + { + uuid_t *sharedCacheUUID_address = 0; + int wordsize = sizeof (uint8_t *); + if (wordsize == 8) + { + sharedCacheUUID_address = (uuid_t*) ((uint8_t*) dyld_all_image_infos_address + 160); // sharedCacheUUID + } + else + { + sharedCacheUUID_address = (uuid_t*) ((uint8_t*) dyld_all_image_infos_address + 84); // sharedCacheUUID + } + uuid.SetBytes (sharedCacheUUID_address); + } + } + } +#endif + return uuid; +} + +uint32_t +ObjectFileMachO::GetMinimumOSVersion (uint32_t *versions, uint32_t num_versions) +{ + if (m_min_os_versions.empty()) + { + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + bool success = false; + for (uint32_t i=0; success == false && i < m_header.ncmds; ++i) + { + const lldb::offset_t load_cmd_offset = offset; + + version_min_command lc; + if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL) + break; + if (lc.cmd == llvm::MachO::LC_VERSION_MIN_MACOSX + || lc.cmd == llvm::MachO::LC_VERSION_MIN_IPHONEOS + || lc.cmd == llvm::MachO::LC_VERSION_MIN_TVOS + || lc.cmd == llvm::MachO::LC_VERSION_MIN_WATCHOS) + { + if (m_data.GetU32 (&offset, &lc.version, (sizeof(lc) / sizeof(uint32_t)) - 2)) + { + const uint32_t xxxx = lc.version >> 16; + const uint32_t yy = (lc.version >> 8) & 0xffu; + const uint32_t zz = lc.version & 0xffu; + if (xxxx) + { + m_min_os_versions.push_back(xxxx); + m_min_os_versions.push_back(yy); + m_min_os_versions.push_back(zz); + } + success = true; + } + } + offset = load_cmd_offset + lc.cmdsize; + } + + if (success == false) + { + // Push an invalid value so we don't keep trying to + m_min_os_versions.push_back(UINT32_MAX); + } + } + + if (m_min_os_versions.size() > 1 || m_min_os_versions[0] != UINT32_MAX) + { + if (versions != NULL && num_versions > 0) + { + for (size_t i=0; i> 16; + const uint32_t yy = (lc.sdk >> 8) & 0xffu; + const uint32_t zz = lc.sdk & 0xffu; + if (xxxx) + { + m_sdk_versions.push_back(xxxx); + m_sdk_versions.push_back(yy); + m_sdk_versions.push_back(zz); + } + success = true; + } + } + offset = load_cmd_offset + lc.cmdsize; + } + + if (success == false) + { + // Push an invalid value so we don't keep trying to + m_sdk_versions.push_back(UINT32_MAX); + } + } + + if (m_sdk_versions.size() > 1 || m_sdk_versions[0] != UINT32_MAX) + { + if (versions != NULL && num_versions > 0) + { + for (size_t i=0; iGetSize(); + + for (size_t sect_idx = 0; + sect_idx < num_sections && mach_base_file_addr == LLDB_INVALID_ADDRESS; + ++sect_idx) + { + Section *section = section_list->GetSectionAtIndex (sect_idx).get(); + if (section && + section->GetFileSize() > 0 && + section->GetFileOffset() == 0 && + section->IsThreadSpecific() == false && + module_sp.get() == section->GetModule().get()) + { + return section; + } + } + } + } + return nullptr; +} + +lldb::addr_t +ObjectFileMachO::CalculateSectionLoadAddressForMemoryImage(lldb::addr_t mach_header_load_address, const Section *mach_header_section, const Section *section) +{ + ModuleSP module_sp = GetModule(); + if (module_sp && mach_header_section && section && mach_header_load_address != LLDB_INVALID_ADDRESS) + { + lldb::addr_t mach_header_file_addr = mach_header_section->GetFileAddress(); + if (mach_header_file_addr != LLDB_INVALID_ADDRESS) + { + if (section && + section->GetFileSize() > 0 && + section->IsThreadSpecific() == false && + module_sp.get() == section->GetModule().get()) + { + // Ignore __LINKEDIT and __DWARF segments + if (section->GetName() == GetSegmentNameLINKEDIT()) + { + // Only map __LINKEDIT if we have an in memory image and this isn't + // a kernel binary like a kext or mach_kernel. + const bool is_memory_image = (bool)m_process_wp.lock(); + const Strata strata = GetStrata(); + if (is_memory_image == false || strata == eStrataKernel) + return LLDB_INVALID_ADDRESS; + } + return section->GetFileAddress() - mach_header_file_addr + mach_header_load_address; + } + } + } + return LLDB_INVALID_ADDRESS; +} + +bool +ObjectFileMachO::SetLoadAddress (Target &target, + lldb::addr_t value, + bool value_is_offset) +{ + ModuleSP module_sp = GetModule(); + if (module_sp) + { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList (); + if (section_list) + { + const size_t num_sections = section_list->GetSize(); + + if (value_is_offset) + { + // "value" is an offset to apply to each top level segment + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find all + // of the sections that size on disk (to avoid __PAGEZERO) + // and load them + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + if (section_sp && + section_sp->GetFileSize() > 0 && + section_sp->IsThreadSpecific() == false && + module_sp.get() == section_sp->GetModule().get()) + { + // Ignore __LINKEDIT and __DWARF segments + if (section_sp->GetName() == GetSegmentNameLINKEDIT()) + { + // Only map __LINKEDIT if we have an in memory image and this isn't + // a kernel binary like a kext or mach_kernel. + const bool is_memory_image = (bool)m_process_wp.lock(); + const Strata strata = GetStrata(); + if (is_memory_image == false || strata == eStrataKernel) + continue; + } + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + else + { + // "value" is the new base address of the mach_header, adjust each + // section accordingly + + Section *mach_header_section = GetMachHeaderSection(); + if (mach_header_section) + { + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + + lldb::addr_t section_load_addr = CalculateSectionLoadAddressForMemoryImage(value, mach_header_section, section_sp.get()); + if (section_load_addr != LLDB_INVALID_ADDRESS) + { + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_load_addr)) + ++num_loaded_sections; + } + } + } + } + } + return num_loaded_sections > 0; + } + return false; +} + +bool +ObjectFileMachO::SaveCore (const lldb::ProcessSP &process_sp, + const FileSpec &outfile, + Error &error) +{ + if (process_sp) + { + Target &target = process_sp->GetTarget(); + const ArchSpec target_arch = target.GetArchitecture(); + const llvm::Triple &target_triple = target_arch.GetTriple(); + if (target_triple.getVendor() == llvm::Triple::Apple && + (target_triple.getOS() == llvm::Triple::MacOSX + || target_triple.getOS() == llvm::Triple::IOS + || target_triple.getOS() == llvm::Triple::WatchOS + || target_triple.getOS() == llvm::Triple::TvOS)) + { + bool make_core = false; + switch (target_arch.GetMachine()) + { + case llvm::Triple::aarch64: + case llvm::Triple::arm: + case llvm::Triple::thumb: + case llvm::Triple::x86: + case llvm::Triple::x86_64: + make_core = true; + break; + default: + error.SetErrorStringWithFormat ("unsupported core architecture: %s", target_triple.str().c_str()); + break; + } + + if (make_core) + { + std::vector segment_load_commands; +// uint32_t range_info_idx = 0; + MemoryRegionInfo range_info; + Error range_error = process_sp->GetMemoryRegionInfo(0, range_info); + const uint32_t addr_byte_size = target_arch.GetAddressByteSize(); + const ByteOrder byte_order = target_arch.GetByteOrder(); + if (range_error.Success()) + { + while (range_info.GetRange().GetRangeBase() != LLDB_INVALID_ADDRESS) + { + const addr_t addr = range_info.GetRange().GetRangeBase(); + const addr_t size = range_info.GetRange().GetByteSize(); + + if (size == 0) + break; + + // Calculate correct protections + uint32_t prot = 0; + if (range_info.GetReadable() == MemoryRegionInfo::eYes) + prot |= VM_PROT_READ; + if (range_info.GetWritable() == MemoryRegionInfo::eYes) + prot |= VM_PROT_WRITE; + if (range_info.GetExecutable() == MemoryRegionInfo::eYes) + prot |= VM_PROT_EXECUTE; + +// printf ("[%3u] [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") %c%c%c\n", +// range_info_idx, +// addr, +// size, +// (prot & VM_PROT_READ ) ? 'r' : '-', +// (prot & VM_PROT_WRITE ) ? 'w' : '-', +// (prot & VM_PROT_EXECUTE) ? 'x' : '-'); + + if (prot != 0) + { + uint32_t cmd_type = LC_SEGMENT_64; + uint32_t segment_size = sizeof (segment_command_64); + if (addr_byte_size == 4) + { + cmd_type = LC_SEGMENT; + segment_size = sizeof (segment_command); + } + segment_command_64 segment = { + cmd_type, // uint32_t cmd; + segment_size, // uint32_t cmdsize; + {0}, // char segname[16]; + addr, // uint64_t vmaddr; // uint32_t for 32-bit Mach-O + size, // uint64_t vmsize; // uint32_t for 32-bit Mach-O + 0, // uint64_t fileoff; // uint32_t for 32-bit Mach-O + size, // uint64_t filesize; // uint32_t for 32-bit Mach-O + prot, // uint32_t maxprot; + prot, // uint32_t initprot; + 0, // uint32_t nsects; + 0 }; // uint32_t flags; + segment_load_commands.push_back(segment); + } + else + { + // No protections and a size of 1 used to be returned from old + // debugservers when we asked about a region that was past the + // last memory region and it indicates the end... + if (size == 1) + break; + } + + range_error = process_sp->GetMemoryRegionInfo(range_info.GetRange().GetRangeEnd(), range_info); + if (range_error.Fail()) + break; + } + + StreamString buffer (Stream::eBinary, + addr_byte_size, + byte_order); + + mach_header_64 mach_header; + if (addr_byte_size == 8) + { + mach_header.magic = MH_MAGIC_64; + } + else + { + mach_header.magic = MH_MAGIC; + } + mach_header.cputype = target_arch.GetMachOCPUType(); + mach_header.cpusubtype = target_arch.GetMachOCPUSubType(); + mach_header.filetype = MH_CORE; + mach_header.ncmds = segment_load_commands.size(); + mach_header.flags = 0; + mach_header.reserved = 0; + ThreadList &thread_list = process_sp->GetThreadList(); + const uint32_t num_threads = thread_list.GetSize(); + + // Make an array of LC_THREAD data items. Each one contains + // the contents of the LC_THREAD load command. The data doesn't + // contain the load command + load command size, we will + // add the load command and load command size as we emit the data. + std::vector LC_THREAD_datas(num_threads); + for (auto &LC_THREAD_data : LC_THREAD_datas) + { + LC_THREAD_data.GetFlags().Set(Stream::eBinary); + LC_THREAD_data.SetAddressByteSize(addr_byte_size); + LC_THREAD_data.SetByteOrder(byte_order); + } + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) + { + ThreadSP thread_sp (thread_list.GetThreadAtIndex(thread_idx)); + if (thread_sp) + { + switch (mach_header.cputype) + { + case llvm::MachO::CPU_TYPE_ARM64: + RegisterContextDarwin_arm64_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]); + break; + + case llvm::MachO::CPU_TYPE_ARM: + RegisterContextDarwin_arm_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]); + break; + + case llvm::MachO::CPU_TYPE_I386: + RegisterContextDarwin_i386_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]); + break; + + case llvm::MachO::CPU_TYPE_X86_64: + RegisterContextDarwin_x86_64_Mach::Create_LC_THREAD (thread_sp.get(), LC_THREAD_datas[thread_idx]); + break; + } + + } + } + + // The size of the load command is the size of the segments... + if (addr_byte_size == 8) + { + mach_header.sizeofcmds = segment_load_commands.size() * sizeof (struct segment_command_64); + } + else + { + mach_header.sizeofcmds = segment_load_commands.size() * sizeof (struct segment_command); + } + + // and the size of all LC_THREAD load command + for (const auto &LC_THREAD_data : LC_THREAD_datas) + { + ++mach_header.ncmds; + mach_header.sizeofcmds += 8 + LC_THREAD_data.GetSize(); + } + + printf ("mach_header: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + mach_header.magic, + mach_header.cputype, + mach_header.cpusubtype, + mach_header.filetype, + mach_header.ncmds, + mach_header.sizeofcmds, + mach_header.flags, + mach_header.reserved); + + // Write the mach header + buffer.PutHex32(mach_header.magic); + buffer.PutHex32(mach_header.cputype); + buffer.PutHex32(mach_header.cpusubtype); + buffer.PutHex32(mach_header.filetype); + buffer.PutHex32(mach_header.ncmds); + buffer.PutHex32(mach_header.sizeofcmds); + buffer.PutHex32(mach_header.flags); + if (addr_byte_size == 8) + { + buffer.PutHex32(mach_header.reserved); + } + + // Skip the mach header and all load commands and align to the next + // 0x1000 byte boundary + addr_t file_offset = buffer.GetSize() + mach_header.sizeofcmds; + if (file_offset & 0x00000fff) + { + file_offset += 0x00001000ull; + file_offset &= (~0x00001000ull + 1); + } + + for (auto &segment : segment_load_commands) + { + segment.fileoff = file_offset; + file_offset += segment.filesize; + } + + // Write out all of the LC_THREAD load commands + for (const auto &LC_THREAD_data : LC_THREAD_datas) + { + const size_t LC_THREAD_data_size = LC_THREAD_data.GetSize(); + buffer.PutHex32(LC_THREAD); + buffer.PutHex32(8 + LC_THREAD_data_size); // cmd + cmdsize + data + buffer.Write(LC_THREAD_data.GetData(), LC_THREAD_data_size); + } + + // Write out all of the segment load commands + for (const auto &segment : segment_load_commands) + { + printf ("0x%8.8x 0x%8.8x [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") [0x%16.16" PRIx64 " 0x%16.16" PRIx64 ") 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x]\n", + segment.cmd, + segment.cmdsize, + segment.vmaddr, + segment.vmaddr + segment.vmsize, + segment.fileoff, + segment.filesize, + segment.maxprot, + segment.initprot, + segment.nsects, + segment.flags); + + buffer.PutHex32(segment.cmd); + buffer.PutHex32(segment.cmdsize); + buffer.PutRawBytes(segment.segname, sizeof(segment.segname)); + if (addr_byte_size == 8) + { + buffer.PutHex64(segment.vmaddr); + buffer.PutHex64(segment.vmsize); + buffer.PutHex64(segment.fileoff); + buffer.PutHex64(segment.filesize); + } + else + { + buffer.PutHex32(static_cast(segment.vmaddr)); + buffer.PutHex32(static_cast(segment.vmsize)); + buffer.PutHex32(static_cast(segment.fileoff)); + buffer.PutHex32(static_cast(segment.filesize)); + } + buffer.PutHex32(segment.maxprot); + buffer.PutHex32(segment.initprot); + buffer.PutHex32(segment.nsects); + buffer.PutHex32(segment.flags); + } + + File core_file; + std::string core_file_path(outfile.GetPath()); + error = core_file.Open(core_file_path.c_str(), + File::eOpenOptionWrite | + File::eOpenOptionTruncate | + File::eOpenOptionCanCreate); + if (error.Success()) + { + // Read 1 page at a time + uint8_t bytes[0x1000]; + // Write the mach header and load commands out to the core file + size_t bytes_written = buffer.GetString().size(); + error = core_file.Write(buffer.GetString().data(), bytes_written); + if (error.Success()) + { + // Now write the file data for all memory segments in the process + for (const auto &segment : segment_load_commands) + { + if (core_file.SeekFromStart(segment.fileoff) == -1) + { + error.SetErrorStringWithFormat("unable to seek to offset 0x%" PRIx64 " in '%s'", segment.fileoff, core_file_path.c_str()); + break; + } + + printf ("Saving %" PRId64 " bytes of data for memory region at 0x%" PRIx64 "\n", segment.vmsize, segment.vmaddr); + addr_t bytes_left = segment.vmsize; + addr_t addr = segment.vmaddr; + Error memory_read_error; + while (bytes_left > 0 && error.Success()) + { + const size_t bytes_to_read = bytes_left > sizeof(bytes) ? sizeof(bytes) : bytes_left; + const size_t bytes_read = process_sp->ReadMemory(addr, bytes, bytes_to_read, memory_read_error); + if (bytes_read == bytes_to_read) + { + size_t bytes_written = bytes_read; + error = core_file.Write(bytes, bytes_written); + bytes_left -= bytes_read; + addr += bytes_read; + } + else + { + // Some pages within regions are not readable, those + // should be zero filled + memset (bytes, 0, bytes_to_read); + size_t bytes_written = bytes_to_read; + error = core_file.Write(bytes, bytes_written); + bytes_left -= bytes_to_read; + addr += bytes_to_read; + } + } + } + } + } + } + else + { + error.SetErrorString("process doesn't support getting memory region info"); + } + } + return true; // This is the right plug to handle saving core files for this process + } + } + return false; +} diff --git a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h new file mode 100644 index 000000000000..6b7ad531b83b --- /dev/null +++ b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -0,0 +1,252 @@ +//===-- 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_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/SafeMachO.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Host/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: + ObjectFileMachO(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t offset, + lldb::offset_t length); + + ObjectFileMachO(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + ~ObjectFileMachO() override = default; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectFile * + CreateInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length); + + static lldb_private::ObjectFile * + CreateMemoryInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool + SaveCore (const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error); + + static bool + MagicBytesMatch (lldb::DataBufferSP& data_sp, + lldb::addr_t offset, + lldb::addr_t length); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + bool + ParseHeader() override; + + bool + SetLoadAddress(lldb_private::Target &target, + lldb::addr_t value, + bool value_is_offset) override; + + lldb::ByteOrder + GetByteOrder() const override; + + bool + IsExecutable() const override; + + uint32_t + GetAddressByteSize() const override; + + lldb::AddressClass + GetAddressClass(lldb::addr_t file_addr) override; + + lldb_private::Symtab * + GetSymtab() override; + + bool + IsStripped() override; + + void + CreateSections(lldb_private::SectionList &unified_section_list) override; + + void + Dump(lldb_private::Stream *s) override; + + bool + GetArchitecture(lldb_private::ArchSpec &arch) override; + + bool + GetUUID(lldb_private::UUID* uuid) override; + + uint32_t + GetDependentModules(lldb_private::FileSpecList& files) override; + + lldb_private::FileSpecList + GetReExportedLibraries() override + { + return m_reexported_dylibs; + } + + lldb_private::Address + GetEntryPointAddress() override; + + lldb_private::Address + GetHeaderAddress() override; + + uint32_t + GetNumThreadContexts() override; + + lldb::RegisterContextSP + GetThreadContextAtIndex(uint32_t idx, lldb_private::Thread &thread) override; + + ObjectFile::Type + CalculateType() override; + + ObjectFile::Strata + CalculateStrata() override; + + uint32_t + GetVersion(uint32_t *versions, uint32_t num_versions) override; + + uint32_t + GetMinimumOSVersion(uint32_t *versions, uint32_t num_versions) override; + + uint32_t + GetSDKVersion(uint32_t *versions, uint32_t num_versions) override; + + bool + GetIsDynamicLinkEditor() override; + + static bool + ParseHeader (lldb_private::DataExtractor &data, + lldb::offset_t *data_offset_ptr, + llvm::MachO::mach_header &header); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + static bool + GetUUID (const llvm::MachO::mach_header &header, + const lldb_private::DataExtractor &data, + lldb::offset_t lc_offset, // Offset to the first load command + lldb_private::UUID& uuid); + + static bool + GetArchitecture (const llvm::MachO::mach_header &header, + const lldb_private::DataExtractor &data, + lldb::offset_t lc_offset, + lldb_private::ArchSpec &arch); + + // Intended for same-host arm device debugging where lldb needs to + // detect libraries in the shared cache and augment the nlist entries + // with an on-disk dyld_shared_cache file. The process will record + // the shared cache UUID so the on-disk cache can be matched or rejected + // correctly. + lldb_private::UUID + GetProcessSharedCacheUUID (lldb_private::Process *); + + // Intended for same-host arm device debugging where lldb will read + // shared cache libraries out of its own memory instead of the remote + // process' memory as an optimization. If lldb's shared cache UUID + // does not match the process' shared cache UUID, this optimization + // should not be used. + lldb_private::UUID + GetLLDBSharedCacheUUID (); + + lldb_private::Section * + GetMachHeaderSection(); + + lldb::addr_t + CalculateSectionLoadAddressForMemoryImage(lldb::addr_t mach_header_load_address, + const lldb_private::Section *mach_header_section, + const lldb_private::Section *section); + + lldb_private::UUID + GetSharedCacheUUID (lldb_private::FileSpec dyld_shared_cache, const lldb::ByteOrder byte_order, const uint32_t addr_byte_size); + + size_t + ParseSymtab(); + + llvm::MachO::mach_header m_header; + static const lldb_private::ConstString &GetSegmentNameTEXT(); + static const lldb_private::ConstString &GetSegmentNameDATA(); + static const lldb_private::ConstString &GetSegmentNameDATA_DIRTY(); + static const lldb_private::ConstString &GetSegmentNameDATA_CONST(); + static const lldb_private::ConstString &GetSegmentNameOBJC(); + static const lldb_private::ConstString &GetSegmentNameLINKEDIT(); + static const lldb_private::ConstString &GetSectionNameEHFrame(); + + llvm::MachO::dysymtab_command m_dysymtab; + std::vector m_mach_segments; + std::vector m_mach_sections; + std::vector m_min_os_versions; + std::vector m_sdk_versions; + typedef lldb_private::RangeVector FileRangeArray; + lldb_private::Address m_entry_point_address; + FileRangeArray m_thread_context_offsets; + bool m_thread_context_offsets_valid; + lldb_private::FileSpecList m_reexported_dylibs; +}; + +#endif // liblldb_ObjectFileMachO_h_ diff --git a/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt b/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt new file mode 100644 index 000000000000..5c7c488f362f --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_library(lldbPluginObjectFilePECOFF + ObjectFilePECOFF.cpp + WindowsMiniDump.cpp + ) diff --git a/source/Plugins/ObjectFile/PECOFF/Makefile b/source/Plugins/ObjectFile/PECOFF/Makefile new file mode 100644 index 000000000000..1b5abc461e82 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ObjectFile/PECOFF/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginObjectFilePECOFF +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp new file mode 100644 index 000000000000..b66244813240 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -0,0 +1,1056 @@ +//===-- ObjectFilePECOFF.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFilePECOFF.h" +#include "WindowsMiniDump.h" + +#include "llvm/Support/COFF.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" + +#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ +#define IMAGE_NT_SIGNATURE 0x00004550 // PE00 +#define OPT_HEADER_MAGIC_PE32 0x010b +#define OPT_HEADER_MAGIC_PE32_PLUS 0x020b + +using namespace lldb; +using namespace lldb_private; + +void +ObjectFilePECOFF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + CreateMemoryInstance, + GetModuleSpecifications, + SaveCore); +} + +void +ObjectFilePECOFF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +ObjectFilePECOFF::GetPluginNameStatic() +{ + static ConstString g_name("pe-coff"); + return g_name; +} + +const char * +ObjectFilePECOFF::GetPluginDescriptionStatic() +{ + return "Portable Executable and Common Object File Format object file reader (32 and 64 bit)"; +} + + +ObjectFile * +ObjectFilePECOFF::CreateInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) +{ + if (!data_sp) + { + data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); + data_offset = 0; + } + + if (ObjectFilePECOFF::MagicBytesMatch(data_sp)) + { + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) + data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); + std::unique_ptr objfile_ap(new ObjectFilePECOFF (module_sp, data_sp, data_offset, file, file_offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +ObjectFile * +ObjectFilePECOFF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr) +{ + return NULL; +} + +size_t +ObjectFilePECOFF::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs) +{ + const size_t initial_count = specs.GetSize(); + + if (ObjectFilePECOFF::MagicBytesMatch(data_sp)) + { + DataExtractor data; + data.SetData(data_sp, data_offset, length); + data.SetByteOrder(eByteOrderLittle); + + dos_header_t dos_header; + coff_header_t coff_header; + + if (ParseDOSHeader(data, dos_header)) + { + lldb::offset_t offset = dos_header.e_lfanew; + uint32_t pe_signature = data.GetU32(&offset); + if (pe_signature != IMAGE_NT_SIGNATURE) + return false; + if (ParseCOFFHeader(data, &offset, coff_header)) + { + ArchSpec spec; + if (coff_header.machine == MachineAmd64) + { + spec.SetTriple("x86_64-pc-windows"); + specs.Append(ModuleSpec(file, spec)); + } + else if (coff_header.machine == MachineX86) + { + spec.SetTriple("i386-pc-windows"); + specs.Append(ModuleSpec(file, spec)); + spec.SetTriple("i686-pc-windows"); + specs.Append(ModuleSpec(file, spec)); + } + } + } + } + + return specs.GetSize() - initial_count; +} + +bool +ObjectFilePECOFF::SaveCore(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error) +{ + return SaveMiniDump(process_sp, outfile, error); +} + + +bool +ObjectFilePECOFF::MagicBytesMatch (DataBufferSP& data_sp) +{ + DataExtractor data(data_sp, eByteOrderLittle, 4); + lldb::offset_t offset = 0; + uint16_t magic = data.GetU16 (&offset); + return magic == IMAGE_DOS_SIGNATURE; +} + + +ObjectFilePECOFF::ObjectFilePECOFF (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) : + ObjectFile (module_sp, file, file_offset, length, data_sp, data_offset), + m_dos_header (), + m_coff_header (), + m_coff_header_opt (), + m_sect_headers () +{ + ::memset (&m_dos_header, 0, sizeof(m_dos_header)); + ::memset (&m_coff_header, 0, sizeof(m_coff_header)); + ::memset (&m_coff_header_opt, 0, sizeof(m_coff_header_opt)); +} + + +ObjectFilePECOFF::~ObjectFilePECOFF() +{ +} + + +bool +ObjectFilePECOFF::ParseHeader () +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + m_sect_headers.clear(); + m_data.SetByteOrder (eByteOrderLittle); + lldb::offset_t offset = 0; + + if (ParseDOSHeader(m_data, m_dos_header)) + { + offset = m_dos_header.e_lfanew; + uint32_t pe_signature = m_data.GetU32 (&offset); + if (pe_signature != IMAGE_NT_SIGNATURE) + return false; + if (ParseCOFFHeader(m_data, &offset, m_coff_header)) + { + if (m_coff_header.hdrsize > 0) + ParseCOFFOptionalHeader(&offset); + ParseSectionHeaders (offset); + } + return true; + } + } + return false; +} + +bool +ObjectFilePECOFF::SetLoadAddress(Target &target, addr_t value, bool value_is_offset) +{ + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) + { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList (); + if (section_list) + { + if (!value_is_offset) + { + value -= m_image_base; + } + + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find all + // of the sections that have SHF_ALLOC in their flag bits. + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + if (section_sp && !section_sp->IsThreadSpecific()) + { + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + + +ByteOrder +ObjectFilePECOFF::GetByteOrder () const +{ + return eByteOrderLittle; +} + +bool +ObjectFilePECOFF::IsExecutable() const +{ + return (m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0; +} + +uint32_t +ObjectFilePECOFF::GetAddressByteSize () const +{ + if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32_PLUS) + return 8; + else if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) + return 4; + return 4; +} + +//---------------------------------------------------------------------- +// NeedsEndianSwap +// +// Return true if an endian swap needs to occur when extracting data +// from this file. +//---------------------------------------------------------------------- +bool +ObjectFilePECOFF::NeedsEndianSwap() const +{ +#if defined(__LITTLE_ENDIAN__) + return false; +#else + return true; +#endif +} +//---------------------------------------------------------------------- +// ParseDOSHeader +//---------------------------------------------------------------------- +bool +ObjectFilePECOFF::ParseDOSHeader (DataExtractor &data, dos_header_t &dos_header) +{ + bool success = false; + lldb::offset_t offset = 0; + success = data.ValidOffsetForDataOfSize(0, sizeof(dos_header)); + + if (success) + { + dos_header.e_magic = data.GetU16(&offset); // Magic number + success = dos_header.e_magic == IMAGE_DOS_SIGNATURE; + + if (success) + { + dos_header.e_cblp = data.GetU16(&offset); // Bytes on last page of file + dos_header.e_cp = data.GetU16(&offset); // Pages in file + dos_header.e_crlc = data.GetU16(&offset); // Relocations + dos_header.e_cparhdr = data.GetU16(&offset); // Size of header in paragraphs + dos_header.e_minalloc = data.GetU16(&offset); // Minimum extra paragraphs needed + dos_header.e_maxalloc = data.GetU16(&offset); // Maximum extra paragraphs needed + dos_header.e_ss = data.GetU16(&offset); // Initial (relative) SS value + dos_header.e_sp = data.GetU16(&offset); // Initial SP value + dos_header.e_csum = data.GetU16(&offset); // Checksum + dos_header.e_ip = data.GetU16(&offset); // Initial IP value + dos_header.e_cs = data.GetU16(&offset); // Initial (relative) CS value + dos_header.e_lfarlc = data.GetU16(&offset); // File address of relocation table + dos_header.e_ovno = data.GetU16(&offset); // Overlay number + + dos_header.e_res[0] = data.GetU16(&offset); // Reserved words + dos_header.e_res[1] = data.GetU16(&offset); // Reserved words + dos_header.e_res[2] = data.GetU16(&offset); // Reserved words + dos_header.e_res[3] = data.GetU16(&offset); // Reserved words + + dos_header.e_oemid = data.GetU16(&offset); // OEM identifier (for e_oeminfo) + dos_header.e_oeminfo = data.GetU16(&offset); // OEM information; e_oemid specific + dos_header.e_res2[0] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[1] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[2] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[3] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[4] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[5] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[6] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[7] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[8] = data.GetU16(&offset); // Reserved words + dos_header.e_res2[9] = data.GetU16(&offset); // Reserved words + + dos_header.e_lfanew = data.GetU32(&offset); // File address of new exe header + } + } + if (!success) + memset(&dos_header, 0, sizeof(dos_header)); + return success; +} + + +//---------------------------------------------------------------------- +// ParserCOFFHeader +//---------------------------------------------------------------------- +bool +ObjectFilePECOFF::ParseCOFFHeader(DataExtractor &data, lldb::offset_t *offset_ptr, coff_header_t &coff_header) +{ + bool success = data.ValidOffsetForDataOfSize (*offset_ptr, sizeof(coff_header)); + if (success) + { + coff_header.machine = data.GetU16(offset_ptr); + coff_header.nsects = data.GetU16(offset_ptr); + coff_header.modtime = data.GetU32(offset_ptr); + coff_header.symoff = data.GetU32(offset_ptr); + coff_header.nsyms = data.GetU32(offset_ptr); + coff_header.hdrsize = data.GetU16(offset_ptr); + coff_header.flags = data.GetU16(offset_ptr); + } + if (!success) + memset(&coff_header, 0, sizeof(coff_header)); + return success; +} + +bool +ObjectFilePECOFF::ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr) +{ + bool success = false; + const lldb::offset_t end_offset = *offset_ptr + m_coff_header.hdrsize; + if (*offset_ptr < end_offset) + { + success = true; + m_coff_header_opt.magic = m_data.GetU16(offset_ptr); + m_coff_header_opt.major_linker_version = m_data.GetU8 (offset_ptr); + m_coff_header_opt.minor_linker_version = m_data.GetU8 (offset_ptr); + m_coff_header_opt.code_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.data_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.bss_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.entry = m_data.GetU32(offset_ptr); + m_coff_header_opt.code_offset = m_data.GetU32(offset_ptr); + + const uint32_t addr_byte_size = GetAddressByteSize (); + + if (*offset_ptr < end_offset) + { + if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) + { + // PE32 only + m_coff_header_opt.data_offset = m_data.GetU32(offset_ptr); + } + else + m_coff_header_opt.data_offset = 0; + + if (*offset_ptr < end_offset) + { + m_coff_header_opt.image_base = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.sect_alignment = m_data.GetU32(offset_ptr); + m_coff_header_opt.file_alignment = m_data.GetU32(offset_ptr); + m_coff_header_opt.major_os_system_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.minor_os_system_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.major_image_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.minor_image_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.major_subsystem_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.minor_subsystem_version = m_data.GetU16(offset_ptr); + m_coff_header_opt.reserved1 = m_data.GetU32(offset_ptr); + m_coff_header_opt.image_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.header_size = m_data.GetU32(offset_ptr); + m_coff_header_opt.checksum = m_data.GetU32(offset_ptr); + m_coff_header_opt.subsystem = m_data.GetU16(offset_ptr); + m_coff_header_opt.dll_flags = m_data.GetU16(offset_ptr); + m_coff_header_opt.stack_reserve_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.stack_commit_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.heap_reserve_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.heap_commit_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); + m_coff_header_opt.loader_flags = m_data.GetU32(offset_ptr); + uint32_t num_data_dir_entries = m_data.GetU32(offset_ptr); + m_coff_header_opt.data_dirs.clear(); + m_coff_header_opt.data_dirs.resize(num_data_dir_entries); + uint32_t i; + for (i=0; i 0) + { + const uint32_t addr_byte_size = GetAddressByteSize (); + const size_t section_header_byte_size = nsects * sizeof(section_header_t); + DataBufferSP section_header_data_sp(m_file.ReadFileContents (section_header_data_offset, section_header_byte_size)); + DataExtractor section_header_data (section_header_data_sp, GetByteOrder(), addr_byte_size); + + lldb::offset_t offset = 0; + if (section_header_data.ValidOffsetForDataOfSize (offset, section_header_byte_size)) + { + m_sect_headers.resize(nsects); + + for (uint32_t idx = 0; idxGetMutex()); + if (m_symtab_ap.get() == NULL) + { + SectionList *sect_list = GetSectionList(); + m_symtab_ap.reset(new Symtab(this)); + Mutex::Locker symtab_locker (m_symtab_ap->GetMutex()); + + const uint32_t num_syms = m_coff_header.nsyms; + + if (num_syms > 0 && m_coff_header.symoff > 0) + { + const uint32_t symbol_size = 18; + const uint32_t addr_byte_size = GetAddressByteSize (); + const size_t symbol_data_size = num_syms * symbol_size; + // Include the 4 bytes string table size at the end of the symbols + DataBufferSP symtab_data_sp(m_file.ReadFileContents (m_coff_header.symoff, symbol_data_size + 4)); + DataExtractor symtab_data (symtab_data_sp, GetByteOrder(), addr_byte_size); + lldb::offset_t offset = symbol_data_size; + const uint32_t strtab_size = symtab_data.GetU32 (&offset); + DataBufferSP strtab_data_sp(m_file.ReadFileContents (m_coff_header.symoff + symbol_data_size, strtab_size)); + DataExtractor strtab_data (strtab_data_sp, GetByteOrder(), addr_byte_size); + + // First 4 bytes should be zeroed after strtab_size has been read, + // because it is used as offset 0 to encode a NULL string. + uint32_t* strtab_data_start = (uint32_t*)strtab_data_sp->GetBytes(); + strtab_data_start[0] = 0; + + offset = 0; + std::string symbol_name; + Symbol *symbols = m_symtab_ap->Resize (num_syms); + for (uint32_t i=0; i= 1) + { + Address symbol_addr(sect_list->GetSectionAtIndex(symbol.sect-1), symbol.value); + symbols[i].GetAddressRef() = symbol_addr; + } + + if (symbol.naux > 0) + { + i += symbol.naux; + offset += symbol_size; + } + } + + } + + // Read export header + if (coff_data_dir_export_table < m_coff_header_opt.data_dirs.size() + && m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmsize > 0 && m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr > 0) + { + export_directory_entry export_table; + uint32_t data_start = m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr; + Address address(m_coff_header_opt.image_base + data_start, sect_list); + DataBufferSP symtab_data_sp(m_file.ReadFileContents(address.GetSection()->GetFileOffset() + address.GetOffset(), m_coff_header_opt.data_dirs[0].vmsize)); + DataExtractor symtab_data (symtab_data_sp, GetByteOrder(), GetAddressByteSize()); + lldb::offset_t offset = 0; + + // Read export_table header + export_table.characteristics = symtab_data.GetU32(&offset); + export_table.time_date_stamp = symtab_data.GetU32(&offset); + export_table.major_version = symtab_data.GetU16(&offset); + export_table.minor_version = symtab_data.GetU16(&offset); + export_table.name = symtab_data.GetU32(&offset); + export_table.base = symtab_data.GetU32(&offset); + export_table.number_of_functions = symtab_data.GetU32(&offset); + export_table.number_of_names = symtab_data.GetU32(&offset); + export_table.address_of_functions = symtab_data.GetU32(&offset); + export_table.address_of_names = symtab_data.GetU32(&offset); + export_table.address_of_name_ordinals = symtab_data.GetU32(&offset); + + bool has_ordinal = export_table.address_of_name_ordinals != 0; + + lldb::offset_t name_offset = export_table.address_of_names - data_start; + lldb::offset_t name_ordinal_offset = export_table.address_of_name_ordinals - data_start; + + Symbol *symbols = m_symtab_ap->Resize(export_table.number_of_names); + + std::string symbol_name; + + // Read each export table entry + for (size_t i = 0; i < export_table.number_of_names; ++i) + { + uint32_t name_ordinal = has_ordinal ? symtab_data.GetU16(&name_ordinal_offset) : i; + uint32_t name_address = symtab_data.GetU32(&name_offset); + + const char* symbol_name_cstr = symtab_data.PeekCStr(name_address - data_start); + symbol_name.assign(symbol_name_cstr); + + lldb::offset_t function_offset = export_table.address_of_functions - data_start + sizeof(uint32_t) * name_ordinal; + uint32_t function_rva = symtab_data.GetU32(&function_offset); + + Address symbol_addr(m_coff_header_opt.image_base + function_rva, sect_list); + symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str())); + symbols[i].GetAddressRef() = symbol_addr; + symbols[i].SetType(lldb::eSymbolTypeCode); + symbols[i].SetDebug(true); + } + } + } + } + return m_symtab_ap.get(); + +} + +bool +ObjectFilePECOFF::IsStripped () +{ + // TODO: determine this for COFF + return false; +} + + + +void +ObjectFilePECOFF::CreateSections (SectionList &unified_section_list) +{ + if (!m_sections_ap.get()) + { + m_sections_ap.reset(new SectionList()); + + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + const uint32_t nsects = m_sect_headers.size(); + ModuleSP module_sp (GetModule()); + for (uint32_t idx = 0; idxSetIsEncrypted (segment_is_encrypted); + + unified_section_list.AddSection(section_sp); + m_sections_ap->AddSection (section_sp); + } + } + } +} + +bool +ObjectFilePECOFF::GetUUID (UUID* uuid) +{ + return false; +} + +uint32_t +ObjectFilePECOFF::GetDependentModules (FileSpecList& files) +{ + return 0; +} + + +//---------------------------------------------------------------------- +// Dump +// +// Dump the specifics of the runtime file container (such as any headers +// segments, sections, etc). +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::Dump(Stream *s) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + s->Printf("%p: ", static_cast(this)); + s->Indent(); + s->PutCString("ObjectFilePECOFF"); + + ArchSpec header_arch; + GetArchitecture (header_arch); + + *s << ", file = '" << m_file << "', arch = " << header_arch.GetArchitectureName() << "\n"; + + SectionList *sections = GetSectionList(); + if (sections) + sections->Dump(s, NULL, true, UINT32_MAX); + + if (m_symtab_ap.get()) + m_symtab_ap->Dump(s, NULL, eSortOrderNone); + + if (m_dos_header.e_magic) + DumpDOSHeader (s, m_dos_header); + if (m_coff_header.machine) + { + DumpCOFFHeader (s, m_coff_header); + if (m_coff_header.hdrsize) + DumpOptCOFFHeader (s, m_coff_header_opt); + } + s->EOL(); + DumpSectionHeaders(s); + s->EOL(); + } +} + +//---------------------------------------------------------------------- +// DumpDOSHeader +// +// Dump the MS-DOS header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpDOSHeader(Stream *s, const dos_header_t& header) +{ + s->PutCString ("MSDOS Header\n"); + s->Printf (" e_magic = 0x%4.4x\n", header.e_magic); + s->Printf (" e_cblp = 0x%4.4x\n", header.e_cblp); + s->Printf (" e_cp = 0x%4.4x\n", header.e_cp); + s->Printf (" e_crlc = 0x%4.4x\n", header.e_crlc); + s->Printf (" e_cparhdr = 0x%4.4x\n", header.e_cparhdr); + s->Printf (" e_minalloc = 0x%4.4x\n", header.e_minalloc); + s->Printf (" e_maxalloc = 0x%4.4x\n", header.e_maxalloc); + s->Printf (" e_ss = 0x%4.4x\n", header.e_ss); + s->Printf (" e_sp = 0x%4.4x\n", header.e_sp); + s->Printf (" e_csum = 0x%4.4x\n", header.e_csum); + s->Printf (" e_ip = 0x%4.4x\n", header.e_ip); + s->Printf (" e_cs = 0x%4.4x\n", header.e_cs); + s->Printf (" e_lfarlc = 0x%4.4x\n", header.e_lfarlc); + s->Printf (" e_ovno = 0x%4.4x\n", header.e_ovno); + s->Printf (" e_res[4] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n", + header.e_res[0], + header.e_res[1], + header.e_res[2], + header.e_res[3]); + s->Printf (" e_oemid = 0x%4.4x\n", header.e_oemid); + s->Printf (" e_oeminfo = 0x%4.4x\n", header.e_oeminfo); + s->Printf (" e_res2[10] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n", + header.e_res2[0], + header.e_res2[1], + header.e_res2[2], + header.e_res2[3], + header.e_res2[4], + header.e_res2[5], + header.e_res2[6], + header.e_res2[7], + header.e_res2[8], + header.e_res2[9]); + s->Printf (" e_lfanew = 0x%8.8x\n", header.e_lfanew); +} + +//---------------------------------------------------------------------- +// DumpCOFFHeader +// +// Dump the COFF header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpCOFFHeader(Stream *s, const coff_header_t& header) +{ + s->PutCString ("COFF Header\n"); + s->Printf (" machine = 0x%4.4x\n", header.machine); + s->Printf (" nsects = 0x%4.4x\n", header.nsects); + s->Printf (" modtime = 0x%8.8x\n", header.modtime); + s->Printf (" symoff = 0x%8.8x\n", header.symoff); + s->Printf (" nsyms = 0x%8.8x\n", header.nsyms); + s->Printf (" hdrsize = 0x%4.4x\n", header.hdrsize); +} + +//---------------------------------------------------------------------- +// DumpOptCOFFHeader +// +// Dump the optional COFF header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpOptCOFFHeader(Stream *s, const coff_opt_header_t& header) +{ + s->PutCString ("Optional COFF Header\n"); + s->Printf (" magic = 0x%4.4x\n", header.magic); + s->Printf (" major_linker_version = 0x%2.2x\n", header.major_linker_version); + s->Printf (" minor_linker_version = 0x%2.2x\n", header.minor_linker_version); + s->Printf (" code_size = 0x%8.8x\n", header.code_size); + s->Printf (" data_size = 0x%8.8x\n", header.data_size); + s->Printf (" bss_size = 0x%8.8x\n", header.bss_size); + s->Printf (" entry = 0x%8.8x\n", header.entry); + s->Printf (" code_offset = 0x%8.8x\n", header.code_offset); + s->Printf (" data_offset = 0x%8.8x\n", header.data_offset); + s->Printf (" image_base = 0x%16.16" PRIx64 "\n", header.image_base); + s->Printf (" sect_alignment = 0x%8.8x\n", header.sect_alignment); + s->Printf (" file_alignment = 0x%8.8x\n", header.file_alignment); + s->Printf (" major_os_system_version = 0x%4.4x\n", header.major_os_system_version); + s->Printf (" minor_os_system_version = 0x%4.4x\n", header.minor_os_system_version); + s->Printf (" major_image_version = 0x%4.4x\n", header.major_image_version); + s->Printf (" minor_image_version = 0x%4.4x\n", header.minor_image_version); + s->Printf (" major_subsystem_version = 0x%4.4x\n", header.major_subsystem_version); + s->Printf (" minor_subsystem_version = 0x%4.4x\n", header.minor_subsystem_version); + s->Printf (" reserved1 = 0x%8.8x\n", header.reserved1); + s->Printf (" image_size = 0x%8.8x\n", header.image_size); + s->Printf (" header_size = 0x%8.8x\n", header.header_size); + s->Printf (" checksum = 0x%8.8x\n", header.checksum); + s->Printf (" subsystem = 0x%4.4x\n", header.subsystem); + s->Printf (" dll_flags = 0x%4.4x\n", header.dll_flags); + s->Printf (" stack_reserve_size = 0x%16.16" PRIx64 "\n", header.stack_reserve_size); + s->Printf (" stack_commit_size = 0x%16.16" PRIx64 "\n", header.stack_commit_size); + s->Printf (" heap_reserve_size = 0x%16.16" PRIx64 "\n", header.heap_reserve_size); + s->Printf (" heap_commit_size = 0x%16.16" PRIx64 "\n", header.heap_commit_size); + s->Printf (" loader_flags = 0x%8.8x\n", header.loader_flags); + s->Printf (" num_data_dir_entries = 0x%8.8x\n", (uint32_t)header.data_dirs.size()); + uint32_t i; + for (i=0; iPrintf (" data_dirs[%2u] vmaddr = 0x%8.8x, vmsize = 0x%8.8x\n", + i, + header.data_dirs[i].vmaddr, + header.data_dirs[i].vmsize); + } +} +//---------------------------------------------------------------------- +// DumpSectionHeader +// +// Dump a single ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpSectionHeader(Stream *s, const section_header_t& sh) +{ + std::string name; + GetSectionName(name, sh); + s->Printf ("%-16s 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%4.4x 0x%4.4x 0x%8.8x\n", + name.c_str(), + sh.vmaddr, + sh.vmsize, + sh.offset, + sh.size, + sh.reloff, + sh.lineoff, + sh.nreloc, + sh.nline, + sh.flags); +} + + +//---------------------------------------------------------------------- +// DumpSectionHeaders +// +// Dump all of the ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFilePECOFF::DumpSectionHeaders(Stream *s) +{ + + s->PutCString ("Section Headers\n"); + s->PutCString ("IDX name vm addr vm size file off file size reloc off line off nreloc nline flags\n"); + s->PutCString ("==== ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ------ ------ ----------\n"); + + uint32_t idx = 0; + SectionHeaderCollIter pos, end = m_sect_headers.end(); + + for (pos = m_sect_headers.begin(); pos != end; ++pos, ++idx) + { + s->Printf ("[%2u] ", idx); + ObjectFilePECOFF::DumpSectionHeader(s, *pos); + } +} + +bool +ObjectFilePECOFF::GetArchitecture (ArchSpec &arch) +{ + uint16_t machine = m_coff_header.machine; + switch (machine) + { + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + case llvm::COFF::IMAGE_FILE_MACHINE_POWERPC: + case llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP: + case llvm::COFF::IMAGE_FILE_MACHINE_ARM: + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + case llvm::COFF::IMAGE_FILE_MACHINE_THUMB: + arch.SetArchitecture (eArchTypeCOFF, machine, LLDB_INVALID_CPUTYPE); + return true; + default: + break; + } + return false; +} + +ObjectFile::Type +ObjectFilePECOFF::CalculateType() +{ + if (m_coff_header.machine != 0) + { + if ((m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0) + return eTypeExecutable; + else + return eTypeSharedLibrary; + } + return eTypeExecutable; +} + +ObjectFile::Strata +ObjectFilePECOFF::CalculateStrata() +{ + return eStrataUser; +} +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +ObjectFilePECOFF::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFilePECOFF::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h b/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h new file mode 100644 index 000000000000..fd33cd3d32f8 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h @@ -0,0 +1,303 @@ +//===-- ObjectFilePECOFF.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFilePECOFF_h_ +#define liblldb_ObjectFilePECOFF_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Symbol/ObjectFile.h" + +class ObjectFilePECOFF : + public lldb_private::ObjectFile +{ +public: + typedef enum MachineType + { + MachineUnknown = 0x0, + MachineAm33 = 0x1d3, + MachineAmd64 = 0x8664, + MachineArm = 0x1c0, + MachineArmNt = 0x1c4, + MachineArm64 = 0xaa64, + MachineEbc = 0xebc, + MachineX86 = 0x14c, + MachineIA64 = 0x200, + MachineM32R = 0x9041, + MachineMips16 = 0x266, + MachineMipsFpu = 0x366, + MachineMipsFpu16 = 0x466, + MachinePowerPc = 0x1f0, + MachinePowerPcfp = 0x1f1, + MachineR4000 = 0x166, + MachineSh3 = 0x1a2, + MachineSh3dsp = 0x1a3, + MachineSh4 = 0x1a6, + MachineSh5 = 0x1a8, + MachineThumb = 0x1c2, + MachineWcemIpsv2 = 0x169 + } MachineType; + + ObjectFilePECOFF(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length); + + ~ObjectFilePECOFF() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static ObjectFile * + CreateInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t offset, + lldb::offset_t length); + + static lldb_private::ObjectFile * + CreateMemoryInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool + SaveCore (const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error); + + static bool + MagicBytesMatch (lldb::DataBufferSP& data_sp); + + bool + ParseHeader() override; + + bool + SetLoadAddress(lldb_private::Target &target, lldb::addr_t value, bool value_is_offset) override; + + lldb::ByteOrder + GetByteOrder() const override; + + bool + IsExecutable() const override; + + uint32_t + GetAddressByteSize() const override; + +// virtual lldb_private::AddressClass +// GetAddressClass (lldb::addr_t file_addr); + + lldb_private::Symtab * + GetSymtab() override; + + bool + IsStripped() override; + + void + CreateSections(lldb_private::SectionList &unified_section_list) override; + + void + Dump(lldb_private::Stream *s) override; + + bool + GetArchitecture(lldb_private::ArchSpec &arch) override; + + bool + GetUUID(lldb_private::UUID* uuid) override; + + uint32_t + GetDependentModules(lldb_private::FileSpecList& files) override; + +// virtual lldb_private::Address +// GetEntryPointAddress (); + + ObjectFile::Type + CalculateType() override; + + ObjectFile::Strata + CalculateStrata() override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + bool NeedsEndianSwap() const; + + typedef struct dos_header { // DOS .EXE header + uint16_t e_magic; // Magic number + uint16_t e_cblp; // Bytes on last page of file + uint16_t e_cp; // Pages in file + uint16_t e_crlc; // Relocations + uint16_t e_cparhdr; // Size of header in paragraphs + uint16_t e_minalloc; // Minimum extra paragraphs needed + uint16_t e_maxalloc; // Maximum extra paragraphs needed + uint16_t e_ss; // Initial (relative) SS value + uint16_t e_sp; // Initial SP value + uint16_t e_csum; // Checksum + uint16_t e_ip; // Initial IP value + uint16_t e_cs; // Initial (relative) CS value + uint16_t e_lfarlc; // File address of relocation table + uint16_t e_ovno; // Overlay number + uint16_t e_res[4]; // Reserved words + uint16_t e_oemid; // OEM identifier (for e_oeminfo) + uint16_t e_oeminfo; // OEM information; e_oemid specific + uint16_t e_res2[10]; // Reserved words + uint32_t e_lfanew; // File address of new exe header + } dos_header_t; + + typedef struct coff_header { + uint16_t machine; + uint16_t nsects; + uint32_t modtime; + uint32_t symoff; + uint32_t nsyms; + uint16_t hdrsize; + uint16_t flags; + } coff_header_t; + + typedef struct data_directory { + uint32_t vmaddr; + uint32_t vmsize; + } data_directory_t; + + typedef struct coff_opt_header + { + uint16_t magic; + uint8_t major_linker_version; + uint8_t minor_linker_version; + uint32_t code_size; + uint32_t data_size; + uint32_t bss_size; + uint32_t entry; + uint32_t code_offset; + uint32_t data_offset; + + uint64_t image_base; + uint32_t sect_alignment; + uint32_t file_alignment; + uint16_t major_os_system_version; + uint16_t minor_os_system_version; + uint16_t major_image_version; + uint16_t minor_image_version; + uint16_t major_subsystem_version; + uint16_t minor_subsystem_version; + uint32_t reserved1; + uint32_t image_size; + uint32_t header_size; + uint32_t checksum; + uint16_t subsystem; + uint16_t dll_flags; + uint64_t stack_reserve_size; + uint64_t stack_commit_size; + uint64_t heap_reserve_size; + uint64_t heap_commit_size; + uint32_t loader_flags; + // uint32_t num_data_dir_entries; + std::vector data_dirs; // will contain num_data_dir_entries entries + } coff_opt_header_t; + + typedef enum coff_data_dir_type + { + coff_data_dir_export_table = 0, + coff_data_dir_import_table = 1, + } coff_data_dir_type; + + typedef struct section_header { + char name[8]; + uint32_t vmsize; // Virtual Size + uint32_t vmaddr; // Virtual Addr + uint32_t size; // File size + uint32_t offset; // File offset + uint32_t reloff; // Offset to relocations + uint32_t lineoff;// Offset to line table entries + uint16_t nreloc; // Number of relocation entries + uint16_t nline; // Number of line table entries + uint32_t flags; + } section_header_t; + + typedef struct coff_symbol { + char name[8]; + uint32_t value; + uint16_t sect; + uint16_t type; + uint8_t storage; + uint8_t naux; + } coff_symbol_t; + + typedef struct export_directory_entry { + uint32_t characteristics; + uint32_t time_date_stamp; + uint16_t major_version; + uint16_t minor_version; + uint32_t name; + uint32_t base; + uint32_t number_of_functions; + uint32_t number_of_names; + uint32_t address_of_functions; + uint32_t address_of_names; + uint32_t address_of_name_ordinals; + } export_directory_entry; + + static bool ParseDOSHeader (lldb_private::DataExtractor &data, dos_header_t &dos_header); + static bool ParseCOFFHeader (lldb_private::DataExtractor &data, lldb::offset_t *offset_ptr, coff_header_t &coff_header); + bool ParseCOFFOptionalHeader (lldb::offset_t *offset_ptr); + bool ParseSectionHeaders (uint32_t offset); + + static void DumpDOSHeader(lldb_private::Stream *s, const dos_header_t& header); + static void DumpCOFFHeader(lldb_private::Stream *s, const coff_header_t& header); + static void DumpOptCOFFHeader(lldb_private::Stream *s, const coff_opt_header_t& header); + void DumpSectionHeaders(lldb_private::Stream *s); + void DumpSectionHeader(lldb_private::Stream *s, const section_header_t& sh); + bool GetSectionName(std::string& sect_name, const section_header_t& sect); + + typedef std::vector SectionHeaderColl; + typedef SectionHeaderColl::iterator SectionHeaderCollIter; + typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter; +private: + dos_header_t m_dos_header; + coff_header_t m_coff_header; + coff_opt_header_t m_coff_header_opt; + SectionHeaderColl m_sect_headers; + lldb::addr_t m_image_base; +}; + +#endif // liblldb_ObjectFilePECOFF_h_ diff --git a/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp b/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp new file mode 100644 index 000000000000..14c73eff2388 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp @@ -0,0 +1,56 @@ +//===-- WindowsMiniDump.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This function is separated out from ObjectFilePECOFF.cpp to name avoid name +// collisions with WinAPI preprocessor macros. + +#include "WindowsMiniDump.h" +#include "lldb/Host/FileSpec.h" +#include "llvm/Support/ConvertUTF.h" + +#ifdef _WIN32 +#include "lldb/Host/windows/windows.h" +#include // for MiniDumpWriteDump +#endif + +namespace lldb_private { + +bool +SaveMiniDump(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error) +{ + if (!process_sp) return false; +#ifdef _WIN32 + HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_sp->GetID()); + const std::string file_name = outfile.GetCString(); + std::wstring wide_name; + wide_name.resize(file_name.size() + 1); + char * result_ptr = reinterpret_cast(&wide_name[0]); + const UTF8 *error_ptr = nullptr; + if (!llvm::ConvertUTF8toWide(sizeof(wchar_t), file_name, result_ptr, error_ptr)) { + error.SetErrorString("cannot convert file name"); + return false; + } + HANDLE file_handle = ::CreateFileW(wide_name.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + const auto result = ::MiniDumpWriteDump(process_handle, process_sp->GetID(), file_handle, + MiniDumpWithFullMemoryInfo, NULL, NULL, NULL); + ::CloseHandle(file_handle); + ::CloseHandle(process_handle); + if (!result) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return false; + } + return true; +#endif + return false; +} + +} // namesapce lldb_private diff --git a/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h b/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h new file mode 100644 index 000000000000..cbea88af1fb0 --- /dev/null +++ b/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h @@ -0,0 +1,24 @@ +//===-- WindowsMiniDump.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_WindowsMiniDump_h_ +#define liblldb_WindowsMiniDump_h_ + +#include "lldb/Target/Process.h" + +namespace lldb_private { + +bool +SaveMiniDump(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error); + +} // namespace lldb_private + +#endif diff --git a/source/Plugins/OperatingSystem/CMakeLists.txt b/source/Plugins/OperatingSystem/CMakeLists.txt new file mode 100644 index 000000000000..1f017adcd02b --- /dev/null +++ b/source/Plugins/OperatingSystem/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(Go) +add_subdirectory(Python) diff --git a/source/Plugins/OperatingSystem/Go/CMakeLists.txt b/source/Plugins/OperatingSystem/Go/CMakeLists.txt new file mode 100644 index 000000000000..1ca82c462069 --- /dev/null +++ b/source/Plugins/OperatingSystem/Go/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginOSGo + OperatingSystemGo.cpp + ) diff --git a/source/Plugins/OperatingSystem/Go/Makefile b/source/Plugins/OperatingSystem/Go/Makefile new file mode 100644 index 000000000000..7d06d483d3ae --- /dev/null +++ b/source/Plugins/OperatingSystem/Go/Makefile @@ -0,0 +1,14 @@ +##==- source/Plugins/OperatingSystem/Go/Makefile --------*- Makefile -*-==## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginOSGo +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/OperatingSystem/Python/CMakeLists.txt b/source/Plugins/OperatingSystem/Python/CMakeLists.txt new file mode 100644 index 000000000000..7188e6f67ba0 --- /dev/null +++ b/source/Plugins/OperatingSystem/Python/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginOSPython + OperatingSystemPython.cpp + ) diff --git a/source/Plugins/OperatingSystem/Python/Makefile b/source/Plugins/OperatingSystem/Python/Makefile new file mode 100644 index 000000000000..67cd0acd7038 --- /dev/null +++ b/source/Plugins/OperatingSystem/Python/Makefile @@ -0,0 +1,14 @@ +##==- source/Plugins/OperatingSystem/Python/Makefile --------*- Makefile -*-==## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginOSPython +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Android/AdbClient.cpp b/source/Plugins/Platform/Android/AdbClient.cpp new file mode 100644 index 000000000000..736447fd22d2 --- /dev/null +++ b/source/Plugins/Platform/Android/AdbClient.cpp @@ -0,0 +1,569 @@ +//===-- AdbClient.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 "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataEncoder.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileUtilities.h" + +// Project includes +#include "AdbClient.h" + +#include + +#include +#include +#include + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_android; + +namespace { + +const uint32_t kReadTimeout = 1000000; // 1 second +const char * kOKAY = "OKAY"; +const char * kFAIL = "FAIL"; +const char * kDATA = "DATA"; +const char * kDONE = "DONE"; + +const char * kSEND = "SEND"; +const char * kRECV = "RECV"; +const char * kSTAT = "STAT"; + +const size_t kSyncPacketLen = 8; +// Maximum size of a filesync DATA packet. +const size_t kMaxPushData = 2*1024; +// Default mode for pushed files. +const uint32_t kDefaultMode = 0100770; // S_IFREG | S_IRWXU | S_IRWXG + +const char * kSocketNamespaceAbstract = "localabstract"; +const char * kSocketNamespaceFileSystem = "localfilesystem"; + +} // namespace + +Error +AdbClient::CreateByDeviceID(const std::string &device_id, AdbClient &adb) +{ + DeviceIDList connect_devices; + auto error = adb.GetDevices(connect_devices); + if (error.Fail()) + return error; + + if (device_id.empty()) + { + if (connect_devices.size() != 1) + return Error("Expected a single connected device, got instead %" PRIu64, + static_cast(connect_devices.size())); + + adb.SetDeviceID(connect_devices.front()); + } + else + { + auto find_it = std::find(connect_devices.begin(), connect_devices.end(), device_id); + if (find_it == connect_devices.end()) + return Error("Device \"%s\" not found", device_id.c_str()); + + adb.SetDeviceID(*find_it); + } + return error; +} + +AdbClient::AdbClient (const std::string &device_id) + : m_device_id (device_id) +{ +} + +void +AdbClient::SetDeviceID (const std::string &device_id) +{ + m_device_id = device_id; +} + +const std::string& +AdbClient::GetDeviceID() const +{ + return m_device_id; +} + +Error +AdbClient::Connect () +{ + Error error; + m_conn.Connect ("connect://localhost:5037", &error); + + return error; +} + +Error +AdbClient::GetDevices (DeviceIDList &device_list) +{ + device_list.clear (); + + auto error = SendMessage ("host:devices"); + if (error.Fail ()) + return error; + + error = ReadResponseStatus (); + if (error.Fail ()) + return error; + + std::vector in_buffer; + error = ReadMessage (in_buffer); + + llvm::StringRef response (&in_buffer[0], in_buffer.size ()); + llvm::SmallVector devices; + response.split (devices, "\n", -1, false); + + for (const auto device: devices) + device_list.push_back (device.split ('\t').first); + + return error; +} + +Error +AdbClient::SetPortForwarding (const uint16_t local_port, const uint16_t remote_port) +{ + char message[48]; + snprintf (message, sizeof (message), "forward:tcp:%d;tcp:%d", local_port, remote_port); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::SetPortForwarding (const uint16_t local_port, + const char* remote_socket_name, + const UnixSocketNamespace socket_namespace) +{ + char message[PATH_MAX]; + const char * sock_namespace_str = (socket_namespace == UnixSocketNamespaceAbstract) ? + kSocketNamespaceAbstract : kSocketNamespaceFileSystem; + snprintf (message, sizeof (message), "forward:tcp:%d;%s:%s", + local_port, + sock_namespace_str, + remote_socket_name); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::DeletePortForwarding (const uint16_t local_port) +{ + char message[32]; + snprintf (message, sizeof (message), "killforward:tcp:%d", local_port); + + const auto error = SendDeviceMessage (message); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::SendMessage (const std::string &packet, const bool reconnect) +{ + Error error; + if (reconnect) + { + error = Connect (); + if (error.Fail ()) + return error; + } + + char length_buffer[5]; + snprintf (length_buffer, sizeof (length_buffer), "%04x", static_cast(packet.size ())); + + ConnectionStatus status; + + m_conn.Write (length_buffer, 4, status, &error); + if (error.Fail ()) + return error; + + m_conn.Write (packet.c_str (), packet.size (), status, &error); + return error; +} + +Error +AdbClient::SendDeviceMessage (const std::string &packet) +{ + std::ostringstream msg; + msg << "host-serial:" << m_device_id << ":" << packet; + return SendMessage (msg.str ()); +} + +Error +AdbClient::ReadMessage (std::vector &message) +{ + message.clear (); + + char buffer[5]; + buffer[4] = 0; + + auto error = ReadAllBytes (buffer, 4); + if (error.Fail ()) + return error; + + unsigned int packet_len = 0; + sscanf (buffer, "%x", &packet_len); + + message.resize (packet_len, 0); + error = ReadAllBytes (&message[0], packet_len); + if (error.Fail ()) + message.clear (); + + return error; +} + +Error +AdbClient::ReadMessageStream (std::vector& message, uint32_t timeout_ms) +{ + auto start = std::chrono::steady_clock::now(); + message.clear(); + + Error error; + lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; + char buffer[1024]; + while (error.Success() && status == lldb::eConnectionStatusSuccess) + { + auto end = std::chrono::steady_clock::now(); + uint32_t elapsed_time = std::chrono::duration_cast(end - start).count(); + if (elapsed_time >= timeout_ms) + return Error("Timed out"); + + size_t n = m_conn.Read(buffer, sizeof(buffer), 1000 * (timeout_ms - elapsed_time), status, &error); + if (n > 0) + message.insert(message.end(), &buffer[0], &buffer[n]); + } + return error; +} + +Error +AdbClient::ReadResponseStatus() +{ + char response_id[5]; + + static const size_t packet_len = 4; + response_id[packet_len] = 0; + + auto error = ReadAllBytes (response_id, packet_len); + if (error.Fail ()) + return error; + + if (strncmp (response_id, kOKAY, packet_len) != 0) + return GetResponseError (response_id); + + return error; +} + +Error +AdbClient::GetResponseError (const char *response_id) +{ + if (strcmp (response_id, kFAIL) != 0) + return Error ("Got unexpected response id from adb: \"%s\"", response_id); + + std::vector error_message; + auto error = ReadMessage (error_message); + if (error.Success ()) + error.SetErrorString (std::string (&error_message[0], error_message.size ()).c_str ()); + + return error; +} + +Error +AdbClient::SwitchDeviceTransport () +{ + std::ostringstream msg; + msg << "host:transport:" << m_device_id; + + auto error = SendMessage (msg.str ()); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::PullFile (const FileSpec &remote_file, const FileSpec &local_file) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const auto local_file_path = local_file.GetPath (); + llvm::FileRemover local_file_remover (local_file_path.c_str ()); + + std::ofstream dst (local_file_path, std::ios::out | std::ios::binary); + if (!dst.is_open ()) + return Error ("Unable to open local file %s", local_file_path.c_str()); + + const auto remote_file_path = remote_file.GetPath (false); + error = SendSyncRequest (kRECV, remote_file_path.length (), remote_file_path.c_str ()); + if (error.Fail ()) + return error; + + std::vector chunk; + bool eof = false; + while (!eof) + { + error = PullFileChunk (chunk, eof); + if (error.Fail ()) + return error; + if (!eof) + dst.write (&chunk[0], chunk.size ()); + } + + local_file_remover.releaseFile (); + return error; +} + +Error +AdbClient::PushFile (const FileSpec &local_file, const FileSpec &remote_file) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const auto local_file_path (local_file.GetPath ()); + std::ifstream src (local_file_path.c_str(), std::ios::in | std::ios::binary); + if (!src.is_open ()) + return Error ("Unable to open local file %s", local_file_path.c_str()); + + std::stringstream file_description; + file_description << remote_file.GetPath(false).c_str() << "," << kDefaultMode; + std::string file_description_str = file_description.str(); + error = SendSyncRequest (kSEND, file_description_str.length(), file_description_str.c_str()); + if (error.Fail ()) + return error; + + char chunk[kMaxPushData]; + while (!src.eof() && !src.read(chunk, kMaxPushData).bad()) + { + size_t chunk_size = src.gcount(); + error = SendSyncRequest(kDATA, chunk_size, chunk); + if (error.Fail ()) + return Error ("Failed to send file chunk: %s", error.AsCString ()); + } + error = SendSyncRequest(kDONE, local_file.GetModificationTime().seconds(), nullptr); + if (error.Fail ()) + return error; + + std::string response_id; + uint32_t data_len; + error = ReadSyncHeader (response_id, data_len); + if (error.Fail ()) + return Error ("Failed to read DONE response: %s", error.AsCString ()); + if (response_id == kFAIL) + { + std::string error_message (data_len, 0); + error = ReadAllBytes (&error_message[0], data_len); + if (error.Fail ()) + return Error ("Failed to read DONE error message: %s", error.AsCString ()); + return Error ("Failed to push file: %s", error_message.c_str ()); + } + else if (response_id != kOKAY) + return Error ("Got unexpected DONE response: %s", response_id.c_str ()); + + // If there was an error reading the source file, finish the adb file + // transfer first so that adb isn't expecting any more data. + if (src.bad()) + return Error ("Failed read on %s", local_file_path.c_str()); + return error; +} + +Error +AdbClient::StartSync () +{ + auto error = SwitchDeviceTransport (); + if (error.Fail ()) + return Error ("Failed to switch to device transport: %s", error.AsCString ()); + + error = Sync (); + if (error.Fail ()) + return Error ("Sync failed: %s", error.AsCString ()); + + return error; +} + +Error +AdbClient::Sync () +{ + auto error = SendMessage ("sync:", false); + if (error.Fail ()) + return error; + + return ReadResponseStatus (); +} + +Error +AdbClient::PullFileChunk (std::vector &buffer, bool &eof) +{ + buffer.clear (); + + std::string response_id; + uint32_t data_len; + auto error = ReadSyncHeader (response_id, data_len); + if (error.Fail ()) + return error; + + if (response_id == kDATA) + { + buffer.resize (data_len, 0); + error = ReadAllBytes (&buffer[0], data_len); + if (error.Fail ()) + buffer.clear (); + } + else if (response_id == kDONE) + { + eof = true; + } + else if (response_id == kFAIL) + { + std::string error_message (data_len, 0); + error = ReadAllBytes (&error_message[0], data_len); + if (error.Fail ()) + return Error ("Failed to read pull error message: %s", error.AsCString ()); + return Error ("Failed to pull file: %s", error_message.c_str ()); + } + else + return Error ("Pull failed with unknown response: %s", response_id.c_str ()); + + return Error (); +} + +Error +AdbClient::SendSyncRequest (const char *request_id, const uint32_t data_len, const void *data) +{ + const DataBufferSP data_sp (new DataBufferHeap (kSyncPacketLen, 0)); + DataEncoder encoder (data_sp, eByteOrderLittle, sizeof (void*)); + auto offset = encoder.PutData (0, request_id, strlen(request_id)); + encoder.PutU32 (offset, data_len); + + Error error; + ConnectionStatus status; + m_conn.Write (data_sp->GetBytes (), kSyncPacketLen, status, &error); + if (error.Fail ()) + return error; + + if (data) + m_conn.Write (data, data_len, status, &error); + return error; +} + +Error +AdbClient::ReadSyncHeader (std::string &response_id, uint32_t &data_len) +{ + char buffer[kSyncPacketLen]; + + auto error = ReadAllBytes (buffer, kSyncPacketLen); + if (error.Success ()) + { + response_id.assign (&buffer[0], 4); + DataExtractor extractor (&buffer[4], 4, eByteOrderLittle, sizeof (void*)); + offset_t offset = 0; + data_len = extractor.GetU32 (&offset); + } + + return error; +} + +Error +AdbClient::ReadAllBytes (void *buffer, size_t size) +{ + Error error; + ConnectionStatus status; + char *read_buffer = static_cast(buffer); + + size_t tota_read_bytes = 0; + while (tota_read_bytes < size) + { + auto read_bytes = m_conn.Read (read_buffer + tota_read_bytes, size - tota_read_bytes, kReadTimeout, status, &error); + if (error.Fail ()) + return error; + tota_read_bytes += read_bytes; + } + return error; +} + +Error +AdbClient::Stat (const FileSpec &remote_file, uint32_t &mode, uint32_t &size, uint32_t &mtime) +{ + auto error = StartSync (); + if (error.Fail ()) + return error; + + const std::string remote_file_path (remote_file.GetPath (false)); + error = SendSyncRequest (kSTAT, remote_file_path.length (), remote_file_path.c_str ()); + if (error.Fail ()) + return Error ("Failed to send request: %s", error.AsCString ()); + + static const size_t stat_len = strlen (kSTAT); + static const size_t response_len = stat_len + (sizeof (uint32_t) * 3); + + std::vector buffer (response_len); + error = ReadAllBytes (&buffer[0], buffer.size ()); + if (error.Fail ()) + return Error ("Failed to read response: %s", error.AsCString ()); + + DataExtractor extractor (&buffer[0], buffer.size (), eByteOrderLittle, sizeof (void*)); + offset_t offset = 0; + + const void* command = extractor.GetData (&offset, stat_len); + if (!command) + return Error ("Failed to get response command"); + const char* command_str = static_cast (command); + if (strncmp (command_str, kSTAT, stat_len)) + return Error ("Got invalid stat command: %s", command_str); + + mode = extractor.GetU32 (&offset); + size = extractor.GetU32 (&offset); + mtime = extractor.GetU32 (&offset); + return Error (); +} + +Error +AdbClient::Shell (const char* command, uint32_t timeout_ms, std::string* output) +{ + auto error = SwitchDeviceTransport (); + if (error.Fail ()) + return Error ("Failed to switch to device transport: %s", error.AsCString ()); + + StreamString adb_command; + adb_command.Printf("shell:%s", command); + error = SendMessage (adb_command.GetData(), false); + if (error.Fail ()) + return error; + + error = ReadResponseStatus (); + if (error.Fail ()) + return error; + + std::vector in_buffer; + error = ReadMessageStream (in_buffer, timeout_ms); + if (error.Fail()) + return error; + + if (output) + output->assign(in_buffer.begin(), in_buffer.end()); + return error; +} diff --git a/source/Plugins/Platform/Android/AdbClient.h b/source/Plugins/Platform/Android/AdbClient.h new file mode 100644 index 000000000000..4ec411d1411d --- /dev/null +++ b/source/Plugins/Platform/Android/AdbClient.h @@ -0,0 +1,132 @@ +//===-- AdbClient.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AdbClient_h_ +#define liblldb_AdbClient_h_ + +// C Includes + +// C++ Includes + +#include +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Error.h" +#include "lldb/Host/ConnectionFileDescriptor.h" + +namespace lldb_private { + +class FileSpec; + +namespace platform_android { + +class AdbClient +{ +public: + enum UnixSocketNamespace + { + UnixSocketNamespaceAbstract, + UnixSocketNamespaceFileSystem, + }; + + using DeviceIDList = std::list; + + static Error + CreateByDeviceID(const std::string &device_id, AdbClient &adb); + + AdbClient () = default; + explicit AdbClient (const std::string &device_id); + + const std::string& + GetDeviceID() const; + + Error + GetDevices (DeviceIDList &device_list); + + Error + SetPortForwarding (const uint16_t local_port, const uint16_t remote_port); + + Error + SetPortForwarding (const uint16_t local_port, + const char* remote_socket_name, + const UnixSocketNamespace socket_namespace); + + Error + DeletePortForwarding (const uint16_t local_port); + + Error + PullFile (const FileSpec &remote_file, const FileSpec &local_file); + + Error + PushFile (const FileSpec &local_file, const FileSpec &remote_file); + + Error + Stat (const FileSpec &remote_file, uint32_t &mode, uint32_t &size, uint32_t &mtime); + + Error + Shell (const char* command, uint32_t timeout_ms, std::string* output); + +private: + Error + Connect (); + + void + SetDeviceID (const std::string &device_id); + + Error + SendMessage (const std::string &packet, const bool reconnect = true); + + Error + SendDeviceMessage (const std::string &packet); + + Error + SendSyncRequest (const char *request_id, const uint32_t data_len, const void *data); + + Error + ReadSyncHeader (std::string &response_id, uint32_t &data_len); + + Error + ReadMessage (std::vector &message); + + Error + ReadMessageStream (std::vector &message, uint32_t timeout_ms); + + Error + GetResponseError (const char *response_id); + + Error + ReadResponseStatus (); + + Error + SwitchDeviceTransport (); + + Error + Sync (); + + Error + StartSync (); + + Error + PullFileChunk (std::vector &buffer, bool &eof); + + Error + ReadAllBytes (void *buffer, size_t size); + + std::string m_device_id; + ConnectionFileDescriptor m_conn; +}; + +} // namespace platform_android +} // namespace lldb_private + +#endif // liblldb_AdbClient_h_ diff --git a/source/Plugins/Platform/Android/CMakeLists.txt b/source/Plugins/Platform/Android/CMakeLists.txt new file mode 100644 index 000000000000..e831a33a4b6d --- /dev/null +++ b/source/Plugins/Platform/Android/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginPlatformAndroid + AdbClient.cpp + PlatformAndroid.cpp + PlatformAndroidRemoteGDBServer.cpp + ) diff --git a/source/Plugins/Platform/Android/Makefile b/source/Plugins/Platform/Android/Makefile new file mode 100644 index 000000000000..aa186f924e66 --- /dev/null +++ b/source/Plugins/Platform/Android/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Android/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformAndroid +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Android/PlatformAndroid.cpp b/source/Plugins/Platform/Android/PlatformAndroid.cpp new file mode 100644 index 000000000000..e842884c046a --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -0,0 +1,389 @@ +//===-- PlatformAndroid.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/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/StringConvert.h" +#include "Utility/UriParser.h" + +// Project includes +#include "AdbClient.h" +#include "PlatformAndroid.h" +#include "PlatformAndroidRemoteGDBServer.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_android; + +static uint32_t g_initialize_count = 0; +static const unsigned int g_android_default_cache_size = 2048; // Fits inside 4k adb packet. + +void +PlatformAndroid::Initialize () +{ + PlatformLinux::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined(__ANDROID__) + PlatformSP default_platform_sp (new PlatformAndroid(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin (PlatformAndroid::GetPluginNameStatic(false), + PlatformAndroid::GetPluginDescriptionStatic(false), + PlatformAndroid::CreateInstance); + } +} + +void +PlatformAndroid::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformAndroid::CreateInstance); + } + } + + PlatformLinux::Terminate (); +} + +PlatformSP +PlatformAndroid::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::PC: + create = true; + break; + +#if defined(__ANDROID__) + // Only accept "unknown" for the vendor if the host is android and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified_ + case llvm::Triple::VendorType::UnknownVendor: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Android: + break; + +#if defined(__ANDROID__) + // Only accept "unknown" for the OS if the host is android and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::OSType::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + + if (create) + { + if (log) + log->Printf ("PlatformAndroid::%s() creating remote-android platform", __FUNCTION__); + return PlatformSP(new PlatformAndroid(false)); + } + + if (log) + log->Printf ("PlatformAndroid::%s() aborting creation of remote-android platform", __FUNCTION__); + + return PlatformSP(); +} + +PlatformAndroid::PlatformAndroid (bool is_host) : + PlatformLinux(is_host), + m_sdk_version(0) +{ +} + +PlatformAndroid::~PlatformAndroid() +{ +} + +ConstString +PlatformAndroid::GetPluginNameStatic (bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-android"); + return g_remote_name; + } +} + +const char * +PlatformAndroid::GetPluginDescriptionStatic (bool is_host) +{ + if (is_host) + return "Local Android user platform plug-in."; + else + return "Remote Android user platform plug-in."; +} + +ConstString +PlatformAndroid::GetPluginName() +{ + return GetPluginNameStatic(IsHost()); +} + +Error +PlatformAndroid::ConnectRemote(Args& args) +{ + m_device_id.clear(); + + if (IsHost()) + { + return Error ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString()); + } + + if (!m_remote_platform_sp) + m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); + + int port; + std::string scheme, host, path; + const char *url = args.GetArgumentAtIndex(0); + if (!url) + return Error("URL is null."); + if (!UriParser::Parse(url, scheme, host, port, path)) + return Error("Invalid URL: %s", url); + if (host != "localhost") + m_device_id = host; + + auto error = PlatformLinux::ConnectRemote(args); + if (error.Success()) + { + AdbClient adb; + error = AdbClient::CreateByDeviceID(m_device_id, adb); + if (error.Fail()) + return error; + + m_device_id = adb.GetDeviceID(); + } + return error; +} + +Error +PlatformAndroid::GetFile (const FileSpec& source, + const FileSpec& destination) +{ + if (IsHost() || !m_remote_platform_sp) + return PlatformLinux::GetFile(source, destination); + + FileSpec source_spec (source.GetPath (false), false, FileSpec::ePathSyntaxPosix); + if (source_spec.IsRelative()) + source_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (source_spec.GetCString (false)); + + AdbClient adb (m_device_id); + return adb.PullFile (source_spec, destination); +} + +Error +PlatformAndroid::PutFile (const FileSpec& source, + const FileSpec& destination, + uint32_t uid, + uint32_t gid) +{ + if (IsHost() || !m_remote_platform_sp) + return PlatformLinux::PutFile (source, destination, uid, gid); + + FileSpec destination_spec (destination.GetPath (false), false, FileSpec::ePathSyntaxPosix); + if (destination_spec.IsRelative()) + destination_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (destination_spec.GetCString (false)); + + AdbClient adb (m_device_id); + // TODO: Set correct uid and gid on remote file. + return adb.PushFile(source, destination_spec); +} + +const char * +PlatformAndroid::GetCacheHostname () +{ + return m_device_id.c_str (); +} + +Error +PlatformAndroid::DownloadModuleSlice (const FileSpec &src_file_spec, + const uint64_t src_offset, + const uint64_t src_size, + const FileSpec &dst_file_spec) +{ + if (src_offset != 0) + return Error ("Invalid offset - %" PRIu64, src_offset); + + return GetFile (src_file_spec, dst_file_spec); +} + +Error +PlatformAndroid::DisconnectRemote() +{ + Error error = PlatformLinux::DisconnectRemote(); + if (error.Success()) + { + m_device_id.clear(); + m_sdk_version = 0; + } + return error; +} + +uint32_t +PlatformAndroid::GetDefaultMemoryCacheLineSize() +{ + return g_android_default_cache_size; +} + +uint32_t +PlatformAndroid::GetSdkVersion() +{ + if (!IsConnected()) + return 0; + + if (m_sdk_version != 0) + return m_sdk_version; + + std::string version_string; + AdbClient adb(m_device_id); + Error error = adb.Shell("getprop ro.build.version.sdk", 5000 /* ms */, &version_string); + version_string = llvm::StringRef(version_string).trim().str(); + + if (error.Fail() || version_string.empty()) + { + Log* log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM); + if (log) + log->Printf("Get SDK version failed. (error: %s, output: %s)", + error.AsCString(), version_string.c_str()); + return 0; + } + + m_sdk_version = StringConvert::ToUInt32(version_string.c_str()); + return m_sdk_version; +} + +Error +PlatformAndroid::DownloadSymbolFile (const lldb::ModuleSP& module_sp, + const FileSpec& dst_file_spec) +{ + // For oat file we can try to fetch additional debug info from the device + if (module_sp->GetFileSpec().GetFileNameExtension() != ConstString("oat")) + return Error("Symbol file downloading only supported for oat files"); + + // If we have no information about the platform file we can't execute oatdump + if (!module_sp->GetPlatformFileSpec()) + return Error("No platform file specified"); + + // Symbolizer isn't available before SDK version 23 + if (GetSdkVersion() < 23) + return Error("Symbol file generation only supported on SDK 23+"); + + // If we already have symtab then we don't have to try and generate one + if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != nullptr) + return Error("Symtab already available in the module"); + + AdbClient adb(m_device_id); + + std::string tmpdir; + Error error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp", 5000 /* ms */, &tmpdir); + if (error.Fail() || tmpdir.empty()) + return Error("Failed to generate temporary directory on the device (%s)", error.AsCString()); + tmpdir = llvm::StringRef(tmpdir).trim().str(); + + // Create file remover for the temporary directory created on the device + std::unique_ptr> tmpdir_remover( + &tmpdir, + [this, &adb](std::string* s) { + StreamString command; + command.Printf("rm -rf %s", s->c_str()); + Error error = adb.Shell(command.GetData(), 5000 /* ms */, nullptr); + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (error.Fail()) + log->Printf("Failed to remove temp directory: %s", error.AsCString()); + } + ); + + FileSpec symfile_platform_filespec(tmpdir.c_str(), false); + symfile_platform_filespec.AppendPathComponent("symbolized.oat"); + + // Execute oatdump on the remote device to generate a file with symtab + StreamString command; + command.Printf("oatdump --symbolize=%s --output=%s", + module_sp->GetPlatformFileSpec().GetCString(false), + symfile_platform_filespec.GetCString(false)); + error = adb.Shell(command.GetData(), 60000 /* ms */, nullptr); + if (error.Fail()) + return Error("Oatdump failed: %s", error.AsCString()); + + // Download the symbolfile from the remote device + return GetFile(symfile_platform_filespec, dst_file_spec); +} + +bool +PlatformAndroid::GetRemoteOSVersion () +{ + m_major_os_version = GetSdkVersion(); + m_minor_os_version = 0; + m_update_os_version = 0; + return m_major_os_version != 0; +} + +const char* +PlatformAndroid::GetLibdlFunctionDeclarations() const +{ + return R"( + extern "C" void* dlopen(const char*, int) asm("__dl_dlopen"); + extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym"); + extern "C" int dlclose(void*) asm("__dl_dlclose"); + extern "C" char* dlerror(void) asm("__dl_dlerror"); + )"; +} diff --git a/source/Plugins/Platform/Android/PlatformAndroid.h b/source/Plugins/Platform/Android/PlatformAndroid.h new file mode 100644 index 000000000000..119d0a0bdf04 --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroid.h @@ -0,0 +1,114 @@ +//===-- PlatformAndroid.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAndroid_h_ +#define liblldb_PlatformAndroid_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "Plugins/Platform/Linux/PlatformLinux.h" + +namespace lldb_private { +namespace platform_android { + + class PlatformAndroid : public platform_linux::PlatformLinux + { + public: + PlatformAndroid(bool is_host); + + ~PlatformAndroid() override; + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const ArchSpec *arch); + + static ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetPluginDescriptionStatic (bool is_host); + + ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + + Error + ConnectRemote (Args& args) override; + + Error + GetFile (const FileSpec& source, + const FileSpec& destination) override; + + Error + PutFile (const FileSpec& source, + const FileSpec& destination, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX) override; + + uint32_t + GetSdkVersion(); + + bool + GetRemoteOSVersion() override; + + Error + DisconnectRemote () override; + + uint32_t + GetDefaultMemoryCacheLineSize() override; + + protected: + const char * + GetCacheHostname () override; + + Error + DownloadModuleSlice (const FileSpec &src_file_spec, + const uint64_t src_offset, + const uint64_t src_size, + const FileSpec &dst_file_spec) override; + + Error + DownloadSymbolFile (const lldb::ModuleSP& module_sp, + const FileSpec& dst_file_spec) override; + + const char* + GetLibdlFunctionDeclarations() const override; + + private: + std::string m_device_id; + uint32_t m_sdk_version; + + DISALLOW_COPY_AND_ASSIGN (PlatformAndroid); + }; + +} // namespace platofor_android +} // namespace lldb_private + +#endif // liblldb_PlatformAndroid_h_ diff --git a/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp new file mode 100644 index 000000000000..3d91dd6b7a32 --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp @@ -0,0 +1,275 @@ +//===-- PlatformAndroidRemoteGDBServer.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 "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/common/TCPSocket.h" +#include "PlatformAndroidRemoteGDBServer.h" +#include "Utility/UriParser.h" + +#include + +using namespace lldb; +using namespace lldb_private; +using namespace platform_android; + +static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform + +static Error +ForwardPortWithAdb (const uint16_t local_port, + const uint16_t remote_port, + const char* remote_socket_name, + const llvm::Optional& socket_namespace, + std::string& device_id) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + + AdbClient adb; + auto error = AdbClient::CreateByDeviceID(device_id, adb); + if (error.Fail ()) + return error; + + device_id = adb.GetDeviceID(); + if (log) + log->Printf("Connected to Android device \"%s\"", device_id.c_str ()); + + if (remote_port != 0) + { + if (log) + log->Printf("Forwarding remote TCP port %d to local TCP port %d", remote_port, local_port); + return adb.SetPortForwarding(local_port, remote_port); + } + + if (log) + log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", remote_socket_name, local_port); + + if (!socket_namespace) + return Error("Invalid socket namespace"); + + return adb.SetPortForwarding(local_port, remote_socket_name, *socket_namespace); +} + +static Error +DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id) +{ + AdbClient adb (device_id); + return adb.DeletePortForwarding (local_port); +} + +static Error +FindUnusedPort (uint16_t& port) +{ + Error error; + std::unique_ptr tcp_socket(new TCPSocket(false, error)); + if (error.Fail()) + return error; + + error = tcp_socket->Listen("127.0.0.1:0", 1); + if (error.Success()) + port = tcp_socket->GetLocalPortNumber(); + + return error; +} + +PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer () +{ +} + +PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer () +{ + for (const auto& it : m_port_forwards) + DeleteForwardPortWithAdb(it.second, m_device_id); +} + +bool +PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) +{ + uint16_t remote_port = 0; + std::string socket_name; + if (!m_gdb_client.LaunchGDBServer ("127.0.0.1", pid, remote_port, socket_name)) + return false; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + + auto error = MakeConnectURL (pid, + remote_port, + socket_name.c_str (), + connect_url); + if (error.Success() && log) + log->Printf("gdbserver connect URL: %s", connect_url.c_str()); + + return error.Success(); +} + +bool +PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid) +{ + DeleteForwardPort (pid); + return m_gdb_client.KillSpawnedProcess (pid); +} + +Error +PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args) +{ + m_device_id.clear(); + + if (args.GetArgumentCount() != 1) + return Error("\"platform connect\" takes a single argument: "); + + int remote_port; + std::string scheme, host, path; + const char *url = args.GetArgumentAtIndex (0); + if (!url) + return Error("URL is null."); + if (!UriParser::Parse (url, scheme, host, remote_port, path)) + return Error("Invalid URL: %s", url); + if (host != "localhost") + m_device_id = host; + + m_socket_namespace.reset(); + if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME) + m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; + else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME) + m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; + + std::string connect_url; + auto error = MakeConnectURL (g_remote_platform_pid, + (remote_port < 0) ? 0 : remote_port, + path.c_str (), + connect_url); + + if (error.Fail ()) + return error; + + args.ReplaceArgumentAtIndex (0, connect_url.c_str ()); + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("Rewritten platform connect URL: %s", connect_url.c_str()); + + error = PlatformRemoteGDBServer::ConnectRemote(args); + if (error.Fail ()) + DeleteForwardPort (g_remote_platform_pid); + + return error; +} + +Error +PlatformAndroidRemoteGDBServer::DisconnectRemote () +{ + DeleteForwardPort (g_remote_platform_pid); + return PlatformRemoteGDBServer::DisconnectRemote (); +} + +void +PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + + auto it = m_port_forwards.find(pid); + if (it == m_port_forwards.end()) + return; + + const auto port = it->second; + const auto error = DeleteForwardPortWithAdb(port, m_device_id); + if (error.Fail()) { + if (log) + log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s", + pid, port, m_device_id.c_str(), error.AsCString()); + } + m_port_forwards.erase(it); +} + +Error +PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid, + const uint16_t remote_port, + const char* remote_socket_name, + std::string& connect_url) +{ + static const int kAttempsNum = 5; + + Error error; + // There is a race possibility that somebody will occupy + // a port while we're in between FindUnusedPort and ForwardPortWithAdb - + // adding the loop to mitigate such problem. + for (auto i = 0; i < kAttempsNum; ++i) + { + uint16_t local_port = 0; + error = FindUnusedPort(local_port); + if (error.Fail()) + return error; + + error = ForwardPortWithAdb(local_port, + remote_port, + remote_socket_name, + m_socket_namespace, + m_device_id); + if (error.Success()) + { + m_port_forwards[pid] = local_port; + std::ostringstream url_str; + url_str << "connect://localhost:" << local_port; + connect_url = url_str.str(); + break; + } + } + + return error; +} + +lldb::ProcessSP +PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) +{ + // We don't have the pid of the remote gdbserver when it isn't started by us but we still want + // to store the list of port forwards we set up in our port forward map. Generate a fake pid for + // these cases what won't collide with any other valid pid on android. + static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; + + int remote_port; + std::string scheme, host, path; + if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) + { + error.SetErrorStringWithFormat("Invalid URL: %s", connect_url); + return nullptr; + } + + std::string new_connect_url; + error = MakeConnectURL(s_remote_gdbserver_fake_pid--, + (remote_port < 0) ? 0 : remote_port, + path.c_str(), + new_connect_url); + if (error.Fail()) + return nullptr; + + return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(), + plugin_name, + debugger, + target, + error); +} + +size_t +PlatformAndroidRemoteGDBServer::ConnectToWaitingProcesses(Debugger& debugger, Error& error) +{ + std::vector connection_urls; + GetPendingGdbServerList(connection_urls); + + for (size_t i = 0; i < connection_urls.size(); ++i) + { + ConnectProcess(connection_urls[i].c_str(), nullptr, debugger, nullptr, error); + if (error.Fail()) + return i; // We already connected to i process succsessfully + } + return connection_urls.size(); +} diff --git a/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h new file mode 100644 index 000000000000..3d2653812ded --- /dev/null +++ b/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h @@ -0,0 +1,79 @@ +//===-- PlatformAndroidRemoteGDBServer.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAndroidRemoteGDBServer_h_ +#define liblldb_PlatformAndroidRemoteGDBServer_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" + +#include "llvm/ADT/Optional.h" + +#include "AdbClient.h" + +namespace lldb_private { +namespace platform_android { + +class PlatformAndroidRemoteGDBServer : public platform_gdb_server::PlatformRemoteGDBServer +{ +public: + PlatformAndroidRemoteGDBServer(); + + ~PlatformAndroidRemoteGDBServer() override; + + Error + ConnectRemote (Args& args) override; + + Error + DisconnectRemote () override; + + lldb::ProcessSP + ConnectProcess (const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) override; + + size_t + ConnectToWaitingProcesses(lldb_private::Debugger& debugger, lldb_private::Error& error) override; + +protected: + std::string m_device_id; + std::map m_port_forwards; + llvm::Optional m_socket_namespace; + + bool + LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) override; + + bool + KillSpawnedProcess (lldb::pid_t pid) override; + + void + DeleteForwardPort (lldb::pid_t pid); + + Error + MakeConnectURL(const lldb::pid_t pid, + const uint16_t remote_port, + const char* remote_socket_name, + std::string& connect_url); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAndroidRemoteGDBServer); +}; + +} // namespace platform_android +} // namespace lldb_private + +#endif // liblldb_PlatformAndroidRemoteGDBServer_h_ diff --git a/source/Plugins/Platform/CMakeLists.txt b/source/Plugins/Platform/CMakeLists.txt new file mode 100644 index 000000000000..2e3a3f7c1b2e --- /dev/null +++ b/source/Plugins/Platform/CMakeLists.txt @@ -0,0 +1,16 @@ +#if (CMAKE_SYSTEM_NAME MATCHES "Linux") + add_subdirectory(Linux) +#elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_subdirectory(FreeBSD) +#elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + add_subdirectory(NetBSD) +#elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(MacOSX) +#elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") + add_subdirectory(Windows) +#endif() + +add_subdirectory(POSIX) +add_subdirectory(gdb-server) +add_subdirectory(Kalimba) +add_subdirectory(Android) diff --git a/source/Plugins/Platform/FreeBSD/CMakeLists.txt b/source/Plugins/Platform/FreeBSD/CMakeLists.txt new file mode 100644 index 000000000000..57153969c3b4 --- /dev/null +++ b/source/Plugins/Platform/FreeBSD/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformFreeBSD + PlatformFreeBSD.cpp + ) diff --git a/source/Plugins/Platform/FreeBSD/Makefile b/source/Plugins/Platform/FreeBSD/Makefile new file mode 100644 index 000000000000..e5c25d8504df --- /dev/null +++ b/source/Plugins/Platform/FreeBSD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/FreeBSD/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformFreeBSD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Kalimba/CMakeLists.txt b/source/Plugins/Platform/Kalimba/CMakeLists.txt new file mode 100644 index 000000000000..df0bf9761a00 --- /dev/null +++ b/source/Plugins/Platform/Kalimba/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformKalimba + PlatformKalimba.cpp + ) diff --git a/source/Plugins/Platform/Kalimba/Makefile b/source/Plugins/Platform/Kalimba/Makefile new file mode 100644 index 000000000000..c22b7d21c13d --- /dev/null +++ b/source/Plugins/Platform/Kalimba/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Kalimba/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformKalimba +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Kalimba/PlatformKalimba.cpp b/source/Plugins/Platform/Kalimba/PlatformKalimba.cpp new file mode 100644 index 000000000000..2f1e4d554320 --- /dev/null +++ b/source/Plugins/Platform/Kalimba/PlatformKalimba.cpp @@ -0,0 +1,324 @@ +//===-- PlatformKalimba.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformKalimba.h" +#include "lldb/Host/Config.h" + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +static uint32_t g_initialize_count = 0; + +PlatformSP +PlatformKalimba::CreateInstance (bool force, const ArchSpec *arch) +{ + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::CSR: + create = true; + break; + + default: + break; + } + } + if (create) + return PlatformSP(new PlatformKalimba(false)); + return PlatformSP(); +} + +lldb_private::ConstString +PlatformKalimba::GetPluginNameStatic (bool /*is_host*/) +{ + static ConstString g_remote_name("kalimba"); + return g_remote_name; +} + +const char * +PlatformKalimba::GetPluginDescriptionStatic (bool /*is_host*/) +{ + return "Kalimba user platform plug-in."; +} + +lldb_private::ConstString +PlatformKalimba::GetPluginName() +{ + return GetPluginNameStatic(false); +} + +void +PlatformKalimba::Initialize () +{ + Platform::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin(PlatformKalimba::GetPluginNameStatic(false), + PlatformKalimba::GetPluginDescriptionStatic(false), + PlatformKalimba::CreateInstance); + } +} + +void +PlatformKalimba::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformKalimba::CreateInstance); + } + } + + Platform::Terminate (); +} + +Error +PlatformKalimba::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + char exe_path[PATH_MAX]; + ModuleSpec resolved_module_spec(ms); + + if (!resolved_module_spec.GetFileSpec().Exists()) + { + resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path); + } + + if (error.Success()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + if (error.Fail()) + { + // If we failed, it may be because the vendor and os aren't known. If that is the + // case, try setting them to the host architecture and give it another try. + llvm::Triple &module_triple = resolved_module_spec.GetArchitecture().GetTriple(); + bool is_vendor_specified = (module_triple.getVendor() != llvm::Triple::UnknownVendor); + bool is_os_specified = (module_triple.getOS() != llvm::Triple::UnknownOS); + if (!is_vendor_specified || !is_os_specified) + { + const llvm::Triple &host_triple = HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple(); + + if (!is_vendor_specified) + module_triple.setVendorName (host_triple.getVendorName()); + if (!is_os_specified) + module_triple.setOSName (host_triple.getOSName()); + + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + } + } + + // TODO find out why exe_module_sp might be NULL + if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + } + + return error; +} + +Error +PlatformKalimba::GetFileWithUUID (const FileSpec & /*platform_file*/, + const UUID * /*uuid_ptr*/, FileSpec & /*local_file*/) +{ + return Error(); +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformKalimba::PlatformKalimba (bool is_host) : + Platform(is_host), // This is the local host platform + m_remote_platform_sp () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformKalimba::~PlatformKalimba() +{ +} + +bool +PlatformKalimba::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = false; + } + else + { + if (m_remote_platform_sp) + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + +bool +PlatformKalimba::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + if (idx == 0) + { + arch = ArchSpec("kalimba3-csr-unknown"); + return true; + } + if (idx == 1) + { + arch = ArchSpec("kalimba4-csr-unknown"); + return true; + } + if (idx == 2) + { + arch = ArchSpec("kalimba5-csr-unknown"); + return true; + } + return false; +} + +void +PlatformKalimba::GetStatus (Stream &strm) +{ + Platform::GetStatus(strm); +} + +size_t +PlatformKalimba::GetSoftwareBreakpointTrapOpcode (Target & /*target*/, + BreakpointSite * /*bp_site*/) +{ + // the target hardware does not support software breakpoints + return 0; +} + +Error +PlatformKalimba::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + + if (IsHost()) + { + error.SetErrorString ("native execution is not possible"); + } + else + { + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +lldb::ProcessSP +PlatformKalimba::Attach(ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, + Error &error) +{ + lldb::ProcessSP process_sp; + if (IsHost()) + { + error.SetErrorString ("native execution is not possible"); + } + else + { + if (m_remote_platform_sp) + process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error); + else + error.SetErrorString ("the platform is not currently connected"); + } + return process_sp; +} + +void +PlatformKalimba::CalculateTrapHandlerSymbolNames () +{ + // TODO Research this sometime. +} diff --git a/source/Plugins/Platform/Kalimba/PlatformKalimba.h b/source/Plugins/Platform/Kalimba/PlatformKalimba.h new file mode 100644 index 000000000000..dd68415838f0 --- /dev/null +++ b/source/Plugins/Platform/Kalimba/PlatformKalimba.h @@ -0,0 +1,99 @@ +//===-- PlatformKalimba.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformKalimba_h_ +#define liblldb_PlatformKalimba_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Platform.h" + +namespace lldb_private { + + class PlatformKalimba : public Platform + { + public: + PlatformKalimba(bool is_host); + + ~PlatformKalimba() override; + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static lldb_private::ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetPluginDescriptionStatic (bool is_host); + + lldb_private::ConstString GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + Error ResolveExecutable(const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription() override + { + return GetPluginDescriptionStatic(IsHost()); + } + + void GetStatus(Stream &strm) override; + + Error GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid, FileSpec &local_file) override; + + bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info) override; + + bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override; + + size_t GetSoftwareBreakpointTrapOpcode(Target &target, BreakpointSite *bp_site) override; + + lldb_private::Error LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) override; + + lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, + Error &error) override; + + // Kalimba processes can not be launched by spawning and attaching. + bool + CanDebugProcess() override + { + return false; + } + + void CalculateTrapHandlerSymbolNames() override; + + protected: + lldb::PlatformSP m_remote_platform_sp; + + private: + DISALLOW_COPY_AND_ASSIGN (PlatformKalimba); + }; + +} // namespace lldb_private + +#endif // liblldb_PlatformKalimba_h_ diff --git a/source/Plugins/Platform/Linux/CMakeLists.txt b/source/Plugins/Platform/Linux/CMakeLists.txt new file mode 100644 index 000000000000..4a9eb1460e38 --- /dev/null +++ b/source/Plugins/Platform/Linux/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformLinux + PlatformLinux.cpp + ) diff --git a/source/Plugins/Platform/Linux/Makefile b/source/Plugins/Platform/Linux/Makefile new file mode 100644 index 000000000000..2877fddf0bcd --- /dev/null +++ b/source/Plugins/Platform/Linux/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Linux/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformLinux +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Linux/PlatformLinux.cpp b/source/Plugins/Platform/Linux/PlatformLinux.cpp new file mode 100644 index 000000000000..8dc9769844c7 --- /dev/null +++ b/source/Plugins/Platform/Linux/PlatformLinux.cpp @@ -0,0 +1,868 @@ +//===-- PlatformLinux.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformLinux.h" +#include "lldb/Host/Config.h" + +// C Includes +#include +#ifndef LLDB_DISABLE_POSIX +#include +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" + +// Define these constants from Linux mman.h for use when targeting +// remote linux systems even when host has different values. +#define MAP_PRIVATE 2 +#define MAP_ANON 0x20 + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_linux; + +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +/// Code to handle the PlatformLinux settings +//------------------------------------------------------------------ + +namespace +{ + class PlatformLinuxProperties : public Properties + { + public: + PlatformLinuxProperties(); + + ~PlatformLinuxProperties() override = default; + + static ConstString& + GetSettingName (); + + private: + static const PropertyDefinition* + GetStaticPropertyDefinitions(); + }; + + typedef std::shared_ptr PlatformLinuxPropertiesSP; + +} // anonymous namespace + +PlatformLinuxProperties::PlatformLinuxProperties() : + Properties () +{ + m_collection_sp.reset (new OptionValueProperties(GetSettingName ())); + m_collection_sp->Initialize (GetStaticPropertyDefinitions ()); +} + +ConstString& +PlatformLinuxProperties::GetSettingName () +{ + static ConstString g_setting_name("linux"); + return g_setting_name; +} + +const PropertyDefinition* +PlatformLinuxProperties::GetStaticPropertyDefinitions() +{ + static PropertyDefinition + g_properties[] = + { + { NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL } + }; + + return g_properties; +} + +static const PlatformLinuxPropertiesSP & +GetGlobalProperties() +{ + static PlatformLinuxPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new PlatformLinuxProperties ()); + return g_settings_sp; +} + +void +PlatformLinux::DebuggerInitialize (Debugger &debugger) +{ + if (!PluginManager::GetSettingForPlatformPlugin (debugger, PlatformLinuxProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForPlatformPlugin (debugger, + GetGlobalProperties()->GetValueProperties(), + ConstString ("Properties for the PlatformLinux plug-in."), + is_global_setting); + } +} + +//------------------------------------------------------------------ + +PlatformSP +PlatformLinux::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformLinux::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getOS()) + { + case llvm::Triple::Linux: + create = true; + break; + +#if defined(__linux__) + // Only accept "unknown" for the OS if the host is linux and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::OSType::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + break; + } + } + + if (create) + { + if (log) + log->Printf ("PlatformLinux::%s() creating remote-linux platform", __FUNCTION__); + return PlatformSP(new PlatformLinux(false)); + } + + if (log) + log->Printf ("PlatformLinux::%s() aborting creation of remote-linux platform", __FUNCTION__); + + return PlatformSP(); +} + +ConstString +PlatformLinux::GetPluginNameStatic (bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-linux"); + return g_remote_name; + } +} + +const char * +PlatformLinux::GetPluginDescriptionStatic (bool is_host) +{ + if (is_host) + return "Local Linux user platform plug-in."; + else + return "Remote Linux user platform plug-in."; +} + +ConstString +PlatformLinux::GetPluginName() +{ + return GetPluginNameStatic(IsHost()); +} + +void +PlatformLinux::Initialize () +{ + PlatformPOSIX::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined(__linux__) && !defined(__ANDROID__) + PlatformSP default_platform_sp (new PlatformLinux(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin(PlatformLinux::GetPluginNameStatic(false), + PlatformLinux::GetPluginDescriptionStatic(false), + PlatformLinux::CreateInstance, + PlatformLinux::DebuggerInitialize); + } +} + +void +PlatformLinux::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformLinux::CreateInstance); + } + } + + PlatformPOSIX::Terminate (); +} + +Error +PlatformLinux::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + char exe_path[PATH_MAX]; + ModuleSpec resolved_module_spec (ms); + + if (IsHost()) + { + // If we have "ls" as the exe_file, resolve the executable location based on + // the current path variables + if (!resolved_module_spec.GetFileSpec().Exists()) + { + resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + resolved_module_spec.GetFileSpec().SetFile(exe_path, true); + } + + if (!resolved_module_spec.GetFileSpec().Exists()) + resolved_module_spec.GetFileSpec().ResolveExecutableLocation (); + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + { + error.SetErrorStringWithFormat("unable to find executable for '%s'", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + else + { + if (m_remote_platform_sp) + { + error = GetCachedExecutable (resolved_module_spec, exe_module_sp, module_search_paths_ptr, *m_remote_platform_sp); + } + else + { + // We may connect to a process and use the provided executable (Don't use local $PATH). + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path); + } + } + + if (error.Success()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + if (error.Fail()) + { + // If we failed, it may be because the vendor and os aren't known. If that is the + // case, try setting them to the host architecture and give it another try. + llvm::Triple &module_triple = resolved_module_spec.GetArchitecture().GetTriple(); + bool is_vendor_specified = (module_triple.getVendor() != llvm::Triple::UnknownVendor); + bool is_os_specified = (module_triple.getOS() != llvm::Triple::UnknownOS); + if (!is_vendor_specified || !is_os_specified) + { + const llvm::Triple &host_triple = HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple(); + + if (!is_vendor_specified) + module_triple.setVendorName (host_triple.getVendorName()); + if (!is_os_specified) + module_triple.setOSName (host_triple.getOSName()); + + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + } + } + + // TODO find out why exe_module_sp might be NULL + if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + } + + return error; +} + +Error +PlatformLinux::GetFileWithUUID (const FileSpec &platform_file, + const UUID *uuid_ptr, FileSpec &local_file) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file); + } + + // Default to the local case + local_file = platform_file; + return Error(); +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformLinux::PlatformLinux (bool is_host) : + PlatformPOSIX(is_host) // This is the local host platform +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformLinux::~PlatformLinux() = default; + +bool +PlatformLinux::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = Platform::GetProcessInfo (pid, process_info); + } + else + { + if (m_remote_platform_sp) + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + +uint32_t +PlatformLinux::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + uint32_t match_count = 0; + if (IsHost()) + { + // Let the base class figure out the host details + match_count = Platform::FindProcesses (match_info, process_infos); + } + else + { + // If we are remote, we can only return results if we are connected + if (m_remote_platform_sp) + match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos); + } + return match_count; +} + +bool +PlatformLinux::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + if (IsHost()) + { + ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + if (hostArch.GetTriple().isOSLinux()) + { + if (idx == 0) + { + arch = hostArch; + return arch.IsValid(); + } + else if (idx == 1) + { + // If the default host architecture is 64-bit, look for a 32-bit variant + if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) + { + arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); + return arch.IsValid(); + } + } + } + } + else + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch); + + llvm::Triple triple; + // Set the OS to linux + triple.setOS(llvm::Triple::Linux); + // Set the architecture + switch (idx) + { + case 0: triple.setArchName("x86_64"); break; + case 1: triple.setArchName("i386"); break; + case 2: triple.setArchName("arm"); break; + case 3: triple.setArchName("aarch64"); break; + case 4: triple.setArchName("mips64"); break; + case 5: triple.setArchName("hexagon"); break; + case 6: triple.setArchName("mips"); break; + case 7: triple.setArchName("mips64el"); break; + case 8: triple.setArchName("mipsel"); break; + default: return false; + } + // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the vendor by + // calling triple.SetVendorName("unknown") so that it is a "unspecified unknown". + // This means when someone calls triple.GetVendorName() it will return an empty string + // which indicates that the vendor can be set when two architectures are merged + + // Now set the triple into "arch" and return true + arch.SetTriple(triple); + return true; + } + return false; +} + +void +PlatformLinux::GetStatus (Stream &strm) +{ + Platform::GetStatus(strm); + +#ifndef LLDB_DISABLE_POSIX + // Display local kernel information only when we are running in host mode. + // Otherwise, we would end up printing non-Linux information (when running + // on Mac OS for example). + if (IsHost()) + { + struct utsname un; + + if (uname(&un)) + return; + + strm.Printf (" Kernel: %s\n", un.sysname); + strm.Printf (" Release: %s\n", un.release); + strm.Printf (" Version: %s\n", un.version); + } +#endif +} + +size_t +PlatformLinux::GetSoftwareBreakpointTrapOpcode (Target &target, + BreakpointSite *bp_site) +{ + ArchSpec arch = target.GetArchitecture(); + const uint8_t *trap_opcode = NULL; + size_t trap_opcode_size = 0; + + switch (arch.GetMachine()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case llvm::Triple::aarch64: + { + static const uint8_t g_aarch64_opcode[] = { 0x00, 0x00, 0x20, 0xd4 }; + trap_opcode = g_aarch64_opcode; + trap_opcode_size = sizeof(g_aarch64_opcode); + } + break; + case llvm::Triple::x86: + case llvm::Triple::x86_64: + { + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + } + break; + case llvm::Triple::hexagon: + { + static const uint8_t g_hex_opcode[] = { 0x0c, 0xdb, 0x00, 0x54 }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } + break; + case llvm::Triple::arm: + { + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe + // but the linux kernel does otherwise. + static const uint8_t g_arm_breakpoint_opcode[] = { 0xf0, 0x01, 0xf0, 0xe7 }; + static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde }; + + lldb::BreakpointLocationSP bp_loc_sp (bp_site->GetOwnerAtIndex (0)); + AddressClass addr_class = eAddressClassUnknown; + + if (bp_loc_sp) + { + addr_class = bp_loc_sp->GetAddress ().GetAddressClass (); + + if (addr_class == eAddressClassUnknown && + (bp_loc_sp->GetAddress ().GetFileAddress () & 1)) + { + addr_class = eAddressClassCodeAlternateISA; + } + } + + if (addr_class == eAddressClassCodeAlternateISA) + { + trap_opcode = g_thumb_breakpoint_opcode; + trap_opcode_size = sizeof(g_thumb_breakpoint_opcode); + } + else + { + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + } + } + break; + case llvm::Triple::mips: + case llvm::Triple::mips64: + { + static const uint8_t g_hex_opcode[] = { 0x00, 0x00, 0x00, 0x0d }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } + break; + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: + { + static const uint8_t g_hex_opcode[] = { 0x0d, 0x00, 0x00, 0x00 }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } + break; + } + + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + return 0; +} + +int32_t +PlatformLinux::GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) +{ + int32_t resume_count = 0; + + // Always resume past the initial stop when we use eLaunchFlagDebug + if (launch_info.GetFlags ().Test (eLaunchFlagDebug)) + { + // Resume past the stop for the final exec into the true inferior. + ++resume_count; + } + + // If we're not launching a shell, we're done. + const FileSpec &shell = launch_info.GetShell(); + if (!shell) + return resume_count; + + std::string shell_string = shell.GetPath(); + // We're in a shell, so for sure we have to resume past the shell exec. + ++resume_count; + + // Figure out what shell we're planning on using. + const char *shell_name = strrchr (shell_string.c_str(), '/'); + if (shell_name == NULL) + shell_name = shell_string.c_str(); + else + shell_name++; + + if (strcmp (shell_name, "csh") == 0 + || strcmp (shell_name, "tcsh") == 0 + || strcmp (shell_name, "zsh") == 0 + || strcmp (shell_name, "sh") == 0) + { + // These shells seem to re-exec themselves. Add another resume. + ++resume_count; + } + + return resume_count; +} + +bool +PlatformLinux::CanDebugProcess () +{ + if (IsHost ()) + { + return true; + } + else + { + // If we're connected, we can debug. + return IsConnected (); + } +} + +// For local debugging, Linux will override the debug logic to use llgs-launch rather than +// lldb-launch, llgs-attach. This differs from current lldb-launch, debugserver-attach +// approach on MacOSX. +lldb::ProcessSP +PlatformLinux::DebugProcess (ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Error &error) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("PlatformLinux::%s entered (target %p)", __FUNCTION__, static_cast(target)); + + // If we're a remote host, use standard behavior from parent class. + if (!IsHost ()) + return PlatformPOSIX::DebugProcess (launch_info, debugger, target, error); + + // + // For local debugging, we'll insist on having ProcessGDBRemote create the process. + // + + ProcessSP process_sp; + + // Make sure we stop at the entry point + launch_info.GetFlags ().Set (eLaunchFlagDebug); + + // We always launch the process we are going to debug in a separate process + // group, since then we can handle ^C interrupts ourselves w/o having to worry + // about the target getting them as well. + launch_info.SetLaunchInSeparateProcessGroup(true); + + // Ensure we have a target. + if (target == nullptr) + { + if (log) + log->Printf ("PlatformLinux::%s creating new target", __FUNCTION__); + + TargetSP new_target_sp; + error = debugger.GetTargetList().CreateTarget (debugger, + nullptr, + nullptr, + false, + nullptr, + new_target_sp); + if (error.Fail ()) + { + if (log) + log->Printf ("PlatformLinux::%s failed to create new target: %s", __FUNCTION__, error.AsCString ()); + return process_sp; + } + + target = new_target_sp.get(); + if (!target) + { + error.SetErrorString ("CreateTarget() returned nullptr"); + if (log) + log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); + return process_sp; + } + } + else + { + if (log) + log->Printf ("PlatformLinux::%s using provided target", __FUNCTION__); + } + + // Mark target as currently selected target. + debugger.GetTargetList().SetSelectedTarget(target); + + // Now create the gdb-remote process. + if (log) + log->Printf ("PlatformLinux::%s having target create process with gdb-remote plugin", __FUNCTION__); + process_sp = target->CreateProcess (launch_info.GetListenerForProcess(debugger), "gdb-remote", nullptr); + + if (!process_sp) + { + error.SetErrorString ("CreateProcess() failed for gdb-remote process"); + if (log) + log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); + return process_sp; + } + else + { + if (log) + log->Printf ("PlatformLinux::%s successfully created process", __FUNCTION__); + } + + // Adjust launch for a hijacker. + ListenerSP listener_sp; + if (!launch_info.GetHijackListener ()) + { + if (log) + log->Printf ("PlatformLinux::%s setting up hijacker", __FUNCTION__); + + listener_sp.reset (new Listener("lldb.PlatformLinux.DebugProcess.hijack")); + launch_info.SetHijackListener (listener_sp); + process_sp->HijackProcessEvents (listener_sp.get ()); + } + + // Log file actions. + if (log) + { + log->Printf ("PlatformLinux::%s launching process with the following file actions:", __FUNCTION__); + + StreamString stream; + size_t i = 0; + const FileAction *file_action; + while ((file_action = launch_info.GetFileActionAtIndex (i++)) != nullptr) + { + file_action->Dump (stream); + log->PutCString (stream.GetString().c_str ()); + stream.Clear(); + } + } + + // Do the launch. + error = process_sp->Launch(launch_info); + if (error.Success ()) + { + // Handle the hijacking of process events. + if (listener_sp) + { + const StateType state = process_sp->WaitForProcessToStop (NULL, NULL, false, listener_sp.get()); + + if (state == eStateStopped) + { + if (log) + log->Printf ("PlatformLinux::%s pid %" PRIu64 " state %s\n", + __FUNCTION__, process_sp->GetID (), StateAsCString (state)); + } + else + { + if (log) + log->Printf ("PlatformLinux::%s pid %" PRIu64 " state is not stopped - %s\n", + __FUNCTION__, process_sp->GetID (), StateAsCString (state)); + } + } + + // Hook up process PTY if we have one (which we should for local debugging with llgs). + int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); + if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) + { + process_sp->SetSTDIOFileDescriptor(pty_fd); + if (log) + log->Printf ("PlatformLinux::%s pid %" PRIu64 " hooked up STDIO pty to process", __FUNCTION__, process_sp->GetID ()); + } + else + { + if (log) + log->Printf ("PlatformLinux::%s pid %" PRIu64 " not using process STDIO pty", __FUNCTION__, process_sp->GetID ()); + } + } + else + { + if (log) + log->Printf ("PlatformLinux::%s process launch failed: %s", __FUNCTION__, error.AsCString ()); + // FIXME figure out appropriate cleanup here. Do we delete the target? Do we delete the process? Does our caller do that? + } + + return process_sp; +} + +void +PlatformLinux::CalculateTrapHandlerSymbolNames () +{ + m_trap_handlers.push_back (ConstString ("_sigtramp")); +} + +uint64_t +PlatformLinux::ConvertMmapFlagsToPlatform(const ArchSpec &arch, unsigned flags) +{ + uint64_t flags_platform = 0; + uint64_t map_anon = MAP_ANON; + + // To get correct flags for MIPS Architecture + if (arch.GetTriple ().getArch () == llvm::Triple::mips64 + || arch.GetTriple ().getArch () == llvm::Triple::mips64el + || arch.GetTriple ().getArch () == llvm::Triple::mips + || arch.GetTriple ().getArch () == llvm::Triple::mipsel) + map_anon = 0x800; + + if (flags & eMmapFlagsPrivate) + flags_platform |= MAP_PRIVATE; + if (flags & eMmapFlagsAnon) + flags_platform |= map_anon; + return flags_platform; +} + +ConstString +PlatformLinux::GetFullNameForDylib (ConstString basename) +{ + if (basename.IsEmpty()) + return basename; + + StreamString stream; + stream.Printf("lib%s.so", basename.GetCString()); + return ConstString(stream.GetData()); +} diff --git a/source/Plugins/Platform/Linux/PlatformLinux.h b/source/Plugins/Platform/Linux/PlatformLinux.h new file mode 100644 index 000000000000..770a20c90cce --- /dev/null +++ b/source/Plugins/Platform/Linux/PlatformLinux.h @@ -0,0 +1,122 @@ +//===-- PlatformLinux.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformLinux_h_ +#define liblldb_PlatformLinux_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" + +namespace lldb_private { +namespace platform_linux { + + class PlatformLinux : public PlatformPOSIX + { + public: + PlatformLinux(bool is_host); + + ~PlatformLinux() override; + + static void + DebuggerInitialize (Debugger &debugger); + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const ArchSpec *arch); + + static ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetPluginDescriptionStatic (bool is_host); + + ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + Error + ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetPluginDescriptionStatic(IsHost()); + } + + void + GetStatus (Stream &strm) override; + + Error + GetFileWithUUID (const FileSpec &platform_file, + const UUID* uuid, FileSpec &local_file) override; + + bool + GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info) override; + + uint32_t + FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) override; + + size_t + GetSoftwareBreakpointTrapOpcode (Target &target, + BreakpointSite *bp_site) override; + + int32_t + GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) override; + + bool + CanDebugProcess () override; + + lldb::ProcessSP + DebugProcess (ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target *target, + Error &error) override; + + void + CalculateTrapHandlerSymbolNames () override; + + uint64_t + ConvertMmapFlagsToPlatform(const ArchSpec &arch, unsigned flags) override; + + ConstString + GetFullNameForDylib (ConstString basename) override; + + private: + DISALLOW_COPY_AND_ASSIGN (PlatformLinux); + }; + +} // namespace platform_linux +} // namespace lldb_private + +#endif // liblldb_PlatformLinux_h_ diff --git a/source/Plugins/Platform/MacOSX/CMakeLists.txt b/source/Plugins/Platform/MacOSX/CMakeLists.txt new file mode 100644 index 000000000000..02566ab3db06 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/CMakeLists.txt @@ -0,0 +1,27 @@ +list(APPEND PLUGIN_PLATFORM_MACOSX_SOURCES + PlatformDarwin.cpp + PlatformDarwinKernel.cpp + PlatformMacOSX.cpp + PlatformRemoteiOS.cpp + PlatformRemoteAppleTV.cpp + PlatformRemoteAppleWatch.cpp + ) + +list(APPEND PLUGIN_PLATFORM_MACOSX_DARWIN_ONLY_SOURCES + PlatformAppleSimulator.cpp + PlatformiOSSimulator.cpp + PlatformiOSSimulatorCoreSimulatorSupport.mm + PlatformAppleTVSimulator.cpp + PlatformAppleWatchSimulator.cpp + ) + +if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + include_directories(${LIBXML2_INCLUDE_DIR}) + list(APPEND PLUGIN_PLATFORM_MACOSX_SOURCES + ${PLUGIN_PLATFORM_MACOSX_DARWIN_ONLY_SOURCES}) +else() + list(APPEND LLVM_OPTIONAL_SOURCES + ${PLUGIN_PLATFORM_MACOSX_DARWIN_ONLY_SOURCES}) +endif() + +add_lldb_library(lldbPluginPlatformMacOSX ${PLUGIN_PLATFORM_MACOSX_SOURCES}) diff --git a/source/Plugins/Platform/MacOSX/Makefile b/source/Plugins/Platform/MacOSX/Makefile new file mode 100644 index 000000000000..3e61dc982bd6 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/Makefile @@ -0,0 +1,15 @@ +##===- source/Plugins/Platform/MacOSX/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformMacOSX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile + diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp b/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp new file mode 100644 index 000000000000..eea2844d5649 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp @@ -0,0 +1,303 @@ +//===-- PlatformAppleSimulator.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformAppleSimulator.h" + +// C Includes +#if defined(__APPLE__) +#include +#endif + +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/PseudoTerminal.h" + +using namespace lldb; +using namespace lldb_private; + +#if !defined(__APPLE__) +#define UNSUPPORTED_ERROR ("Apple simulators aren't supported on this platform") +#endif + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformAppleSimulator::Initialize () +{ + PlatformDarwin::Initialize (); +} + +void +PlatformAppleSimulator::Terminate () +{ + PlatformDarwin::Terminate (); +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformAppleSimulator::PlatformAppleSimulator () : + PlatformDarwin (true), + m_core_simulator_framework_path() +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformAppleSimulator::~PlatformAppleSimulator() +{ +} + +lldb_private::Error +PlatformAppleSimulator::LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) +{ +#if defined(__APPLE__) + LoadCoreSimulator(); + CoreSimulatorSupport::Device device(GetSimulatorDevice()); + + if (device.GetState() != CoreSimulatorSupport::Device::State::Booted) + { + Error boot_err; + device.Boot(boot_err); + if (boot_err.Fail()) + return boot_err; + } + + auto spawned = device.Spawn(launch_info); + + if (spawned) + { + launch_info.SetProcessID(spawned.GetPID()); + return Error(); + } + else + return spawned.GetError(); +#else + Error err; + err.SetErrorString(UNSUPPORTED_ERROR); + return err; +#endif +} + +void +PlatformAppleSimulator::GetStatus (Stream &strm) +{ +#if defined(__APPLE__) + // This will get called by subclasses, so just output status on the + // current simulator + PlatformAppleSimulator::LoadCoreSimulator(); + + CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices(); + const size_t num_devices = devices.GetNumDevices(); + if (num_devices) + { + strm.Printf("Available devices:\n"); + for (size_t i=0; ioperator bool()) + { + strm.Printf("Current device: %s: %s", m_device->GetUDID().c_str(), m_device->GetName().c_str()); + if (m_device->GetState() == CoreSimulatorSupport::Device::State::Booted) + { + strm.Printf(" state = booted"); + } + strm.Printf("\nType \"platform connect \" where is a device UDID or a device name to disconnect and connect to a different device.\n"); + + } + else + { + strm.Printf("No current device is selected, \"platform connect \" where is a device UDID or a device name to connect to a specific device.\n"); + } + + } + else + { + strm.Printf("No devices are available.\n"); + } +#else + strm.Printf(UNSUPPORTED_ERROR); +#endif +} + +Error +PlatformAppleSimulator::ConnectRemote (Args& args) +{ +#if defined(__APPLE__) + Error error; + if (args.GetArgumentCount() == 1) + { + if (m_device) + DisconnectRemote (); + PlatformAppleSimulator::LoadCoreSimulator(); + const char *arg_cstr = args.GetArgumentAtIndex(0); + if (arg_cstr) + { + std::string arg_str(arg_cstr); + CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices(); + devices.ForEach([this, &arg_str](const CoreSimulatorSupport::Device &device) -> bool { + if (arg_str == device.GetUDID() || arg_str == device.GetName()) + { + m_device = device; + return false; // Stop iterating + } + else + { + return true; // Keep iterating + } + }); + if (!m_device) + error.SetErrorStringWithFormat("no device with UDID or name '%s' was found", arg_cstr); + } + } + else + { + error.SetErrorString("this command take a single UDID argument of the device you want to connect to."); + } + return error; +#else + Error err; + err.SetErrorString(UNSUPPORTED_ERROR); + return err; +#endif +} + +Error +PlatformAppleSimulator::DisconnectRemote () +{ +#if defined(__APPLE__) + m_device.reset(); + return Error(); +#else + Error err; + err.SetErrorString(UNSUPPORTED_ERROR); + return err; +#endif +} + + +lldb::ProcessSP +PlatformAppleSimulator::DebugProcess (ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Error &error) +{ +#if defined(__APPLE__) + ProcessSP process_sp; + // Make sure we stop at the entry point + launch_info.GetFlags ().Set (eLaunchFlagDebug); + // We always launch the process we are going to debug in a separate process + // group, since then we can handle ^C interrupts ourselves w/o having to worry + // about the target getting them as well. + launch_info.SetLaunchInSeparateProcessGroup(true); + + error = LaunchProcess (launch_info); + if (error.Success()) + { + if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + { + ProcessAttachInfo attach_info (launch_info); + process_sp = Attach (attach_info, debugger, target, error); + if (process_sp) + { + launch_info.SetHijackListener(attach_info.GetHijackListener()); + + // Since we attached to the process, it will think it needs to detach + // if the process object just goes away without an explicit call to + // Process::Kill() or Process::Detach(), so let it know to kill the + // process if this happens. + process_sp->SetShouldDetach (false); + + // If we didn't have any file actions, the pseudo terminal might + // have been used where the slave side was given as the file to + // open for stdin/out/err after we have already opened the master + // so we can read/write stdin/out/err. + int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); + if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) + { + process_sp->SetSTDIOFileDescriptor(pty_fd); + } + } + } + } + + return process_sp; +#else + return ProcessSP(); +#endif +} + +FileSpec +PlatformAppleSimulator::GetCoreSimulatorPath() +{ +#if defined(__APPLE__) + Mutex::Locker locker (m_mutex); + if (!m_core_simulator_framework_path.hasValue()) + { + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir) + { + StreamString cs_path; + cs_path.Printf("%s/Library/PrivateFrameworks/CoreSimulator.framework/CoreSimulator", developer_dir); + const bool resolve_path = true; + m_core_simulator_framework_path = FileSpec(cs_path.GetData(), resolve_path); + } + } + + return m_core_simulator_framework_path.getValue(); +#else + return FileSpec(); +#endif +} + +void +PlatformAppleSimulator::LoadCoreSimulator () +{ +#if defined(__APPLE__) + static std::once_flag g_load_core_sim_flag; + std::call_once(g_load_core_sim_flag, [this] { + const std::string core_sim_path(GetCoreSimulatorPath().GetPath()); + if (core_sim_path.size()) + dlopen(core_sim_path.c_str(), RTLD_LAZY); + }); +#endif +} + +#if defined(__APPLE__) +CoreSimulatorSupport::Device +PlatformAppleSimulator::GetSimulatorDevice () +{ + if (!m_device.hasValue()) + { + const CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id = CoreSimulatorSupport::DeviceType::ProductFamilyID::iPhone; + m_device = CoreSimulatorSupport::DeviceSet::GetAvailableDevices().GetFanciest(dev_id); + } + + if (m_device.hasValue()) + return m_device.getValue(); + else + return CoreSimulatorSupport::Device(); +} +#endif + diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h b/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h new file mode 100644 index 000000000000..de8673b2a2af --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h @@ -0,0 +1,81 @@ +//===-- PlatformAppleSimulator.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformAppleSimulator_h_ +#define liblldb_PlatformAppleSimulator_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "PlatformDarwin.h" +#include "PlatformiOSSimulatorCoreSimulatorSupport.h" + +#include "llvm/ADT/Optional.h" + +class PlatformAppleSimulator : public PlatformDarwin +{ +public: + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + PlatformAppleSimulator (); + + virtual + ~PlatformAppleSimulator(); + + lldb_private::Error + LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) override; + + void + GetStatus (lldb_private::Stream &strm) override; + + lldb_private::Error + ConnectRemote (lldb_private::Args& args) override; + + lldb_private::Error + DisconnectRemote () override; + + lldb::ProcessSP + DebugProcess (lldb_private::ProcessLaunchInfo &launch_info, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) override; + +protected: + llvm::Optional m_core_simulator_framework_path; + llvm::Optional m_device; + + lldb_private::FileSpec + GetCoreSimulatorPath(); + + void + LoadCoreSimulator (); + +#if defined(__APPLE__) + CoreSimulatorSupport::Device + GetSimulatorDevice (); +#endif + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAppleSimulator); + +}; + +#endif // liblldb_PlatformAppleSimulator_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp b/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp new file mode 100644 index 000000000000..f537934a9172 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp @@ -0,0 +1,466 @@ +//===-- PlatformAppleTVSimulator.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformAppleTVSimulator.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformAppleTVSimulator::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformAppleTVSimulator::GetPluginNameStatic(), + PlatformAppleTVSimulator::GetDescriptionStatic(), + PlatformAppleTVSimulator::CreateInstance); + } +} + +void +PlatformAppleTVSimulator::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformAppleTVSimulator::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformAppleTVSimulator::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformAppleTVSimulator::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::x86_64: + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::TvOS: + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + if (create) + { + if (log) + log->Printf ("PlatformAppleTVSimulator::%s() creating platform", __FUNCTION__); + + return PlatformSP(new PlatformAppleTVSimulator ()); + } + + if (log) + log->Printf ("PlatformAppleTVSimulator::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + + +lldb_private::ConstString +PlatformAppleTVSimulator::GetPluginNameStatic () +{ + static ConstString g_name("tvos-simulator"); + return g_name; +} + +const char * +PlatformAppleTVSimulator::GetDescriptionStatic() +{ + return "Apple TV simulator platform plug-in."; +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformAppleTVSimulator::PlatformAppleTVSimulator () : + PlatformDarwin (true), + m_sdk_directory () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformAppleTVSimulator::~PlatformAppleTVSimulator() +{ +} + + +void +PlatformAppleTVSimulator::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetSDKDirectoryAsCString(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); +} + + +Error +PlatformAppleTVSimulator::ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(module_spec); + + // If we have "ls" as the exe_file, resolve the executable loation based on + // the current path variables + // TODO: resolve bare executables in the Platform SDK +// if (!resolved_exe_file.Exists()) +// resolved_exe_file.ResolveExecutableLocation (); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + ArchSpec platform_arch; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + // Only match x86 with x86 and x86_64 with x86_64... + if (!module_spec.GetArchitecture().IsValid() || module_spec.GetArchitecture().GetCore() == resolved_module_spec.GetArchitecture().GetCore()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (platform_arch.GetArchitectureName()); + } + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +static FileSpec::EnumerateDirectoryResult +EnumerateDirectoryCallback (void *baton, FileSpec::FileType file_type, const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory) + { + const char *filename = file_spec.GetFilename().GetCString(); + if (filename && strncmp(filename, "AppleTVSimulator", strlen ("AppleTVSimulator")) == 0) + { + ::snprintf ((char *)baton, PATH_MAX, "%s", filename); + return FileSpec::eEnumerateDirectoryResultQuit; + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + + + +const char * +PlatformAppleTVSimulator::GetSDKDirectoryAsCString() +{ + Mutex::Locker locker (m_mutex); + if (m_sdk_directory.empty()) + { + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir) + { + char sdks_directory[PATH_MAX]; + char sdk_dirname[PATH_MAX]; + sdk_dirname[0] = '\0'; + snprintf (sdks_directory, + sizeof(sdks_directory), + "%s/Platforms/AppleTVSimulator.platform/Developer/SDKs", + developer_dir); + FileSpec simulator_sdk_spec; + bool find_directories = true; + bool find_files = false; + bool find_other = false; + FileSpec::EnumerateDirectory (sdks_directory, + find_directories, + find_files, + find_other, + EnumerateDirectoryCallback, + sdk_dirname); + + if (sdk_dirname[0]) + { + m_sdk_directory = sdks_directory; + m_sdk_directory.append (1, '/'); + m_sdk_directory.append (sdk_dirname); + return m_sdk_directory.c_str(); + } + } + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_sdk_directory.assign (1, '\0'); + } + + // We should have put a single NULL character into m_sdk_directory + // or it should have a valid path if the code gets here + assert (m_sdk_directory.empty() == false); + if (m_sdk_directory[0]) + return m_sdk_directory.c_str(); + return NULL; +} + +Error +PlatformAppleTVSimulator::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * sdk_dir = GetSDKDirectoryAsCString(); + if (sdk_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + sdk_dir, + platform_file_path); + + // First try in the SDK and see if the file is in there + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + // Else fall back to the actual path itself + local_file.SetFile(platform_file_path, true); + if (local_file.Exists()) + return error; + + } + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformAppleTVSimulator::GetSharedModule (const ModuleSpec &module_spec, + lldb_private::Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For AppleTV, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + Error error; + ModuleSpec platform_module_spec (module_spec); + const FileSpec &platform_file = module_spec.GetFileSpec(); + error = GetSymbolFile (platform_file, module_spec.GetUUIDPtr(), platform_module_spec.GetFileSpec()); + if (error.Success()) + { + error = ResolveExecutable (platform_module_spec, module_sp, module_search_paths_ptr); + } + else + { + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + } + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + + +uint32_t +PlatformAppleTVSimulator::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + ProcessInstanceInfoList all_osx_process_infos; + // First we get all OSX processes + const uint32_t n = Host::FindProcesses (match_info, all_osx_process_infos); + + // Now we filter them down to only the TvOS triples + for (uint32_t i=0; i &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneSimulator); + } + +protected: + std::string m_sdk_directory; + std::string m_build_update; + + const char * + GetSDKDirectoryAsCString(); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAppleTVSimulator); + +}; + + +#endif // liblldb_PlatformAppleTVSimulator_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp b/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp new file mode 100644 index 000000000000..ea8e789b2920 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp @@ -0,0 +1,466 @@ +//===-- PlatformAppleWatchSimulator.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformAppleWatchSimulator.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformAppleWatchSimulator::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformAppleWatchSimulator::GetPluginNameStatic(), + PlatformAppleWatchSimulator::GetDescriptionStatic(), + PlatformAppleWatchSimulator::CreateInstance); + } +} + +void +PlatformAppleWatchSimulator::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformAppleWatchSimulator::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformAppleWatchSimulator::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformAppleWatchSimulator::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::x86_64: + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::WatchOS: + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + if (create) + { + if (log) + log->Printf ("PlatformAppleWatchSimulator::%s() creating platform", __FUNCTION__); + + return PlatformSP(new PlatformAppleWatchSimulator ()); + } + + if (log) + log->Printf ("PlatformAppleWatchSimulator::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + + +lldb_private::ConstString +PlatformAppleWatchSimulator::GetPluginNameStatic () +{ + static ConstString g_name("watchos-simulator"); + return g_name; +} + +const char * +PlatformAppleWatchSimulator::GetDescriptionStatic() +{ + return "Apple Watch simulator platform plug-in."; +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformAppleWatchSimulator::PlatformAppleWatchSimulator () : + PlatformDarwin (true), + m_sdk_directory () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformAppleWatchSimulator::~PlatformAppleWatchSimulator() +{ +} + + +void +PlatformAppleWatchSimulator::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetSDKDirectoryAsCString(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); +} + + +Error +PlatformAppleWatchSimulator::ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(module_spec); + + // If we have "ls" as the exe_file, resolve the executable loation based on + // the current path variables + // TODO: resolve bare executables in the Platform SDK +// if (!resolved_exe_file.Exists()) +// resolved_exe_file.ResolveExecutableLocation (); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + ArchSpec platform_arch; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + // Only match x86 with x86 and x86_64 with x86_64... + if (!module_spec.GetArchitecture().IsValid() || module_spec.GetArchitecture().GetCore() == resolved_module_spec.GetArchitecture().GetCore()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (platform_arch.GetArchitectureName()); + } + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +static FileSpec::EnumerateDirectoryResult +EnumerateDirectoryCallback (void *baton, FileSpec::FileType file_type, const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory) + { + const char *filename = file_spec.GetFilename().GetCString(); + if (filename && strncmp(filename, "AppleWatchSimulator", strlen ("AppleWatchSimulator")) == 0) + { + ::snprintf ((char *)baton, PATH_MAX, "%s", filename); + return FileSpec::eEnumerateDirectoryResultQuit; + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + + + +const char * +PlatformAppleWatchSimulator::GetSDKDirectoryAsCString() +{ + Mutex::Locker locker (m_mutex); + if (m_sdk_directory.empty()) + { + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir) + { + char sdks_directory[PATH_MAX]; + char sdk_dirname[PATH_MAX]; + sdk_dirname[0] = '\0'; + snprintf (sdks_directory, + sizeof(sdks_directory), + "%s/Platforms/AppleWatchSimulator.platform/Developer/SDKs", + developer_dir); + FileSpec simulator_sdk_spec; + bool find_directories = true; + bool find_files = false; + bool find_other = false; + FileSpec::EnumerateDirectory (sdks_directory, + find_directories, + find_files, + find_other, + EnumerateDirectoryCallback, + sdk_dirname); + + if (sdk_dirname[0]) + { + m_sdk_directory = sdks_directory; + m_sdk_directory.append (1, '/'); + m_sdk_directory.append (sdk_dirname); + return m_sdk_directory.c_str(); + } + } + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_sdk_directory.assign (1, '\0'); + } + + // We should have put a single NULL character into m_sdk_directory + // or it should have a valid path if the code gets here + assert (m_sdk_directory.empty() == false); + if (m_sdk_directory[0]) + return m_sdk_directory.c_str(); + return NULL; +} + +Error +PlatformAppleWatchSimulator::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * sdk_dir = GetSDKDirectoryAsCString(); + if (sdk_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + sdk_dir, + platform_file_path); + + // First try in the SDK and see if the file is in there + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + // Else fall back to the actual path itself + local_file.SetFile(platform_file_path, true); + if (local_file.Exists()) + return error; + + } + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformAppleWatchSimulator::GetSharedModule (const ModuleSpec &module_spec, + lldb_private::Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For AppleWatch, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + Error error; + ModuleSpec platform_module_spec (module_spec); + const FileSpec &platform_file = module_spec.GetFileSpec(); + error = GetSymbolFile (platform_file, module_spec.GetUUIDPtr(), platform_module_spec.GetFileSpec()); + if (error.Success()) + { + error = ResolveExecutable (platform_module_spec, module_sp, module_search_paths_ptr); + } + else + { + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + } + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + + +uint32_t +PlatformAppleWatchSimulator::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + ProcessInstanceInfoList all_osx_process_infos; + // First we get all OSX processes + const uint32_t n = Host::FindProcesses (match_info, all_osx_process_infos); + + // Now we filter them down to only the WatchOS triples + for (uint32_t i=0; i &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneSimulator); + } + +protected: + std::string m_sdk_directory; + std::string m_build_update; + + const char * + GetSDKDirectoryAsCString(); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformAppleWatchSimulator); + +}; + +#endif // liblldb_PlatformAppleWatchSimulator_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp new file mode 100644 index 000000000000..fb38630710a1 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -0,0 +1,1705 @@ +//===-- PlatformDarwin.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformDarwin.h" + +// C Includes +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +#include "clang/Basic/VersionTuple.h" +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Host/XML.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "llvm/ADT/STLExtras.h" + +#if defined (__APPLE__) +#include // for TARGET_OS_TV, TARGET_OS_WATCH +#endif + +using namespace lldb; +using namespace lldb_private; + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformDarwin::PlatformDarwin (bool is_host) : + PlatformPOSIX(is_host), // This is the local host platform + m_developer_directory () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformDarwin::~PlatformDarwin() +{ +} + +FileSpecList +PlatformDarwin::LocateExecutableScriptingResources (Target *target, + Module &module, + Stream* feedback_stream) +{ + FileSpecList file_list; + if (target && target->GetDebugger().GetScriptLanguage() == eScriptLanguagePython) + { + // NB some extensions might be meaningful and should not be stripped - "this.binary.file" + // should not lose ".file" but GetFileNameStrippingExtension() will do precisely that. + // Ideally, we should have a per-platform list of extensions (".exe", ".app", ".dSYM", ".framework") + // which should be stripped while leaving "this.binary.file" as-is. + ScriptInterpreter *script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + + FileSpec module_spec = module.GetFileSpec(); + + if (module_spec) + { + SymbolVendor *symbols = module.GetSymbolVendor (); + if (symbols) + { + SymbolFile *symfile = symbols->GetSymbolFile(); + if (symfile) + { + ObjectFile *objfile = symfile->GetObjectFile(); + if (objfile) + { + FileSpec symfile_spec (objfile->GetFileSpec()); + if (symfile_spec && symfile_spec.Exists()) + { + while (module_spec.GetFilename()) + { + std::string module_basename (module_spec.GetFilename().GetCString()); + std::string original_module_basename (module_basename); + + bool was_keyword = false; + + // FIXME: for Python, we cannot allow certain characters in module + // filenames we import. Theoretically, different scripting languages may + // have different sets of forbidden tokens in filenames, and that should + // be dealt with by each ScriptInterpreter. For now, we just replace dots + // with underscores, but if we ever support anything other than Python + // we will need to rework this + std::replace(module_basename.begin(), module_basename.end(), '.', '_'); + std::replace(module_basename.begin(), module_basename.end(), ' ', '_'); + std::replace(module_basename.begin(), module_basename.end(), '-', '_'); + if (script_interpreter && script_interpreter->IsReservedWord(module_basename.c_str())) + { + module_basename.insert(module_basename.begin(), '_'); + was_keyword = true; + } + + StreamString path_string; + StreamString original_path_string; + // for OSX we are going to be in .dSYM/Contents/Resources/DWARF/ + // let us go to .dSYM/Contents/Resources/Python/.py and see if the file exists + path_string.Printf("%s/../Python/%s.py",symfile_spec.GetDirectory().GetCString(), module_basename.c_str()); + original_path_string.Printf("%s/../Python/%s.py",symfile_spec.GetDirectory().GetCString(), original_module_basename.c_str()); + FileSpec script_fspec(path_string.GetData(), true); + FileSpec orig_script_fspec(original_path_string.GetData(), true); + + // if we did some replacements of reserved characters, and a file with the untampered name + // exists, then warn the user that the file as-is shall not be loaded + if (feedback_stream) + { + if (module_basename != original_module_basename + && orig_script_fspec.Exists()) + { + const char* reason_for_complaint = was_keyword ? "conflicts with a keyword" : "contains reserved characters"; + if (script_fspec.Exists()) + feedback_stream->Printf("warning: the symbol file '%s' contains a debug script. However, its name" + " '%s' %s and as such cannot be loaded. LLDB will" + " load '%s' instead. Consider removing the file with the malformed name to" + " eliminate this warning.\n", + symfile_spec.GetPath().c_str(), + original_path_string.GetData(), + reason_for_complaint, + path_string.GetData()); + else + feedback_stream->Printf("warning: the symbol file '%s' contains a debug script. However, its name" + " %s and as such cannot be loaded. If you intend" + " to have this script loaded, please rename '%s' to '%s' and retry.\n", + symfile_spec.GetPath().c_str(), + reason_for_complaint, + original_path_string.GetData(), + path_string.GetData()); + } + } + + if (script_fspec.Exists()) + { + file_list.Append (script_fspec); + break; + } + + // If we didn't find the python file, then keep + // stripping the extensions and try again + ConstString filename_no_extension (module_spec.GetFileNameStrippingExtension()); + if (module_spec.GetFilename() == filename_no_extension) + break; + + module_spec.GetFilename() = filename_no_extension; + } + } + } + } + } + } + } + return file_list; +} + +Error +PlatformDarwin::ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + char exe_path[PATH_MAX]; + ModuleSpec resolved_module_spec(module_spec); + + if (IsHost()) + { + // If we have "ls" as the exe_file, resolve the executable loation based on + // the current path variables + if (!resolved_module_spec.GetFileSpec().Exists()) + { + module_spec.GetFileSpec().GetPath (exe_path, sizeof(exe_path)); + resolved_module_spec.GetFileSpec().SetFile(exe_path, true); + } + + if (!resolved_module_spec.GetFileSpec().Exists()) + resolved_module_spec.GetFileSpec().ResolveExecutableLocation (); + + // Resolve any executable within a bundle on MacOSX + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + { + const uint32_t permissions = resolved_module_spec.GetFileSpec().GetPermissions(); + if (permissions && (permissions & eFilePermissionsEveryoneR) == 0) + error.SetErrorStringWithFormat ("executable '%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + else + error.SetErrorStringWithFormat ("unable to find executable for '%s'", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + else + { + if (m_remote_platform_sp) + { + error = GetCachedExecutable (resolved_module_spec, exe_module_sp, module_search_paths_ptr, *m_remote_platform_sp); + } + else + { + // We may connect to a process and use the provided executable (Don't use local $PATH). + + // Resolve any executable within a bundle on MacOSX + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", resolved_module_spec.GetFileSpec().GetFilename().AsCString("")); + } + } + + + if (error.Success()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + module_search_paths_ptr, + NULL, + NULL); + + if (error.Fail() || exe_module_sp.get() == NULL || exe_module_sp->GetObjectFile() == NULL) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = GetSharedModule (resolved_module_spec, + NULL, + exe_module_sp, + module_search_paths_ptr, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + } + + return error; +} + +Error +PlatformDarwin::ResolveSymbolFile (Target &target, + const ModuleSpec &sym_spec, + FileSpec &sym_file) +{ + Error error; + sym_file = sym_spec.GetSymbolFileSpec(); + if (sym_file.Exists()) + { + if (sym_file.GetFileType() == FileSpec::eFileTypeDirectory) + { + sym_file = Symbols::FindSymbolFileInBundle (sym_file, + sym_spec.GetUUIDPtr(), + sym_spec.GetArchitecturePtr()); + } + } + else + { + if (sym_spec.GetUUID().IsValid()) + { + + } + } + return error; + +} + +static lldb_private::Error +MakeCacheFolderForFile (const FileSpec& module_cache_spec) +{ + FileSpec module_cache_folder = module_cache_spec.CopyByRemovingLastPathComponent(); + return FileSystem::MakeDirectory(module_cache_folder, eFilePermissionsDirectoryDefault); +} + +static lldb_private::Error +BringInRemoteFile (Platform* platform, + const lldb_private::ModuleSpec &module_spec, + const FileSpec& module_cache_spec) +{ + MakeCacheFolderForFile(module_cache_spec); + Error err = platform->GetFile(module_spec.GetFileSpec(), module_cache_spec); + return err; +} + +lldb_private::Error +PlatformDarwin::GetSharedModuleWithLocalCache (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] Trying to find module %s/%s - platform path %s/%s symbol path %s/%s", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString(), + module_spec.GetPlatformFileSpec().GetDirectory().AsCString(), + module_spec.GetPlatformFileSpec().GetFilename().AsCString(), + module_spec.GetSymbolFileSpec().GetDirectory().AsCString(), + module_spec.GetSymbolFileSpec().GetFilename().AsCString()); + + Error err; + + err = ModuleList::GetSharedModule(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + if (module_sp) + return err; + + if (!IsHost()) + { + std::string cache_path(GetLocalCacheDirectory()); + // Only search for a locally cached file if we have a valid cache path + if (!cache_path.empty()) + { + std::string module_path (module_spec.GetFileSpec().GetPath()); + cache_path.append(module_path); + FileSpec module_cache_spec(cache_path.c_str(),false); + + // if rsync is supported, always bring in the file - rsync will be very efficient + // when files are the same on the local and remote end of the connection + if (this->GetSupportsRSync()) + { + err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s was rsynced and is now there", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return Error(); + } + } + + // try to find the module in the cache + if (module_cache_spec.Exists()) + { + // get the local and remote MD5 and compare + if (m_remote_platform_sp) + { + // when going over the *slow* GDB remote transfer mechanism we first check + // the hashes of the files - and only do the actual transfer if they differ + uint64_t high_local,high_remote,low_local,low_remote; + FileSystem::CalculateMD5(module_cache_spec, low_local, high_local); + m_remote_platform_sp->CalculateMD5(module_spec.GetFileSpec(), low_remote, high_remote); + if (low_local != low_remote || high_local != high_remote) + { + // bring in the remote file + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s needs to be replaced from remote copy", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + Error err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + } + } + + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s was found in the cache", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + return Error(); + } + + // bring in the remote module file + if (log) + log->Printf("[%s] module %s/%s needs to come in remotely", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + Error err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s is now cached and fine", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return Error(); + } + else + return Error("unable to obtain valid module file"); + } + else + return Error("no cache path"); + } + else + return Error ("unable to resolve module"); +} + +Error +PlatformDarwin::GetSharedModule (const ModuleSpec &module_spec, + Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error; + module_sp.reset(); + + if (IsRemote()) + { + // If we have a remote platform always, let it try and locate + // the shared module first. + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->GetSharedModule (module_spec, + process, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + } + } + + if (!module_sp) + { + // Fall back to the local platform and find the file locally + error = Platform::GetSharedModule (module_spec, + process, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + + const FileSpec &platform_file = module_spec.GetFileSpec(); + if (!module_sp && module_search_paths_ptr && platform_file) + { + // We can try to pull off part of the file path up to the bundle + // directory level and try any module search paths... + FileSpec bundle_directory; + if (Host::GetBundleDirectory (platform_file, bundle_directory)) + { + if (platform_file == bundle_directory) + { + ModuleSpec new_module_spec (module_spec); + new_module_spec.GetFileSpec() = bundle_directory; + if (Host::ResolveExecutableInBundle (new_module_spec.GetFileSpec())) + { + Error new_error (Platform::GetSharedModule (new_module_spec, + process, + module_sp, + NULL, + old_module_sp_ptr, + did_create_ptr)); + + if (module_sp) + return new_error; + } + } + else + { + char platform_path[PATH_MAX]; + char bundle_dir[PATH_MAX]; + platform_file.GetPath (platform_path, sizeof(platform_path)); + const size_t bundle_directory_len = bundle_directory.GetPath (bundle_dir, sizeof(bundle_dir)); + char new_path[PATH_MAX]; + size_t num_module_search_paths = module_search_paths_ptr->GetSize(); + for (size_t i=0; iGetFileSpecAtIndex(i).GetPath(new_path, sizeof(new_path)); + if (search_path_len < sizeof(new_path)) + { + snprintf (new_path + search_path_len, sizeof(new_path) - search_path_len, "/%s", platform_path + bundle_directory_len); + FileSpec new_file_spec (new_path, false); + if (new_file_spec.Exists()) + { + ModuleSpec new_module_spec (module_spec); + new_module_spec.GetFileSpec() = new_file_spec; + Error new_error (Platform::GetSharedModule (new_module_spec, + process, + module_sp, + NULL, + old_module_sp_ptr, + did_create_ptr)); + + if (module_sp) + { + module_sp->SetPlatformFileSpec(new_file_spec); + return new_error; + } + } + } + } + } + } + } + } + if (module_sp) + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return error; +} + +size_t +PlatformDarwin::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site) +{ + const uint8_t *trap_opcode = NULL; + uint32_t trap_opcode_size = 0; + bool bp_is_thumb = false; + + llvm::Triple::ArchType machine = target.GetArchitecture().GetMachine(); + switch (machine) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + { + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + } + break; + + case llvm::Triple::aarch64: + { + // TODO: fix this with actual darwin breakpoint opcode for arm64. + // right now debugging uses the Z packets with GDB remote so this + // is not needed, but the size needs to be correct... + static const uint8_t g_arm64_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + trap_opcode = g_arm64_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm64_breakpoint_opcode); + } + break; + + case llvm::Triple::thumb: + bp_is_thumb = true; // Fall through... + case llvm::Triple::arm: + { + static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + + // Auto detect arm/thumb if it wasn't explicitly specified + if (!bp_is_thumb) + { + lldb::BreakpointLocationSP bp_loc_sp (bp_site->GetOwnerAtIndex (0)); + if (bp_loc_sp) + bp_is_thumb = bp_loc_sp->GetAddress().GetAddressClass () == eAddressClassCodeAlternateISA; + } + if (bp_is_thumb) + { + trap_opcode = g_thumb_breakpooint_opcode; + trap_opcode_size = sizeof(g_thumb_breakpooint_opcode); + break; + } + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + } + break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + { + static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + trap_opcode = g_ppc_breakpoint_opcode; + trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); + } + break; + + default: + assert(!"Unhandled architecture in PlatformDarwin::GetSoftwareBreakpointTrapOpcode()"); + break; + } + + if (trap_opcode && trap_opcode_size) + { + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + } + return 0; + +} + +bool +PlatformDarwin::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = Platform::GetProcessInfo (pid, process_info); + } + else + { + if (m_remote_platform_sp) + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + +uint32_t +PlatformDarwin::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + uint32_t match_count = 0; + if (IsHost()) + { + // Let the base class figure out the host details + match_count = Platform::FindProcesses (match_info, process_infos); + } + else + { + // If we are remote, we can only return results if we are connected + if (m_remote_platform_sp) + match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos); + } + return match_count; +} + +bool +PlatformDarwin::ModuleIsExcludedForUnconstrainedSearches (lldb_private::Target &target, const lldb::ModuleSP &module_sp) +{ + if (!module_sp) + return false; + + ObjectFile *obj_file = module_sp->GetObjectFile(); + if (!obj_file) + return false; + + ObjectFile::Type obj_type = obj_file->GetType(); + if (obj_type == ObjectFile::eTypeDynamicLinker) + return true; + else + return false; +} + +bool +PlatformDarwin::x86GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + ArchSpec host_arch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + if (host_arch.GetCore() == ArchSpec::eCore_x86_64_x86_64h) + { + switch (idx) + { + case 0: + arch = host_arch; + return true; + + case 1: + arch.SetTriple("x86_64-apple-macosx"); + return true; + + case 2: + arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); + return true; + + default: return false; + } + } + else + { + if (idx == 0) + { + arch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + return arch.IsValid(); + } + else if (idx == 1) + { + ArchSpec platform_arch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault)); + ArchSpec platform_arch64(HostInfo::GetArchitecture(HostInfo::eArchKind64)); + if (platform_arch.IsExactMatch(platform_arch64)) + { + // This macosx platform supports both 32 and 64 bit. Since we already + // returned the 64 bit arch for idx == 0, return the 32 bit arch + // for idx == 1 + arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); + return arch.IsValid(); + } + } + } + return false; +} + +// The architecture selection rules for arm processors +// These cpu subtypes have distinct names (e.g. armv7f) but armv7 binaries run fine on an armv7f processor. + +bool +PlatformDarwin::ARMGetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + ArchSpec system_arch (GetSystemArchitecture()); + + // When lldb is running on a watch or tv, set the arch OS name appropriately. +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 +#define OSNAME "tvos" +#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 +#define OSNAME "watchos" +#else +#define OSNAME "ios" +#endif + + const ArchSpec::Core system_core = system_arch.GetCore(); + switch (system_core) + { + default: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv7f-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv7k-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv7s-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv7m-apple-" OSNAME); return true; + case 6: arch.SetTriple ("armv7em-apple-" OSNAME); return true; + case 7: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 8: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 9: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 10: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 11: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumbv7f-apple-" OSNAME); return true; + case 14: arch.SetTriple ("thumbv7k-apple-" OSNAME); return true; + case 15: arch.SetTriple ("thumbv7s-apple-" OSNAME); return true; + case 16: arch.SetTriple ("thumbv7m-apple-" OSNAME); return true; + case 17: arch.SetTriple ("thumbv7em-apple-" OSNAME); return true; + case 18: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 19: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 20: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 21: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 22: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_arm64: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7s-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv7f-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv7m-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv7em-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 6: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 7: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 8: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 9: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 10: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv7f-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumbv7k-apple-" OSNAME); return true; + case 14: arch.SetTriple ("thumbv7s-apple-" OSNAME); return true; + case 15: arch.SetTriple ("thumbv7m-apple-" OSNAME); return true; + case 16: arch.SetTriple ("thumbv7em-apple-" OSNAME); return true; + case 17: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 18: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 19: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 20: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 21: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7f: + switch (idx) + { + case 0: arch.SetTriple ("armv7f-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7f-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7k: + switch (idx) + { + case 0: arch.SetTriple ("armv7k-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7k-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7s: + switch (idx) + { + case 0: arch.SetTriple ("armv7s-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7s-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7m: + switch (idx) + { + case 0: arch.SetTriple ("armv7m-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7m-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7em: + switch (idx) + { + case 0: arch.SetTriple ("armv7em-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 5: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 6: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv7em-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 12: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 13: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7: + switch (idx) + { + case 0: arch.SetTriple ("armv7-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 4: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 5: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 6: arch.SetTriple ("thumbv7-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 10: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 11: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv6m: + switch (idx) + { + case 0: arch.SetTriple ("armv6m-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 3: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 4: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 5: arch.SetTriple ("thumbv6m-apple-" OSNAME); return true; + case 6: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 8: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 9: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv6: + switch (idx) + { + case 0: arch.SetTriple ("armv6-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 2: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 3: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 4: arch.SetTriple ("thumbv6-apple-" OSNAME); return true; + case 5: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 6: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 7: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv5: + switch (idx) + { + case 0: arch.SetTriple ("armv5-apple-" OSNAME); return true; + case 1: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 2: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 3: arch.SetTriple ("thumbv5-apple-" OSNAME); return true; + case 4: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 5: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv4: + switch (idx) + { + case 0: arch.SetTriple ("armv4-apple-" OSNAME); return true; + case 1: arch.SetTriple ("arm-apple-" OSNAME); return true; + case 2: arch.SetTriple ("thumbv4t-apple-" OSNAME); return true; + case 3: arch.SetTriple ("thumb-apple-" OSNAME); return true; + default: break; + } + break; + } + arch.Clear(); + return false; +} + + +const char * +PlatformDarwin::GetDeveloperDirectory() +{ + Mutex::Locker locker (m_mutex); + if (m_developer_directory.empty()) + { + bool developer_dir_path_valid = false; + char developer_dir_path[PATH_MAX]; + FileSpec temp_file_spec; + if (HostInfo::GetLLDBPath(ePathTypeLLDBShlibDir, temp_file_spec)) + { + if (temp_file_spec.GetPath (developer_dir_path, sizeof(developer_dir_path))) + { + char *shared_frameworks = strstr (developer_dir_path, "/SharedFrameworks/LLDB.framework"); + if (shared_frameworks) + { + ::snprintf (shared_frameworks, + sizeof(developer_dir_path) - (shared_frameworks - developer_dir_path), + "/Developer"); + developer_dir_path_valid = true; + } + else + { + char *lib_priv_frameworks = strstr (developer_dir_path, "/Library/PrivateFrameworks/LLDB.framework"); + if (lib_priv_frameworks) + { + *lib_priv_frameworks = '\0'; + developer_dir_path_valid = true; + } + } + } + } + + if (!developer_dir_path_valid) + { + std::string xcode_dir_path; + const char *xcode_select_prefix_dir = getenv ("XCODE_SELECT_PREFIX_DIR"); + if (xcode_select_prefix_dir) + xcode_dir_path.append (xcode_select_prefix_dir); + xcode_dir_path.append ("/usr/share/xcode-select/xcode_dir_path"); + temp_file_spec.SetFile(xcode_dir_path.c_str(), false); + size_t bytes_read = temp_file_spec.ReadFileContents(0, developer_dir_path, sizeof(developer_dir_path), NULL); + if (bytes_read > 0) + { + developer_dir_path[bytes_read] = '\0'; + while (developer_dir_path[bytes_read-1] == '\r' || + developer_dir_path[bytes_read-1] == '\n') + developer_dir_path[--bytes_read] = '\0'; + developer_dir_path_valid = true; + } + } + + if (!developer_dir_path_valid) + { + FileSpec xcode_select_cmd ("/usr/bin/xcode-select", false); + if (xcode_select_cmd.Exists()) + { + int exit_status = -1; + int signo = -1; + std::string command_output; + Error error = Host::RunShellCommand ("/usr/bin/xcode-select --print-path", + NULL, // current working directory + &exit_status, + &signo, + &command_output, + 2, // short timeout + false); // don't run in a shell + if (error.Success() && exit_status == 0 && !command_output.empty()) + { + const char *cmd_output_ptr = command_output.c_str(); + developer_dir_path[sizeof (developer_dir_path) - 1] = '\0'; + size_t i; + for (i = 0; i < sizeof (developer_dir_path) - 1; i++) + { + if (cmd_output_ptr[i] == '\r' || cmd_output_ptr[i] == '\n' || cmd_output_ptr[i] == '\0') + break; + developer_dir_path[i] = cmd_output_ptr[i]; + } + developer_dir_path[i] = '\0'; + + FileSpec devel_dir (developer_dir_path, false); + if (devel_dir.Exists() && devel_dir.IsDirectory()) + { + developer_dir_path_valid = true; + } + } + } + } + + if (developer_dir_path_valid) + { + temp_file_spec.SetFile (developer_dir_path, false); + if (temp_file_spec.Exists()) + { + m_developer_directory.assign (developer_dir_path); + return m_developer_directory.c_str(); + } + } + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_developer_directory.assign (1, '\0'); + } + + // We should have put a single NULL character into m_developer_directory + // or it should have a valid path if the code gets here + assert (m_developer_directory.empty() == false); + if (m_developer_directory[0]) + return m_developer_directory.c_str(); + return NULL; +} + + +BreakpointSP +PlatformDarwin::SetThreadCreationBreakpoint (Target &target) +{ + BreakpointSP bp_sp; + static const char *g_bp_names[] = + { + "start_wqthread", + "_pthread_wqthread", + "_pthread_start", + }; + + static const char *g_bp_modules[] = + { + "libsystem_c.dylib", + "libSystem.B.dylib" + }; + + FileSpecList bp_modules; + for (size_t i = 0; i < llvm::array_lengthof(g_bp_modules); i++) + { + const char *bp_module = g_bp_modules[i]; + bp_modules.Append(FileSpec(bp_module, false)); + } + + bool internal = true; + bool hardware = false; + LazyBool skip_prologue = eLazyBoolNo; + bp_sp = target.CreateBreakpoint (&bp_modules, + NULL, + g_bp_names, + llvm::array_lengthof(g_bp_names), + eFunctionNameTypeFull, + eLanguageTypeUnknown, + skip_prologue, + internal, + hardware); + bp_sp->SetBreakpointKind("thread-creation"); + + return bp_sp; +} + + +int32_t +PlatformDarwin::GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) +{ + const FileSpec &shell = launch_info.GetShell(); + if (!shell) + return 1; + + std::string shell_string = shell.GetPath(); + const char *shell_name = strrchr (shell_string.c_str(), '/'); + if (shell_name == NULL) + shell_name = shell_string.c_str(); + else + shell_name++; + + if (strcmp (shell_name, "sh") == 0) + { + // /bin/sh re-exec's itself as /bin/bash requiring another resume. + // But it only does this if the COMMAND_MODE environment variable + // is set to "legacy". + const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector(); + if (envp != NULL) + { + for (int i = 0; envp[i] != NULL; i++) + { + if (strcmp (envp[i], "COMMAND_MODE=legacy" ) == 0) + return 2; + } + } + return 1; + } + else if (strcmp (shell_name, "csh") == 0 + || strcmp (shell_name, "tcsh") == 0 + || strcmp (shell_name, "zsh") == 0) + { + // csh and tcsh always seem to re-exec themselves. + return 2; + } + else + return 1; +} + +void +PlatformDarwin::CalculateTrapHandlerSymbolNames () +{ + m_trap_handlers.push_back (ConstString ("_sigtramp")); +} + + +static const char *const sdk_strings[] = { + "MacOSX", + "iPhoneSimulator", + "iPhoneOS", +}; + +static FileSpec +CheckPathForXcode(const FileSpec &fspec) +{ + if (fspec.Exists()) + { + const char substr[] = ".app/Contents/"; + + std::string path_to_shlib = fspec.GetPath(); + size_t pos = path_to_shlib.rfind(substr); + if (pos != std::string::npos) + { + path_to_shlib.erase(pos + strlen(substr)); + FileSpec ret (path_to_shlib.c_str(), false); + + FileSpec xcode_binary_path = ret; + xcode_binary_path.AppendPathComponent("MacOS"); + xcode_binary_path.AppendPathComponent("Xcode"); + + if (xcode_binary_path.Exists()) + { + return ret; + } + } + } + return FileSpec(); +} + +static FileSpec +GetXcodeContentsPath () +{ + static FileSpec g_xcode_filespec; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + + + FileSpec fspec; + + // First get the program file spec. If lldb.so or LLDB.framework is running + // in a program and that program is Xcode, the path returned with be the path + // to Xcode.app/Contents/MacOS/Xcode, so this will be the correct Xcode to use. + fspec = HostInfo::GetProgramFileSpec(); + + if (fspec) + { + // Ignore the current binary if it is python. + std::string basename_lower = fspec.GetFilename ().GetCString (); + std::transform(basename_lower.begin (), basename_lower.end (), basename_lower.begin (), tolower); + if (basename_lower != "python") + { + g_xcode_filespec = CheckPathForXcode(fspec); + } + } + + // Next check DEVELOPER_DIR environment variable + if (!g_xcode_filespec) + { + const char *developer_dir_env_var = getenv("DEVELOPER_DIR"); + if (developer_dir_env_var && developer_dir_env_var[0]) + { + g_xcode_filespec = CheckPathForXcode(FileSpec(developer_dir_env_var, true)); + } + + // Fall back to using "xcrun" to find the selected Xcode + if (!g_xcode_filespec) + { + int status = 0; + int signo = 0; + std::string output; + const char *command = "/usr/bin/xcode-select -p"; + lldb_private::Error error = Host::RunShellCommand (command, // shell command to run + NULL, // current working directory + &status, // Put the exit status of the process in here + &signo, // Put the signal that caused the process to exit in here + &output, // Get the output from the command and place it in this string + 3); // Timeout in seconds to wait for shell program to finish + if (status == 0 && !output.empty()) + { + size_t first_non_newline = output.find_last_not_of("\r\n"); + if (first_non_newline != std::string::npos) + { + output.erase(first_non_newline+1); + } + output.append("/.."); + + g_xcode_filespec = CheckPathForXcode(FileSpec(output.c_str(), false)); + } + } + } + }); + + return g_xcode_filespec; +} + +bool +PlatformDarwin::SDKSupportsModules (SDKType sdk_type, uint32_t major, uint32_t minor, uint32_t micro) +{ + switch (sdk_type) + { + case SDKType::MacOSX: + if (major > 10 || (major == 10 && minor >= 10)) + return true; + break; + case SDKType::iPhoneOS: + case SDKType::iPhoneSimulator: + if (major >= 8) + return true; + break; + } + + return false; +} + +bool +PlatformDarwin::SDKSupportsModules (SDKType desired_type, const FileSpec &sdk_path) +{ + ConstString last_path_component = sdk_path.GetLastPathComponent(); + + if (last_path_component) + { + const llvm::StringRef sdk_name = last_path_component.GetStringRef(); + + llvm::StringRef version_part; + + if (sdk_name.startswith(sdk_strings[(int)desired_type])) + { + version_part = sdk_name.drop_front(strlen(sdk_strings[(int)desired_type])); + } + else + { + return false; + } + + const size_t major_dot_offset = version_part.find('.'); + if (major_dot_offset == llvm::StringRef::npos) + return false; + + const llvm::StringRef major_version = version_part.slice(0, major_dot_offset); + const llvm::StringRef minor_part = version_part.drop_front(major_dot_offset + 1); + + const size_t minor_dot_offset = minor_part.find('.'); + if (minor_dot_offset == llvm::StringRef::npos) + return false; + + const llvm::StringRef minor_version = minor_part.slice(0, minor_dot_offset); + + unsigned int major = 0; + unsigned int minor = 0; + unsigned int micro = 0; + + if (major_version.getAsInteger(10, major)) + return false; + + if (minor_version.getAsInteger(10, minor)) + return false; + + return SDKSupportsModules(desired_type, major, minor, micro); + } + + return false; +} + +FileSpec::EnumerateDirectoryResult +PlatformDarwin::DirectoryEnumerator(void *baton, + FileSpec::FileType file_type, + const FileSpec &spec) +{ + SDKEnumeratorInfo *enumerator_info = static_cast(baton); + + if (SDKSupportsModules(enumerator_info->sdk_type, spec)) + { + enumerator_info->found_path = spec; + return FileSpec::EnumerateDirectoryResult::eEnumerateDirectoryResultNext; + } + + return FileSpec::EnumerateDirectoryResult::eEnumerateDirectoryResultNext; +} + +FileSpec +PlatformDarwin::FindSDKInXcodeForModules (SDKType sdk_type, + const FileSpec &sdks_spec) +{ + // Look inside Xcode for the required installed iOS SDK version + + if (!sdks_spec.IsDirectory()) + return FileSpec(); + + const bool find_directories = true; + const bool find_files = false; + const bool find_other = true; // include symlinks + + SDKEnumeratorInfo enumerator_info; + + enumerator_info.sdk_type = sdk_type; + + FileSpec::EnumerateDirectory(sdks_spec.GetPath().c_str(), + find_directories, + find_files, + find_other, + DirectoryEnumerator, + &enumerator_info); + + if (enumerator_info.found_path.IsDirectory()) + return enumerator_info.found_path; + else + return FileSpec(); +} + +FileSpec +PlatformDarwin::GetSDKDirectoryForModules (SDKType sdk_type) +{ + switch (sdk_type) + { + case SDKType::MacOSX: + case SDKType::iPhoneSimulator: + case SDKType::iPhoneOS: + break; + } + + FileSpec sdks_spec = GetXcodeContentsPath(); + sdks_spec.AppendPathComponent("Developer"); + sdks_spec.AppendPathComponent("Platforms"); + + switch (sdk_type) + { + case SDKType::MacOSX: + sdks_spec.AppendPathComponent("MacOSX.platform"); + break; + case SDKType::iPhoneSimulator: + sdks_spec.AppendPathComponent("iPhoneSimulator.platform"); + break; + case SDKType::iPhoneOS: + sdks_spec.AppendPathComponent("iPhoneOS.platform"); + break; + } + + sdks_spec.AppendPathComponent("Developer"); + sdks_spec.AppendPathComponent("SDKs"); + + if (sdk_type == SDKType::MacOSX) + { + uint32_t major = 0; + uint32_t minor = 0; + uint32_t micro = 0; + + if (HostInfo::GetOSVersion(major, minor, micro)) + { + if (SDKSupportsModules(SDKType::MacOSX, major, minor, micro)) + { + // We slightly prefer the exact SDK for this machine. See if it is there. + + FileSpec native_sdk_spec = sdks_spec; + StreamString native_sdk_name; + native_sdk_name.Printf("MacOSX%u.%u.sdk", major, minor); + native_sdk_spec.AppendPathComponent(native_sdk_name.GetString().c_str()); + + if (native_sdk_spec.Exists()) + { + return native_sdk_spec; + } + } + } + } + + return FindSDKInXcodeForModules(sdk_type, sdks_spec); +} + +void +PlatformDarwin::AddClangModuleCompilationOptionsForSDKType (Target *target, std::vector &options, SDKType sdk_type) +{ + const std::vector apple_arguments = + { + "-x", "objective-c++", + "-fobjc-arc", + "-fblocks", + "-D_ISO646_H", + "-D__ISO646_H" + }; + + options.insert(options.end(), + apple_arguments.begin(), + apple_arguments.end()); + + StreamString minimum_version_option; + uint32_t versions[3] = { 0, 0, 0 }; + bool use_current_os_version = false; + switch (sdk_type) + { + case SDKType::iPhoneOS: +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + use_current_os_version = true; +#else + use_current_os_version = false; +#endif + break; + + case SDKType::iPhoneSimulator: + use_current_os_version = false; + break; + + case SDKType::MacOSX: +#if defined (__i386__) || defined (__x86_64__) + use_current_os_version = true; +#else + use_current_os_version = false; +#endif + break; + } + + bool versions_valid = false; + if (use_current_os_version) + versions_valid = GetOSVersion(versions[0], versions[1], versions[2]); + else if (target) + { + // Our OS doesn't match our executable so we need to get the min OS version from the object file + ModuleSP exe_module_sp = target->GetExecutableModule(); + if (exe_module_sp) + { + ObjectFile *object_file = exe_module_sp->GetObjectFile(); + if (object_file) + versions_valid = object_file->GetMinimumOSVersion(versions, 3) > 0; + } + } + // Only add the version-min options if we got a version from somewhere + if (versions_valid && versions[0] != UINT32_MAX) + { + // Make any invalid versions be zero if needed + if (versions[1] == UINT32_MAX) + versions[1] = 0; + if (versions[2] == UINT32_MAX) + versions[2] = 0; + + switch (sdk_type) + { + case SDKType::iPhoneOS: + minimum_version_option.PutCString("-mios-version-min="); + minimum_version_option.PutCString(clang::VersionTuple(versions[0], versions[1], versions[2]).getAsString().c_str()); + break; + case SDKType::iPhoneSimulator: + minimum_version_option.PutCString("-mios-simulator-version-min="); + minimum_version_option.PutCString(clang::VersionTuple(versions[0], versions[1], versions[2]).getAsString().c_str()); + break; + case SDKType::MacOSX: + minimum_version_option.PutCString("-mmacosx-version-min="); + minimum_version_option.PutCString(clang::VersionTuple(versions[0], versions[1], versions[2]).getAsString().c_str()); + } + options.push_back(minimum_version_option.GetString()); + } + + FileSpec sysroot_spec; + // Scope for mutex locker below + { + Mutex::Locker locker (m_mutex); + sysroot_spec = GetSDKDirectoryForModules(sdk_type); + } + + if (sysroot_spec.IsDirectory()) + { + options.push_back("-isysroot"); + options.push_back(sysroot_spec.GetPath()); + } +} + +ConstString +PlatformDarwin::GetFullNameForDylib (ConstString basename) +{ + if (basename.IsEmpty()) + return basename; + + StreamString stream; + stream.Printf("lib%s.dylib", basename.GetCString()); + return ConstString(stream.GetData()); +} + +bool +PlatformDarwin::GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update, + Process *process) +{ + if (process && strstr(GetPluginName().GetCString(), "-simulator")) + { + lldb_private::ProcessInstanceInfo proc_info; + if (Host::GetProcessInfo(process->GetID(), proc_info)) + { + Args &env = proc_info.GetEnvironmentEntries(); + const size_t n = env.GetArgumentCount(); + const llvm::StringRef k_runtime_version("SIMULATOR_RUNTIME_VERSION="); + const llvm::StringRef k_dyld_root_path("DYLD_ROOT_PATH="); + std::string dyld_root_path; + + for (size_t i=0; i g_executable_dirs; + + // Find the global list of directories that we will search for + // executables once so we don't keep doing the work over and over. + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + + // When locating executables, trust the DEVELOPER_DIR first if it is set + FileSpec xcode_contents_dir = GetXcodeContentsPath(); + if (xcode_contents_dir) + { + FileSpec xcode_lldb_resources = xcode_contents_dir; + xcode_lldb_resources.AppendPathComponent("SharedFrameworks"); + xcode_lldb_resources.AppendPathComponent("LLDB.framework"); + xcode_lldb_resources.AppendPathComponent("Resources"); + if (xcode_lldb_resources.Exists()) + { + FileSpec dir; + dir.GetDirectory().SetCString(xcode_lldb_resources.GetPath().c_str()); + g_executable_dirs.push_back(dir); + } + } + }); + + // Now search the global list of executable directories for the executable we + // are looking for + for (const auto &executable_dir : g_executable_dirs) + { + FileSpec executable_file; + executable_file.GetDirectory() = executable_dir.GetDirectory(); + executable_file.GetFilename().SetCString(basename); + if (executable_file.Exists()) + return executable_file; + } + + return FileSpec(); +} diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/source/Plugins/Platform/MacOSX/PlatformDarwin.h new file mode 100644 index 000000000000..b280b35da655 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -0,0 +1,156 @@ +//===-- PlatformDarwin.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformDarwin_h_ +#define liblldb_PlatformDarwin_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" + +class PlatformDarwin : public PlatformPOSIX +{ +public: + PlatformDarwin(bool is_host); + + ~PlatformDarwin() override; + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + lldb_private::Error + ResolveSymbolFile (lldb_private::Target &target, + const lldb_private::ModuleSpec &sym_spec, + lldb_private::FileSpec &sym_file) override; + + lldb_private::FileSpecList + LocateExecutableScriptingResources (lldb_private::Target *target, + lldb_private::Module &module, + lldb_private::Stream* feedback_stream) override; + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + size_t + GetSoftwareBreakpointTrapOpcode (lldb_private::Target &target, + lldb_private::BreakpointSite *bp_site) override; + + bool + GetProcessInfo (lldb::pid_t pid, + lldb_private::ProcessInstanceInfo &proc_info) override; + + lldb::BreakpointSP + SetThreadCreationBreakpoint (lldb_private::Target &target) override; + + uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos) override; + + bool + ModuleIsExcludedForUnconstrainedSearches(lldb_private::Target &target, + const lldb::ModuleSP &module_sp) override; + + bool + ARMGetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); + + bool + x86GetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); + + int32_t + GetResumeCountForLaunchInfo (lldb_private::ProcessLaunchInfo &launch_info) override; + + void + CalculateTrapHandlerSymbolNames () override; + + bool + GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update, + lldb_private::Process *process = nullptr) override; + + bool + SupportsModules () override { return true; } + + lldb_private::ConstString + GetFullNameForDylib (lldb_private::ConstString basename) override; + + lldb_private::FileSpec + LocateExecutable (const char *basename) override; + +protected: + void + ReadLibdispatchOffsetsAddress (lldb_private::Process *process); + + void + ReadLibdispatchOffsets (lldb_private::Process *process); + + virtual lldb_private::Error + GetSharedModuleWithLocalCache (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr); + + enum class SDKType { + MacOSX = 0, + iPhoneSimulator, + iPhoneOS, + }; + + static bool + SDKSupportsModules (SDKType sdk_type, uint32_t major, uint32_t minor, uint32_t micro); + + static bool + SDKSupportsModules (SDKType desired_type, const lldb_private::FileSpec &sdk_path); + + struct SDKEnumeratorInfo { + lldb_private::FileSpec found_path; + SDKType sdk_type; + }; + + static lldb_private::FileSpec::EnumerateDirectoryResult + DirectoryEnumerator(void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &spec); + + static lldb_private::FileSpec + FindSDKInXcodeForModules (SDKType sdk_type, + const lldb_private::FileSpec &sdks_spec); + + static lldb_private::FileSpec + GetSDKDirectoryForModules (PlatformDarwin::SDKType sdk_type); + + void + AddClangModuleCompilationOptionsForSDKType (lldb_private::Target *target, std::vector &options, SDKType sdk_type); + + std::string m_developer_directory; + + const char * + GetDeveloperDirectory(); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformDarwin); +}; + +#endif // liblldb_PlatformDarwin_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp b/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp new file mode 100644 index 000000000000..a502aa03eb26 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp @@ -0,0 +1,979 @@ +//===-- PlatformDarwinKernel.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformDarwinKernel.h" + +#if defined (__APPLE__) // This Plugin uses the Mac-specific source/Host/macosx/cfcpp utilities + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/OptionValueFileSpecList.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include + +#include "Host/macosx/cfcpp/CFCBundle.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformDarwinKernel::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformDarwinKernel::GetPluginNameStatic(), + PlatformDarwinKernel::GetDescriptionStatic(), + PlatformDarwinKernel::CreateInstance, + PlatformDarwinKernel::DebuggerInitialize); + } +} + +void +PlatformDarwinKernel::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformDarwinKernel::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformDarwinKernel::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformDarwinKernel::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + // This is a special plugin that we don't want to activate just based on an ArchSpec for normal + // userland debugging. It is only useful in kernel debug sessions and the DynamicLoaderDarwinPlugin + // (or a user doing 'platform select') will force the creation of this Platform plugin. + if (force == false) + { + if (log) + log->Printf ("PlatformDarwinKernel::%s() aborting creation of platform because force == false", __FUNCTION__); + return PlatformSP(); + } + + bool create = force; + LazyBool is_ios_debug_session = eLazyBoolCalculate; + + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + + // Only accept "unknown" for vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::WatchOS: + case llvm::Triple::TvOS: + break; + // Only accept "vendor" for vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; + default: + create = false; + break; + } + } + } + if (arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + is_ios_debug_session = eLazyBoolNo; + break; + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::thumb: + is_ios_debug_session = eLazyBoolYes; + break; + default: + is_ios_debug_session = eLazyBoolCalculate; + break; + } + } + if (create) + { + if (log) + log->Printf ("PlatformDarwinKernel::%s() creating platform", __FUNCTION__); + + return PlatformSP(new PlatformDarwinKernel (is_ios_debug_session)); + } + + if (log) + log->Printf ("PlatformDarwinKernel::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + + +lldb_private::ConstString +PlatformDarwinKernel::GetPluginNameStatic () +{ + static ConstString g_name("darwin-kernel"); + return g_name; +} + +const char * +PlatformDarwinKernel::GetDescriptionStatic() +{ + return "Darwin Kernel platform plug-in."; +} + +//------------------------------------------------------------------ +/// Code to handle the PlatformDarwinKernel settings +//------------------------------------------------------------------ + +static PropertyDefinition +g_properties[] = +{ + { "search-locally-for-kexts" , OptionValue::eTypeBoolean, true, true, NULL, NULL, "Automatically search for kexts on the local system when doing kernel debugging." }, + { "kext-directories", OptionValue::eTypeFileSpecList, false, 0, NULL, NULL, "Directories/KDKs to search for kexts in when starting a kernel debug session." }, + { NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL } +}; + +enum { + ePropertySearchForKexts = 0, + ePropertyKextDirectories +}; + + + +class PlatformDarwinKernelProperties : public Properties +{ +public: + + static ConstString & + GetSettingName () + { + static ConstString g_setting_name("darwin-kernel"); + return g_setting_name; + } + + PlatformDarwinKernelProperties() : + Properties () + { + m_collection_sp.reset (new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual + ~PlatformDarwinKernelProperties() + { + } + + bool + GetSearchForKexts() const + { + const uint32_t idx = ePropertySearchForKexts; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); + } + + FileSpecList & + GetKextDirectories() const + { + const uint32_t idx = ePropertyKextDirectories; + OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList (NULL, false, idx); + assert(option_value); + return option_value->GetCurrentValue(); + } +}; + +typedef std::shared_ptr PlatformDarwinKernelPropertiesSP; + +static const PlatformDarwinKernelPropertiesSP & +GetGlobalProperties() +{ + static PlatformDarwinKernelPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new PlatformDarwinKernelProperties ()); + return g_settings_sp; +} + +void +PlatformDarwinKernel::DebuggerInitialize (lldb_private::Debugger &debugger) +{ + if (!PluginManager::GetSettingForPlatformPlugin (debugger, PlatformDarwinKernelProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForPlatformPlugin (debugger, + GetGlobalProperties()->GetValueProperties(), + ConstString ("Properties for the PlatformDarwinKernel plug-in."), + is_global_setting); + } +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformDarwinKernel::PlatformDarwinKernel (lldb_private::LazyBool is_ios_debug_session) : + PlatformDarwin (false), // This is a remote platform + m_name_to_kext_path_map(), + m_search_directories(), + m_kernel_binaries(), + m_ios_debug_session(is_ios_debug_session) + +{ + if (GetGlobalProperties()->GetSearchForKexts()) + { + CollectKextAndKernelDirectories (); + IndexKextsInDirectories (); + IndexKernelsInDirectories (); + } +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformDarwinKernel::~PlatformDarwinKernel() +{ +} + + +void +PlatformDarwinKernel::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + strm.Printf (" Debug session type: "); + if (m_ios_debug_session == eLazyBoolYes) + strm.Printf ("iOS kernel debugging\n"); + else if (m_ios_debug_session == eLazyBoolNo) + strm.Printf ("Mac OS X kernel debugging\n"); + else + strm.Printf ("unknown kernel debugging\n"); + const uint32_t num_kext_dirs = m_search_directories.size(); + for (uint32_t i=0; i sdk_dirs; + if (m_ios_debug_session != eLazyBoolNo) + { + GetiOSSDKDirectoriesToSearch (sdk_dirs); + GetAppleTVOSSDKDirectoriesToSearch (sdk_dirs); + GetWatchOSSDKDirectoriesToSearch (sdk_dirs); + } + if (m_ios_debug_session != eLazyBoolYes) + GetMacSDKDirectoriesToSearch (sdk_dirs); + + GetGenericSDKDirectoriesToSearch (sdk_dirs); + + // Build up a list of directories that hold may kext bundles & kernels + // + // e.g. given /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ + // find + // /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.Internal.sdk/ + // and + // /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.Internal.sdk/System/Library/Extensions + + std::vector kext_dirs; + SearchSDKsForKextDirectories (sdk_dirs, kext_dirs); + + if (m_ios_debug_session != eLazyBoolNo) + GetiOSDirectoriesToSearch (kext_dirs); + if (m_ios_debug_session != eLazyBoolYes) + GetMacDirectoriesToSearch (kext_dirs); + + GetGenericDirectoriesToSearch (kext_dirs); + + GetUserSpecifiedDirectoriesToSearch (kext_dirs); + + GetKernelDirectoriesToSearch (kext_dirs); + + GetCurrentDirectoryToSearch (kext_dirs); + + // We now have a complete list of directories that we will search for kext bundles + m_search_directories = kext_dirs; +} + +void +PlatformDarwinKernel::GetiOSSDKDirectoriesToSearch (std::vector &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/iPhoneOS.platform/Developer/SDKs", developer_dir); + FileSpec ios_sdk(pathbuf, true); + if (ios_sdk.Exists() && ios_sdk.IsDirectory()) + { + directories.push_back (ios_sdk); + } +} + +void +PlatformDarwinKernel::GetAppleTVOSSDKDirectoriesToSearch (std::vector &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/AppleTVOS.platform/Developer/SDKs", developer_dir); + FileSpec ios_sdk(pathbuf, true); + if (ios_sdk.Exists() && ios_sdk.IsDirectory()) + { + directories.push_back (ios_sdk); + } +} + +void +PlatformDarwinKernel::GetWatchOSSDKDirectoriesToSearch (std::vector &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/watchOS.platform/Developer/SDKs", developer_dir); + FileSpec ios_sdk(pathbuf, true); + if (ios_sdk.Exists() && ios_sdk.IsDirectory()) + { + directories.push_back (ios_sdk); + } + else + { + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/WatchOS.platform/Developer/SDKs", developer_dir); + FileSpec alt_watch_sdk(pathbuf, true); + if (ios_sdk.Exists() && ios_sdk.IsDirectory()) + { + directories.push_back (ios_sdk); + } + } +} + + +void +PlatformDarwinKernel::GetMacSDKDirectoriesToSearch (std::vector &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/Platforms/MacOSX.platform/Developer/SDKs", developer_dir); + FileSpec mac_sdk(pathbuf, true); + if (mac_sdk.Exists() && mac_sdk.IsDirectory()) + { + directories.push_back (mac_sdk); + } +} + +void +PlatformDarwinKernel::GetGenericSDKDirectoriesToSearch (std::vector &directories) +{ + FileSpec generic_sdk("/AppleInternal/Developer/KDKs", true); + if (generic_sdk.Exists() && generic_sdk.IsDirectory()) + { + directories.push_back (generic_sdk); + } + + // The KDKs distributed from Apple installed on external + // developer systems may be in directories like + // /Library/Developer/KDKs/KDK_10.10_14A298i.kdk + FileSpec installed_kdks("/Library/Developer/KDKs", true); + if (installed_kdks.Exists() && installed_kdks.IsDirectory()) + { + directories.push_back (installed_kdks); + } +} + +void +PlatformDarwinKernel::GetiOSDirectoriesToSearch (std::vector &directories) +{ +} + +void +PlatformDarwinKernel::GetMacDirectoriesToSearch (std::vector &directories) +{ + FileSpec sle("/System/Library/Extensions", true); + if (sle.Exists() && sle.IsDirectory()) + { + directories.push_back(sle); + } + + FileSpec le("/Library/Extensions", true); + if (le.Exists() && le.IsDirectory()) + { + directories.push_back(le); + } + + FileSpec kdk("/Volumes/KernelDebugKit", true); + if (kdk.Exists() && kdk.IsDirectory()) + { + directories.push_back(kdk); + } +} + +void +PlatformDarwinKernel::GetGenericDirectoriesToSearch (std::vector &directories) +{ + // DeveloperDirectory is something like "/Applications/Xcode.app/Contents/Developer" + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir == NULL) + developer_dir = "/Applications/Xcode.app/Contents/Developer"; + + char pathbuf[PATH_MAX]; + ::snprintf (pathbuf, sizeof (pathbuf), "%s/../Symbols", developer_dir); + FileSpec symbols_dir (pathbuf, true); + if (symbols_dir.Exists() && symbols_dir.IsDirectory()) + { + directories.push_back (symbols_dir); + } +} + +void +PlatformDarwinKernel::GetKernelDirectoriesToSearch (std::vector &directories) +{ + FileSpec system_library_kernels ("/System/Library/Kernels", true); + if (system_library_kernels.Exists() && system_library_kernels.IsDirectory()) + { + directories.push_back (system_library_kernels); + } + FileSpec slek("/System/Library/Extensions/KDK", true); + if (slek.Exists() && slek.IsDirectory()) + { + directories.push_back(slek); + } +} + +void +PlatformDarwinKernel::GetCurrentDirectoryToSearch (std::vector &directories) +{ + directories.push_back (FileSpec (".", true)); + + FileSpec sle_directory ("System/Library/Extensions", true); + if (sle_directory.Exists() && sle_directory.IsDirectory()) + { + directories.push_back (sle_directory); + } + + FileSpec le_directory ("Library/Extensions", true); + if (le_directory.Exists() && le_directory.IsDirectory()) + { + directories.push_back (le_directory); + } + + FileSpec slk_directory ("System/Library/Kernels", true); + if (slk_directory.Exists() && slk_directory.IsDirectory()) + { + directories.push_back (slk_directory); + } + FileSpec slek("System/Library/Extensions/KDK", true); + if (slek.Exists() && slek.IsDirectory()) + { + directories.push_back(slek); + } +} + +void +PlatformDarwinKernel::GetUserSpecifiedDirectoriesToSearch (std::vector &directories) +{ + FileSpecList user_dirs(GetGlobalProperties()->GetKextDirectories()); + std::vector possible_sdk_dirs; + + const uint32_t user_dirs_count = user_dirs.GetSize(); + for (uint32_t i = 0; i < user_dirs_count; i++) + { + FileSpec dir = user_dirs.GetFileSpecAtIndex (i); + dir.ResolvePath(); + if (dir.Exists() && dir.IsDirectory()) + { + directories.push_back (dir); + possible_sdk_dirs.push_back (dir); // does this directory have a *.sdk or *.kdk that we should look in? + + // Is there a "System/Library/Extensions" subdir of this directory? + std::string dir_sle_path = dir.GetPath(); + dir_sle_path.append ("/System/Library/Extensions"); + FileSpec dir_sle(dir_sle_path.c_str(), true); + if (dir_sle.Exists() && dir_sle.IsDirectory()) + { + directories.push_back (dir_sle); + } + + // Is there a "System/Library/Kernels" subdir of this directory? + std::string dir_slk_path = dir.GetPath(); + dir_slk_path.append ("/System/Library/Kernels"); + FileSpec dir_slk(dir_slk_path.c_str(), true); + if (dir_slk.Exists() && dir_slk.IsDirectory()) + { + directories.push_back (dir_slk); + } + + // Is there a "System/Library/Extensions/KDK" subdir of this directory? + std::string dir_slek_path = dir.GetPath(); + dir_slek_path.append ("/System/Library/Kernels"); + FileSpec dir_slek(dir_slek_path.c_str(), true); + if (dir_slek.Exists() && dir_slek.IsDirectory()) + { + directories.push_back (dir_slek); + } + } + } + + SearchSDKsForKextDirectories (possible_sdk_dirs, directories); +} + +// Scan through the SDK directories, looking for directories where kexts are likely. +// Add those directories to kext_dirs. +void +PlatformDarwinKernel::SearchSDKsForKextDirectories (std::vector sdk_dirs, std::vector &kext_dirs) +{ + const uint32_t num_sdks = sdk_dirs.size(); + for (uint32_t i = 0; i < num_sdks; i++) + { + const FileSpec &sdk_dir = sdk_dirs[i]; + std::string sdk_dir_path = sdk_dir.GetPath(); + if (!sdk_dir_path.empty()) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + FileSpec::EnumerateDirectory (sdk_dir_path.c_str(), + find_directories, + find_files, + find_other, + GetKextDirectoriesInSDK, + &kext_dirs); + } + } +} + +// Callback for FileSpec::EnumerateDirectory(). +// Step through the entries in a directory like +// /Applications/Xcode.app//Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs +// looking for any subdirectories of the form MacOSX10.8.Internal.sdk/System/Library/Extensions +// Adds these to the vector of FileSpec's. + +FileSpec::EnumerateDirectoryResult +PlatformDarwinKernel::GetKextDirectoriesInSDK (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory + && (file_spec.GetFileNameExtension() == ConstString("sdk") + || file_spec.GetFileNameExtension() == ConstString("kdk"))) + { + std::string kext_directory_path = file_spec.GetPath(); + + // Append the raw directory path, e.g. /Library/Developer/KDKs/KDK_10.10_14A298i.kdk + // to the directory search list -- there may be kexts sitting directly + // in that directory instead of being in a System/Library/Extensions subdir. + ((std::vector *)baton)->push_back(file_spec); + + // Check to see if there is a System/Library/Extensions subdir & add it if it exists + + std::string sle_kext_directory_path (kext_directory_path); + sle_kext_directory_path.append ("/System/Library/Extensions"); + FileSpec sle_kext_directory (sle_kext_directory_path.c_str(), true); + if (sle_kext_directory.Exists() && sle_kext_directory.IsDirectory()) + { + ((std::vector *)baton)->push_back(sle_kext_directory); + } + + // Check to see if there is a Library/Extensions subdir & add it if it exists + + std::string le_kext_directory_path (kext_directory_path); + le_kext_directory_path.append ("/Library/Extensions"); + FileSpec le_kext_directory (le_kext_directory_path.c_str(), true); + if (le_kext_directory.Exists() && le_kext_directory.IsDirectory()) + { + ((std::vector *)baton)->push_back(le_kext_directory); + } + + // Check to see if there is a System/Library/Kernels subdir & add it if it exists + std::string slk_kernel_path (kext_directory_path); + slk_kernel_path.append ("/System/Library/Kernels"); + FileSpec slk_kernel_directory (slk_kernel_path.c_str(), true); + if (slk_kernel_directory.Exists() && slk_kernel_directory.IsDirectory()) + { + ((std::vector *)baton)->push_back(slk_kernel_directory); + } + + // Check to see if there is a System/Library/Extensions/KDK subdir & add it if it exists + std::string slek_kernel_path (kext_directory_path); + slek_kernel_path.append ("/System/Library/Extensions/KDK"); + FileSpec slek_kernel_directory (slek_kernel_path.c_str(), true); + if (slek_kernel_directory.Exists() && slek_kernel_directory.IsDirectory()) + { + ((std::vector *)baton)->push_back(slek_kernel_directory); + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + +void +PlatformDarwinKernel::IndexKextsInDirectories () +{ + std::vector kext_bundles; + + const uint32_t num_dirs = m_search_directories.size(); + for (uint32_t i = 0; i < num_dirs; i++) + { + const FileSpec &dir = m_search_directories[i]; + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + FileSpec::EnumerateDirectory (dir.GetPath().c_str(), + find_directories, + find_files, + find_other, + GetKextsInDirectory, + &kext_bundles); + } + + const uint32_t num_kexts = kext_bundles.size(); + for (uint32_t i = 0; i < num_kexts; i++) + { + const FileSpec &kext = kext_bundles[i]; + CFCBundle bundle (kext.GetPath().c_str()); + CFStringRef bundle_id (bundle.GetIdentifier()); + if (bundle_id && CFGetTypeID (bundle_id) == CFStringGetTypeID ()) + { + char bundle_id_buf[PATH_MAX]; + if (CFStringGetCString (bundle_id, bundle_id_buf, sizeof (bundle_id_buf), kCFStringEncodingUTF8)) + { + ConstString bundle_conststr(bundle_id_buf); + m_name_to_kext_path_map.insert(std::pair(bundle_conststr, kext)); + } + } + } +} + +// Callback for FileSpec::EnumerateDirectory(). +// Step through the entries in a directory like /System/Library/Extensions, find .kext bundles, add them +// to the vector of FileSpecs. +// If a .kext bundle has a Contents/PlugIns or PlugIns subdir, search for kexts in there too. + +FileSpec::EnumerateDirectoryResult +PlatformDarwinKernel::GetKextsInDirectory (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory && file_spec.GetFileNameExtension() == ConstString("kext")) + { + ((std::vector *)baton)->push_back(file_spec); + std::string kext_bundle_path = file_spec.GetPath(); + std::string search_here_too; + std::string contents_plugins_path = kext_bundle_path + "/Contents/PlugIns"; + FileSpec contents_plugins (contents_plugins_path.c_str(), false); + if (contents_plugins.Exists() && contents_plugins.IsDirectory()) + { + search_here_too = contents_plugins_path; + } + else + { + std::string plugins_path = kext_bundle_path + "/PlugIns"; + FileSpec plugins (plugins_path.c_str(), false); + if (plugins.Exists() && plugins.IsDirectory()) + { + search_here_too = plugins_path; + } + } + + if (!search_here_too.empty()) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + FileSpec::EnumerateDirectory (search_here_too.c_str(), + find_directories, + find_files, + find_other, + GetKextsInDirectory, + baton); + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + +void +PlatformDarwinKernel::IndexKernelsInDirectories () +{ + std::vector kernels; + + + const uint32_t num_dirs = m_search_directories.size(); + for (uint32_t i = 0; i < num_dirs; i++) + { + const FileSpec &dir = m_search_directories[i]; + const bool find_directories = false; + const bool find_files = true; + const bool find_other = true; // I think eFileTypeSymbolicLink are "other"s. + FileSpec::EnumerateDirectory (dir.GetPath().c_str(), + find_directories, + find_files, + find_other, + GetKernelsInDirectory, + &m_kernel_binaries); + } +} + +// Callback for FileSpec::EnumerateDirectory(). +// Step through the entries in a directory like /System/Library/Kernels/, find kernel binaries, +// add them to m_kernel_binaries. + +// We're only doing a filename match here. We won't try opening the file to see if it's really +// a kernel or not until we need to find a kernel of a given UUID. There's no cheap way to find +// the UUID of a file (or if it's a Mach-O binary at all) without creating a whole Module for +// the file and throwing it away if it's not wanted. + +FileSpec::EnumerateDirectoryResult +PlatformDarwinKernel::GetKernelsInDirectory (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeRegular || file_type == FileSpec::eFileTypeSymbolicLink) + { + ConstString filename = file_spec.GetFilename(); + if (strncmp (filename.GetCString(), "kernel", 6) == 0 + || strncmp (filename.GetCString(), "mach", 4) == 0) + { + // This is m_kernel_binaries but we're in a class method here + ((std::vector *)baton)->push_back(file_spec); + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + + +Error +PlatformDarwinKernel::GetSharedModule (const ModuleSpec &module_spec, + Process *process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error; + module_sp.reset(); + const FileSpec &platform_file = module_spec.GetFileSpec(); + + // Treat the file's path as a kext bundle ID (e.g. "com.apple.driver.AppleIRController") and search our kext index. + std::string kext_bundle_id = platform_file.GetPath(); + if (!kext_bundle_id.empty()) + { + ConstString kext_bundle_cs(kext_bundle_id.c_str()); + if (m_name_to_kext_path_map.count(kext_bundle_cs) > 0) + { + for (BundleIDToKextIterator it = m_name_to_kext_path_map.begin (); it != m_name_to_kext_path_map.end (); ++it) + { + if (it->first == kext_bundle_cs) + { + error = ExamineKextForMatchingUUID (it->second, module_spec.GetUUID(), module_spec.GetArchitecture(), module_sp); + if (module_sp.get()) + { + return error; + } + } + } + } + } + + if (kext_bundle_id.compare("mach_kernel") == 0 && module_spec.GetUUID().IsValid()) + { + for (auto possible_kernel : m_kernel_binaries) + { + if (possible_kernel.Exists()) + { + ModuleSpec kern_spec (possible_kernel); + kern_spec.GetUUID() = module_spec.GetUUID(); + ModuleSP module_sp (new Module (kern_spec)); + if (module_sp && module_sp->GetObjectFile() && module_sp->MatchesModuleSpec (kern_spec)) + { + Error error; + error = ModuleList::GetSharedModule (kern_spec, module_sp, NULL, NULL, NULL); + if (module_sp && module_sp->GetObjectFile()) + { + return error; + } + } + } + } + } + + // Else fall back to treating the file's path as an actual file path - defer to PlatformDarwin's GetSharedModule. + return PlatformDarwin::GetSharedModule (module_spec, process, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); +} + +Error +PlatformDarwinKernel::ExamineKextForMatchingUUID (const FileSpec &kext_bundle_path, const lldb_private::UUID &uuid, const ArchSpec &arch, ModuleSP &exe_module_sp) +{ + Error error; + FileSpec exe_file = kext_bundle_path; + Host::ResolveExecutableInBundle (exe_file); + if (exe_file.Exists()) + { + ModuleSpec exe_spec (exe_file); + exe_spec.GetUUID() = uuid; + if (!uuid.IsValid()) + { + exe_spec.GetArchitecture() = arch; + } + + // First try to create a ModuleSP with the file / arch and see if the UUID matches. + // If that fails (this exec file doesn't have the correct uuid), don't call GetSharedModule + // (which may call in to the DebugSymbols framework and therefore can be slow.) + ModuleSP module_sp (new Module (exe_spec)); + if (module_sp && module_sp->GetObjectFile() && module_sp->MatchesModuleSpec (exe_spec)) + { + error = ModuleList::GetSharedModule (exe_spec, exe_module_sp, NULL, NULL, NULL); + if (exe_module_sp && exe_module_sp->GetObjectFile()) + { + return error; + } + } + exe_module_sp.reset(); + } + return error; +} + +bool +PlatformDarwinKernel::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + return ARMGetSupportedArchitectureAtIndex (idx, arch); +#else + return x86GetSupportedArchitectureAtIndex (idx, arch); +#endif +} + +void +PlatformDarwinKernel::CalculateTrapHandlerSymbolNames () +{ + m_trap_handlers.push_back(ConstString ("trap_from_kernel")); + m_trap_handlers.push_back(ConstString ("hndl_machine_check")); + m_trap_handlers.push_back(ConstString ("hndl_double_fault")); + m_trap_handlers.push_back(ConstString ("hndl_allintrs")); + m_trap_handlers.push_back(ConstString ("hndl_alltraps")); + m_trap_handlers.push_back(ConstString ("interrupt")); + m_trap_handlers.push_back(ConstString ("fleh_prefabt")); + m_trap_handlers.push_back(ConstString ("ExceptionVectorsBase")); + m_trap_handlers.push_back(ConstString ("ExceptionVectorsTable")); + m_trap_handlers.push_back(ConstString ("fleh_undef")); + m_trap_handlers.push_back(ConstString ("fleh_dataabt")); + m_trap_handlers.push_back(ConstString ("fleh_irq")); + m_trap_handlers.push_back(ConstString ("fleh_decirq")); + m_trap_handlers.push_back(ConstString ("fleh_fiq_generic")); + m_trap_handlers.push_back(ConstString ("fleh_dec")); + +} + +#else // __APPLE__ + +// Since DynamicLoaderDarwinKernel is compiled in for all systems, and relies on +// PlatformDarwinKernel for the plug-in name, we compile just the plug-in name in +// here to avoid issues. We are tracking an internal bug to resolve this issue by +// either not compiling in DynamicLoaderDarwinKernel for non-apple builds, or to make +// PlatformDarwinKernel build on all systems. PlatformDarwinKernel is currently not +// compiled on other platforms due to the use of the Mac-specific +// source/Host/macosx/cfcpp utilities. + +lldb_private::ConstString +PlatformDarwinKernel::GetPluginNameStatic () +{ + static lldb_private::ConstString g_name("darwin-kernel"); + return g_name; +} + +#endif // __APPLE__ diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h b/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h new file mode 100644 index 000000000000..c1fe23178bf4 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h @@ -0,0 +1,228 @@ +//===-- PlatformDarwinKernel.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformDarwinKernel_h_ +#define liblldb_PlatformDarwinKernel_h_ + +#include "lldb/Core/ConstString.h" + +#if defined (__APPLE__) // This Plugin uses the Mac-specific source/Host/macosx/cfcpp utilities + + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Host/FileSpec.h" + +// Project includes +#include "PlatformDarwin.h" + +class PlatformDarwinKernel : public PlatformDarwin +{ +public: + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + DebuggerInitialize (lldb_private::Debugger &debugger); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + PlatformDarwinKernel (lldb_private::LazyBool is_ios_debug_session); + + virtual + ~PlatformDarwinKernel(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process *process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + bool + SupportsModules() override { return false; } + + void + CalculateTrapHandlerSymbolNames () override; + +protected: + + // Map from kext bundle ID ("com.apple.filesystems.exfat") to FileSpec for the kext bundle on + // the host ("/System/Library/Extensions/exfat.kext/Contents/Info.plist"). + typedef std::multimap BundleIDToKextMap; + typedef BundleIDToKextMap::iterator BundleIDToKextIterator; + + typedef std::vector KernelBinaryCollection; + + // Array of directories that were searched for kext bundles (used only for reporting to user) + typedef std::vector DirectoriesSearchedCollection; + typedef DirectoriesSearchedCollection::iterator DirectoriesSearchedIterator; + + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetKextDirectoriesInSDK (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetKextsInDirectory (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + // Populate m_search_directories vector of directories + void + CollectKextAndKernelDirectories (); + + // Directories where we may find iOS SDKs with kext bundles in them + void + GetiOSSDKDirectoriesToSearch (std::vector &directories); + + // Directories where we may find AppleTVOS SDKs with kext bundles in them + void + GetAppleTVOSSDKDirectoriesToSearch (std::vector &directories); + + // Directories where we may find WatchOS SDKs with kext bundles in them + void + GetWatchOSSDKDirectoriesToSearch (std::vector &directories); + + // Directories where we may find Mac OS X SDKs with kext bundles in them + void + GetMacSDKDirectoriesToSearch (std::vector &directories); + + // Directories where we may find Mac OS X or iOS SDKs with kext bundles in them + void + GetGenericSDKDirectoriesToSearch (std::vector &directories); + + // Directories where we may find iOS kext bundles + void + GetiOSDirectoriesToSearch (std::vector &directories); + + // Directories where we may find MacOSX kext bundles + void + GetMacDirectoriesToSearch (std::vector &directories); + + // Directories where we may find iOS or MacOSX kext bundles + void + GetGenericDirectoriesToSearch (std::vector &directories); + + // Directories specified via the "kext-directories" setting - maybe KDK/SDKs, may be plain directories + void + GetUserSpecifiedDirectoriesToSearch (std::vector &directories); + + void + GetCurrentDirectoryToSearch (std::vector &directories); + + // Directories where we may find kernels exclusively + void + GetKernelDirectoriesToSearch (std::vector &directories); + + // Search through a vector of SDK FileSpecs, add any directories that may contain kexts + // to the vector of kext dir FileSpecs + void + SearchSDKsForKextDirectories (std::vector sdk_dirs, std::vector &kext_dirs); + + // Search through all of the directories passed in, find all .kext bundles in those directories, + // get the CFBundleIDs out of the Info.plists and add the bundle ID and kext path to m_name_to_kext_path_map. + void + IndexKextsInDirectories (); + + // Search through all of the directories passed in, find all kernel binaries in those directories + // (look for "kernel*", "mach.*", assume those are kernels. False positives aren't a huge problem.) + void + IndexKernelsInDirectories (); + + // Callback which iterates over all the files in a given directory, looking for kernel binaries + static lldb_private::FileSpec::EnumerateDirectoryResult + GetKernelsInDirectory (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + lldb_private::Error + ExamineKextForMatchingUUID (const lldb_private::FileSpec &kext_bundle_path, const lldb_private::UUID &uuid, const lldb_private::ArchSpec &arch, lldb::ModuleSP &exe_module_sp); + +private: + + BundleIDToKextMap m_name_to_kext_path_map; // multimap of CFBundleID to FileSpec on local filesystem + DirectoriesSearchedCollection m_search_directories; // list of directories we search for kexts/kernels + KernelBinaryCollection m_kernel_binaries; // list of kernel binaries we found on local filesystem + lldb_private::LazyBool m_ios_debug_session; + + DISALLOW_COPY_AND_ASSIGN (PlatformDarwinKernel); + +}; + +#else // __APPLE__ + +// Since DynamicLoaderDarwinKernel is compiled in for all systems, and relies on +// PlatformDarwinKernel for the plug-in name, we compile just the plug-in name in +// here to avoid issues. We are tracking an internal bug to resolve this issue by +// either not compiling in DynamicLoaderDarwinKernel for non-apple builds, or to make +// PlatformDarwinKernel build on all systems. PlatformDarwinKernel is currently not +// compiled on other platforms due to the use of the Mac-specific +// source/Host/macosx/cfcpp utilities. + +class PlatformDarwinKernel +{ + static lldb_private::ConstString + GetPluginNameStatic (); +}; + +#endif // __APPLE__ + +#endif // liblldb_PlatformDarwinKernel_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp b/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp new file mode 100644 index 000000000000..7e15facc1b03 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp @@ -0,0 +1,386 @@ +//===-- PlatformMacOSX.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformMacOSX.h" +#include "lldb/Host/Config.h" + +// C++ Includes + +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +static uint32_t g_initialize_count = 0; + +void +PlatformMacOSX::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined (__APPLE__) + PlatformSP default_platform_sp (new PlatformMacOSX(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin (PlatformMacOSX::GetPluginNameStatic(false), + PlatformMacOSX::GetDescriptionStatic(false), + PlatformMacOSX::CreateInstance); + } + +} + +void +PlatformMacOSX::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformMacOSX::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformMacOSX::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformMacOSX::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + // The only time we create an instance is when we are creating a remote + // macosx platform + const bool is_host = false; + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Darwin: // Deprecated, but still support Darwin for historical reasons + case llvm::Triple::MacOSX: + break; +#if defined(__APPLE__) + // Only accept "vendor" for vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + if (create) + { + if (log) + log->Printf ("PlatformMacOSX::%s() creating platform", __FUNCTION__); + return PlatformSP(new PlatformMacOSX (is_host)); + } + + if (log) + log->Printf ("PlatformMacOSX::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + +lldb_private::ConstString +PlatformMacOSX::GetPluginNameStatic (bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-macosx"); + return g_remote_name; + } +} + +const char * +PlatformMacOSX::GetDescriptionStatic (bool is_host) +{ + if (is_host) + return "Local Mac OS X user platform plug-in."; + else + return "Remote Mac OS X user platform plug-in."; +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformMacOSX::PlatformMacOSX (bool is_host) : + PlatformDarwin (is_host) +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformMacOSX::~PlatformMacOSX() +{ +} + +ConstString +PlatformMacOSX::GetSDKDirectory (lldb_private::Target &target) +{ + ModuleSP exe_module_sp (target.GetExecutableModule()); + if (exe_module_sp) + { + ObjectFile *objfile = exe_module_sp->GetObjectFile(); + if (objfile) + { + std::string xcode_contents_path; + std::string default_xcode_sdk; + FileSpec fspec; + uint32_t versions[2]; + if (objfile->GetSDKVersion(versions, sizeof(versions))) + { + if (HostInfo::GetLLDBPath(ePathTypeLLDBShlibDir, fspec)) + { + std::string path; + xcode_contents_path = fspec.GetPath(); + size_t pos = xcode_contents_path.find("/Xcode.app/Contents/"); + if (pos != std::string::npos) + { + // LLDB.framework is inside an Xcode app bundle, we can locate the SDK from here + xcode_contents_path.erase(pos + strlen("/Xcode.app/Contents/")); + } + else + { + xcode_contents_path.clear(); + // Use the selected Xcode + int status = 0; + int signo = 0; + std::string output; + const char *command = "xcrun -sdk macosx --show-sdk-path"; + lldb_private::Error error = RunShellCommand (command, // shell command to run + NULL, // current working directory + &status, // Put the exit status of the process in here + &signo, // Put the signal that caused the process to exit in here + &output, // Get the output from the command and place it in this string + 3); // Timeout in seconds to wait for shell program to finish + if (status == 0 && !output.empty()) + { + size_t first_non_newline = output.find_last_not_of("\r\n"); + if (first_non_newline != std::string::npos) + output.erase(first_non_newline+1); + default_xcode_sdk = output; + + pos = default_xcode_sdk.find("/Xcode.app/Contents/"); + if (pos != std::string::npos) + xcode_contents_path = default_xcode_sdk.substr(0, pos + strlen("/Xcode.app/Contents/")); + } + } + } + + if (!xcode_contents_path.empty()) + { + StreamString sdk_path; + sdk_path.Printf("%sDeveloper/Platforms/MacOSX.platform/Developer/SDKs/MacOSX%u.%u.sdk", xcode_contents_path.c_str(), versions[0], versions[1]); + fspec.SetFile(sdk_path.GetString().c_str(), false); + if (fspec.Exists()) + return ConstString(sdk_path.GetString().c_str()); + } + + if (!default_xcode_sdk.empty()) + { + fspec.SetFile(default_xcode_sdk.c_str(), false); + if (fspec.Exists()) + return ConstString(default_xcode_sdk.c_str()); + } + } + } + } + return ConstString(); +} + + +Error +PlatformMacOSX::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file); + } + + // Default to the local case + local_file = platform_file; + return Error(); +} + +lldb_private::Error +PlatformMacOSX::GetFileWithUUID (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file) +{ + if (IsRemote() && m_remote_platform_sp) + { + std::string local_os_build; +#if !defined(__linux__) + HostInfo::GetOSBuildString(local_os_build); +#endif + std::string remote_os_build; + m_remote_platform_sp->GetOSBuildString(remote_os_build); + if (local_os_build.compare(remote_os_build) == 0) + { + // same OS version: the local file is good enough + local_file = platform_file; + return Error(); + } + else + { + // try to find the file in the cache + std::string cache_path(GetLocalCacheDirectory()); + std::string module_path (platform_file.GetPath()); + cache_path.append(module_path); + FileSpec module_cache_spec(cache_path.c_str(),false); + if (module_cache_spec.Exists()) + { + local_file = module_cache_spec; + return Error(); + } + // bring in the remote module file + FileSpec module_cache_folder = module_cache_spec.CopyByRemovingLastPathComponent(); + // try to make the local directory first + Error err = + FileSystem::MakeDirectory(module_cache_folder, eFilePermissionsDirectoryDefault); + if (err.Fail()) + return err; + err = GetFile(platform_file, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + local_file = module_cache_spec; + return Error(); + } + else + return Error("unable to obtain valid module file"); + } + } + local_file = platform_file; + return Error(); +} + +bool +PlatformMacOSX::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + return ARMGetSupportedArchitectureAtIndex (idx, arch); +#else + return x86GetSupportedArchitectureAtIndex (idx, arch); +#endif +} + +lldb_private::Error +PlatformMacOSX::GetSharedModule (const lldb_private::ModuleSpec &module_spec, + Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error = GetSharedModuleWithLocalCache(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + + if (module_sp) + { + if (module_spec.GetArchitecture().GetCore() == ArchSpec::eCore_x86_64_x86_64h) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile == NULL) + { + // We didn't find an x86_64h slice, fall back to a x86_64 slice + ModuleSpec module_spec_x86_64 (module_spec); + module_spec_x86_64.GetArchitecture() = ArchSpec("x86_64-apple-macosx"); + lldb::ModuleSP x86_64_module_sp; + lldb::ModuleSP old_x86_64_module_sp; + bool did_create = false; + Error x86_64_error = GetSharedModuleWithLocalCache(module_spec_x86_64, x86_64_module_sp, module_search_paths_ptr, &old_x86_64_module_sp, &did_create); + if (x86_64_module_sp && x86_64_module_sp->GetObjectFile()) + { + module_sp = x86_64_module_sp; + if (old_module_sp_ptr) + *old_module_sp_ptr = old_x86_64_module_sp; + if (did_create_ptr) + *did_create_ptr = did_create; + return x86_64_error; + } + } + } + } + return error; +} + diff --git a/source/Plugins/Platform/MacOSX/PlatformMacOSX.h b/source/Plugins/Platform/MacOSX/PlatformMacOSX.h new file mode 100644 index 000000000000..10e177ea6362 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformMacOSX.h @@ -0,0 +1,104 @@ +//===-- PlatformMacOSX.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformMacOSX_h_ +#define liblldb_PlatformMacOSX_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "PlatformDarwin.h" + +class PlatformMacOSX : public PlatformDarwin +{ +public: + PlatformMacOSX(bool is_host); + + ~PlatformMacOSX() override; + + //------------------------------------------------------------ + // Class functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetDescriptionStatic(bool is_host); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic (IsHost()); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic (IsHost()); + } + + lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetFile (const lldb_private::FileSpec& source, + const lldb_private::FileSpec& destination) override + { + return PlatformDarwin::GetFile (source,destination); + } + + lldb_private::Error + GetFileWithUUID (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file) override; + + bool GetSupportedArchitectureAtIndex(uint32_t idx, lldb_private::ArchSpec &arch) override; + + lldb_private::ConstString GetSDKDirectory(lldb_private::Target &target) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::MacOSX); + } + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformMacOSX); +}; + +#endif // liblldb_PlatformMacOSX_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.cpp b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.cpp new file mode 100644 index 000000000000..04231f27ff9b --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleTV.cpp @@ -0,0 +1,912 @@ +//===-- PlatformRemoteAppleTV.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 +#include + +// Other libraries and framework includes +// Project includes +#include "PlatformRemoteAppleTV.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformRemoteAppleTV::PlatformRemoteAppleTV () : + PlatformDarwin (false), // This is a remote platform + m_sdk_directory_infos(), + m_device_support_directory(), + m_device_support_directory_for_os_version (), + m_build_update(), + m_last_module_sdk_idx (UINT32_MAX), + m_connected_module_sdk_idx (UINT32_MAX) +{ +} + +PlatformRemoteAppleTV::SDKDirectoryInfo::SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir) : + directory(sdk_dir), + build(), + version_major(0), + version_minor(0), + version_update(0), + user_cached(false) +{ + const char *dirname_cstr = sdk_dir.GetFilename().GetCString(); + const char *pos = Args::StringToVersion (dirname_cstr, + version_major, + version_minor, + version_update); + + if (pos && pos[0] == ' ' && pos[1] == '(') + { + const char *build_start = pos + 2; + const char *end_paren = strchr (build_start, ')'); + if (end_paren && build_start < end_paren) + build.SetCStringWithLength(build_start, end_paren - build_start); + } +} + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformRemoteAppleTV::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformRemoteAppleTV::GetPluginNameStatic(), + PlatformRemoteAppleTV::GetDescriptionStatic(), + PlatformRemoteAppleTV::CreateInstance); + } +} + +void +PlatformRemoteAppleTV::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformRemoteAppleTV::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformRemoteAppleTV::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformRemoteAppleTV::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (!create && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::thumb: + { + const llvm::Triple &triple = arch->GetTriple(); + llvm::Triple::VendorType vendor = triple.getVendor(); + switch (vendor) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + +#endif + default: + break; + } + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::TvOS: // This is the right triple value for Apple TV debugging + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + + if (create) + { + if (log) + log->Printf ("PlatformRemoteAppleTV::%s() creating platform", __FUNCTION__); + + return lldb::PlatformSP(new PlatformRemoteAppleTV ()); + } + + if (log) + log->Printf ("PlatformRemoteAppleTV::%s() aborting creation of platform", __FUNCTION__); + + return lldb::PlatformSP(); +} + +lldb_private::ConstString +PlatformRemoteAppleTV::GetPluginNameStatic () +{ + static ConstString g_name("remote-tvos"); + return g_name; +} + +const char * +PlatformRemoteAppleTV::GetDescriptionStatic() +{ + return "Remote Apple TV platform plug-in."; +} + +void +PlatformRemoteAppleTV::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetDeviceSupportDirectoryForOSVersion(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; iGetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +FileSpec::EnumerateDirectoryResult +PlatformRemoteAppleTV::GetContainedFilesIntoVectorOfStringsCallback (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + ((PlatformRemoteAppleTV::SDKDirectoryInfoCollection *)baton)->push_back(PlatformRemoteAppleTV::SDKDirectoryInfo(file_spec)); + return FileSpec::eEnumerateDirectoryResultNext; +} + +bool +PlatformRemoteAppleTV::UpdateSDKDirectoryInfosIfNeeded() +{ + if (m_sdk_directory_infos.empty()) + { + const char *device_support_dir = GetDeviceSupportDirectory(); + if (device_support_dir) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + + SDKDirectoryInfoCollection builtin_sdk_directory_infos; + FileSpec::EnumerateDirectory (m_device_support_directory.c_str(), + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &builtin_sdk_directory_infos); + + // Only add SDK directories that have symbols in them, some SDKs only contain + // developer disk images and no symbols, so they aren't useful to us. + FileSpec sdk_symbols_symlink_fspec; + for (const auto &sdk_directory_info : builtin_sdk_directory_infos) + { + sdk_symbols_symlink_fspec = sdk_directory_info.directory; + sdk_symbols_symlink_fspec.AppendPathComponent("Symbols.Internal"); + if (sdk_symbols_symlink_fspec.Exists()) + { + m_sdk_directory_infos.push_back(sdk_directory_info); + } + else + { + sdk_symbols_symlink_fspec.GetFilename().SetCString("Symbols"); + if (sdk_symbols_symlink_fspec.Exists()) + m_sdk_directory_infos.push_back(sdk_directory_info); + } + } + + const uint32_t num_installed = m_sdk_directory_infos.size(); + FileSpec local_sdk_cache("~/Library/Developer/Xcode/tvOS DeviceSupport", true); + if (!local_sdk_cache.Exists()) + { + // Try looking for another possible name + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/Apple TVOS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + // Try looking for another possible name + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/AppleTVOS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + // Try looking for another possible name + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/AppleTV OS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + // Try looking for another possible name + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/Apple TV OS DeviceSupport", true); + } + if (local_sdk_cache.Exists()) + { + char path[PATH_MAX]; + if (local_sdk_cache.GetPath(path, sizeof(path))) + { + FileSpec::EnumerateDirectory (path, + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &m_sdk_directory_infos); + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=num_installed; i check_sdk_info(num_sdk_infos, true); + ConstString build(m_sdk_build); + if (build) + { + for (i=0; i result->version_major) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_major == result->version_major) + { + if (sdk_dir_info.version_minor > result->version_minor) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_minor == result->version_minor) + { + if (sdk_dir_info.version_update > result->version_update) + { + result = &sdk_dir_info; + } + } + } + } + } + } + return result; +} + +const char * +PlatformRemoteAppleTV::GetDeviceSupportDirectory() +{ + if (m_device_support_directory.empty()) + { + const char *device_support_dir = GetDeveloperDirectory(); + if (device_support_dir) + { + m_device_support_directory.assign (device_support_dir); + m_device_support_directory.append ("/Platforms/AppleTVOS.platform/DeviceSupport"); + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory + // or it should have a valid path if the code gets here + assert (m_device_support_directory.empty() == false); + if (m_device_support_directory[0]) + return m_device_support_directory.c_str(); + return nullptr; +} + +const char * +PlatformRemoteAppleTV::GetDeviceSupportDirectoryForOSVersion() +{ + if (m_sdk_sysroot) + return m_sdk_sysroot.GetCString(); + + if (m_device_support_directory_for_os_version.empty()) + { + const PlatformRemoteAppleTV::SDKDirectoryInfo *sdk_dir_info = GetSDKDirectoryForCurrentOSVersion (); + if (sdk_dir_info == nullptr) + sdk_dir_info = GetSDKDirectoryForLatestOSVersion (); + if (sdk_dir_info) + { + char path[PATH_MAX]; + if (sdk_dir_info->directory.GetPath(path, sizeof(path))) + { + m_device_support_directory_for_os_version = path; + return m_device_support_directory_for_os_version.c_str(); + } + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory_for_os_version.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory_for_os_version + // or it should have a valid path if the code gets here + assert (m_device_support_directory_for_os_version.empty() == false); + if (m_device_support_directory_for_os_version[0]) + return m_device_support_directory_for_os_version.c_str(); + return nullptr; +} + +uint32_t +PlatformRemoteAppleTV::FindFileInAllSDKs (const char *platform_file_path, + FileSpecList &file_list) +{ + if (platform_file_path && platform_file_path[0] && UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + lldb_private::FileSpec local_file; + // First try for an exact match of major, minor and update + for (uint32_t sdk_idx=0; sdk_idxSetPlatformFileSpec(platform_file); + + return error; +} + +bool +PlatformRemoteAppleTV::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + ArchSpec system_arch (GetSystemArchitecture()); + + const ArchSpec::Core system_core = system_arch.GetCore(); + switch (system_core) + { + default: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-tvos"); return true; + case 1: arch.SetTriple ("armv7s-apple-tvos"); return true; + case 2: arch.SetTriple ("armv7-apple-tvos"); return true; + case 3: arch.SetTriple ("thumbv7s-apple-tvos"); return true; + case 4: arch.SetTriple ("thumbv7-apple-tvos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_arm64: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-tvos"); return true; + case 1: arch.SetTriple ("armv7s-apple-tvos"); return true; + case 2: arch.SetTriple ("armv7-apple-tvos"); return true; + case 3: arch.SetTriple ("thumbv7s-apple-tvos"); return true; + case 4: arch.SetTriple ("thumbv7-apple-tvos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7s: + switch (idx) + { + case 0: arch.SetTriple ("armv7s-apple-tvos"); return true; + case 1: arch.SetTriple ("armv7-apple-tvos"); return true; + case 2: arch.SetTriple ("thumbv7s-apple-tvos"); return true; + case 3: arch.SetTriple ("thumbv7-apple-tvos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7: + switch (idx) + { + case 0: arch.SetTriple ("armv7-apple-tvos"); return true; + case 1: arch.SetTriple ("thumbv7-apple-tvos"); return true; + default: break; + } + break; + } + arch.Clear(); + return false; +} + +uint32_t +PlatformRemoteAppleTV::GetConnectedSDKIndex () +{ + if (IsConnected()) + { + if (m_connected_module_sdk_idx == UINT32_MAX) + { + std::string build; + if (GetRemoteOSBuildString(build)) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" + +#include "PlatformDarwin.h" + +class PlatformRemoteAppleTV : public PlatformDarwin +{ +public: + PlatformRemoteAppleTV(); + + ~PlatformRemoteAppleTV() override = default; + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneOS); + } + +protected: + struct SDKDirectoryInfo + { + SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir_spec); + lldb_private::FileSpec directory; + lldb_private::ConstString build; + uint32_t version_major; + uint32_t version_minor; + uint32_t version_update; + bool user_cached; + }; + typedef std::vector SDKDirectoryInfoCollection; + SDKDirectoryInfoCollection m_sdk_directory_infos; + std::string m_device_support_directory; + std::string m_device_support_directory_for_os_version; + std::string m_build_update; + uint32_t m_last_module_sdk_idx; + uint32_t m_connected_module_sdk_idx; + + bool + UpdateSDKDirectoryInfosIfNeeded(); + + const char * + GetDeviceSupportDirectory(); + + const char * + GetDeviceSupportDirectoryForOSVersion(); + + const SDKDirectoryInfo * + GetSDKDirectoryForLatestOSVersion (); + + const SDKDirectoryInfo * + GetSDKDirectoryForCurrentOSVersion (); + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetContainedFilesIntoVectorOfStringsCallback (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + uint32_t + FindFileInAllSDKs (const char *platform_file_path, + lldb_private::FileSpecList &file_list); + + bool + GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file); + + bool + GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file); + + uint32_t + FindFileInAllSDKs (const lldb_private::FileSpec &platform_file, + lldb_private::FileSpecList &file_list); + + uint32_t + GetConnectedSDKIndex (); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformRemoteAppleTV); +}; + +#endif // liblldb_PlatformRemoteAppleTV_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.cpp b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.cpp new file mode 100644 index 000000000000..808fd96a5284 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.cpp @@ -0,0 +1,944 @@ +//===-- PlatformRemoteAppleWatch.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 +#include + +// Other libraries and framework includes +// Project includes +#include "PlatformRemoteAppleWatch.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformRemoteAppleWatch::PlatformRemoteAppleWatch () : + PlatformDarwin (false), // This is a remote platform + m_sdk_directory_infos(), + m_device_support_directory(), + m_device_support_directory_for_os_version (), + m_build_update(), + m_last_module_sdk_idx (UINT32_MAX), + m_connected_module_sdk_idx (UINT32_MAX) +{ +} + +PlatformRemoteAppleWatch::SDKDirectoryInfo::SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir) : + directory(sdk_dir), + build(), + version_major(0), + version_minor(0), + version_update(0), + user_cached(false) +{ + const char *dirname_cstr = sdk_dir.GetFilename().GetCString(); + const char *pos = Args::StringToVersion (dirname_cstr, + version_major, + version_minor, + version_update); + + if (pos && pos[0] == ' ' && pos[1] == '(') + { + const char *build_start = pos + 2; + const char *end_paren = strchr (build_start, ')'); + if (end_paren && build_start < end_paren) + build.SetCStringWithLength(build_start, end_paren - build_start); + } +} + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformRemoteAppleWatch::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformRemoteAppleWatch::GetPluginNameStatic(), + PlatformRemoteAppleWatch::GetDescriptionStatic(), + PlatformRemoteAppleWatch::CreateInstance); + } +} + +void +PlatformRemoteAppleWatch::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformRemoteAppleWatch::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformRemoteAppleWatch::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformRemoteAppleWatch::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (!create && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::thumb: + { + const llvm::Triple &triple = arch->GetTriple(); + llvm::Triple::VendorType vendor = triple.getVendor(); + switch (vendor) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + +#endif + default: + break; + } + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::WatchOS: // This is the right triple value for Apple Watch debugging + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + +#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) + // If lldb is running on a watch, this isn't a RemoteWatch environment; it's a local system environment. + if (force == false) + { + create = false; + } +#endif + + if (create) + { + if (log) + log->Printf ("PlatformRemoteAppleWatch::%s() creating platform", __FUNCTION__); + + return lldb::PlatformSP(new PlatformRemoteAppleWatch ()); + } + + if (log) + log->Printf ("PlatformRemoteAppleWatch::%s() aborting creation of platform", __FUNCTION__); + + return lldb::PlatformSP(); +} + +lldb_private::ConstString +PlatformRemoteAppleWatch::GetPluginNameStatic () +{ + static ConstString g_name("remote-watchos"); + return g_name; +} + +const char * +PlatformRemoteAppleWatch::GetDescriptionStatic() +{ + return "Remote Apple Watch platform plug-in."; +} + +void +PlatformRemoteAppleWatch::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetDeviceSupportDirectoryForOSVersion(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; iGetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +FileSpec::EnumerateDirectoryResult +PlatformRemoteAppleWatch::GetContainedFilesIntoVectorOfStringsCallback (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + ((PlatformRemoteAppleWatch::SDKDirectoryInfoCollection *)baton)->push_back(PlatformRemoteAppleWatch::SDKDirectoryInfo(file_spec)); + return FileSpec::eEnumerateDirectoryResultNext; +} + +bool +PlatformRemoteAppleWatch::UpdateSDKDirectoryInfosIfNeeded() +{ + if (m_sdk_directory_infos.empty()) + { + const char *device_support_dir = GetDeviceSupportDirectory(); + if (device_support_dir) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + + SDKDirectoryInfoCollection builtin_sdk_directory_infos; + FileSpec::EnumerateDirectory (m_device_support_directory.c_str(), + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &builtin_sdk_directory_infos); + + // Only add SDK directories that have symbols in them, some SDKs only contain + // developer disk images and no symbols, so they aren't useful to us. + FileSpec sdk_symbols_symlink_fspec; + for (const auto &sdk_directory_info : builtin_sdk_directory_infos) + { + sdk_symbols_symlink_fspec = sdk_directory_info.directory; + sdk_symbols_symlink_fspec.AppendPathComponent("Symbols.Internal"); + if (sdk_symbols_symlink_fspec.Exists()) + { + m_sdk_directory_infos.push_back(sdk_directory_info); + } + else + { + sdk_symbols_symlink_fspec.GetFilename().SetCString("Symbols"); + if (sdk_symbols_symlink_fspec.Exists()) + m_sdk_directory_infos.push_back(sdk_directory_info); + } + } + + const uint32_t num_installed = m_sdk_directory_infos.size(); + FileSpec local_sdk_cache("~/Library/Developer/Xcode/watchOS DeviceSupport", true); + if (!local_sdk_cache.Exists()) + { + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/watch OS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/WatchOS DeviceSupport", true); + } + if (!local_sdk_cache.Exists()) + { + local_sdk_cache = FileSpec("~/Library/Developer/Xcode/Watch OS DeviceSupport", true); + } + if (local_sdk_cache.Exists()) + { + char path[PATH_MAX]; + if (local_sdk_cache.GetPath(path, sizeof(path))) + { + FileSpec::EnumerateDirectory (path, + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &m_sdk_directory_infos); + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=num_installed; i check_sdk_info(num_sdk_infos, true); + ConstString build(m_sdk_build); + if (build) + { + for (i=0; i result->version_major) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_major == result->version_major) + { + if (sdk_dir_info.version_minor > result->version_minor) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_minor == result->version_minor) + { + if (sdk_dir_info.version_update > result->version_update) + { + result = &sdk_dir_info; + } + } + } + } + } + } + return result; +} + +const char * +PlatformRemoteAppleWatch::GetDeviceSupportDirectory() +{ + if (m_device_support_directory.empty()) + { + const char *device_support_dir = GetDeveloperDirectory(); + if (device_support_dir) + { + m_device_support_directory.assign (device_support_dir); + m_device_support_directory.append ("/Platforms/watchOS.platform/DeviceSupport"); + FileSpec platform_device_support_dir (m_device_support_directory.c_str(), true); + if (!platform_device_support_dir.Exists()) + { + std::string alt_platform_dirname = device_support_dir; + alt_platform_dirname.append ("/Platforms/WatchOS.platform/DeviceSupport"); + FileSpec alt_platform_device_support_dir (m_device_support_directory.c_str(), true); + if (alt_platform_device_support_dir.Exists()) + { + m_device_support_directory = alt_platform_dirname; + } + } + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory + // or it should have a valid path if the code gets here + assert (m_device_support_directory.empty() == false); + if (m_device_support_directory[0]) + return m_device_support_directory.c_str(); + return nullptr; +} + +const char * +PlatformRemoteAppleWatch::GetDeviceSupportDirectoryForOSVersion() +{ + if (m_sdk_sysroot) + return m_sdk_sysroot.GetCString(); + + if (m_device_support_directory_for_os_version.empty()) + { + const PlatformRemoteAppleWatch::SDKDirectoryInfo *sdk_dir_info = GetSDKDirectoryForCurrentOSVersion (); + if (sdk_dir_info == nullptr) + sdk_dir_info = GetSDKDirectoryForLatestOSVersion (); + if (sdk_dir_info) + { + char path[PATH_MAX]; + if (sdk_dir_info->directory.GetPath(path, sizeof(path))) + { + m_device_support_directory_for_os_version = path; + return m_device_support_directory_for_os_version.c_str(); + } + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory_for_os_version.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory_for_os_version + // or it should have a valid path if the code gets here + assert (m_device_support_directory_for_os_version.empty() == false); + if (m_device_support_directory_for_os_version[0]) + return m_device_support_directory_for_os_version.c_str(); + return nullptr; +} + +uint32_t +PlatformRemoteAppleWatch::FindFileInAllSDKs (const char *platform_file_path, + FileSpecList &file_list) +{ + if (platform_file_path && platform_file_path[0] && UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + lldb_private::FileSpec local_file; + // First try for an exact match of major, minor and update + for (uint32_t sdk_idx=0; sdk_idxSetPlatformFileSpec(platform_file); + + return error; +} + +bool +PlatformRemoteAppleWatch::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + ArchSpec system_arch (GetSystemArchitecture()); + + const ArchSpec::Core system_core = system_arch.GetCore(); + switch (system_core) + { + default: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 2: arch.SetTriple ("armv7s-apple-watchos"); return true; + case 3: arch.SetTriple ("armv7-apple-watchos"); return true; + case 4: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 5: arch.SetTriple ("thumbv7-apple-watchos"); return true; + case 6: arch.SetTriple ("thumbv7s-apple-watchos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_arm64: + switch (idx) + { + case 0: arch.SetTriple ("arm64-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 2: arch.SetTriple ("armv7s-apple-watchos"); return true; + case 3: arch.SetTriple ("armv7-apple-watchos"); return true; + case 4: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 5: arch.SetTriple ("thumbv7-apple-watchos"); return true; + case 6: arch.SetTriple ("thumbv7s-apple-watchos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7k: + switch (idx) + { + case 0: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7s-apple-watchos"); return true; + case 2: arch.SetTriple ("armv7-apple-watchos"); return true; + case 3: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 4: arch.SetTriple ("thumbv7-apple-watchos"); return true; + case 5: arch.SetTriple ("thumbv7s-apple-watchos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7s: + switch (idx) + { + case 0: arch.SetTriple ("armv7s-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 2: arch.SetTriple ("armv7-apple-watchos"); return true; + case 3: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 4: arch.SetTriple ("thumbv7-apple-watchos"); return true; + case 5: arch.SetTriple ("thumbv7s-apple-watchos"); return true; + default: break; + } + break; + + case ArchSpec::eCore_arm_armv7: + switch (idx) + { + case 0: arch.SetTriple ("armv7-apple-watchos"); return true; + case 1: arch.SetTriple ("armv7k-apple-watchos"); return true; + case 2: arch.SetTriple ("thumbv7k-apple-watchos"); return true; + case 3: arch.SetTriple ("thumbv7-apple-watchos"); return true; + default: break; + } + break; + } + arch.Clear(); + return false; +} + +uint32_t +PlatformRemoteAppleWatch::GetConnectedSDKIndex () +{ + if (IsConnected()) + { + if (m_connected_module_sdk_idx == UINT32_MAX) + { + std::string build; + if (GetRemoteOSBuildString(build)) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" + +#include "PlatformDarwin.h" + +class PlatformRemoteAppleWatch : public PlatformDarwin +{ +public: + PlatformRemoteAppleWatch(); + + ~PlatformRemoteAppleWatch() override = default; + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneOS); + } + +protected: + struct SDKDirectoryInfo + { + SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir_spec); + lldb_private::FileSpec directory; + lldb_private::ConstString build; + uint32_t version_major; + uint32_t version_minor; + uint32_t version_update; + bool user_cached; + }; + typedef std::vector SDKDirectoryInfoCollection; + SDKDirectoryInfoCollection m_sdk_directory_infos; + std::string m_device_support_directory; + std::string m_device_support_directory_for_os_version; + std::string m_build_update; + uint32_t m_last_module_sdk_idx; + uint32_t m_connected_module_sdk_idx; + + bool + UpdateSDKDirectoryInfosIfNeeded(); + + const char * + GetDeviceSupportDirectory(); + + const char * + GetDeviceSupportDirectoryForOSVersion(); + + const SDKDirectoryInfo * + GetSDKDirectoryForLatestOSVersion (); + + const SDKDirectoryInfo * + GetSDKDirectoryForCurrentOSVersion (); + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetContainedFilesIntoVectorOfStringsCallback (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + uint32_t + FindFileInAllSDKs (const char *platform_file_path, + lldb_private::FileSpecList &file_list); + + bool + GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file); + + bool + GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file); + + uint32_t + FindFileInAllSDKs (const lldb_private::FileSpec &platform_file, + lldb_private::FileSpecList &file_list); + + uint32_t + GetConnectedSDKIndex (); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformRemoteAppleWatch); +}; + +#endif // liblldb_PlatformRemoteAppleWatch_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp b/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp new file mode 100644 index 000000000000..75afa9019dcd --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp @@ -0,0 +1,974 @@ +//===-- PlatformRemoteiOS.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformRemoteiOS.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +PlatformRemoteiOS::SDKDirectoryInfo::SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir) : + directory(sdk_dir), + build(), + version_major(0), + version_minor(0), + version_update(0), + user_cached(false) +{ + const char *dirname_cstr = sdk_dir.GetFilename().GetCString(); + const char *pos = Args::StringToVersion (dirname_cstr, + version_major, + version_minor, + version_update); + + if (pos && pos[0] == ' ' && pos[1] == '(') + { + const char *build_start = pos + 2; + const char *end_paren = strchr (build_start, ')'); + if (end_paren && build_start < end_paren) + build.SetCStringWithLength(build_start, end_paren - build_start); + } +} + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformRemoteiOS::Initialize () +{ + PlatformDarwin::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformRemoteiOS::GetPluginNameStatic(), + PlatformRemoteiOS::GetDescriptionStatic(), + PlatformRemoteiOS::CreateInstance); + } +} + +void +PlatformRemoteiOS::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformRemoteiOS::CreateInstance); + } + } + + PlatformDarwin::Terminate (); +} + +PlatformSP +PlatformRemoteiOS::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformRemoteiOS::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::thumb: + { + const llvm::Triple &triple = arch->GetTriple(); + llvm::Triple::VendorType vendor = triple.getVendor(); + switch (vendor) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + +#endif + default: + break; + } + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Darwin: // Deprecated, but still support Darwin for historical reasons + case llvm::Triple::IOS: // This is the right triple value for iOS debugging + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + + if (create) + { + if (log) + log->Printf ("PlatformRemoteiOS::%s() creating platform", __FUNCTION__); + + return lldb::PlatformSP(new PlatformRemoteiOS ()); + } + + if (log) + log->Printf ("PlatformRemoteiOS::%s() aborting creation of platform", __FUNCTION__); + + return lldb::PlatformSP(); +} + + +lldb_private::ConstString +PlatformRemoteiOS::GetPluginNameStatic () +{ + static ConstString g_name("remote-ios"); + return g_name; +} + +const char * +PlatformRemoteiOS::GetDescriptionStatic() +{ + return "Remote iOS platform plug-in."; +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformRemoteiOS::PlatformRemoteiOS () : + PlatformDarwin (false), // This is a remote platform + m_sdk_directory_infos(), + m_device_support_directory(), + m_device_support_directory_for_os_version (), + m_build_update(), + m_last_module_sdk_idx (UINT32_MAX), + m_connected_module_sdk_idx (UINT32_MAX) +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformRemoteiOS::~PlatformRemoteiOS() +{ +} + + +void +PlatformRemoteiOS::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetDeviceSupportDirectoryForOSVersion(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; iGetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +FileSpec::EnumerateDirectoryResult +PlatformRemoteiOS::GetContainedFilesIntoVectorOfStringsCallback (void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec) +{ + ((PlatformRemoteiOS::SDKDirectoryInfoCollection *)baton)->push_back(PlatformRemoteiOS::SDKDirectoryInfo(file_spec)); + return FileSpec::eEnumerateDirectoryResultNext; +} + +bool +PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (m_sdk_directory_infos.empty()) + { + // A --sysroot option was supplied - add it to our list of SDKs to check + if (m_sdk_sysroot) + { + FileSpec sdk_sysroot_fspec(m_sdk_sysroot.GetCString(), true); + const SDKDirectoryInfo sdk_sysroot_directory_info(sdk_sysroot_fspec); + m_sdk_directory_infos.push_back(sdk_sysroot_directory_info); + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded added --sysroot SDK directory %s", m_sdk_sysroot.GetCString()); + } + return true; + } + const char *device_support_dir = GetDeviceSupportDirectory(); + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded Got DeviceSupport directory %s", device_support_dir); + } + if (device_support_dir) + { + const bool find_directories = true; + const bool find_files = false; + const bool find_other = false; + + SDKDirectoryInfoCollection builtin_sdk_directory_infos; + FileSpec::EnumerateDirectory (m_device_support_directory.c_str(), + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &builtin_sdk_directory_infos); + + // Only add SDK directories that have symbols in them, some SDKs only contain + // developer disk images and no symbols, so they aren't useful to us. + FileSpec sdk_symbols_symlink_fspec; + for (const auto &sdk_directory_info : builtin_sdk_directory_infos) + { + sdk_symbols_symlink_fspec = sdk_directory_info.directory; + sdk_symbols_symlink_fspec.AppendPathComponent("Symbols"); + if (sdk_symbols_symlink_fspec.Exists()) + { + m_sdk_directory_infos.push_back(sdk_directory_info); + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded added builtin SDK directory %s", sdk_symbols_symlink_fspec.GetPath().c_str()); + } + } + } + + const uint32_t num_installed = m_sdk_directory_infos.size(); + FileSpec local_sdk_cache("~/Library/Developer/Xcode/iOS DeviceSupport", true); + if (local_sdk_cache.Exists()) + { + if (log) + { + log->Printf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded searching %s for additional SDKs", local_sdk_cache.GetPath().c_str()); + } + char path[PATH_MAX]; + if (local_sdk_cache.GetPath(path, sizeof(path))) + { + FileSpec::EnumerateDirectory (path, + find_directories, + find_files, + find_other, + GetContainedFilesIntoVectorOfStringsCallback, + &m_sdk_directory_infos); + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + // First try for an exact match of major, minor and update + for (uint32_t i=num_installed; iPrintf ("PlatformRemoteiOS::UpdateSDKDirectoryInfosIfNeeded user SDK directory %s", m_sdk_directory_infos[i].directory.GetPath().c_str()); + } + } + } + } + } + } + return !m_sdk_directory_infos.empty(); +} + +const PlatformRemoteiOS::SDKDirectoryInfo * +PlatformRemoteiOS::GetSDKDirectoryForCurrentOSVersion () +{ + uint32_t i; + if (UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + + // Check to see if the user specified a build string. If they did, then + // be sure to match it. + std::vector check_sdk_info(num_sdk_infos, true); + ConstString build(m_sdk_build); + if (build) + { + for (i=0; i result->version_major) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_major == result->version_major) + { + if (sdk_dir_info.version_minor > result->version_minor) + { + result = &sdk_dir_info; + } + else if (sdk_dir_info.version_minor == result->version_minor) + { + if (sdk_dir_info.version_update > result->version_update) + { + result = &sdk_dir_info; + } + } + } + } + } + } + return result; +} + + + +const char * +PlatformRemoteiOS::GetDeviceSupportDirectory() +{ + if (m_device_support_directory.empty()) + { + const char *device_support_dir = GetDeveloperDirectory(); + if (device_support_dir) + { + m_device_support_directory.assign (device_support_dir); + m_device_support_directory.append ("/Platforms/iPhoneOS.platform/DeviceSupport"); + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory + // or it should have a valid path if the code gets here + assert (m_device_support_directory.empty() == false); + if (m_device_support_directory[0]) + return m_device_support_directory.c_str(); + return NULL; +} + + +const char * +PlatformRemoteiOS::GetDeviceSupportDirectoryForOSVersion() +{ + if (m_sdk_sysroot) + return m_sdk_sysroot.GetCString(); + + if (m_device_support_directory_for_os_version.empty()) + { + const PlatformRemoteiOS::SDKDirectoryInfo *sdk_dir_info = GetSDKDirectoryForCurrentOSVersion (); + if (sdk_dir_info == NULL) + sdk_dir_info = GetSDKDirectoryForLatestOSVersion (); + if (sdk_dir_info) + { + char path[PATH_MAX]; + if (sdk_dir_info->directory.GetPath(path, sizeof(path))) + { + m_device_support_directory_for_os_version = path; + return m_device_support_directory_for_os_version.c_str(); + } + } + else + { + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_device_support_directory_for_os_version.assign (1, '\0'); + } + } + // We should have put a single NULL character into m_device_support_directory_for_os_version + // or it should have a valid path if the code gets here + assert (m_device_support_directory_for_os_version.empty() == false); + if (m_device_support_directory_for_os_version[0]) + return m_device_support_directory_for_os_version.c_str(); + return NULL; +} + +uint32_t +PlatformRemoteiOS::FindFileInAllSDKs (const char *platform_file_path, + FileSpecList &file_list) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | LIBLLDB_LOG_VERBOSE); + if (platform_file_path && platform_file_path[0] && UpdateSDKDirectoryInfosIfNeeded()) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + lldb_private::FileSpec local_file; + // First try for an exact match of major, minor and update + for (uint32_t sdk_idx=0; sdk_idxPrintf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, + sdk_idx, + local_file)) + { + file_list.Append(local_file); + } + } + } + return file_list.GetSize(); +} + +bool +PlatformRemoteiOS::GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file) +{ + if (sdk_idx < m_sdk_directory_infos.size()) + { + char sdkroot_path[PATH_MAX]; + const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[sdk_idx]; + if (sdk_dir_info.directory.GetPath(sdkroot_path, sizeof(sdkroot_path))) + { + const bool symbols_dirs_only = true; + + return GetFileInSDKRoot (platform_file_path, + sdkroot_path, + symbols_dirs_only, + local_file); + } + } + return false; +} + + +bool +PlatformRemoteiOS::GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (sdkroot_path && sdkroot_path[0] && platform_file_path && platform_file_path[0]) + { + char resolved_path[PATH_MAX]; + + if (!symbols_dirs_only) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the SDK dir %s", platform_file_path, sdkroot_path); + } + return true; + } + } + + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols.Internal%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the SDK dir %s/Symbols.Internal", platform_file_path, sdkroot_path); + } + return true; + } + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols%s", + sdkroot_path, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the SDK dir %s/Symbols", platform_file_path, sdkroot_path); + } + return true; + } + } + return false; +} + + +Error +PlatformRemoteiOS::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * os_version_dir = GetDeviceSupportDirectoryForOSVersion(); + if (os_version_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the DeviceSupport dir %s", platform_file_path, os_version_dir); + } + return error; + } + + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols.Internal/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the DeviceSupport dir %s/Symbols.Internal", platform_file_path, os_version_dir); + } + return error; + } + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/Symbols/%s", + os_version_dir, + platform_file_path); + + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + { + if (log) + { + log->Printf ("Found a copy of %s in the DeviceSupport dir %s/Symbols", platform_file_path, os_version_dir); + } + return error; + } + + } + local_file = platform_file; + if (local_file.Exists()) + return error; + + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformRemoteiOS::GetSharedModule (const ModuleSpec &module_spec, + Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For iOS, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + const FileSpec &platform_file = module_spec.GetFileSpec(); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | LIBLLDB_LOG_VERBOSE); + + Error error; + char platform_file_path[PATH_MAX]; + + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + ModuleSpec platform_module_spec(module_spec); + + UpdateSDKDirectoryInfosIfNeeded(); + + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + + // If we are connected we migth be able to correctly deduce the SDK directory + // using the OS build. + const uint32_t connected_sdk_idx = GetConnectedSDKIndex (); + if (connected_sdk_idx < num_sdk_infos) + { + if (log) + { + log->Printf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[connected_sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, connected_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable (platform_module_spec, + module_sp, + NULL); + if (module_sp) + { + m_last_module_sdk_idx = connected_sdk_idx; + error.Clear(); + return error; + } + } + } + + // Try the last SDK index if it is set as most files from an SDK + // will tend to be valid in that same SDK. + if (m_last_module_sdk_idx < num_sdk_infos) + { + if (log) + { + log->Printf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[m_last_module_sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, m_last_module_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable (platform_module_spec, + module_sp, + NULL); + if (module_sp) + { + error.Clear(); + return error; + } + } + } + + // First try for an exact match of major, minor and update: + // If a particalar SDK version was specified via --version or --build, look for a match on disk. + const SDKDirectoryInfo *current_sdk_info = GetSDKDirectoryForCurrentOSVersion(); + const uint32_t current_sdk_idx = GetSDKIndexBySDKDirectoryInfo(current_sdk_info); + if (current_sdk_idx < num_sdk_infos && current_sdk_idx != m_last_module_sdk_idx) + { + if (log) + { + log->Printf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[current_sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, current_sdk_idx, platform_module_spec.GetFileSpec())) + { + module_sp.reset(); + error = ResolveExecutable (platform_module_spec, + module_sp, + NULL); + if (module_sp) + { + m_last_module_sdk_idx = current_sdk_idx; + error.Clear(); + return error; + } + } + } + + // Second try all SDKs that were found. + for (uint32_t sdk_idx=0; sdk_idxPrintf ("Searching for %s in sdk path %s", platform_file_path, m_sdk_directory_infos[sdk_idx].directory.GetPath().c_str()); + } + if (GetFileInSDK (platform_file_path, sdk_idx, platform_module_spec.GetFileSpec())) + { + //printf ("sdk[%u]: '%s'\n", sdk_idx, local_file.GetPath().c_str()); + + error = ResolveExecutable (platform_module_spec, module_sp, NULL); + if (module_sp) + { + // Remember the index of the last SDK that we found a file + // in in case the wrong SDK was selected. + m_last_module_sdk_idx = sdk_idx; + error.Clear(); + return error; + } + } + } + } + // Not the module we are looking for... Nothing to see here... + module_sp.reset(); + + // This may not be an SDK-related module. Try whether we can bring in the thing to our local cache. + error = GetSharedModuleWithLocalCache(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + if (error.Success()) + return error; + + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + +bool +PlatformRemoteiOS::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + return ARMGetSupportedArchitectureAtIndex (idx, arch); +} + +uint32_t +PlatformRemoteiOS::GetConnectedSDKIndex () +{ + if (IsConnected()) + { + if (m_connected_module_sdk_idx == UINT32_MAX) + { + std::string build; + if (GetRemoteOSBuildString(build)) + { + const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); + for (uint32_t i=0; i + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "PlatformDarwin.h" + +class PlatformRemoteiOS : public PlatformDarwin +{ +public: + PlatformRemoteiOS (); + + ~PlatformRemoteiOS() override; + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneOS); + } + +protected: + struct SDKDirectoryInfo + { + SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir_spec); + lldb_private::FileSpec directory; + lldb_private::ConstString build; + uint32_t version_major; + uint32_t version_minor; + uint32_t version_update; + bool user_cached; + }; + + typedef std::vector SDKDirectoryInfoCollection; + + SDKDirectoryInfoCollection m_sdk_directory_infos; + std::string m_device_support_directory; + std::string m_device_support_directory_for_os_version; + std::string m_build_update; + uint32_t m_last_module_sdk_idx; + uint32_t m_connected_module_sdk_idx; + + bool + UpdateSDKDirectoryInfosIfNeeded(); + + const char * + GetDeviceSupportDirectory(); + + const char * + GetDeviceSupportDirectoryForOSVersion(); + + const SDKDirectoryInfo * + GetSDKDirectoryForLatestOSVersion (); + + const SDKDirectoryInfo * + GetSDKDirectoryForCurrentOSVersion (); + + static lldb_private::FileSpec::EnumerateDirectoryResult + GetContainedFilesIntoVectorOfStringsCallback (void *baton, + lldb_private::FileSpec::FileType file_type, + const lldb_private::FileSpec &file_spec); + + uint32_t + FindFileInAllSDKs (const char *platform_file_path, + lldb_private::FileSpecList &file_list); + + bool + GetFileInSDK (const char *platform_file_path, + uint32_t sdk_idx, + lldb_private::FileSpec &local_file); + + bool + GetFileInSDKRoot (const char *platform_file_path, + const char *sdkroot_path, + bool symbols_dirs_only, + lldb_private::FileSpec &local_file); + + uint32_t + FindFileInAllSDKs (const lldb_private::FileSpec &platform_file, + lldb_private::FileSpecList &file_list); + + uint32_t + GetConnectedSDKIndex (); + + // Get index of SDK in SDKDirectoryInfoCollection by its pointer and return UINT32_MAX if that SDK not found. + uint32_t + GetSDKIndexBySDKDirectoryInfo (const SDKDirectoryInfo *sdk_info); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformRemoteiOS); +}; + +#endif // liblldb_PlatformRemoteiOS_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp b/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp new file mode 100644 index 000000000000..cbe9c7949a4a --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp @@ -0,0 +1,502 @@ +//===-- PlatformiOSSimulator.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformiOSSimulator.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Static Variables +//------------------------------------------------------------------ +static uint32_t g_initialize_count = 0; + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +void +PlatformiOSSimulator::Initialize () +{ + PlatformAppleSimulator::Initialize (); + + if (g_initialize_count++ == 0) + { + PluginManager::RegisterPlugin (PlatformiOSSimulator::GetPluginNameStatic(), + PlatformiOSSimulator::GetDescriptionStatic(), + PlatformiOSSimulator::CreateInstance); + } +} + +void +PlatformiOSSimulator::Terminate () +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { + PluginManager::UnregisterPlugin (PlatformiOSSimulator::CreateInstance); + } + } + + PlatformAppleSimulator::Terminate (); +} + +PlatformSP +PlatformiOSSimulator::CreateInstance (bool force, const ArchSpec *arch) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + { + const char *arch_name; + if (arch && arch->GetArchitectureName ()) + arch_name = arch->GetArchitectureName (); + else + arch_name = ""; + + const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; + + log->Printf ("PlatformiOSSimulator::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + switch (arch->GetMachine()) + { + case llvm::Triple::x86_64: + case llvm::Triple::x86: + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::Apple: + create = true; + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the vendor if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Darwin: // Deprecated, but still support Darwin for historical reasons + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: // IOS is not used for simulator triples, but accept it just in case + break; + +#if defined(__APPLE__) + // Only accept "unknown" for the OS if the host is Apple and + // it "unknown" wasn't specified (it was just returned because it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + break; + default: + break; + } + } + if (create) + { + if (log) + log->Printf ("PlatformiOSSimulator::%s() creating platform", __FUNCTION__); + + return PlatformSP(new PlatformiOSSimulator ()); + } + + if (log) + log->Printf ("PlatformiOSSimulator::%s() aborting creation of platform", __FUNCTION__); + + return PlatformSP(); +} + + +lldb_private::ConstString +PlatformiOSSimulator::GetPluginNameStatic () +{ + static ConstString g_name("ios-simulator"); + return g_name; +} + +const char * +PlatformiOSSimulator::GetDescriptionStatic() +{ + return "iOS simulator platform plug-in."; +} + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformiOSSimulator::PlatformiOSSimulator () : +PlatformAppleSimulator (), +m_sdk_directory () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformiOSSimulator::~PlatformiOSSimulator() +{ +} + + +void +PlatformiOSSimulator::GetStatus (Stream &strm) +{ + Platform::GetStatus (strm); + const char *sdk_directory = GetSDKDirectoryAsCString(); + if (sdk_directory) + strm.Printf (" SDK Path: \"%s\"\n", sdk_directory); + else + strm.PutCString (" SDK Path: error: unable to locate SDK\n"); + PlatformAppleSimulator::GetStatus(strm); +} + + +Error +PlatformiOSSimulator::ResolveExecutable (const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + ModuleSpec resolved_module_spec(module_spec); + + // If we have "ls" as the exe_file, resolve the executable loation based on + // the current path variables + // TODO: resolve bare executables in the Platform SDK + // if (!resolved_exe_file.Exists()) + // resolved_exe_file.ResolveExecutableLocation (); + + // Resolve any executable within a bundle on MacOSX + // TODO: verify that this handles shallow bundles, if not then implement one ourselves + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); + + if (resolved_module_spec.GetFileSpec().Exists()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + + if (exe_module_sp && exe_module_sp->GetObjectFile()) + return error; + exe_module_sp.reset(); + } + // No valid architecture was specified or the exact ARM slice wasn't + // found so ask the platform for the architectures that we should be + // using (in the correct order) and see if we can find a match that way + StreamString arch_names; + ArchSpec platform_arch; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + // Only match x86 with x86 and x86_64 with x86_64... + if (!module_spec.GetArchitecture().IsValid() || module_spec.GetArchitecture().GetCore() == resolved_module_spec.GetArchitecture().GetCore()) + { + error = ModuleList::GetSharedModule (resolved_module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (platform_arch.GetArchitectureName()); + } + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + module_spec.GetFileSpec().GetPath().c_str()); + } + + return error; +} + +static FileSpec::EnumerateDirectoryResult +EnumerateDirectoryCallback (void *baton, FileSpec::FileType file_type, const FileSpec &file_spec) +{ + if (file_type == FileSpec::eFileTypeDirectory) + { + const char *filename = file_spec.GetFilename().GetCString(); + if (filename && strncmp(filename, "iPhoneSimulator", strlen ("iPhoneSimulator")) == 0) + { + ::snprintf ((char *)baton, PATH_MAX, "%s", filename); + return FileSpec::eEnumerateDirectoryResultQuit; + } + } + return FileSpec::eEnumerateDirectoryResultNext; +} + + + +const char * +PlatformiOSSimulator::GetSDKDirectoryAsCString() +{ + Mutex::Locker locker (m_mutex); + if (m_sdk_directory.empty()) + { + const char *developer_dir = GetDeveloperDirectory(); + if (developer_dir) + { + char sdks_directory[PATH_MAX]; + char sdk_dirname[PATH_MAX]; + sdk_dirname[0] = '\0'; + snprintf (sdks_directory, + sizeof(sdks_directory), + "%s/Platforms/iPhoneSimulator.platform/Developer/SDKs", + developer_dir); + FileSpec simulator_sdk_spec; + bool find_directories = true; + bool find_files = false; + bool find_other = false; + FileSpec::EnumerateDirectory (sdks_directory, + find_directories, + find_files, + find_other, + EnumerateDirectoryCallback, + sdk_dirname); + + if (sdk_dirname[0]) + { + m_sdk_directory = sdks_directory; + m_sdk_directory.append (1, '/'); + m_sdk_directory.append (sdk_dirname); + return m_sdk_directory.c_str(); + } + } + // Assign a single NULL character so we know we tried to find the device + // support directory and we don't keep trying to find it over and over. + m_sdk_directory.assign (1, '\0'); + } + + // We should have put a single NULL character into m_sdk_directory + // or it should have a valid path if the code gets here + assert (m_sdk_directory.empty() == false); + if (m_sdk_directory[0]) + return m_sdk_directory.c_str(); + return NULL; +} + +Error +PlatformiOSSimulator::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + Error error; + char platform_file_path[PATH_MAX]; + if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path))) + { + char resolved_path[PATH_MAX]; + + const char * sdk_dir = GetSDKDirectoryAsCString(); + if (sdk_dir) + { + ::snprintf (resolved_path, + sizeof(resolved_path), + "%s/%s", + sdk_dir, + platform_file_path); + + // First try in the SDK and see if the file is in there + local_file.SetFile(resolved_path, true); + if (local_file.Exists()) + return error; + + // Else fall back to the actual path itself + local_file.SetFile(platform_file_path, true); + if (local_file.Exists()) + return error; + + } + error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'", + platform_file_path, + GetPluginName().GetCString()); + } + else + { + error.SetErrorString ("invalid platform file argument"); + } + return error; +} + +Error +PlatformiOSSimulator::GetSharedModule (const ModuleSpec &module_spec, + Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + // For iOS, the SDK files are all cached locally on the host + // system. So first we ask for the file in the cached SDK, + // then we attempt to get a shared module for the right architecture + // with the right UUID. + Error error; + ModuleSpec platform_module_spec (module_spec); + const FileSpec &platform_file = module_spec.GetFileSpec(); + error = GetSymbolFile (platform_file, module_spec.GetUUIDPtr(), platform_module_spec.GetFileSpec()); + if (error.Success()) + { + error = ResolveExecutable (platform_module_spec, module_sp, module_search_paths_ptr); + } + else + { + const bool always_create = false; + error = ModuleList::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr, + always_create); + + } + if (module_sp) + module_sp->SetPlatformFileSpec(platform_file); + + return error; +} + + +uint32_t +PlatformiOSSimulator::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + ProcessInstanceInfoList all_osx_process_infos; + // First we get all OSX processes + const uint32_t n = Host::FindProcesses (match_info, all_osx_process_infos); + + // Now we filter them down to only the iOS triples + for (uint32_t i=0; i + +// Other libraries and framework includes +// Project includes +#include "PlatformAppleSimulator.h" + +class PlatformiOSSimulator : public PlatformAppleSimulator +{ +public: + PlatformiOSSimulator (); + + ~PlatformiOSSimulator() override; + + //------------------------------------------------------------ + // Class Functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetDescriptionStatic(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override + { + return GetPluginNameStatic(); + } + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + lldb_private::Error + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription () override + { + return GetDescriptionStatic(); + } + + void + GetStatus (lldb_private::Stream &strm) override; + + virtual lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos) override; + + bool + GetSupportedArchitectureAtIndex (uint32_t idx, + lldb_private::ArchSpec &arch) override; + + void + AddClangModuleCompilationOptions (lldb_private::Target *target, std::vector &options) override + { + return PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(target, options, PlatformDarwin::SDKType::iPhoneSimulator); + } + +protected: + std::string m_sdk_directory; + std::string m_build_update; + + const char * + GetSDKDirectoryAsCString(); + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformiOSSimulator); +}; + +#endif // liblldb_PlatformiOSSimulator_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h b/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h new file mode 100644 index 000000000000..8393ea304906 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.h @@ -0,0 +1,315 @@ +//===-- PlatformiOSSimulatorCoreSimulatorSupport.h ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_ +#define liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_ + +// C Includes +// C++ Includes +#include +#include +#include +#include +// Other libraries and framework includes +#ifdef __APPLE__ +#include +#else +typedef void *id; +#endif +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Target/ProcessLaunchInfo.h" + +#include "llvm/ADT/Optional.h" + +// And now the actual magic +namespace CoreSimulatorSupport +{ + class Process + { + public: + lldb::pid_t + GetPID () + { + return m_pid; + } + + explicit operator bool () + { + return m_pid != LLDB_INVALID_PROCESS_ID; + } + + lldb_private::Error + GetError () + { + return m_error; + } + + private: + Process (lldb::pid_t p); + + Process(lldb_private::Error error); + + Process (lldb::pid_t p, lldb_private::Error error); + + lldb::pid_t m_pid; + lldb_private::Error m_error; + + friend class Device; + }; + + class ModelIdentifier { + public: + ModelIdentifier (const std::string& mi); + ModelIdentifier (); + + explicit operator bool () const + { + return !m_versions.empty(); + } + + size_t + GetNumVersions () const + { + return m_versions.size(); + } + + unsigned int + GetVersionAtIndex (size_t idx) const + { + return m_versions[idx]; + } + + std::string + GetFamily () const + { + return m_family.c_str(); + } + + private: + std::string m_family; + std::vector m_versions; + }; + + class DeviceType + { + public: + enum class ProductFamilyID : int32_t + { + iPhone = 1, + iPad = 2, + appleTV = 3, + appleWatch = 4 + }; + + DeviceType (); + + DeviceType (id d); + + explicit operator bool (); + + std::string + GetName (); + + lldb_private::ConstString + GetIdentifier (); + + ModelIdentifier + GetModelIdentifier (); + + lldb_private::ConstString + GetProductFamily (); + + ProductFamilyID + GetProductFamilyID (); + + private: + id m_dev; + llvm::Optional m_model_identifier; + }; + + class OSVersion { + public: + OSVersion (const std::string& ver, + const std::string& build); + + OSVersion (); + + explicit operator bool () const + { + return !m_versions.empty(); + } + + size_t + GetNumVersions () const + { + return m_versions.size(); + } + + unsigned int + GetVersionAtIndex (size_t idx) const + { + return m_versions[idx]; + } + + const char* + GetBuild () const + { + return m_build.c_str(); + } + + private: + std::vector m_versions; + std::string m_build; + }; + + class DeviceRuntime + { + public: + DeviceRuntime (); + + DeviceRuntime (id d); + + explicit operator bool (); + + OSVersion + GetVersion (); + + bool + IsAvailable (); + + private: + id m_dev; + llvm::Optional m_os_version; + }; + + class Device + { + private: + typedef unsigned long int NSUInteger; + + public: + enum class State : NSUInteger + { + Creating, + Shutdown, + Booting, + Booted, + ShuttingDown + }; + + Device (); + + Device (id d); + + explicit operator bool (); + + std::string + GetName () const; + + DeviceType + GetDeviceType (); + + DeviceRuntime + GetDeviceRuntime (); + + State + GetState (); + + bool + Boot (lldb_private::Error &err); + + bool + Shutdown (lldb_private::Error &err); + + std::string + GetUDID () const; + + Process + Spawn (lldb_private::ProcessLaunchInfo& launch_info); + + private: + id m_dev; + llvm::Optional m_dev_type; + llvm::Optional m_dev_runtime; + + friend class DeviceSet; + }; + + bool + operator > (const OSVersion& lhs, + const OSVersion& rhs); + + bool + operator > (const ModelIdentifier& lhs, + const ModelIdentifier& rhs); + + bool + operator < (const OSVersion& lhs, + const OSVersion& rhs); + + bool + operator < (const ModelIdentifier& lhs, + const ModelIdentifier& rhs); + + bool + operator == (const OSVersion& lhs, + const OSVersion& rhs); + + bool + operator == (const ModelIdentifier& lhs, + const ModelIdentifier& rhs); + + bool + operator != (const OSVersion& lhs, + const OSVersion& rhs); + + bool + operator != (const ModelIdentifier& lhs, + const ModelIdentifier& rhs); + + class DeviceSet + { + public: + static DeviceSet + GetAllDevices (); + + static DeviceSet + GetAvailableDevices (); + + size_t + GetNumDevices (); + + Device + GetDeviceAtIndex (size_t idx); + + void + ForEach (std::function f); + + DeviceSet + GetDevicesIf (std::function f); + + DeviceSet + GetDevices (DeviceType::ProductFamilyID dev_id); + + Device + GetFanciest (DeviceType::ProductFamilyID dev_id); + + private: + DeviceSet (id arr) : m_dev(arr) + { + } + + id m_dev; + }; +} + +#endif // liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_ diff --git a/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm b/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm new file mode 100644 index 000000000000..7075de552529 --- /dev/null +++ b/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm @@ -0,0 +1,773 @@ +//===-- PlatformiOSSimulatorCoreSimulatorSupport.cpp ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformiOSSimulatorCoreSimulatorSupport.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include +#include +// Project includes +#include "lldb/Target/FileAction.h" +#include "lldb/Utility/PseudoTerminal.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb_private; +using namespace lldb_utility; +// CoreSimulator lives as part of Xcode, which means we can't really link against it, so we dlopen() +// it at runtime, and error out nicely if that fails +@interface SimDeviceSet +{} ++ (id) defaultSet; +@end +// However, the drawback is that the compiler will not know about the selectors we're trying to use +// until runtime; to appease clang in this regard, define a fake protocol on NSObject that exposes +// the needed interface names for us +@protocol LLDBCoreSimulatorSupport +- (NSArray *) devices; +- (id) deviceType; +- (NSString *) name; +- (NSString *) identifier; +- (NSString *) modelIdentifier; +- (NSString *) productFamily; +- (int32_t) productFamilyID; +- (id) runtime; +- (BOOL) available; +- (NSString *) versionString; +- (NSString *) buildVersionString; +- (BOOL) bootWithOptions:(NSDictionary *)options error:(NSError**)error; +- (NSUInteger) state; +- (BOOL) shutdownWithError:(NSError **)error; +- (NSUUID *) UDID; +- (pid_t) spawnWithPath:(NSString *)path options:(NSDictionary *)options terminationHandler:(void (^)(int status)) terminationHandler error:(NSError **)error; +@end + +CoreSimulatorSupport::Process::Process (lldb::pid_t p) : + m_pid (p), + m_error () +{ +} + +CoreSimulatorSupport::Process::Process(Error error) : + m_pid (LLDB_INVALID_PROCESS_ID), + m_error (error) +{ +} + +CoreSimulatorSupport::Process::Process (lldb::pid_t p, Error error) : + m_pid (p), + m_error (error) +{ +} + + +CoreSimulatorSupport::DeviceType::DeviceType () : + m_dev (nil), + m_model_identifier () +{ +} + +CoreSimulatorSupport::DeviceType::DeviceType (id d) : + m_dev (d), + m_model_identifier () +{ +} + +CoreSimulatorSupport::DeviceType::operator bool () +{ + return m_dev != nil; +} + +ConstString +CoreSimulatorSupport::DeviceType::GetIdentifier () +{ + return ConstString( [[m_dev identifier] UTF8String] ); +} + +ConstString +CoreSimulatorSupport::DeviceType::GetProductFamily () +{ + return ConstString( [[m_dev productFamily] UTF8String] ); +} + +CoreSimulatorSupport::DeviceType::ProductFamilyID +CoreSimulatorSupport::DeviceType::GetProductFamilyID () +{ + return ProductFamilyID([m_dev productFamilyID]); +} + +CoreSimulatorSupport::DeviceRuntime::DeviceRuntime () : + m_dev (nil), + m_os_version () +{ +} + +CoreSimulatorSupport::DeviceRuntime::DeviceRuntime (id d) : + m_dev (d), + m_os_version () +{ +} + +CoreSimulatorSupport::DeviceRuntime::operator bool () +{ + return m_dev != nil; +} + +bool +CoreSimulatorSupport::DeviceRuntime::IsAvailable () +{ + return [m_dev available]; +} + +CoreSimulatorSupport::Device::Device () : + m_dev (nil), + m_dev_type (), + m_dev_runtime () +{ +} + +CoreSimulatorSupport::Device::Device (id d) : + m_dev (d), + m_dev_type (), + m_dev_runtime () +{ +} + +CoreSimulatorSupport::Device::operator bool () +{ + return m_dev != nil; +} + +CoreSimulatorSupport::Device::State +CoreSimulatorSupport::Device::GetState () +{ + return (State)([m_dev state]); +} + +CoreSimulatorSupport::ModelIdentifier::ModelIdentifier (const std::string& mi) : + m_family (), + m_versions () +{ + bool any = false; + bool first_digit = false; + unsigned int val = 0; + + for (char c : mi) + { + any = true; + if (::isdigit(c)) + { + if (!first_digit) + first_digit = true; + val = 10*val + (c - '0'); + } + else if (c == ',') + { + if (first_digit) + { + m_versions.push_back(val); + val = 0; + } + else + m_family.push_back(c); + } + else + { + if (first_digit) + { + m_family.clear(); + m_versions.clear(); + return; + } + else + { + m_family.push_back(c); + } + } + } + + if (first_digit) + m_versions.push_back(val); +} + +CoreSimulatorSupport::ModelIdentifier::ModelIdentifier () : +ModelIdentifier("") +{ +} + +CoreSimulatorSupport::OSVersion::OSVersion (const std::string& ver, + const std::string& build) : + m_versions (), + m_build (build) +{ + bool any = false; + unsigned int val = 0; + for (char c : ver) + { + if (c == '.') + { + m_versions.push_back(val); + val = 0; + } + else if (::isdigit(c)) + { + val = 10*val + (c - '0'); + any = true; + } + else + { + m_versions.clear(); + return; + } + } + if (any) + m_versions.push_back(val); +} + +CoreSimulatorSupport::OSVersion::OSVersion () : + OSVersion("","") +{ +} + +CoreSimulatorSupport::ModelIdentifier +CoreSimulatorSupport::DeviceType::GetModelIdentifier () +{ + if (!m_model_identifier.hasValue()) + { + auto utf8_model_id = [[m_dev modelIdentifier] UTF8String]; + if (utf8_model_id && *utf8_model_id) + m_model_identifier = ModelIdentifier (utf8_model_id); + } + + if (m_model_identifier.hasValue()) + return m_model_identifier.getValue(); + else + return ModelIdentifier(); +} + +CoreSimulatorSupport::OSVersion +CoreSimulatorSupport::DeviceRuntime::GetVersion () +{ + if (!m_os_version.hasValue()) + { + auto utf8_ver_string = [[m_dev versionString] UTF8String]; + auto utf8_build_ver = [[m_dev buildVersionString] UTF8String]; + if (utf8_ver_string && *utf8_ver_string && + utf8_build_ver && *utf8_build_ver) + { + m_os_version = OSVersion(utf8_ver_string, utf8_build_ver); + } + } + + if (m_os_version.hasValue()) + return m_os_version.getValue(); + return OSVersion(); +} + +std::string +CoreSimulatorSupport::DeviceType::GetName () +{ + auto utf8_name = [[m_dev name] UTF8String]; + if (utf8_name) + return std::string(utf8_name); + return ""; +} + +std::string +CoreSimulatorSupport::Device::GetName () const +{ + auto utf8_name = [[m_dev name] UTF8String]; + if (utf8_name) + return std::string(utf8_name); + return ""; +} + +std::string +CoreSimulatorSupport::Device::GetUDID () const +{ + auto utf8_udid = [ [[m_dev UDID] UUIDString] UTF8String]; + if (utf8_udid) + return std::string(utf8_udid); + else + return std::string(); +} + +CoreSimulatorSupport::DeviceType +CoreSimulatorSupport::Device::GetDeviceType () +{ + if (!m_dev_type.hasValue()) + m_dev_type = DeviceType([m_dev deviceType]); + + return m_dev_type.getValue(); +} + +CoreSimulatorSupport::DeviceRuntime +CoreSimulatorSupport::Device::GetDeviceRuntime () +{ + if (!m_dev_runtime.hasValue()) + m_dev_runtime = DeviceRuntime([m_dev runtime]); + + return m_dev_runtime.getValue(); +} + +bool +CoreSimulatorSupport::operator > (const CoreSimulatorSupport::OSVersion& lhs, + const CoreSimulatorSupport::OSVersion& rhs) +{ + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l > r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator > (const CoreSimulatorSupport::ModelIdentifier& lhs, + const CoreSimulatorSupport::ModelIdentifier& rhs) +{ + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l > r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator < (const CoreSimulatorSupport::OSVersion& lhs, + const CoreSimulatorSupport::OSVersion& rhs) +{ + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l < r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator < (const CoreSimulatorSupport::ModelIdentifier& lhs, + const CoreSimulatorSupport::ModelIdentifier& rhs) +{ + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l < r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator == (const CoreSimulatorSupport::OSVersion& lhs, + const CoreSimulatorSupport::OSVersion& rhs) +{ + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return false; + } + return true; +} + +bool +CoreSimulatorSupport::operator == (const CoreSimulatorSupport::ModelIdentifier& lhs, + const CoreSimulatorSupport::ModelIdentifier& rhs) +{ + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return false; + } + return true; +} + +bool +CoreSimulatorSupport::operator != (const CoreSimulatorSupport::OSVersion& lhs, + const CoreSimulatorSupport::OSVersion& rhs) +{ + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::operator != (const CoreSimulatorSupport::ModelIdentifier& lhs, + const CoreSimulatorSupport::ModelIdentifier& rhs) +{ + if (lhs.GetFamily() != rhs.GetFamily()) + return false; + + for (size_t i = 0; + i < rhs.GetNumVersions(); + i++) + { + unsigned int l = lhs.GetVersionAtIndex(i); + unsigned int r = rhs.GetVersionAtIndex(i); + if (l != r) + return true; + } + return false; +} + +bool +CoreSimulatorSupport::Device::Boot (Error &err) +{ + if (m_dev == nil) + { + err.SetErrorString("no valid simulator instance"); + return false; + } + +#define kSimDeviceBootEnv @"env" /* An NSDictionary of "extra" environment key/values */ +#define kSimDeviceBootPersist @"persist" /* An NSNumber (boolean) indicating whether or not the session should outlive the calling process (default false) */ +#define kSimDeviceBootDisabledJobs @"disabled_jobs" /* An NSDictionary of NSStrings -> NSNumbers, each string is the name of a job, and the value is the corresponding state (true if disabled) */ + + NSDictionary *options = @{ + kSimDeviceBootPersist : @NO, + kSimDeviceBootDisabledJobs : @{@"com.apple.backboardd" : @YES} + }; + +#undef kSimDeviceBootEnv +#undef kSimDeviceBootPersist +#undef kSimDeviceBootDisabledJobs + + NSError* nserror; + if ([m_dev bootWithOptions:options error:&nserror]) + { + err.Clear(); + return true; + } + else + { + err.SetErrorString([[nserror description] UTF8String]); + return false; + } +} + +bool +CoreSimulatorSupport::Device::Shutdown (Error &err) +{ + NSError* nserror; + if ([m_dev shutdownWithError:&nserror]) + { + err.Clear(); + return true; + } + else + { + err.SetErrorString([[nserror description] UTF8String]); + return false; + } +} + + +static Error +HandleFileAction(ProcessLaunchInfo& launch_info, + NSMutableDictionary *options, + NSString *key, + const int fd, + File &file) +{ + Error error; + const FileAction *file_action = launch_info.GetFileActionForFD (fd); + if (file_action) + { + switch (file_action->GetAction()) + { + case FileAction::eFileActionNone: + break; + + case FileAction::eFileActionClose: + error.SetErrorStringWithFormat ("close file action for %i not supported", fd); + break; + + case FileAction::eFileActionDuplicate: + error.SetErrorStringWithFormat ("duplication file action for %i not supported", fd); + break; + + case FileAction::eFileActionOpen: + { + FileSpec file_spec = file_action->GetFileSpec(); + if (file_spec) + { + const int master_fd = launch_info.GetPTY().GetMasterFileDescriptor(); + if (master_fd != PseudoTerminal::invalid_fd) + { + // Check in case our file action open wants to open the slave + const char *slave_path = launch_info.GetPTY().GetSlaveName(NULL, 0); + if (slave_path) + { + FileSpec slave_spec(slave_path, false); + if (file_spec == slave_spec) + { + int slave_fd = launch_info.GetPTY().GetSlaveFileDescriptor(); + if (slave_fd == PseudoTerminal::invalid_fd) + slave_fd = launch_info.GetPTY().OpenSlave(O_RDWR, nullptr, 0); + if (slave_fd == PseudoTerminal::invalid_fd) + { + error.SetErrorStringWithFormat("unable to open slave pty '%s'", slave_path); + return error; // Failure + } + [options setValue:[NSNumber numberWithInteger:slave_fd] forKey:key]; + return error; // Success + } + } + } + Error posix_error; + int created_fd = open(file_spec.GetPath().c_str(), file_action->GetActionArgument(), S_IRUSR | S_IWUSR); + if (created_fd >= 0) + { + file.SetDescriptor(created_fd, true); + [options setValue:[NSNumber numberWithInteger:created_fd] forKey:key]; + return error; // Success + } + else + { + posix_error.SetErrorToErrno(); + error.SetErrorStringWithFormat("unable to open file '%s': %s", file_spec.GetPath().c_str(), posix_error.AsCString()); + } + } + } + break; + } + } + return error; // Success, no file action, nothing to do +} + +CoreSimulatorSupport::Process +CoreSimulatorSupport::Device::Spawn (ProcessLaunchInfo& launch_info) +{ +#define kSimDeviceSpawnEnvironment @"environment" /* An NSDictionary (NSStrings -> NSStrings) of environment key/values */ +#define kSimDeviceSpawnStdin @"stdin" /* An NSNumber corresponding to a fd */ +#define kSimDeviceSpawnStdout @"stdout" /* An NSNumber corresponding to a fd */ +#define kSimDeviceSpawnStderr @"stderr" /* An NSNumber corresponding to a fd */ +#define kSimDeviceSpawnArguments @"arguments" /* An NSArray of strings to use as the argv array. If not provided, path will be argv[0] */ +#define kSimDeviceSpawnWaitForDebugger @"wait_for_debugger" /* An NSNumber (bool) */ + + NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; + + if (launch_info.GetFlags().Test(lldb::eLaunchFlagDebug)) + [options setObject:@YES forKey:kSimDeviceSpawnWaitForDebugger]; + + if (launch_info.GetArguments().GetArgumentCount()) + { + const Args& args(launch_info.GetArguments()); + NSMutableArray *args_array = [[NSMutableArray alloc] init]; + for (size_t idx = 0; + idx < args.GetArgumentCount(); + idx++) + [args_array addObject:[NSString stringWithUTF8String:args.GetArgumentAtIndex(idx)]]; + + [options setObject:args_array forKey:kSimDeviceSpawnArguments]; + } + + if (launch_info.GetEnvironmentEntries().GetArgumentCount()) + { + const Args& envs(launch_info.GetEnvironmentEntries()); + NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init]; + for (size_t idx = 0; + idx < envs.GetArgumentCount(); + idx++) + { + llvm::StringRef arg_sr(envs.GetArgumentAtIndex(idx)); + auto first_eq = arg_sr.find('='); + if (first_eq == llvm::StringRef::npos) + continue; + llvm::StringRef key = arg_sr.substr(0, first_eq); + llvm::StringRef value = arg_sr.substr(first_eq+1); + + NSString *key_ns = [NSString stringWithUTF8String:key.str().c_str()]; + NSString *value_ns = [NSString stringWithUTF8String:value.str().c_str()]; + + [env_dict setValue:value_ns forKey:key_ns]; + } + + [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment]; + } + + Error error; + File stdin_file; + File stdout_file; + File stderr_file; + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdin, STDIN_FILENO, stdin_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout, STDOUT_FILENO, stdout_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + + error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr, STDERR_FILENO, stderr_file); + + if (error.Fail()) + return CoreSimulatorSupport::Process(error); + +#undef kSimDeviceSpawnEnvironment +#undef kSimDeviceSpawnStdin +#undef kSimDeviceSpawnStdout +#undef kSimDeviceSpawnStderr +#undef kSimDeviceSpawnWaitForDebugger +#undef kSimDeviceSpawnArguments + + NSError* nserror; + + pid_t pid = [m_dev spawnWithPath: [NSString stringWithUTF8String: launch_info.GetExecutableFile().GetPath().c_str()] + options: options + terminationHandler: nil + error: &nserror]; + + + if (pid < 0) + { + const char* nserror_string = [[nserror description] UTF8String]; + error.SetErrorString(nserror_string ? nserror_string : "unable to launch"); + } + + return CoreSimulatorSupport::Process (pid, error); +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetAllDevices () +{ + return DeviceSet([[NSClassFromString(@"SimDeviceSet") defaultSet] devices]); +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetAvailableDevices () +{ + return GetAllDevices().GetDevicesIf( [] (Device d) -> bool { + return (d && d.GetDeviceType() && d.GetDeviceRuntime() && d.GetDeviceRuntime().IsAvailable()); + }); +} + +size_t +CoreSimulatorSupport::DeviceSet::GetNumDevices () +{ + return [m_dev count]; +} + +CoreSimulatorSupport::Device +CoreSimulatorSupport::DeviceSet::GetDeviceAtIndex (size_t idx) +{ + if (idx < GetNumDevices()) + return Device([m_dev objectAtIndex:idx]); + return Device(); +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetDevicesIf (std::function f) +{ + NSMutableArray *array = [[NSMutableArray alloc] init]; + for (NSUInteger i = 0; + i < GetNumDevices(); + i++) + { + Device d(GetDeviceAtIndex(i)); + if (f(d)) + [array addObject:(id)d.m_dev]; + } + + return DeviceSet(array); +} + +void +CoreSimulatorSupport::DeviceSet::ForEach (std::function f) +{ + const size_t n = GetNumDevices(); + for (NSUInteger i = 0; i < n; ++i) + { + if (f(GetDeviceAtIndex(i)) == false) + break; + } +} + +CoreSimulatorSupport::DeviceSet +CoreSimulatorSupport::DeviceSet::GetDevices (CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) +{ + NSMutableArray *array = [[NSMutableArray alloc] init]; + const size_t n = GetNumDevices(); + for (NSUInteger i = 0; i < n; ++i) + { + Device d(GetDeviceAtIndex(i)); + if (d && d.GetDeviceType() && d.GetDeviceType().GetProductFamilyID() == dev_id) + [array addObject:(id)d.m_dev]; + } + + return DeviceSet(array); +} + +CoreSimulatorSupport::Device +CoreSimulatorSupport::DeviceSet::GetFanciest (CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) +{ + Device dev; + + for (NSUInteger i = 0; + i < GetNumDevices(); + i++) + { + Device d(GetDeviceAtIndex(i)); + if (d && d.GetDeviceType() && d.GetDeviceType().GetProductFamilyID() == dev_id) + { + if (!dev) + dev = d; + else + { + if ((d.GetDeviceType().GetModelIdentifier() > dev.GetDeviceType().GetModelIdentifier()) || + d.GetDeviceRuntime().GetVersion() > dev.GetDeviceRuntime().GetVersion()) + dev = d; + } + } + } + + return dev; +} diff --git a/source/Plugins/Platform/Makefile b/source/Plugins/Platform/Makefile new file mode 100644 index 000000000000..572b08644074 --- /dev/null +++ b/source/Plugins/Platform/Makefile @@ -0,0 +1,36 @@ +##===- source/Plugins/Platform/Makefile --------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../.. + +include $(LLDB_LEVEL)/../../Makefile.config + +PARALLEL_DIRS := gdb-server MacOSX Linux FreeBSD NetBSD POSIX Windows Kalimba Android + +# ifeq ($(HOST_OS),Darwin) +# DIRS += MacOSX +# endif +# +# ifeq ($(HOST_OS),Linux) +# DIRS += Linux +# endif +# +# ifeq ($(HOST_OS),FreeBSD) +# DIRS += FreeBSD +# endif +# +# ifeq ($(HOST_OS),GNU/kFreeBSD) +# DIRS += FreeBSD +# endif +# +# ifeq ($(HOST_OS),NetBSD) +# DIRS += NetBSD +# endif + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/NetBSD/CMakeLists.txt b/source/Plugins/Platform/NetBSD/CMakeLists.txt new file mode 100644 index 000000000000..c70b419b98b4 --- /dev/null +++ b/source/Plugins/Platform/NetBSD/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformNetBSD + PlatformNetBSD.cpp + ) diff --git a/source/Plugins/Platform/NetBSD/Makefile b/source/Plugins/Platform/NetBSD/Makefile new file mode 100644 index 000000000000..2c480bdbe8da --- /dev/null +++ b/source/Plugins/Platform/NetBSD/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/NetBSD/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformNetBSD +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/POSIX/CMakeLists.txt b/source/Plugins/Platform/POSIX/CMakeLists.txt new file mode 100644 index 000000000000..c23e68155c2b --- /dev/null +++ b/source/Plugins/Platform/POSIX/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformPOSIX + PlatformPOSIX.cpp + ) diff --git a/source/Plugins/Platform/POSIX/Makefile b/source/Plugins/Platform/POSIX/Makefile new file mode 100644 index 000000000000..eca927720ba8 --- /dev/null +++ b/source/Plugins/Platform/POSIX/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/POSIX/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformPOSIX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Windows/CMakeLists.txt b/source/Plugins/Platform/Windows/CMakeLists.txt new file mode 100644 index 000000000000..09fbc11d33f2 --- /dev/null +++ b/source/Plugins/Platform/Windows/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformWindows + PlatformWindows.cpp + ) diff --git a/source/Plugins/Platform/Windows/Makefile b/source/Plugins/Platform/Windows/Makefile new file mode 100644 index 000000000000..b78cd7bfd080 --- /dev/null +++ b/source/Plugins/Platform/Windows/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/Windows/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformWindows +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Platform/Windows/PlatformWindows.cpp b/source/Plugins/Platform/Windows/PlatformWindows.cpp new file mode 100644 index 000000000000..4172f80176d3 --- /dev/null +++ b/source/Plugins/Platform/Windows/PlatformWindows.cpp @@ -0,0 +1,753 @@ +//===-- PlatformWindows.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformWindows.h" + +// C Includes +#include +#if defined (_WIN32) +#include "lldb/Host/windows/windows.h" +#include +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +static uint32_t g_initialize_count = 0; + +namespace +{ + class SupportedArchList + { + public: + SupportedArchList() + { + AddArch(ArchSpec("i686-pc-windows")); + AddArch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault)); + AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind32)); + AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind64)); + AddArch(ArchSpec("i386-pc-windows")); + } + + size_t Count() const { return m_archs.size(); } + + const ArchSpec& operator[](int idx) { return m_archs[idx]; } + + private: + void AddArch(const ArchSpec& spec) + { + auto iter = std::find_if( + m_archs.begin(), m_archs.end(), + [spec](const ArchSpec& rhs) { return spec.IsExactMatch(rhs); }); + if (iter != m_archs.end()) + return; + if (spec.IsValid()) + m_archs.push_back(spec); + } + + std::vector m_archs; + }; +} // anonymous namespace + +PlatformSP +PlatformWindows::CreateInstance (bool force, const lldb_private::ArchSpec *arch) +{ + // The only time we create an instance is when we are creating a remote + // windows platform + const bool is_host = false; + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::PC: + create = true; + break; + + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; + + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::Win32: + break; + + case llvm::Triple::UnknownOS: + create = arch->TripleOSWasSpecified(); + break; + + default: + create = false; + break; + } + } + } + if (create) + return PlatformSP(new PlatformWindows (is_host)); + return PlatformSP(); +} + +lldb_private::ConstString +PlatformWindows::GetPluginNameStatic(bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-windows"); + return g_remote_name; + } +} + +const char * +PlatformWindows::GetPluginDescriptionStatic(bool is_host) +{ + return is_host ? + "Local Windows user platform plug-in." : + "Remote Windows user platform plug-in."; +} + +lldb_private::ConstString +PlatformWindows::GetPluginName() +{ + return GetPluginNameStatic(IsHost()); +} + +void +PlatformWindows::Initialize() +{ + Platform::Initialize (); + + if (g_initialize_count++ == 0) + { +#if defined (_WIN32) + WSADATA dummy; + WSAStartup(MAKEWORD(2,2), &dummy); + // Force a host flag to true for the default platform object. + PlatformSP default_platform_sp (new PlatformWindows(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin(PlatformWindows::GetPluginNameStatic(false), + PlatformWindows::GetPluginDescriptionStatic(false), + PlatformWindows::CreateInstance); + } +} + +void +PlatformWindows::Terminate( void ) +{ + if (g_initialize_count > 0) + { + if (--g_initialize_count == 0) + { +#ifdef _WIN32 + WSACleanup(); +#endif + PluginManager::UnregisterPlugin (PlatformWindows::CreateInstance); + } + } + + Platform::Terminate (); +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformWindows::PlatformWindows (bool is_host) : + Platform(is_host) +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformWindows::~PlatformWindows() = default; + +bool +PlatformWindows::GetModuleSpec (const FileSpec& module_file_spec, + const ArchSpec& arch, + ModuleSpec &module_spec) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetModuleSpec (module_file_spec, arch, module_spec); + + return Platform::GetModuleSpec (module_file_spec, arch, module_spec); +} + +Error +PlatformWindows::ResolveExecutable (const ModuleSpec &ms, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + char exe_path[PATH_MAX]; + ModuleSpec resolved_module_spec(ms); + + if (IsHost()) + { + // if we cant resolve the executable loation based on the current path variables + if (!resolved_module_spec.GetFileSpec().Exists()) + { + resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + resolved_module_spec.GetFileSpec().SetFile(exe_path, true); + } + + if (!resolved_module_spec.GetFileSpec().Exists()) + resolved_module_spec.GetFileSpec().ResolveExecutableLocation (); + + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + { + ms.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path); + } + } + else + { + if (m_remote_platform_sp) + { + error = GetCachedExecutable (resolved_module_spec, exe_module_sp, nullptr, *m_remote_platform_sp); + } + else + { + // We may connect to a process and use the provided executable (Don't use local $PATH). + if (resolved_module_spec.GetFileSpec().Exists()) + error.Clear(); + else + error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path); + } + } + + if (error.Success()) + { + if (resolved_module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + + if (!exe_module_sp || exe_module_sp->GetObjectFile() == nullptr) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) + { + error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, + nullptr, + nullptr, + nullptr); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + if (resolved_module_spec.GetFileSpec().Readable()) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + resolved_module_spec.GetFileSpec().GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + else + { + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); + } + } + } + } + + return error; +} + +size_t +PlatformWindows::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site) +{ + ArchSpec arch = target.GetArchitecture(); + const uint8_t *trap_opcode = nullptr; + size_t trap_opcode_size = 0; + + switch (arch.GetMachine()) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + { + static const uint8_t g_i386_opcode[] = { 0xCC }; + trap_opcode = g_i386_opcode; + trap_opcode_size = sizeof(g_i386_opcode); + } + break; + + case llvm::Triple::hexagon: + { + static const uint8_t g_hex_opcode[] = { 0x0c, 0xdb, 0x00, 0x54 }; + trap_opcode = g_hex_opcode; + trap_opcode_size = sizeof(g_hex_opcode); + } + break; + default: + llvm_unreachable("Unhandled architecture in PlatformWindows::GetSoftwareBreakpointTrapOpcode()"); + break; + } + + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + + return 0; +} + +bool +PlatformWindows::GetRemoteOSVersion () +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetOSVersion (m_major_os_version, + m_minor_os_version, + m_update_os_version); + return false; +} + +bool +PlatformWindows::GetRemoteOSBuildString (std::string &s) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteOSBuildString (s); + s.clear(); + return false; +} + +bool +PlatformWindows::GetRemoteOSKernelDescription (std::string &s) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteOSKernelDescription (s); + s.clear(); + return false; +} + +// Remote Platform subclasses need to override this function +ArchSpec +PlatformWindows::GetRemoteSystemArchitecture () +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteSystemArchitecture (); + return ArchSpec(); +} + +const char * +PlatformWindows::GetHostname () +{ + if (IsHost()) + return Platform::GetHostname(); + + if (m_remote_platform_sp) + return m_remote_platform_sp->GetHostname (); + return nullptr; +} + +bool +PlatformWindows::IsConnected () const +{ + if (IsHost()) + return true; + else if (m_remote_platform_sp) + return m_remote_platform_sp->IsConnected(); + return false; +} + +Error +PlatformWindows::ConnectRemote (Args& args) +{ + Error error; + if (IsHost()) + { + error.SetErrorStringWithFormat ("can't connect to the host platform '%s', always connected", GetPluginName().AsCString() ); + } + else + { + if (!m_remote_platform_sp) + m_remote_platform_sp = Platform::Create (ConstString("remote-gdb-server"), error); + + if (m_remote_platform_sp) + { + if (error.Success()) + { + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->ConnectRemote (args); + } + else + { + error.SetErrorString ("\"platform connect\" takes a single argument: "); + } + } + } + else + error.SetErrorString ("failed to create a 'remote-gdb-server' platform"); + + if (error.Fail()) + m_remote_platform_sp.reset(); + } + + return error; +} + +Error +PlatformWindows::DisconnectRemote () +{ + Error error; + + if (IsHost()) + { + error.SetErrorStringWithFormat ("can't disconnect from the host platform '%s', always connected", GetPluginName().AsCString() ); + } + else + { + if (m_remote_platform_sp) + error = m_remote_platform_sp->DisconnectRemote (); + else + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +bool +PlatformWindows::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = Platform::GetProcessInfo (pid, process_info); + } + else if (m_remote_platform_sp) + { + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + +uint32_t +PlatformWindows::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + uint32_t match_count = 0; + if (IsHost()) + { + // Let the base class figure out the host details + match_count = Platform::FindProcesses (match_info, process_infos); + } + else + { + // If we are remote, we can only return results if we are connected + if (m_remote_platform_sp) + match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos); + } + return match_count; +} + +Error +PlatformWindows::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + if (IsHost()) + { + error = Platform::LaunchProcess (launch_info); + } + else + { + if (m_remote_platform_sp) + error = m_remote_platform_sp->LaunchProcess (launch_info); + else + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +ProcessSP +PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, Error &error) +{ + // Windows has special considerations that must be followed when launching or attaching to a process. The + // key requirement is that when launching or attaching to a process, you must do it from the same the thread + // that will go into a permanent loop which will then receive debug events from the process. In particular, + // this means we can't use any of LLDB's generic mechanisms to do it for us, because it doesn't have the + // special knowledge required for setting up the background thread or passing the right flags. + // + // Another problem is that that LLDB's standard model for debugging a process is to first launch it, have + // it stop at the entry point, and then attach to it. In Windows this doesn't quite work, you have to + // specify as an argument to CreateProcess() that you're going to debug the process. So we override DebugProcess + // here to handle this. Launch operations go directly to the process plugin, and attach operations almost go + // directly to the process plugin (but we hijack the events first). In essence, we encapsulate all the logic + // of Launching and Attaching in the process plugin, and PlatformWindows::DebugProcess is just a pass-through + // to get to the process plugin. + + if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + { + // This is a process attach. Don't need to launch anything. + ProcessAttachInfo attach_info(launch_info); + return Attach(attach_info, debugger, target, error); + } + else + { + ProcessSP process_sp = target->CreateProcess(launch_info.GetListenerForProcess(debugger), + launch_info.GetProcessPluginName(), + nullptr); + + // We need to launch and attach to the process. + launch_info.GetFlags().Set(eLaunchFlagDebug); + if (process_sp) + error = process_sp->Launch(launch_info); + + return process_sp; + } +} + +lldb::ProcessSP +PlatformWindows::Attach(ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, + Error &error) +{ + error.Clear(); + lldb::ProcessSP process_sp; + if (!IsHost()) + { + if (m_remote_platform_sp) + process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error); + else + error.SetErrorString ("the platform is not currently connected"); + return process_sp; + } + + if (target == nullptr) + { + TargetSP new_target_sp; + FileSpec emptyFileSpec; + ArchSpec emptyArchSpec; + + error = debugger.GetTargetList().CreateTarget(debugger, + nullptr, + nullptr, + false, + nullptr, + new_target_sp); + target = new_target_sp.get(); + } + + if (!target || error.Fail()) + return process_sp; + + debugger.GetTargetList().SetSelectedTarget(target); + + const char *plugin_name = attach_info.GetProcessPluginName(); + process_sp = target->CreateProcess(attach_info.GetListenerForProcess(debugger), plugin_name, nullptr); + + process_sp->HijackProcessEvents(attach_info.GetHijackListener().get()); + if (process_sp) + error = process_sp->Attach (attach_info); + + return process_sp; +} + +const char * +PlatformWindows::GetUserName (uint32_t uid) +{ + // Check the cache in Platform in case we have already looked this uid up + const char *user_name = Platform::GetUserName(uid); + if (user_name) + return user_name; + + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetUserName(uid); + return nullptr; +} + +const char * +PlatformWindows::GetGroupName (uint32_t gid) +{ + const char *group_name = Platform::GetGroupName(gid); + if (group_name) + return group_name; + + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetGroupName(gid); + return nullptr; +} + +Error +PlatformWindows::GetFileWithUUID (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file); + } + + // Default to the local case + local_file = platform_file; + return Error(); +} + +Error +PlatformWindows::GetSharedModule (const ModuleSpec &module_spec, + Process* process, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error; + module_sp.reset(); + + if (IsRemote()) + { + // If we have a remote platform always, let it try and locate + // the shared module first. + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->GetSharedModule (module_spec, + process, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + } + } + + if (!module_sp) + { + // Fall back to the local platform and find the file locally + error = Platform::GetSharedModule (module_spec, + process, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + } + if (module_sp) + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return error; +} + +bool +PlatformWindows::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + static SupportedArchList architectures; + + if (idx >= architectures.Count()) + return false; + arch = architectures[idx]; + return true; +} + +void +PlatformWindows::GetStatus (Stream &strm) +{ + Platform::GetStatus(strm); + +#ifdef _WIN32 + uint32_t major; + uint32_t minor; + uint32_t update; + if (!HostInfo::GetOSVersion(major, minor, update)) + { + strm << "Windows"; + return; + } + + strm << "Host: Windows " << major + << '.' << minor + << " Build: " << update << '\n'; +#endif +} + +bool +PlatformWindows::CanDebugProcess() +{ + return true; +} + +size_t +PlatformWindows::GetEnvironment(StringList &env) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetEnvironment(env); + return 0; + } + + return Host::GetEnvironment(env); +} + +ConstString +PlatformWindows::GetFullNameForDylib (ConstString basename) +{ + if (basename.IsEmpty()) + return basename; + + StreamString stream; + stream.Printf("%s.dll", basename.GetCString()); + return ConstString(stream.GetData()); +} diff --git a/source/Plugins/Platform/Windows/PlatformWindows.h b/source/Plugins/Platform/Windows/PlatformWindows.h new file mode 100644 index 000000000000..e9a04b4cc33d --- /dev/null +++ b/source/Plugins/Platform/Windows/PlatformWindows.h @@ -0,0 +1,169 @@ +//===-- PlatformWindows.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformWindows_h_ +#define liblldb_PlatformWindows_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Platform.h" + +namespace lldb_private +{ + +class PlatformWindows : public Platform +{ +public: + PlatformWindows(bool is_host); + + ~PlatformWindows() override; + + static void + Initialize(); + + static void + Terminate(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + static lldb::PlatformSP + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static lldb_private::ConstString + GetPluginNameStatic(bool is_host); + + static const char * + GetPluginDescriptionStatic(bool is_host); + + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override + { + return 1; + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + bool + GetModuleSpec (const lldb_private::FileSpec& module_file_spec, + const lldb_private::ArchSpec& arch, + lldb_private::ModuleSpec &module_spec) override; + + Error + ResolveExecutable(const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr) override; + + const char * + GetDescription() override + { + return GetPluginDescriptionStatic(IsHost()); + } + + size_t + GetSoftwareBreakpointTrapOpcode(lldb_private::Target &target, + lldb_private::BreakpointSite *bp_site) override; + + bool + GetRemoteOSVersion() override; + + bool + GetRemoteOSBuildString(std::string &s) override; + + bool + GetRemoteOSKernelDescription(std::string &s) override; + + // Remote Platform subclasses need to override this function + lldb_private::ArchSpec + GetRemoteSystemArchitecture() override; + + bool + IsConnected() const override; + + lldb_private::Error + ConnectRemote(lldb_private::Args& args) override; + + lldb_private::Error + DisconnectRemote() override; + + const char * + GetHostname() override; + + const char * + GetUserName(uint32_t uid) override; + + const char * + GetGroupName(uint32_t gid) override; + + bool + GetProcessInfo(lldb::pid_t pid, + lldb_private::ProcessInstanceInfo &proc_info) override; + + uint32_t + FindProcesses(const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos) override; + + lldb_private::Error + LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) override; + + lldb::ProcessSP + DebugProcess(lldb_private::ProcessLaunchInfo &launch_info, lldb_private::Debugger &debugger, + lldb_private::Target *target, lldb_private::Error &error) override; + + lldb::ProcessSP + Attach(lldb_private::ProcessAttachInfo &attach_info, lldb_private::Debugger &debugger, + lldb_private::Target *target, lldb_private::Error &error) override; + + lldb_private::Error + GetFileWithUUID(const lldb_private::FileSpec &platform_file, + const lldb_private::UUID* uuid, lldb_private::FileSpec &local_file) override; + + lldb_private::Error + GetSharedModule(const lldb_private::ModuleSpec &module_spec, + lldb_private::Process* process, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) override; + + bool + GetSupportedArchitectureAtIndex(uint32_t idx, lldb_private::ArchSpec &arch) override; + + void + GetStatus(lldb_private::Stream &strm) override; + + bool CanDebugProcess() override; + + size_t GetEnvironment(StringList &env) override; + + // FIXME not sure what the _sigtramp equivalent would be on this platform + void + CalculateTrapHandlerSymbolNames () override + { + } + + ConstString + GetFullNameForDylib (ConstString basename) override; + +protected: + lldb::PlatformSP m_remote_platform_sp; + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformWindows); +}; + +} // namespace lldb_private + +#endif // liblldb_PlatformWindows_h_ diff --git a/source/Plugins/Platform/gdb-server/CMakeLists.txt b/source/Plugins/Platform/gdb-server/CMakeLists.txt new file mode 100644 index 000000000000..b0b669e5b8b7 --- /dev/null +++ b/source/Plugins/Platform/gdb-server/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginPlatformGDB + PlatformRemoteGDBServer.cpp + ) diff --git a/source/Plugins/Platform/gdb-server/Makefile b/source/Plugins/Platform/gdb-server/Makefile new file mode 100644 index 000000000000..c56613b2a653 --- /dev/null +++ b/source/Plugins/Platform/gdb-server/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/gdb-server/Makefile ---------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformGDB +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/CMakeLists.txt b/source/Plugins/Process/CMakeLists.txt new file mode 100644 index 000000000000..d15df94414b3 --- /dev/null +++ b/source/Plugins/Process/CMakeLists.txt @@ -0,0 +1,19 @@ +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + add_subdirectory(Linux) + add_subdirectory(POSIX) +elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_subdirectory(FreeBSD) + add_subdirectory(POSIX) +elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + add_subdirectory(POSIX) +elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") + add_subdirectory(Windows/Live) + add_subdirectory(Windows/MiniDump) + add_subdirectory(Windows/Common) +elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(MacOSX-Kernel) +endif() +add_subdirectory(gdb-remote) +add_subdirectory(Utility) +add_subdirectory(mach-core) +add_subdirectory(elf-core) diff --git a/source/Plugins/Process/FreeBSD/CMakeLists.txt b/source/Plugins/Process/FreeBSD/CMakeLists.txt new file mode 100644 index 000000000000..c0e3374fef89 --- /dev/null +++ b/source/Plugins/Process/FreeBSD/CMakeLists.txt @@ -0,0 +1,16 @@ +include_directories(.) +include_directories(../POSIX) +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessFreeBSD + ProcessFreeBSD.cpp + FreeBSDThread.cpp + ProcessMonitor.cpp + + POSIXStopInfo.cpp + RegisterContextPOSIXProcessMonitor_arm.cpp + RegisterContextPOSIXProcessMonitor_arm64.cpp + RegisterContextPOSIXProcessMonitor_powerpc.cpp + RegisterContextPOSIXProcessMonitor_x86.cpp + RegisterContextPOSIXProcessMonitor_mips64.cpp + ) diff --git a/source/Plugins/Process/FreeBSD/Makefile b/source/Plugins/Process/FreeBSD/Makefile new file mode 100644 index 000000000000..7f546540e556 --- /dev/null +++ b/source/Plugins/Process/FreeBSD/Makefile @@ -0,0 +1,17 @@ +##===- source/Plugins/Process/FreeBSD/Makefile ---------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessFreeBSD +BUILD_ARCHIVE = 1 + +# Extend the include path so we may locate UnwindLLDB.h +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h index 3cc46f489875..5f9365418d7a 100644 --- a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h @@ -88,7 +88,7 @@ class ProcessFreeBSD : DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) override; lldb_private::Error - DoLaunch (lldb_private::Module *exe_module, + DoLaunch (lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; void @@ -160,7 +160,7 @@ class ProcessFreeBSD : UpdateThreadListIfNeeded(); bool - UpdateThreadList(lldb_private::ThreadList &old_thread_list, + UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; virtual lldb::ByteOrder diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp index ceb527b61d80..cd016fbd4b8c 100644 --- a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -317,7 +317,7 @@ ReadRegOperation::Execute(ProcessMonitor *monitor) else if (m_size == sizeof(uint64_t)) m_value = *(uint64_t *)(((caddr_t)®s) + m_offset); else - memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); + memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } @@ -393,7 +393,7 @@ ReadDebugRegOperation::Execute(ProcessMonitor *monitor) if (m_size == sizeof(uintptr_t)) m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); else - memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); + memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } diff --git a/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp index 012f4b6e1555..a1a0cab82a15 100644 --- a/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp @@ -260,9 +260,7 @@ RegisterContextPOSIXProcessMonitor_arm64::HardwareSingleStep(bool enable) bool RegisterContextPOSIXProcessMonitor_arm64::UpdateAfterBreakpoint() { - lldb::addr_t pc; - - if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + if (GetPC() == LLDB_INVALID_ADDRESS) return false; return true; diff --git a/source/Plugins/Process/Linux/CMakeLists.txt b/source/Plugins/Process/Linux/CMakeLists.txt new file mode 100644 index 000000000000..80de8413d209 --- /dev/null +++ b/source/Plugins/Process/Linux/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(.) +include_directories(../POSIX) +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessLinux + NativeProcessLinux.cpp + NativeRegisterContextLinux.cpp + NativeRegisterContextLinux_arm.cpp + NativeRegisterContextLinux_arm64.cpp + NativeRegisterContextLinux_x86_64.cpp + NativeRegisterContextLinux_mips64.cpp + NativeThreadLinux.cpp + ProcFileReader.cpp + ) diff --git a/source/Plugins/Process/Linux/Makefile b/source/Plugins/Process/Linux/Makefile new file mode 100644 index 000000000000..239e94d608e9 --- /dev/null +++ b/source/Plugins/Process/Linux/Makefile @@ -0,0 +1,17 @@ +##===- source/Plugins/Process/Linux/Makefile ---------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessLinux +BUILD_ARCHIVE = 1 + +# Extend the include path so we may locate UnwindLLDB.h +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/source/Plugins/Process/Linux/NativeProcessLinux.cpp new file mode 100644 index 000000000000..87c76f57830c --- /dev/null +++ b/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -0,0 +1,3189 @@ +//===-- NativeProcessLinux.cpp -------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeProcessLinux.h" + +// C Includes +#include +#include +#include +#include + +// C++ Includes +#include +#include +#include +#include +#include + +// Other libraries and framework includes +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/State.h" +#include "lldb/Host/common/NativeBreakpoint.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/PseudoTerminal.h" +#include "lldb/Utility/StringExtractor.h" + +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "NativeThreadLinux.h" +#include "ProcFileReader.h" +#include "Procfs.h" + +// System includes - They have to be included after framework includes because they define some +// macros which collide with variable names in other modules +#include +#include + +#include +#include +#include +#include + +#include "lldb/Host/linux/Personality.h" +#include "lldb/Host/linux/Ptrace.h" +#include "lldb/Host/linux/Signalfd.h" +#include "lldb/Host/linux/Uio.h" +#include "lldb/Host/android/Android.h" + +#define LLDB_PERSONALITY_GET_CURRENT_SETTINGS 0xffffffff + +// Support hardware breakpoints in case it has not been defined +#ifndef TRAP_HWBKPT + #define TRAP_HWBKPT 4 +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; +using namespace llvm; + +// Private bits we only need internally. + +static bool ProcessVmReadvSupported() +{ + static bool is_supported; + static std::once_flag flag; + + std::call_once(flag, [] { + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + uint32_t source = 0x47424742; + uint32_t dest = 0; + + struct iovec local, remote; + remote.iov_base = &source; + local.iov_base = &dest; + remote.iov_len = local.iov_len = sizeof source; + + // We shall try if cross-process-memory reads work by attempting to read a value from our own process. + ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); + is_supported = (res == sizeof(source) && source == dest); + if (log) + { + if (is_supported) + log->Printf("%s: Detected kernel support for process_vm_readv syscall. Fast memory reads enabled.", + __FUNCTION__); + else + log->Printf("%s: syscall process_vm_readv failed (error: %s). Fast memory reads disabled.", + __FUNCTION__, strerror(errno)); + } + }); + + return is_supported; +} + +namespace +{ + Error + ResolveProcessArchitecture (lldb::pid_t pid, Platform &platform, ArchSpec &arch) + { + // Grab process info for the running process. + ProcessInstanceInfo process_info; + if (!platform.GetProcessInfo (pid, process_info)) + return Error("failed to get process info"); + + // Resolve the executable module. + ModuleSP exe_module_sp; + ModuleSpec exe_module_spec(process_info.GetExecutableFile(), process_info.GetArchitecture()); + FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths ()); + Error error = platform.ResolveExecutable( + exe_module_spec, + exe_module_sp, + executable_search_paths.GetSize () ? &executable_search_paths : NULL); + + if (!error.Success ()) + return error; + + // Check if we've got our architecture from the exe_module. + arch = exe_module_sp->GetArchitecture (); + if (arch.IsValid ()) + return Error(); + else + return Error("failed to retrieve a valid architecture from the exe module"); + } + + void + DisplayBytes (StreamString &s, void *bytes, uint32_t count) + { + uint8_t *ptr = (uint8_t *)bytes; + const uint32_t loop_count = std::min(DEBUG_PTRACE_MAXBYTES, count); + for(uint32_t i=0; iPrintf("PTRACE_POKETEXT %s", buf.GetData()); + break; + } + case PTRACE_POKEDATA: + { + DisplayBytes(buf, &data, 8); + verbose_log->Printf("PTRACE_POKEDATA %s", buf.GetData()); + break; + } + case PTRACE_POKEUSER: + { + DisplayBytes(buf, &data, 8); + verbose_log->Printf("PTRACE_POKEUSER %s", buf.GetData()); + break; + } + case PTRACE_SETREGS: + { + DisplayBytes(buf, data, data_size); + verbose_log->Printf("PTRACE_SETREGS %s", buf.GetData()); + break; + } + case PTRACE_SETFPREGS: + { + DisplayBytes(buf, data, data_size); + verbose_log->Printf("PTRACE_SETFPREGS %s", buf.GetData()); + break; + } + case PTRACE_SETSIGINFO: + { + DisplayBytes(buf, data, sizeof(siginfo_t)); + verbose_log->Printf("PTRACE_SETSIGINFO %s", buf.GetData()); + break; + } + case PTRACE_SETREGSET: + { + // Extract iov_base from data, which is a pointer to the struct IOVEC + DisplayBytes(buf, *(void **)data, data_size); + verbose_log->Printf("PTRACE_SETREGSET %s", buf.GetData()); + break; + } + default: + { + } + } + } + } + + static constexpr unsigned k_ptrace_word_size = sizeof(void*); + static_assert(sizeof(long) >= k_ptrace_word_size, "Size of long must be larger than ptrace word size"); +} // end of anonymous namespace + +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static Error +EnsureFDFlags(int fd, int flags) +{ + Error error; + + int status = fcntl(fd, F_GETFL); + if (status == -1) + { + error.SetErrorToErrno(); + return error; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) + { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +NativeProcessLinux::LaunchArgs::LaunchArgs(Module *module, + char const **argv, + char const **envp, + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, + const ProcessLaunchInfo &launch_info) + : m_module(module), + m_argv(argv), + m_envp(envp), + m_stdin_file_spec(stdin_file_spec), + m_stdout_file_spec(stdout_file_spec), + m_stderr_file_spec(stderr_file_spec), + m_working_dir(working_dir), + m_launch_info(launch_info) +{ +} + +NativeProcessLinux::LaunchArgs::~LaunchArgs() +{ } + +// ----------------------------------------------------------------------------- +// Public Static Methods +// ----------------------------------------------------------------------------- + +Error +NativeProcessProtocol::Launch ( + ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &native_process_sp) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + lldb::ModuleSP exe_module_sp; + PlatformSP platform_sp (Platform::GetHostPlatform ()); + Error error = platform_sp->ResolveExecutable( + ModuleSpec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()), + exe_module_sp, + nullptr); + + if (! error.Success()) + return error; + + // Verify the working directory is valid if one was specified. + FileSpec working_dir{launch_info.GetWorkingDirectory()}; + if (working_dir && + (!working_dir.ResolvePath() || + working_dir.GetFileType() != FileSpec::eFileTypeDirectory)) + { + error.SetErrorStringWithFormat ("No such file or directory: %s", + working_dir.GetCString()); + return error; + } + + const FileAction *file_action; + + // Default of empty will mean to use existing open file descriptors. + FileSpec stdin_file_spec{}; + FileSpec stdout_file_spec{}; + FileSpec stderr_file_spec{}; + + file_action = launch_info.GetFileActionForFD (STDIN_FILENO); + if (file_action) + stdin_file_spec = file_action->GetFileSpec(); + + file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); + if (file_action) + stdout_file_spec = file_action->GetFileSpec(); + + file_action = launch_info.GetFileActionForFD (STDERR_FILENO); + if (file_action) + stderr_file_spec = file_action->GetFileSpec(); + + if (log) + { + if (stdin_file_spec) + log->Printf ("NativeProcessLinux::%s setting STDIN to '%s'", + __FUNCTION__, stdin_file_spec.GetCString()); + else + log->Printf ("NativeProcessLinux::%s leaving STDIN as is", __FUNCTION__); + + if (stdout_file_spec) + log->Printf ("NativeProcessLinux::%s setting STDOUT to '%s'", + __FUNCTION__, stdout_file_spec.GetCString()); + else + log->Printf ("NativeProcessLinux::%s leaving STDOUT as is", __FUNCTION__); + + if (stderr_file_spec) + log->Printf ("NativeProcessLinux::%s setting STDERR to '%s'", + __FUNCTION__, stderr_file_spec.GetCString()); + else + log->Printf ("NativeProcessLinux::%s leaving STDERR as is", __FUNCTION__); + } + + // Create the NativeProcessLinux in launch mode. + native_process_sp.reset (new NativeProcessLinux ()); + + if (log) + { + int i = 0; + for (const char **args = launch_info.GetArguments ().GetConstArgumentVector (); *args; ++args, ++i) + { + log->Printf ("NativeProcessLinux::%s arg %d: \"%s\"", __FUNCTION__, i, *args ? *args : "nullptr"); + ++i; + } + } + + if (!native_process_sp->RegisterNativeDelegate (native_delegate)) + { + native_process_sp.reset (); + error.SetErrorStringWithFormat ("failed to register the native delegate"); + return error; + } + + std::static_pointer_cast (native_process_sp)->LaunchInferior ( + mainloop, + exe_module_sp.get(), + launch_info.GetArguments ().GetConstArgumentVector (), + launch_info.GetEnvironmentEntries ().GetConstArgumentVector (), + stdin_file_spec, + stdout_file_spec, + stderr_file_spec, + working_dir, + launch_info, + error); + + if (error.Fail ()) + { + native_process_sp.reset (); + if (log) + log->Printf ("NativeProcessLinux::%s failed to launch process: %s", __FUNCTION__, error.AsCString ()); + return error; + } + + launch_info.SetProcessID (native_process_sp->GetID ()); + + return error; +} + +Error +NativeProcessProtocol::Attach ( + lldb::pid_t pid, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &native_process_sp) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log && log->GetMask ().Test (POSIX_LOG_VERBOSE)) + log->Printf ("NativeProcessLinux::%s(pid = %" PRIi64 ")", __FUNCTION__, pid); + + // Grab the current platform architecture. This should be Linux, + // since this code is only intended to run on a Linux host. + PlatformSP platform_sp (Platform::GetHostPlatform ()); + if (!platform_sp) + return Error("failed to get a valid default platform"); + + // Retrieve the architecture for the running process. + ArchSpec process_arch; + Error error = ResolveProcessArchitecture (pid, *platform_sp.get (), process_arch); + if (!error.Success ()) + return error; + + std::shared_ptr native_process_linux_sp (new NativeProcessLinux ()); + + if (!native_process_linux_sp->RegisterNativeDelegate (native_delegate)) + { + error.SetErrorStringWithFormat ("failed to register the native delegate"); + return error; + } + + native_process_linux_sp->AttachToInferior (mainloop, pid, error); + if (!error.Success ()) + return error; + + native_process_sp = native_process_linux_sp; + return error; +} + +// ----------------------------------------------------------------------------- +// Public Instance Methods +// ----------------------------------------------------------------------------- + +NativeProcessLinux::NativeProcessLinux () : + NativeProcessProtocol (LLDB_INVALID_PROCESS_ID), + m_arch (), + m_supports_mem_region (eLazyBoolCalculate), + m_mem_region_cache (), + m_mem_region_cache_mutex(), + m_pending_notification_tid(LLDB_INVALID_THREAD_ID) +{ +} + +void +NativeProcessLinux::LaunchInferior ( + MainLoop &mainloop, + Module *module, + const char *argv[], + const char *envp[], + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, + const ProcessLaunchInfo &launch_info, + Error &error) +{ + m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD, + [this] (MainLoopBase &) { SigchldHandler(); }, error); + if (! m_sigchld_handle) + return; + + if (module) + m_arch = module->GetArchitecture (); + + SetState (eStateLaunching); + + std::unique_ptr args( + new LaunchArgs(module, argv, envp, + stdin_file_spec, + stdout_file_spec, + stderr_file_spec, + working_dir, + launch_info)); + + Launch(args.get(), error); +} + +void +NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ")", __FUNCTION__, pid); + + m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD, + [this] (MainLoopBase &) { SigchldHandler(); }, error); + if (! m_sigchld_handle) + return; + + // We can use the Host for everything except the ResolveExecutable portion. + PlatformSP platform_sp = Platform::GetHostPlatform (); + if (!platform_sp) + { + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 "): no default platform set", __FUNCTION__, pid); + error.SetErrorString ("no default platform available"); + return; + } + + // Gather info about the process. + ProcessInstanceInfo process_info; + if (!platform_sp->GetProcessInfo (pid, process_info)) + { + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 "): failed to get process info", __FUNCTION__, pid); + error.SetErrorString ("failed to get process info"); + return; + } + + // Resolve the executable module + ModuleSP exe_module_sp; + FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); + ModuleSpec exe_module_spec(process_info.GetExecutableFile(), process_info.GetArchitecture()); + error = platform_sp->ResolveExecutable(exe_module_spec, exe_module_sp, + executable_search_paths.GetSize() ? &executable_search_paths : NULL); + if (!error.Success()) + return; + + // Set the architecture to the exe architecture. + m_arch = exe_module_sp->GetArchitecture(); + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ") detected architecture %s", __FUNCTION__, pid, m_arch.GetArchitectureName ()); + + m_pid = pid; + SetState(eStateAttaching); + + Attach(pid, error); +} + +::pid_t +NativeProcessLinux::Launch(LaunchArgs *args, Error &error) +{ + assert (args && "null args"); + + const char **argv = args->m_argv; + const char **envp = args->m_envp; + const FileSpec working_dir = args->m_working_dir; + + lldb_utility::PseudoTerminal terminal; + const size_t err_len = 1024; + char err_str[err_len]; + lldb::pid_t pid; + + // Propagate the environment if one is not supplied. + if (envp == NULL || envp[0] == NULL) + envp = const_cast(environ); + + if ((pid = terminal.Fork(err_str, err_len)) == static_cast (-1)) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Process fork failed: %s", err_str); + return -1; + } + + // Recognized child exit status codes. + enum { + ePtraceFailed = 1, + eDupStdinFailed, + eDupStdoutFailed, + eDupStderrFailed, + eChdirFailed, + eExecFailed, + eSetGidFailed, + eSetSigMaskFailed + }; + + // Child process. + if (pid == 0) + { + // First, make sure we disable all logging. If we are logging to stdout, our logs can be + // mistaken for inferior output. + Log::DisableAllLogChannels(nullptr); + // FIXME consider opening a pipe between parent/child and have this forked child + // send log info to parent re: launch status. + + // Start tracing this child that is about to exec. + error = PtraceWrapper(PTRACE_TRACEME, 0); + if (error.Fail()) + exit(ePtraceFailed); + + // terminal has already dupped the tty descriptors to stdin/out/err. + // This closes original fd from which they were copied (and avoids + // leaking descriptors to the debugged process. + terminal.CloseSlaveFileDescriptor(); + + // Do not inherit setgid powers. + if (setgid(getgid()) != 0) + exit(eSetGidFailed); + + // Attempt to have our own process group. + if (setpgid(0, 0) != 0) + { + // FIXME log that this failed. This is common. + // Don't allow this to prevent an inferior exec. + } + + // Dup file descriptors if needed. + if (args->m_stdin_file_spec) + if (!DupDescriptor(args->m_stdin_file_spec, STDIN_FILENO, O_RDONLY)) + exit(eDupStdinFailed); + + if (args->m_stdout_file_spec) + if (!DupDescriptor(args->m_stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + exit(eDupStdoutFailed); + + if (args->m_stderr_file_spec) + if (!DupDescriptor(args->m_stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + exit(eDupStderrFailed); + + // Close everything besides stdin, stdout, and stderr that has no file + // action to avoid leaking + for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) + if (!args->m_launch_info.GetFileActionForFD(fd)) + close(fd); + + // Change working directory + if (working_dir && 0 != ::chdir(working_dir.GetCString())) + exit(eChdirFailed); + + // Disable ASLR if requested. + if (args->m_launch_info.GetFlags ().Test (lldb::eLaunchFlagDisableASLR)) + { + const int old_personality = personality (LLDB_PERSONALITY_GET_CURRENT_SETTINGS); + if (old_personality == -1) + { + // Can't retrieve Linux personality. Cannot disable ASLR. + } + else + { + const int new_personality = personality (ADDR_NO_RANDOMIZE | old_personality); + if (new_personality == -1) + { + // Disabling ASLR failed. + } + else + { + // Disabling ASLR succeeded. + } + } + } + + // Clear the signal mask to prevent the child from being affected by + // any masking done by the parent. + sigset_t set; + if (sigemptyset(&set) != 0 || pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) + exit(eSetSigMaskFailed); + + // Execute. We should never return... + execve(argv[0], + const_cast(argv), + const_cast(envp)); + + // ...unless exec fails. In which case we definitely need to end the child here. + exit(eExecFailed); + } + + // + // This is the parent code here. + // + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // Wait for the child process to trap on its call to execve. + ::pid_t wpid; + int status; + if ((wpid = waitpid(pid, &status, 0)) < 0) + { + error.SetErrorToErrno(); + if (log) + log->Printf ("NativeProcessLinux::%s waitpid for inferior failed with %s", + __FUNCTION__, error.AsCString ()); + + // Mark the inferior as invalid. + // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. + SetState (StateType::eStateInvalid); + + return -1; + } + else if (WIFEXITED(status)) + { + // open, dup or execve likely failed for some reason. + error.SetErrorToGenericError(); + switch (WEXITSTATUS(status)) + { + case ePtraceFailed: + error.SetErrorString("Child ptrace failed."); + break; + case eDupStdinFailed: + error.SetErrorString("Child open stdin failed."); + break; + case eDupStdoutFailed: + error.SetErrorString("Child open stdout failed."); + break; + case eDupStderrFailed: + error.SetErrorString("Child open stderr failed."); + break; + case eChdirFailed: + error.SetErrorString("Child failed to set working directory."); + break; + case eExecFailed: + error.SetErrorString("Child exec failed."); + break; + case eSetGidFailed: + error.SetErrorString("Child setgid failed."); + break; + case eSetSigMaskFailed: + error.SetErrorString("Child failed to set signal mask."); + break; + default: + error.SetErrorString("Child returned unknown exit status."); + break; + } + + if (log) + { + log->Printf ("NativeProcessLinux::%s inferior exited with status %d before issuing a STOP", + __FUNCTION__, + WEXITSTATUS(status)); + } + + // Mark the inferior as invalid. + // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. + SetState (StateType::eStateInvalid); + + return -1; + } + assert(WIFSTOPPED(status) && (wpid == static_cast< ::pid_t> (pid)) && + "Could not sync with inferior process."); + + if (log) + log->Printf ("NativeProcessLinux::%s inferior started, now in stopped state", __FUNCTION__); + + error = SetDefaultPtraceOpts(pid); + if (error.Fail()) + { + if (log) + log->Printf ("NativeProcessLinux::%s inferior failed to set default ptrace options: %s", + __FUNCTION__, error.AsCString ()); + + // Mark the inferior as invalid. + // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. + SetState (StateType::eStateInvalid); + + return -1; + } + + // Release the master terminal descriptor and pass it off to the + // NativeProcessLinux instance. Similarly stash the inferior pid. + m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); + m_pid = pid; + + // Set the terminal fd to be in non blocking mode (it simplifies the + // implementation of ProcessLinux::GetSTDOUT to have a non-blocking + // descriptor to read from). + error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); + if (error.Fail()) + { + if (log) + log->Printf ("NativeProcessLinux::%s inferior EnsureFDFlags failed for ensuring terminal O_NONBLOCK setting: %s", + __FUNCTION__, error.AsCString ()); + + // Mark the inferior as invalid. + // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. + SetState (StateType::eStateInvalid); + + return -1; + } + + if (log) + log->Printf ("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, pid); + + NativeThreadLinuxSP thread_sp = AddThread(pid); + assert (thread_sp && "AddThread() returned a nullptr thread"); + thread_sp->SetStoppedBySignal(SIGSTOP); + ThreadWasCreated(*thread_sp); + + // Let our process instance know the thread has stopped. + SetCurrentThreadID (thread_sp->GetID ()); + SetState (StateType::eStateStopped); + + if (log) + { + if (error.Success ()) + { + log->Printf ("NativeProcessLinux::%s inferior launching succeeded", __FUNCTION__); + } + else + { + log->Printf ("NativeProcessLinux::%s inferior launching failed: %s", + __FUNCTION__, error.AsCString ()); + return -1; + } + } + return pid; +} + +::pid_t +NativeProcessLinux::Attach(lldb::pid_t pid, Error &error) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // Use a map to keep track of the threads which we have attached/need to attach. + Host::TidMap tids_to_attach; + if (pid <= 1) + { + error.SetErrorToGenericError(); + error.SetErrorString("Attaching to process 1 is not allowed."); + return -1; + } + + while (Host::FindProcessThreads(pid, tids_to_attach)) + { + for (Host::TidMap::iterator it = tids_to_attach.begin(); + it != tids_to_attach.end();) + { + if (it->second == false) + { + lldb::tid_t tid = it->first; + + // Attach to the requested process. + // An attach will cause the thread to stop with a SIGSTOP. + error = PtraceWrapper(PTRACE_ATTACH, tid); + if (error.Fail()) + { + // No such thread. The thread may have exited. + // More error handling may be needed. + if (error.GetError() == ESRCH) + { + it = tids_to_attach.erase(it); + continue; + } + else + return -1; + } + + int status; + // Need to use __WALL otherwise we receive an error with errno=ECHLD + // At this point we should have a thread stopped if waitpid succeeds. + if ((status = waitpid(tid, NULL, __WALL)) < 0) + { + // No such thread. The thread may have exited. + // More error handling may be needed. + if (errno == ESRCH) + { + it = tids_to_attach.erase(it); + continue; + } + else + { + error.SetErrorToErrno(); + return -1; + } + } + + error = SetDefaultPtraceOpts(tid); + if (error.Fail()) + return -1; + + if (log) + log->Printf ("NativeProcessLinux::%s() adding tid = %" PRIu64, __FUNCTION__, tid); + + it->second = true; + + // Create the thread, mark it as stopped. + NativeThreadLinuxSP thread_sp (AddThread(static_cast(tid))); + assert (thread_sp && "AddThread() returned a nullptr"); + + // This will notify this is a new thread and tell the system it is stopped. + thread_sp->SetStoppedBySignal(SIGSTOP); + ThreadWasCreated(*thread_sp); + SetCurrentThreadID (thread_sp->GetID ()); + } + + // move the loop forward + ++it; + } + } + + if (tids_to_attach.size() > 0) + { + m_pid = pid; + // Let our process instance know the thread has stopped. + SetState (StateType::eStateStopped); + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString("No such process."); + return -1; + } + + return pid; +} + +Error +NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) +{ + long ptrace_opts = 0; + + // Have the child raise an event on exit. This is used to keep the child in + // limbo until it is destroyed. + ptrace_opts |= PTRACE_O_TRACEEXIT; + + // Have the tracer trace threads which spawn in the inferior process. + // TODO: if we want to support tracing the inferiors' child, add the + // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) + ptrace_opts |= PTRACE_O_TRACECLONE; + + // Have the tracer notify us before execve returns + // (needed to disable legacy SIGTRAP generation) + ptrace_opts |= PTRACE_O_TRACEEXEC; + + return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void*)ptrace_opts); +} + +static ExitType convert_pid_status_to_exit_type (int status) +{ + if (WIFEXITED (status)) + return ExitType::eExitTypeExit; + else if (WIFSIGNALED (status)) + return ExitType::eExitTypeSignal; + else if (WIFSTOPPED (status)) + return ExitType::eExitTypeStop; + else + { + // We don't know what this is. + return ExitType::eExitTypeInvalid; + } +} + +static int convert_pid_status_to_return_code (int status) +{ + if (WIFEXITED (status)) + return WEXITSTATUS (status); + else if (WIFSIGNALED (status)) + return WTERMSIG (status); + else if (WIFSTOPPED (status)) + return WSTOPSIG (status); + else + { + // We don't know what this is. + return ExitType::eExitTypeInvalid; + } +} + +// Handles all waitpid events from the inferior process. +void +NativeProcessLinux::MonitorCallback(lldb::pid_t pid, + bool exited, + int signal, + int status) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // Certain activities differ based on whether the pid is the tid of the main thread. + const bool is_main_thread = (pid == GetID ()); + + // Handle when the thread exits. + if (exited) + { + if (log) + log->Printf ("NativeProcessLinux::%s() got exit signal(%d) , tid = %" PRIu64 " (%s main thread)", __FUNCTION__, signal, pid, is_main_thread ? "is" : "is not"); + + // This is a thread that exited. Ensure we're not tracking it anymore. + const bool thread_found = StopTrackingThread (pid); + + if (is_main_thread) + { + // We only set the exit status and notify the delegate if we haven't already set the process + // state to an exited state. We normally should have received a SIGTRAP | (PTRACE_EVENT_EXIT << 8) + // for the main thread. + const bool already_notified = (GetState() == StateType::eStateExited) || (GetState () == StateType::eStateCrashed); + if (!already_notified) + { + if (log) + log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " handling main thread exit (%s), expected exit state already set but state was %s instead, setting exit state now", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found", StateAsCString (GetState ())); + // The main thread exited. We're done monitoring. Report to delegate. + SetExitStatus (convert_pid_status_to_exit_type (status), convert_pid_status_to_return_code (status), nullptr, true); + + // Notify delegate that our process has exited. + SetState (StateType::eStateExited, true); + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " main thread now exited (%s)", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found"); + } + } + else + { + // Do we want to report to the delegate in this case? I think not. If this was an orderly + // thread exit, we would already have received the SIGTRAP | (PTRACE_EVENT_EXIT << 8) signal, + // and we would have done an all-stop then. + if (log) + log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " handling non-main thread exit (%s)", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found"); + } + return; + } + + siginfo_t info; + const auto info_err = GetSignalInfo(pid, &info); + auto thread_sp = GetThreadByID(pid); + + if (! thread_sp) + { + // Normally, the only situation when we cannot find the thread is if we have just + // received a new thread notification. This is indicated by GetSignalInfo() returning + // si_code == SI_USER and si_pid == 0 + if (log) + log->Printf("NativeProcessLinux::%s received notification about an unknown tid %" PRIu64 ".", __FUNCTION__, pid); + + if (info_err.Fail()) + { + if (log) + log->Printf("NativeProcessLinux::%s (tid %" PRIu64 ") GetSignalInfo failed (%s). Ingoring this notification.", __FUNCTION__, pid, info_err.AsCString()); + return; + } + + if (log && (info.si_code != SI_USER || info.si_pid != 0)) + log->Printf("NativeProcessLinux::%s (tid %" PRIu64 ") unexpected signal info (si_code: %d, si_pid: %d). Treating as a new thread notification anyway.", __FUNCTION__, pid, info.si_code, info.si_pid); + + auto thread_sp = AddThread(pid); + // Resume the newly created thread. + ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); + ThreadWasCreated(*thread_sp); + return; + } + + // Get details on the signal raised. + if (info_err.Success()) + { + // We have retrieved the signal info. Dispatch appropriately. + if (info.si_signo == SIGTRAP) + MonitorSIGTRAP(info, *thread_sp); + else + MonitorSignal(info, *thread_sp, exited); + } + else + { + if (info_err.GetError() == EINVAL) + { + // This is a group stop reception for this tid. + // We can reach here if we reinject SIGSTOP, SIGSTP, SIGTTIN or SIGTTOU into the + // tracee, triggering the group-stop mechanism. Normally receiving these would stop + // the process, pending a SIGCONT. Simulating this state in a debugger is hard and is + // generally not needed (one use case is debugging background task being managed by a + // shell). For general use, it is sufficient to stop the process in a signal-delivery + // stop which happens before the group stop. This done by MonitorSignal and works + // correctly for all signals. + if (log) + log->Printf("NativeProcessLinux::%s received a group stop for pid %" PRIu64 " tid %" PRIu64 ". Transparent handling of group stops not supported, resuming the thread.", __FUNCTION__, GetID (), pid); + ResumeThread(*thread_sp, thread_sp->GetState(), LLDB_INVALID_SIGNAL_NUMBER); + } + else + { + // ptrace(GETSIGINFO) failed (but not due to group-stop). + + // A return value of ESRCH means the thread/process is no longer on the system, + // so it was killed somehow outside of our control. Either way, we can't do anything + // with it anymore. + + // Stop tracking the metadata for the thread since it's entirely off the system now. + const bool thread_found = StopTrackingThread (pid); + + if (log) + log->Printf ("NativeProcessLinux::%s GetSignalInfo failed: %s, tid = %" PRIu64 ", signal = %d, status = %d (%s, %s, %s)", + __FUNCTION__, info_err.AsCString(), pid, signal, status, info_err.GetError() == ESRCH ? "thread/process killed" : "unknown reason", is_main_thread ? "is main thread" : "is not main thread", thread_found ? "thread metadata removed" : "thread metadata not found"); + + if (is_main_thread) + { + // Notify the delegate - our process is not available but appears to have been killed outside + // our control. Is eStateExited the right exit state in this case? + SetExitStatus (convert_pid_status_to_exit_type (status), convert_pid_status_to_return_code (status), nullptr, true); + SetState (StateType::eStateExited, true); + } + else + { + // This thread was pulled out from underneath us. Anything to do here? Do we want to do an all stop? + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 " non-main thread exit occurred, didn't tell delegate anything since thread disappeared out from underneath us", __FUNCTION__, GetID (), pid); + } + } + } +} + +void +NativeProcessLinux::WaitForNewThread(::pid_t tid) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + NativeThreadLinuxSP new_thread_sp = GetThreadByID(tid); + + if (new_thread_sp) + { + // We are already tracking the thread - we got the event on the new thread (see + // MonitorSignal) before this one. We are done. + return; + } + + // The thread is not tracked yet, let's wait for it to appear. + int status = -1; + ::pid_t wait_pid; + do + { + if (log) + log->Printf ("NativeProcessLinux::%s() received thread creation event for tid %" PRIu32 ". tid not tracked yet, waiting for thread to appear...", __FUNCTION__, tid); + wait_pid = waitpid(tid, &status, __WALL); + } + while (wait_pid == -1 && errno == EINTR); + // Since we are waiting on a specific tid, this must be the creation event. But let's do + // some checks just in case. + if (wait_pid != tid) { + if (log) + log->Printf ("NativeProcessLinux::%s() waiting for tid %" PRIu32 " failed. Assuming the thread has disappeared in the meantime", __FUNCTION__, tid); + // The only way I know of this could happen is if the whole process was + // SIGKILLed in the mean time. In any case, we can't do anything about that now. + return; + } + if (WIFEXITED(status)) + { + if (log) + log->Printf ("NativeProcessLinux::%s() waiting for tid %" PRIu32 " returned an 'exited' event. Not tracking the thread.", __FUNCTION__, tid); + // Also a very improbable event. + return; + } + + siginfo_t info; + Error error = GetSignalInfo(tid, &info); + if (error.Fail()) + { + if (log) + log->Printf ("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32 " failed. Assuming the thread has disappeared in the meantime.", __FUNCTION__, tid); + return; + } + + if (((info.si_pid != 0) || (info.si_code != SI_USER)) && log) + { + // We should be getting a thread creation signal here, but we received something + // else. There isn't much we can do about it now, so we will just log that. Since the + // thread is alive and we are receiving events from it, we shall pretend that it was + // created properly. + log->Printf ("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32 " received unexpected signal with code %d from pid %d.", __FUNCTION__, tid, info.si_code, info.si_pid); + } + + if (log) + log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 ": tracking new thread tid %" PRIu32, + __FUNCTION__, GetID (), tid); + + new_thread_sp = AddThread(tid); + ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); + ThreadWasCreated(*new_thread_sp); +} + +void +NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + const bool is_main_thread = (thread.GetID() == GetID ()); + + assert(info.si_signo == SIGTRAP && "Unexpected child signal!"); + + Mutex::Locker locker (m_threads_mutex); + + switch (info.si_code) + { + // TODO: these two cases are required if we want to support tracing of the inferiors' children. We'd need this to debug a monitor. + // case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): + // case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): + + case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): + { + // This is the notification on the parent thread which informs us of new thread + // creation. + // We don't want to do anything with the parent thread so we just resume it. In case we + // want to implement "break on thread creation" functionality, we would need to stop + // here. + + unsigned long event_message = 0; + if (GetEventMessage(thread.GetID(), &event_message).Fail()) + { + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " received thread creation event but GetEventMessage failed so we don't know the new tid", __FUNCTION__, thread.GetID()); + } else + WaitForNewThread(event_message); + + ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + break; + } + + case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)): + { + NativeThreadLinuxSP main_thread_sp; + if (log) + log->Printf ("NativeProcessLinux::%s() received exec event, code = %d", __FUNCTION__, info.si_code ^ SIGTRAP); + + // Exec clears any pending notifications. + m_pending_notification_tid = LLDB_INVALID_THREAD_ID; + + // Remove all but the main thread here. Linux fork creates a new process which only copies the main thread. Mutexes are in undefined state. + if (log) + log->Printf ("NativeProcessLinux::%s exec received, stop tracking all but main thread", __FUNCTION__); + + for (auto thread_sp : m_threads) + { + const bool is_main_thread = thread_sp && thread_sp->GetID () == GetID (); + if (is_main_thread) + { + main_thread_sp = std::static_pointer_cast(thread_sp); + if (log) + log->Printf ("NativeProcessLinux::%s found main thread with tid %" PRIu64 ", keeping", __FUNCTION__, main_thread_sp->GetID ()); + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s discarding non-main-thread tid %" PRIu64 " due to exec", __FUNCTION__, thread_sp->GetID ()); + } + } + + m_threads.clear (); + + if (main_thread_sp) + { + m_threads.push_back (main_thread_sp); + SetCurrentThreadID (main_thread_sp->GetID ()); + main_thread_sp->SetStoppedByExec(); + } + else + { + SetCurrentThreadID (LLDB_INVALID_THREAD_ID); + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 "no main thread found, discarded all threads, we're in a no-thread state!", __FUNCTION__, GetID ()); + } + + // Tell coordinator about about the "new" (since exec) stopped main thread. + ThreadWasCreated(*main_thread_sp); + + // Let our delegate know we have just exec'd. + NotifyDidExec (); + + // If we have a main thread, indicate we are stopped. + assert (main_thread_sp && "exec called during ptraced process but no main thread metadata tracked"); + + // Let the process know we're stopped. + StopRunningThreads(main_thread_sp->GetID()); + + break; + } + + case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)): + { + // The inferior process or one of its threads is about to exit. + // We don't want to do anything with the thread so we just resume it. In case we + // want to implement "break on thread exit" functionality, we would need to stop + // here. + + unsigned long data = 0; + if (GetEventMessage(thread.GetID(), &data).Fail()) + data = -1; + + if (log) + { + log->Printf ("NativeProcessLinux::%s() received PTRACE_EVENT_EXIT, data = %lx (WIFEXITED=%s,WIFSIGNALED=%s), pid = %" PRIu64 " (%s)", + __FUNCTION__, + data, WIFEXITED (data) ? "true" : "false", WIFSIGNALED (data) ? "true" : "false", + thread.GetID(), + is_main_thread ? "is main thread" : "not main thread"); + } + + if (is_main_thread) + { + SetExitStatus (convert_pid_status_to_exit_type (data), convert_pid_status_to_return_code (data), nullptr, true); + } + + StateType state = thread.GetState(); + if (! StateIsRunningState(state)) + { + // Due to a kernel bug, we may sometimes get this stop after the inferior gets a + // SIGKILL. This confuses our state tracking logic in ResumeThread(), since normally, + // we should not be receiving any ptrace events while the inferior is stopped. This + // makes sure that the inferior is resumed and exits normally. + state = eStateRunning; + } + ResumeThread(thread, state, LLDB_INVALID_SIGNAL_NUMBER); + + break; + } + + case 0: + case TRAP_TRACE: // We receive this on single stepping. + case TRAP_HWBKPT: // We receive this on watchpoint hit + { + // If a watchpoint was hit, report it + uint32_t wp_index; + Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(wp_index, (uintptr_t)info.si_addr); + if (error.Fail() && log) + log->Printf("NativeProcessLinux::%s() " + "received error while checking for watchpoint hits, " + "pid = %" PRIu64 " error = %s", + __FUNCTION__, thread.GetID(), error.AsCString()); + if (wp_index != LLDB_INVALID_INDEX32) + { + MonitorWatchpoint(thread, wp_index); + break; + } + + // Otherwise, report step over + MonitorTrace(thread); + break; + } + + case SI_KERNEL: +#if defined __mips__ + // For mips there is no special signal for watchpoint + // So we check for watchpoint in kernel trap + { + // If a watchpoint was hit, report it + uint32_t wp_index; + Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(wp_index, LLDB_INVALID_ADDRESS); + if (error.Fail() && log) + log->Printf("NativeProcessLinux::%s() " + "received error while checking for watchpoint hits, " + "pid = %" PRIu64 " error = %s", + __FUNCTION__, thread.GetID(), error.AsCString()); + if (wp_index != LLDB_INVALID_INDEX32) + { + MonitorWatchpoint(thread, wp_index); + break; + } + } + // NO BREAK +#endif + case TRAP_BRKPT: + MonitorBreakpoint(thread); + break; + + case SIGTRAP: + case (SIGTRAP | 0x80): + if (log) + log->Printf ("NativeProcessLinux::%s() received unknown SIGTRAP system call stop event, pid %" PRIu64 "tid %" PRIu64 ", resuming", __FUNCTION__, GetID (), thread.GetID()); + + // Ignore these signals until we know more about them. + ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + break; + + default: + assert(false && "Unexpected SIGTRAP code!"); + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 "tid %" PRIu64 " received unhandled SIGTRAP code: 0x%d", + __FUNCTION__, GetID(), thread.GetID(), info.si_code); + break; + + } +} + +void +NativeProcessLinux::MonitorTrace(NativeThreadLinux &thread) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("NativeProcessLinux::%s() received trace event, pid = %" PRIu64 " (single stepping)", + __FUNCTION__, thread.GetID()); + + // This thread is currently stopped. + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void +NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread) +{ + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf("NativeProcessLinux::%s() received breakpoint event, pid = %" PRIu64, + __FUNCTION__, thread.GetID()); + + // Mark the thread as stopped at breakpoint. + thread.SetStoppedByBreakpoint(); + Error error = FixupBreakpointPCAsNeeded(thread); + if (error.Fail()) + if (log) + log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " fixup: %s", + __FUNCTION__, thread.GetID(), error.AsCString()); + + if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != m_threads_stepping_with_breakpoint.end()) + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void +NativeProcessLinux::MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index) +{ + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS)); + if (log) + log->Printf("NativeProcessLinux::%s() received watchpoint event, " + "pid = %" PRIu64 ", wp_index = %" PRIu32, + __FUNCTION__, thread.GetID(), wp_index); + + // Mark the thread as stopped at watchpoint. + // The address is at (lldb::addr_t)info->si_addr if we need it. + thread.SetStoppedByWatchpoint(wp_index); + + // We need to tell all other running threads before we notify the delegate about this stop. + StopRunningThreads(thread.GetID()); +} + +void +NativeProcessLinux::MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited) +{ + const int signo = info.si_signo; + const bool is_from_llgs = info.si_pid == getpid (); + + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, + // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a + // kill(2) or raise(3). Similarly for tgkill(2) on Linux. + // + // IOW, user generated signals never generate what we consider to be a + // "crash". + // + // Similarly, ACK signals generated by this monitor. + + Mutex::Locker locker (m_threads_mutex); + + // Handle the signal. + if (info.si_code == SI_TKILL || info.si_code == SI_USER) + { + if (log) + log->Printf ("NativeProcessLinux::%s() received signal %s (%d) with code %s, (siginfo pid = %d (%s), waitpid pid = %" PRIu64 ")", + __FUNCTION__, + Host::GetSignalAsCString(signo), + signo, + (info.si_code == SI_TKILL ? "SI_TKILL" : "SI_USER"), + info.si_pid, + is_from_llgs ? "from llgs" : "not from llgs", + thread.GetID()); + } + + // Check for thread stop notification. + if (is_from_llgs && (info.si_code == SI_TKILL) && (signo == SIGSTOP)) + { + // This is a tgkill()-based stop. + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread stopped", + __FUNCTION__, + GetID (), + thread.GetID()); + + // Check that we're not already marked with a stop reason. + // Note this thread really shouldn't already be marked as stopped - if we were, that would imply that + // the kernel signaled us with the thread stopping which we handled and marked as stopped, + // and that, without an intervening resume, we received another stop. It is more likely + // that we are missing the marking of a run state somewhere if we find that the thread was + // marked as stopped. + const StateType thread_state = thread.GetState(); + if (!StateIsStoppedState (thread_state, false)) + { + // An inferior thread has stopped because of a SIGSTOP we have sent it. + // Generally, these are not important stops and we don't want to report them as + // they are just used to stop other threads when one thread (the one with the + // *real* stop reason) hits a breakpoint (watchpoint, etc...). However, in the + // case of an asynchronous Interrupt(), this *is* the real stop reason, so we + // leave the signal intact if this is the thread that was chosen as the + // triggering thread. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) + { + if (m_pending_notification_tid == thread.GetID()) + thread.SetStoppedBySignal(SIGSTOP, &info); + else + thread.SetStoppedWithNoReason(); + + SetCurrentThreadID (thread.GetID ()); + SignalIfAllThreadsStopped(); + } + else + { + // We can end up here if stop was initiated by LLGS but by this time a + // thread stop has occurred - maybe initiated by another event. + Error error = ResumeThread(thread, thread.GetState(), 0); + if (error.Fail() && log) + { + log->Printf("NativeProcessLinux::%s failed to resume thread tid %" PRIu64 ": %s", + __FUNCTION__, thread.GetID(), error.AsCString()); + } + } + } + else + { + if (log) + { + // Retrieve the signal name if the thread was stopped by a signal. + int stop_signo = 0; + const bool stopped_by_signal = thread.IsStopped(&stop_signo); + const char *signal_name = stopped_by_signal ? Host::GetSignalAsCString(stop_signo) : ""; + if (!signal_name) + signal_name = ""; + + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread was already marked as a stopped state (state=%s, signal=%d (%s)), leaving stop signal as is", + __FUNCTION__, + GetID (), + thread.GetID(), + StateAsCString (thread_state), + stop_signo, + signal_name); + } + SignalIfAllThreadsStopped(); + } + + // Done handling. + return; + } + + if (log) + log->Printf ("NativeProcessLinux::%s() received signal %s", __FUNCTION__, Host::GetSignalAsCString(signo)); + + // This thread is stopped. + thread.SetStoppedBySignal(signo, &info); + + // Send a stop to the debugger after we get all other threads to stop. + StopRunningThreads(thread.GetID()); +} + +namespace { + +struct EmulatorBaton +{ + NativeProcessLinux* m_process; + NativeRegisterContext* m_reg_context; + + // eRegisterKindDWARF -> RegsiterValue + std::unordered_map m_register_values; + + EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : + m_process(process), m_reg_context(reg_context) {} +}; + +} // anonymous namespace + +static size_t +ReadMemoryCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + void *dst, + size_t length) +{ + EmulatorBaton* emulator_baton = static_cast(baton); + + size_t bytes_read; + emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); + return bytes_read; +} + +static bool +ReadRegisterCallback (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast(baton); + + auto it = emulator_baton->m_register_values.find(reg_info->kinds[eRegisterKindDWARF]); + if (it != emulator_baton->m_register_values.end()) + { + reg_value = it->second; + return true; + } + + // The emulator only fill in the dwarf regsiter numbers (and in some case + // the generic register numbers). Get the full register info from the + // register context based on the dwarf register numbers. + const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( + eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); + + Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); + if (error.Success()) + return true; + + return false; +} + +static bool +WriteRegisterCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast(baton); + emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = reg_value; + return true; +} + +static size_t +WriteMemoryCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t length) +{ + return length; +} + +static lldb::addr_t +ReadFlags (NativeRegisterContext* regsiter_context) +{ + const RegisterInfo* flags_info = regsiter_context->GetRegisterInfo( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + return regsiter_context->ReadRegisterAsUnsigned(flags_info, LLDB_INVALID_ADDRESS); +} + +Error +NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadLinux &thread) +{ + Error error; + NativeRegisterContextSP register_context_sp = thread.GetRegisterContext(); + + std::unique_ptr emulator_ap( + EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, nullptr)); + + if (emulator_ap == nullptr) + return Error("Instruction emulator not found!"); + + EmulatorBaton baton(this, register_context_sp.get()); + emulator_ap->SetBaton(&baton); + emulator_ap->SetReadMemCallback(&ReadMemoryCallback); + emulator_ap->SetReadRegCallback(&ReadRegisterCallback); + emulator_ap->SetWriteMemCallback(&WriteMemoryCallback); + emulator_ap->SetWriteRegCallback(&WriteRegisterCallback); + + if (!emulator_ap->ReadInstruction()) + return Error("Read instruction failed!"); + + bool emulation_result = emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC); + + const RegisterInfo* reg_info_pc = register_context_sp->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo* reg_info_flags = register_context_sp->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + + auto pc_it = baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); + auto flags_it = baton.m_register_values.find(reg_info_flags->kinds[eRegisterKindDWARF]); + + lldb::addr_t next_pc; + lldb::addr_t next_flags; + if (emulation_result) + { + assert(pc_it != baton.m_register_values.end() && "Emulation was successfull but PC wasn't updated"); + next_pc = pc_it->second.GetAsUInt64(); + + if (flags_it != baton.m_register_values.end()) + next_flags = flags_it->second.GetAsUInt64(); + else + next_flags = ReadFlags (register_context_sp.get()); + } + else if (pc_it == baton.m_register_values.end()) + { + // Emulate instruction failed and it haven't changed PC. Advance PC + // with the size of the current opcode because the emulation of all + // PC modifying instruction should be successful. The failure most + // likely caused by a not supported instruction which don't modify PC. + next_pc = register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize(); + next_flags = ReadFlags (register_context_sp.get()); + } + else + { + // The instruction emulation failed after it modified the PC. It is an + // unknown error where we can't continue because the next instruction is + // modifying the PC but we don't know how. + return Error ("Instruction emulation failed unexpectedly."); + } + + if (m_arch.GetMachine() == llvm::Triple::arm) + { + if (next_flags & 0x20) + { + // Thumb mode + error = SetSoftwareBreakpoint(next_pc, 2); + } + else + { + // Arm mode + error = SetSoftwareBreakpoint(next_pc, 4); + } + } + else if (m_arch.GetMachine() == llvm::Triple::mips64 + || m_arch.GetMachine() == llvm::Triple::mips64el + || m_arch.GetMachine() == llvm::Triple::mips + || m_arch.GetMachine() == llvm::Triple::mipsel) + error = SetSoftwareBreakpoint(next_pc, 4); + else + { + // No size hint is given for the next breakpoint + error = SetSoftwareBreakpoint(next_pc, 0); + } + + if (error.Fail()) + return error; + + m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); + + return Error(); +} + +bool +NativeProcessLinux::SupportHardwareSingleStepping() const +{ + if (m_arch.GetMachine() == llvm::Triple::arm + || m_arch.GetMachine() == llvm::Triple::mips64 || m_arch.GetMachine() == llvm::Triple::mips64el + || m_arch.GetMachine() == llvm::Triple::mips || m_arch.GetMachine() == llvm::Triple::mipsel) + return false; + return true; +} + +Error +NativeProcessLinux::Resume (const ResumeActionList &resume_actions) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeProcessLinux::%s called: pid %" PRIu64, __FUNCTION__, GetID ()); + + bool software_single_step = !SupportHardwareSingleStepping(); + + Mutex::Locker locker (m_threads_mutex); + + if (software_single_step) + { + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not contain NULL threads"); + + const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true); + if (action == nullptr) + continue; + + if (action->state == eStateStepping) + { + Error error = SetupSoftwareSingleStepping(static_cast(*thread_sp)); + if (error.Fail()) + return error; + } + } + } + + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not contain NULL threads"); + + const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true); + + if (action == nullptr) + { + if (log) + log->Printf ("NativeProcessLinux::%s no action specified for pid %" PRIu64 " tid %" PRIu64, + __FUNCTION__, GetID (), thread_sp->GetID ()); + continue; + } + + if (log) + { + log->Printf ("NativeProcessLinux::%s processing resume action state %s for pid %" PRIu64 " tid %" PRIu64, + __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ()); + } + + switch (action->state) + { + case eStateRunning: + case eStateStepping: + { + // Run the thread, possibly feeding it the signal. + const int signo = action->signal; + ResumeThread(static_cast(*thread_sp), action->state, signo); + break; + } + + case eStateSuspended: + case eStateStopped: + lldbassert(0 && "Unexpected state"); + + default: + return Error ("NativeProcessLinux::%s (): unexpected state %s specified for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ()); + } + } + + return Error(); +} + +Error +NativeProcessLinux::Halt () +{ + Error error; + + if (kill (GetID (), SIGSTOP) != 0) + error.SetErrorToErrno (); + + return error; +} + +Error +NativeProcessLinux::Detach () +{ + Error error; + + // Stop monitoring the inferior. + m_sigchld_handle.reset(); + + // Tell ptrace to detach from the process. + if (GetID () == LLDB_INVALID_PROCESS_ID) + return error; + + for (auto thread_sp : m_threads) + { + Error e = Detach(thread_sp->GetID()); + if (e.Fail()) + error = e; // Save the error, but still attempt to detach from other threads. + } + + return error; +} + +Error +NativeProcessLinux::Signal (int signo) +{ + Error error; + + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s: sending signal %d (%s) to pid %" PRIu64, + __FUNCTION__, signo, Host::GetSignalAsCString(signo), GetID()); + + if (kill(GetID(), signo)) + error.SetErrorToErrno(); + + return error; +} + +Error +NativeProcessLinux::Interrupt () +{ + // Pick a running thread (or if none, a not-dead stopped thread) as + // the chosen thread that will be the stop-reason thread. + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + NativeThreadProtocolSP running_thread_sp; + NativeThreadProtocolSP stopped_thread_sp; + + if (log) + log->Printf ("NativeProcessLinux::%s selecting running thread for interrupt target", __FUNCTION__); + + Mutex::Locker locker (m_threads_mutex); + + for (auto thread_sp : m_threads) + { + // The thread shouldn't be null but lets just cover that here. + if (!thread_sp) + continue; + + // If we have a running or stepping thread, we'll call that the + // target of the interrupt. + const auto thread_state = thread_sp->GetState (); + if (thread_state == eStateRunning || + thread_state == eStateStepping) + { + running_thread_sp = thread_sp; + break; + } + else if (!stopped_thread_sp && StateIsStoppedState (thread_state, true)) + { + // Remember the first non-dead stopped thread. We'll use that as a backup if there are no running threads. + stopped_thread_sp = thread_sp; + } + } + + if (!running_thread_sp && !stopped_thread_sp) + { + Error error("found no running/stepping or live stopped threads as target for interrupt"); + if (log) + log->Printf ("NativeProcessLinux::%s skipping due to error: %s", __FUNCTION__, error.AsCString ()); + + return error; + } + + NativeThreadProtocolSP deferred_signal_thread_sp = running_thread_sp ? running_thread_sp : stopped_thread_sp; + + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " %s tid %" PRIu64 " chosen for interrupt target", + __FUNCTION__, + GetID (), + running_thread_sp ? "running" : "stopped", + deferred_signal_thread_sp->GetID ()); + + StopRunningThreads(deferred_signal_thread_sp->GetID()); + + return Error(); +} + +Error +NativeProcessLinux::Kill () +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s called for PID %" PRIu64, __FUNCTION__, GetID ()); + + Error error; + + switch (m_state) + { + case StateType::eStateInvalid: + case StateType::eStateExited: + case StateType::eStateCrashed: + case StateType::eStateDetached: + case StateType::eStateUnloaded: + // Nothing to do - the process is already dead. + if (log) + log->Printf ("NativeProcessLinux::%s ignored for PID %" PRIu64 " due to current state: %s", __FUNCTION__, GetID (), StateAsCString (m_state)); + return error; + + case StateType::eStateConnected: + case StateType::eStateAttaching: + case StateType::eStateLaunching: + case StateType::eStateStopped: + case StateType::eStateRunning: + case StateType::eStateStepping: + case StateType::eStateSuspended: + // We can try to kill a process in these states. + break; + } + + if (kill (GetID (), SIGKILL) != 0) + { + error.SetErrorToErrno (); + return error; + } + + return error; +} + +static Error +ParseMemoryRegionInfoFromProcMapsLine (const std::string &maps_line, MemoryRegionInfo &memory_region_info) +{ + memory_region_info.Clear(); + + StringExtractor line_extractor (maps_line.c_str ()); + + // Format: {address_start_hex}-{address_end_hex} perms offset dev inode pathname + // perms: rwxp (letter is present if set, '-' if not, final character is p=private, s=shared). + + // Parse out the starting address + lldb::addr_t start_address = line_extractor.GetHexMaxU64 (false, 0); + + // Parse out hyphen separating start and end address from range. + if (!line_extractor.GetBytesLeft () || (line_extractor.GetChar () != '-')) + return Error ("malformed /proc/{pid}/maps entry, missing dash between address range"); + + // Parse out the ending address + lldb::addr_t end_address = line_extractor.GetHexMaxU64 (false, start_address); + + // Parse out the space after the address. + if (!line_extractor.GetBytesLeft () || (line_extractor.GetChar () != ' ')) + return Error ("malformed /proc/{pid}/maps entry, missing space after range"); + + // Save the range. + memory_region_info.GetRange ().SetRangeBase (start_address); + memory_region_info.GetRange ().SetRangeEnd (end_address); + + // Parse out each permission entry. + if (line_extractor.GetBytesLeft () < 4) + return Error ("malformed /proc/{pid}/maps entry, missing some portion of permissions"); + + // Handle read permission. + const char read_perm_char = line_extractor.GetChar (); + if (read_perm_char == 'r') + memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eYes); + else + { + assert ( (read_perm_char == '-') && "unexpected /proc/{pid}/maps read permission char" ); + memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); + } + + // Handle write permission. + const char write_perm_char = line_extractor.GetChar (); + if (write_perm_char == 'w') + memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eYes); + else + { + assert ( (write_perm_char == '-') && "unexpected /proc/{pid}/maps write permission char" ); + memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); + } + + // Handle execute permission. + const char exec_perm_char = line_extractor.GetChar (); + if (exec_perm_char == 'x') + memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eYes); + else + { + assert ( (exec_perm_char == '-') && "unexpected /proc/{pid}/maps exec permission char" ); + memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); + } + + return Error (); +} + +Error +NativeProcessLinux::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info) +{ + // FIXME review that the final memory region returned extends to the end of the virtual address space, + // with no perms if it is not mapped. + + // Use an approach that reads memory regions from /proc/{pid}/maps. + // Assume proc maps entries are in ascending order. + // FIXME assert if we find differently. + Mutex::Locker locker (m_mem_region_cache_mutex); + + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + Error error; + + if (m_supports_mem_region == LazyBool::eLazyBoolNo) + { + // We're done. + error.SetErrorString ("unsupported"); + return error; + } + + // If our cache is empty, pull the latest. There should always be at least one memory region + // if memory region handling is supported. + if (m_mem_region_cache.empty ()) + { + error = ProcFileReader::ProcessLineByLine (GetID (), "maps", + [&] (const std::string &line) -> bool + { + MemoryRegionInfo info; + const Error parse_error = ParseMemoryRegionInfoFromProcMapsLine (line, info); + if (parse_error.Success ()) + { + m_mem_region_cache.push_back (info); + return true; + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s failed to parse proc maps line '%s': %s", __FUNCTION__, line.c_str (), error.AsCString ()); + return false; + } + }); + + // If we had an error, we'll mark unsupported. + if (error.Fail ()) + { + m_supports_mem_region = LazyBool::eLazyBoolNo; + return error; + } + else if (m_mem_region_cache.empty ()) + { + // No entries after attempting to read them. This shouldn't happen if /proc/{pid}/maps + // is supported. Assume we don't support map entries via procfs. + if (log) + log->Printf ("NativeProcessLinux::%s failed to find any procfs maps entries, assuming no support for memory region metadata retrieval", __FUNCTION__); + m_supports_mem_region = LazyBool::eLazyBoolNo; + error.SetErrorString ("not supported"); + return error; + } + + if (log) + log->Printf ("NativeProcessLinux::%s read %" PRIu64 " memory region entries from /proc/%" PRIu64 "/maps", __FUNCTION__, static_cast (m_mem_region_cache.size ()), GetID ()); + + // We support memory retrieval, remember that. + m_supports_mem_region = LazyBool::eLazyBoolYes; + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s reusing %" PRIu64 " cached memory region entries", __FUNCTION__, static_cast (m_mem_region_cache.size ())); + } + + lldb::addr_t prev_base_address = 0; + + // FIXME start by finding the last region that is <= target address using binary search. Data is sorted. + // There can be a ton of regions on pthreads apps with lots of threads. + for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end (); ++it) + { + MemoryRegionInfo &proc_entry_info = *it; + + // Sanity check assumption that /proc/{pid}/maps entries are ascending. + assert ((proc_entry_info.GetRange ().GetRangeBase () >= prev_base_address) && "descending /proc/pid/maps entries detected, unexpected"); + prev_base_address = proc_entry_info.GetRange ().GetRangeBase (); + + // If the target address comes before this entry, indicate distance to next region. + if (load_addr < proc_entry_info.GetRange ().GetRangeBase ()) + { + range_info.GetRange ().SetRangeBase (load_addr); + range_info.GetRange ().SetByteSize (proc_entry_info.GetRange ().GetRangeBase () - load_addr); + range_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); + + return error; + } + else if (proc_entry_info.GetRange ().Contains (load_addr)) + { + // The target address is within the memory region we're processing here. + range_info = proc_entry_info; + return error; + } + + // The target memory address comes somewhere after the region we just parsed. + } + + // If we made it here, we didn't find an entry that contained the given address. Return the + // load_addr as start and the amount of bytes betwwen load address and the end of the memory as + // size. + range_info.GetRange ().SetRangeBase (load_addr); + switch (m_arch.GetAddressByteSize()) + { + case 4: + range_info.GetRange ().SetByteSize (0x100000000ull - load_addr); + break; + case 8: + range_info.GetRange ().SetByteSize (0ull - load_addr); + break; + default: + assert(false && "Unrecognized data byte size"); + break; + } + range_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); + return error; +} + +void +NativeProcessLinux::DoStopIDBumped (uint32_t newBumpId) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s(newBumpId=%" PRIu32 ") called", __FUNCTION__, newBumpId); + + { + Mutex::Locker locker (m_mem_region_cache_mutex); + if (log) + log->Printf ("NativeProcessLinux::%s clearing %" PRIu64 " entries from the cache", __FUNCTION__, static_cast (m_mem_region_cache.size ())); + m_mem_region_cache.clear (); + } +} + +Error +NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) +{ + // FIXME implementing this requires the equivalent of + // InferiorCallPOSIX::InferiorCallMmap, which depends on + // functional ThreadPlans working with Native*Protocol. +#if 1 + return Error ("not implemented yet"); +#else + addr = LLDB_INVALID_ADDRESS; + + unsigned prot = 0; + if (permissions & lldb::ePermissionsReadable) + prot |= eMmapProtRead; + if (permissions & lldb::ePermissionsWritable) + prot |= eMmapProtWrite; + if (permissions & lldb::ePermissionsExecutable) + prot |= eMmapProtExec; + + // TODO implement this directly in NativeProcessLinux + // (and lift to NativeProcessPOSIX if/when that class is + // refactored out). + if (InferiorCallMmap(this, addr, 0, size, prot, + eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { + m_addr_to_mmap_size[addr] = size; + return Error (); + } else { + addr = LLDB_INVALID_ADDRESS; + return Error("unable to allocate %" PRIu64 " bytes of memory with permissions %s", size, GetPermissionsAsCString (permissions)); + } +#endif +} + +Error +NativeProcessLinux::DeallocateMemory (lldb::addr_t addr) +{ + // FIXME see comments in AllocateMemory - required lower-level + // bits not in place yet (ThreadPlans) + return Error ("not implemented"); +} + +lldb::addr_t +NativeProcessLinux::GetSharedLibraryInfoAddress () +{ +#if 1 + // punt on this for now + return LLDB_INVALID_ADDRESS; +#else + // Return the image info address for the exe module +#if 1 + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + ModuleSP module_sp; + Error error = GetExeModuleSP (module_sp); + if (error.Fail ()) + { + if (log) + log->Warning ("NativeProcessLinux::%s failed to retrieve exe module: %s", __FUNCTION__, error.AsCString ()); + return LLDB_INVALID_ADDRESS; + } + + if (module_sp == nullptr) + { + if (log) + log->Warning ("NativeProcessLinux::%s exe module returned was NULL", __FUNCTION__); + return LLDB_INVALID_ADDRESS; + } + + ObjectFileSP object_file_sp = module_sp->GetObjectFile (); + if (object_file_sp == nullptr) + { + if (log) + log->Warning ("NativeProcessLinux::%s exe module returned a NULL object file", __FUNCTION__); + return LLDB_INVALID_ADDRESS; + } + + return obj_file_sp->GetImageInfoAddress(); +#else + Target *target = &GetTarget(); + ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(target); + + if (addr.IsValid()) + return addr.GetLoadAddress(target); + return LLDB_INVALID_ADDRESS; +#endif +#endif // punt on this for now +} + +size_t +NativeProcessLinux::UpdateThreads () +{ + // The NativeProcessLinux monitoring threads are always up to date + // with respect to thread state and they keep the thread list + // populated properly. All this method needs to do is return the + // thread count. + Mutex::Locker locker (m_threads_mutex); + return m_threads.size (); +} + +bool +NativeProcessLinux::GetArchitecture (ArchSpec &arch) const +{ + arch = m_arch; + return true; +} + +Error +NativeProcessLinux::GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size) +{ + // FIXME put this behind a breakpoint protocol class that can be + // set per architecture. Need ARM, MIPS support here. + static const uint8_t g_i386_opcode [] = { 0xCC }; + + switch (m_arch.GetMachine ()) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + actual_opcode_size = static_cast (sizeof(g_i386_opcode)); + return Error (); + + case llvm::Triple::arm: + case llvm::Triple::aarch64: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + case llvm::Triple::mips: + case llvm::Triple::mipsel: + // On these architectures the PC don't get updated for breakpoint hits + actual_opcode_size = 0; + return Error (); + + default: + assert(false && "CPU type not supported!"); + return Error ("CPU type not supported"); + } +} + +Error +NativeProcessLinux::SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware) +{ + if (hardware) + return Error ("NativeProcessLinux does not support hardware breakpoints"); + else + return SetSoftwareBreakpoint (addr, size); +} + +Error +NativeProcessLinux::GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hint, + size_t &actual_opcode_size, + const uint8_t *&trap_opcode_bytes) +{ + // FIXME put this behind a breakpoint protocol class that can be set per + // architecture. Need MIPS support here. + static const uint8_t g_aarch64_opcode[] = { 0x00, 0x00, 0x20, 0xd4 }; + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the + // linux kernel does otherwise. + static const uint8_t g_arm_breakpoint_opcode[] = { 0xf0, 0x01, 0xf0, 0xe7 }; + static const uint8_t g_i386_opcode [] = { 0xCC }; + static const uint8_t g_mips64_opcode[] = { 0x00, 0x00, 0x00, 0x0d }; + static const uint8_t g_mips64el_opcode[] = { 0x0d, 0x00, 0x00, 0x00 }; + static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde }; + + switch (m_arch.GetMachine ()) + { + case llvm::Triple::aarch64: + trap_opcode_bytes = g_aarch64_opcode; + actual_opcode_size = sizeof(g_aarch64_opcode); + return Error (); + + case llvm::Triple::arm: + switch (trap_opcode_size_hint) + { + case 2: + trap_opcode_bytes = g_thumb_breakpoint_opcode; + actual_opcode_size = sizeof(g_thumb_breakpoint_opcode); + return Error (); + case 4: + trap_opcode_bytes = g_arm_breakpoint_opcode; + actual_opcode_size = sizeof(g_arm_breakpoint_opcode); + return Error (); + default: + assert(false && "Unrecognised trap opcode size hint!"); + return Error ("Unrecognised trap opcode size hint!"); + } + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + trap_opcode_bytes = g_i386_opcode; + actual_opcode_size = sizeof(g_i386_opcode); + return Error (); + + case llvm::Triple::mips: + case llvm::Triple::mips64: + trap_opcode_bytes = g_mips64_opcode; + actual_opcode_size = sizeof(g_mips64_opcode); + return Error (); + + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: + trap_opcode_bytes = g_mips64el_opcode; + actual_opcode_size = sizeof(g_mips64el_opcode); + return Error (); + + default: + assert(false && "CPU type not supported!"); + return Error ("CPU type not supported"); + } +} + +#if 0 +ProcessMessage::CrashReason +NativeProcessLinux::GetCrashReasonForSIGSEGV(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGSEGV); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGSEGV"); + break; + case SI_KERNEL: + // Linux will occasionally send spurious SI_KERNEL codes. + // (this is poorly documented in sigaction) + // One way to get this is via unaligned SIMD loads. + reason = ProcessMessage::eInvalidAddress; // for lack of anything better + break; + case SEGV_MAPERR: + reason = ProcessMessage::eInvalidAddress; + break; + case SEGV_ACCERR: + reason = ProcessMessage::ePrivilegedAddress; + break; + } + + return reason; +} +#endif + + +#if 0 +ProcessMessage::CrashReason +NativeProcessLinux::GetCrashReasonForSIGILL(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGILL); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGILL"); + break; + case ILL_ILLOPC: + reason = ProcessMessage::eIllegalOpcode; + break; + case ILL_ILLOPN: + reason = ProcessMessage::eIllegalOperand; + break; + case ILL_ILLADR: + reason = ProcessMessage::eIllegalAddressingMode; + break; + case ILL_ILLTRP: + reason = ProcessMessage::eIllegalTrap; + break; + case ILL_PRVOPC: + reason = ProcessMessage::ePrivilegedOpcode; + break; + case ILL_PRVREG: + reason = ProcessMessage::ePrivilegedRegister; + break; + case ILL_COPROC: + reason = ProcessMessage::eCoprocessorError; + break; + case ILL_BADSTK: + reason = ProcessMessage::eInternalStackError; + break; + } + + return reason; +} +#endif + +#if 0 +ProcessMessage::CrashReason +NativeProcessLinux::GetCrashReasonForSIGFPE(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGFPE); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGFPE"); + break; + case FPE_INTDIV: + reason = ProcessMessage::eIntegerDivideByZero; + break; + case FPE_INTOVF: + reason = ProcessMessage::eIntegerOverflow; + break; + case FPE_FLTDIV: + reason = ProcessMessage::eFloatDivideByZero; + break; + case FPE_FLTOVF: + reason = ProcessMessage::eFloatOverflow; + break; + case FPE_FLTUND: + reason = ProcessMessage::eFloatUnderflow; + break; + case FPE_FLTRES: + reason = ProcessMessage::eFloatInexactResult; + break; + case FPE_FLTINV: + reason = ProcessMessage::eFloatInvalidOperation; + break; + case FPE_FLTSUB: + reason = ProcessMessage::eFloatSubscriptRange; + break; + } + + return reason; +} +#endif + +#if 0 +ProcessMessage::CrashReason +NativeProcessLinux::GetCrashReasonForSIGBUS(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGBUS); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGBUS"); + break; + case BUS_ADRALN: + reason = ProcessMessage::eIllegalAlignment; + break; + case BUS_ADRERR: + reason = ProcessMessage::eIllegalAddress; + break; + case BUS_OBJERR: + reason = ProcessMessage::eHardwareError; + break; + } + + return reason; +} +#endif + +Error +NativeProcessLinux::ReadMemory (lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) +{ + if (ProcessVmReadvSupported()) { + // The process_vm_readv path is about 50 times faster than ptrace api. We want to use + // this syscall if it is supported. + + const ::pid_t pid = GetID(); + + struct iovec local_iov, remote_iov; + local_iov.iov_base = buf; + local_iov.iov_len = size; + remote_iov.iov_base = reinterpret_cast(addr); + remote_iov.iov_len = size; + + bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0); + const bool success = bytes_read == size; + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s using process_vm_readv to read %zd bytes from inferior address 0x%" PRIx64": %s", + __FUNCTION__, size, addr, success ? "Success" : strerror(errno)); + + if (success) + return Error(); + // else + // the call failed for some reason, let's retry the read using ptrace api. + } + + unsigned char *dst = static_cast(buf); + size_t remainder; + long data; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL)); + if (log) + ProcessPOSIXLog::IncNestLevel(); + if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY)) + log->Printf ("NativeProcessLinux::%s(%p, %p, %zd, _)", __FUNCTION__, (void*)addr, buf, size); + + for (bytes_read = 0; bytes_read < size; bytes_read += remainder) + { + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_PEEKDATA, GetID(), (void*)addr, nullptr, 0, &data); + if (error.Fail()) + { + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; + } + + remainder = size - bytes_read; + remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; + + // Copy the data into our buffer + memcpy(dst, &data, remainder); + + if (log && ProcessPOSIXLog::AtTopNestLevel() && + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) || + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) && + size <= POSIX_LOG_MEMORY_SHORT_BYTES))) + { + uintptr_t print_dst = 0; + // Format bytes from data by moving into print_dst for log output + for (unsigned i = 0; i < remainder; ++i) + print_dst |= (((data >> i*8) & 0xFF) << i*8); + log->Printf ("NativeProcessLinux::%s() [0x%" PRIx64 "]:0x%" PRIx64 " (0x%" PRIx64 ")", + __FUNCTION__, addr, uint64_t(print_dst), uint64_t(data)); + } + addr += k_ptrace_word_size; + dst += k_ptrace_word_size; + } + + if (log) + ProcessPOSIXLog::DecNestLevel(); + return Error(); +} + +Error +NativeProcessLinux::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) +{ + Error error = ReadMemory(addr, buf, size, bytes_read); + if (error.Fail()) return error; + return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size); +} + +Error +NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) +{ + const unsigned char *src = static_cast(buf); + size_t remainder; + Error error; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL)); + if (log) + ProcessPOSIXLog::IncNestLevel(); + if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY)) + log->Printf ("NativeProcessLinux::%s(0x%" PRIx64 ", %p, %zu)", __FUNCTION__, addr, buf, size); + + for (bytes_written = 0; bytes_written < size; bytes_written += remainder) + { + remainder = size - bytes_written; + remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; + + if (remainder == k_ptrace_word_size) + { + unsigned long data = 0; + memcpy(&data, src, k_ptrace_word_size); + + if (log && ProcessPOSIXLog::AtTopNestLevel() && + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) || + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) && + size <= POSIX_LOG_MEMORY_SHORT_BYTES))) + log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__, + (void*)addr, *(const unsigned long*)src, data); + + error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(), (void*)addr, (void*)data); + if (error.Fail()) + { + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; + } + } + else + { + unsigned char buff[8]; + size_t bytes_read; + error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read); + if (error.Fail()) + { + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; + } + + memcpy(buff, src, remainder); + + size_t bytes_written_rec; + error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec); + if (error.Fail()) + { + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; + } + + if (log && ProcessPOSIXLog::AtTopNestLevel() && + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) || + (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) && + size <= POSIX_LOG_MEMORY_SHORT_BYTES))) + log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__, + (void*)addr, *(const unsigned long*)src, *(unsigned long*)buff); + } + + addr += k_ptrace_word_size; + src += k_ptrace_word_size; + } + if (log) + ProcessPOSIXLog::DecNestLevel(); + return error; +} + +Error +NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + if (log) + log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid, + Host::GetSignalAsCString(signo)); + + + + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + Error error = PtraceWrapper(PTRACE_CONT, tid, nullptr, (void*)data); + + if (log) + log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " result = %s", __FUNCTION__, tid, error.Success() ? "true" : "false"); + return error; +} + +Error +NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) +{ + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // If hardware single-stepping is not supported, we just do a continue. The breakpoint on the + // next instruction has been setup in NativeProcessLinux::Resume. + return PtraceWrapper(SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT, + tid, nullptr, (void*)data); +} + +Error +NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) +{ + return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo); +} + +Error +NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message) +{ + return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message); +} + +Error +NativeProcessLinux::Detach(lldb::tid_t tid) +{ + if (tid == LLDB_INVALID_THREAD_ID) + return Error(); + + return PtraceWrapper(PTRACE_DETACH, tid); +} + +bool +NativeProcessLinux::DupDescriptor(const FileSpec &file_spec, int fd, int flags) +{ + int target_fd = open(file_spec.GetCString(), flags, 0666); + + if (target_fd == -1) + return false; + + if (dup2(target_fd, fd) == -1) + return false; + + return (close(target_fd) == -1) ? false : true; +} + +bool +NativeProcessLinux::HasThreadNoLock (lldb::tid_t thread_id) +{ + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not contain NULL threads"); + if (thread_sp->GetID () == thread_id) + { + // We have this thread. + return true; + } + } + + // We don't have this thread. + return false; +} + +bool +NativeProcessLinux::StopTrackingThread (lldb::tid_t thread_id) +{ + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + + if (log) + log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__, thread_id); + + bool found = false; + + Mutex::Locker locker (m_threads_mutex); + for (auto it = m_threads.begin (); it != m_threads.end (); ++it) + { + if (*it && ((*it)->GetID () == thread_id)) + { + m_threads.erase (it); + found = true; + break; + } + } + + SignalIfAllThreadsStopped(); + + return found; +} + +NativeThreadLinuxSP +NativeProcessLinux::AddThread (lldb::tid_t thread_id) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + Mutex::Locker locker (m_threads_mutex); + + if (log) + { + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " adding thread with tid %" PRIu64, + __FUNCTION__, + GetID (), + thread_id); + } + + assert (!HasThreadNoLock (thread_id) && "attempted to add a thread by id that already exists"); + + // If this is the first thread, save it as the current thread + if (m_threads.empty ()) + SetCurrentThreadID (thread_id); + + auto thread_sp = std::make_shared(this, thread_id); + m_threads.push_back (thread_sp); + return thread_sp; +} + +Error +NativeProcessLinux::FixupBreakpointPCAsNeeded(NativeThreadLinux &thread) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + Error error; + + // Find out the size of a breakpoint (might depend on where we are in the code). + NativeRegisterContextSP context_sp = thread.GetRegisterContext(); + if (!context_sp) + { + error.SetErrorString ("cannot get a NativeRegisterContext for the thread"); + if (log) + log->Printf ("NativeProcessLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); + return error; + } + + uint32_t breakpoint_size = 0; + error = GetSoftwareBreakpointPCOffset(breakpoint_size); + if (error.Fail ()) + { + if (log) + log->Printf ("NativeProcessLinux::%s GetBreakpointSize() failed: %s", __FUNCTION__, error.AsCString ()); + return error; + } + else + { + if (log) + log->Printf ("NativeProcessLinux::%s breakpoint size: %" PRIu32, __FUNCTION__, breakpoint_size); + } + + // First try probing for a breakpoint at a software breakpoint location: PC - breakpoint size. + const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation (); + lldb::addr_t breakpoint_addr = initial_pc_addr; + if (breakpoint_size > 0) + { + // Do not allow breakpoint probe to wrap around. + if (breakpoint_addr >= breakpoint_size) + breakpoint_addr -= breakpoint_size; + } + + // Check if we stopped because of a breakpoint. + NativeBreakpointSP breakpoint_sp; + error = m_breakpoint_list.GetBreakpoint (breakpoint_addr, breakpoint_sp); + if (!error.Success () || !breakpoint_sp) + { + // We didn't find one at a software probe location. Nothing to do. + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " no lldb breakpoint found at current pc with adjustment: 0x%" PRIx64, __FUNCTION__, GetID (), breakpoint_addr); + return Error (); + } + + // If the breakpoint is not a software breakpoint, nothing to do. + if (!breakpoint_sp->IsSoftwareBreakpoint ()) + { + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " breakpoint found at 0x%" PRIx64 ", not software, nothing to adjust", __FUNCTION__, GetID (), breakpoint_addr); + return Error (); + } + + // + // We have a software breakpoint and need to adjust the PC. + // + + // Sanity check. + if (breakpoint_size == 0) + { + // Nothing to do! How did we get here? + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " breakpoint found at 0x%" PRIx64 ", it is software, but the size is zero, nothing to do (unexpected)", __FUNCTION__, GetID (), breakpoint_addr); + return Error (); + } + + // Change the program counter. + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 ": changing PC from 0x%" PRIx64 " to 0x%" PRIx64, __FUNCTION__, GetID(), thread.GetID(), initial_pc_addr, breakpoint_addr); + + error = context_sp->SetPC (breakpoint_addr); + if (error.Fail ()) + { + if (log) + log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 ": failed to set PC: %s", __FUNCTION__, GetID(), thread.GetID(), error.AsCString ()); + return error; + } + + return error; +} + +Error +NativeProcessLinux::GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec) +{ + FileSpec module_file_spec(module_path, true); + + bool found = false; + file_spec.Clear(); + ProcFileReader::ProcessLineByLine(GetID(), "maps", + [&] (const std::string &line) + { + SmallVector columns; + StringRef(line).split(columns, " ", -1, false); + if (columns.size() < 6) + return true; // continue searching + + FileSpec this_file_spec(columns[5].str().c_str(), false); + if (this_file_spec.GetFilename() != module_file_spec.GetFilename()) + return true; // continue searching + + file_spec = this_file_spec; + found = true; + return false; // we are done + }); + + if (! found) + return Error("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", + module_file_spec.GetFilename().AsCString(), GetID()); + + return Error(); +} + +Error +NativeProcessLinux::GetFileLoadAddress(const llvm::StringRef& file_name, lldb::addr_t& load_addr) +{ + load_addr = LLDB_INVALID_ADDRESS; + Error error = ProcFileReader::ProcessLineByLine (GetID (), "maps", + [&] (const std::string &line) -> bool + { + StringRef maps_row(line); + + SmallVector maps_columns; + maps_row.split(maps_columns, StringRef(" "), -1, false); + + if (maps_columns.size() < 6) + { + // Return true to continue reading the proc file + return true; + } + + if (maps_columns[5] == file_name) + { + StringExtractor addr_extractor(maps_columns[0].str().c_str()); + load_addr = addr_extractor.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + + // Return false to stop reading the proc file further + return false; + } + + // Return true to continue reading the proc file + return true; + }); + return error; +} + +NativeThreadLinuxSP +NativeProcessLinux::GetThreadByID(lldb::tid_t tid) +{ + return std::static_pointer_cast(NativeProcessProtocol::GetThreadByID(tid)); +} + +Error +NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo) +{ + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + + if (log) + log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", + __FUNCTION__, thread.GetID()); + + // Before we do the resume below, first check if we have a pending + // stop notification that is currently waiting for + // all threads to stop. This is potentially a buggy situation since + // we're ostensibly waiting for threads to stop before we send out the + // pending notification, and here we are resuming one before we send + // out the pending stop notification. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && log) + { + log->Printf("NativeProcessLinux::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that is actively waiting for this thread to stop. Valid sequence of events?", __FUNCTION__, thread.GetID(), m_pending_notification_tid); + } + + // Request a resume. We expect this to be synchronous and the system + // to reflect it is running after this completes. + switch (state) + { + case eStateRunning: + { + thread.SetRunning(); + const auto resume_result = Resume(thread.GetID(), signo); + if (resume_result.Success()) + SetState(eStateRunning, true); + return resume_result; + } + case eStateStepping: + { + thread.SetStepping(); + const auto step_result = SingleStep(thread.GetID(), signo); + if (step_result.Success()) + SetState(eStateRunning, true); + return step_result; + } + default: + if (log) + log->Printf("NativeProcessLinux::%s Unhandled state %s.", + __FUNCTION__, StateAsCString(state)); + llvm_unreachable("Unhandled state for resume"); + } +} + +//===----------------------------------------------------------------------===// + +void +NativeProcessLinux::StopRunningThreads(const lldb::tid_t triggering_tid) +{ + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + + if (log) + { + log->Printf("NativeProcessLinux::%s about to process event: (triggering_tid: %" PRIu64 ")", + __FUNCTION__, triggering_tid); + } + + m_pending_notification_tid = triggering_tid; + + // Request a stop for all the thread stops that need to be stopped + // and are not already known to be stopped. + for (const auto &thread_sp: m_threads) + { + if (StateIsRunningState(thread_sp->GetState())) + static_pointer_cast(thread_sp)->RequestStop(); + } + + SignalIfAllThreadsStopped(); + + if (log) + { + log->Printf("NativeProcessLinux::%s event processing done", __FUNCTION__); + } +} + +void +NativeProcessLinux::SignalIfAllThreadsStopped() +{ + if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID) + return; // No pending notification. Nothing to do. + + for (const auto &thread_sp: m_threads) + { + if (StateIsRunningState(thread_sp->GetState())) + return; // Some threads are still running. Don't signal yet. + } + + // We have a pending notification and all threads have stopped. + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); + + // Clear any temporary breakpoints we used to implement software single stepping. + for (const auto &thread_info: m_threads_stepping_with_breakpoint) + { + Error error = RemoveBreakpoint (thread_info.second); + if (error.Fail()) + if (log) + log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " remove stepping breakpoint: %s", + __FUNCTION__, thread_info.first, error.AsCString()); + } + m_threads_stepping_with_breakpoint.clear(); + + // Notify the delegate about the stop + SetCurrentThreadID(m_pending_notification_tid); + SetState(StateType::eStateStopped, true); + m_pending_notification_tid = LLDB_INVALID_THREAD_ID; +} + +void +NativeProcessLinux::ThreadWasCreated(NativeThreadLinux &thread) +{ + Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); + + if (log) + log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__, thread.GetID()); + + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && StateIsRunningState(thread.GetState())) + { + // We will need to wait for this new thread to stop as well before firing the + // notification. + thread.RequestStop(); + } +} + +void +NativeProcessLinux::SigchldHandler() +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + // Process all pending waitpid notifications. + while (true) + { + int status = -1; + ::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG); + + if (wait_pid == 0) + break; // We are done. + + if (wait_pid == -1) + { + if (errno == EINTR) + continue; + + Error error(errno, eErrorTypePOSIX); + if (log) + log->Printf("NativeProcessLinux::%s waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG) failed: %s", + __FUNCTION__, error.AsCString()); + break; + } + + bool exited = false; + int signal = 0; + int exit_status = 0; + const char *status_cstr = nullptr; + if (WIFSTOPPED(status)) + { + signal = WSTOPSIG(status); + status_cstr = "STOPPED"; + } + else if (WIFEXITED(status)) + { + exit_status = WEXITSTATUS(status); + status_cstr = "EXITED"; + exited = true; + } + else if (WIFSIGNALED(status)) + { + signal = WTERMSIG(status); + status_cstr = "SIGNALED"; + if (wait_pid == static_cast< ::pid_t>(GetID())) { + exited = true; + exit_status = -1; + } + } + else + status_cstr = "(\?\?\?)"; + + if (log) + log->Printf("NativeProcessLinux::%s: waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG)" + "=> pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i", + __FUNCTION__, wait_pid, status, status_cstr, signal, exit_status); + + MonitorCallback (wait_pid, exited, signal, exit_status); + } +} + +// Wrapper for ptrace to catch errors and log calls. +// Note that ptrace sets errno on error because -1 can be a valid result (i.e. for PTRACE_PEEK*) +Error +NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, long *result) +{ + Error error; + long int ret; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PTRACE)); + + PtraceDisplayBytes(req, data, data_size); + + errno = 0; + if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET) + ret = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), *(unsigned int *)addr, data); + else + ret = ptrace(static_cast<__ptrace_request>(req), static_cast< ::pid_t>(pid), addr, data); + + if (ret == -1) + error.SetErrorToErrno(); + + if (result) + *result = ret; + + if (log) + log->Printf("ptrace(%d, %" PRIu64 ", %p, %p, %zu)=%lX", req, pid, addr, data, data_size, ret); + + PtraceDisplayBytes(req, data, data_size); + + if (log && error.GetError() != 0) + { + const char* str; + switch (error.GetError()) + { + case ESRCH: str = "ESRCH"; break; + case EINVAL: str = "EINVAL"; break; + case EBUSY: str = "EBUSY"; break; + case EPERM: str = "EPERM"; break; + default: str = error.AsCString(); + } + log->Printf("ptrace() failed; errno=%d (%s)", error.GetError(), str); + } + + return error; +} diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.h b/source/Plugins/Process/Linux/NativeProcessLinux.h new file mode 100644 index 000000000000..7bac1dc8dbb7 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -0,0 +1,329 @@ +//===-- NativeProcessLinux.h ---------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeProcessLinux_H_ +#define liblldb_NativeProcessLinux_H_ + +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/lldb-types.h" +#include "lldb/Host/Debug.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/MemoryRegionInfo.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "NativeThreadLinux.h" + +namespace lldb_private { + class Error; + class Module; + class Scalar; + +namespace process_linux { + /// @class NativeProcessLinux + /// @brief Manages communication with the inferior (debugee) process. + /// + /// Upon construction, this class prepares and launches an inferior process for + /// debugging. + /// + /// Changes in the inferior process state are broadcasted. + class NativeProcessLinux: public NativeProcessProtocol + { + friend Error + NativeProcessProtocol::Launch (ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &process_sp); + + friend Error + NativeProcessProtocol::Attach (lldb::pid_t pid, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &process_sp); + + public: + // --------------------------------------------------------------------- + // NativeProcessProtocol Interface + // --------------------------------------------------------------------- + Error + Resume (const ResumeActionList &resume_actions) override; + + Error + Halt () override; + + Error + Detach () override; + + Error + Signal (int signo) override; + + Error + Interrupt () override; + + Error + Kill () override; + + Error + GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info) override; + + Error + ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; + + Error + ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; + + Error + WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; + + Error + AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) override; + + Error + DeallocateMemory (lldb::addr_t addr) override; + + lldb::addr_t + GetSharedLibraryInfoAddress () override; + + size_t + UpdateThreads () override; + + bool + GetArchitecture (ArchSpec &arch) const override; + + Error + SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware) override; + + void + DoStopIDBumped (uint32_t newBumpId) override; + + Error + GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec) override; + + Error + GetFileLoadAddress(const llvm::StringRef& file_name, lldb::addr_t& load_addr) override; + + NativeThreadLinuxSP + GetThreadByID(lldb::tid_t id); + + // --------------------------------------------------------------------- + // Interface used by NativeRegisterContext-derived classes. + // --------------------------------------------------------------------- + static Error + PtraceWrapper(int req, + lldb::pid_t pid, + void *addr = nullptr, + void *data = nullptr, + size_t data_size = 0, + long *result = nullptr); + + protected: + // --------------------------------------------------------------------- + // NativeProcessProtocol protected interface + // --------------------------------------------------------------------- + Error + GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) override; + + private: + + MainLoop::SignalHandleUP m_sigchld_handle; + ArchSpec m_arch; + + LazyBool m_supports_mem_region; + std::vector m_mem_region_cache; + Mutex m_mem_region_cache_mutex; + + lldb::tid_t m_pending_notification_tid; + + // List of thread ids stepping with a breakpoint with the address of + // the relevan breakpoint + std::map m_threads_stepping_with_breakpoint; + + /// @class LauchArgs + /// + /// @brief Simple structure to pass data to the thread responsible for + /// launching a child process. + struct LaunchArgs + { + LaunchArgs(Module *module, + char const **argv, + char const **envp, + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, + const ProcessLaunchInfo &launch_info); + + ~LaunchArgs(); + + Module *m_module; // The executable image to launch. + char const **m_argv; // Process arguments. + char const **m_envp; // Process environment. + const FileSpec m_stdin_file_spec; // Redirect stdin if not empty. + const FileSpec m_stdout_file_spec; // Redirect stdout if not empty. + const FileSpec m_stderr_file_spec; // Redirect stderr if not empty. + const FileSpec m_working_dir; // Working directory or empty. + const ProcessLaunchInfo &m_launch_info; + }; + + typedef std::function< ::pid_t(Error &)> InitialOperation; + + // --------------------------------------------------------------------- + // Private Instance Methods + // --------------------------------------------------------------------- + NativeProcessLinux (); + + /// Launches an inferior process ready for debugging. Forms the + /// implementation of Process::DoLaunch. + void + LaunchInferior ( + MainLoop &mainloop, + Module *module, + char const *argv[], + char const *envp[], + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, + const ProcessLaunchInfo &launch_info, + Error &error); + + /// Attaches to an existing process. Forms the + /// implementation of Process::DoAttach + void + AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error); + + ::pid_t + Launch(LaunchArgs *args, Error &error); + + ::pid_t + Attach(lldb::pid_t pid, Error &error); + + static Error + SetDefaultPtraceOpts(const lldb::pid_t); + + static bool + DupDescriptor(const FileSpec &file_spec, int fd, int flags); + + static void * + MonitorThread(void *baton); + + void + MonitorCallback(lldb::pid_t pid, bool exited, int signal, int status); + + void + WaitForNewThread(::pid_t tid); + + void + MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread); + + void + MonitorTrace(NativeThreadLinux &thread); + + void + MonitorBreakpoint(NativeThreadLinux &thread); + + void + MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index); + + void + MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited); + + bool + SupportHardwareSingleStepping() const; + + Error + SetupSoftwareSingleStepping(NativeThreadLinux &thread); + +#if 0 + static ::ProcessMessage::CrashReason + GetCrashReasonForSIGSEGV(const siginfo_t *info); + + static ::ProcessMessage::CrashReason + GetCrashReasonForSIGILL(const siginfo_t *info); + + static ::ProcessMessage::CrashReason + GetCrashReasonForSIGFPE(const siginfo_t *info); + + static ::ProcessMessage::CrashReason + GetCrashReasonForSIGBUS(const siginfo_t *info); +#endif + + bool + HasThreadNoLock (lldb::tid_t thread_id); + + bool + StopTrackingThread (lldb::tid_t thread_id); + + NativeThreadLinuxSP + AddThread (lldb::tid_t thread_id); + + Error + GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size); + + Error + FixupBreakpointPCAsNeeded(NativeThreadLinux &thread); + + /// Writes a siginfo_t structure corresponding to the given thread ID to the + /// memory region pointed to by @p siginfo. + Error + GetSignalInfo(lldb::tid_t tid, void *siginfo); + + /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) + /// corresponding to the given thread ID to the memory pointed to by @p + /// message. + Error + GetEventMessage(lldb::tid_t tid, unsigned long *message); + + /// Resumes the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Error + Resume(lldb::tid_t tid, uint32_t signo); + + /// Single steps the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Error + SingleStep(lldb::tid_t tid, uint32_t signo); + + void + NotifyThreadDeath (lldb::tid_t tid); + + Error + Detach(lldb::tid_t tid); + + + // This method is requests a stop on all threads which are still running. It sets up a + // deferred delegate notification, which will fire once threads report as stopped. The + // triggerring_tid will be set as the current thread (main stop reason). + void + StopRunningThreads(lldb::tid_t triggering_tid); + + // Notify the delegate if all threads have stopped. + void SignalIfAllThreadsStopped(); + + // Resume the given thread, optionally passing it the given signal. The type of resume + // operation (continue, single-step) depends on the state parameter. + Error + ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo); + + void + ThreadWasCreated(NativeThreadLinux &thread); + + void + SigchldHandler(); + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessLinux_H_ diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp new file mode 100644 index 000000000000..df0a008ff5f7 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp @@ -0,0 +1,230 @@ +//===-- NativeRegisterContextLinux.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeRegisterContextLinux.h" + +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Host/linux/Ptrace.h" + +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +NativeRegisterContextLinux::NativeRegisterContextLinux(NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx, + RegisterInfoInterface *reg_info_interface_p) : + NativeRegisterContextRegisterInfo(native_thread, concrete_frame_idx, reg_info_interface_p) +{} + +lldb::ByteOrder +NativeRegisterContextLinux::GetByteOrder() const +{ + // Get the target process whose privileged thread was used for the register read. + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid; + + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + if (!process_sp) + return byte_order; + + if (!process_sp->GetByteOrder (byte_order)) + { + // FIXME log here + } + + return byte_order; +} + +Error +NativeRegisterContextLinux::ReadRegisterRaw(uint32_t reg_index, RegisterValue ®_value) +{ + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + return Error("register %" PRIu32 " not found", reg_index); + + return DoReadRegisterValue(reg_info->byte_offset, reg_info->name, reg_info->byte_size, reg_value); +} + +Error +NativeRegisterContextLinux::WriteRegisterRaw(uint32_t reg_index, const RegisterValue ®_value) +{ + uint32_t reg_to_write = reg_index; + RegisterValue value_to_write = reg_value; + + // Check if this is a subregister of a full register. + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_index); + if (reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) + { + Error error; + + RegisterValue full_value; + uint32_t full_reg = reg_info->invalidate_regs[0]; + const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg); + + // Read the full register. + error = ReadRegister(full_reg_info, full_value); + if (error.Fail ()) + return error; + + lldb::ByteOrder byte_order = GetByteOrder(); + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the full register. + const uint32_t dest_size = full_value.GetAsMemoryData (full_reg_info, + dst, + sizeof(dst), + byte_order, + error); + if (error.Success() && dest_size) + { + uint8_t src[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the source data. + const uint32_t src_size = reg_value.GetAsMemoryData (reg_info, src, sizeof(src), byte_order, error); + if (error.Success() && src_size && (src_size < dest_size)) + { + // Copy the src bytes to the destination. + memcpy (dst + (reg_info->byte_offset & 0x1), src, src_size); + // Set this full register as the value to write. + value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order); + value_to_write.SetType(full_reg_info); + reg_to_write = full_reg; + } + } + } + + const RegisterInfo *const register_to_write_info_p = GetRegisterInfoAtIndex (reg_to_write); + assert (register_to_write_info_p && "register to write does not have valid RegisterInfo"); + if (!register_to_write_info_p) + return Error("NativeRegisterContextLinux::%s failed to get RegisterInfo for write register index %" PRIu32, __FUNCTION__, reg_to_write); + + return DoWriteRegisterValue(reg_info->byte_offset, reg_info->name, reg_value); +} + +Error +NativeRegisterContextLinux::ReadGPR() +{ + void* buf = GetGPRBuffer(); + if (!buf) + return Error("GPR buffer is NULL"); + size_t buf_size = GetGPRSize(); + + return DoReadGPR(buf, buf_size); +} + +Error +NativeRegisterContextLinux::WriteGPR() +{ + void* buf = GetGPRBuffer(); + if (!buf) + return Error("GPR buffer is NULL"); + size_t buf_size = GetGPRSize(); + + return DoWriteGPR(buf, buf_size); +} + +Error +NativeRegisterContextLinux::ReadFPR() +{ + void* buf = GetFPRBuffer(); + if (!buf) + return Error("FPR buffer is NULL"); + size_t buf_size = GetFPRSize(); + + return DoReadFPR(buf, buf_size); +} + +Error +NativeRegisterContextLinux::WriteFPR() +{ + void* buf = GetFPRBuffer(); + if (!buf) + return Error("FPR buffer is NULL"); + size_t buf_size = GetFPRSize(); + + return DoWriteFPR(buf, buf_size); +} + +Error +NativeRegisterContextLinux::ReadRegisterSet(void *buf, size_t buf_size, unsigned int regset) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), + static_cast(®set), buf, buf_size); +} + +Error +NativeRegisterContextLinux::WriteRegisterSet(void *buf, size_t buf_size, unsigned int regset) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + static_cast(®set), buf, buf_size); +} + +Error +NativeRegisterContextLinux::DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS)); + + long data; + Error error = NativeProcessLinux::PtraceWrapper( + PTRACE_PEEKUSER, m_thread.GetID(), reinterpret_cast(offset), nullptr, 0, &data); + + if (error.Success()) + // First cast to an unsigned of the same size to avoid sign extension. + value.SetUInt64(static_cast(data)); + + if (log) + log->Printf ("NativeRegisterContextLinux::%s() reg %s: 0x%lx", __FUNCTION__, reg_name, data); + + return error; +} + +Error +NativeRegisterContextLinux::DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS)); + + void* buf = reinterpret_cast(value.GetAsUInt64()); + + if (log) + log->Printf ("NativeRegisterContextLinux::%s() reg %s: %p", __FUNCTION__, reg_name, buf); + + return NativeProcessLinux::PtraceWrapper( + PTRACE_POKEUSER, m_thread.GetID(), reinterpret_cast(offset), buf); +} + +Error +NativeRegisterContextLinux::DoReadGPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), nullptr, buf, buf_size); +} + +Error +NativeRegisterContextLinux::DoWriteGPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), nullptr, buf, buf_size); +} + +Error +NativeRegisterContextLinux::DoReadFPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_GETFPREGS, m_thread.GetID(), nullptr, buf, buf_size); +} + +Error +NativeRegisterContextLinux::DoWriteFPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SETFPREGS, m_thread.GetID(), nullptr, buf, buf_size); +} diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux.h new file mode 100644 index 000000000000..0b0b747984b4 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux.h @@ -0,0 +1,107 @@ +//===-- NativeRegisterContextLinux.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_NativeRegisterContextLinux_h +#define lldb_NativeRegisterContextLinux_h + +#include "lldb/Host/common/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include "Plugins/Process/Linux/NativeProcessLinux.h" + +namespace lldb_private { +namespace process_linux { + +class NativeRegisterContextLinux : public NativeRegisterContextRegisterInfo +{ +public: + NativeRegisterContextLinux(NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx, + RegisterInfoInterface *reg_info_interface_p); + + // This function is implemented in the NativeRegisterContextLinux_* subclasses to create a new + // instance of the host specific NativeRegisterContextLinux. The implementations can't collide + // as only one NativeRegisterContextLinux_* variant should be compiled into the final + // executable. + static NativeRegisterContextLinux* + CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + +protected: + lldb::ByteOrder + GetByteOrder() const; + + virtual Error + ReadRegisterRaw(uint32_t reg_index, RegisterValue& reg_value); + + virtual Error + WriteRegisterRaw(uint32_t reg_index, const RegisterValue& reg_value); + + virtual Error + ReadRegisterSet(void *buf, size_t buf_size, unsigned int regset); + + virtual Error + WriteRegisterSet(void *buf, size_t buf_size, unsigned int regset); + + virtual Error + ReadGPR(); + + virtual Error + WriteGPR(); + + virtual Error + ReadFPR(); + + virtual Error + WriteFPR(); + + virtual void* + GetGPRBuffer() { return nullptr; } + + virtual size_t + GetGPRSize() { return GetRegisterInfoInterface().GetGPRSize(); } + + virtual void* + GetFPRBuffer() { return nullptr; } + + virtual size_t + GetFPRSize() { return 0; } + + + // The Do*** functions are executed on the privileged thread and can perform ptrace + // operations directly. + virtual Error + DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value); + + virtual Error + DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value); + + virtual Error + DoReadGPR(void *buf, size_t buf_size); + + virtual Error + DoWriteGPR(void *buf, size_t buf_size); + + virtual Error + DoReadFPR(void *buf, size_t buf_size); + + virtual Error + DoWriteFPR(void *buf, size_t buf_size); +}; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_h diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp new file mode 100644 index 000000000000..31752fed5b42 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -0,0 +1,1017 @@ +//===-- NativeRegisterContextLinux_arm.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(__arm__) + +#include "NativeRegisterContextLinux_arm.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" + +#include "Plugins/Process/Utility/RegisterContextLinux_arm.h" + +#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof (m_fpr)) + +#ifndef PTRACE_GETVFPREGS + #define PTRACE_GETVFPREGS 27 + #define PTRACE_SETVFPREGS 28 +#endif +#ifndef PTRACE_GETHBPREGS + #define PTRACE_GETHBPREGS 29 + #define PTRACE_SETHBPREGS 30 +#endif +#if !defined(PTRACE_TYPE_ARG3) + #define PTRACE_TYPE_ARG3 void * +#endif +#if !defined(PTRACE_TYPE_ARG4) + #define PTRACE_TYPE_ARG4 void * +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// arm general purpose registers. +static const uint32_t g_gpr_regnums_arm[] = +{ + gpr_r0_arm, + gpr_r1_arm, + gpr_r2_arm, + gpr_r3_arm, + gpr_r4_arm, + gpr_r5_arm, + gpr_r6_arm, + gpr_r7_arm, + gpr_r8_arm, + gpr_r9_arm, + gpr_r10_arm, + gpr_r11_arm, + gpr_r12_arm, + gpr_sp_arm, + gpr_lr_arm, + gpr_pc_arm, + gpr_cpsr_arm, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) == k_num_gpr_registers_arm, \ + "g_gpr_regnums_arm has wrong number of register infos"); + +// arm floating point registers. +static const uint32_t g_fpu_regnums_arm[] = +{ + fpu_s0_arm, + fpu_s1_arm, + fpu_s2_arm, + fpu_s3_arm, + fpu_s4_arm, + fpu_s5_arm, + fpu_s6_arm, + fpu_s7_arm, + fpu_s8_arm, + fpu_s9_arm, + fpu_s10_arm, + fpu_s11_arm, + fpu_s12_arm, + fpu_s13_arm, + fpu_s14_arm, + fpu_s15_arm, + fpu_s16_arm, + fpu_s17_arm, + fpu_s18_arm, + fpu_s19_arm, + fpu_s20_arm, + fpu_s21_arm, + fpu_s22_arm, + fpu_s23_arm, + fpu_s24_arm, + fpu_s25_arm, + fpu_s26_arm, + fpu_s27_arm, + fpu_s28_arm, + fpu_s29_arm, + fpu_s30_arm, + fpu_s31_arm, + fpu_fpscr_arm, + fpu_d0_arm, + fpu_d1_arm, + fpu_d2_arm, + fpu_d3_arm, + fpu_d4_arm, + fpu_d5_arm, + fpu_d6_arm, + fpu_d7_arm, + fpu_d8_arm, + fpu_d9_arm, + fpu_d10_arm, + fpu_d11_arm, + fpu_d12_arm, + fpu_d13_arm, + fpu_d14_arm, + fpu_d15_arm, + fpu_d16_arm, + fpu_d17_arm, + fpu_d18_arm, + fpu_d19_arm, + fpu_d20_arm, + fpu_d21_arm, + fpu_d22_arm, + fpu_d23_arm, + fpu_d24_arm, + fpu_d25_arm, + fpu_d26_arm, + fpu_d27_arm, + fpu_d28_arm, + fpu_d29_arm, + fpu_d30_arm, + fpu_d31_arm, + fpu_q0_arm, + fpu_q1_arm, + fpu_q2_arm, + fpu_q3_arm, + fpu_q4_arm, + fpu_q5_arm, + fpu_q6_arm, + fpu_q7_arm, + fpu_q8_arm, + fpu_q9_arm, + fpu_q10_arm, + fpu_q11_arm, + fpu_q12_arm, + fpu_q13_arm, + fpu_q14_arm, + fpu_q15_arm, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_fpu_regnums_arm / sizeof g_fpu_regnums_arm[0]) - 1) == k_num_fpr_registers_arm, \ + "g_fpu_regnums_arm has wrong number of register infos"); + +namespace { + // Number of register sets provided by this context. + enum + { + k_num_register_sets = 2 + }; +} + +// Register sets for arm. +static const RegisterSet +g_reg_sets_arm[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers_arm, g_gpr_regnums_arm }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_arm, g_fpu_regnums_arm } +}; + +NativeRegisterContextLinux* +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_arm(target_arch, native_thread, concrete_frame_idx); +} + +NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) : + NativeRegisterContextLinux (native_thread, concrete_frame_idx, new RegisterContextLinux_arm(target_arch)) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::arm: + m_reg_info.num_registers = k_num_registers_arm; + m_reg_info.num_gpr_registers = k_num_gpr_registers_arm; + m_reg_info.num_fpr_registers = k_num_fpr_registers_arm; + m_reg_info.last_gpr = k_last_gpr_arm; + m_reg_info.first_fpr = k_first_fpr_arm; + m_reg_info.last_fpr = k_last_fpr_arm; + m_reg_info.first_fpr_v = fpu_s0_arm; + m_reg_info.last_fpr_v = fpu_s31_arm; + m_reg_info.gpr_flags = gpr_cpsr_arm; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + ::memset(&m_fpr, 0, sizeof (m_fpr)); + ::memset(&m_gpr_arm, 0, sizeof (m_gpr_arm)); + ::memset(&m_hwp_regs, 0, sizeof (m_hwp_regs)); + + // 16 is just a maximum value, query hardware for actual watchpoint count + m_max_hwp_supported = 16; + m_max_hbp_supported = 16; + m_refresh_hwdebug_info = true; +} + +uint32_t +NativeRegisterContextLinux_arm::GetRegisterSetCount () const +{ + return k_num_register_sets; +} + +uint32_t +NativeRegisterContextLinux_arm::GetUserRegisterCount() const +{ + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + count += g_reg_sets_arm[set_index].num_registers; + return count; +} + +const RegisterSet * +NativeRegisterContextLinux_arm::GetRegisterSet (uint32_t set_index) const +{ + if (set_index < k_num_register_sets) + return &g_reg_sets_arm[set_index]; + + return nullptr; +} + +Error +NativeRegisterContextLinux_arm::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + Error error; + + if (!reg_info) + { + error.SetErrorString ("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (IsFPR(reg)) + { + error = ReadFPR(); + if (error.Fail()) + return error; + } + else + { + uint32_t full_reg = reg; + bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM); + + if (is_subreg) + { + // Read the full aligned 64-bit register. + full_reg = reg_info->invalidate_regs[0]; + } + + error = ReadRegisterRaw(full_reg, reg_value); + + if (error.Success ()) + { + // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right. + if (is_subreg && (reg_info->byte_offset & 0x1)) + reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8); + + // If our return byte size was greater than the return value reg size, then + // use the type specified by reg_info rather than the uint64_t default + if (reg_value.GetByteSize() > reg_info->byte_size) + reg_value.SetType(reg_info); + } + return error; + } + + // Get pointer to m_fpr variable and set the data from it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert (fpr_offset < sizeof m_fpr); + uint8_t *src = (uint8_t *)&m_fpr + fpr_offset; + switch (reg_info->byte_size) + { + case 2: + reg_value.SetUInt16(*(uint16_t *)src); + break; + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + case 16: + reg_value.SetBytes(src, 16, GetByteOrder()); + break; + default: + assert(false && "Unhandled data size."); + error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); + break; + } + + return error; +} + +Error +NativeRegisterContextLinux_arm::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (!reg_info) + return Error ("reg_info NULL"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : ""); + + if (IsGPR(reg_index)) + return WriteRegisterRaw(reg_index, reg_value); + + if (IsFPR(reg_index)) + { + // Get pointer to m_fpr variable and set the data to it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert (fpr_offset < sizeof m_fpr); + uint8_t *dst = (uint8_t *)&m_fpr + fpr_offset; + switch (reg_info->byte_size) + { + case 2: + *(uint16_t *)dst = reg_value.GetAsUInt16(); + break; + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error ("unhandled register data size %" PRIu32, reg_info->byte_size); + } + + Error error = WriteFPR(); + if (error.Fail()) + return error; + + return Error (); + } + + return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown"); +} + +Error +NativeRegisterContextLinux_arm::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (!data_sp) + return Error ("failed to allocate DataBufferHeap instance of size %" PRIu64, (uint64_t)REG_CONTEXT_SIZE); + + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes (); + if (dst == nullptr) + { + error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", (uint64_t)REG_CONTEXT_SIZE); + return error; + } + + ::memcpy (dst, &m_gpr_arm, GetGPRSize()); + dst += GetGPRSize(); + ::memcpy (dst, &m_fpr, sizeof(m_fpr)); + + return error; +} + +Error +NativeRegisterContextLinux_arm::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, (uint64_t)REG_CONTEXT_SIZE, data_sp->GetByteSize ()); + return error; + } + + + uint8_t *src = data_sp->GetBytes (); + if (src == nullptr) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + ::memcpy (&m_gpr_arm, src, GetRegisterInfoInterface ().GetGPRSize ()); + + error = WriteGPR(); + if (error.Fail()) + return error; + + src += GetRegisterInfoInterface ().GetGPRSize (); + ::memcpy (&m_fpr, src, sizeof(m_fpr)); + + error = WriteFPR(); + if (error.Fail()) + return error; + + return error; +} + +bool +NativeRegisterContextLinux_arm::IsGPR(unsigned reg) const +{ + return reg <= m_reg_info.last_gpr; // GPR's come first. +} + +bool +NativeRegisterContextLinux_arm::IsFPR(unsigned reg) const +{ + return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); +} + +uint32_t +NativeRegisterContextLinux_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, bp_index = 0; + + // Check if size has a valid hardware breakpoint length. + // Thumb instructions are 2-bytes but we have no way here to determine + // if target address is a thumb or arm instruction. + // TODO: Add support for setting thumb mode hardware breakpoints + if (size != 4 && size != 2) + return LLDB_INVALID_INDEX32; + + // Setup control value + // Make the byte_mask into a valid Byte Address Select mask + control_value = 0xfu << 5; + + // Enable this breakpoint and make it stop in privileged or user mode; + control_value |= 7; + + // Make sure bits 1:0 are clear in our address + // This should be different once we support thumb here. + addr &= ~((lldb::addr_t)3); + + // Iterate over stored hardware breakpoints + // Find a free bp_index or update reference count if duplicate. + bp_index = LLDB_INVALID_INDEX32; + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) + { + if ((m_hbr_regs[i].control & 1) == 0) + { + bp_index = i; // Mark last free slot + } + else if (m_hbr_regs[i].address == addr && m_hbr_regs[i].control == control_value) + { + bp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (bp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing breakpoint + if ((m_hbr_regs[bp_index].control & 1) == 0) + { + m_hbr_regs[bp_index].address = addr; + m_hbr_regs[bp_index].control = control_value; + m_hbr_regs[bp_index].refcount = 1; + + // PTRACE call to set corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK, bp_index); + + if (error.Fail()) + { + m_hbr_regs[bp_index].address = 0; + m_hbr_regs[bp_index].control &= ~1; + m_hbr_regs[bp_index].refcount = 0; + + return LLDB_INVALID_INDEX32; + } + } + else + m_hbr_regs[bp_index].refcount++; + + return bp_index; +} + +bool +NativeRegisterContextLinux_arm::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return false; + + if (hw_idx >= m_max_hbp_supported) + return false; + + // Update reference count if multiple references. + if (m_hbr_regs[hw_idx].refcount > 1) + { + m_hbr_regs[hw_idx].refcount--; + return true; + } + else if (m_hbr_regs[hw_idx].refcount == 1) + { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; + uint32_t tempControl = m_hbr_regs[hw_idx].control; + uint32_t tempRefCount = m_hbr_regs[hw_idx].refcount; + + m_hbr_regs[hw_idx].control &= ~1; + m_hbr_regs[hw_idx].address = 0; + m_hbr_regs[hw_idx].refcount = 0; + + // PTRACE call to clear corresponding hardware breakpoint register. + WriteHardwareDebugRegs(eDREGTypeBREAK, hw_idx); + + if (error.Fail()) + { + m_hbr_regs[hw_idx].control = tempControl; + m_hbr_regs[hw_idx].address = tempAddr; + m_hbr_regs[hw_idx].refcount = tempRefCount; + + return false; + } + + return true; + } + + return false; +} + +uint32_t +NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + return m_max_hwp_supported; +} + +uint32_t +NativeRegisterContextLinux_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0; + + // Check if we are setting watchpoint other than read/write/access + // Also update watchpoint flag to match Arm write-read bit configuration. + switch (watch_flags) + { + case 1: + watch_flags = 2; + break; + case 2: + watch_flags = 1; + break; + case 3: + break; + default: + return LLDB_INVALID_INDEX32; + } + + // Can't watch zero bytes + // Can't watch more than 4 bytes per WVR/WCR pair + + if (size == 0 || size > 4) + return LLDB_INVALID_INDEX32; + + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair, so make sure we can properly encode this. + addr_word_offset = addr % 4; + byte_mask = ((1u << size) - 1u) << addr_word_offset; + + // Check if we need multiple watchpoint register + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Setup control value + // Make the byte_mask into a valid Byte Address Select mask + control_value = byte_mask << 5; + + //Turn on appropriate watchpoint flags read or write + control_value |= (watch_flags << 3); + + // Enable this watchpoint and make it stop in privileged or user mode; + control_value |= 7; + + // Make sure bits 1:0 are clear in our address + addr &= ~((lldb::addr_t)3); + + // Iterate over stored watchpoints + // Find a free wp_index or update reference count if duplicate. + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if ((m_hwp_regs[i].control & 1) == 0) + { + wp_index = i; // Mark last free slot + } + else if (m_hwp_regs[i].address == addr && m_hwp_regs[i].control == control_value) + { + wp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing watchpoint + if ((m_hwp_regs[wp_index].control & 1) == 0) + { + // Update watchpoint in local cache + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + m_hwp_regs[wp_index].refcount = 1; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index); + + if (error.Fail()) + { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].refcount = 0; + + return LLDB_INVALID_INDEX32; + } + } + else + m_hwp_regs[wp_index].refcount++; + + return wp_index; +} + +bool +NativeRegisterContextLinux_arm::ClearHardwareWatchpoint (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return false; + + if (wp_index >= m_max_hwp_supported) + return false; + + // Update reference count if multiple references. + if (m_hwp_regs[wp_index].refcount > 1) + { + m_hwp_regs[wp_index].refcount--; + return true; + } + else if (m_hwp_regs[wp_index].refcount == 1) + { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + uint32_t tempRefCount = m_hwp_regs[wp_index].refcount; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].refcount = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index); + + if (error.Fail()) + { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + m_hwp_regs[wp_index].refcount = tempRefCount; + + return false; + } + + return true; + } + + return false; +} + +Error +NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return error; + + lldb::addr_t tempAddr = 0; + uint32_t tempControl = 0, tempRefCount = 0; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if (m_hwp_regs[i].control & 0x01) + { + // Create a backup we can revert to in case of failure. + tempAddr = m_hwp_regs[i].address; + tempControl = m_hwp_regs[i].control; + tempRefCount = m_hwp_regs[i].refcount; + + // Clear watchpoints in local cache + m_hwp_regs[i].control &= ~1; + m_hwp_regs[i].address = 0; + m_hwp_regs[i].refcount = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH, i); + + if (error.Fail()) + { + m_hwp_regs[i].control = tempControl; + m_hwp_regs[i].address = tempAddr; + m_hwp_regs[i].refcount = tempRefCount; + + return error; + } + } + } + + return Error(); +} + +uint32_t +NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f) + { + case 0x01: + return 1; + case 0x03: + return 2; + case 0x07: + return 3; + case 0x0f: + return 4; + default: + return 0; + } +} +bool +NativeRegisterContextLinux_arm::WatchpointIsEnabled(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) + return true; + else + return false; +} + +Error +NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) + { + watch_size = GetWatchpointSize (wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index) + && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) + { + return Error(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +lldb::addr_t +NativeRegisterContextLinux_arm::GetWatchpointAddress (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].address; + else + return LLDB_INVALID_ADDRESS; +} + +Error +NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() +{ + Error error; + + if (!m_refresh_hwdebug_info) + { + return Error(); + } + + unsigned int cap_val; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(), nullptr, &cap_val, sizeof(unsigned int)); + + if (error.Fail()) + return error; + + m_max_hwp_supported = (cap_val >> 8) & 0xff; + m_max_hbp_supported = cap_val & 0xff; + m_refresh_hwdebug_info = false; + + return error; +} + +Error +NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType, int hwb_index) +{ + Error error; + + lldb::addr_t *addr_buf; + uint32_t *ctrl_buf; + + if (hwbType == eDREGTypeWATCH) + { + addr_buf = &m_hwp_regs[hwb_index].address; + ctrl_buf = &m_hwp_regs[hwb_index].control; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS, + m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 1), + addr_buf, sizeof(unsigned int)); + + if (error.Fail()) + return error; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS, + m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 2), + ctrl_buf, sizeof(unsigned int)); + } + else + { + addr_buf = &m_hwp_regs[hwb_index].address; + ctrl_buf = &m_hwp_regs[hwb_index].control; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS, + m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 1), + addr_buf, sizeof(unsigned int)); + + if (error.Fail()) + return error; + + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS, + m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 2), + ctrl_buf, sizeof(unsigned int)); + + } + + return error; +} + +uint32_t +NativeRegisterContextLinux_arm::CalculateFprOffset(const RegisterInfo* reg_info) const +{ + return reg_info->byte_offset - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset; +} + +Error +NativeRegisterContextLinux_arm::DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) +{ + // PTRACE_POKEUSER don't work in the aarch64 liux kernel used on android devices (always return + // "Bad address"). To avoid using PTRACE_POKEUSER we read out the full GPR register set, modify + // the requested register and write it back. This approach is about 4 times slower but the + // performance overhead is negligible in comparision to processing time in lldb-server. + assert(offset % 4 == 0 && "Try to write a register with unaligned offset"); + if (offset + sizeof(uint32_t) > sizeof(m_gpr_arm)) + return Error("Register isn't fit into the size of the GPR area"); + + Error error = DoReadGPR(m_gpr_arm, sizeof(m_gpr_arm)); + if (error.Fail()) + return error; + + uint32_t reg_value = value.GetAsUInt32(); + // As precaution for an undefined behavior encountered while setting PC we + // will clear thumb bit of new PC if we are already in thumb mode; that is + // CPSR thumb mode bit is set. + if (offset / sizeof(uint32_t) == gpr_pc_arm) + { + // Check if we are already in thumb mode and + // thumb bit of current PC is read out to be zero and + // thumb bit of next PC is read out to be one. + if ((m_gpr_arm[gpr_cpsr_arm] & 0x20) && + !(m_gpr_arm[gpr_pc_arm] & 0x01) && + (value.GetAsUInt32() & 0x01)) + { + reg_value &= (~1ull); + } + } + + m_gpr_arm[offset / sizeof(uint32_t)] = reg_value; + return DoWriteGPR(m_gpr_arm, sizeof(m_gpr_arm)); +} + +Error +NativeRegisterContextLinux_arm::DoReadFPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_GETVFPREGS, + m_thread.GetID(), + nullptr, + buf, + buf_size); +} + +Error +NativeRegisterContextLinux_arm::DoWriteFPR(void *buf, size_t buf_size) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SETVFPREGS, + m_thread.GetID(), + nullptr, + buf, + buf_size); +} + +#endif // defined(__arm__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h new file mode 100644 index 000000000000..611b36ad4db1 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -0,0 +1,185 @@ +//===-- NativeRegisterContextLinux_arm.h ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__arm__) // arm register context only needed on arm devices + +#ifndef lldb_NativeRegisterContextLinux_arm_h +#define lldb_NativeRegisterContextLinux_arm_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/lldb-arm-register-enums.h" + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux + { + public: + NativeRegisterContextLinux_arm (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount () const override; + + const RegisterSet * + GetRegisterSet (uint32_t set_index) const override; + + uint32_t + GetUserRegisterCount() const override; + + Error + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + + //------------------------------------------------------------------ + // Hardware breakpoints/watchpoint mangement functions + //------------------------------------------------------------------ + + uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size) override; + + bool + ClearHardwareBreakpoint (uint32_t hw_idx) override; + + uint32_t + NumSupportedHardwareWatchpoints () override; + + uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) override; + + bool + ClearHardwareWatchpoint (uint32_t hw_index) override; + + Error + ClearAllHardwareWatchpoints () override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + lldb::addr_t + GetWatchpointAddress (uint32_t wp_index) override; + + uint32_t + GetWatchpointSize(uint32_t wp_index); + + bool + WatchpointIsEnabled(uint32_t wp_index); + + // Debug register type select + enum DREGType + { + eDREGTypeWATCH = 0, + eDREGTypeBREAK + }; + + protected: + Error + DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) override; + + Error + DoReadFPR(void *buf, size_t buf_size) override; + + Error + DoWriteFPR(void *buf, size_t buf_size) override; + + void* + GetGPRBuffer() override { return &m_gpr_arm; } + + void* + GetFPRBuffer() override { return &m_fpr; } + + size_t + GetFPRSize() override { return sizeof(m_fpr); } + + private: + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + + uint32_t first_fpr_v; + uint32_t last_fpr_v; + + uint32_t gpr_flags; + }; + + struct QReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + union { + uint32_t s[32]; + uint64_t d[32]; + QReg q[16]; // the 128-bit NEON registers + } floats; + uint32_t fpscr; + }; + + uint32_t m_gpr_arm[k_num_gpr_registers_arm]; + RegInfo m_reg_info; + FPU m_fpr; + + // Debug register info for hardware breakpoints and watchpoints management. + struct DREG + { + lldb::addr_t address; // Breakpoint/watchpoint address value. + uint32_t control; // Breakpoint/watchpoint control value. + uint32_t refcount; // Serves as enable/disable and refernce counter. + }; + + struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints + struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints + + uint32_t m_max_hwp_supported; + uint32_t m_max_hbp_supported; + bool m_refresh_hwdebug_info; + + bool + IsGPR(unsigned reg) const; + + bool + IsFPR(unsigned reg) const; + + Error + ReadHardwareDebugInfo(); + + Error + WriteHardwareDebugRegs(int hwbType, int hwb_index); + + uint32_t + CalculateFprOffset(const RegisterInfo* reg_info) const; + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_arm_h + +#endif // defined(__arm__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp new file mode 100644 index 000000000000..e4d26fc640f3 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -0,0 +1,1042 @@ +//===-- NativeRegisterContextLinux_arm64.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 (__arm64__) || defined (__aarch64__) + +#include "NativeRegisterContextLinux_arm64.h" + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/common/NativeProcessProtocol.h" + +#include "Plugins/Process/Linux/NativeProcessLinux.h" +#include "Plugins/Process/Linux/Procfs.h" +#include "Plugins/Process/Utility/RegisterContextLinux_arm64.h" + +// System includes - They have to be included after framework includes because they define some +// macros which collide with variable names in other modules +#include +// NT_PRSTATUS and NT_FPREGSET definition +#include + +#define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize()) + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// ARM64 general purpose registers. +static const uint32_t g_gpr_regnums_arm64[] = +{ + gpr_x0_arm64, + gpr_x1_arm64, + gpr_x2_arm64, + gpr_x3_arm64, + gpr_x4_arm64, + gpr_x5_arm64, + gpr_x6_arm64, + gpr_x7_arm64, + gpr_x8_arm64, + gpr_x9_arm64, + gpr_x10_arm64, + gpr_x11_arm64, + gpr_x12_arm64, + gpr_x13_arm64, + gpr_x14_arm64, + gpr_x15_arm64, + gpr_x16_arm64, + gpr_x17_arm64, + gpr_x18_arm64, + gpr_x19_arm64, + gpr_x20_arm64, + gpr_x21_arm64, + gpr_x22_arm64, + gpr_x23_arm64, + gpr_x24_arm64, + gpr_x25_arm64, + gpr_x26_arm64, + gpr_x27_arm64, + gpr_x28_arm64, + gpr_fp_arm64, + gpr_lr_arm64, + gpr_sp_arm64, + gpr_pc_arm64, + gpr_cpsr_arm64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) - 1) == k_num_gpr_registers_arm64, \ + "g_gpr_regnums_arm64 has wrong number of register infos"); + +// ARM64 floating point registers. +static const uint32_t g_fpu_regnums_arm64[] = +{ + fpu_v0_arm64, + fpu_v1_arm64, + fpu_v2_arm64, + fpu_v3_arm64, + fpu_v4_arm64, + fpu_v5_arm64, + fpu_v6_arm64, + fpu_v7_arm64, + fpu_v8_arm64, + fpu_v9_arm64, + fpu_v10_arm64, + fpu_v11_arm64, + fpu_v12_arm64, + fpu_v13_arm64, + fpu_v14_arm64, + fpu_v15_arm64, + fpu_v16_arm64, + fpu_v17_arm64, + fpu_v18_arm64, + fpu_v19_arm64, + fpu_v20_arm64, + fpu_v21_arm64, + fpu_v22_arm64, + fpu_v23_arm64, + fpu_v24_arm64, + fpu_v25_arm64, + fpu_v26_arm64, + fpu_v27_arm64, + fpu_v28_arm64, + fpu_v29_arm64, + fpu_v30_arm64, + fpu_v31_arm64, + fpu_fpsr_arm64, + fpu_fpcr_arm64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_fpu_regnums_arm64 / sizeof g_fpu_regnums_arm64[0]) - 1) == k_num_fpr_registers_arm64, \ + "g_fpu_regnums_arm64 has wrong number of register infos"); + +namespace { + // Number of register sets provided by this context. + enum + { + k_num_register_sets = 2 + }; +} + +// Register sets for ARM64. +static const RegisterSet +g_reg_sets_arm64[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers_arm64, g_gpr_regnums_arm64 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_arm64, g_fpu_regnums_arm64 } +}; + +NativeRegisterContextLinux* +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_arm64(target_arch, native_thread, concrete_frame_idx); +} + +NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) : + NativeRegisterContextLinux (native_thread, concrete_frame_idx, new RegisterContextLinux_arm64(target_arch)) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::aarch64: + m_reg_info.num_registers = k_num_registers_arm64; + m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64; + m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64; + m_reg_info.last_gpr = k_last_gpr_arm64; + m_reg_info.first_fpr = k_first_fpr_arm64; + m_reg_info.last_fpr = k_last_fpr_arm64; + m_reg_info.first_fpr_v = fpu_v0_arm64; + m_reg_info.last_fpr_v = fpu_v31_arm64; + m_reg_info.gpr_flags = gpr_cpsr_arm64; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + ::memset(&m_fpr, 0, sizeof (m_fpr)); + ::memset(&m_gpr_arm64, 0, sizeof (m_gpr_arm64)); + ::memset(&m_hwp_regs, 0, sizeof (m_hwp_regs)); + + // 16 is just a maximum value, query hardware for actual watchpoint count + m_max_hwp_supported = 16; + m_max_hbp_supported = 16; + m_refresh_hwdebug_info = true; +} + +uint32_t +NativeRegisterContextLinux_arm64::GetRegisterSetCount () const +{ + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextLinux_arm64::GetRegisterSet (uint32_t set_index) const +{ + if (set_index < k_num_register_sets) + return &g_reg_sets_arm64[set_index]; + + return nullptr; +} + +uint32_t +NativeRegisterContextLinux_arm64::GetUserRegisterCount() const +{ + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + count += g_reg_sets_arm64[set_index].num_registers; + return count; +} + +Error +NativeRegisterContextLinux_arm64::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + Error error; + + if (!reg_info) + { + error.SetErrorString ("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (IsFPR(reg)) + { + error = ReadFPR(); + if (error.Fail()) + return error; + } + else + { + uint32_t full_reg = reg; + bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM); + + if (is_subreg) + { + // Read the full aligned 64-bit register. + full_reg = reg_info->invalidate_regs[0]; + } + + error = ReadRegisterRaw(full_reg, reg_value); + + if (error.Success ()) + { + // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right. + if (is_subreg && (reg_info->byte_offset & 0x1)) + reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8); + + // If our return byte size was greater than the return value reg size, then + // use the type specified by reg_info rather than the uint64_t default + if (reg_value.GetByteSize() > reg_info->byte_size) + reg_value.SetType(reg_info); + } + return error; + } + + // Get pointer to m_fpr variable and set the data from it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert (fpr_offset < sizeof m_fpr); + uint8_t *src = (uint8_t *)&m_fpr + fpr_offset; + reg_value.SetFromMemoryData(reg_info, src, reg_info->byte_size, eByteOrderLittle, error); + + return error; +} + +Error +NativeRegisterContextLinux_arm64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (!reg_info) + return Error ("reg_info NULL"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : ""); + + if (IsGPR(reg_index)) + return WriteRegisterRaw(reg_index, reg_value); + + if (IsFPR(reg_index)) + { + // Get pointer to m_fpr variable and set the data to it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert (fpr_offset < sizeof m_fpr); + uint8_t *dst = (uint8_t *)&m_fpr + fpr_offset; + switch (reg_info->byte_size) + { + case 2: + *(uint16_t *)dst = reg_value.GetAsUInt16(); + break; + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error ("unhandled register data size %" PRIu32, reg_info->byte_size); + } + + Error error = WriteFPR(); + if (error.Fail()) + return error; + + return Error (); + } + + return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown"); +} + +Error +NativeRegisterContextLinux_arm64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (!data_sp) + return Error ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); + + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes (); + if (dst == nullptr) + { + error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); + return error; + } + + ::memcpy (dst, &m_gpr_arm64, GetGPRSize()); + dst += GetGPRSize(); + ::memcpy (dst, &m_fpr, sizeof(m_fpr)); + + return error; +} + +Error +NativeRegisterContextLinux_arm64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ()); + return error; + } + + + uint8_t *src = data_sp->GetBytes (); + if (src == nullptr) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + ::memcpy (&m_gpr_arm64, src, GetRegisterInfoInterface ().GetGPRSize ()); + + error = WriteGPR(); + if (error.Fail()) + return error; + + src += GetRegisterInfoInterface ().GetGPRSize (); + ::memcpy (&m_fpr, src, sizeof(m_fpr)); + + error = WriteFPR(); + if (error.Fail()) + return error; + + return error; +} + +bool +NativeRegisterContextLinux_arm64::IsGPR(unsigned reg) const +{ + return reg <= m_reg_info.last_gpr; // GPR's come first. +} + +bool +NativeRegisterContextLinux_arm64::IsFPR(unsigned reg) const +{ + return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); +} + +uint32_t +NativeRegisterContextLinux_arm64::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, bp_index = 0; + + // Check if size has a valid hardware breakpoint length. + if (size != 4) + return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware breakpoint + + // Check 4-byte alignment for hardware breakpoint target address. + if (addr & 0x03) + return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned. + + // Setup control value + control_value = 0; + control_value |= ((1 << size) - 1) << 5; + control_value |= (2 << 1) | 1; + + // Iterate over stored hardware breakpoints + // Find a free bp_index or update reference count if duplicate. + bp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hbp_supported; i++) + { + if ((m_hbr_regs[i].control & 1) == 0) + { + bp_index = i; // Mark last free slot + } + else if (m_hbr_regs[i].address == addr && m_hbr_regs[i].control == control_value) + { + bp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (bp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing breakpoint + if ((m_hbr_regs[bp_index].control & 1) == 0) + { + m_hbr_regs[bp_index].address = addr; + m_hbr_regs[bp_index].control = control_value; + m_hbr_regs[bp_index].refcount = 1; + + // PTRACE call to set corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) + { + m_hbr_regs[bp_index].address = 0; + m_hbr_regs[bp_index].control &= ~1; + m_hbr_regs[bp_index].refcount = 0; + + return LLDB_INVALID_INDEX32; + } + } + else + m_hbr_regs[bp_index].refcount++; + + return bp_index; +} + +bool +NativeRegisterContextLinux_arm64::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return false; + + if (hw_idx >= m_max_hbp_supported) + return false; + + // Update reference count if multiple references. + if (m_hbr_regs[hw_idx].refcount > 1) + { + m_hbr_regs[hw_idx].refcount--; + return true; + } + else if (m_hbr_regs[hw_idx].refcount == 1) + { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; + uint32_t tempControl = m_hbr_regs[hw_idx].control; + uint32_t tempRefCount = m_hbr_regs[hw_idx].refcount; + + m_hbr_regs[hw_idx].control &= ~1; + m_hbr_regs[hw_idx].address = 0; + m_hbr_regs[hw_idx].refcount = 0; + + // PTRACE call to clear corresponding hardware breakpoint register. + WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) + { + m_hbr_regs[hw_idx].control = tempControl; + m_hbr_regs[hw_idx].address = tempAddr; + m_hbr_regs[hw_idx].refcount = tempRefCount; + + return false; + } + + return true; + } + + return false; +} + +uint32_t +NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + return m_max_hwp_supported; +} + +uint32_t +NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, wp_index = 0; + + // Check if we are setting watchpoint other than read/write/access + // Also update watchpoint flag to match AArch64 write-read bit configuration. + switch (watch_flags) + { + case 1: + watch_flags = 2; + break; + case 2: + watch_flags = 1; + break; + case 3: + break; + default: + return LLDB_INVALID_INDEX32; + } + + // Check if size has a valid hardware watchpoint length. + if (size != 1 && size != 2 && size != 4 && size != 8) + return LLDB_INVALID_INDEX32; + + // Check 8-byte alignment for hardware watchpoint target address. + // TODO: Add support for watching un-aligned addresses + if (addr & 0x07) + return LLDB_INVALID_INDEX32; + + // Setup control value + control_value = watch_flags << 3; + control_value |= ((1 << size) - 1) << 5; + control_value |= (2 << 1) | 1; + + // Iterate over stored watchpoints + // Find a free wp_index or update reference count if duplicate. + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if ((m_hwp_regs[i].control & 1) == 0) + { + wp_index = i; // Mark last free slot + } + else if (m_hwp_regs[i].address == addr && m_hwp_regs[i].control == control_value) + { + wp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing watchpoint + if ((m_hwp_regs[wp_index].control & 1) == 0) + { + // Update watchpoint in local cache + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + m_hwp_regs[wp_index].refcount = 1; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) + { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].refcount = 0; + + return LLDB_INVALID_INDEX32; + } + } + else + m_hwp_regs[wp_index].refcount++; + + return wp_index; +} + +bool +NativeRegisterContextLinux_arm64::ClearHardwareWatchpoint (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return false; + + if (wp_index >= m_max_hwp_supported) + return false; + + // Update reference count if multiple references. + if (m_hwp_regs[wp_index].refcount > 1) + { + m_hwp_regs[wp_index].refcount--; + return true; + } + else if (m_hwp_regs[wp_index].refcount == 1) + { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + uint32_t tempRefCount = m_hwp_regs[wp_index].refcount; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].refcount = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) + { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + m_hwp_regs[wp_index].refcount = tempRefCount; + + return false; + } + + return true; + } + + return false; +} + +Error +NativeRegisterContextLinux_arm64::ClearAllHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo (); + + if (error.Fail()) + return error; + + lldb::addr_t tempAddr = 0; + uint32_t tempControl = 0, tempRefCount = 0; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if (m_hwp_regs[i].control & 0x01) + { + // Create a backup we can revert to in case of failure. + tempAddr = m_hwp_regs[i].address; + tempControl = m_hwp_regs[i].control; + tempRefCount = m_hwp_regs[i].refcount; + + // Clear watchpoints in local cache + m_hwp_regs[i].control &= ~1; + m_hwp_regs[i].address = 0; + m_hwp_regs[i].refcount = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) + { + m_hwp_regs[i].control = tempControl; + m_hwp_regs[i].address = tempAddr; + m_hwp_regs[i].refcount = tempRefCount; + + return error; + } + } + } + + return Error(); +} + +uint32_t +NativeRegisterContextLinux_arm64::GetWatchpointSize(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) + { + case 0x01: + return 1; + case 0x03: + return 2; + case 0x0f: + return 4; + case 0xff: + return 8; + default: + return 0; + } +} +bool +NativeRegisterContextLinux_arm64::WatchpointIsEnabled(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) + return true; + else + return false; +} + +Error +NativeRegisterContextLinux_arm64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) + { + watch_size = GetWatchpointSize (wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index) + && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) + { + return Error(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +lldb::addr_t +NativeRegisterContextLinux_arm64::GetWatchpointAddress (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].address; + else + return LLDB_INVALID_ADDRESS; +} + +Error +NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() +{ + if (!m_refresh_hwdebug_info) + { + return Error(); + } + + ::pid_t tid = m_thread.GetID(); + + int regset = NT_ARM_HW_WATCH; + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + Error error; + + ioVec.iov_base = &dreg_state; + ioVec.iov_len = sizeof (dreg_state); + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len); + + if (error.Fail()) + return error; + + m_max_hwp_supported = dreg_state.dbg_info & 0xff; + + regset = NT_ARM_HW_BREAK; + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, ioVec.iov_len); + + if (error.Fail()) + return error; + + m_max_hbp_supported = dreg_state.dbg_info & 0xff; + m_refresh_hwdebug_info = false; + + return error; +} + +Error +NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) +{ + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + Error error; + + memset (&dreg_state, 0, sizeof (dreg_state)); + ioVec.iov_base = &dreg_state; + + if (hwbType == eDREGTypeWATCH) + { + hwbType = NT_ARM_HW_WATCH; + ioVec.iov_len = sizeof (dreg_state.dbg_info) + sizeof (dreg_state.pad) + + (sizeof (dreg_state.dbg_regs [0]) * m_max_hwp_supported); + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control; + } + } + else + { + hwbType = NT_ARM_HW_BREAK; + ioVec.iov_len = sizeof (dreg_state.dbg_info) + sizeof (dreg_state.pad) + + (sizeof (dreg_state.dbg_regs [0]) * m_max_hbp_supported); + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) + { + dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control; + } + } + + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), &hwbType, &ioVec, ioVec.iov_len); +} + +Error +NativeRegisterContextLinux_arm64::DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) +{ + Error error; + if (offset > sizeof(struct user_pt_regs)) + { + uintptr_t offset = offset - sizeof(struct user_pt_regs); + if (offset > sizeof(struct user_fpsimd_state)) + { + error.SetErrorString("invalid offset value"); + return error; + } + elf_fpregset_t regs; + int regset = NT_FPREGSET; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + error = NativeProcessLinux::PtraceWrapper( + PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, sizeof regs); + if (error.Success()) + { + ArchSpec arch; + if (m_thread.GetProcess()->GetArchitecture(arch)) + value.SetBytes((void *)(((unsigned char *)(®s)) + offset), 16, arch.GetByteOrder()); + else + error.SetErrorString("failed to get architecture"); + } + } + else + { + elf_gregset_t regs; + int regset = NT_PRSTATUS; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + error = NativeProcessLinux::PtraceWrapper( + PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, sizeof regs); + if (error.Success()) + { + ArchSpec arch; + if (m_thread.GetProcess()->GetArchitecture(arch)) + value.SetBytes((void *)(((unsigned char *)(regs)) + offset), 8, arch.GetByteOrder()); + else + error.SetErrorString("failed to get architecture"); + } + } + return error; +} + +Error +NativeRegisterContextLinux_arm64::DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) +{ + Error error; + ::pid_t tid = m_thread.GetID(); + if (offset > sizeof(struct user_pt_regs)) + { + uintptr_t offset = offset - sizeof(struct user_pt_regs); + if (offset > sizeof(struct user_fpsimd_state)) + { + error.SetErrorString("invalid offset value"); + return error; + } + elf_fpregset_t regs; + int regset = NT_FPREGSET; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + error = NativeProcessLinux::PtraceWrapper( PTRACE_GETREGSET, tid, ®set, &ioVec, sizeof regs); + + if (error.Success()) + { + ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), 16); + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, ®set, &ioVec, sizeof regs); + } + } + else + { + elf_gregset_t regs; + int regset = NT_PRSTATUS; + struct iovec ioVec; + + ioVec.iov_base = ®s; + ioVec.iov_len = sizeof regs; + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, &ioVec, sizeof regs); + if (error.Success()) + { + ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), 8); + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, ®set, &ioVec, sizeof regs); + } + } + return error; +} + +Error +NativeRegisterContextLinux_arm64::DoReadGPR(void *buf, size_t buf_size) +{ + int regset = NT_PRSTATUS; + struct iovec ioVec; + Error error; + + ioVec.iov_base = buf; + ioVec.iov_len = buf_size; + return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); +} + +Error +NativeRegisterContextLinux_arm64::DoWriteGPR(void *buf, size_t buf_size) +{ + int regset = NT_PRSTATUS; + struct iovec ioVec; + Error error; + + ioVec.iov_base = buf; + ioVec.iov_len = buf_size; + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); +} + +Error +NativeRegisterContextLinux_arm64::DoReadFPR(void *buf, size_t buf_size) +{ + int regset = NT_FPREGSET; + struct iovec ioVec; + Error error; + + ioVec.iov_base = buf; + ioVec.iov_len = buf_size; + return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); +} + +Error +NativeRegisterContextLinux_arm64::DoWriteFPR(void *buf, size_t buf_size) +{ + int regset = NT_FPREGSET; + struct iovec ioVec; + Error error; + + ioVec.iov_base = buf; + ioVec.iov_len = buf_size; + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), ®set, &ioVec, buf_size); +} + +uint32_t +NativeRegisterContextLinux_arm64::CalculateFprOffset(const RegisterInfo* reg_info) const +{ + return reg_info->byte_offset - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset; +} + +#endif // defined (__arm64__) || defined (__aarch64__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h new file mode 100644 index 000000000000..c60baa637b8a --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -0,0 +1,196 @@ +//===-- NativeRegisterContextLinux_arm64.h ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__arm64__) || defined (__aarch64__) + +#ifndef lldb_NativeRegisterContextLinux_arm64_h +#define lldb_NativeRegisterContextLinux_arm64_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/lldb-arm64-register-enums.h" + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeRegisterContextLinux_arm64 : public NativeRegisterContextLinux + { + public: + NativeRegisterContextLinux_arm64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount () const override; + + uint32_t + GetUserRegisterCount() const override; + + const RegisterSet * + GetRegisterSet (uint32_t set_index) const override; + + Error + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + + //------------------------------------------------------------------ + // Hardware breakpoints/watchpoint mangement functions + //------------------------------------------------------------------ + + uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size) override; + + bool + ClearHardwareBreakpoint (uint32_t hw_idx) override; + + uint32_t + NumSupportedHardwareWatchpoints () override; + + uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) override; + + bool + ClearHardwareWatchpoint (uint32_t hw_index) override; + + Error + ClearAllHardwareWatchpoints () override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + lldb::addr_t + GetWatchpointAddress (uint32_t wp_index) override; + + uint32_t + GetWatchpointSize(uint32_t wp_index); + + bool + WatchpointIsEnabled(uint32_t wp_index); + + // Debug register type select + enum DREGType + { + eDREGTypeWATCH = 0, + eDREGTypeBREAK + }; + + protected: + Error + DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) override; + + Error + DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) override; + + Error + DoReadGPR(void *buf, size_t buf_size) override; + + Error + DoWriteGPR(void *buf, size_t buf_size) override; + + Error + DoReadFPR(void *buf, size_t buf_size) override; + + Error + DoWriteFPR(void *buf, size_t buf_size) override; + + void* + GetGPRBuffer() override { return &m_gpr_arm64; } + + void* + GetFPRBuffer() override { return &m_fpr; } + + size_t + GetFPRSize() override { return sizeof(m_fpr); } + + private: + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + + uint32_t first_fpr_v; + uint32_t last_fpr_v; + + uint32_t gpr_flags; + }; + + // based on RegisterContextDarwin_arm64.h + struct VReg + { + uint8_t bytes[16]; + }; + + // based on RegisterContextDarwin_arm64.h + struct FPU + { + VReg v[32]; + uint32_t fpsr; + uint32_t fpcr; + }; + + uint64_t m_gpr_arm64[k_num_gpr_registers_arm64]; // 64-bit general purpose registers. + RegInfo m_reg_info; + FPU m_fpr; // floating-point registers including extended register sets. + + // Debug register info for hardware breakpoints and watchpoints management. + struct DREG + { + lldb::addr_t address; // Breakpoint/watchpoint address value. + uint32_t control; // Breakpoint/watchpoint control value. + uint32_t refcount; // Serves as enable/disable and refernce counter. + }; + + struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints + struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints + + uint32_t m_max_hwp_supported; + uint32_t m_max_hbp_supported; + bool m_refresh_hwdebug_info; + + bool + IsGPR(unsigned reg) const; + + bool + IsFPR(unsigned reg) const; + + Error + ReadHardwareDebugInfo(); + + Error + WriteHardwareDebugRegs(int hwbType); + + uint32_t + CalculateFprOffset(const RegisterInfo* reg_info) const; + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_arm64_h + +#endif // defined (__arm64__) || defined (__aarch64__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp new file mode 100644 index 000000000000..3cfeaf5546bc --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp @@ -0,0 +1,1431 @@ +//===-- NativeRegisterContextLinux_mips64.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 (__mips__) + +#include "NativeRegisterContextLinux_mips64.h" + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private-enumerations.h" +#include "Plugins/Process/Linux/NativeProcessLinux.h" +#include "Plugins/Process/Linux/Procfs.h" +#include "Plugins/Process/Utility/RegisterContextLinux_mips64.h" +#include "Plugins/Process/Utility/RegisterContextLinux_mips.h" +#define NT_MIPS_MSA 0x600 +#define CONFIG5_FRE (1 << 8) +#define SR_FR (1 << 26) +#define NUM_REGISTERS 32 + +#include +#include + +#ifndef PTRACE_GET_WATCH_REGS +enum pt_watch_style +{ + pt_watch_style_mips32, + pt_watch_style_mips64 +}; +struct mips32_watch_regs +{ + uint32_t watchlo[8]; + uint16_t watchhi[8]; + uint16_t watch_masks[8]; + uint32_t num_valid; +} __attribute__((aligned(8))); + +struct mips64_watch_regs +{ + uint64_t watchlo[8]; + uint16_t watchhi[8]; + uint16_t watch_masks[8]; + uint32_t num_valid; +} __attribute__((aligned(8))); + +struct pt_watch_regs +{ + enum pt_watch_style style; + union + { + struct mips32_watch_regs mips32; + struct mips64_watch_regs mips64; + }; +}; + +#define PTRACE_GET_WATCH_REGS 0xd0 +#define PTRACE_SET_WATCH_REGS 0xd1 +#endif + +#define W (1 << 0) +#define R (1 << 1) +#define I (1 << 2) + +#define IRW (I | R | W) + +struct pt_watch_regs default_watch_regs; + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// ---------------------------------------------------------------------------- +// Private namespace. +// ---------------------------------------------------------------------------- + +namespace +{ + // mips general purpose registers. + const uint32_t + g_gp_regnums_mips[] = + { + gpr_zero_mips, + gpr_r1_mips, + gpr_r2_mips, + gpr_r3_mips, + gpr_r4_mips, + gpr_r5_mips, + gpr_r6_mips, + gpr_r7_mips, + gpr_r8_mips, + gpr_r9_mips, + gpr_r10_mips, + gpr_r11_mips, + gpr_r12_mips, + gpr_r13_mips, + gpr_r14_mips, + gpr_r15_mips, + gpr_r16_mips, + gpr_r17_mips, + gpr_r18_mips, + gpr_r19_mips, + gpr_r20_mips, + gpr_r21_mips, + gpr_r22_mips, + gpr_r23_mips, + gpr_r24_mips, + gpr_r25_mips, + gpr_r26_mips, + gpr_r27_mips, + gpr_gp_mips, + gpr_sp_mips, + gpr_r30_mips, + gpr_ra_mips, + gpr_sr_mips, + gpr_mullo_mips, + gpr_mulhi_mips, + gpr_badvaddr_mips, + gpr_cause_mips, + gpr_pc_mips, + gpr_config5_mips, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_gp_regnums_mips) / sizeof(g_gp_regnums_mips[0])) - 1 == k_num_gpr_registers_mips, + "g_gp_regnums_mips has wrong number of register infos"); + + // mips floating point registers. + const uint32_t + g_fp_regnums_mips[] = + { + fpr_f0_mips, + fpr_f1_mips, + fpr_f2_mips, + fpr_f3_mips, + fpr_f4_mips, + fpr_f5_mips, + fpr_f6_mips, + fpr_f7_mips, + fpr_f8_mips, + fpr_f9_mips, + fpr_f10_mips, + fpr_f11_mips, + fpr_f12_mips, + fpr_f13_mips, + fpr_f14_mips, + fpr_f15_mips, + fpr_f16_mips, + fpr_f17_mips, + fpr_f18_mips, + fpr_f19_mips, + fpr_f20_mips, + fpr_f21_mips, + fpr_f22_mips, + fpr_f23_mips, + fpr_f24_mips, + fpr_f25_mips, + fpr_f26_mips, + fpr_f27_mips, + fpr_f28_mips, + fpr_f29_mips, + fpr_f30_mips, + fpr_f31_mips, + fpr_fcsr_mips, + fpr_fir_mips, + fpr_config5_mips, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_fp_regnums_mips) / sizeof(g_fp_regnums_mips[0])) - 1 == k_num_fpr_registers_mips, + "g_fp_regnums_mips has wrong number of register infos"); + + // mips MSA registers. + const uint32_t + g_msa_regnums_mips[] = + { + msa_w0_mips, + msa_w1_mips, + msa_w2_mips, + msa_w3_mips, + msa_w4_mips, + msa_w5_mips, + msa_w6_mips, + msa_w7_mips, + msa_w8_mips, + msa_w9_mips, + msa_w10_mips, + msa_w11_mips, + msa_w12_mips, + msa_w13_mips, + msa_w14_mips, + msa_w15_mips, + msa_w16_mips, + msa_w17_mips, + msa_w18_mips, + msa_w19_mips, + msa_w20_mips, + msa_w21_mips, + msa_w22_mips, + msa_w23_mips, + msa_w24_mips, + msa_w25_mips, + msa_w26_mips, + msa_w27_mips, + msa_w28_mips, + msa_w29_mips, + msa_w30_mips, + msa_w31_mips, + msa_fcsr_mips, + msa_fir_mips, + msa_mcsr_mips, + msa_mir_mips, + msa_config5_mips, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_msa_regnums_mips) / sizeof(g_msa_regnums_mips[0])) - 1 == k_num_msa_registers_mips, + "g_msa_regnums_mips has wrong number of register infos"); + + // mips64 general purpose registers. + const uint32_t + g_gp_regnums_mips64[] = + { + gpr_zero_mips64, + gpr_r1_mips64, + gpr_r2_mips64, + gpr_r3_mips64, + gpr_r4_mips64, + gpr_r5_mips64, + gpr_r6_mips64, + gpr_r7_mips64, + gpr_r8_mips64, + gpr_r9_mips64, + gpr_r10_mips64, + gpr_r11_mips64, + gpr_r12_mips64, + gpr_r13_mips64, + gpr_r14_mips64, + gpr_r15_mips64, + gpr_r16_mips64, + gpr_r17_mips64, + gpr_r18_mips64, + gpr_r19_mips64, + gpr_r20_mips64, + gpr_r21_mips64, + gpr_r22_mips64, + gpr_r23_mips64, + gpr_r24_mips64, + gpr_r25_mips64, + gpr_r26_mips64, + gpr_r27_mips64, + gpr_gp_mips64, + gpr_sp_mips64, + gpr_r30_mips64, + gpr_ra_mips64, + gpr_sr_mips64, + gpr_mullo_mips64, + gpr_mulhi_mips64, + gpr_badvaddr_mips64, + gpr_cause_mips64, + gpr_pc_mips64, + gpr_config5_mips64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_gp_regnums_mips64) / sizeof(g_gp_regnums_mips64[0])) - 1 == k_num_gpr_registers_mips64, + "g_gp_regnums_mips64 has wrong number of register infos"); + + // mips64 floating point registers. + const uint32_t + g_fp_regnums_mips64[] = + { + fpr_f0_mips64, + fpr_f1_mips64, + fpr_f2_mips64, + fpr_f3_mips64, + fpr_f4_mips64, + fpr_f5_mips64, + fpr_f6_mips64, + fpr_f7_mips64, + fpr_f8_mips64, + fpr_f9_mips64, + fpr_f10_mips64, + fpr_f11_mips64, + fpr_f12_mips64, + fpr_f13_mips64, + fpr_f14_mips64, + fpr_f15_mips64, + fpr_f16_mips64, + fpr_f17_mips64, + fpr_f18_mips64, + fpr_f19_mips64, + fpr_f20_mips64, + fpr_f21_mips64, + fpr_f22_mips64, + fpr_f23_mips64, + fpr_f24_mips64, + fpr_f25_mips64, + fpr_f26_mips64, + fpr_f27_mips64, + fpr_f28_mips64, + fpr_f29_mips64, + fpr_f30_mips64, + fpr_f31_mips64, + fpr_fcsr_mips64, + fpr_fir_mips64, + fpr_config5_mips64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_fp_regnums_mips64) / sizeof(g_fp_regnums_mips64[0])) - 1 == k_num_fpr_registers_mips64, + "g_fp_regnums_mips64 has wrong number of register infos"); + + // mips64 MSA registers. + const uint32_t + g_msa_regnums_mips64[] = + { + msa_w0_mips64, + msa_w1_mips64, + msa_w2_mips64, + msa_w3_mips64, + msa_w4_mips64, + msa_w5_mips64, + msa_w6_mips64, + msa_w7_mips64, + msa_w8_mips64, + msa_w9_mips64, + msa_w10_mips64, + msa_w11_mips64, + msa_w12_mips64, + msa_w13_mips64, + msa_w14_mips64, + msa_w15_mips64, + msa_w16_mips64, + msa_w17_mips64, + msa_w18_mips64, + msa_w19_mips64, + msa_w20_mips64, + msa_w21_mips64, + msa_w22_mips64, + msa_w23_mips64, + msa_w24_mips64, + msa_w25_mips64, + msa_w26_mips64, + msa_w27_mips64, + msa_w28_mips64, + msa_w29_mips64, + msa_w30_mips64, + msa_w31_mips64, + msa_fcsr_mips64, + msa_fir_mips64, + msa_mcsr_mips64, + msa_mir_mips64, + msa_config5_mips64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + + static_assert((sizeof(g_msa_regnums_mips64) / sizeof(g_msa_regnums_mips64[0])) - 1 == k_num_msa_registers_mips64, + "g_msa_regnums_mips64 has wrong number of register infos"); + + // Number of register sets provided by this context. + enum + { + k_num_register_sets = 3 + }; + + // Register sets for mips. + static const RegisterSet + g_reg_sets_mips[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_mips, g_gp_regnums_mips }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_mips, g_fp_regnums_mips }, + { "MSA Registers", "msa", k_num_msa_registers_mips, g_msa_regnums_mips } + }; + + // Register sets for mips64. + static const RegisterSet + g_reg_sets_mips64[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_mips64, g_gp_regnums_mips64 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_mips64, g_fp_regnums_mips64 }, + { "MSA Registers", "msa", k_num_msa_registers_mips64, g_msa_regnums_mips64 }, + }; + +} // end of anonymous namespace + +NativeRegisterContextLinux* +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_mips64(target_arch, native_thread, concrete_frame_idx); +} + +#define REG_CONTEXT_SIZE (GetRegisterInfoInterface ().GetGPRSize () + sizeof(FPR_linux_mips) + sizeof(MSA_linux_mips)) + +// ---------------------------------------------------------------------------- +// NativeRegisterContextLinux_mips64 members. +// ---------------------------------------------------------------------------- + +static RegisterInfoInterface* +CreateRegisterInfoInterface(const ArchSpec& target_arch) +{ + if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) + { + // 32-bit hosts run with a RegisterContextLinux_mips context. + return new RegisterContextLinux_mips(target_arch, NativeRegisterContextLinux_mips64::IsMSAAvailable()); + } + else + { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + // mips64 hosts know how to work with 64-bit and 32-bit EXEs using the mips64 register context. + return new RegisterContextLinux_mips64 (target_arch, NativeRegisterContextLinux_mips64::IsMSAAvailable()); + } +} + +NativeRegisterContextLinux_mips64::NativeRegisterContextLinux_mips64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) : + NativeRegisterContextLinux (native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)) +{ + switch (target_arch.GetMachine ()) + { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + m_reg_info.num_registers = k_num_registers_mips; + m_reg_info.num_gpr_registers = k_num_gpr_registers_mips; + m_reg_info.num_fpr_registers = k_num_fpr_registers_mips; + m_reg_info.last_gpr = k_last_gpr_mips; + m_reg_info.first_fpr = k_first_fpr_mips; + m_reg_info.last_fpr = k_last_fpr_mips; + m_reg_info.first_msa = k_first_msa_mips; + m_reg_info.last_msa = k_last_msa_mips; + break; + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + m_reg_info.num_registers = k_num_registers_mips64; + m_reg_info.num_gpr_registers = k_num_gpr_registers_mips64; + m_reg_info.num_fpr_registers = k_num_fpr_registers_mips64; + m_reg_info.last_gpr = k_last_gpr_mips64; + m_reg_info.first_fpr = k_first_fpr_mips64; + m_reg_info.last_fpr = k_last_fpr_mips64; + m_reg_info.first_msa = k_first_msa_mips64; + m_reg_info.last_msa = k_last_msa_mips64; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + // Initialize m_iovec to point to the buffer and buffer size + // using the conventions of Berkeley style UIO structures, as required + // by PTRACE extensions. + m_iovec.iov_base = &m_msa; + m_iovec.iov_len = sizeof(MSA_linux_mips); + + // init h/w watchpoint addr map + for (int index = 0;index <= MAX_NUM_WP; index++) + hw_addr_map[index] = LLDB_INVALID_ADDRESS; + + ::memset(&m_gpr, 0, sizeof(GPR_linux_mips)); + ::memset(&m_fpr, 0, sizeof(FPR_linux_mips)); + ::memset(&m_msa, 0, sizeof(MSA_linux_mips)); +} + +uint32_t +NativeRegisterContextLinux_mips64::GetRegisterSetCount () const +{ + return k_num_register_sets; +} + +lldb::addr_t +NativeRegisterContextLinux_mips64::GetPCfromBreakpointLocation (lldb::addr_t fail_value) +{ + Error error; + RegisterValue pc_value; + lldb::addr_t pc = fail_value; + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_mips64::%s Reading PC from breakpoint location", __FUNCTION__); + + // PC register is at index 34 of the register array + const RegisterInfo *const pc_info_p = GetRegisterInfoAtIndex (gpr_pc_mips64); + + error = ReadRegister (pc_info_p, pc_value); + if (error.Success ()) + { + pc = pc_value.GetAsUInt64 (); + + // CAUSE register is at index 37 of the register array + const RegisterInfo *const cause_info_p = GetRegisterInfoAtIndex (gpr_cause_mips64); + RegisterValue cause_value; + + ReadRegister (cause_info_p, cause_value); + + uint64_t cause = cause_value.GetAsUInt64 (); + + if (log) + log->Printf ("NativeRegisterContextLinux_mips64::%s PC 0x%" PRIx64 " Cause 0x%" PRIx64, __FUNCTION__, pc, cause); + + /* + * The breakpoint might be in a delay slot. In this case PC points + * to the delayed branch instruction rather then the instruction + * in the delay slot. If the CAUSE.BD flag is set then adjust the + * PC based on the size of the branch instruction. + */ + if ((cause & (1 << 31)) != 0) + { + lldb::addr_t branch_delay = 0; + branch_delay = 4; // FIXME - Adjust according to size of branch instruction at PC + pc = pc + branch_delay; + pc_value.SetUInt64 (pc); + WriteRegister (pc_info_p, pc_value); + + if (log) + log->Printf ("NativeRegisterContextLinux_mips64::%s New PC 0x%" PRIx64, __FUNCTION__, pc); + } + } + + return pc; +} + +const RegisterSet * +NativeRegisterContextLinux_mips64::GetRegisterSet (uint32_t set_index) const +{ + if (set_index >= k_num_register_sets) + return nullptr; + + switch (GetRegisterInfoInterface ().GetTargetArchitecture ().GetMachine ()) + { + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + return &g_reg_sets_mips64[set_index]; + case llvm::Triple::mips: + case llvm::Triple::mipsel: + return &g_reg_sets_mips[set_index]; + default: + assert (false && "Unhandled target architecture."); + return nullptr; + } + + return nullptr; +} + +lldb_private::Error +NativeRegisterContextLinux_mips64::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + Error error; + + if (!reg_info) + { + error.SetErrorString ("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + { + // This is likely an internal register for lldb use only and should not be directly queried. + error.SetErrorStringWithFormat ("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name); + return error; + } + + if (IsMSA(reg) && !IsMSAAvailable()) + { + error.SetErrorString ("MSA not available on this processor"); + return error; + } + + if (IsMSA(reg) || IsFPR(reg)) + { + uint8_t *src; + + error = ReadCP1(); + + if (!error.Success()) + { + error.SetErrorString ("failed to read co-processor 1 register"); + return error; + } + + if (IsFPR(reg)) + { + assert (reg_info->byte_offset < sizeof(UserArea)); + src = (uint8_t *)&m_fpr + reg_info->byte_offset - (sizeof(m_gpr)); + } + else + { + assert (reg_info->byte_offset < sizeof(UserArea)); + src = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr)); + } + switch (reg_info->byte_size) + { + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + case 16: + reg_value.SetBytes((const void *)src, 16, GetByteOrder()); + break; + default: + assert(false && "Unhandled data size."); + error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); + break; + } + } + else + { + error = ReadRegisterRaw(reg, reg_value); + } + + return error; +} + +lldb_private::Error +NativeRegisterContextLinux_mips64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + Error error; + + assert (reg_info && "reg_info is null"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg_index == LLDB_INVALID_REGNUM) + return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : ""); + + if (IsMSA(reg_index) && !IsMSAAvailable()) + { + error.SetErrorString ("MSA not available on this processor"); + return error; + } + + if (IsFPR(reg_index) || IsMSA(reg_index)) + { + uint8_t *dst; + uint64_t *src; + + // Initialise the FP and MSA buffers by reading all co-processor 1 registers + ReadCP1(); + + if (IsFPR(reg_index)) + { + assert (reg_info->byte_offset < sizeof(UserArea)); + dst = (uint8_t *)&m_fpr + reg_info->byte_offset - (sizeof(m_gpr)); + } + else + { + assert (reg_info->byte_offset < sizeof(UserArea)); + dst = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr)); + } + switch (reg_info->byte_size) + { + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + case 16: + src = (uint64_t *)reg_value.GetBytes(); + *(uint64_t *)dst = *src; + *(uint64_t *)(dst + 8) = *(src + 1); + break; + default: + assert(false && "Unhandled data size."); + error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); + break; + } + error = WriteCP1(); + if (!error.Success()) + { + error.SetErrorString ("failed to write co-processor 1 register"); + return error; + } + } + else + { + error = WriteRegisterRaw(reg_index, reg_value); + } + + return error; +} + +Error +NativeRegisterContextLinux_mips64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (!data_sp) + { + error.SetErrorStringWithFormat ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); + return error; + } + + error = ReadGPR(); + if (!error.Success()) + { + error.SetErrorString ("ReadGPR() failed"); + return error; + } + + error = ReadCP1(); + if (!error.Success()) + { + error.SetErrorString ("ReadCP1() failed"); + return error; + } + + uint8_t *dst = data_sp->GetBytes (); + if (dst == nullptr) + { + error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); + return error; + } + + ::memcpy (dst, &m_gpr, GetRegisterInfoInterface ().GetGPRSize ()); + dst += GetRegisterInfoInterface ().GetGPRSize (); + + ::memcpy (dst, &m_fpr, GetFPRSize ()); + dst += GetFPRSize (); + + ::memcpy (dst, &m_msa, sizeof(MSA_linux_mips)); + + return error; +} + +Error +NativeRegisterContextLinux_mips64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ()); + return error; + } + + + uint8_t *src = data_sp->GetBytes (); + if (src == nullptr) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + + ::memcpy (&m_gpr, src, GetRegisterInfoInterface ().GetGPRSize ()); + src += GetRegisterInfoInterface ().GetGPRSize (); + + ::memcpy (&m_fpr, src, GetFPRSize ()); + src += GetFPRSize (); + + ::memcpy (&m_msa, src, sizeof(MSA_linux_mips)); + + error = WriteGPR(); + if (!error.Success()) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s WriteGPR() failed", __FUNCTION__); + return error; + } + + error = WriteCP1(); + if (!error.Success()) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s WriteCP1() failed", __FUNCTION__); + return error; + } + + return error; +} + +Error +NativeRegisterContextLinux_mips64::ReadCP1() +{ + Error error; + + uint8_t *src, *dst; + + lldb::ByteOrder byte_order = GetByteOrder(); + + uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig); + + if (IsMSAAvailable()) + { + error = NativeRegisterContextLinux::ReadRegisterSet(&m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA); + src = (uint8_t *)&m_msa + (IsBigEndian * 8); + dst = (uint8_t *)&m_fpr; + for ( int i = 0; i < NUM_REGISTERS; i++) + { + // Copy fp values from msa buffer fetched via ptrace + *(uint64_t *) dst = *(uint64_t *) src; + src = src + 16; + dst = dst + 8; + } + m_fpr.fir = m_msa.fir; + m_fpr.fcsr = m_msa.fcsr; + m_fpr.config5 = m_msa.config5; + } + else + { + error = NativeRegisterContextLinux::ReadFPR(); + } + + if (IsFR0() || IsFRE()) + { + src = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4); + dst = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4); + for (int i = 0; i < (NUM_REGISTERS / 2); i++) + { + // copy odd single from top of neighbouring even double + *(uint32_t *) dst = *(uint32_t *) src; + src = src + 16; + dst = dst + 16; + } + } + + return error; +} + +Error +NativeRegisterContextLinux_mips64::WriteCP1() +{ + Error error; + + uint8_t *src, *dst; + + lldb::ByteOrder byte_order = GetByteOrder(); + + uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig); + + if (IsFR0() || IsFRE()) + { + src = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4); + dst = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4); + for (int i = 0; i < (NUM_REGISTERS / 2); i++) + { + // copy odd single to top of neighbouring even double + *(uint32_t *) dst = *(uint32_t *) src; + src = src + 16; + dst = dst + 16; + } + } + + if (IsMSAAvailable()) + { + dst = (uint8_t *)&m_msa + (IsBigEndian * 8); + src = (uint8_t *)&m_fpr; + for (int i = 0; i < NUM_REGISTERS; i++) + { + // Copy fp values to msa buffer for ptrace + *(uint64_t *) dst = *(uint64_t *) src; + dst = dst + 16; + src = src + 8; + } + m_msa.fir = m_fpr.fir; + m_msa.fcsr = m_fpr.fcsr; + m_msa.config5 = m_fpr.config5; + error = NativeRegisterContextLinux::WriteRegisterSet(&m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA); + } + else + { + error = NativeRegisterContextLinux::WriteFPR(); + } + + return error; +} + +bool +NativeRegisterContextLinux_mips64::IsFR0() +{ + const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex (gpr_sr_mips64); + + RegisterValue reg_value; + ReadRegister (reg_info_p, reg_value); + + uint64_t value = reg_value.GetAsUInt64(); + + return (!(value & SR_FR)); +} + +bool +NativeRegisterContextLinux_mips64::IsFRE() +{ + const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex (gpr_config5_mips64); + + RegisterValue reg_value; + ReadRegister (reg_info_p, reg_value); + + uint64_t config5 = reg_value.GetAsUInt64(); + + return (config5 & CONFIG5_FRE); +} + +bool +NativeRegisterContextLinux_mips64::IsFPR(uint32_t reg_index) const +{ + return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr); +} + +static uint32_t +GetWatchHi (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watchhi[index]; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watchhi[index]; + if(log) + log->Printf("Invalid watch register style"); + return 0; +} + +static void +SetWatchHi (struct pt_watch_regs *regs, uint32_t index, uint16_t value) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + regs->mips32.watchhi[index] = value; + else if (regs->style == pt_watch_style_mips64) + regs->mips64.watchhi[index] = value; + if(log) + log->Printf("Invalid watch register style"); + return; +} + +static lldb::addr_t +GetWatchLo (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watchlo[index]; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watchlo[index]; + if(log) + log->Printf("Invalid watch register style"); + return LLDB_INVALID_ADDRESS; +} + +static void +SetWatchLo (struct pt_watch_regs *regs, uint32_t index, uint64_t value) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + regs->mips32.watchlo[index] = (uint32_t) value; + else if (regs->style == pt_watch_style_mips64) + regs->mips64.watchlo[index] = value; + if(log) + log->Printf("Invalid watch register style"); + return; +} + +static uint32_t +GetIRWMask (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watch_masks[index] & IRW; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watch_masks[index] & IRW; + if(log) + log->Printf("Invalid watch register style"); + return 0; +} + +static uint32_t +GetRegMask (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watch_masks[index] & ~IRW; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watch_masks[index] & ~IRW; + if(log) + log->Printf("Invalid watch register style"); + return 0; +} + +static lldb::addr_t +GetRangeMask (lldb::addr_t mask) +{ + lldb::addr_t mask_bit = 1; + while (mask_bit < mask) + { + mask = mask | mask_bit; + mask_bit <<= 1; + } + return mask; +} + +static int +GetVacantWatchIndex (struct pt_watch_regs *regs, lldb::addr_t addr, uint32_t size, uint32_t irw, uint32_t num_valid) +{ + lldb::addr_t last_byte = addr + size - 1; + lldb::addr_t mask = GetRangeMask (addr ^ last_byte) | IRW; + lldb::addr_t base_addr = addr & ~mask; + + // Check if this address is already watched by previous watch points. + lldb::addr_t lo; + uint16_t hi; + uint32_t vacant_watches = 0; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (regs, index); + if (lo != 0 && irw == ((uint32_t) lo & irw)) + { + hi = GetWatchHi (regs, index) | IRW; + lo &= ~(lldb::addr_t) hi; + if (addr >= lo && last_byte <= (lo + hi)) + return index; + } + else + vacant_watches++; + } + + // Now try to find a vacant index + if(vacant_watches > 0) + { + vacant_watches = 0; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (regs, index); + if (lo == 0 + && irw == (GetIRWMask (regs, index) & irw)) + { + if (mask <= (GetRegMask (regs, index) | IRW)) + { + // It fits, we can use it. + SetWatchLo (regs, index, base_addr | irw); + SetWatchHi (regs, index, mask & ~IRW); + return index; + } + else + { + // It doesn't fit, but has the proper IRW capabilities + vacant_watches++; + } + } + } + + if (vacant_watches > 1) + { + // Split this watchpoint accross several registers + struct pt_watch_regs regs_copy; + regs_copy = *regs; + lldb::addr_t break_addr; + uint32_t segment_size; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (®s_copy, index); + hi = GetRegMask (®s_copy, index) | IRW; + if (lo == 0 && irw == (hi & irw)) + { + lo = addr & ~(lldb::addr_t) hi; + break_addr = lo + hi + 1; + if (break_addr >= addr + size) + segment_size = size; + else + segment_size = break_addr - addr; + mask = GetRangeMask (addr ^ (addr + segment_size - 1)); + SetWatchLo (®s_copy, index, (addr & ~mask) | irw); + SetWatchHi (®s_copy, index, mask & ~IRW); + if (break_addr >= addr + size) + { + *regs = regs_copy; + return index; + } + size = addr + size - break_addr; + addr = break_addr; + } + } + } + } + return LLDB_INVALID_INDEX32; +} + +bool +NativeRegisterContextLinux_mips64::IsMSA(uint32_t reg_index) const +{ + return (m_reg_info.first_msa <= reg_index && reg_index <= m_reg_info.last_msa); +} + +bool +NativeRegisterContextLinux_mips64::IsMSAAvailable() +{ + MSA_linux_mips msa_buf; + unsigned int regset = NT_MIPS_MSA; + + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, Host::GetCurrentProcessID(), static_cast(®set), &msa_buf, sizeof(MSA_linux_mips)); + + if (error.Success() && msa_buf.mir) + { + return true; + } + + return false; +} + +Error +NativeRegisterContextLinux_mips64::IsWatchpointHit (uint32_t wp_index, bool &is_hit) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + // reading the current state of watch regs + struct pt_watch_regs watch_readback; + Error error = DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast(&watch_readback)); + + if (GetWatchHi (&watch_readback, wp_index) & (IRW)) + { + // clear hit flag in watchhi + SetWatchHi (&watch_readback, wp_index, (GetWatchHi (&watch_readback, wp_index) & ~(IRW))); + DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast(&watch_readback)); + + is_hit = true; + return error; + } + is_hit = false; + return error; +} + +Error +NativeRegisterContextLinux_mips64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) + { + bool is_hit; + Error error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) { + wp_index = LLDB_INVALID_INDEX32; + } else if (is_hit) { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +Error +NativeRegisterContextLinux_mips64::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) +{ + is_vacant = false; + return Error("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointVacant not implemented"); +} + +bool +NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + struct pt_watch_regs regs; + // First reading the current state of watch regs + DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); + + if (regs.style == pt_watch_style_mips32) + { + regs.mips32.watchlo[wp_index] = default_watch_regs.mips32.watchlo[wp_index]; + regs.mips32.watchhi[wp_index] = default_watch_regs.mips32.watchhi[wp_index]; + regs.mips32.watch_masks[wp_index] = default_watch_regs.mips32.watch_masks[wp_index]; + } + else // pt_watch_style_mips64 + { + regs.mips64.watchlo[wp_index] = default_watch_regs.mips64.watchlo[wp_index]; + regs.mips64.watchhi[wp_index] = default_watch_regs.mips64.watchhi[wp_index]; + regs.mips64.watch_masks[wp_index] = default_watch_regs.mips64.watch_masks[wp_index]; + } + + Error error = DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); + if(!error.Fail()) + { + hw_addr_map[wp_index] = LLDB_INVALID_ADDRESS; + return true; + } + return false; +} + +Error +NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints() +{ + return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast(&default_watch_regs)); +} + +Error +NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex ( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) +{ + Error error; + error.SetErrorString ("MIPS TODO: NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex not implemented"); + return error; +} + +uint32_t +NativeRegisterContextLinux_mips64::SetHardwareWatchpoint ( + lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + struct pt_watch_regs regs; + + // First reading the current state of watch regs + DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); + + // Try if a new watch point fits in this state + int index = GetVacantWatchIndex (®s, addr, size, watch_flags, NumSupportedHardwareWatchpoints()); + + // New watchpoint doesn't fit + if (index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + + // It fits, so we go ahead with updating the state of watch regs + DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); + + // Storing exact address + hw_addr_map[index] = addr; + return index; +} + +lldb::addr_t +NativeRegisterContextLinux_mips64::GetWatchpointAddress (uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + + return hw_addr_map[wp_index]; +} + +struct EmulatorBaton +{ + lldb::addr_t m_watch_hit_addr; + NativeProcessLinux* m_process; + NativeRegisterContext* m_reg_context; + + EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : + m_watch_hit_addr(LLDB_INVALID_ADDRESS), + m_process(process), + m_reg_context(reg_context) + {} +}; + +static size_t +ReadMemoryCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, lldb::addr_t addr, + void *dst, size_t length) +{ + size_t bytes_read; + EmulatorBaton* emulator_baton = static_cast(baton); + emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); + return bytes_read; +} + +static size_t +WriteMemoryCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, const void *dst, size_t length) +{ + return length; +} + +static bool +ReadRegisterCallback (EmulateInstruction *instruction, void *baton, + const RegisterInfo *reg_info, RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast(baton); + + const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( + lldb::eRegisterKindDWARF, reg_info->kinds[lldb::eRegisterKindDWARF]); + + Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); + if (error.Success()) + return true; + + return false; +} + +static bool +WriteRegisterCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (reg_info->kinds[lldb::eRegisterKindDWARF] == dwarf_bad_mips64) + { + EmulatorBaton* emulator_baton = static_cast(baton); + emulator_baton->m_watch_hit_addr = reg_value.GetAsUInt64 (); + } + + return true; +} + +/* + * MIPS Linux kernel returns a masked address (last 3bits are masked) + * when a HW watchpoint is hit. However user may not have set a watchpoint + * on this address. Emulate instruction at PC and find the base address of + * the load/store instruction. This will give the exact address used to + * read/write the variable. Send this exact address to client so that + * it can decide to stop or continue the thread. +*/ +lldb::addr_t +NativeRegisterContextLinux_mips64::GetWatchpointHitAddress (uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + + lldb_private::ArchSpec arch; + arch = GetRegisterInfoInterface().GetTargetArchitecture(); + std::unique_ptr emulator_ap( + EmulateInstruction::FindPlugin(arch, lldb_private::eInstructionTypeAny, nullptr)); + + if (emulator_ap == nullptr) + return LLDB_INVALID_ADDRESS; + + EmulatorBaton baton(static_cast(m_thread.GetProcess().get()), this); + emulator_ap->SetBaton (&baton); + emulator_ap->SetReadMemCallback (&ReadMemoryCallback); + emulator_ap->SetReadRegCallback (&ReadRegisterCallback); + emulator_ap->SetWriteMemCallback (&WriteMemoryCallback); + emulator_ap->SetWriteRegCallback (&WriteRegisterCallback); + + if (!emulator_ap->ReadInstruction()) + return LLDB_INVALID_ADDRESS; + + if (emulator_ap->EvaluateInstruction(lldb::eEmulateInstructionOptionNone)) + return baton.m_watch_hit_addr; + + return LLDB_INVALID_ADDRESS; +} + +uint32_t +NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints () +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + struct pt_watch_regs regs; + static int num_valid = 0; + if (!num_valid) + { + DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast(®s)); + default_watch_regs = regs; // Keeping default watch regs values for future use + switch (regs.style) + { + case pt_watch_style_mips32: + num_valid = regs.mips32.num_valid; // Using num_valid as cache + return num_valid; + case pt_watch_style_mips64: + num_valid = regs.mips64.num_valid; + return num_valid; + default: + if(log) + log->Printf("NativeRegisterContextLinux_mips64::%s Error: Unrecognized watch register style", __FUNCTION__); + } + return 0; + } + return num_valid; +} +Error +NativeRegisterContextLinux_mips64::DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) +{ + GPR_linux_mips regs; + ::memset(®s, 0, sizeof(GPR_linux_mips)); + + // Clear all bits in RegisterValue before writing actual value read from ptrace to avoid garbage value in 32-bit MSB + value.SetBytes((void *)(((unsigned char *)®s) + offset), 8, GetByteOrder()); + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); + if (error.Success()) + { + lldb_private::ArchSpec arch; + if (m_thread.GetProcess()->GetArchitecture(arch)) + value.SetBytes((void *)(((unsigned char *)®s) + offset + 4 * (arch.GetMachine() == llvm::Triple::mips)), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8, arch.GetByteOrder()); + else + error.SetErrorString("failed to get architecture"); + } + return error; +} + +Error +NativeRegisterContextLinux_mips64::DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) +{ + GPR_linux_mips regs; + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); + if (error.Success()) + { + lldb_private::ArchSpec arch; + if (m_thread.GetProcess()->GetArchitecture(arch)) + { + ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8); + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); + } + else + error.SetErrorString("failed to get architecture"); + } + return error; +} + +Error +NativeRegisterContextLinux_mips64::DoReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback) +{ + return NativeProcessLinux::PtraceWrapper( PTRACE_GET_WATCH_REGS, m_thread.GetID(), watch_readback); +} + +Error +NativeRegisterContextLinux_mips64::DoWriteWatchPointRegisterValue(lldb::tid_t tid, void* watch_reg_value) +{ + return NativeProcessLinux::PtraceWrapper(PTRACE_SET_WATCH_REGS, m_thread.GetID(), watch_reg_value); +} + +#endif // defined (__mips__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h new file mode 100644 index 000000000000..9368645116e9 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h @@ -0,0 +1,167 @@ +//===-- NativeRegisterContextLinux_mips64.h ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__mips__) + +#ifndef lldb_NativeRegisterContextLinux_mips64_h +#define lldb_NativeRegisterContextLinux_mips64_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/RegisterContext_mips.h" +#include "Plugins/Process/Utility/lldb-mips-linux-register-enums.h" + +#define MAX_NUM_WP 8 + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeRegisterContextLinux_mips64 : public NativeRegisterContextLinux + { + public: + NativeRegisterContextLinux_mips64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount () const override; + + lldb::addr_t + GetPCfromBreakpointLocation (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS) override; + + lldb::addr_t + GetWatchpointHitAddress (uint32_t wp_index) override; + + const RegisterSet * + GetRegisterSet (uint32_t set_index) const override; + + Error + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + + Error + ReadCP1(); + + Error + WriteCP1(); + + Error + IsWatchpointHit (uint32_t wp_index, bool &is_hit) override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + Error + IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) override; + + bool + ClearHardwareWatchpoint (uint32_t wp_index) override; + + Error + ClearAllHardwareWatchpoints () override; + + Error + SetHardwareWatchpointWithIndex (lldb::addr_t addr, size_t size, + uint32_t watch_flags, uint32_t wp_index); + + uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t + GetWatchpointAddress (uint32_t wp_index) override; + + uint32_t + NumSupportedHardwareWatchpoints () override; + + static bool + IsMSAAvailable(); + + protected: + Error + DoReadRegisterValue(uint32_t offset, + const char* reg_name, + uint32_t size, + RegisterValue &value) override; + + Error + DoWriteRegisterValue(uint32_t offset, + const char* reg_name, + const RegisterValue &value) override; + + Error + DoReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback); + + Error + DoWriteWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback); + + bool + IsFR0(); + + bool + IsFRE(); + + bool + IsFPR(uint32_t reg_index) const; + + bool + IsMSA(uint32_t reg_index) const; + + void* + GetGPRBuffer() override { return &m_gpr; } + + void* + GetFPRBuffer() override { return &m_fpr; } + + size_t + GetFPRSize() override { return sizeof(FPR_linux_mips); } + + private: + // Info about register ranges. + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + uint32_t first_msa; + uint32_t last_msa; + }; + + RegInfo m_reg_info; + + GPR_linux_mips m_gpr; + + FPR_linux_mips m_fpr; + + MSA_linux_mips m_msa; + + lldb::addr_t hw_addr_map[MAX_NUM_WP]; + + IOVEC_mips m_iovec; + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_mips64_h + +#endif // defined (__mips__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp new file mode 100755 index 000000000000..2080d2ede372 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -0,0 +1,1239 @@ +//===-- NativeRegisterContextLinux_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 "NativeRegisterContextLinux_x86_64.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/HostInfo.h" + +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// ---------------------------------------------------------------------------- +// Private namespace. +// ---------------------------------------------------------------------------- + +namespace +{ + // x86 32-bit general purpose registers. + const uint32_t + g_gpr_regnums_i386[] = + { + lldb_eax_i386, + lldb_ebx_i386, + lldb_ecx_i386, + lldb_edx_i386, + lldb_edi_i386, + lldb_esi_i386, + lldb_ebp_i386, + lldb_esp_i386, + lldb_eip_i386, + lldb_eflags_i386, + lldb_cs_i386, + lldb_fs_i386, + lldb_gs_i386, + lldb_ss_i386, + lldb_ds_i386, + lldb_es_i386, + lldb_ax_i386, + lldb_bx_i386, + lldb_cx_i386, + lldb_dx_i386, + lldb_di_i386, + lldb_si_i386, + lldb_bp_i386, + lldb_sp_i386, + lldb_ah_i386, + lldb_bh_i386, + lldb_ch_i386, + lldb_dh_i386, + lldb_al_i386, + lldb_bl_i386, + lldb_cl_i386, + lldb_dl_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) - 1 == k_num_gpr_registers_i386, + "g_gpr_regnums_i386 has wrong number of register infos"); + + // x86 32-bit floating point registers. + const uint32_t + g_fpu_regnums_i386[] = + { + lldb_fctrl_i386, + lldb_fstat_i386, + lldb_ftag_i386, + lldb_fop_i386, + lldb_fiseg_i386, + lldb_fioff_i386, + lldb_foseg_i386, + lldb_fooff_i386, + lldb_mxcsr_i386, + lldb_mxcsrmask_i386, + lldb_st0_i386, + lldb_st1_i386, + lldb_st2_i386, + lldb_st3_i386, + lldb_st4_i386, + lldb_st5_i386, + lldb_st6_i386, + lldb_st7_i386, + lldb_mm0_i386, + lldb_mm1_i386, + lldb_mm2_i386, + lldb_mm3_i386, + lldb_mm4_i386, + lldb_mm5_i386, + lldb_mm6_i386, + lldb_mm7_i386, + lldb_xmm0_i386, + lldb_xmm1_i386, + lldb_xmm2_i386, + lldb_xmm3_i386, + lldb_xmm4_i386, + lldb_xmm5_i386, + lldb_xmm6_i386, + lldb_xmm7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_fpu_regnums_i386) / sizeof(g_fpu_regnums_i386[0])) - 1 == k_num_fpr_registers_i386, + "g_fpu_regnums_i386 has wrong number of register infos"); + + // x86 32-bit AVX registers. + const uint32_t + g_avx_regnums_i386[] = + { + lldb_ymm0_i386, + lldb_ymm1_i386, + lldb_ymm2_i386, + lldb_ymm3_i386, + lldb_ymm4_i386, + lldb_ymm5_i386, + lldb_ymm6_i386, + lldb_ymm7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) - 1 == k_num_avx_registers_i386, + " g_avx_regnums_i386 has wrong number of register infos"); + + // x86 64-bit general purpose registers. + static const + uint32_t g_gpr_regnums_x86_64[] = + { + lldb_rax_x86_64, + lldb_rbx_x86_64, + lldb_rcx_x86_64, + lldb_rdx_x86_64, + lldb_rdi_x86_64, + lldb_rsi_x86_64, + lldb_rbp_x86_64, + lldb_rsp_x86_64, + lldb_r8_x86_64, + lldb_r9_x86_64, + lldb_r10_x86_64, + lldb_r11_x86_64, + lldb_r12_x86_64, + lldb_r13_x86_64, + lldb_r14_x86_64, + lldb_r15_x86_64, + lldb_rip_x86_64, + lldb_rflags_x86_64, + lldb_cs_x86_64, + lldb_fs_x86_64, + lldb_gs_x86_64, + lldb_ss_x86_64, + lldb_ds_x86_64, + lldb_es_x86_64, + lldb_eax_x86_64, + lldb_ebx_x86_64, + lldb_ecx_x86_64, + lldb_edx_x86_64, + lldb_edi_x86_64, + lldb_esi_x86_64, + lldb_ebp_x86_64, + lldb_esp_x86_64, + lldb_r8d_x86_64, // Low 32 bits or r8 + lldb_r9d_x86_64, // Low 32 bits or r9 + lldb_r10d_x86_64, // Low 32 bits or r10 + lldb_r11d_x86_64, // Low 32 bits or r11 + lldb_r12d_x86_64, // Low 32 bits or r12 + lldb_r13d_x86_64, // Low 32 bits or r13 + lldb_r14d_x86_64, // Low 32 bits or r14 + lldb_r15d_x86_64, // Low 32 bits or r15 + lldb_ax_x86_64, + lldb_bx_x86_64, + lldb_cx_x86_64, + lldb_dx_x86_64, + lldb_di_x86_64, + lldb_si_x86_64, + lldb_bp_x86_64, + lldb_sp_x86_64, + lldb_r8w_x86_64, // Low 16 bits or r8 + lldb_r9w_x86_64, // Low 16 bits or r9 + lldb_r10w_x86_64, // Low 16 bits or r10 + lldb_r11w_x86_64, // Low 16 bits or r11 + lldb_r12w_x86_64, // Low 16 bits or r12 + lldb_r13w_x86_64, // Low 16 bits or r13 + lldb_r14w_x86_64, // Low 16 bits or r14 + lldb_r15w_x86_64, // Low 16 bits or r15 + lldb_ah_x86_64, + lldb_bh_x86_64, + lldb_ch_x86_64, + lldb_dh_x86_64, + lldb_al_x86_64, + lldb_bl_x86_64, + lldb_cl_x86_64, + lldb_dl_x86_64, + lldb_dil_x86_64, + lldb_sil_x86_64, + lldb_bpl_x86_64, + lldb_spl_x86_64, + lldb_r8l_x86_64, // Low 8 bits or r8 + lldb_r9l_x86_64, // Low 8 bits or r9 + lldb_r10l_x86_64, // Low 8 bits or r10 + lldb_r11l_x86_64, // Low 8 bits or r11 + lldb_r12l_x86_64, // Low 8 bits or r12 + lldb_r13l_x86_64, // Low 8 bits or r13 + lldb_r14l_x86_64, // Low 8 bits or r14 + lldb_r15l_x86_64, // Low 8 bits or r15 + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) - 1 == k_num_gpr_registers_x86_64, + "g_gpr_regnums_x86_64 has wrong number of register infos"); + + // x86 64-bit floating point registers. + static const uint32_t + g_fpu_regnums_x86_64[] = + { + lldb_fctrl_x86_64, + lldb_fstat_x86_64, + lldb_ftag_x86_64, + lldb_fop_x86_64, + lldb_fiseg_x86_64, + lldb_fioff_x86_64, + lldb_foseg_x86_64, + lldb_fooff_x86_64, + lldb_mxcsr_x86_64, + lldb_mxcsrmask_x86_64, + lldb_st0_x86_64, + lldb_st1_x86_64, + lldb_st2_x86_64, + lldb_st3_x86_64, + lldb_st4_x86_64, + lldb_st5_x86_64, + lldb_st6_x86_64, + lldb_st7_x86_64, + lldb_mm0_x86_64, + lldb_mm1_x86_64, + lldb_mm2_x86_64, + lldb_mm3_x86_64, + lldb_mm4_x86_64, + lldb_mm5_x86_64, + lldb_mm6_x86_64, + lldb_mm7_x86_64, + lldb_xmm0_x86_64, + lldb_xmm1_x86_64, + lldb_xmm2_x86_64, + lldb_xmm3_x86_64, + lldb_xmm4_x86_64, + lldb_xmm5_x86_64, + lldb_xmm6_x86_64, + lldb_xmm7_x86_64, + lldb_xmm8_x86_64, + lldb_xmm9_x86_64, + lldb_xmm10_x86_64, + lldb_xmm11_x86_64, + lldb_xmm12_x86_64, + lldb_xmm13_x86_64, + lldb_xmm14_x86_64, + lldb_xmm15_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_fpu_regnums_x86_64) / sizeof(g_fpu_regnums_x86_64[0])) - 1 == k_num_fpr_registers_x86_64, + "g_fpu_regnums_x86_64 has wrong number of register infos"); + + // x86 64-bit AVX registers. + static const uint32_t + g_avx_regnums_x86_64[] = + { + lldb_ymm0_x86_64, + lldb_ymm1_x86_64, + lldb_ymm2_x86_64, + lldb_ymm3_x86_64, + lldb_ymm4_x86_64, + lldb_ymm5_x86_64, + lldb_ymm6_x86_64, + lldb_ymm7_x86_64, + lldb_ymm8_x86_64, + lldb_ymm9_x86_64, + lldb_ymm10_x86_64, + lldb_ymm11_x86_64, + lldb_ymm12_x86_64, + lldb_ymm13_x86_64, + lldb_ymm14_x86_64, + lldb_ymm15_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) - 1 == k_num_avx_registers_x86_64, + "g_avx_regnums_x86_64 has wrong number of register infos"); + + // Number of register sets provided by this context. + enum + { + k_num_extended_register_sets = 1, + k_num_register_sets = 3 + }; + + // Register sets for x86 32-bit. + static const RegisterSet + g_reg_sets_i386[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_i386, g_gpr_regnums_i386 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_i386, g_fpu_regnums_i386 }, + { "Advanced Vector Extensions", "avx", k_num_avx_registers_i386, g_avx_regnums_i386 } + }; + + // Register sets for x86 64-bit. + static const RegisterSet + g_reg_sets_x86_64[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_x86_64, g_gpr_regnums_x86_64 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_x86_64, g_fpu_regnums_x86_64 }, + { "Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64, g_avx_regnums_x86_64 } + }; +} + +#define REG_CONTEXT_SIZE (GetRegisterInfoInterface ().GetGPRSize () + sizeof(FPR)) + +// ---------------------------------------------------------------------------- +// Required ptrace defines. +// ---------------------------------------------------------------------------- + +// Support ptrace extensions even when compiled without required kernel support +#ifndef NT_X86_XSTATE +#define NT_X86_XSTATE 0x202 +#endif +#ifndef NT_PRXFPREG +#define NT_PRXFPREG 0x46e62b7f +#endif + +NativeRegisterContextLinux* +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_x86_64(target_arch, native_thread, concrete_frame_idx); +} + +// ---------------------------------------------------------------------------- +// NativeRegisterContextLinux_x86_64 members. +// ---------------------------------------------------------------------------- + +static RegisterInfoInterface* +CreateRegisterInfoInterface(const ArchSpec& target_arch) +{ + if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) + { + // 32-bit hosts run with a RegisterContextLinux_i386 context. + return new RegisterContextLinux_i386(target_arch); + } + else + { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the x86_64 register context. + return new RegisterContextLinux_x86_64 (target_arch); + } +} + +NativeRegisterContextLinux_x86_64::NativeRegisterContextLinux_x86_64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) : + NativeRegisterContextLinux (native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)), + m_fpr_type (eFPRTypeNotValid), + m_fpr (), + m_iovec (), + m_ymm_set (), + m_reg_info (), + m_gpr_x86_64 () +{ + // Set up data about ranges of valid registers. + switch (target_arch.GetMachine ()) + { + case llvm::Triple::x86: + m_reg_info.num_registers = k_num_registers_i386; + m_reg_info.num_gpr_registers = k_num_gpr_registers_i386; + m_reg_info.num_fpr_registers = k_num_fpr_registers_i386; + m_reg_info.num_avx_registers = k_num_avx_registers_i386; + m_reg_info.last_gpr = k_last_gpr_i386; + m_reg_info.first_fpr = k_first_fpr_i386; + m_reg_info.last_fpr = k_last_fpr_i386; + m_reg_info.first_st = lldb_st0_i386; + m_reg_info.last_st = lldb_st7_i386; + m_reg_info.first_mm = lldb_mm0_i386; + m_reg_info.last_mm = lldb_mm7_i386; + m_reg_info.first_xmm = lldb_xmm0_i386; + m_reg_info.last_xmm = lldb_xmm7_i386; + m_reg_info.first_ymm = lldb_ymm0_i386; + m_reg_info.last_ymm = lldb_ymm7_i386; + m_reg_info.first_dr = lldb_dr0_i386; + m_reg_info.gpr_flags = lldb_eflags_i386; + break; + case llvm::Triple::x86_64: + m_reg_info.num_registers = k_num_registers_x86_64; + m_reg_info.num_gpr_registers = k_num_gpr_registers_x86_64; + m_reg_info.num_fpr_registers = k_num_fpr_registers_x86_64; + m_reg_info.num_avx_registers = k_num_avx_registers_x86_64; + m_reg_info.last_gpr = k_last_gpr_x86_64; + m_reg_info.first_fpr = k_first_fpr_x86_64; + m_reg_info.last_fpr = k_last_fpr_x86_64; + m_reg_info.first_st = lldb_st0_x86_64; + m_reg_info.last_st = lldb_st7_x86_64; + m_reg_info.first_mm = lldb_mm0_x86_64; + m_reg_info.last_mm = lldb_mm7_x86_64; + m_reg_info.first_xmm = lldb_xmm0_x86_64; + m_reg_info.last_xmm = lldb_xmm15_x86_64; + m_reg_info.first_ymm = lldb_ymm0_x86_64; + m_reg_info.last_ymm = lldb_ymm15_x86_64; + m_reg_info.first_dr = lldb_dr0_x86_64; + m_reg_info.gpr_flags = lldb_rflags_x86_64; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + // Initialize m_iovec to point to the buffer and buffer size + // using the conventions of Berkeley style UIO structures, as required + // by PTRACE extensions. + m_iovec.iov_base = &m_fpr.xstate.xsave; + m_iovec.iov_len = sizeof(m_fpr.xstate.xsave); + + // Clear out the FPR state. + ::memset(&m_fpr, 0, sizeof(FPR)); + + // Store byte offset of fctrl (i.e. first register of FPR) + const RegisterInfo *reg_info_fctrl = GetRegisterInfoByName("fctrl"); + m_fctrl_offset_in_userarea = reg_info_fctrl->byte_offset; +} + +// CONSIDER after local and llgs debugging are merged, register set support can +// be moved into a base x86-64 class with IsRegisterSetAvailable made virtual. +uint32_t +NativeRegisterContextLinux_x86_64::GetRegisterSetCount () const +{ + uint32_t sets = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + { + if (IsRegisterSetAvailable (set_index)) + ++sets; + } + + return sets; +} + +uint32_t +NativeRegisterContextLinux_x86_64::GetUserRegisterCount() const +{ + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + { + const RegisterSet* set = GetRegisterSet(set_index); + if (set) + count += set->num_registers; + } + return count; +} + +const RegisterSet * +NativeRegisterContextLinux_x86_64::GetRegisterSet (uint32_t set_index) const +{ + if (!IsRegisterSetAvailable (set_index)) + return nullptr; + + switch (GetRegisterInfoInterface ().GetTargetArchitecture ().GetMachine ()) + { + case llvm::Triple::x86: + return &g_reg_sets_i386[set_index]; + case llvm::Triple::x86_64: + return &g_reg_sets_x86_64[set_index]; + default: + assert (false && "Unhandled target architecture."); + return nullptr; + } + + return nullptr; +} + +Error +NativeRegisterContextLinux_x86_64::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + Error error; + + if (!reg_info) + { + error.SetErrorString ("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + { + // This is likely an internal register for lldb use only and should not be directly queried. + error.SetErrorStringWithFormat ("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name); + return error; + } + + if (IsFPR(reg, GetFPRType())) + { + error = ReadFPR(); + if (error.Fail()) + return error; + } + else + { + uint32_t full_reg = reg; + bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM); + + if (is_subreg) + { + // Read the full aligned 64-bit register. + full_reg = reg_info->invalidate_regs[0]; + } + + error = ReadRegisterRaw(full_reg, reg_value); + + if (error.Success ()) + { + // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right. + if (is_subreg && (reg_info->byte_offset & 0x1)) + reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8); + + // If our return byte size was greater than the return value reg size, then + // use the type specified by reg_info rather than the uint64_t default + if (reg_value.GetByteSize() > reg_info->byte_size) + reg_value.SetType(reg_info); + } + return error; + } + + if (reg_info->encoding == lldb::eEncodingVector) + { + lldb::ByteOrder byte_order = GetByteOrder(); + + if (byte_order != lldb::eByteOrderInvalid) + { + if (reg >= m_reg_info.first_st && reg <= m_reg_info.last_st) + reg_value.SetBytes(m_fpr.xstate.fxsave.stmm[reg - m_reg_info.first_st].bytes, reg_info->byte_size, byte_order); + if (reg >= m_reg_info.first_mm && reg <= m_reg_info.last_mm) + reg_value.SetBytes(m_fpr.xstate.fxsave.stmm[reg - m_reg_info.first_mm].bytes, reg_info->byte_size, byte_order); + if (reg >= m_reg_info.first_xmm && reg <= m_reg_info.last_xmm) + reg_value.SetBytes(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_xmm].bytes, reg_info->byte_size, byte_order); + if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm) + { + // Concatenate ymm using the register halves in xmm.bytes and ymmh.bytes + if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoYMM(reg, byte_order)) + reg_value.SetBytes(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes, reg_info->byte_size, byte_order); + else + { + error.SetErrorString ("failed to copy ymm register value"); + return error; + } + } + + if (reg_value.GetType() != RegisterValue::eTypeBytes) + error.SetErrorString ("write failed - type was expected to be RegisterValue::eTypeBytes"); + + return error; + } + + error.SetErrorString ("byte order is invalid"); + return error; + } + + // Get pointer to m_fpr.xstate.fxsave variable and set the data from it. + + // Byte offsets of all registers are calculated wrt 'UserArea' structure. + // However, ReadFPR() reads fpu registers {using ptrace(PTRACE_GETFPREGS,..)} + // and stores them in 'm_fpr' (of type FPR structure). To extract values of fpu + // registers, m_fpr should be read at byte offsets calculated wrt to FPR structure. + + // Since, FPR structure is also one of the member of UserArea structure. + // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) - byte_offset(fctrl wrt UserArea) + assert ( (reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr)); + uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea; + switch (reg_info->byte_size) + { + case 1: + reg_value.SetUInt8(*(uint8_t *)src); + break; + case 2: + reg_value.SetUInt16(*(uint16_t *)src); + break; + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + default: + assert(false && "Unhandled data size."); + error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); + break; + } + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + assert (reg_info && "reg_info is null"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : ""); + + if (IsGPR(reg_index)) + return WriteRegisterRaw(reg_index, reg_value); + + if (IsFPR(reg_index, GetFPRType())) + { + if (reg_info->encoding == lldb::eEncodingVector) + { + if (reg_index >= m_reg_info.first_st && reg_index <= m_reg_info.last_st) + ::memcpy (m_fpr.xstate.fxsave.stmm[reg_index - m_reg_info.first_st].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + + if (reg_index >= m_reg_info.first_mm && reg_index <= m_reg_info.last_mm) + ::memcpy (m_fpr.xstate.fxsave.stmm[reg_index - m_reg_info.first_mm].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + + if (reg_index >= m_reg_info.first_xmm && reg_index <= m_reg_info.last_xmm) + ::memcpy (m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_xmm].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + + if (reg_index >= m_reg_info.first_ymm && reg_index <= m_reg_info.last_ymm) + { + if (GetFPRType() != eFPRTypeXSAVE) + return Error ("target processor does not support AVX"); + + // Store ymm register content, and split into the register halves in xmm.bytes and ymmh.bytes + ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + if (!CopyYMMtoXSTATE(reg_index, GetByteOrder())) + return Error ("CopyYMMtoXSTATE() failed"); + } + } + else + { + // Get pointer to m_fpr.xstate.fxsave variable and set the data to it. + + // Byte offsets of all registers are calculated wrt 'UserArea' structure. + // However, WriteFPR() takes m_fpr (of type FPR structure) and writes only fpu + // registers using ptrace(PTRACE_SETFPREGS,..) API. Hence fpu registers should + // be written in m_fpr at byte offsets calculated wrt FPR structure. + + // Since, FPR structure is also one of the member of UserArea structure. + // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) - byte_offset(fctrl wrt UserArea) + assert ( (reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr)); + uint8_t *dst = (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea; + switch (reg_info->byte_size) + { + case 1: + *(uint8_t *)dst = reg_value.GetAsUInt8(); + break; + case 2: + *(uint16_t *)dst = reg_value.GetAsUInt16(); + break; + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error ("unhandled register data size %" PRIu32, reg_info->byte_size); + } + } + + Error error = WriteFPR(); + if (error.Fail()) + return error; + + if (IsAVX(reg_index)) + { + if (!CopyYMMtoXSTATE(reg_index, GetByteOrder())) + return Error ("CopyYMMtoXSTATE() failed"); + } + return Error (); + } + return Error ("failed - register wasn't recognized to be a GPR or an FPR, write strategy unknown"); +} + +Error +NativeRegisterContextLinux_x86_64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (!data_sp) + { + error.SetErrorStringWithFormat ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); + return error; + } + + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes (); + if (dst == nullptr) + { + error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); + return error; + } + + ::memcpy (dst, &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ()); + dst += GetRegisterInfoInterface ().GetGPRSize (); + if (GetFPRType () == eFPRTypeFXSAVE) + ::memcpy (dst, &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave)); + else if (GetFPRType () == eFPRTypeXSAVE) + { + lldb::ByteOrder byte_order = GetByteOrder (); + + // Assemble the YMM register content from the register halves. + for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; ++reg) + { + if (!CopyXSTATEtoYMM (reg, byte_order)) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s CopyXSTATEtoYMM() failed for reg num %" PRIu32, __FUNCTION__, reg); + return error; + } + } + + // Copy the extended register state including the assembled ymm registers. + ::memcpy (dst, &m_fpr, sizeof (m_fpr)); + } + else + { + assert (false && "how do we save the floating point registers?"); + error.SetErrorString ("unsure how to save the floating point registers"); + } + /** The following code is specific to Linux x86 based architectures, + * where the register orig_eax (32 bit)/orig_rax (64 bit) is set to + * -1 to solve the bug 23659, such a setting prevents the automatic + * decrement of the instruction pointer which was causing the SIGILL + * exception. + * **/ + + RegisterValue value((uint64_t) -1); + const RegisterInfo *reg_info = GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_eax"); + if (reg_info == nullptr) + reg_info = GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_rax"); + + if (reg_info != nullptr) + return DoWriteRegisterValue(reg_info->byte_offset,reg_info->name,value); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ()); + return error; + } + + + uint8_t *src = data_sp->GetBytes (); + if (src == nullptr) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + ::memcpy (&m_gpr_x86_64, src, GetRegisterInfoInterface ().GetGPRSize ()); + + error = WriteGPR(); + if (error.Fail()) + return error; + + src += GetRegisterInfoInterface ().GetGPRSize (); + if (GetFPRType () == eFPRTypeFXSAVE) + ::memcpy (&m_fpr.xstate.fxsave, src, sizeof(m_fpr.xstate.fxsave)); + else if (GetFPRType () == eFPRTypeXSAVE) + ::memcpy (&m_fpr.xstate.xsave, src, sizeof(m_fpr.xstate.xsave)); + + error = WriteFPR(); + if (error.Fail()) + return error; + + if (GetFPRType() == eFPRTypeXSAVE) + { + lldb::ByteOrder byte_order = GetByteOrder(); + + // Parse the YMM register content from the register halves. + for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; ++reg) + { + if (!CopyYMMtoXSTATE (reg, byte_order)) + { + error.SetErrorStringWithFormat ("NativeRegisterContextLinux_x86_64::%s CopyYMMtoXSTATE() failed for reg num %" PRIu32, __FUNCTION__, reg); + return error; + } + } + } + + return error; +} + +bool +NativeRegisterContextLinux_x86_64::IsRegisterSetAvailable (uint32_t set_index) const +{ + // Note: Extended register sets are assumed to be at the end of g_reg_sets. + uint32_t num_sets = k_num_register_sets - k_num_extended_register_sets; + + if (GetFPRType () == eFPRTypeXSAVE) + { + // AVX is the first extended register set. + ++num_sets; + } + return (set_index < num_sets); +} + +bool +NativeRegisterContextLinux_x86_64::IsGPR(uint32_t reg_index) const +{ + // GPRs come first. + return reg_index <= m_reg_info.last_gpr; +} + +NativeRegisterContextLinux_x86_64::FPRType +NativeRegisterContextLinux_x86_64::GetFPRType () const +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (m_fpr_type == eFPRTypeNotValid) + { + // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx. + + // Try and see if AVX register retrieval works. + m_fpr_type = eFPRTypeXSAVE; + if (const_cast(this)->ReadFPR().Fail()) + { + // Fall back to general floating point with no AVX support. + m_fpr_type = eFPRTypeFXSAVE; + + // Check if FXSAVE area can be read. + if (const_cast(this)->ReadFPR().Fail()) + { + if (log) + log->Printf("NativeRegisterContextLinux_x86_64::%s ptrace APIs failed to read XSAVE/FXSAVE area", __FUNCTION__); + } + } + } + return m_fpr_type; +} + +bool +NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index) const +{ + return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr); +} + +bool +NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index, FPRType fpr_type) const +{ + bool generic_fpr = IsFPR(reg_index); + + if (fpr_type == eFPRTypeXSAVE) + return generic_fpr || IsAVX(reg_index); + return generic_fpr; +} + +Error +NativeRegisterContextLinux_x86_64::WriteFPR() +{ + const FPRType fpr_type = GetFPRType (); + const lldb_private::ArchSpec& target_arch = GetRegisterInfoInterface().GetTargetArchitecture(); + switch (fpr_type) + { + case FPRType::eFPRTypeFXSAVE: + // For 32-bit inferiors on x86_32/x86_64 architectures, + // FXSAVE area can be written using PTRACE_SETREGSET ptrace api + // For 64-bit inferiors on x86_64 architectures, + // FXSAVE area can be written using PTRACE_SETFPREGS ptrace api + switch (target_arch.GetMachine ()) + { + case llvm::Triple::x86: + return WriteRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_PRXFPREG); + case llvm::Triple::x86_64: + return NativeRegisterContextLinux::WriteFPR(); + default: + assert(false && "Unhandled target architecture."); + break; + } + case FPRType::eFPRTypeXSAVE: + return WriteRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE); + default: + return Error("Unrecognized FPR type"); + } +} + +bool +NativeRegisterContextLinux_x86_64::IsAVX(uint32_t reg_index) const +{ + return (m_reg_info.first_ymm <= reg_index && reg_index <= m_reg_info.last_ymm); +} + +bool +NativeRegisterContextLinux_x86_64::CopyXSTATEtoYMM (uint32_t reg_index, lldb::ByteOrder byte_order) +{ + if (!IsAVX (reg_index)) + return false; + + if (byte_order == lldb::eByteOrderLittle) + { + ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes, + m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_ymm].bytes, + sizeof (XMMReg)); + ::memcpy (m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes + sizeof (XMMReg), + m_fpr.xstate.xsave.ymmh[reg_index - m_reg_info.first_ymm].bytes, + sizeof (YMMHReg)); + return true; + } + + if (byte_order == lldb::eByteOrderBig) + { + ::memcpy(m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes + sizeof (XMMReg), + m_fpr.xstate.fxsave.xmm[reg_index - m_reg_info.first_ymm].bytes, + sizeof (XMMReg)); + ::memcpy(m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes, + m_fpr.xstate.xsave.ymmh[reg_index - m_reg_info.first_ymm].bytes, + sizeof (YMMHReg)); + return true; + } + return false; // unsupported or invalid byte order + +} + +bool +NativeRegisterContextLinux_x86_64::CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order) +{ + if (!IsAVX(reg)) + return false; + + if (byte_order == lldb::eByteOrderLittle) + { + ::memcpy(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_ymm].bytes, + m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes, + sizeof(XMMReg)); + ::memcpy(m_fpr.xstate.xsave.ymmh[reg - m_reg_info.first_ymm].bytes, + m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes + sizeof(XMMReg), + sizeof(YMMHReg)); + return true; + } + + if (byte_order == lldb::eByteOrderBig) + { + ::memcpy(m_fpr.xstate.fxsave.xmm[reg - m_reg_info.first_ymm].bytes, + m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes + sizeof(XMMReg), + sizeof(XMMReg)); + ::memcpy(m_fpr.xstate.xsave.ymmh[reg - m_reg_info.first_ymm].bytes, + m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes, + sizeof(YMMHReg)); + return true; + } + return false; // unsupported or invalid byte order +} + +void* +NativeRegisterContextLinux_x86_64::GetFPRBuffer() +{ + const FPRType fpr_type = GetFPRType (); + switch (fpr_type) + { + case FPRType::eFPRTypeFXSAVE: + return &m_fpr.xstate.fxsave; + case FPRType::eFPRTypeXSAVE: + return &m_iovec; + default: + return nullptr; + } +} + +size_t +NativeRegisterContextLinux_x86_64::GetFPRSize() +{ + const FPRType fpr_type = GetFPRType (); + switch (fpr_type) + { + case FPRType::eFPRTypeFXSAVE: + return sizeof(m_fpr.xstate.fxsave); + case FPRType::eFPRTypeXSAVE: + return sizeof(m_iovec); + default: + return 0; + } +} + +Error +NativeRegisterContextLinux_x86_64::ReadFPR () +{ + const FPRType fpr_type = GetFPRType (); + const lldb_private::ArchSpec& target_arch = GetRegisterInfoInterface().GetTargetArchitecture(); + switch (fpr_type) + { + case FPRType::eFPRTypeFXSAVE: + // For 32-bit inferiors on x86_32/x86_64 architectures, + // FXSAVE area can be read using PTRACE_GETREGSET ptrace api + // For 64-bit inferiors on x86_64 architectures, + // FXSAVE area can be read using PTRACE_GETFPREGS ptrace api + switch (target_arch.GetMachine ()) + { + case llvm::Triple::x86: + return ReadRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_PRXFPREG); + case llvm::Triple::x86_64: + return NativeRegisterContextLinux::ReadFPR(); + default: + assert(false && "Unhandled target architecture."); + break; + } + case FPRType::eFPRTypeXSAVE: + return ReadRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE); + default: + return Error("Unrecognized FPR type"); + } +} + +Error +NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint32_t wp_index, bool &is_hit) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + RegisterValue reg_value; + Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); + if (error.Fail()) + { + is_hit = false; + return error; + } + + uint64_t status_bits = reg_value.GetAsUInt64(); + + is_hit = status_bits & (1 << wp_index); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) + { + bool is_hit; + Error error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) { + wp_index = LLDB_INVALID_INDEX32; + return error; + } else if (is_hit) { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +Error +NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + RegisterValue reg_value; + Error error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); + if (error.Fail()) + { + is_vacant = false; + return error; + } + + uint64_t control_bits = reg_value.GetAsUInt64(); + + is_vacant = !(control_bits & (1 << (2 * wp_index))); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + // Read only watchpoints aren't supported on x86_64. Fall back to read/write waitchpoints instead. + // TODO: Add logic to detect when a write happens and ignore that watchpoint hit. + if (watch_flags == 0x2) + watch_flags = 0x3; + + if (watch_flags != 0x1 && watch_flags != 0x3) + return Error ("Invalid read/write bits for watchpoint"); + + if (size != 1 && size != 2 && size != 4 && size != 8) + return Error ("Invalid size for watchpoint"); + + bool is_vacant; + Error error = IsWatchpointVacant (wp_index, is_vacant); + if (error.Fail()) return error; + if (!is_vacant) return Error("Watchpoint index not vacant"); + + RegisterValue reg_value; + error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); + if (error.Fail()) return error; + + // for watchpoints 0, 1, 2, or 3, respectively, + // set bits 1, 3, 5, or 7 + uint64_t enable_bit = 1 << (2 * wp_index); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint64_t rw_bits = watch_flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + + control_bits |= enable_bit | rw_bits | size_bits; + + error = WriteRegisterRaw(m_reg_info.first_dr + wp_index, RegisterValue(addr)); + if (error.Fail()) return error; + + error = WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)); + if (error.Fail()) return error; + + error.Clear(); + return error; +} + +bool +NativeRegisterContextLinux_x86_64::ClearHardwareWatchpoint(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + RegisterValue reg_value; + + // for watchpoints 0, 1, 2, or 3, respectively, + // clear bits 0, 1, 2, or 3 of the debug status register (DR6) + Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); + if (error.Fail()) return false; + uint64_t bit_mask = 1 << wp_index; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegisterRaw(m_reg_info.first_dr + 6, RegisterValue(status_bits)); + if (error.Fail()) return false; + + // for watchpoints 0, 1, 2, or 3, respectively, + // clear bits {0-1,16-19}, {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} + // of the debug control register (DR7) + error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); + if (error.Fail()) return false; + bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)).Success(); +} + +Error +NativeRegisterContextLinux_x86_64::ClearAllHardwareWatchpoints() +{ + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); + if (error.Fail()) return error; + uint64_t bit_mask = 0xF; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegisterRaw(m_reg_info.first_dr + 6, RegisterValue(status_bits)); + if (error.Fail()) return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); + if (error.Fail()) return error; + bit_mask = 0xFF | (0xFFFF << 16); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)); +} + +uint32_t +NativeRegisterContextLinux_x86_64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) + { + bool is_vacant; + Error error = IsWatchpointVacant(wp_index, is_vacant); + if (is_vacant) + { + error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index); + if (error.Success()) + return wp_index; + } + if (error.Fail() && log) + { + log->Printf("NativeRegisterContextLinux_x86_64::%s Error: %s", + __FUNCTION__, error.AsCString()); + } + } + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextLinux_x86_64::GetWatchpointAddress(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + RegisterValue reg_value; + if (ReadRegisterRaw(m_reg_info.first_dr + wp_index, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + return reg_value.GetAsUInt64(); +} + +uint32_t +NativeRegisterContextLinux_x86_64::NumSupportedHardwareWatchpoints () +{ + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} + +#endif // defined(__i386__) || defined(__x86_64__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h new file mode 100644 index 000000000000..b04be4bb7688 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -0,0 +1,171 @@ +//===-- NativeRegisterContextLinux_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. +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#ifndef lldb_NativeRegisterContextLinux_x86_64_h +#define lldb_NativeRegisterContextLinux_x86_64_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeRegisterContextLinux_x86_64 : public NativeRegisterContextLinux + { + public: + NativeRegisterContextLinux_x86_64 (const ArchSpec& target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount () const override; + + const RegisterSet * + GetRegisterSet (uint32_t set_index) const override; + + uint32_t + GetUserRegisterCount() const override; + + Error + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + + Error + IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + Error + IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool + ClearHardwareWatchpoint(uint32_t wp_index) override; + + Error + ClearAllHardwareWatchpoints () override; + + Error + SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, uint32_t wp_index); + + uint32_t + SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t + GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t + NumSupportedHardwareWatchpoints() override; + + protected: + void* + GetGPRBuffer() override { return &m_gpr_x86_64; } + + void* + GetFPRBuffer() override; + + size_t + GetFPRSize() override; + + Error + ReadFPR() override; + + Error + WriteFPR() override; + + private: + + // Private member types. + enum FPRType + { + eFPRTypeNotValid = 0, + eFPRTypeFXSAVE, + eFPRTypeXSAVE + }; + + // Info about register ranges. + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + uint32_t num_avx_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + + uint32_t first_st; + uint32_t last_st; + uint32_t first_mm; + uint32_t last_mm; + uint32_t first_xmm; + uint32_t last_xmm; + uint32_t first_ymm; + uint32_t last_ymm; + + uint32_t first_dr; + uint32_t gpr_flags; + }; + + // Private member variables. + mutable FPRType m_fpr_type; + FPR m_fpr; + IOVEC m_iovec; + YMM m_ymm_set; + RegInfo m_reg_info; + uint64_t m_gpr_x86_64[k_num_gpr_registers_x86_64]; + uint32_t m_fctrl_offset_in_userarea; + + // Private member methods. + bool IsRegisterSetAvailable (uint32_t set_index) const; + + bool + IsGPR(uint32_t reg_index) const; + + FPRType + GetFPRType () const; + + bool + IsFPR(uint32_t reg_index) const; + + bool + IsFPR(uint32_t reg_index, FPRType fpr_type) const; + + bool + CopyXSTATEtoYMM (uint32_t reg_index, lldb::ByteOrder byte_order); + + bool + CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order); + + bool + IsAVX (uint32_t reg_index) const; + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_x86_64_h + +#endif // defined(__i386__) || defined(__x86_64__) diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/source/Plugins/Process/Linux/NativeThreadLinux.cpp new file mode 100644 index 000000000000..cbf82885e23a --- /dev/null +++ b/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -0,0 +1,440 @@ +//===-- NativeThreadLinux.cpp --------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeThreadLinux.h" + +#include +#include + +#include "NativeProcessLinux.h" +#include "NativeRegisterContextLinux.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/ADT/SmallString.h" + +#include "Plugins/Process/POSIX/CrashReason.h" + +#include +// Try to define a macro to encapsulate the tgkill syscall +#define tgkill(pid, tid, sig) \ + syscall(SYS_tgkill, static_cast< ::pid_t>(pid), static_cast< ::pid_t>(tid), sig) + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; + +namespace +{ + void LogThreadStopInfo (Log &log, const ThreadStopInfo &stop_info, const char *const header) + { + switch (stop_info.reason) + { + case eStopReasonNone: + log.Printf ("%s: %s no stop reason", __FUNCTION__, header); + return; + case eStopReasonTrace: + log.Printf ("%s: %s trace, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonBreakpoint: + log.Printf ("%s: %s breakpoint, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonWatchpoint: + log.Printf ("%s: %s watchpoint, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonSignal: + log.Printf ("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonException: + log.Printf ("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, stop_info.details.exception.type); + return; + case eStopReasonExec: + log.Printf ("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonPlanComplete: + log.Printf ("%s: %s plan complete", __FUNCTION__, header); + return; + case eStopReasonThreadExiting: + log.Printf ("%s: %s thread exiting", __FUNCTION__, header); + return; + case eStopReasonInstrumentation: + log.Printf ("%s: %s instrumentation", __FUNCTION__, header); + return; + default: + log.Printf ("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header, static_cast (stop_info.reason)); + } + } +} + +NativeThreadLinux::NativeThreadLinux (NativeProcessLinux *process, lldb::tid_t tid) : + NativeThreadProtocol (process, tid), + m_state (StateType::eStateInvalid), + m_stop_info (), + m_reg_context_sp (), + m_stop_description () +{ +} + +std::string +NativeThreadLinux::GetName() +{ + NativeProcessProtocolSP process_sp = m_process_wp.lock (); + if (!process_sp) + return ""; + + // const NativeProcessLinux *const process = reinterpret_cast (process_sp->get ()); + llvm::SmallString<32> thread_name; + HostNativeThread::GetName(GetID(), thread_name); + return thread_name.c_str(); +} + +lldb::StateType +NativeThreadLinux::GetState () +{ + return m_state; +} + + +bool +NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info, std::string& description) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + description.clear(); + + switch (m_state) + { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + if (log) + LogThreadStopInfo (*log, m_stop_info, "m_stop_info in thread:"); + stop_info = m_stop_info; + description = m_stop_description; + if (log) + LogThreadStopInfo (*log, stop_info, "returned stop_info:"); + + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + if (log) + { + log->Printf ("NativeThreadLinux::%s tid %" PRIu64 " in state %s cannot answer stop reason", + __FUNCTION__, GetID (), StateAsCString (m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +NativeRegisterContextSP +NativeThreadLinux::GetRegisterContext () +{ + // Return the register context if we already created it. + if (m_reg_context_sp) + return m_reg_context_sp; + + NativeProcessProtocolSP m_process_sp = m_process_wp.lock (); + if (!m_process_sp) + return NativeRegisterContextSP (); + + ArchSpec target_arch; + if (!m_process_sp->GetArchitecture (target_arch)) + return NativeRegisterContextSP (); + + const uint32_t concrete_frame_idx = 0; + m_reg_context_sp.reset (NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(target_arch, + *this, + concrete_frame_idx)); + + return m_reg_context_sp; +} + +Error +NativeThreadLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) +{ + if (!hardware) + return Error ("not implemented"); + if (m_state == eStateLaunching) + return Error (); + Error error = RemoveWatchpoint(addr); + if (error.Fail()) return error; + NativeRegisterContextSP reg_ctx = GetRegisterContext (); + uint32_t wp_index = + reg_ctx->SetHardwareWatchpoint (addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Error ("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Error (); +} + +Error +NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr) +{ + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Error (); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) + return Error (); + return Error ("Clearing hardware watchpoint failed."); +} + +void +NativeThreadLinux::SetRunning () +{ + const StateType new_state = StateType::eStateRunning; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_description.clear(); + + // If watchpoints have been set, but none on this thread, + // then this is a new thread. So set all existing watchpoints. + if (m_watchpoint_index_map.empty()) + { + const auto process_sp = GetProcess(); + if (process_sp) + { + const auto &watchpoint_map = process_sp->GetWatchpointMap(); + if (watchpoint_map.empty()) return; + GetRegisterContext()->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) + { + const auto& wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); + } + } + } +} + +void +NativeThreadLinux::SetStepping () +{ + const StateType new_state = StateType::eStateStepping; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; +} + +void +NativeThreadLinux::SetStoppedBySignal(uint32_t signo, const siginfo_t *info) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeThreadLinux::%s called with signal 0x%02" PRIx32, __FUNCTION__, signo); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.details.signal.signo = signo; + + m_stop_description.clear(); + if (info) + { + switch (signo) + { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + //In case of MIPS64 target, SI_KERNEL is generated for invalid 64bit address. + const auto reason = (info->si_signo == SIGBUS && info->si_code == SI_KERNEL) ? + CrashReason::eInvalidAddress : GetCrashReason(*info); + m_stop_description = GetCrashReasonString(reason, reinterpret_cast(info->si_addr)); + break; + } + } +} + +bool +NativeThreadLinux::IsStopped (int *signo) +{ + if (!StateIsStoppedState (m_state, false)) + return false; + + // If we are stopped by a signal, return the signo. + if (signo && + m_state == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonSignal) + { + *signo = m_stop_info.details.signal.signo; + } + + // Regardless, we are stopped. + return true; +} + + +void +NativeThreadLinux::SetStoppedByExec () +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeThreadLinux::%s()", __FUNCTION__); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.details.signal.signo = SIGSTOP; +} + +void +NativeThreadLinux::SetStoppedByBreakpoint () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.details.signal.signo = SIGTRAP; + m_stop_description.clear(); +} + +void +NativeThreadLinux::SetStoppedByWatchpoint (uint32_t wp_index) +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + m_stop_description.clear (); + + lldbassert(wp_index != LLDB_INVALID_INDEX32 && + "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " "; + ostr << wp_index; + + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at 'm', then + * watch exception is generated even when 'n' is read/written. To handle this case, + * find the base address of the load/store instruction and append it in the stop-info + * packet. + */ + ostr << " " << GetRegisterContext()->GetWatchpointHitAddress(wp_index); + + m_stop_description = ostr.str(); + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.details.signal.signo = SIGTRAP; +} + +bool +NativeThreadLinux::IsStoppedAtBreakpoint () +{ + return GetState () == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonBreakpoint; +} + +bool +NativeThreadLinux::IsStoppedAtWatchpoint () +{ + return GetState () == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonWatchpoint; +} + +void +NativeThreadLinux::SetStoppedByTrace () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.details.signal.signo = SIGTRAP; +} + +void +NativeThreadLinux::SetStoppedWithNoReason () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.details.signal.signo = 0; +} + +void +NativeThreadLinux::SetExited () +{ + const StateType new_state = StateType::eStateExited; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonThreadExiting; +} + +Error +NativeThreadLinux::RequestStop () +{ + Log* log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + const auto process_sp = GetProcess(); + if (! process_sp) + return Error("Process is null."); + + lldb::pid_t pid = process_sp->GetID(); + lldb::tid_t tid = GetID(); + + if (log) + log->Printf ("NativeThreadLinux::%s requesting thread stop(pid: %" PRIu64 ", tid: %" PRIu64 ")", __FUNCTION__, pid, tid); + + Error err; + errno = 0; + if (::tgkill (pid, tid, SIGSTOP) != 0) + { + err.SetErrorToErrno (); + if (log) + log->Printf ("NativeThreadLinux::%s tgkill(%" PRIu64 ", %" PRIu64 ", SIGSTOP) failed: %s", __FUNCTION__, pid, tid, err.AsCString ()); + } + + return err; +} + +void +NativeThreadLinux::MaybeLogStateChange (lldb::StateType new_state) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + // If we're not logging, we're done. + if (!log) + return; + + // If this is a state change to the same state, we're done. + lldb::StateType old_state = m_state; + if (new_state == old_state) + return; + + NativeProcessProtocolSP m_process_sp = m_process_wp.lock (); + lldb::pid_t pid = m_process_sp ? m_process_sp->GetID () : LLDB_INVALID_PROCESS_ID; + + // Log it. + log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") changing from state %s to %s", pid, GetID (), StateAsCString (old_state), StateAsCString (new_state)); +} diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.h b/source/Plugins/Process/Linux/NativeThreadLinux.h new file mode 100644 index 000000000000..bf6b00a78cfd --- /dev/null +++ b/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -0,0 +1,120 @@ +//===-- NativeThreadLinux.h ----------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeThreadLinux_H_ +#define liblldb_NativeThreadLinux_H_ + +#include "lldb/lldb-private-forward.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include +#include +#include + +namespace lldb_private { +namespace process_linux { + + class NativeProcessLinux; + + class NativeThreadLinux : public NativeThreadProtocol + { + friend class NativeProcessLinux; + + public: + NativeThreadLinux (NativeProcessLinux *process, lldb::tid_t tid); + + // --------------------------------------------------------------------- + // NativeThreadProtocol Interface + // --------------------------------------------------------------------- + std::string + GetName() override; + + lldb::StateType + GetState () override; + + bool + GetStopReason (ThreadStopInfo &stop_info, std::string& description) override; + + NativeRegisterContextSP + GetRegisterContext () override; + + Error + SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override; + + Error + RemoveWatchpoint (lldb::addr_t addr) override; + + private: + // --------------------------------------------------------------------- + // Interface for friend classes + // --------------------------------------------------------------------- + void + SetRunning (); + + void + SetStepping (); + + void + SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); + + /// Return true if the thread is stopped. + /// If stopped by a signal, indicate the signo in the signo argument. + /// Otherwise, return LLDB_INVALID_SIGNAL_NUMBER. + bool + IsStopped (int *signo); + + void + SetStoppedByExec (); + + void + SetStoppedByBreakpoint (); + + void + SetStoppedByWatchpoint (uint32_t wp_index); + + bool + IsStoppedAtBreakpoint (); + + bool + IsStoppedAtWatchpoint (); + + void + SetStoppedByTrace (); + + void + SetStoppedWithNoReason (); + + void + SetExited (); + + Error + RequestStop (); + + // --------------------------------------------------------------------- + // Private interface + // --------------------------------------------------------------------- + void + MaybeLogStateChange (lldb::StateType new_state); + + // --------------------------------------------------------------------- + // Member Variables + // --------------------------------------------------------------------- + lldb::StateType m_state; + ThreadStopInfo m_stop_info; + NativeRegisterContextSP m_reg_context_sp; + std::string m_stop_description; + using WatchpointIndexMap = std::map; + WatchpointIndexMap m_watchpoint_index_map; + }; + + typedef std::shared_ptr NativeThreadLinuxSP; +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadLinux_H_ diff --git a/source/Plugins/Process/Linux/ProcFileReader.cpp b/source/Plugins/Process/Linux/ProcFileReader.cpp new file mode 100644 index 000000000000..4d1f231f4f9b --- /dev/null +++ b/source/Plugins/Process/Linux/ProcFileReader.cpp @@ -0,0 +1,108 @@ +//===-- ProcFileReader.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Process/Linux/ProcFileReader.h" + +// C Headers +#include +#include +#include +#include +#include + +// C++ Headers +#include + +// LLDB Headers +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +lldb::DataBufferSP +ProcFileReader::ReadIntoDataBuffer (lldb::pid_t pid, const char *name) +{ + int fd; + char path[PATH_MAX]; + + // Make sure we've got a nil terminated buffer for all the folks calling + // GetBytes() directly off our returned DataBufferSP if we hit an error. + lldb::DataBufferSP buf_sp (new DataBufferHeap(1, 0)); + + // Ideally, we would simply create a FileSpec and call ReadFileContents. + // However, files in procfs have zero size (since they are, in general, + // dynamically generated by the kernel) which is incompatible with the + // current ReadFileContents implementation. Therefore we simply stream the + // data into a DataBuffer ourselves. + if (snprintf (path, PATH_MAX, "/proc/%" PRIu64 "/%s", pid, name) > 0) + { + if ((fd = open (path, O_RDONLY, 0)) >= 0) + { + size_t bytes_read = 0; + std::unique_ptr buf_ap(new DataBufferHeap(1024, 0)); + + for (;;) + { + size_t avail = buf_ap->GetByteSize() - bytes_read; + ssize_t status = read (fd, buf_ap->GetBytes() + bytes_read, avail); + + if (status < 0) + break; + + if (status == 0) + { + buf_ap->SetByteSize (bytes_read); + buf_sp.reset (buf_ap.release()); + break; + } + + bytes_read += status; + + if (avail - status == 0) + buf_ap->SetByteSize (2 * buf_ap->GetByteSize()); + } + + close (fd); + } + } + + return buf_sp; +} + +Error +ProcFileReader::ProcessLineByLine (lldb::pid_t pid, const char *name, std::function line_parser) +{ + Error error; + + // Try to open the /proc/{pid}/maps entry. + char filename [PATH_MAX]; + snprintf (filename, sizeof(filename), "/proc/%" PRIu64 "/%s", pid, name); + filename[sizeof (filename) - 1] = '\0'; + + std::ifstream proc_file (filename); + if (proc_file.fail ()) + { + error.SetErrorStringWithFormat ("failed to open file '%s'", filename); + return error; + } + + // Read the file line by line, processing until either end of file or when the line_parser returns false. + std::string line; + bool should_continue = true; + + while (should_continue && std::getline (proc_file, line)) + { + // Pass the line over to the line_parser for processing. If the line_parser returns false, we + // stop processing. + should_continue = line_parser (line); + } + + return error; +} diff --git a/source/Plugins/Process/Linux/ProcFileReader.h b/source/Plugins/Process/Linux/ProcFileReader.h new file mode 100644 index 000000000000..7b3812433068 --- /dev/null +++ b/source/Plugins/Process/Linux/ProcFileReader.h @@ -0,0 +1,37 @@ +//===-- ProcFileReader.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcFileReader_h_ +#define liblldb_ProcFileReader_h_ + +#include + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +namespace process_linux { + + class ProcFileReader + { + public: + + static lldb::DataBufferSP + ReadIntoDataBuffer (lldb::pid_t pid, const char *name); + + /// Parse the /proc/{@a pid}/{@a name} file line by line, passing each line to line_parser, until + /// either end of file or until line_parser returns false. + static Error + ProcessLineByLine (lldb::pid_t pid, const char *name, std::function line_parser); + }; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef liblldb_ProcFileReader_h_ diff --git a/source/Plugins/Process/Linux/Procfs.h b/source/Plugins/Process/Linux/Procfs.h new file mode 100644 index 000000000000..cad433fb095d --- /dev/null +++ b/source/Plugins/Process/Linux/Procfs.h @@ -0,0 +1,31 @@ +//===-- Procfs.h ---------------------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// source/Plugins/Process/Linux/Procfs.h defines the symbols we need from +// sys/procfs.h on Android/Linux for all supported architectures. + +#include + +#ifdef __ANDROID__ +#if defined (__arm64__) || defined (__aarch64__) +typedef unsigned long elf_greg_t; +typedef elf_greg_t elf_gregset_t[(sizeof (struct user_pt_regs) / sizeof(elf_greg_t))]; +typedef struct user_fpsimd_state elf_fpregset_t; +#ifndef NT_FPREGSET + #define NT_FPREGSET NT_PRFPREG +#endif // NT_FPREGSET +#elif defined (__mips__) +#ifndef NT_FPREGSET + #define NT_FPREGSET NT_PRFPREG +#endif // NT_FPREGSET +#endif +#else // __ANDROID__ +#include +#endif // __ANDROID__ + diff --git a/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt b/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt new file mode 100644 index 000000000000..681b7405e2b8 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginProcessMacOSXKernel + CommunicationKDP.cpp + ProcessKDP.cpp + ProcessKDPLog.cpp + RegisterContextKDP_arm.cpp + RegisterContextKDP_arm64.cpp + RegisterContextKDP_i386.cpp + RegisterContextKDP_x86_64.cpp + ThreadKDP.cpp + ) diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp new file mode 100644 index 000000000000..5c1c3284a35c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp @@ -0,0 +1,1445 @@ +//===-- CommunicationKDP.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "CommunicationKDP.h" + +// C Includes +#include +#include +#include + +// C++ Includes + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Target/Process.h" + +// Project includes +#include "ProcessKDPLog.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// CommunicationKDP constructor +//---------------------------------------------------------------------- +CommunicationKDP::CommunicationKDP (const char *comm_name) : + Communication(comm_name), + m_addr_byte_size (4), + m_byte_order (eByteOrderLittle), + m_packet_timeout (5), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_is_running (false), + m_session_key (0u), + m_request_sequence_id (0u), + m_exception_sequence_id (0u), + m_kdp_version_version (0u), + m_kdp_version_feature (0u), + m_kdp_hostinfo_cpu_mask (0u), + m_kdp_hostinfo_cpu_type (0u), + m_kdp_hostinfo_cpu_subtype (0u) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommunicationKDP::~CommunicationKDP() +{ + if (IsConnected()) + { + Disconnect(); + } +} + +bool +CommunicationKDP::SendRequestPacket (const PacketStreamType &request_packet) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendRequestPacketNoLock (request_packet); +} + +#if 0 +typedef struct { + uint8_t request; // Either: CommandType | ePacketTypeRequest, or CommandType | ePacketTypeReply + uint8_t sequence; + uint16_t length; // Length of entire packet including this header + uint32_t key; // Session key +} kdp_hdr_t; +#endif + +void +CommunicationKDP::MakeRequestPacketHeader (CommandType request_type, + PacketStreamType &request_packet, + uint16_t request_length) +{ + request_packet.Clear(); + request_packet.PutHex8 (request_type | ePacketTypeRequest); // Set the request type + request_packet.PutHex8 (m_request_sequence_id++); // Sequence number + request_packet.PutHex16 (request_length); // Length of the packet including this header + request_packet.PutHex32 (m_session_key); // Session key +} + +bool +CommunicationKDP::SendRequestAndGetReply (const CommandType command, + const PacketStreamType &request_packet, + DataExtractor &reply_packet) +{ + if (IsRunning()) + { + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, request_packet.GetData(), request_packet.GetSize()); + log->Printf("error: kdp running, not sending packet: %.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + return false; + } + + Mutex::Locker locker(m_sequence_mutex); +#ifdef LLDB_CONFIGURATION_DEBUG + // NOTE: this only works for packets that are in native endian byte order + assert (request_packet.GetSize() == *((uint16_t *)(request_packet.GetData() + 2))); +#endif + lldb::offset_t offset = 1; + const uint32_t num_retries = 3; + for (uint32_t i=0; i request_sequence_id) + { + // Sequence ID was greater than the sequence ID of the packet we sent, something + // is really wrong... + reply_packet.Clear(); + return false; + } + else + { + // The reply sequence ID was less than our current packet's sequence ID + // so we should keep trying to get a response because this was a response + // for a previous packet that we must have retried. + } + } + else + { + // Break and retry sending the packet as we didn't get a response due to timeout + break; + } + } + } + } + reply_packet.Clear(); + return false; +} + +bool +CommunicationKDP::SendRequestPacketNoLock (const PacketStreamType &request_packet) +{ + if (IsConnected()) + { + const char *packet_data = request_packet.GetData(); + const size_t packet_size = request_packet.GetSize(); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, packet_data, packet_size); + log->Printf("%.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + ConnectionStatus status = eConnectionStatusSuccess; + + size_t bytes_written = Write (packet_data, + packet_size, + status, + NULL); + + if (bytes_written == packet_size) + return true; + + if (log) + log->Printf ("error: failed to send packet entire packet %" PRIu64 " of %" PRIu64 " bytes sent", (uint64_t)bytes_written, (uint64_t)packet_size); + } + return false; +} + +bool +CommunicationKDP::GetSequenceMutex (Mutex::Locker& locker) +{ + return locker.TryLock (m_sequence_mutex); +} + + +bool +CommunicationKDP::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) +{ + return m_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSeconds (DataExtractor &packet, uint32_t timeout_usec) +{ + Mutex::Locker locker(m_sequence_mutex); + return WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); +} + +size_t +CommunicationKDP::WaitForPacketWithTimeoutMicroSecondsNoLock (DataExtractor &packet, uint32_t timeout_usec) +{ + uint8_t buffer[8192]; + Error error; + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS | KDP_LOG_VERBOSE)); + + // Check for a packet from our cache first without trying any reading... + if (CheckForPacket (NULL, 0, packet)) + return packet.GetByteSize(); + + bool timed_out = false; + while (IsConnected() && !timed_out) + { + lldb::ConnectionStatus status = eConnectionStatusNoConnection; + size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error); + + if (log) + log->Printf ("%s: Read (buffer, (sizeof(buffer), timeout_usec = 0x%x, status = %s, error = %s) => bytes_read = %" PRIu64, + __PRETTY_FUNCTION__, + timeout_usec, + Communication::ConnectionStatusAsCString (status), + error.AsCString(), + (uint64_t)bytes_read); + + if (bytes_read > 0) + { + if (CheckForPacket (buffer, bytes_read, packet)) + return packet.GetByteSize(); + } + else + { + switch (status) + { + case eConnectionStatusInterrupted: + case eConnectionStatusTimedOut: + timed_out = true; + break; + case eConnectionStatusSuccess: + //printf ("status = success but error = %s\n", error.AsCString("")); + break; + + case eConnectionStatusEndOfFile: + case eConnectionStatusNoConnection: + case eConnectionStatusLostConnection: + case eConnectionStatusError: + Disconnect(); + break; + } + } + } + packet.Clear (); + return 0; +} + +bool +CommunicationKDP::CheckForPacket (const uint8_t *src, size_t src_len, DataExtractor &packet) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); + + if (src && src_len > 0) + { + if (log && log->GetVerbose()) + { + PacketStreamType log_strm; + DataExtractor::DumpHexBytes (&log_strm, src, src_len, UINT32_MAX, LLDB_INVALID_ADDRESS); + log->Printf ("CommunicationKDP::%s adding %u bytes: %s", + __FUNCTION__, + (uint32_t)src_len, + log_strm.GetData()); + } + m_bytes.append ((const char *)src, src_len); + } + + // Make sure we at least have enough bytes for a packet header + const size_t bytes_available = m_bytes.size(); + if (bytes_available >= 8) + { + packet.SetData (&m_bytes[0], bytes_available, m_byte_order); + lldb::offset_t offset = 0; + uint8_t reply_command = packet.GetU8(&offset); + switch (reply_command) + { + case ePacketTypeRequest | KDP_EXCEPTION: + case ePacketTypeRequest | KDP_TERMINATION: + // We got an exception request, so be sure to send an ACK + { + PacketStreamType request_ack_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + // Set the reply but and make the ACK packet + request_ack_packet.PutHex8 (reply_command | ePacketTypeReply); + request_ack_packet.PutHex8 (packet.GetU8(&offset)); + request_ack_packet.PutHex16 (packet.GetU16(&offset)); + request_ack_packet.PutHex32 (packet.GetU32(&offset)); + m_is_running.SetValue(false, eBroadcastAlways); + // Ack to the exception or termination + SendRequestPacketNoLock (request_ack_packet); + } + // Fall through to case below to get packet contents + case ePacketTypeReply | KDP_CONNECT: + case ePacketTypeReply | KDP_DISCONNECT: + case ePacketTypeReply | KDP_HOSTINFO: + case ePacketTypeReply | KDP_VERSION: + case ePacketTypeReply | KDP_MAXBYTES: + case ePacketTypeReply | KDP_READMEM: + case ePacketTypeReply | KDP_WRITEMEM: + case ePacketTypeReply | KDP_READREGS: + case ePacketTypeReply | KDP_WRITEREGS: + case ePacketTypeReply | KDP_LOAD: + case ePacketTypeReply | KDP_IMAGEPATH: + case ePacketTypeReply | KDP_SUSPEND: + case ePacketTypeReply | KDP_RESUMECPUS: + case ePacketTypeReply | KDP_BREAKPOINT_SET: + case ePacketTypeReply | KDP_BREAKPOINT_REMOVE: + case ePacketTypeReply | KDP_REGIONS: + case ePacketTypeReply | KDP_REATTACH: + case ePacketTypeReply | KDP_HOSTREBOOT: + case ePacketTypeReply | KDP_READMEM64: + case ePacketTypeReply | KDP_WRITEMEM64: + case ePacketTypeReply | KDP_BREAKPOINT_SET64: + case ePacketTypeReply | KDP_BREAKPOINT_REMOVE64: + case ePacketTypeReply | KDP_KERNELVERSION: + case ePacketTypeReply | KDP_READPHYSMEM64: + case ePacketTypeReply | KDP_WRITEPHYSMEM64: + case ePacketTypeReply | KDP_READIOPORT: + case ePacketTypeReply | KDP_WRITEIOPORT: + case ePacketTypeReply | KDP_READMSR64: + case ePacketTypeReply | KDP_WRITEMSR64: + case ePacketTypeReply | KDP_DUMPINFO: + { + offset = 2; + const uint16_t length = packet.GetU16 (&offset); + if (length <= bytes_available) + { + // We have an entire packet ready, we need to copy the data + // bytes into a buffer that will be owned by the packet and + // erase the bytes from our communcation buffer "m_bytes" + packet.SetData (DataBufferSP (new DataBufferHeap (&m_bytes[0], length))); + m_bytes.erase (0, length); + + if (log) + { + PacketStreamType log_strm; + DumpPacket (log_strm, packet); + + log->Printf("%.*s", (uint32_t)log_strm.GetSize(), log_strm.GetData()); + } + return true; + } + } + break; + + default: + // Unrecognized reply command byte, erase this byte and try to get back on track + if (log) + log->Printf ("CommunicationKDP::%s: tossing junk byte: 0x%2.2x", + __FUNCTION__, + (uint8_t)m_bytes[0]); + m_bytes.erase(0, 1); + break; + } + } + packet.Clear(); + return false; +} + + +bool +CommunicationKDP::SendRequestConnect (uint16_t reply_port, + uint16_t exc_port, + const char *greeting) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + if (greeting == NULL) + greeting = ""; + + const CommandType command = KDP_CONNECT; + // Length is 82 uint16_t and the length of the greeting C string with the terminating NULL + const uint32_t command_length = 8 + 2 + 2 + ::strlen(greeting) + 1; + MakeRequestPacketHeader (command, request_packet, command_length); + // Always send connect ports as little endian + request_packet.SetByteOrder (eByteOrderLittle); + request_packet.PutHex16 (htons(reply_port)); + request_packet.PutHex16 (htons(exc_port)); + request_packet.SetByteOrder (m_byte_order); + request_packet.PutCString (greeting); + DataExtractor reply_packet; + return SendRequestAndGetReply (command, request_packet, reply_packet); +} + +void +CommunicationKDP::ClearKDPSettings () +{ + m_request_sequence_id = 0; + m_kdp_version_version = 0; + m_kdp_version_feature = 0; + m_kdp_hostinfo_cpu_mask = 0; + m_kdp_hostinfo_cpu_type = 0; + m_kdp_hostinfo_cpu_subtype = 0; +} + +bool +CommunicationKDP::SendRequestReattach (uint16_t reply_port) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_REATTACH; + // Length is 8 bytes for the header plus 2 bytes for the reply UDP port + const uint32_t command_length = 8 + 2; + MakeRequestPacketHeader (command, request_packet, command_length); + // Always send connect ports as little endian + request_packet.SetByteOrder (eByteOrderLittle); + request_packet.PutHex16(htons(reply_port)); + request_packet.SetByteOrder (m_byte_order); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + // Reset the sequence ID to zero for reattach + ClearKDPSettings (); + lldb::offset_t offset = 4; + m_session_key = reply_packet.GetU32 (&offset); + return true; + } + return false; +} + +uint32_t +CommunicationKDP::GetVersion () +{ + if (!VersionIsValid()) + SendRequestVersion(); + return m_kdp_version_version; +} + +uint32_t +CommunicationKDP::GetFeatureFlags () +{ + if (!VersionIsValid()) + SendRequestVersion(); + return m_kdp_version_feature; +} + +bool +CommunicationKDP::SendRequestVersion () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_VERSION; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + m_kdp_version_version = reply_packet.GetU32 (&offset); + m_kdp_version_feature = reply_packet.GetU32 (&offset); + return true; + } + return false; +} + +#if 0 // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... +const char * +CommunicationKDP::GetImagePath () +{ + if (m_image_path.empty()) + SendRequestImagePath(); + return m_image_path.c_str(); +} + +bool +CommunicationKDP::SendRequestImagePath () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_IMAGEPATH; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + const char *path = reply_packet.PeekCStr(8); + if (path && path[0]) + m_kernel_version.assign (path); + return true; + } + return false; +} +#endif + +uint32_t +CommunicationKDP::GetCPUMask () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_mask; +} + +uint32_t +CommunicationKDP::GetCPUType () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_type; +} + +uint32_t +CommunicationKDP::GetCPUSubtype () +{ + if (!HostInfoIsValid()) + SendRequestHostInfo(); + return m_kdp_hostinfo_cpu_subtype; +} + +lldb_private::UUID +CommunicationKDP::GetUUID () +{ + UUID uuid; + if (GetKernelVersion() == NULL) + return uuid; + + if (m_kernel_version.find("UUID=") == std::string::npos) + return uuid; + + size_t p = m_kernel_version.find("UUID=") + strlen ("UUID="); + std::string uuid_str = m_kernel_version.substr(p, 36); + if (uuid_str.size() < 32) + return uuid; + + if (uuid.SetFromCString (uuid_str.c_str()) == 0) + { + UUID invalid_uuid; + return invalid_uuid; + } + + return uuid; +} + +bool +CommunicationKDP::RemoteIsEFI () +{ + if (GetKernelVersion() == NULL) + return false; + if (strncmp (m_kernel_version.c_str(), "EFI", 3) == 0) + return true; + else + return false; +} + +bool +CommunicationKDP::RemoteIsDarwinKernel () +{ + if (GetKernelVersion() == NULL) + return false; + if (m_kernel_version.find("Darwin Kernel") != std::string::npos) + return true; + else + return false; +} + +lldb::addr_t +CommunicationKDP::GetLoadAddress () +{ + if (GetKernelVersion() == NULL) + return LLDB_INVALID_ADDRESS; + + if (m_kernel_version.find("stext=") == std::string::npos) + return LLDB_INVALID_ADDRESS; + size_t p = m_kernel_version.find("stext=") + strlen ("stext="); + if (m_kernel_version[p] != '0' || m_kernel_version[p + 1] != 'x') + return LLDB_INVALID_ADDRESS; + + addr_t kernel_load_address; + errno = 0; + kernel_load_address = ::strtoul (m_kernel_version.c_str() + p, NULL, 16); + if (errno != 0 || kernel_load_address == 0) + return LLDB_INVALID_ADDRESS; + + return kernel_load_address; +} + +bool +CommunicationKDP::SendRequestHostInfo () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_HOSTINFO; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + m_kdp_hostinfo_cpu_mask = reply_packet.GetU32 (&offset); + m_kdp_hostinfo_cpu_type = reply_packet.GetU32 (&offset); + m_kdp_hostinfo_cpu_subtype = reply_packet.GetU32 (&offset); + + ArchSpec kernel_arch; + kernel_arch.SetArchitecture (eArchTypeMachO, + m_kdp_hostinfo_cpu_type, + m_kdp_hostinfo_cpu_subtype); + + m_addr_byte_size = kernel_arch.GetAddressByteSize(); + m_byte_order = kernel_arch.GetByteOrder(); + return true; + } + return false; +} + +const char * +CommunicationKDP::GetKernelVersion () +{ + if (m_kernel_version.empty()) + SendRequestKernelVersion (); + return m_kernel_version.c_str(); +} + +bool +CommunicationKDP::SendRequestKernelVersion () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_KERNELVERSION; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + const char *kernel_version_cstr = reply_packet.PeekCStr(8); + if (kernel_version_cstr && kernel_version_cstr[0]) + m_kernel_version.assign (kernel_version_cstr); + return true; + } + return false; +} + +bool +CommunicationKDP::SendRequestDisconnect () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_DISCONNECT; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + // Are we supposed to get a reply for disconnect? + } + ClearKDPSettings (); + return true; +} + +uint32_t +CommunicationKDP::SendRequestReadMemory (lldb::addr_t addr, + void *dst, + uint32_t dst_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = use_64 ? KDP_READMEM64 : KDP_READMEM; + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + command_addr_byte_size + 4; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + request_packet.PutHex32 (dst_len); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + uint32_t src_len = reply_packet.GetByteSize() - 12; + + if (src_len > 0) + { + const void *src = reply_packet.GetData(&offset, src_len); + if (src) + { + ::memcpy (dst, src, src_len); + error.Clear(); + return src_len; + } + } + if (kdp_error) + error.SetErrorStringWithFormat ("kdp read memory failed (error %u)", kdp_error); + else + error.SetErrorString ("kdp read memory failed"); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + + +uint32_t +CommunicationKDP::SendRequestWriteMemory (lldb::addr_t addr, + const void *src, + uint32_t src_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = use_64 ? KDP_WRITEMEM64 : KDP_WRITEMEM; + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + command_addr_byte_size + 4 + src_len; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + request_packet.PutHex32 (src_len); + request_packet.PutRawBytes(src, src_len); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error) + error.SetErrorStringWithFormat ("kdp write memory failed (error %u)", kdp_error); + else + { + error.Clear(); + return src_len; + } + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + +bool +CommunicationKDP::SendRawRequest (uint8_t command_byte, + const void *src, // Raw packet payload bytes + uint32_t src_len, // Raw packet payload length + DataExtractor &reply_packet, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + // Size is header + address size + uint32_t length + const uint32_t command_length = 8 + src_len; + const CommandType command = (CommandType)command_byte; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutRawBytes(src, src_len); + + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error && (command_byte != KDP_DUMPINFO)) + error.SetErrorStringWithFormat ("request packet 0x%8.8x failed (error %u)", command_byte, kdp_error); + else + { + error.Clear(); + return true; + } + } + else + { + error.SetErrorString ("failed to send packet"); + } + return false; +} + + +const char * +CommunicationKDP::GetCommandAsCString (uint8_t command) +{ + switch (command) + { + case KDP_CONNECT: return "KDP_CONNECT"; + case KDP_DISCONNECT: return "KDP_DISCONNECT"; + case KDP_HOSTINFO: return "KDP_HOSTINFO"; + case KDP_VERSION: return "KDP_VERSION"; + case KDP_MAXBYTES: return "KDP_MAXBYTES"; + case KDP_READMEM: return "KDP_READMEM"; + case KDP_WRITEMEM: return "KDP_WRITEMEM"; + case KDP_READREGS: return "KDP_READREGS"; + case KDP_WRITEREGS: return "KDP_WRITEREGS"; + case KDP_LOAD: return "KDP_LOAD"; + case KDP_IMAGEPATH: return "KDP_IMAGEPATH"; + case KDP_SUSPEND: return "KDP_SUSPEND"; + case KDP_RESUMECPUS: return "KDP_RESUMECPUS"; + case KDP_EXCEPTION: return "KDP_EXCEPTION"; + case KDP_TERMINATION: return "KDP_TERMINATION"; + case KDP_BREAKPOINT_SET: return "KDP_BREAKPOINT_SET"; + case KDP_BREAKPOINT_REMOVE: return "KDP_BREAKPOINT_REMOVE"; + case KDP_REGIONS: return "KDP_REGIONS"; + case KDP_REATTACH: return "KDP_REATTACH"; + case KDP_HOSTREBOOT: return "KDP_HOSTREBOOT"; + case KDP_READMEM64: return "KDP_READMEM64"; + case KDP_WRITEMEM64: return "KDP_WRITEMEM64"; + case KDP_BREAKPOINT_SET64: return "KDP_BREAKPOINT64_SET"; + case KDP_BREAKPOINT_REMOVE64: return "KDP_BREAKPOINT64_REMOVE"; + case KDP_KERNELVERSION: return "KDP_KERNELVERSION"; + case KDP_READPHYSMEM64: return "KDP_READPHYSMEM64"; + case KDP_WRITEPHYSMEM64: return "KDP_WRITEPHYSMEM64"; + case KDP_READIOPORT: return "KDP_READIOPORT"; + case KDP_WRITEIOPORT: return "KDP_WRITEIOPORT"; + case KDP_READMSR64: return "KDP_READMSR64"; + case KDP_WRITEMSR64: return "KDP_WRITEMSR64"; + case KDP_DUMPINFO: return "KDP_DUMPINFO"; + } + return NULL; +} + +void +CommunicationKDP::DumpPacket (Stream &s, const void *data, uint32_t data_len) +{ + DataExtractor extractor (data, data_len, m_byte_order, m_addr_byte_size); + DumpPacket (s, extractor); +} + +void +CommunicationKDP::DumpPacket (Stream &s, const DataExtractor& packet) +{ + const char *error_desc = NULL; + if (packet.GetByteSize() < 8) + { + error_desc = "error: invalid packet (too short): "; + } + else + { + lldb::offset_t offset = 0; + const uint8_t first_packet_byte = packet.GetU8 (&offset); + const uint8_t sequence_id = packet.GetU8 (&offset); + const uint16_t length = packet.GetU16 (&offset); + const uint32_t key = packet.GetU32 (&offset); + const CommandType command = ExtractCommand (first_packet_byte); + const char *command_name = GetCommandAsCString (command); + if (command_name) + { + const bool is_reply = ExtractIsReply(first_packet_byte); + s.Printf ("(running=%i) %s %24s: 0x%2.2x 0x%2.2x 0x%4.4x 0x%8.8x ", + IsRunning(), + is_reply ? "<--" : "-->", + command_name, + first_packet_byte, + sequence_id, + length, + key); + + if (is_reply) + { + // Dump request reply packets + switch (command) + { + // Commands that return a single 32 bit error + case KDP_CONNECT: + case KDP_WRITEMEM: + case KDP_WRITEMEM64: + case KDP_BREAKPOINT_SET: + case KDP_BREAKPOINT_REMOVE: + case KDP_BREAKPOINT_SET64: + case KDP_BREAKPOINT_REMOVE64: + case KDP_WRITEREGS: + case KDP_LOAD: + case KDP_WRITEIOPORT: + case KDP_WRITEMSR64: + { + const uint32_t error = packet.GetU32 (&offset); + s.Printf(" (error=0x%8.8x)", error); + } + break; + + case KDP_DISCONNECT: + case KDP_REATTACH: + case KDP_HOSTREBOOT: + case KDP_SUSPEND: + case KDP_RESUMECPUS: + case KDP_EXCEPTION: + case KDP_TERMINATION: + // No return value for the reply, just the header to ack + s.PutCString(" ()"); + break; + + case KDP_HOSTINFO: + { + const uint32_t cpu_mask = packet.GetU32 (&offset); + const uint32_t cpu_type = packet.GetU32 (&offset); + const uint32_t cpu_subtype = packet.GetU32 (&offset); + s.Printf(" (cpu_mask=0x%8.8x, cpu_type=0x%8.8x, cpu_subtype=0x%8.8x)", cpu_mask, cpu_type, cpu_subtype); + } + break; + + case KDP_VERSION: + { + const uint32_t version = packet.GetU32 (&offset); + const uint32_t feature = packet.GetU32 (&offset); + s.Printf(" (version=0x%8.8x, feature=0x%8.8x)", version, feature); + } + break; + + case KDP_REGIONS: + { + const uint32_t region_count = packet.GetU32 (&offset); + s.Printf(" (count = %u", region_count); + for (uint32_t i=0; i 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatBytesWithASCII, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + m_last_read_memory_addr, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_READREGS: + { + const uint32_t error = packet.GetU32 (&offset); + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (error = 0x%8.8x regs:\n", error); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + m_addr_byte_size, // Size of each item in bytes + count / m_addr_byte_size, // Number of items + 16 / m_addr_byte_size, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_KERNELVERSION: + { + const char *kernel_version = packet.PeekCStr(8); + s.Printf(" (version = \"%s\")", kernel_version); + } + break; + + case KDP_MAXBYTES: + { + const uint32_t max_bytes = packet.GetU32 (&offset); + s.Printf(" (max_bytes = 0x%8.8x (%u))", max_bytes, max_bytes); + } + break; + case KDP_IMAGEPATH: + { + const char *path = packet.GetCStr(&offset); + s.Printf(" (path = \"%s\")", path); + } + break; + + case KDP_READIOPORT: + case KDP_READMSR64: + { + const uint32_t error = packet.GetU32 (&offset); + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (error = 0x%8.8x io:\n", error); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + case KDP_DUMPINFO: + { + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (count = %u, bytes = \n", count); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + + } + break; + + default: + s.Printf(" (add support for dumping this packet reply!!!"); + break; + + } + } + else + { + // Dump request packets + switch (command) + { + case KDP_CONNECT: + { + const uint16_t reply_port = ntohs(packet.GetU16 (&offset)); + const uint16_t exc_port = ntohs(packet.GetU16 (&offset)); + s.Printf(" (reply_port = %u, exc_port = %u, greeting = \"%s\")", reply_port, exc_port, packet.GetCStr(&offset)); + } + break; + + case KDP_DISCONNECT: + case KDP_HOSTREBOOT: + case KDP_HOSTINFO: + case KDP_VERSION: + case KDP_REGIONS: + case KDP_KERNELVERSION: + case KDP_MAXBYTES: + case KDP_IMAGEPATH: + case KDP_SUSPEND: + // No args, just the header in the request... + s.PutCString(" ()"); + break; + + case KDP_RESUMECPUS: + { + const uint32_t cpu_mask = packet.GetU32 (&offset); + s.Printf(" (cpu_mask = 0x%8.8x)", cpu_mask); + } + break; + + case KDP_READMEM: + { + const uint32_t addr = packet.GetU32 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x, size = %u)", addr, size); + m_last_read_memory_addr = addr; + } + break; + + case KDP_WRITEMEM: + { + const uint32_t addr = packet.GetU32 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x, size = %u, bytes = \n", addr, size); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_READMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ", size = %u)", addr, size); + m_last_read_memory_addr = addr; + } + break; + + case KDP_READPHYSMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + const uint32_t lcpu = packet.GetU16 (&offset); + s.Printf(" (addr = 0x%16.16llx, size = %u, lcpu = %u)", addr, size, lcpu); + m_last_read_memory_addr = addr; + } + break; + + case KDP_WRITEMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ", size = %u, bytes = \n", addr, size); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_WRITEPHYSMEM64: + { + const uint64_t addr = packet.GetU64 (&offset); + const uint32_t size = packet.GetU32 (&offset); + const uint32_t lcpu = packet.GetU16 (&offset); + s.Printf(" (addr = 0x%16.16llx, size = %u, lcpu = %u, bytes = \n", addr, size, lcpu); + if (size > 0) + DataExtractor::DumpHexBytes(&s, packet.GetData(&offset, size), size, 32, addr); + } + break; + + case KDP_READREGS: + { + const uint32_t cpu = packet.GetU32 (&offset); + const uint32_t flavor = packet.GetU32 (&offset); + s.Printf(" (cpu = %u, flavor = %u)", cpu, flavor); + } + break; + + case KDP_WRITEREGS: + { + const uint32_t cpu = packet.GetU32 (&offset); + const uint32_t flavor = packet.GetU32 (&offset); + const uint32_t nbytes = packet.GetByteSize() - offset; + s.Printf(" (cpu = %u, flavor = %u, regs = \n", cpu, flavor); + if (nbytes > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + m_addr_byte_size, // Size of each item in bytes + nbytes / m_addr_byte_size, // Number of items + 16 / m_addr_byte_size, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + + case KDP_BREAKPOINT_SET: + case KDP_BREAKPOINT_REMOVE: + { + const uint32_t addr = packet.GetU32 (&offset); + s.Printf(" (addr = 0x%8.8x)", addr); + } + break; + + case KDP_BREAKPOINT_SET64: + case KDP_BREAKPOINT_REMOVE64: + { + const uint64_t addr = packet.GetU64 (&offset); + s.Printf(" (addr = 0x%16.16" PRIx64 ")", addr); + } + break; + + + case KDP_LOAD: + { + const char *path = packet.GetCStr(&offset); + s.Printf(" (path = \"%s\")", path); + } + break; + + case KDP_EXCEPTION: + { + const uint32_t count = packet.GetU32 (&offset); + + for (uint32_t i=0; i 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + nbytes, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_READIOPORT: + { + const uint16_t lcpu = packet.GetU16 (&offset); + const uint16_t address = packet.GetU16 (&offset); + const uint16_t nbytes = packet.GetU16 (&offset); + s.Printf(" (lcpu=0x%4.4x, address=0x%4.4x, nbytes=%u)", lcpu, address, nbytes); + } + break; + + case KDP_WRITEIOPORT: + { + const uint16_t lcpu = packet.GetU16 (&offset); + const uint16_t address = packet.GetU16 (&offset); + const uint16_t nbytes = packet.GetU16 (&offset); + s.Printf(" (lcpu = %u, addr = 0x%4.4x, nbytes = %u, bytes = \n", lcpu, address, nbytes); + if (nbytes > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + nbytes, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + } + break; + + case KDP_DUMPINFO: + { + const uint32_t count = packet.GetByteSize() - offset; + s.Printf(" (count = %u, bytes = \n", count); + if (count > 0) + packet.Dump (&s, // Stream to dump to + offset, // Offset within "packet" + eFormatHex, // Format to use + 1, // Size of each item in bytes + count, // Number of items + 16, // Number per line + LLDB_INVALID_ADDRESS, // Don't show addresses before each line + 0, 0); // No bitfields + + } + break; + + } + } + } + else + { + error_desc = "error: invalid packet command: "; + } + } + + if (error_desc) + { + s.PutCString (error_desc); + + packet.Dump (&s, // Stream to dump to + 0, // Offset into "packet" + eFormatBytes, // Dump as hex bytes + 1, // Size of each item is 1 for single bytes + packet.GetByteSize(), // Number of bytes + UINT32_MAX, // Num bytes per line + LLDB_INVALID_ADDRESS, // Base address + 0, 0); // Bitfield info set to not do anything bitfield related + } +} + +uint32_t +CommunicationKDP::SendRequestReadRegisters (uint32_t cpu, + uint32_t flavor, + void *dst, + uint32_t dst_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_READREGS; + // Size is header + 4 byte cpu and 4 byte flavor + const uint32_t command_length = 8 + 4 + 4; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32 (cpu); + request_packet.PutHex32 (flavor); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + uint32_t src_len = reply_packet.GetByteSize() - 12; + + if (src_len > 0) + { + const uint32_t bytes_to_copy = std::min(src_len, dst_len); + const void *src = reply_packet.GetData(&offset, bytes_to_copy); + if (src) + { + ::memcpy (dst, src, bytes_to_copy); + error.Clear(); + // Return the number of bytes we could have returned regardless if + // we copied them or not, just so we know when things don't match up + return src_len; + } + } + if (kdp_error) + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u (error %u)", cpu, flavor, kdp_error); + else + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u", cpu, flavor); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + +uint32_t +CommunicationKDP::SendRequestWriteRegisters (uint32_t cpu, + uint32_t flavor, + const void *src, + uint32_t src_len, + Error &error) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_WRITEREGS; + // Size is header + 4 byte cpu and 4 byte flavor + const uint32_t command_length = 8 + 4 + 4 + src_len; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32 (cpu); + request_packet.PutHex32 (flavor); + request_packet.Write(src, src_len); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error == 0) + return src_len; + error.SetErrorStringWithFormat("failed to read kdp registers for cpu %u flavor %u (error %u)", cpu, flavor, kdp_error); + } + else + { + error.SetErrorString ("failed to send packet"); + } + return 0; +} + + +bool +CommunicationKDP::SendRequestResume () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_RESUMECPUS; + const uint32_t command_length = 12; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutHex32(GetCPUMask()); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + return true; + return false; +} + +bool +CommunicationKDP::SendRequestBreakpoint (bool set, addr_t addr) +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + bool use_64 = (GetVersion() >= 11); + uint32_t command_addr_byte_size = use_64 ? 8 : 4; + const CommandType command = set ? (use_64 ? KDP_BREAKPOINT_SET64 : KDP_BREAKPOINT_SET ): + (use_64 ? KDP_BREAKPOINT_REMOVE64 : KDP_BREAKPOINT_REMOVE); + + const uint32_t command_length = 8 + command_addr_byte_size; + MakeRequestPacketHeader (command, request_packet, command_length); + request_packet.PutMaxHex64 (addr, command_addr_byte_size); + + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + { + lldb::offset_t offset = 8; + uint32_t kdp_error = reply_packet.GetU32 (&offset); + if (kdp_error == 0) + return true; + } + return false; +} + +bool +CommunicationKDP::SendRequestSuspend () +{ + PacketStreamType request_packet (Stream::eBinary, m_addr_byte_size, m_byte_order); + const CommandType command = KDP_SUSPEND; + const uint32_t command_length = 8; + MakeRequestPacketHeader (command, request_packet, command_length); + DataExtractor reply_packet; + if (SendRequestAndGetReply (command, request_packet, reply_packet)) + return true; + return false; +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h new file mode 100644 index 000000000000..98a146d5a06d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h @@ -0,0 +1,347 @@ +//===-- CommunicationKDP.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommunicationKDP_h_ +#define liblldb_CommunicationKDP_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Listener.h" +#include "lldb/Core/StreamBuffer.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/TimeValue.h" + +class CommunicationKDP : public lldb_private::Communication +{ +public: + enum + { + eBroadcastBitRunPacketSent = kLoUserBroadcastBit + }; + + const static uint32_t kMaxPacketSize = 1200; + const static uint32_t kMaxDataSize = 1024; + typedef lldb_private::StreamBuffer<1024> PacketStreamType; + typedef enum + { + KDP_CONNECT = 0u, + KDP_DISCONNECT, + KDP_HOSTINFO, + KDP_VERSION, + KDP_MAXBYTES, + KDP_READMEM, + KDP_WRITEMEM, + KDP_READREGS, + KDP_WRITEREGS, + KDP_LOAD, + KDP_IMAGEPATH, + KDP_SUSPEND, + KDP_RESUMECPUS, + KDP_EXCEPTION, + KDP_TERMINATION, + KDP_BREAKPOINT_SET, + KDP_BREAKPOINT_REMOVE, + KDP_REGIONS, + KDP_REATTACH, + KDP_HOSTREBOOT, + KDP_READMEM64, + KDP_WRITEMEM64, + KDP_BREAKPOINT_SET64, + KDP_BREAKPOINT_REMOVE64, + KDP_KERNELVERSION, + KDP_READPHYSMEM64, + KDP_WRITEPHYSMEM64, + KDP_READIOPORT, + KDP_WRITEIOPORT, + KDP_READMSR64, + KDP_WRITEMSR64, + KDP_DUMPINFO + } CommandType; + + enum + { + KDP_FEATURE_BP = (1u << 0) + }; + + typedef enum + { + KDP_PROTERR_SUCCESS = 0, + KDP_PROTERR_ALREADY_CONNECTED, + KDP_PROTERR_BAD_NBYTES, + KDP_PROTERR_BADFLAVOR + } KDPError; + + typedef enum + { + ePacketTypeRequest = 0x00u, + ePacketTypeReply = 0x80u, + ePacketTypeMask = 0x80u, + eCommandTypeMask = 0x7fu + } PacketType; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommunicationKDP (const char *comm_name); + + virtual + ~CommunicationKDP(); + + bool + SendRequestPacket (const PacketStreamType &request_packet); + + // Wait for a packet within 'nsec' seconds + size_t + WaitForPacketWithTimeoutMicroSeconds (lldb_private::DataExtractor &response, + uint32_t usec); + + bool + GetSequenceMutex(lldb_private::Mutex::Locker& locker); + + bool + CheckForPacket (const uint8_t *src, + size_t src_len, + lldb_private::DataExtractor &packet); + bool + IsRunning() const + { + return m_is_running.GetValue(); + } + + //------------------------------------------------------------------ + // Set the global packet timeout. + // + // For clients, this is the timeout that gets used when sending + // packets and waiting for responses. For servers, this might not + // get used, and if it doesn't this should be moved to the + // CommunicationKDPClient. + //------------------------------------------------------------------ + uint32_t + SetPacketTimeout (uint32_t packet_timeout) + { + const uint32_t old_packet_timeout = m_packet_timeout; + m_packet_timeout = packet_timeout; + return old_packet_timeout; + } + + uint32_t + GetPacketTimeoutInMicroSeconds () const + { + return m_packet_timeout * lldb_private::TimeValue::MicroSecPerSec; + } + + //------------------------------------------------------------------ + // Public Request Packets + //------------------------------------------------------------------ + bool + SendRequestConnect (uint16_t reply_port, + uint16_t exc_port, + const char *greeting); + + bool + SendRequestReattach (uint16_t reply_port); + + bool + SendRequestDisconnect (); + + uint32_t + SendRequestReadMemory (lldb::addr_t addr, + void *dst, + uint32_t dst_size, + lldb_private::Error &error); + + uint32_t + SendRequestWriteMemory (lldb::addr_t addr, + const void *src, + uint32_t src_len, + lldb_private::Error &error); + + bool + SendRawRequest (uint8_t command_byte, + const void *src, + uint32_t src_len, + lldb_private::DataExtractor &reply, + lldb_private::Error &error); + + uint32_t + SendRequestReadRegisters (uint32_t cpu, + uint32_t flavor, + void *dst, + uint32_t dst_size, + lldb_private::Error &error); + + uint32_t + SendRequestWriteRegisters (uint32_t cpu, + uint32_t flavor, + const void *src, + uint32_t src_size, + lldb_private::Error &error); + + const char * + GetKernelVersion (); + + // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + // const char * + // GetImagePath (); + + uint32_t + GetVersion (); + + uint32_t + GetFeatureFlags (); + + bool + LocalBreakpointsAreSupported () + { + return (GetFeatureFlags() & KDP_FEATURE_BP) != 0; + } + + uint32_t + GetCPUMask (); + + uint32_t + GetCPUType (); + + uint32_t + GetCPUSubtype (); + + lldb_private::UUID + GetUUID (); + + bool + RemoteIsEFI (); + + bool + RemoteIsDarwinKernel (); + + lldb::addr_t + GetLoadAddress (); + + bool + SendRequestResume (); + + bool + SendRequestSuspend (); + + bool + SendRequestBreakpoint (bool set, lldb::addr_t addr); + +protected: + + bool + SendRequestPacketNoLock (const PacketStreamType &request_packet); + + size_t + WaitForPacketWithTimeoutMicroSecondsNoLock (lldb_private::DataExtractor &response, + uint32_t timeout_usec); + + bool + WaitForNotRunningPrivate (const lldb_private::TimeValue *timeout_ptr); + + void + MakeRequestPacketHeader (CommandType request_type, + PacketStreamType &request_packet, + uint16_t request_length); + + //------------------------------------------------------------------ + // Protected Request Packets (use public accessors which will cache + // results. + //------------------------------------------------------------------ + bool + SendRequestVersion (); + + bool + SendRequestHostInfo (); + + bool + SendRequestKernelVersion (); + + // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + //bool + //SendRequestImagePath (); + + void + DumpPacket (lldb_private::Stream &s, + const void *data, + uint32_t data_len); + + void + DumpPacket (lldb_private::Stream &s, + const lldb_private::DataExtractor& extractor); + + bool + VersionIsValid() const + { + return m_kdp_version_version != 0; + } + + bool + HostInfoIsValid() const + { + return m_kdp_hostinfo_cpu_type != 0; + } + + bool + ExtractIsReply (uint8_t first_packet_byte) const + { + // TODO: handle big endian... + return (first_packet_byte & ePacketTypeMask) != 0; + } + + CommandType + ExtractCommand (uint8_t first_packet_byte) const + { + // TODO: handle big endian... + return (CommandType)(first_packet_byte & eCommandTypeMask); + } + + static const char * + GetCommandAsCString (uint8_t command); + + void + ClearKDPSettings (); + + bool + SendRequestAndGetReply (const CommandType command, + const PacketStreamType &request_packet, + lldb_private::DataExtractor &reply_packet); + //------------------------------------------------------------------ + // Classes that inherit from CommunicationKDP can see and modify these + //------------------------------------------------------------------ + uint32_t m_addr_byte_size; + lldb::ByteOrder m_byte_order; + uint32_t m_packet_timeout; + 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; + uint32_t m_session_key; + uint8_t m_request_sequence_id; + uint8_t m_exception_sequence_id; + uint32_t m_kdp_version_version; + uint32_t m_kdp_version_feature; + uint32_t m_kdp_hostinfo_cpu_mask; + uint32_t m_kdp_hostinfo_cpu_type; + uint32_t m_kdp_hostinfo_cpu_subtype; + std::string m_kernel_version; + //std::string m_image_path; // Disable KDP_IMAGEPATH for now, it seems to hang the KDP connection... + lldb::addr_t m_last_read_memory_addr; // Last memory read address for logging +private: + //------------------------------------------------------------------ + // For CommunicationKDP only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommunicationKDP); +}; + +#endif // liblldb_CommunicationKDP_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/Makefile b/source/Plugins/Process/MacOSX-Kernel/Makefile new file mode 100644 index 000000000000..e42f390ffe0d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/MacOSX-Darwin/Makefile -------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessDarwin +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp new file mode 100644 index 000000000000..628f76d104fe --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp @@ -0,0 +1,1211 @@ +//===-- ProcessKDP.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 + +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/State.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionGroupString.h" +#include "lldb/Interpreter/OptionGroupUInt64.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/StringExtractor.h" + +#define USEC_PER_SEC 1000000 + +// Project includes +#include "ProcessKDP.h" +#include "ProcessKDPLog.h" +#include "ThreadKDP.h" +#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" +#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + + static PropertyDefinition + g_properties[] = + { + { "packet-timeout" , OptionValue::eTypeUInt64 , true , 5, NULL, NULL, "Specify the default packet timeout in seconds." }, + { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } + }; + + enum + { + ePropertyPacketTimeout + }; + + class PluginProperties : public Properties + { + public: + + static ConstString + GetSettingName () + { + return ProcessKDP::GetPluginNameStatic(); + } + + PluginProperties() : + Properties () + { + m_collection_sp.reset (new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual + ~PluginProperties() + { + } + + uint64_t + GetPacketTimeout() + { + const uint32_t idx = ePropertyPacketTimeout; + return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value); + } + }; + + typedef std::shared_ptr ProcessKDPPropertiesSP; + + static const ProcessKDPPropertiesSP & + GetGlobalPluginProperties() + { + static ProcessKDPPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new PluginProperties ()); + return g_settings_sp; + } + +} // anonymous namespace end + +static const lldb::tid_t g_kernel_tid = 1; + +ConstString +ProcessKDP::GetPluginNameStatic() +{ + static ConstString g_name("kdp-remote"); + return g_name; +} + +const char * +ProcessKDP::GetPluginDescriptionStatic() +{ + return "KDP Remote protocol based debugging plug-in for darwin kernel debugging."; +} + +void +ProcessKDP::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessKDP::CreateInstance); +} + + +lldb::ProcessSP +ProcessKDP::CreateInstance (TargetSP target_sp, + Listener &listener, + const FileSpec *crash_file_path) +{ + lldb::ProcessSP process_sp; + if (crash_file_path == NULL) + process_sp.reset(new ProcessKDP (target_sp, listener)); + return process_sp; +} + +bool +ProcessKDP::CanDebug(TargetSP target_sp, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + { + const llvm::Triple &triple_ref = target_sp->GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: // Should use "macosx" for desktop and "ios" for iOS, but accept darwin just in case + case llvm::Triple::MacOSX: // For desktop targets + case llvm::Triple::IOS: // For arm targets + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + if (triple_ref.getVendor() == llvm::Triple::Apple) + { + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && + exe_objfile->GetStrata() == ObjectFile::eStrataKernel) + return true; + } + break; + + default: + break; + } + } + return false; +} + +//---------------------------------------------------------------------- +// ProcessKDP constructor +//---------------------------------------------------------------------- +ProcessKDP::ProcessKDP(TargetSP target_sp, Listener &listener) : + Process (target_sp, listener), + m_comm("lldb.process.kdp-remote.communication"), + m_async_broadcaster (NULL, "lldb.process.kdp-remote.async-broadcaster"), + m_dyld_plugin_name (), + m_kernel_load_addr (LLDB_INVALID_ADDRESS), + m_command_sp(), + m_kernel_thread_wp() +{ + m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); + m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); + const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); + if (timeout_seconds > 0) + m_comm.SetPacketTimeout(timeout_seconds); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessKDP::~ProcessKDP() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +lldb_private::ConstString +ProcessKDP::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessKDP::GetPluginVersion() +{ + return 1; +} + +Error +ProcessKDP::WillLaunch (Module* module) +{ + Error error; + error.SetErrorString ("launching not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::WillAttachToProcessWithID (lldb::pid_t pid) +{ + Error error; + error.SetErrorString ("attaching to a by process ID not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) +{ + Error error; + error.SetErrorString ("attaching to a by process name not supported in kdp-remote plug-in"); + return error; +} + +bool +ProcessKDP::GetHostArchitecture(ArchSpec &arch) +{ + uint32_t cpu = m_comm.GetCPUType(); + if (cpu) + { + uint32_t sub = m_comm.GetCPUSubtype(); + arch.SetArchitecture(eArchTypeMachO, cpu, sub); + // Leave architecture vendor as unspecified unknown + arch.GetTriple().setVendor(llvm::Triple::UnknownVendor); + arch.GetTriple().setVendorName(llvm::StringRef()); + return true; + } + arch.Clear(); + return false; +} + +Error +ProcessKDP::DoConnectRemote (Stream *strm, const char *remote_url) +{ + Error error; + + // Don't let any JIT happen when doing KDP as we can't allocate + // memory and we don't want to be mucking with threads that might + // already be handling exceptions + SetCanJIT(false); + + if (remote_url == NULL || remote_url[0] == '\0') + { + error.SetErrorStringWithFormat ("invalid connection URL '%s'", remote_url); + return error; + } + + std::unique_ptr conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) + { + // Only try once for now. + // TODO: check if we should be retrying? + const uint32_t max_retry_count = 1; + for (uint32_t retry_count = 0; retry_count < max_retry_count; ++retry_count) + { + if (conn_ap->Connect(remote_url, &error) == eConnectionStatusSuccess) + break; + usleep (100000); + } + } + + if (conn_ap->IsConnected()) + { + const TCPSocket& socket = static_cast(*conn_ap->GetReadObject()); + const uint16_t reply_port = socket.GetLocalPortNumber(); + + if (reply_port != 0) + { + m_comm.SetConnection(conn_ap.release()); + + if (m_comm.SendRequestReattach(reply_port)) + { + if (m_comm.SendRequestConnect(reply_port, reply_port, "Greetings from LLDB...")) + { + m_comm.GetVersion(); + + Target &target = GetTarget(); + ArchSpec kernel_arch; + // The host architecture + GetHostArchitecture(kernel_arch); + ArchSpec target_arch = target.GetArchitecture(); + // Merge in any unspecified stuff into the target architecture in + // case the target arch isn't set at all or incompletely. + target_arch.MergeFrom(kernel_arch); + target.SetArchitecture(target_arch); + + /* Get the kernel's UUID and load address via KDP_KERNELVERSION packet. */ + /* An EFI kdp session has neither UUID nor load address. */ + + UUID kernel_uuid = m_comm.GetUUID (); + addr_t kernel_load_addr = m_comm.GetLoadAddress (); + + if (m_comm.RemoteIsEFI ()) + { + // Select an invalid plugin name for the dynamic loader so one doesn't get used + // since EFI does its own manual loading via python scripting + static ConstString g_none_dynamic_loader("none"); + m_dyld_plugin_name = g_none_dynamic_loader; + + if (kernel_uuid.IsValid()) { + // If EFI passed in a UUID= try to lookup UUID + // The slide will not be provided. But the UUID + // lookup will be used to launch EFI debug scripts + // from the dSYM, that can load all of the symbols. + ModuleSpec module_spec; + module_spec.GetUUID() = kernel_uuid; + module_spec.GetArchitecture() = target.GetArchitecture(); + + // Lookup UUID locally, before attempting dsymForUUID like action + module_spec.GetSymbolFileSpec() = Symbols::LocateExecutableSymbolFile(module_spec); + if (module_spec.GetSymbolFileSpec()) + { + ModuleSpec executable_module_spec = Symbols::LocateExecutableObjectFile (module_spec); + if (executable_module_spec.GetFileSpec().Exists()) + { + module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); + } + } + if (!module_spec.GetSymbolFileSpec() || !module_spec.GetSymbolFileSpec()) + Symbols::DownloadObjectAndSymbolFile (module_spec, true); + + if (module_spec.GetFileSpec().Exists()) + { + ModuleSP module_sp(new Module (module_spec)); + if (module_sp.get() && module_sp->GetObjectFile()) + { + // Get the current target executable + ModuleSP exe_module_sp (target.GetExecutableModule ()); + + // Make sure you don't already have the right module loaded and they will be uniqued + if (exe_module_sp.get() != module_sp.get()) + target.SetExecutableModule (module_sp, false); + } + } + } + } + else if (m_comm.RemoteIsDarwinKernel ()) + { + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + if (kernel_load_addr != LLDB_INVALID_ADDRESS) + { + m_kernel_load_addr = kernel_load_addr; + } + } + + // Set the thread ID + UpdateThreadListIfNeeded (); + SetID (1); + GetThreadList (); + SetPrivateState (eStateStopped); + StreamSP async_strm_sp(target.GetDebugger().GetAsyncOutputStream()); + if (async_strm_sp) + { + const char *cstr; + if ((cstr = m_comm.GetKernelVersion ()) != NULL) + { + async_strm_sp->Printf ("Version: %s\n", cstr); + async_strm_sp->Flush(); + } +// if ((cstr = m_comm.GetImagePath ()) != NULL) +// { +// async_strm_sp->Printf ("Image Path: %s\n", cstr); +// async_strm_sp->Flush(); +// } + } + } + else + { + error.SetErrorString("KDP_REATTACH failed"); + } + } + else + { + error.SetErrorString("KDP_REATTACH failed"); + } + } + else + { + error.SetErrorString("invalid reply port from UDP connection"); + } + } + else + { + if (error.Success()) + error.SetErrorStringWithFormat ("failed to connect to '%s'", remote_url); + } + if (error.Fail()) + m_comm.Disconnect(); + + return error; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessKDP::DoLaunch (Module *exe_module, + ProcessLaunchInfo &launch_info) +{ + Error error; + error.SetErrorString ("launching not supported in kdp-remote plug-in"); + return error; +} + +Error +ProcessKDP::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) +{ + Error error; + error.SetErrorString ("attach to process by ID is not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::DoAttachToProcessWithName (const char *process_name, const ProcessAttachInfo &attach_info) +{ + Error error; + error.SetErrorString ("attach to process by name is not suppported in kdp remote debugging"); + return error; +} + + +void +ProcessKDP::DidAttach (ArchSpec &process_arch) +{ + Process::DidAttach(process_arch); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DidAttach()"); + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + GetHostArchitecture(process_arch); + } +} + +addr_t +ProcessKDP::GetImageInfoAddress() +{ + return m_kernel_load_addr; +} + +lldb_private::DynamicLoader * +ProcessKDP::GetDynamicLoader () +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, m_dyld_plugin_name.IsEmpty() ? NULL : m_dyld_plugin_name.GetCString())); + return m_dyld_ap.get(); +} + +Error +ProcessKDP::WillResume () +{ + return Error(); +} + +Error +ProcessKDP::DoResume () +{ + Error error; + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + // Only start the async thread if we try to do any process control + if (!m_async_thread.IsJoinable()) + StartAsyncThread(); + + bool resume = false; + + // With KDP there is only one thread we can tell what to do + ThreadSP kernel_thread_sp (m_thread_list.FindThreadByProtocolID(g_kernel_tid)); + + if (kernel_thread_sp) + { + const StateType thread_resume_state = kernel_thread_sp->GetTemporaryResumeState(); + + if (log) + log->Printf ("ProcessKDP::DoResume() thread_resume_state = %s", StateAsCString(thread_resume_state)); + switch (thread_resume_state) + { + case eStateSuspended: + // Nothing to do here when a thread will stay suspended + // we just leave the CPU mask bit set to zero for the thread + if (log) + log->Printf ("ProcessKDP::DoResume() = suspended???"); + break; + + case eStateStepping: + { + lldb::RegisterContextSP reg_ctx_sp (kernel_thread_sp->GetRegisterContext()); + + if (reg_ctx_sp) + { + if (log) + log->Printf ("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (true);"); + reg_ctx_sp->HardwareSingleStep (true); + resume = true; + } + else + { + error.SetErrorStringWithFormat("KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); + } + } + break; + + case eStateRunning: + { + lldb::RegisterContextSP reg_ctx_sp (kernel_thread_sp->GetRegisterContext()); + + if (reg_ctx_sp) + { + if (log) + log->Printf ("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (false);"); + reg_ctx_sp->HardwareSingleStep (false); + resume = true; + } + else + { + error.SetErrorStringWithFormat("KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); + } + } + break; + + default: + // The only valid thread resume states are listed above + assert (!"invalid thread resume state"); + break; + } + } + + if (resume) + { + if (log) + log->Printf ("ProcessKDP::DoResume () sending resume"); + + if (m_comm.SendRequestResume ()) + { + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue); + SetPrivateState(eStateRunning); + } + else + error.SetErrorString ("KDP resume failed"); + } + else + { + error.SetErrorString ("kernel thread is suspended"); + } + + return error; +} + +lldb::ThreadSP +ProcessKDP::GetKernelThread() +{ + // KDP only tells us about one thread/core. Any other threads will usually + // be the ones that are read from memory by the OS plug-ins. + + ThreadSP thread_sp (m_kernel_thread_wp.lock()); + if (!thread_sp) + { + thread_sp.reset(new ThreadKDP (*this, g_kernel_tid)); + m_kernel_thread_wp = thread_sp; + } + return thread_sp; +} + + + + +bool +ProcessKDP::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // locker will keep a mutex locked until it goes out of scope + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_THREAD)); + if (log && log->GetMask().Test(KDP_LOG_VERBOSE)) + log->Printf ("ProcessKDP::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); + + // Even though there is a CPU mask, it doesn't mean we can see each CPU + // individually, there is really only one. Lets call this thread 1. + ThreadSP thread_sp (old_thread_list.FindThreadByProtocolID(g_kernel_tid, false)); + if (!thread_sp) + thread_sp = GetKernelThread (); + new_thread_list.AddThread(thread_sp); + + return new_thread_list.GetSize(false) > 0; +} + +void +ProcessKDP::RefreshStateAfterStop () +{ + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); +} + +Error +ProcessKDP::DoHalt (bool &caused_stop) +{ + Error error; + + if (m_comm.IsRunning()) + { + if (m_destroy_in_process) + { + // If we are attemping to destroy, we need to not return an error to + // Halt or DoDestroy won't get called. + // We are also currently running, so send a process stopped event + SetPrivateState (eStateStopped); + } + else + { + error.SetErrorString ("KDP cannot interrupt a running kernel"); + } + } + return error; +} + +Error +ProcessKDP::DoDetach(bool keep_stopped) +{ + Error error; + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::DoDetach(keep_stopped = %i)", keep_stopped); + + if (m_comm.IsRunning()) + { + // We are running and we can't interrupt a running kernel, so we need + // to just close the connection to the kernel and hope for the best + } + else + { + // If we are going to keep the target stopped, then don't send the disconnect message. + if (!keep_stopped && m_comm.IsConnected()) + { + const bool success = m_comm.SendRequestDisconnect(); + if (log) + { + if (success) + log->PutCString ("ProcessKDP::DoDetach() detach packet sent successfully"); + else + log->PutCString ("ProcessKDP::DoDetach() connection channel shutdown failed"); + } + m_comm.Disconnect (); + } + } + StopAsyncThread (); + m_comm.Clear(); + + SetPrivateState (eStateDetached); + ResumePrivateStateThread(); + + //KillDebugserverProcess (); + return error; +} + +Error +ProcessKDP::DoDestroy () +{ + // For KDP there really is no difference between destroy and detach + bool keep_stopped = false; + return DoDetach(keep_stopped); +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessKDP::IsAlive () +{ + return m_comm.IsConnected() && Process::IsAlive(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessKDP::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + uint8_t *data_buffer = (uint8_t *) buf; + if (m_comm.IsConnected()) + { + const size_t max_read_size = 512; + size_t total_bytes_read = 0; + + // Read the requested amount of memory in 512 byte chunks + while (total_bytes_read < size) + { + size_t bytes_to_read_this_request = size - total_bytes_read; + if (bytes_to_read_this_request > max_read_size) + { + bytes_to_read_this_request = max_read_size; + } + size_t bytes_read = m_comm.SendRequestReadMemory (addr + total_bytes_read, + data_buffer + total_bytes_read, + bytes_to_read_this_request, error); + total_bytes_read += bytes_read; + if (error.Fail() || bytes_read == 0) + { + return total_bytes_read; + } + } + + return total_bytes_read; + } + error.SetErrorString ("not connected"); + return 0; +} + +size_t +ProcessKDP::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + if (m_comm.IsConnected()) + return m_comm.SendRequestWriteMemory (addr, buf, size, error); + error.SetErrorString ("not connected"); + return 0; +} + +lldb::addr_t +ProcessKDP::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) +{ + error.SetErrorString ("memory allocation not suppported in kdp remote debugging"); + return LLDB_INVALID_ADDRESS; +} + +Error +ProcessKDP::DoDeallocateMemory (lldb::addr_t addr) +{ + Error error; + error.SetErrorString ("memory deallocation not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::EnableBreakpointSite (BreakpointSite *bp_site) +{ + if (m_comm.LocalBreakpointsAreSupported ()) + { + Error error; + if (!bp_site->IsEnabled()) + { + if (m_comm.SendRequestBreakpoint(true, bp_site->GetLoadAddress())) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eExternal); + } + else + { + error.SetErrorString ("KDP set breakpoint failed"); + } + } + return error; + } + return EnableSoftwareBreakpoint (bp_site); +} + +Error +ProcessKDP::DisableBreakpointSite (BreakpointSite *bp_site) +{ + if (m_comm.LocalBreakpointsAreSupported ()) + { + Error error; + if (bp_site->IsEnabled()) + { + BreakpointSite::Type bp_type = bp_site->GetType(); + if (bp_type == BreakpointSite::eExternal) + { + if (m_destroy_in_process && m_comm.IsRunning()) + { + // We are trying to destroy our connection and we are running + bp_site->SetEnabled(false); + } + else + { + if (m_comm.SendRequestBreakpoint(false, bp_site->GetLoadAddress())) + bp_site->SetEnabled(false); + else + error.SetErrorString ("KDP remove breakpoint failed"); + } + } + else + { + error = DisableSoftwareBreakpoint (bp_site); + } + } + return error; + } + return DisableSoftwareBreakpoint (bp_site); +} + +Error +ProcessKDP::EnableWatchpoint (Watchpoint *wp, bool notify) +{ + Error error; + error.SetErrorString ("watchpoints are not suppported in kdp remote debugging"); + return error; +} + +Error +ProcessKDP::DisableWatchpoint (Watchpoint *wp, bool notify) +{ + Error error; + error.SetErrorString ("watchpoints are not suppported in kdp remote debugging"); + return error; +} + +void +ProcessKDP::Clear() +{ + m_thread_list.Clear(); +} + +Error +ProcessKDP::DoSignal (int signo) +{ + Error error; + error.SetErrorString ("sending signals is not suppported in kdp remote debugging"); + return error; +} + +void +ProcessKDP::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + DebuggerInitialize); + + Log::Callbacks log_callbacks = { + ProcessKDPLog::DisableLog, + ProcessKDPLog::EnableLog, + ProcessKDPLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessKDP::GetPluginNameStatic(), log_callbacks); + }); +} + +void +ProcessKDP::DebuggerInitialize (lldb_private::Debugger &debugger) +{ + if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForProcessPlugin (debugger, + GetGlobalPluginProperties()->GetValueProperties(), + ConstString ("Properties for the kdp-remote process plug-in."), + is_global_setting); + } +} + +bool +ProcessKDP::StartAsyncThread () +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessKDP::StartAsyncThread ()"); + + if (m_async_thread.IsJoinable()) + return true; + + m_async_thread = ThreadLauncher::LaunchThread("", ProcessKDP::AsyncThread, this, NULL); + return m_async_thread.IsJoinable(); +} + +void +ProcessKDP::StopAsyncThread () +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessKDP::StopAsyncThread ()"); + + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); + + // Stop the stdio thread + if (m_async_thread.IsJoinable()) + m_async_thread.Join(nullptr); +} + + +void * +ProcessKDP::AsyncThread (void *arg) +{ + ProcessKDP *process = (ProcessKDP*) arg; + + const lldb::pid_t pid = process->GetID(); + + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PROCESS)); + if (log) + log->Printf ("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread starting...", arg, pid); + + Listener listener ("ProcessKDP::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 ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", + pid); + if (listener.WaitForEvent (NULL, event_sp)) + { + uint32_t event_type = event_sp->GetType(); + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") Got an event of type: %d...", + pid, + event_type); + + // When we are running, poll for 1 second to try and get an exception + // to indicate the process has stopped. If we don't get one, check to + // make sure no one asked us to exit + bool is_running = false; + DataExtractor exc_reply_packet; + do + { + switch (event_type) + { + case eBroadcastBitAsyncContinue: + { + is_running = true; + if (process->m_comm.WaitForPacketWithTimeoutMicroSeconds (exc_reply_packet, 1 * USEC_PER_SEC)) + { + ThreadSP thread_sp (process->GetKernelThread()); + if (thread_sp) + { + lldb::RegisterContextSP reg_ctx_sp (thread_sp->GetRegisterContext()); + if (reg_ctx_sp) + reg_ctx_sp->InvalidateAllRegisters(); + static_cast(thread_sp.get())->SetStopInfoFrom_KDP_EXCEPTION (exc_reply_packet); + } + + // TODO: parse the stop reply packet + is_running = false; + process->SetPrivateState(eStateStopped); + } + else + { + // Check to see if we are supposed to exit. There is no way to + // interrupt a running kernel, so all we can do is wait for an + // exception or detach... + if (listener.GetNextEvent(event_sp)) + { + // We got an event, go through the loop again + event_type = event_sp->GetType(); + } + } + } + break; + + case eBroadcastBitAsyncThreadShouldExit: + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", + pid); + done = true; + is_running = false; + break; + + default: + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") got unknown event 0x%8.8x", + pid, + event_type); + done = true; + is_running = false; + break; + } + } while (is_running); + } + else + { + if (log) + log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", + pid); + done = true; + } + } + } + + if (log) + log->Printf ("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread exiting...", + arg, + pid); + + process->m_async_thread.Reset(); + return NULL; +} + + +class CommandObjectProcessKDPPacketSend : public CommandObjectParsed +{ +private: + + OptionGroupOptions m_option_group; + OptionGroupUInt64 m_command_byte; + OptionGroupString m_packet_data; + + virtual Options * + GetOptions () + { + return &m_option_group; + } + + +public: + CommandObjectProcessKDPPacketSend(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process plugin packet send", + "Send a custom packet through the KDP protocol by specifying the command byte and the packet payload data. A packet will be sent with a correct header and payload, and the raw result bytes will be displayed as a string value. ", + NULL), + m_option_group (interpreter), + m_command_byte(LLDB_OPT_SET_1, true , "command", 'c', 0, eArgTypeNone, "Specify the command byte to use when sending the KDP request packet.", 0), + m_packet_data (LLDB_OPT_SET_1, false, "payload", 'p', 0, eArgTypeNone, "Specify packet payload bytes as a hex ASCII string with no spaces or hex prefixes.", NULL) + { + m_option_group.Append (&m_command_byte, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_packet_data , LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + ~CommandObjectProcessKDPPacketSend () + { + } + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + if (!m_command_byte.GetOptionValue().OptionWasSet()) + { + result.AppendError ("the --command option must be set to a valid command byte"); + result.SetStatus (eReturnStatusFailed); + } + else + { + const uint64_t command_byte = m_command_byte.GetOptionValue().GetUInt64Value(0); + if (command_byte > 0 && command_byte <= UINT8_MAX) + { + ProcessKDP *process = (ProcessKDP *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + { + const StateType state = process->GetState(); + + if (StateIsStoppedState (state, true)) + { + std::vector payload_bytes; + const char *ascii_hex_bytes_cstr = m_packet_data.GetOptionValue().GetCurrentValue(); + if (ascii_hex_bytes_cstr && ascii_hex_bytes_cstr[0]) + { + StringExtractor extractor(ascii_hex_bytes_cstr); + const size_t ascii_hex_bytes_cstr_len = extractor.GetStringRef().size(); + if (ascii_hex_bytes_cstr_len & 1) + { + result.AppendErrorWithFormat ("payload data must contain an even number of ASCII hex characters: '%s'", ascii_hex_bytes_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + payload_bytes.resize(ascii_hex_bytes_cstr_len/2); + if (extractor.GetHexBytes(&payload_bytes[0], payload_bytes.size(), '\xdd') != payload_bytes.size()) + { + result.AppendErrorWithFormat ("payload data must only contain ASCII hex characters (no spaces or hex prefixes): '%s'", ascii_hex_bytes_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + Error error; + DataExtractor reply; + process->GetCommunication().SendRawRequest (command_byte, + payload_bytes.empty() ? NULL : payload_bytes.data(), + payload_bytes.size(), + reply, + error); + + if (error.Success()) + { + // Copy the binary bytes into a hex ASCII string for the result + StreamString packet; + packet.PutBytesAsRawHex8(reply.GetDataStart(), + reply.GetByteSize(), + endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + result.AppendMessage(packet.GetString().c_str()); + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + else + { + const char *error_cstr = error.AsCString(); + if (error_cstr && error_cstr[0]) + result.AppendError (error_cstr); + else + result.AppendErrorWithFormat ("unknown error 0x%8.8x", error.GetError()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendErrorWithFormat ("process must be stopped in order to send KDP packets, state is %s", StateAsCString (state)); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("invalid process"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("invalid command byte 0x%" PRIx64 ", valid values are 1 - 255", command_byte); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendErrorWithFormat ("'%s' takes no arguments, only options.", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return false; + } +}; + +class CommandObjectProcessKDPPacket : public CommandObjectMultiword +{ +private: + +public: + CommandObjectProcessKDPPacket(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process plugin packet", + "Commands that deal with KDP remote packets.", + NULL) + { + LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessKDPPacketSend (interpreter))); + } + + ~CommandObjectProcessKDPPacket () + { + } +}; + +class CommandObjectMultiwordProcessKDP : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordProcessKDP (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process plugin", + "A set of commands for operating on a ProcessKDP process.", + "process plugin []") + { + LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessKDPPacket (interpreter))); + } + + ~CommandObjectMultiwordProcessKDP () + { + } +}; + +CommandObject * +ProcessKDP::GetPluginCommandObject() +{ + if (!m_command_sp) + m_command_sp.reset (new CommandObjectMultiwordProcessKDP (GetTarget().GetDebugger().GetCommandInterpreter())); + return m_command_sp.get(); +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h new file mode 100644 index 000000000000..fe9a4e2844bf --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h @@ -0,0 +1,274 @@ +//===-- ProcessKDP.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessKDP_h_ +#define liblldb_ProcessKDP_h_ + +// C Includes + +// C++ Includes +#include +#include + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +#include "CommunicationKDP.h" + +class ThreadKDP; + +class ProcessKDP : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance (lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + DebuggerInitialize (lldb_private::Debugger &debugger); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessKDP(lldb::TargetSP target_sp, lldb_private::Listener &listener); + + virtual + ~ProcessKDP(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb::TargetSP target_sp, + bool plugin_specified_by_name); + + virtual lldb_private::CommandObject * + GetPluginCommandObject(); + + //------------------------------------------------------------------ + // 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 *exe_module, + lldb_private::ProcessLaunchInfo &launch_info); + + virtual lldb_private::Error + WillAttachToProcessWithID (lldb::pid_t pid); + + virtual lldb_private::Error + WillAttachToProcessWithName (const char *process_name, bool wait_for_launch); + + virtual lldb_private::Error + DoConnectRemote (lldb_private::Stream *strm, const char *remote_url); + + virtual lldb_private::Error + DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); + + virtual lldb_private::Error + DoAttachToProcessWithName (const char *process_name, const lldb_private::ProcessAttachInfo &attach_info); + + virtual void + DidAttach (lldb_private::ArchSpec &process_arch); + + lldb::addr_t + GetImageInfoAddress(); + + lldb_private::DynamicLoader * + GetDynamicLoader (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + WillResume (); + + virtual lldb_private::Error + DoResume (); + + virtual lldb_private::Error + DoHalt (bool &caused_stop); + + virtual lldb_private::Error + DoDetach (bool keep_stopped); + + virtual lldb_private::Error + DoSignal (int signal); + + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + //------------------------------------------------------------------ + // 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 Breakpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableBreakpointSite (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpointSite (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + + CommunicationKDP & + GetCommunication() + { + return m_comm; + } + +protected: + friend class ThreadKDP; + friend class CommunicationKDP; + + //---------------------------------------------------------------------- + // 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 + GetHostArchitecture (lldb_private::ArchSpec &arch); + + bool + ProcessIDIsValid ( ) const; + + void + Clear ( ); + + virtual bool + UpdateThreadList (lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list); + + enum + { + eBroadcastBitAsyncContinue = (1 << 0), + eBroadcastBitAsyncThreadShouldExit = (1 << 1) + }; + + lldb::ThreadSP + GetKernelThread (); + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + CommunicationKDP m_comm; + lldb_private::Broadcaster m_async_broadcaster; + lldb_private::HostThread m_async_thread; + lldb_private::ConstString m_dyld_plugin_name; + lldb::addr_t m_kernel_load_addr; + lldb::CommandObjectSP m_command_sp; + lldb::ThreadWP m_kernel_thread_wp; + + + bool + StartAsyncThread (); + + void + StopAsyncThread (); + + static void * + AsyncThread (void *arg); + +private: + //------------------------------------------------------------------ + // For ProcessKDP only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (ProcessKDP); + +}; + +#endif // liblldb_ProcessKDP_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp new file mode 100644 index 000000000000..79cb62aa0066 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.cpp @@ -0,0 +1,186 @@ +//===-- ProcessKDPLog.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessKDPLog.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_log_sp the first time this function is +// called. +static bool g_log_enabled = false; +static Log * g_log = NULL; +static Log * +GetLog () +{ + if (!g_log_enabled) + return NULL; + return g_log; +} + +Log * +ProcessKDPLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log(GetLog ()); + if (log && mask) + { + uint32_t log_mask = log->GetMask().Get(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +ProcessKDPLog::DisableLog (const char **categories, Stream *feedback_strm) +{ + Log *log (GetLog ()); + if (log) + { + uint32_t flag_bits = 0; + + if (categories[0] != NULL) + { + flag_bits = log->GetMask().Get(); + for (size_t i = 0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + + if (::strcasecmp (arg, "all") == 0 ) flag_bits &= ~KDP_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits &= ~KDP_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits &= ~KDP_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits &= ~KDP_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits &= ~KDP_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits &= ~KDP_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits &= ~KDP_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits &= ~KDP_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits &= ~KDP_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits &= ~KDP_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits &= ~KDP_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits &= ~KDP_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits &= ~KDP_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits &= ~KDP_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories (feedback_strm); + } + + } + } + + log->GetMask().Reset (flag_bits); + if (flag_bits == 0) + g_log_enabled = false; + } + + return; +} + +Log * +ProcessKDPLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const char **categories, 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 = 0; + if (g_log) + flag_bits = g_log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + bool got_unknown_category = false; + for (size_t i=0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + if (::strcasecmp (arg, "all") == 0 ) flag_bits |= KDP_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits |= KDP_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits |= KDP_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits |= KDP_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= KDP_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits |= KDP_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= KDP_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= KDP_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= KDP_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= KDP_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= KDP_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= KDP_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= KDP_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits |= KDP_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("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 = KDP_LOG_DEFAULT; + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + } + g_log_enabled = true; + return g_log; +} + +void +ProcessKDPLog::ListLogCategories (Stream *strm) +{ + strm->Printf ("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " async - log asynchronous activity\n" + " break - log breakpoints\n" + " communication - log communication activity\n" + " default - enable the default set of logging categories for liblldb\n" + " packets - log gdb remote packets\n" + " memory - log memory reads and writes\n" + " data-short - log memory bytes for memory reads and writes for short transactions only\n" + " data-long - log memory bytes for memory reads and writes for all transactions\n" + " process - log process events and activities\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n" + " watch - log watchpoint related activities\n", + ProcessKDP::GetPluginNameStatic().GetCString()); +} + + +void +ProcessKDPLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h new file mode 100644 index 000000000000..0cb32d9b2dcf --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDPLog.h @@ -0,0 +1,54 @@ +//===-- ProcessKDPLog.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessKDPLog_h_ +#define liblldb_ProcessKDPLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define KDP_LOG_VERBOSE (1u << 0) +#define KDP_LOG_PROCESS (1u << 1) +#define KDP_LOG_THREAD (1u << 2) +#define KDP_LOG_PACKETS (1u << 3) +#define KDP_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define KDP_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define KDP_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define KDP_LOG_BREAKPOINTS (1u << 7) +#define KDP_LOG_WATCHPOINTS (1u << 8) +#define KDP_LOG_STEP (1u << 9) +#define KDP_LOG_COMM (1u << 10) +#define KDP_LOG_ASYNC (1u << 11) +#define KDP_LOG_ALL (UINT32_MAX) +#define KDP_LOG_DEFAULT KDP_LOG_PACKETS + +class ProcessKDPLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (const char **categories, lldb_private::Stream *feedback_strm); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_ProcessKDPLog_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp new file mode 100644 index 000000000000..449ac646ab3c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.cpp @@ -0,0 +1,161 @@ +//===-- RegisterContextKDP_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 "RegisterContextKDP_arm.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_arm::RegisterContextKDP_arm (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_arm (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_arm::~RegisterContextKDP_arm() +{ +} + +int +RegisterContextKDP_arm::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm::DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h new file mode 100644 index 000000000000..1e547289d9ce --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm.h @@ -0,0 +1,61 @@ +//===-- RegisterContextKDP_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_RegisterContextKDP_arm_h_ +#define liblldb_RegisterContextKDP_arm_h_ + +// C Includes + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_arm.h" + +class ThreadKDP; + +class RegisterContextKDP_arm : public RegisterContextDarwin_arm +{ +public: + + RegisterContextKDP_arm (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_arm(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + int + DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_arm_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp new file mode 100644 index 000000000000..ed62f1982d3c --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.cpp @@ -0,0 +1,161 @@ +//===-- RegisterContextKDP_arm64.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextKDP_arm64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_arm64::RegisterContextKDP_arm64 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_arm64 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_arm64::~RegisterContextKDP_arm64() +{ +} + +int +RegisterContextKDP_arm64::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_arm64::DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, DBGRegSet, &dbg, sizeof(dbg), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h new file mode 100644 index 000000000000..8780b7be4a9a --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_arm64.h @@ -0,0 +1,61 @@ +//===-- RegisterContextKDP_arm64.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextKDP_arm64_h_ +#define liblldb_RegisterContextKDP_arm64_h_ + +// C Includes + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_arm64.h" + +class ThreadKDP; + +class RegisterContextKDP_arm64 : public RegisterContextDarwin_arm64 +{ +public: + + RegisterContextKDP_arm64 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_arm64(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + int + DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_arm64_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp new file mode 100644 index 000000000000..882b0c2e931d --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.cpp @@ -0,0 +1,129 @@ +//===-- RegisterContextKDP_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 +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextKDP_i386.h" +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_i386::RegisterContextKDP_i386 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_i386 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_i386::~RegisterContextKDP_i386() +{ +} + +int +RegisterContextKDP_i386::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_i386::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + + diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h new file mode 100644 index 000000000000..4b6bc5b262f7 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_i386.h @@ -0,0 +1,53 @@ +//===-- RegisterContextKDP_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_RegisterContextKDP_i386_h_ +#define liblldb_RegisterContextKDP_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_i386.h" + +class ThreadKDP; + +class RegisterContextKDP_i386 : public RegisterContextDarwin_i386 +{ +public: + RegisterContextKDP_i386 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_i386(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_i386_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp new file mode 100644 index 000000000000..f4247a5da272 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.cpp @@ -0,0 +1,127 @@ +//===-- RegisterContextKDP_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 +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextKDP_x86_64.h" +#include "ProcessKDP.h" +#include "ThreadKDP.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextKDP_x86_64::RegisterContextKDP_x86_64 (ThreadKDP &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_x86_64 (thread, concrete_frame_idx), + m_kdp_thread (thread) +{ +} + +RegisterContextKDP_x86_64::~RegisterContextKDP_x86_64() +{ +} + +int +RegisterContextKDP_x86_64::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestReadRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, GPRRegSet, &gpr, sizeof(gpr), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, FPURegSet, &fpu, sizeof(fpu), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} + +int +RegisterContextKDP_x86_64::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (static_cast(process_sp.get())->GetCommunication().SendRequestWriteRegisters (tid, EXCRegSet, &exc, sizeof(exc), error)) + { + if (error.Success()) + return 0; + } + } + return -1; +} diff --git a/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h new file mode 100644 index 000000000000..a426349198ff --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/RegisterContextKDP_x86_64.h @@ -0,0 +1,54 @@ +//===-- RegisterContextKDP_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_RegisterContextKDP_x86_64_h_ +#define liblldb_RegisterContextKDP_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Plugins/Process/Utility/RegisterContextDarwin_x86_64.h" + +class ThreadKDP; + +class RegisterContextKDP_x86_64 : public RegisterContextDarwin_x86_64 +{ +public: + + RegisterContextKDP_x86_64 (ThreadKDP &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextKDP_x86_64(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + ThreadKDP &m_kdp_thread; +}; + +#endif // liblldb_RegisterContextKDP_x86_64_h_ diff --git a/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp new file mode 100644 index 000000000000..3b8bed101a8a --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp @@ -0,0 +1,211 @@ +//===-- ThreadKDP.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadKDP.h" + +#include "lldb/Utility/SafeMachO.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/State.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Breakpoint/Watchpoint.h" + +#include "ProcessKDP.h" +#include "ProcessKDPLog.h" +#include "RegisterContextKDP_arm.h" +#include "RegisterContextKDP_arm64.h" +#include "RegisterContextKDP_i386.h" +#include "RegisterContextKDP_x86_64.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadKDP::ThreadKDP (Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_thread_name (), + m_dispatch_queue_name (), + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS) +{ + ProcessKDPLog::LogIf(KDP_LOG_THREAD, "%p: ThreadKDP::ThreadKDP (tid = 0x%4.4x)", this, GetID()); +} + +ThreadKDP::~ThreadKDP () +{ + ProcessKDPLog::LogIf(KDP_LOG_THREAD, "%p: ThreadKDP::~ThreadKDP (tid = 0x%4.4x)", this, GetID()); + DestroyThread(); +} + +const char * +ThreadKDP::GetName () +{ + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + +const char * +ThreadKDP::GetQueueName () +{ + return NULL; +} + +void +ThreadKDP::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The KDPRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + const bool force = false; + lldb::RegisterContextSP reg_ctx_sp (GetRegisterContext()); + if (reg_ctx_sp) + reg_ctx_sp->InvalidateIfNeeded (force); +} + +bool +ThreadKDP::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +void +ThreadKDP::Dump(Log *log, uint32_t index) +{ +} + + +bool +ThreadKDP::ShouldStop (bool &step_more) +{ + return true; +} +lldb::RegisterContextSP +ThreadKDP::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadKDP::CreateRegisterContextForFrame (StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex (); + + if (concrete_frame_idx == 0) + { + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + switch (static_cast(process_sp.get())->GetCommunication().GetCPUType()) + { + case llvm::MachO::CPU_TYPE_ARM: + reg_ctx_sp.reset (new RegisterContextKDP_arm (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_ARM64: + reg_ctx_sp.reset (new RegisterContextKDP_arm64 (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_I386: + reg_ctx_sp.reset (new RegisterContextKDP_i386 (*this, concrete_frame_idx)); + break; + case llvm::MachO::CPU_TYPE_X86_64: + reg_ctx_sp.reset (new RegisterContextKDP_x86_64 (*this, concrete_frame_idx)); + break; + default: + assert (!"Add CPU type support in KDP"); + break; + } + } + } + else + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame); + } + return reg_ctx_sp; +} + +bool +ThreadKDP::CalculateStopInfo () +{ + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + if (m_cached_stop_info_sp) + { + SetStopInfo (m_cached_stop_info_sp); + } + else + { + SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP)); + } + return true; + } + return false; +} + +void +ThreadKDP::SetStopInfoFrom_KDP_EXCEPTION (const DataExtractor &exc_reply_packet) +{ + lldb::offset_t offset = 0; + uint8_t reply_command = exc_reply_packet.GetU8(&offset); + if (reply_command == CommunicationKDP::KDP_EXCEPTION) + { + offset = 8; + const uint32_t count = exc_reply_packet.GetU32 (&offset); + if (count >= 1) + { + //const uint32_t cpu = exc_reply_packet.GetU32 (&offset); + offset += 4; // Skip the useless CPU field + const uint32_t exc_type = exc_reply_packet.GetU32 (&offset); + const uint32_t exc_code = exc_reply_packet.GetU32 (&offset); + const uint32_t exc_subcode = exc_reply_packet.GetU32 (&offset); + // We have to make a copy of the stop info because the thread list + // will iterate through the threads and clear all stop infos.. + + // Let the StopInfoMachException::CreateStopReasonWithMachException() + // function update the PC if needed as we might hit a software breakpoint + // and need to decrement the PC (i386 and x86_64 need this) and KDP + // doesn't do this for us. + const bool pc_already_adjusted = false; + const bool adjust_pc_if_needed = true; + + m_cached_stop_info_sp = StopInfoMachException::CreateStopReasonWithMachException (*this, + exc_type, + 2, + exc_code, + exc_subcode, + 0, + pc_already_adjusted, + adjust_pc_if_needed); + } + } +} + diff --git a/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h new file mode 100644 index 000000000000..7dc373f03550 --- /dev/null +++ b/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.h @@ -0,0 +1,98 @@ +//===-- ThreadKDP.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadKDP_h_ +#define liblldb_ThreadKDP_h_ + +#include + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +class ProcessKDP; + +class ThreadKDP : public lldb_private::Thread +{ +public: + ThreadKDP (lldb_private::Process &process, + lldb::tid_t tid); + + virtual + ~ThreadKDP (); + + virtual void + RefreshStateAfterStop(); + + virtual const char * + GetName (); + + virtual const char * + GetQueueName (); + + virtual lldb::RegisterContextSP + GetRegisterContext (); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + void + Dump (lldb_private::Log *log, uint32_t index); + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + bool + ShouldStop (bool &step_more); + + const char * + GetBasicInfoAsString (); + + void + SetName (const char *name) + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + + lldb::addr_t + GetThreadDispatchQAddr () + { + return m_thread_dispatch_qaddr; + } + + void + SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr) + { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + + void + SetStopInfoFrom_KDP_EXCEPTION (const lldb_private::DataExtractor &exc_reply_packet); + +protected: + + friend class ProcessKDP; + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + lldb::StopInfoSP m_cached_stop_info_sp; + //------------------------------------------------------------------ + // Protected member functions. + //------------------------------------------------------------------ + virtual bool + CalculateStopInfo (); +}; + +#endif // liblldb_ThreadKDP_h_ diff --git a/source/Plugins/Process/POSIX/CMakeLists.txt b/source/Plugins/Process/POSIX/CMakeLists.txt new file mode 100644 index 000000000000..2ed7326dbb71 --- /dev/null +++ b/source/Plugins/Process/POSIX/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(.) +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessPOSIX + CrashReason.cpp + ProcessMessage.cpp + ProcessPOSIXLog.cpp + ) diff --git a/source/Plugins/Process/POSIX/Makefile b/source/Plugins/Process/POSIX/Makefile new file mode 100644 index 000000000000..e8ac3a8ae0ed --- /dev/null +++ b/source/Plugins/Process/POSIX/Makefile @@ -0,0 +1,32 @@ +##===- source/Plugins/Process/POSIX/Makefile ---------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessPOSIX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/../../Makefile.config + +# Extend the include path so we may locate UnwindLLDB.h +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility + +ifeq ($(HOST_OS),Linux) +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/Linux + +# Disable warning for now as offsetof is used with an index into a structure member array +# in defining register info tables. +CPP.Flags += -Wno-extended-offsetof +endif + +ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD)) +# Extend the include path so we may locate ProcessMonitor +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/FreeBSD +endif + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Utility/CMakeLists.txt b/source/Plugins/Process/Utility/CMakeLists.txt new file mode 100644 index 000000000000..4a847b4f966a --- /dev/null +++ b/source/Plugins/Process/Utility/CMakeLists.txt @@ -0,0 +1,47 @@ +include_directories(../../../Utility/) + +add_lldb_library(lldbPluginProcessUtility + DynamicRegisterInfo.cpp + FreeBSDSignals.cpp + GDBRemoteSignals.cpp + HistoryThread.cpp + HistoryUnwind.cpp + InferiorCallPOSIX.cpp + LinuxSignals.cpp + MipsLinuxSignals.cpp + NetBSDSignals.cpp + RegisterContextDarwin_arm.cpp + RegisterContextDarwin_arm64.cpp + RegisterContextDarwin_i386.cpp + RegisterContextDarwin_x86_64.cpp + RegisterContextDummy.cpp + RegisterContextFreeBSD_arm.cpp + RegisterContextFreeBSD_arm64.cpp + RegisterContextFreeBSD_i386.cpp + RegisterContextFreeBSD_mips64.cpp + RegisterContextFreeBSD_powerpc.cpp + RegisterContextFreeBSD_x86_64.cpp + RegisterContextHistory.cpp + RegisterContextLinux_arm.cpp + RegisterContextLinux_arm64.cpp + RegisterContextLinux_i386.cpp + RegisterContextLinux_x86_64.cpp + RegisterContextLinux_mips64.cpp + RegisterContextLinux_mips.cpp + RegisterContextLLDB.cpp + RegisterContextMacOSXFrameBackchain.cpp + RegisterContextMach_arm.cpp + RegisterContextMach_i386.cpp + RegisterContextMach_x86_64.cpp + RegisterContextMemory.cpp + RegisterContextPOSIX_arm.cpp + RegisterContextPOSIX_arm64.cpp + RegisterContextPOSIX_mips64.cpp + RegisterContextPOSIX_powerpc.cpp + RegisterContextPOSIX_x86.cpp + RegisterContextThreadMemory.cpp + StopInfoMachException.cpp + ThreadMemory.cpp + UnwindLLDB.cpp + UnwindMacOSXFrameBackchain.cpp + ) diff --git a/source/Plugins/Process/Utility/Makefile b/source/Plugins/Process/Utility/Makefile new file mode 100644 index 000000000000..eb0caf3f92fe --- /dev/null +++ b/source/Plugins/Process/Utility/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Utility/Makefile ---------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessUtility +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Windows/Common/CMakeLists.txt b/source/Plugins/Process/Windows/Common/CMakeLists.txt new file mode 100644 index 000000000000..5a53c3c3300c --- /dev/null +++ b/source/Plugins/Process/Windows/Common/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories(.) +include_directories(../../Utility) + +set(PROC_WINDOWS_COMMON_SOURCES + RegisterContextWindows.cpp + ProcessWindows.cpp + ProcessWindowsLog.cpp + TargetThreadWindows.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_COMMON_SOURCES ${PROC_WINDOWS_COMMON_SOURCES} + x86/RegisterContextWindows_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_COMMON_SOURCES ${PROC_WINDOWS_COMMON_SOURCES} + x64/RegisterContextWindows_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWindowsCommon + ${PROC_WINDOWS_COMMON_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/Common/ExceptionRecord.h b/source/Plugins/Process/Windows/Common/ExceptionRecord.h new file mode 100644 index 000000000000..79dae3fca4e8 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ExceptionRecord.h @@ -0,0 +1,99 @@ +//===-- ExceptionRecord.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_ExceptionRecord_H_ +#define liblldb_Plugins_Process_Windows_ExceptionRecord_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Host/windows/windows.h" +#include + +#include +#include + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +// ExceptionRecord +// +// ExceptionRecord defines an interface which allows implementors to receive +// notification of events that happen in a debugged process. +//---------------------------------------------------------------------- +class ExceptionRecord +{ + public: + ExceptionRecord(const EXCEPTION_RECORD &record, lldb::tid_t thread_id) + { + m_code = record.ExceptionCode; + m_continuable = (record.ExceptionFlags == 0); + if (record.ExceptionRecord) + m_next_exception.reset(new ExceptionRecord(*record.ExceptionRecord, thread_id)); + m_exception_addr = reinterpret_cast(record.ExceptionAddress); + m_thread_id = thread_id; + m_arguments.assign(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters); + } + + // MINIDUMP_EXCEPTIONs are almost identical to EXCEPTION_RECORDs. + ExceptionRecord(const MINIDUMP_EXCEPTION &record, lldb::tid_t thread_id) : + m_code(record.ExceptionCode), + m_continuable(record.ExceptionFlags == 0), + m_next_exception(nullptr), + m_exception_addr(static_cast(record.ExceptionAddress)), + m_thread_id(thread_id), + m_arguments(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters) + { + // Set up link to nested exception. + if (record.ExceptionRecord) + { + m_next_exception.reset(new ExceptionRecord(*reinterpret_cast(record.ExceptionRecord), + thread_id)); + } + } + + virtual ~ExceptionRecord() {} + + DWORD + GetExceptionCode() const + { + return m_code; + } + bool + IsContinuable() const + { + return m_continuable; + } + const ExceptionRecord * + GetNextException() const + { + return m_next_exception.get(); + } + lldb::addr_t + GetExceptionAddress() const + { + return m_exception_addr; + } + + lldb::tid_t + GetThreadID() const + { + return m_thread_id; + } + + private: + DWORD m_code; + bool m_continuable; + std::shared_ptr m_next_exception; + lldb::addr_t m_exception_addr; + lldb::tid_t m_thread_id; + std::vector m_arguments; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/source/Plugins/Process/Windows/Common/ProcessWindows.cpp new file mode 100644 index 000000000000..0e6900d8fb7f --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -0,0 +1,100 @@ +//===-- ProcessWindows.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWindows.h" + +// Other libraries and framework includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +namespace lldb_private +{ + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindows::ProcessWindows(lldb::TargetSP target_sp, Listener &listener) + : lldb_private::Process(target_sp, listener) +{ +} + +ProcessWindows::~ProcessWindows() +{ +} + +size_t +ProcessWindows::GetSTDOUT(char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("GetSTDOUT unsupported on Windows"); + return 0; +} + +size_t +ProcessWindows::GetSTDERR(char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("GetSTDERR unsupported on Windows"); + return 0; +} + +size_t +ProcessWindows::PutSTDIN(const char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("PutSTDIN unsupported on Windows"); + return 0; +} + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + + +lldb::addr_t +ProcessWindows::GetImageInfoAddress() +{ + Target &target = GetTarget(); + ObjectFile *obj_file = target.GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(&target); + if (addr.IsValid()) + return addr.GetLoadAddress(&target); + else + return LLDB_INVALID_ADDRESS; +} + +// The Windows page protection bits are NOT independent masks that can be bitwise-ORed +// together. For example, PAGE_EXECUTE_READ is not (PAGE_EXECUTE | PAGE_READ). +// To test for an access type, it's necessary to test for any of the bits that provide +// that access type. +bool +ProcessWindows::IsPageReadable(uint32_t protect) +{ + return (protect & PAGE_NOACCESS) == 0; +} + +bool +ProcessWindows::IsPageWritable(uint32_t protect) +{ + return (protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY)) != 0; +} + +bool +ProcessWindows::IsPageExecutable(uint32_t protect) +{ + return (protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; +} + +} diff --git a/source/Plugins/Process/Windows/Common/ProcessWindows.h b/source/Plugins/Process/Windows/Common/ProcessWindows.h new file mode 100644 index 000000000000..2a437c0ca909 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindows.h @@ -0,0 +1,52 @@ +//===-- ProcessWindows.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ +#define liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ + +// Other libraries and framework includes +#include "lldb/lldb-forward.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +namespace lldb_private +{ + +class ProcessWindows : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessWindows(lldb::TargetSP target_sp, + lldb_private::Listener &listener); + + ~ProcessWindows(); + + size_t GetSTDOUT(char *buf, size_t buf_size, lldb_private::Error &error) override; + size_t GetSTDERR(char *buf, size_t buf_size, lldb_private::Error &error) override; + size_t PutSTDIN(const char *buf, size_t buf_size, lldb_private::Error &error) override; + + lldb::addr_t GetImageInfoAddress() override; + +protected: + // These decode the page protection bits. + static bool + IsPageReadable(uint32_t protect); + + static bool + IsPageWritable(uint32_t protect); + + static bool + IsPageExecutable(uint32_t protect); +}; + +} + +#endif // liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ diff --git a/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp new file mode 100644 index 000000000000..47722c5146b2 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp @@ -0,0 +1,194 @@ +//===-- ProcessWindowsLog.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWindowsLog.h" + +#include + +#include "lldb/Core/StreamFile.h" +#include "lldb/Interpreter/Args.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_log_sp the first time this function is +// called. +static bool g_log_enabled = false; +static Log * g_log = nullptr; + +static llvm::ManagedStatic g_once_flag; + +void +ProcessWindowsLog::Initialize() +{ + static ConstString g_name("windows"); + + std::call_once(*g_once_flag, [](){ + Log::Callbacks log_callbacks = { + DisableLog, + EnableLog, + ListLogCategories + }; + + Log::RegisterLogChannel(g_name, log_callbacks); + RegisterPluginName(g_name); + }); +} + +void +ProcessWindowsLog::Terminate() +{ +} + +Log * +ProcessWindowsLog::GetLog() +{ + return (g_log_enabled) ? g_log : nullptr; +} + +bool +ProcessWindowsLog::TestLogFlags(uint32_t mask, LogMaskReq req) +{ + Log *log = GetLog(); + if (!log) + return false; + + uint32_t log_mask = log->GetMask().Get(); + if (req == LogMaskReq::All) + return ((log_mask & mask) == mask); + else + return (log_mask & mask); +} + +static uint32_t +GetFlagBits(const char *arg) +{ + if (::strcasecmp(arg, "all") == 0 ) return WINDOWS_LOG_ALL; + else if (::strcasecmp(arg, "break") == 0 ) return WINDOWS_LOG_BREAKPOINTS; + else if (::strcasecmp(arg, "event") == 0 ) return WINDOWS_LOG_EVENT; + else if (::strcasecmp(arg, "exception") == 0 ) return WINDOWS_LOG_EXCEPTION; + else if (::strcasecmp(arg, "memory") == 0 ) return WINDOWS_LOG_MEMORY; + else if (::strcasecmp(arg, "process") == 0 ) return WINDOWS_LOG_PROCESS; + else if (::strcasecmp(arg, "registers") == 0 ) return WINDOWS_LOG_REGISTERS; + else if (::strcasecmp(arg, "step") == 0 ) return WINDOWS_LOG_STEP; + else if (::strcasecmp(arg, "thread") == 0 ) return WINDOWS_LOG_THREAD; + else if (::strcasecmp(arg, "verbose") == 0 ) return WINDOWS_LOG_VERBOSE; + return 0; +} + +void +ProcessWindowsLog::DisableLog(const char **args, Stream *feedback_strm) +{ + Log *log (GetLog()); + if (log) + { + uint32_t flag_bits = 0; + + if (args[0] != nullptr) + { + flag_bits = log->GetMask().Get(); + for (; args[0]; args++) + { + const char *arg = args[0]; + uint32_t bits = GetFlagBits(arg); + + if (bits) + { + flag_bits &= ~bits; + } + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories(feedback_strm); + } + } + } + + log->GetMask().Reset(flag_bits); + if (flag_bits == 0) + { + g_log_enabled = false; + log->SetStream(lldb::StreamSP()); + } + } + + return; +} + +Log * +ProcessWindowsLog::EnableLog(StreamSP &log_stream_sp, uint32_t log_options, const char **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 = 0; + if (g_log) + flag_bits = g_log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + bool got_unknown_category = false; + for (; args[0]; args++) + { + const char *arg = args[0]; + uint32_t bits = GetFlagBits(arg); + + if (bits) + { + flag_bits |= bits; + } + else + { + feedback_strm->Printf("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 = WINDOWS_LOG_ALL; + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + g_log_enabled = true; + } + return g_log; +} + +void +ProcessWindowsLog::ListLogCategories(Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " break - log breakpoints\n" + " event - log low level debugger events\n" + " exception - log exception information\n" + " memory - log memory reads and writes\n" + " process - log process events and activities\n" + " registers - log register read/writes\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n", + ProcessWindowsLog::m_pluginname); +} + +const char *ProcessWindowsLog::m_pluginname = ""; diff --git a/source/Plugins/Process/Windows/Common/ProcessWindowsLog.h b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.h new file mode 100644 index 000000000000..d798d131faeb --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.h @@ -0,0 +1,96 @@ +//===-- ProcessWindowsLog.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessWindowsLog_h_ +#define liblldb_ProcessWindowsLog_h_ + +#include "lldb/Core/Log.h" + +#define WINDOWS_LOG_VERBOSE (1u << 0) +#define WINDOWS_LOG_PROCESS (1u << 1) // Log process operations +#define WINDOWS_LOG_EXCEPTION (1u << 1) // Log exceptions +#define WINDOWS_LOG_THREAD (1u << 2) // Log thread operations +#define WINDOWS_LOG_MEMORY (1u << 3) // Log memory reads/writes calls +#define WINDOWS_LOG_BREAKPOINTS (1u << 4) // Log breakpoint operations +#define WINDOWS_LOG_STEP (1u << 5) // Log step operations +#define WINDOWS_LOG_REGISTERS (1u << 6) // Log register operations +#define WINDOWS_LOG_EVENT (1u << 7) // Low level debug events +#define WINDOWS_LOG_ALL (UINT32_MAX) + +enum class LogMaskReq +{ + All, + Any +}; + +class ProcessWindowsLog +{ + static const char *m_pluginname; + +public: + // --------------------------------------------------------------------- + // Public Static Methods + // --------------------------------------------------------------------- + static void + Initialize(); + + static void + Terminate(); + + static void + RegisterPluginName(const char *pluginName) + { + m_pluginname = pluginName; + } + + static void + RegisterPluginName(lldb_private::ConstString pluginName) + { + m_pluginname = pluginName.GetCString(); + } + + static bool + TestLogFlags(uint32_t mask, LogMaskReq req); + + static lldb_private::Log * + GetLog(); + + static void + DisableLog(const char **args, lldb_private::Stream *feedback_strm); + + static lldb_private::Log * + EnableLog(lldb::StreamSP &log_stream_sp, uint32_t log_options, + const char **args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories(lldb_private::Stream *strm); +}; + +#define WINLOGF_IF(Flags, Req, Method, ...) \ + { \ + if (ProcessWindowsLog::TestLogFlags(Flags, Req)) \ + { \ + Log *log = ProcessWindowsLog::GetLog(); \ + if (log) \ + log->Method(__VA_ARGS__); \ + } \ + } + +#define WINLOG_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Printf, __VA_ARGS__) +#define WINLOG_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Printf, __VA_ARGS__) +#define WINLOGV_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Verbose, __VA_ARGS__) +#define WINLOGV_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Verbose, __VA_ARGS__) +#define WINLOGD_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Debug, __VA_ARGS__) +#define WINLOGD_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Debug, __VA_ARGS__) +#define WINERR_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Error, __VA_ARGS__) +#define WINERR_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Error, __VA_ARGS__) +#define WINWARN_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Warning, __VA_ARGS__) +#define WINWARN_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Warning, __VA_ARGS__) + +#endif // liblldb_ProcessWindowsLog_h_ diff --git a/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp b/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp new file mode 100644 index 000000000000..d61675f09b1b --- /dev/null +++ b/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp @@ -0,0 +1,155 @@ +//===-- RegisterContextWindows.cpp ------------------------------*- 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-types.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "ProcessWindowsLog.h" +#include "RegisterContextWindows.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +const DWORD kWinContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows::RegisterContextWindows(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx) + , m_context() + , m_context_stale(true) +{ +} + +RegisterContextWindows::~RegisterContextWindows() +{ +} + +void +RegisterContextWindows::InvalidateAllRegisters() +{ + m_context_stale = true; +} + +bool +RegisterContextWindows::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + if (!CacheAllRegisterValues()) + return false; + if (data_sp->GetByteSize() < sizeof(m_context)) + { + data_sp.reset(new DataBufferHeap(sizeof(CONTEXT), 0)); + } + memcpy(data_sp->GetBytes(), &m_context, sizeof(m_context)); + return true; +} + +bool +RegisterContextWindows::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + assert(data_sp->GetByteSize() >= sizeof(m_context)); + memcpy(&m_context, data_sp->GetBytes(), sizeof(m_context)); + + TargetThreadWindows &wthread = static_cast(m_thread); + if (!::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context)) + return false; + + return true; +} + +uint32_t +RegisterContextWindows::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) +{ + const uint32_t num_regs = GetRegisterCount(); + + assert(kind < kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} + +//------------------------------------------------------------------ +// Subclasses can these functions if desired +//------------------------------------------------------------------ +uint32_t +RegisterContextWindows::NumSupportedHardwareBreakpoints() +{ + // Support for hardware breakpoints not yet implemented. + return 0; +} + +uint32_t +RegisterContextWindows::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) +{ + return 0; +} + +bool +RegisterContextWindows::ClearHardwareBreakpoint(uint32_t hw_idx) +{ + return false; +} + +uint32_t +RegisterContextWindows::NumSupportedHardwareWatchpoints() +{ + // Support for hardware watchpoints not yet implemented. + return 0; +} + +uint32_t +RegisterContextWindows::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) +{ + return 0; +} + +bool +RegisterContextWindows::ClearHardwareWatchpoint(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextWindows::HardwareSingleStep(bool enable) +{ + return false; +} + +bool +RegisterContextWindows::CacheAllRegisterValues() +{ + if (!m_context_stale) + return true; + + TargetThreadWindows &wthread = static_cast(m_thread); + memset(&m_context, 0, sizeof(m_context)); + m_context.ContextFlags = kWinContextFlags; + if (!::GetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context)) + { + WINERR_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext failed with error %u while caching register values.", + ::GetLastError()); + return false; + } + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext successfully updated the register values.", ::GetLastError()); + m_context_stale = false; + return true; +} diff --git a/source/Plugins/Process/Windows/Common/RegisterContextWindows.h b/source/Plugins/Process/Windows/Common/RegisterContextWindows.h new file mode 100644 index 000000000000..66b7a9004ead --- /dev/null +++ b/source/Plugins/Process/Windows/Common/RegisterContextWindows.h @@ -0,0 +1,67 @@ +//===-- RegisterContextWindows.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_H_ +#define liblldb_RegisterContextWindows_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Target/RegisterContext.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows : public lldb_private::RegisterContext +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + void InvalidateAllRegisters() override; + + bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; + + //------------------------------------------------------------------ + // Subclasses can override these functions if desired + //------------------------------------------------------------------ + uint32_t NumSupportedHardwareBreakpoints() override; + + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + bool HardwareSingleStep(bool enable) override; + + protected: + virtual bool CacheAllRegisterValues(); + + CONTEXT m_context; + bool m_context_stale; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindows_H_ diff --git a/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp new file mode 100644 index 000000000000..dcb6f0c72435 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp @@ -0,0 +1,98 @@ +//===-- TargetThreadWindows.cpp----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Core/Logging.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/RegisterContext.h" + +#include "TargetThreadWindows.h" +#include "ProcessWindows.h" +#include "ProcessWindowsLog.h" +#include "UnwindLLDB.h" + +using namespace lldb; +using namespace lldb_private; + +TargetThreadWindows::TargetThreadWindows(ProcessWindows &process, const HostThread &thread) + : Thread(process, thread.GetNativeThread().GetThreadId()) + , m_host_thread(thread) +{ +} + +TargetThreadWindows::~TargetThreadWindows() +{ + DestroyThread(); +} + +void +TargetThreadWindows::RefreshStateAfterStop() +{ + ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); + SetState(eStateStopped); + GetRegisterContext()->InvalidateIfNeeded(false); +} + +void +TargetThreadWindows::WillResume(lldb::StateType resume_state) +{ +} + +void +TargetThreadWindows::DidStop() +{ +} + +bool +TargetThreadWindows::CalculateStopInfo() +{ + SetStopInfo(m_stop_info_sp); + return true; +} + +Unwind * +TargetThreadWindows::GetUnwinder() +{ + // FIXME: Implement an unwinder based on the Windows unwinder exposed through DIA SDK. + if (m_unwinder_ap.get() == NULL) + m_unwinder_ap.reset(new UnwindLLDB(*this)); + return m_unwinder_ap.get(); +} + +bool +TargetThreadWindows::DoResume() +{ + StateType resume_state = GetTemporaryResumeState(); + StateType current_state = GetState(); + if (resume_state == current_state) + return true; + + if (resume_state == eStateStepping) + { + uint32_t flags_index = GetRegisterContext()->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + uint64_t flags_value = GetRegisterContext()->ReadRegisterAsUnsigned(flags_index, 0); + flags_value |= 0x100; // Set the trap flag on the CPU + GetRegisterContext()->WriteRegisterFromUnsigned(flags_index, flags_value); + } + + if (resume_state == eStateStepping || resume_state == eStateRunning) + { + DWORD previous_suspend_count = 0; + HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); + do + { + previous_suspend_count = ::ResumeThread(thread_handle); + } while (previous_suspend_count > 0); + } + return true; +} diff --git a/source/Plugins/Process/Windows/Common/TargetThreadWindows.h b/source/Plugins/Process/Windows/Common/TargetThreadWindows.h new file mode 100644 index 000000000000..701b56b6d26a --- /dev/null +++ b/source/Plugins/Process/Windows/Common/TargetThreadWindows.h @@ -0,0 +1,50 @@ +//===-- TargetThreadWindows.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_TargetThreadWindows_H_ +#define liblldb_Plugins_Process_Windows_TargetThreadWindows_H_ + +//#include "ForwardDecl.h" +#include "lldb/lldb-forward.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Thread.h" + +namespace lldb_private +{ +class ProcessWindows; +class HostThread; +class StackFrame; + +class TargetThreadWindows : public lldb_private::Thread +{ + public: + TargetThreadWindows(ProcessWindows &process, const HostThread &thread); + virtual ~TargetThreadWindows(); + + // lldb_private::Thread overrides + void RefreshStateAfterStop() override; + void WillResume(lldb::StateType resume_state) override; + void DidStop() override; + bool CalculateStopInfo() override; + Unwind *GetUnwinder() override; + + bool DoResume(); + + HostThread + GetHostThread() const + { + return m_host_thread; + } + + private: + HostThread m_host_thread; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp new file mode 100644 index 000000000000..103cff4a2a56 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp @@ -0,0 +1,323 @@ +//===-- RegisterContextWindows_x64.cpp --------------------------*- 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-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "RegisterContext_x86.h" +#include "RegisterContextWindows_x64.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define DEFINE_GPR(reg, alt) #reg, alt, 8, 0, eEncodingUint, eFormatHexUppercase +#define DEFINE_GPR_BIN(reg, alt) #reg, alt, 8, 0, eEncodingUint, eFormatBinary + +namespace +{ + +// This enum defines the layout of the global RegisterInfo array. This is necessary because +// lldb register sets are defined in terms of indices into the register array. As such, the +// order of RegisterInfos defined in global registers array must match the order defined here. +// When defining the register set layouts, these values can appear in an arbitrary order, and that +// determines the order that register values are displayed in a dump. +enum RegisterIndex +{ + eRegisterIndexRax, + eRegisterIndexRbx, + eRegisterIndexRcx, + eRegisterIndexRdx, + eRegisterIndexRdi, + eRegisterIndexRsi, + eRegisterIndexR8, + eRegisterIndexR9, + eRegisterIndexR10, + eRegisterIndexR11, + eRegisterIndexR12, + eRegisterIndexR13, + eRegisterIndexR14, + eRegisterIndexR15, + eRegisterIndexRbp, + eRegisterIndexRsp, + eRegisterIndexRip, + eRegisterIndexRflags +}; + +// Array of all register information supported by Windows x86 +RegisterInfo g_register_infos[] = { + // Macro auto defines most stuff eh_frame DWARF GENERIC + // GDB LLDB VALUE REGS INVALIDATE REGS + // ================================ ========================= ====================== ========================= + // =================== ================= ========== =============== + {DEFINE_GPR(rax, nullptr), + {dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rax_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rbx, nullptr), + {dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rbx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rcx, nullptr), + {dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rcx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rdx, nullptr), + {dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rdx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rdi, nullptr), + {dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rdi_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rsi, nullptr), + {dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rsi_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r8, nullptr), + {dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r8_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r9, nullptr), + {dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r9_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r10, nullptr), + {dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r10_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r11, nullptr), + {dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r11_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r12, nullptr), + {dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r12_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r13, nullptr), + {dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r13_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r14, nullptr), + {dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r14_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r15, nullptr), + {dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r15_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rbp, "fp"), + {dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, lldb_rbp_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rsp, "sp"), + {dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, lldb_rsp_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rip, "pc"), + {dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_rip_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR_BIN(eflags, "flags"), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, lldb_rflags_x86_64}, + nullptr, + nullptr}, +}; + +// Array of lldb register numbers used to define the set of all General Purpose Registers +uint32_t g_gpr_reg_indices[] = {eRegisterIndexRax, eRegisterIndexRbx, eRegisterIndexRcx, eRegisterIndexRdx, + eRegisterIndexRdi, eRegisterIndexRsi, eRegisterIndexR8, eRegisterIndexR9, + eRegisterIndexR10, eRegisterIndexR11, eRegisterIndexR12, eRegisterIndexR13, + eRegisterIndexR14, eRegisterIndexR15, eRegisterIndexRbp, eRegisterIndexRsp, + eRegisterIndexRip, eRegisterIndexRflags}; + +RegisterSet g_register_sets[] = { + {"General Purpose Registers", "gpr", llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices}, +}; +} + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows_x64::RegisterContextWindows_x64(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows(thread, concrete_frame_idx) +{ +} + +RegisterContextWindows_x64::~RegisterContextWindows_x64() +{ +} + +size_t +RegisterContextWindows_x64::GetRegisterCount() +{ + return llvm::array_lengthof(g_register_infos); +} + +const RegisterInfo * +RegisterContextWindows_x64::GetRegisterInfoAtIndex(size_t reg) +{ + return &g_register_infos[reg]; +} + +size_t +RegisterContextWindows_x64::GetRegisterSetCount() +{ + return llvm::array_lengthof(g_register_sets); +} + +const RegisterSet * +RegisterContextWindows_x64::GetRegisterSet(size_t reg_set) +{ + return &g_register_sets[reg_set]; +} + +bool +RegisterContextWindows_x64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + reg_value.SetUInt64(m_context.Rax); + break; + case lldb_rbx_x86_64: + reg_value.SetUInt64(m_context.Rbx); + break; + case lldb_rcx_x86_64: + reg_value.SetUInt64(m_context.Rcx); + break; + case lldb_rdx_x86_64: + reg_value.SetUInt64(m_context.Rdx); + break; + case lldb_rdi_x86_64: + reg_value.SetUInt64(m_context.Rdi); + break; + case lldb_rsi_x86_64: + reg_value.SetUInt64(m_context.Rsi); + break; + case lldb_r8_x86_64: + reg_value.SetUInt64(m_context.R8); + break; + case lldb_r9_x86_64: + reg_value.SetUInt64(m_context.R9); + break; + case lldb_r10_x86_64: + reg_value.SetUInt64(m_context.R10); + break; + case lldb_r11_x86_64: + reg_value.SetUInt64(m_context.R11); + break; + case lldb_r12_x86_64: + reg_value.SetUInt64(m_context.R12); + break; + case lldb_r13_x86_64: + reg_value.SetUInt64(m_context.R13); + break; + case lldb_r14_x86_64: + reg_value.SetUInt64(m_context.R14); + break; + case lldb_r15_x86_64: + reg_value.SetUInt64(m_context.R15); + break; + case lldb_rbp_x86_64: + reg_value.SetUInt64(m_context.Rbp); + break; + case lldb_rsp_x86_64: + reg_value.SetUInt64(m_context.Rsp); + break; + case lldb_rip_x86_64: + reg_value.SetUInt64(m_context.Rip); + break; + case lldb_rflags_x86_64: + reg_value.SetUInt64(m_context.EFlags); + break; + } + return true; +} + +bool +RegisterContextWindows_x64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + // Since we cannot only write a single register value to the inferior, we need to make sure + // our cached copy of the register values are fresh. Otherwise when writing EAX, for example, + // we may also overwrite some other register with a stale value. + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + m_context.Rax = reg_value.GetAsUInt64(); + break; + case lldb_rbx_x86_64: + m_context.Rbx = reg_value.GetAsUInt64(); + break; + case lldb_rcx_x86_64: + m_context.Rcx = reg_value.GetAsUInt64(); + break; + case lldb_rdx_x86_64: + m_context.Rdx = reg_value.GetAsUInt64(); + break; + case lldb_rdi_x86_64: + m_context.Rdi = reg_value.GetAsUInt64(); + break; + case lldb_rsi_x86_64: + m_context.Rsi = reg_value.GetAsUInt64(); + break; + case lldb_r8_x86_64: + m_context.R8 = reg_value.GetAsUInt64(); + break; + case lldb_r9_x86_64: + m_context.R9 = reg_value.GetAsUInt64(); + break; + case lldb_r10_x86_64: + m_context.R10 = reg_value.GetAsUInt64(); + break; + case lldb_r11_x86_64: + m_context.R11 = reg_value.GetAsUInt64(); + break; + case lldb_r12_x86_64: + m_context.R12 = reg_value.GetAsUInt64(); + break; + case lldb_r13_x86_64: + m_context.R13 = reg_value.GetAsUInt64(); + break; + case lldb_r14_x86_64: + m_context.R14 = reg_value.GetAsUInt64(); + break; + case lldb_r15_x86_64: + m_context.R15 = reg_value.GetAsUInt64(); + break; + case lldb_rbp_x86_64: + m_context.Rbp = reg_value.GetAsUInt64(); + break; + case lldb_rsp_x86_64: + m_context.Rsp = reg_value.GetAsUInt64(); + break; + case lldb_rip_x86_64: + m_context.Rip = reg_value.GetAsUInt64(); + break; + case lldb_rflags_x86_64: + m_context.EFlags = reg_value.GetAsUInt64(); + break; + } + + // Physically update the registers in the target process. + TargetThreadWindows &wthread = static_cast(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} diff --git a/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h new file mode 100644 index 000000000000..e69179d99c6c --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h @@ -0,0 +1,48 @@ +//===-- RegisterContextWindows_x64.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_x64_H_ +#define liblldb_RegisterContextWindows_x64_H_ + +#include "lldb/lldb-forward.h" +#include "RegisterContextWindows.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows_x64 : public RegisterContextWindows +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows_x64(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows_x64(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindows_x64_H_ diff --git a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp new file mode 100644 index 000000000000..e57e1effec9c --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp @@ -0,0 +1,178 @@ +//===-- RegisterContextWindows_x86.cpp --------------------------*- 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-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "ProcessWindowsLog.h" +#include "RegisterContext_x86.h" +#include "RegisterContextWindows_x86.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define DEFINE_GPR(reg, alt) #reg, alt, 4, 0, eEncodingUint, eFormatHexUppercase +#define DEFINE_GPR_BIN(reg, alt) #reg, alt, 4, 0, eEncodingUint, eFormatBinary + +namespace +{ + +// This enum defines the layout of the global RegisterInfo array. This is necessary because +// lldb register sets are defined in terms of indices into the register array. As such, the +// order of RegisterInfos defined in global registers array must match the order defined here. +// When defining the register set layouts, these values can appear in an arbitrary order, and that +// determines the order that register values are displayed in a dump. +enum RegisterIndex +{ + eRegisterIndexEax, + eRegisterIndexEbx, + eRegisterIndexEcx, + eRegisterIndexEdx, + eRegisterIndexEdi, + eRegisterIndexEsi, + eRegisterIndexEbp, + eRegisterIndexEsp, + eRegisterIndexEip, + eRegisterIndexEflags +}; + +// Array of all register information supported by Windows x86 +RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff eh_frame DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS +// ============================== ======================= =================== ========================= =================== ================= ========== =============== + { DEFINE_GPR(eax, nullptr), { ehframe_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_eax_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ebx, nullptr), { ehframe_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ebx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ecx, nullptr), { ehframe_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ecx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(edx, nullptr), { ehframe_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(edi, nullptr), { ehframe_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edi_i386 }, nullptr, nullptr}, + { DEFINE_GPR(esi, nullptr), { ehframe_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_esi_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ebp, "fp"), { ehframe_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, lldb_ebp_i386 }, nullptr, nullptr}, + { DEFINE_GPR(esp, "sp"), { ehframe_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, lldb_esp_i386 }, nullptr, nullptr}, + { DEFINE_GPR(eip, "pc"), { ehframe_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_eip_i386 }, nullptr, nullptr}, + { DEFINE_GPR_BIN(eflags, "flags"), { ehframe_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, lldb_eflags_i386}, nullptr, nullptr}, +}; + +// Array of lldb register numbers used to define the set of all General Purpose Registers +uint32_t g_gpr_reg_indices[] = +{ + eRegisterIndexEax, + eRegisterIndexEbx, + eRegisterIndexEcx, + eRegisterIndexEdx, + eRegisterIndexEdi, + eRegisterIndexEsi, + eRegisterIndexEbp, + eRegisterIndexEsp, + eRegisterIndexEip, + eRegisterIndexEflags +}; + +RegisterSet g_register_sets[] = { + {"General Purpose Registers", "gpr", llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices}, +}; +} + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows_x86::RegisterContextWindows_x86(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows(thread, concrete_frame_idx) +{ +} + +RegisterContextWindows_x86::~RegisterContextWindows_x86() +{ +} + +size_t +RegisterContextWindows_x86::GetRegisterCount() +{ + return llvm::array_lengthof(g_register_infos); +} + +const RegisterInfo * +RegisterContextWindows_x86::GetRegisterInfoAtIndex(size_t reg) +{ + return &g_register_infos[reg]; +} + +size_t +RegisterContextWindows_x86::GetRegisterSetCount() +{ + return llvm::array_lengthof(g_register_sets); +} + +const RegisterSet * +RegisterContextWindows_x86::GetRegisterSet(size_t reg_set) +{ + return &g_register_sets[reg_set]; +} + +bool +RegisterContextWindows_x86::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!CacheAllRegisterValues()) + return false; + + uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + switch (reg) + { + case lldb_eax_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EAX", m_context.Eax); + reg_value.SetUInt32(m_context.Eax); + break; + case lldb_ebx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBX", m_context.Ebx); + reg_value.SetUInt32(m_context.Ebx); + break; + case lldb_ecx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ECX", m_context.Ecx); + reg_value.SetUInt32(m_context.Ecx); + break; + case lldb_edx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDX", m_context.Edx); + reg_value.SetUInt32(m_context.Edx); + break; + case lldb_edi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDI", m_context.Edi); + reg_value.SetUInt32(m_context.Edi); + break; + case lldb_esi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESI", m_context.Esi); + reg_value.SetUInt32(m_context.Esi); + break; + case lldb_ebp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBP", m_context.Ebp); + reg_value.SetUInt32(m_context.Ebp); + break; + case lldb_esp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESP", m_context.Esp); + reg_value.SetUInt32(m_context.Esp); + break; + case lldb_eip_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EIP", m_context.Eip); + reg_value.SetUInt32(m_context.Eip); + break; + case lldb_eflags_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EFLAGS", m_context.EFlags); + reg_value.SetUInt32(m_context.EFlags); + break; + default: + WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Requested unknown register %u", reg); + break; + } + return true; +} diff --git a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h new file mode 100644 index 000000000000..7d854ef64a5c --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h @@ -0,0 +1,48 @@ +//===-- RegisterContextWindows_x86.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_x86_H_ +#define liblldb_RegisterContextWindows_x86_H_ + +#include "lldb/lldb-forward.h" +#include "RegisterContextWindows.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows_x86 : public RegisterContextWindows +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows_x86(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows_x86(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindows_x86_H_ diff --git a/source/Plugins/Process/Windows/Live/CMakeLists.txt b/source/Plugins/Process/Windows/Live/CMakeLists.txt new file mode 100644 index 000000000000..bdb680ad0bee --- /dev/null +++ b/source/Plugins/Process/Windows/Live/CMakeLists.txt @@ -0,0 +1,24 @@ +include_directories(.) +include_directories(../../Utility) +include_directories(../Common) + +set(PROC_WINDOWS_SOURCES + DebuggerThread.cpp + LocalDebugDelegate.cpp + ProcessWindowsLive.cpp + TargetThreadWindowsLive.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} + x86/RegisterContextWindowsLive_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} + x64/RegisterContextWindowsLive_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWindows + ${PROC_WINDOWS_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.cpp b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp new file mode 100644 index 000000000000..d058a412c896 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp @@ -0,0 +1,546 @@ +//===-- DebuggerThread.DebuggerThread --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "IDebugDelegate.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/HostProcessWindows.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/Process.h" + +#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +struct DebugLaunchContext +{ + DebugLaunchContext(DebuggerThread *thread, const ProcessLaunchInfo &launch_info) + : m_thread(thread) + , m_launch_info(launch_info) + { + } + DebuggerThread *m_thread; + ProcessLaunchInfo m_launch_info; +}; + +struct DebugAttachContext +{ + DebugAttachContext(DebuggerThread *thread, lldb::pid_t pid, const ProcessAttachInfo &attach_info) + : m_thread(thread) + , m_pid(pid) + , m_attach_info(attach_info) + { + } + DebuggerThread *m_thread; + lldb::pid_t m_pid; + ProcessAttachInfo m_attach_info; +}; +} + +DebuggerThread::DebuggerThread(DebugDelegateSP debug_delegate) + : m_debug_delegate(debug_delegate) + , m_image_file(nullptr) + , m_debugging_ended_event(nullptr) + , m_is_shutting_down(false) + , m_pid_to_detach(0) + , m_detached(false) +{ + m_debugging_ended_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); +} + +DebuggerThread::~DebuggerThread() +{ + ::CloseHandle(m_debugging_ended_event); +} + +Error +DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info) +{ + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DebuggerThread::DebugLaunch launching '%s'", launch_info.GetExecutableFile().GetPath().c_str()); + + Error error; + DebugLaunchContext *context = new DebugLaunchContext(this, launch_info); + HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", + DebuggerThreadLaunchRoutine, context, &error)); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DebugLaunch couldn't launch debugger thread. %s", error.AsCString()); + } + + return error; +} + +Error +DebuggerThread::DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread::DebugAttach attaching to '%u'", (DWORD)pid); + + Error error; + DebugAttachContext *context = new DebugAttachContext(this, pid, attach_info); + HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", + DebuggerThreadAttachRoutine, context, &error)); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DebugAttach couldn't attach to process '%u'. %s", (DWORD)pid, + error.AsCString()); + } + + return error; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(void *data) +{ + DebugLaunchContext *context = static_cast(data); + lldb::thread_result_t result = context->m_thread->DebuggerThreadLaunchRoutine(context->m_launch_info); + delete context; + return result; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadAttachRoutine(void *data) +{ + DebugAttachContext *context = static_cast(data); + lldb::thread_result_t result = + context->m_thread->DebuggerThreadAttachRoutine(context->m_pid, context->m_attach_info); + delete context; + return result; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info) +{ + // Grab a shared_ptr reference to this so that we know it won't get deleted until after the + // thread routine has exited. + std::shared_ptr this_ref(shared_from_this()); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to launch '%s' on background thread.", + launch_info.GetExecutableFile().GetPath().c_str()); + + Error error; + ProcessLauncherWindows launcher; + HostProcess process(launcher.LaunchProcess(launch_info, error)); + // If we couldn't create the process, notify waiters immediately. Otherwise enter the debug + // loop and wait until we get the create process debug notification. Note that if the process + // was created successfully, we can throw away the process handle we got from CreateProcess + // because Windows will give us another (potentially more useful?) handle when it sends us the + // CREATE_PROCESS_DEBUG_EVENT. + if (error.Success()) + DebugLoop(); + else + m_debug_delegate->OnDebuggerError(error, 0); + + return 0; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + // Grab a shared_ptr reference to this so that we know it won't get deleted until after the + // thread routine has exited. + std::shared_ptr this_ref(shared_from_this()); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to attach to process '%u' on background thread.", + (DWORD)pid); + + if (!DebugActiveProcess((DWORD)pid)) + { + Error error(::GetLastError(), eErrorTypeWin32); + m_debug_delegate->OnDebuggerError(error, 0); + return 0; + } + + // The attach was successful, enter the debug loop. From here on out, this is no different than + // a create process operation, so all the same comments in DebugLaunch should apply from this + // point out. + DebugLoop(); + + return 0; +} + +Error +DebuggerThread::StopDebugging(bool terminate) +{ + Error error; + + lldb::pid_t pid = m_process.GetProcessId(); + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging('%s') called (inferior=%I64u).", + (terminate ? "true" : "false"), pid); + + // Set m_is_shutting_down to true if it was false. Return if it was already true. + bool expected = false; + if (!m_is_shutting_down.compare_exchange_strong(expected, true)) + return error; + + // Make a copy of the process, since the termination sequence will reset + // DebuggerThread's internal copy and it needs to remain open for the Wait operation. + HostProcess process_copy = m_process; + lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle(); + + if (terminate) + { + // Initiate the termination before continuing the exception, so that the next debug + // event we get is the exit process event, and not some other event. + BOOL terminate_suceeded = TerminateProcess(handle, 0); + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging called TerminateProcess(0x%p, 0) (inferior=%I64u), success='%s'", + handle, pid, (terminate_suceeded ? "true" : "false")); + } + + // If we're stuck waiting for an exception to continue (e.g. the user is at a breakpoint + // messing around in the debugger), continue it now. But only AFTER calling TerminateProcess + // to make sure that the very next call to WaitForDebugEvent is an exit process event. + if (m_active_exception.get()) + { + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, + "StopDebugging masking active exception"); + + ContinueAsyncException(ExceptionResult::MaskException); + } + + if (!terminate) + { + // Indicate that we want to detach. + m_pid_to_detach = GetProcess().GetProcessId(); + + // Force a fresh break so that the detach can happen from the debugger thread. + if (!::DebugBreakProcess(GetProcess().GetNativeProcess().GetSystemHandle())) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + } + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid); + + DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000); + if (wait_result != WAIT_OBJECT_0) + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging WaitForSingleObject(0x%p, 5000) returned %u", + m_debugging_ended_event, wait_result); + } + else + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging detach from process %u completed successfully.", pid); + } + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging encountered an error while trying to stop process %u. %s", + pid, error.AsCString()); + } + return error; +} + +void +DebuggerThread::ContinueAsyncException(ExceptionResult result) +{ + if (!m_active_exception.get()) + return; + + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, + "ContinueAsyncException called for inferior process %I64u, broadcasting.", + m_process.GetProcessId()); + + m_active_exception.reset(); + m_exception_pred.SetValue(result, eBroadcastAlways); +} + +void +DebuggerThread::FreeProcessHandles() +{ + m_process = HostProcess(); + m_main_thread = HostThread(); + if (m_image_file) + { + ::CloseHandle(m_image_file); + m_image_file = nullptr; + } +} + +void +DebuggerThread::DebugLoop() +{ + DEBUG_EVENT dbe = {0}; + bool should_debug = true; + WINLOG_IFALL(WINDOWS_LOG_EVENT, "Entering WaitForDebugEvent loop"); + while (should_debug) + { + WINLOGD_IFALL(WINDOWS_LOG_EVENT, "Calling WaitForDebugEvent"); + BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE); + if (wait_result) + { + DWORD continue_status = DBG_CONTINUE; + switch (dbe.dwDebugEventCode) + { + case EXCEPTION_DEBUG_EVENT: + { + ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId); + + if (status == ExceptionResult::MaskException) + continue_status = DBG_CONTINUE; + else if (status == ExceptionResult::SendToApplication) + continue_status = DBG_EXCEPTION_NOT_HANDLED; + + break; + } + case CREATE_THREAD_DEBUG_EVENT: + continue_status = HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId); + break; + case CREATE_PROCESS_DEBUG_EVENT: + continue_status = HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId); + break; + case EXIT_THREAD_DEBUG_EVENT: + continue_status = HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId); + break; + case EXIT_PROCESS_DEBUG_EVENT: + continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId); + should_debug = false; + break; + case LOAD_DLL_DEBUG_EVENT: + continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId); + break; + case UNLOAD_DLL_DEBUG_EVENT: + continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId); + break; + case OUTPUT_DEBUG_STRING_EVENT: + continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId); + break; + case RIP_EVENT: + continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId); + if (dbe.u.RipInfo.dwType == SLE_ERROR) + should_debug = false; + break; + } + + WINLOGD_IFALL(WINDOWS_LOG_EVENT, "DebugLoop calling ContinueDebugEvent(%u, %u, %u) on thread %u.", + dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId()); + + ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status); + + if (m_detached) + { + should_debug = false; + } + } + else + { + WINERR_IFALL(WINDOWS_LOG_EVENT, + "DebugLoop returned FALSE from WaitForDebugEvent. Error = %u", + ::GetCurrentThreadId(), ::GetLastError()); + + should_debug = false; + } + } + FreeProcessHandles(); + + WINLOG_IFALL(WINDOWS_LOG_EVENT, "WaitForDebugEvent loop completed, exiting."); + SetEvent(m_debugging_ended_event); +} + +ExceptionResult +DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id) +{ + if (m_is_shutting_down) + { + // A breakpoint that occurs while `m_pid_to_detach` is non-zero is a magic exception that + // we use simply to wake up the DebuggerThread so that we can close out the debug loop. + if (m_pid_to_detach != 0 && info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + { + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_PROCESS, + "Breakpoint exception is cue to detach from process 0x%x", + m_pid_to_detach); + ::DebugActiveProcessStop(m_pid_to_detach); + m_detached = true; + } + + // Don't perform any blocking operations while we're shutting down. That will + // cause TerminateProcess -> WaitForSingleObject to time out. + return ExceptionResult::SendToApplication; + } + + bool first_chance = (info.dwFirstChance != 0); + + m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord, thread_id)); + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION, + "HandleExceptionEvent encountered %s chance exception 0x%x on thread 0x%x", + first_chance ? "first" : "second", info.ExceptionRecord.ExceptionCode, thread_id); + + ExceptionResult result = m_debug_delegate->OnDebugException(first_chance, + *m_active_exception); + m_exception_pred.SetValue(result, eBroadcastNever); + + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, + "DebuggerThread::HandleExceptionEvent waiting for ExceptionPred != BreakInDebugger"); + + m_exception_pred.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, result); + + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, + "DebuggerThread::HandleExceptionEvent got ExceptionPred = %u", + m_exception_pred.GetValue()); + + return result; +} + +DWORD +DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleCreateThreadEvent Thread 0x%x spawned in process %I64u", + thread_id, m_process.GetProcessId()); + HostThread thread(info.hThread); + thread.GetNativeThread().SetOwnsHandle(false); + m_debug_delegate->OnCreateThread(thread); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ + uint32_t process_id = ::GetProcessId(info.hProcess); + + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_PROCESS, "HandleCreateProcessEvent process %u spawned", process_id); + + std::string thread_name; + llvm::raw_string_ostream name_stream(thread_name); + name_stream << "lldb.plugin.process-windows.slave[" << process_id << "]"; + name_stream.flush(); + ThisThread::SetName(thread_name.c_str()); + + // info.hProcess and info.hThread are closed automatically by Windows when + // EXIT_PROCESS_DEBUG_EVENT is received. + m_process = HostProcess(info.hProcess); + ((HostProcessWindows &)m_process.GetNativeProcess()).SetOwnsHandle(false); + m_main_thread = HostThread(info.hThread); + m_main_thread.GetNativeThread().SetOwnsHandle(false); + m_image_file = info.hFile; + + lldb::addr_t load_addr = reinterpret_cast(info.lpBaseOfImage); + m_debug_delegate->OnDebuggerConnected(load_addr); + + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleExitThreadEvent Thread %u exited with code %u in process %I64u", + thread_id, info.dwExitCode, m_process.GetProcessId()); + m_debug_delegate->OnExitThread(thread_id, info.dwExitCode); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleExitProcessEvent process %I64u exited with code %u", + m_process.GetProcessId(), info.dwExitCode); + + m_debug_delegate->OnExitProcess(info.dwExitCode); + + FreeProcessHandles(); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread_id) +{ + if (info.hFile == nullptr) + { + // Not sure what this is, so just ignore it. + WINWARN_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent has a NULL file handle, returning...", + m_process.GetProcessId()); + return DBG_CONTINUE; + } + + std::vector buffer(1); + DWORD required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS); + if (required_size > 0) + { + buffer.resize(required_size + 1); + required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], required_size + 1, VOLUME_NAME_DOS); + llvm::StringRef path_str(&buffer[0]); + const char *path = path_str.data(); + if (path_str.startswith("\\\\?\\")) + path += 4; + + FileSpec file_spec(path, false); + ModuleSpec module_spec(file_spec); + lldb::addr_t load_addr = reinterpret_cast(info.lpBaseOfDll); + + WINLOG_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent DLL '%s' loaded at address 0x%p...", + m_process.GetProcessId(), path, info.lpBaseOfDll); + + m_debug_delegate->OnLoadDll(module_spec, load_addr); + } + else + { + WINERR_IFALL(WINDOWS_LOG_EVENT, + "Inferior %I64u - HandleLoadDllEvent Error %u occurred calling GetFinalPathNameByHandle", + m_process.GetProcessId(), ::GetLastError()); + } + // Windows does not automatically close info.hFile, so we need to do it. + ::CloseHandle(info.hFile); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFALL(WINDOWS_LOG_EVENT, + "HandleUnloadDllEvent process %I64u unloading DLL at addr 0x%p.", + m_process.GetProcessId(), info.lpBaseOfDll); + + m_debug_delegate->OnUnloadDll(reinterpret_cast(info.lpBaseOfDll)); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD thread_id) +{ + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) +{ + WINERR_IFALL(WINDOWS_LOG_EVENT, + "HandleRipEvent encountered error %u (type=%u) in process %I64u thread %u", + info.dwError, info.dwType, m_process.GetProcessId(), thread_id); + + Error error(info.dwError, eErrorTypeWin32); + m_debug_delegate->OnDebuggerError(error, info.dwType); + + return DBG_CONTINUE; +} diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.h b/source/Plugins/Process/Windows/Live/DebuggerThread.h new file mode 100644 index 000000000000..6a2619413950 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.h @@ -0,0 +1,100 @@ +//===-- DebuggerThread.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_DebuggerThread_H_ +#define liblldb_Plugins_Process_Windows_DebuggerThread_H_ + +#include +#include + +#include "ForwardDecl.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/windows/windows.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +// DebuggerThread +// +// Debugs a single process, notifying listeners as appropriate when interesting +// things occur. +//---------------------------------------------------------------------- +class DebuggerThread : public std::enable_shared_from_this +{ + public: + DebuggerThread(DebugDelegateSP debug_delegate); + virtual ~DebuggerThread(); + + Error DebugLaunch(const ProcessLaunchInfo &launch_info); + Error DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info); + + HostProcess + GetProcess() const + { + return m_process; + } + HostThread + GetMainThread() const + { + return m_main_thread; + } + std::weak_ptr + GetActiveException() + { + return m_active_exception; + } + + Error StopDebugging(bool terminate); + + void ContinueAsyncException(ExceptionResult result); + + private: + void FreeProcessHandles(); + void DebugLoop(); + ExceptionResult HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id); + DWORD HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD thread_id); + DWORD HandleRipEvent(const RIP_INFO &info, DWORD thread_id); + + DebugDelegateSP m_debug_delegate; + + HostProcess m_process; // The process being debugged. + HostThread m_main_thread; // The main thread of the inferior. + HANDLE m_image_file; // The image file of the process being debugged. + + ExceptionRecordSP m_active_exception; // The current exception waiting to be handled + + Predicate m_exception_pred; // A predicate which gets signalled when an exception + // is finished processing and the debug loop can be + // continued. + + HANDLE m_debugging_ended_event; // An event which gets signalled by the debugger thread when it + // exits the debugger loop and is detached from the inferior. + + std::atomic m_pid_to_detach; // Signals the loop to detach from the process (specified by pid). + std::atomic m_is_shutting_down; // Signals the debug loop to stop processing certain types of + // events that block shutdown. + bool m_detached; // Indicates we've detached from the inferior process and the debug loop can exit. + + static lldb::thread_result_t DebuggerThreadLaunchRoutine(void *data); + lldb::thread_result_t DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info); + static lldb::thread_result_t DebuggerThreadAttachRoutine(void *data); + lldb::thread_result_t DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &launch_info); +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/ForwardDecl.h b/source/Plugins/Process/Windows/Live/ForwardDecl.h new file mode 100644 index 000000000000..a782597555e1 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ForwardDecl.h @@ -0,0 +1,41 @@ +//===-- ForwardDecl.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_ForwardDecl_H_ +#define liblldb_Plugins_Process_Windows_ForwardDecl_H_ + +#include + +// ExceptionResult is returned by the debug delegate to specify how it processed +// the exception. +enum class ExceptionResult +{ + BreakInDebugger, // Break in the debugger and give the user a chance to interact with + // the program before continuing. + MaskException, // Eat the exception and don't let the application know it occurred. + SendToApplication // Send the exception to the application to be handled as if there were + // no debugger attached. +}; + +namespace lldb_private +{ + +class ProcessWindows; + +class IDebugDelegate; +class DebuggerThread; +class ExceptionRecord; + +typedef std::shared_ptr DebugDelegateSP; +typedef std::shared_ptr DebuggerThreadSP; +typedef std::shared_ptr ExceptionRecordSP; +typedef std::unique_ptr ExceptionRecordUP; +} + +#endif \ No newline at end of file diff --git a/source/Plugins/Process/Windows/Live/IDebugDelegate.h b/source/Plugins/Process/Windows/Live/IDebugDelegate.h new file mode 100644 index 000000000000..0e7849bb8ffe --- /dev/null +++ b/source/Plugins/Process/Windows/Live/IDebugDelegate.h @@ -0,0 +1,46 @@ +//===-- IDebugDelegate.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_IDebugDelegate_H_ +#define liblldb_Plugins_Process_Windows_IDebugDelegate_H_ + +#include "ForwardDecl.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include + +namespace lldb_private +{ +class Error; +class HostThread; + +//---------------------------------------------------------------------- +// IDebugDelegate +// +// IDebugDelegate defines an interface which allows implementors to receive +// notification of events that happen in a debugged process. +//---------------------------------------------------------------------- +class IDebugDelegate +{ + public: + virtual ~IDebugDelegate() {} + + virtual void OnExitProcess(uint32_t exit_code) = 0; + virtual void OnDebuggerConnected(lldb::addr_t image_base) = 0; + virtual ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) = 0; + virtual void OnCreateThread(const HostThread &thread) = 0; + virtual void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) = 0; + virtual void OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) = 0; + virtual void OnUnloadDll(lldb::addr_t module_addr) = 0; + virtual void OnDebugString(const std::string &string) = 0; + virtual void OnDebuggerError(const Error &error, uint32_t type) = 0; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp new file mode 100644 index 000000000000..a0ac9725c756 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp @@ -0,0 +1,91 @@ +//===-- LocalDebugDelegate.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LocalDebugDelegate.h" +#include "ProcessWindowsLive.h" + +using namespace lldb; +using namespace lldb_private; + +LocalDebugDelegate::LocalDebugDelegate(ProcessWP process) + : m_process(process) +{ +} + +void +LocalDebugDelegate::OnExitProcess(uint32_t exit_code) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnExitProcess(exit_code); +} + +void +LocalDebugDelegate::OnDebuggerConnected(lldb::addr_t image_base) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebuggerConnected(image_base); +} + +ExceptionResult +LocalDebugDelegate::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + return process->OnDebugException(first_chance, record); + else + return ExceptionResult::MaskException; +} + +void +LocalDebugDelegate::OnCreateThread(const HostThread &thread) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnCreateThread(thread); +} + +void +LocalDebugDelegate::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnExitThread(thread_id, exit_code); +} + +void +LocalDebugDelegate::OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnLoadDll(module_spec, module_addr); +} + +void +LocalDebugDelegate::OnUnloadDll(lldb::addr_t module_addr) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnUnloadDll(module_addr); +} + +void +LocalDebugDelegate::OnDebugString(const std::string &string) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebugString(string); +} + +void +LocalDebugDelegate::OnDebuggerError(const Error &error, uint32_t type) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebuggerError(error, type); +} + +ProcessWindowsLiveSP +LocalDebugDelegate::GetProcessPointer() +{ + ProcessSP process = m_process.lock(); + return std::static_pointer_cast(process); +} diff --git a/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h new file mode 100644 index 000000000000..ec2f9418142f --- /dev/null +++ b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h @@ -0,0 +1,68 @@ +//===-- LocalDebugDelegate.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_LocalDebugDelegate_H_ +#define liblldb_Plugins_Process_Windows_LocalDebugDelegate_H_ + +#include + +#include "IDebugDelegate.h" + +#include "lldb/lldb-forward.h" + +namespace lldb_private +{ + +class ProcessWindowsLive; +typedef std::shared_ptr ProcessWindowsLiveSP; + +//---------------------------------------------------------------------- +// LocalDebugDelegate +// +// LocalDebugDelegate creates a connection between a ProcessWindowsLive and the +// debug driver. This serves to decouple ProcessWindowsLive from the debug driver. +// It would be possible to get a similar decoupling by just having +// ProcessWindowsLive implement this interface directly. There are two reasons why +// we don't do this: +// +// 1) In the future when we add support for local debugging through LLGS, and we +// go through the Native*Protocol interface, it is likely we will need the +// additional flexibility provided by this sort of adapter pattern. +// 2) LLDB holds a shared_ptr to the ProcessWindows, and our driver thread +// needs access to it as well. To avoid a race condition, we want to make +// sure that we're also holding onto a shared_ptr. +// lldb_private::Process supports enable_shared_from_this, but that gives us +// a ProcessSP (which is exactly what we are trying to decouple from the +// driver), so this adapter serves as a way to transparently hold the +// ProcessSP while still keeping it decoupled from the driver. +//---------------------------------------------------------------------- +class LocalDebugDelegate : public IDebugDelegate +{ + public: + explicit LocalDebugDelegate(lldb::ProcessWP process); + + void OnExitProcess(uint32_t exit_code) override; + void OnDebuggerConnected(lldb::addr_t image_base) override; + ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) override; + void OnCreateThread(const HostThread &thread) override; + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; + void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override; + void OnUnloadDll(lldb::addr_t module_addr) override; + void OnDebugString(const std::string &message) override; + void OnDebuggerError(const Error &error, uint32_t type) override; + + private: + ProcessWindowsLiveSP + GetProcessPointer(); + + lldb::ProcessWP m_process; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp new file mode 100644 index 000000000000..55102cc12ebb --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp @@ -0,0 +1,989 @@ +//===-- ProcessWindowsLive.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Windows includes +#include "lldb/Host/windows/windows.h" +#include + +// C++ Includes +#include +#include +#include +#include + +// Other libraries and framework includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostNativeProcessBase.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/MonitoringProcessLauncher.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/FileAction.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" + +#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "LocalDebugDelegate.h" +#include "ProcessWindowsLive.h" +#include "TargetThreadWindowsLive.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +#define BOOL_STR(b) ((b) ? "true" : "false") + +namespace +{ + +std::string +GetProcessExecutableName(HANDLE process_handle) +{ + std::vector file_name; + DWORD file_name_size = MAX_PATH; // first guess, not an absolute limit + DWORD copied = 0; + do + { + file_name_size *= 2; + file_name.resize(file_name_size); + copied = ::GetModuleFileNameEx(process_handle, NULL, file_name.data(), file_name_size); + } while (copied >= file_name_size); + file_name.resize(copied); + return std::string(file_name.begin(), file_name.end()); +} + +std::string +GetProcessExecutableName(DWORD pid) +{ + std::string file_name; + HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (process_handle != NULL) + { + file_name = GetProcessExecutableName(process_handle); + ::CloseHandle(process_handle); + } + return file_name; +} + +} // anonymous namespace + +namespace lldb_private +{ + +// We store a pointer to this class in the ProcessWindows, so that we don't expose Windows +// OS specific types and implementation details from a public header file. +class ProcessWindowsData +{ + public: + ProcessWindowsData(bool stop_at_entry) + : m_stop_at_entry(stop_at_entry) + , m_initial_stop_event(nullptr) + , m_initial_stop_received(false) + { + m_initial_stop_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + } + + ~ProcessWindowsData() { ::CloseHandle(m_initial_stop_event); } + + lldb_private::Error m_launch_error; + lldb_private::DebuggerThreadSP m_debugger; + StopInfoSP m_pending_stop_info; + HANDLE m_initial_stop_event; + bool m_stop_at_entry; + bool m_initial_stop_received; + std::map m_new_threads; + std::set m_exited_threads; +}; +} +//------------------------------------------------------------------------------ +// Static functions. + +ProcessSP +ProcessWindowsLive::CreateInstance(lldb::TargetSP target_sp, Listener &listener, const FileSpec *) +{ + return ProcessSP(new ProcessWindowsLive(target_sp, listener)); +} + +void +ProcessWindowsLive::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindowsLive::ProcessWindowsLive(lldb::TargetSP target_sp, Listener &listener) + : lldb_private::ProcessWindows(target_sp, listener) +{ +} + +ProcessWindowsLive::~ProcessWindowsLive() +{ +} + +void +ProcessWindowsLive::Terminate() +{ +} + +lldb_private::ConstString +ProcessWindowsLive::GetPluginNameStatic() +{ + static ConstString g_name("windows"); + return g_name; +} + +const char * +ProcessWindowsLive::GetPluginDescriptionStatic() +{ + return "Process plugin for Windows"; +} + +Error +ProcessWindowsLive::EnableBreakpointSite(BreakpointSite *bp_site) +{ + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite called with bp_site 0x%p " + "(id=%d, addr=0x%x)", + bp_site->GetID(), bp_site->GetLoadAddress()); + + Error error = EnableSoftwareBreakpoint(bp_site); + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite failed. %s", error.AsCString()); + } + return error; +} + +Error +ProcessWindowsLive::DisableBreakpointSite(BreakpointSite *bp_site) +{ + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite called with bp_site 0x%p " + "(id=%d, addr=0x%x)", + bp_site->GetID(), bp_site->GetLoadAddress()); + + Error error = DisableSoftwareBreakpoint(bp_site); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite failed. %s", error.AsCString()); + } + return error; +} + +bool +ProcessWindowsLive::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // Add all the threads that were previously running and for which we did not detect a thread + // exited event. + int new_size = 0; + int continued_threads = 0; + int exited_threads = 0; + int new_threads = 0; + + for (ThreadSP old_thread : old_thread_list.Threads()) + { + lldb::tid_t old_thread_id = old_thread->GetID(); + auto exited_thread_iter = m_session_data->m_exited_threads.find(old_thread_id); + if (exited_thread_iter == m_session_data->m_exited_threads.end()) + { + new_thread_list.AddThread(old_thread); + ++new_size; + ++continued_threads; + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and is still running.", + old_thread_id); + } + else + { + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and has exited.", + old_thread_id); + ++exited_threads; + } + } + + // Also add all the threads that are new since the last time we broke into the debugger. + for (const auto &thread_info : m_session_data->m_new_threads) + { + ThreadSP thread(new TargetThreadWindowsLive(*this, thread_info.second)); + thread->SetID(thread_info.first); + new_thread_list.AddThread(thread); + ++new_size; + ++new_threads; + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u is new since last update.", thread_info.first); + } + + WINLOG_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - %d new threads, %d old threads, %d exited threads.", + new_threads, continued_threads, exited_threads); + + m_session_data->m_new_threads.clear(); + m_session_data->m_exited_threads.clear(); + + return new_size > 0; +} + +Error +ProcessWindowsLive::DoLaunch(Module *exe_module, + ProcessLaunchInfo &launch_info) +{ + // Even though m_session_data is accessed here, it is before a debugger thread has been + // kicked off. So there's no race conditions, and it shouldn't be necessary to acquire + // the mutex. + + Error result; + if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) + { + StreamString stream; + stream.Printf("ProcessWindows unable to launch '%s'. ProcessWindows can only be used for debug launches.", + launch_info.GetExecutableFile().GetPath().c_str()); + std::string message = stream.GetString(); + result.SetErrorString(message.c_str()); + + WINERR_IFALL(WINDOWS_LOG_PROCESS, message.c_str()); + return result; + } + + bool stop_at_entry = launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); + m_session_data.reset(new ProcessWindowsData(stop_at_entry)); + + SetPrivateState(eStateLaunching); + DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); + m_session_data->m_debugger.reset(new DebuggerThread(delegate)); + DebuggerThreadSP debugger = m_session_data->m_debugger; + + // Kick off the DebugLaunch asynchronously and wait for it to complete. + result = debugger->DebugLaunch(launch_info); + if (result.Fail()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", + launch_info.GetExecutableFile().GetPath().c_str(), result.AsCString()); + return result; + } + + HostProcess process; + Error error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", + launch_info.GetExecutableFile().GetPath().c_str(), error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch successfully launched '%s'", + launch_info.GetExecutableFile().GetPath().c_str()); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state + // should already be set to eStateStopped as a result of hitting the initial breakpoint. If + // it was not set, the breakpoint should have already been resumed from and the private state + // should already be eStateRunning. + launch_info.SetProcessID(process.GetProcessId()); + SetID(process.GetProcessId()); + + return result; +} + +Error +ProcessWindowsLive::DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + m_session_data.reset(new ProcessWindowsData(!attach_info.GetContinueOnceAttached())); + + DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); + DebuggerThreadSP debugger(new DebuggerThread(delegate)); + + m_session_data->m_debugger = debugger; + + DWORD process_id = static_cast(pid); + Error error = debugger->DebugAttach(process_id, attach_info); + if (error.Fail()) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DoAttachToProcessWithID encountered an error occurred initiating the asynchronous attach. %s", + error.AsCString()); + return error; + } + + HostProcess process; + error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DoAttachToProcessWithID encountered an error waiting for the debugger to connect. %s", + error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoAttachToProcessWithID successfully attached to process with pid=%u", + process_id); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state + // should already be set to eStateStopped as a result of hitting the initial breakpoint. If + // it was not set, the breakpoint should have already been resumed from and the private state + // should already be eStateRunning. + SetID(process.GetProcessId()); + return error; +} + +Error +ProcessWindowsLive::WaitForDebuggerConnection(DebuggerThreadSP debugger, HostProcess &process) +{ + Error result; + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection Waiting for loader breakpoint."); + + // Block this function until we receive the initial stop from the process. + if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == WAIT_OBJECT_0) + { + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection hit loader breakpoint, returning."); + + process = debugger->GetProcess(); + return m_session_data->m_launch_error; + } + else + return Error(::GetLastError(), eErrorTypeWin32); +} + +Error +ProcessWindowsLive::DoResume() +{ + llvm::sys::ScopedLock lock(m_mutex); + Error error; + + StateType private_state = GetPrivateState(); + if (private_state == eStateStopped || private_state == eStateCrashed) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u while state is %u. Resuming...", + m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState()); + + ExceptionRecordSP active_exception = + m_session_data->m_debugger->GetActiveException().lock(); + if (active_exception) + { + // Resume the process and continue processing debug events. Mask + // the exception so that from the process's view, there is no + // indication that anything happened. + m_session_data->m_debugger->ContinueAsyncException( + ExceptionResult::MaskException); + } + + WINLOG_IFANY(WINDOWS_LOG_PROCESS | WINDOWS_LOG_THREAD, "DoResume resuming %u threads.", + m_thread_list.GetSize()); + + for (int i = 0; i < m_thread_list.GetSize(); ++i) + { + auto thread = std::static_pointer_cast( + m_thread_list.GetThreadAtIndex(i)); + thread->DoResume(); + } + + SetPrivateState(eStateRunning); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u but state is %u. Returning...", + m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState()); + } + return error; +} + + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + +lldb_private::ConstString +ProcessWindowsLive::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWindowsLive::GetPluginVersion() +{ + return 1; +} + +Error +ProcessWindowsLive::DoDetach(bool keep_stopped) +{ + DebuggerThreadSP debugger_thread; + StateType private_state; + { + // Acquire the lock only long enough to get the DebuggerThread. + // StopDebugging() will trigger a call back into ProcessWindows which + // will also acquire the lock. Thus we have to release the lock before + // calling StopDebugging(). + llvm::sys::ScopedLock lock(m_mutex); + + private_state = GetPrivateState(); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called while state = %u, but there is no active session.", + private_state); + return Error(); + } + + debugger_thread = m_session_data->m_debugger; + } + + Error error; + if (private_state != eStateExited && private_state != eStateDetached) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called for process %I64u while state = %u. Detaching...", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + error = debugger_thread->StopDebugging(false); + if (error.Success()) + { + SetPrivateState(eStateDetached); + } + + // By the time StopDebugging returns, there is no more debugger thread, so + // we can be assured that no other thread will race for the session data. + m_session_data.reset(); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DoDetach called for process %I64u while state = %u, but cannot destroy in this state.", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + } + + return error; +} + +Error +ProcessWindowsLive::DoDestroy() +{ + DebuggerThreadSP debugger_thread; + StateType private_state; + { + // Acquire this lock inside an inner scope, only long enough to get the DebuggerThread. + // StopDebugging() will trigger a call back into ProcessWindows which will acquire the lock + // again, so we need to not deadlock. + llvm::sys::ScopedLock lock(m_mutex); + + private_state = GetPrivateState(); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called while state = %u, but there is no active session.", + private_state); + return Error(); + } + + debugger_thread = m_session_data->m_debugger; + } + + Error error; + if (private_state != eStateExited && private_state != eStateDetached) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called for process %I64u while state = %u. Shutting down...", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + error = debugger_thread->StopDebugging(true); + + // By the time StopDebugging returns, there is no more debugger thread, so + // we can be assured that no other thread will race for the session data. + m_session_data.reset(); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DoDestroy called for process %I64u while state = %u, but cannot destroy in this state.", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + } + + return error; +} + +void +ProcessWindowsLive::RefreshStateAfterStop() +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called with no active session. Returning..."); + return; + } + + m_thread_list.RefreshStateAfterStop(); + + std::weak_ptr exception_record = m_session_data->m_debugger->GetActiveException(); + ExceptionRecordSP active_exception = exception_record.lock(); + if (!active_exception) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called for process %I64u but there is no " + "active exception. Why is the process stopped?", + m_session_data->m_debugger->GetProcess().GetProcessId()); + return; + } + + StopInfoSP stop_info; + m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); + ThreadSP stop_thread = m_thread_list.GetSelectedThread(); + if (!stop_thread) + return; + + RegisterContextSP register_context = stop_thread->GetRegisterContext(); + + // The current EIP is AFTER the BP opcode, which is one byte. + uint64_t pc = register_context->GetPC() - 1; + if (active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT) + { + BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc)); + + if (site) + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "RefreshStateAfterStop detected breakpoint in process %I64u at " + "address 0x%I64x with breakpoint site %d", + m_session_data->m_debugger->GetProcess().GetProcessId(), pc, site->GetID()); + + if (site->ValidForThisThread(stop_thread.get())) + { + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "Breakpoint site %d is valid for this thread (0x%I64x), creating stop info.", + site->GetID(), stop_thread->GetID()); + + stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID( + *stop_thread, site->GetID()); + register_context->SetPC(pc); + } + else + { + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "Breakpoint site %d is not valid for this thread, creating empty stop info.", + site->GetID()); + } + } + stop_thread->SetStopInfo(stop_info); + } + else if (active_exception->GetExceptionCode() == EXCEPTION_SINGLE_STEP) + { + stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); + stop_thread->SetStopInfo(stop_info); + WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, "RefreshStateAfterStop single stepping thread %u", + stop_thread->GetID()); + } + else + { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " << llvm::format_hex(active_exception->GetExceptionCode(), 8) + << " encountered at address " << llvm::format_hex(pc, 8); + stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); + stop_thread->SetStopInfo(stop_info); + WINLOG_IFALL(WINDOWS_LOG_EXCEPTION, desc_stream.str().c_str()); + } +} + +bool +ProcessWindowsLive::IsAlive() +{ + StateType state = GetPrivateState(); + switch (state) + { + case eStateCrashed: + case eStateDetached: + case eStateUnloaded: + case eStateExited: + case eStateInvalid: + return false; + default: + return true; + } +} + +Error +ProcessWindowsLive::DoHalt(bool &caused_stop) +{ + Error error; + StateType state = GetPrivateState(); + if (state == eStateStopped) + caused_stop = false; + else + { + llvm::sys::ScopedLock lock(m_mutex); + caused_stop = ::DebugBreakProcess(m_session_data->m_debugger->GetProcess().GetNativeProcess().GetSystemHandle()); + if (!caused_stop) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoHalt called DebugBreakProcess, but it failed with error %u", + error.GetError()); + } + } + return error; +} + +void +ProcessWindowsLive::DidLaunch() +{ + ArchSpec arch_spec; + DidAttach(arch_spec); +} + +void +ProcessWindowsLive::DidAttach(ArchSpec &arch_spec) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // The initial stop won't broadcast the state change event, so account for that here. + if (m_session_data && GetPrivateState() == eStateStopped && m_session_data->m_stop_at_entry) + RefreshStateAfterStop(); +} + +size_t +ProcessWindowsLive::DoReadMemory(lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error) +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + return 0; + + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory attempting to read %u bytes from address 0x%I64x", size, vm_addr); + + HostProcess process = m_session_data->m_debugger->GetProcess(); + void *addr = reinterpret_cast(vm_addr); + SIZE_T bytes_read = 0; + if (!ReadProcessMemory(process.GetNativeProcess().GetSystemHandle(), addr, buf, size, &bytes_read)) + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory failed with error code %u", error.GetError()); + } + return bytes_read; +} + +size_t +ProcessWindowsLive::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) +{ + llvm::sys::ScopedLock lock(m_mutex); + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory attempting to write %u bytes into address 0x%I64x", size, vm_addr); + + if (!m_session_data) + { + WINERR_IFANY(WINDOWS_LOG_MEMORY, "DoWriteMemory cannot write, there is no active debugger connection."); + return 0; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + void *addr = reinterpret_cast(vm_addr); + SIZE_T bytes_written = 0; + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (WriteProcessMemory(handle, addr, buf, size, &bytes_written)) + FlushInstructionCache(handle, addr, bytes_written); + else + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory failed with error code %u", error.GetError()); + } + return bytes_written; +} + +Error +ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo &info) +{ + Error error; + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + { + error.SetErrorString("GetMemoryRegionInfo called with no debugging session."); + WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); + return error; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (handle == nullptr || handle == LLDB_INVALID_PROCESS) + { + error.SetErrorString("GetMemoryRegionInfo called with an invalid target process."); + WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "GetMemoryRegionInfo getting info for address 0x%I64x", vm_addr); + + void *addr = reinterpret_cast(vm_addr); + MEMORY_BASIC_INFORMATION mem_info = {0}; + SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info)); + if (result == 0) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_MEMORY, + "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", + error.GetError(), vm_addr); + return error; + } + const bool readable = IsPageReadable(mem_info.Protect); + const bool executable = IsPageExecutable(mem_info.Protect); + const bool writable = IsPageWritable(mem_info.Protect); + info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + + error.SetError(::GetLastError(), eErrorTypeWin32); + WINLOGV_IFALL(WINDOWS_LOG_MEMORY, "Memory region info for address 0x%I64u: readable=%s, executable=%s, writable=%s", + BOOL_STR(readable), BOOL_STR(executable), BOOL_STR(writable)); + return error; +} + +bool +ProcessWindowsLive::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target_sp->GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + // However, if there is no executable module, we return true since we might be preparing to attach. + return true; +} + +void +ProcessWindowsLive::OnExitProcess(uint32_t exit_code) +{ + // No need to acquire the lock since m_session_data isn't accessed. + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Process %u exited with code %u", GetID(), exit_code); + + TargetSP target = m_target_sp.lock(); + if (target) + { + ModuleSP executable_module = target->GetExecutableModule(); + ModuleList unloaded_modules; + unloaded_modules.Append(executable_module); + target->ModulesDidUnload(unloaded_modules, true); + } + + SetProcessExitStatus(nullptr, GetID(), true, 0, exit_code); + SetPrivateState(eStateExited); +} + +void +ProcessWindowsLive::OnDebuggerConnected(lldb::addr_t image_base) +{ + DebuggerThreadSP debugger = m_session_data->m_debugger; + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger connected to process %I64u. Image base = 0x%I64x", + debugger->GetProcess().GetProcessId(), image_base); + + ModuleSP module = GetTarget().GetExecutableModule(); + if (!module) + { + // During attach, we won't have the executable module, so find it now. + const DWORD pid = debugger->GetProcess().GetProcessId(); + const std::string file_name = GetProcessExecutableName(pid); + if (file_name.empty()) + { + return; + } + + FileSpec executable_file(file_name, true); + ModuleSpec module_spec(executable_file); + Error error; + module = GetTarget().GetSharedModule(module_spec, &error); + if (!module) + { + return; + } + + GetTarget().SetExecutableModule(module, false); + } + + bool load_addr_changed; + module->SetLoadAddress(GetTarget(), image_base, false, load_addr_changed); + + ModuleList loaded_modules; + loaded_modules.Append(module); + GetTarget().ModulesDidLoad(loaded_modules); + + // Add the main executable module to the list of pending module loads. We can't call + // GetTarget().ModulesDidLoad() here because we still haven't returned from DoLaunch() / DoAttach() yet + // so the target may not have set the process instance to `this` yet. + llvm::sys::ScopedLock lock(m_mutex); + const HostThreadWindows &wmain_thread = debugger->GetMainThread().GetNativeThread(); + m_session_data->m_new_threads[wmain_thread.GetThreadId()] = debugger->GetMainThread(); +} + +ExceptionResult +ProcessWindowsLive::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // FIXME: Without this check, occasionally when running the test suite there is + // an issue where m_session_data can be null. It's not clear how this could happen + // but it only surfaces while running the test suite. In order to properly diagnose + // this, we probably need to first figure allow the test suite to print out full + // lldb logs, and then add logging to the process plugin. + if (!m_session_data) + { + WINERR_IFANY(WINDOWS_LOG_EXCEPTION, + "Debugger thread reported exception 0x%x at address 0x%I64x, but there is no session.", + record.GetExceptionCode(), record.GetExceptionAddress()); + return ExceptionResult::SendToApplication; + } + + if (!first_chance) + { + // Any second chance exception is an application crash by definition. + SetPrivateState(eStateCrashed); + } + + ExceptionResult result = ExceptionResult::SendToApplication; + switch (record.GetExceptionCode()) + { + case EXCEPTION_BREAKPOINT: + // Handle breakpoints at the first chance. + result = ExceptionResult::BreakInDebugger; + + if (!m_session_data->m_initial_stop_received) + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, + "Hit loader breakpoint at address 0x%I64x, setting initial stop event.", + record.GetExceptionAddress()); + m_session_data->m_initial_stop_received = true; + ::SetEvent(m_session_data->m_initial_stop_event); + } + else + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, + "Hit non-loader breakpoint at address 0x%I64x.", + record.GetExceptionAddress()); + } + SetPrivateState(eStateStopped); + break; + case EXCEPTION_SINGLE_STEP: + result = ExceptionResult::BreakInDebugger; + SetPrivateState(eStateStopped); + break; + default: + WINLOG_IFANY(WINDOWS_LOG_EXCEPTION, + "Debugger thread reported exception 0x%x at address 0x%I64x (first_chance=%s)", + record.GetExceptionCode(), record.GetExceptionAddress(), BOOL_STR(first_chance)); + // For non-breakpoints, give the application a chance to handle the exception first. + if (first_chance) + result = ExceptionResult::SendToApplication; + else + result = ExceptionResult::BreakInDebugger; + } + + return result; +} + +void +ProcessWindowsLive::OnCreateThread(const HostThread &new_thread) +{ + llvm::sys::ScopedLock lock(m_mutex); + const HostThreadWindows &wnew_thread = new_thread.GetNativeThread(); + m_session_data->m_new_threads[wnew_thread.GetThreadId()] = new_thread; +} + +void +ProcessWindowsLive::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // On a forced termination, we may get exit thread events after the session + // data has been cleaned up. + if (!m_session_data) + return; + + // A thread may have started and exited before the debugger stopped allowing a refresh. + // Just remove it from the new threads list in that case. + auto iter = m_session_data->m_new_threads.find(thread_id); + if (iter != m_session_data->m_new_threads.end()) + m_session_data->m_new_threads.erase(iter); + else + m_session_data->m_exited_threads.insert(thread_id); +} + +void +ProcessWindowsLive::OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) +{ + // Confusingly, there is no Target::AddSharedModule. Instead, calling GetSharedModule() with + // a new module will add it to the module list and return a corresponding ModuleSP. + Error error; + ModuleSP module = GetTarget().GetSharedModule(module_spec, &error); + bool load_addr_changed = false; + module->SetLoadAddress(GetTarget(), module_addr, false, load_addr_changed); + + ModuleList loaded_modules; + loaded_modules.Append(module); + GetTarget().ModulesDidLoad(loaded_modules); +} + +void +ProcessWindowsLive::OnUnloadDll(lldb::addr_t module_addr) +{ + Address resolved_addr; + if (GetTarget().ResolveLoadAddress(module_addr, resolved_addr)) + { + ModuleSP module = resolved_addr.GetModule(); + if (module) + { + ModuleList unloaded_modules; + unloaded_modules.Append(module); + GetTarget().ModulesDidUnload(unloaded_modules, false); + } + } +} + +void +ProcessWindowsLive::OnDebugString(const std::string &string) +{ +} + +void +ProcessWindowsLive::OnDebuggerError(const Error &error, uint32_t type) +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (m_session_data->m_initial_stop_received) + { + // This happened while debugging. Do we shutdown the debugging session, try to continue, + // or do something else? + WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred during debugging. Unexpected behavior may result. %s", + error.GetError(), error.AsCString()); + } + else + { + // If we haven't actually launched the process yet, this was an error launching the + // process. Set the internal error and signal the initial stop event so that the DoLaunch + // method wakes up and returns a failure. + m_session_data->m_launch_error = error; + ::SetEvent(m_session_data->m_initial_stop_event); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred launching the process before the initial stop. %s", + error.GetError(), error.AsCString()); + return; + } +} diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h new file mode 100644 index 000000000000..2429f873c823 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h @@ -0,0 +1,125 @@ +//===-- ProcessWindowsLive.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ +#define liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ + +// C Includes + +// C++ Includes +#include +#include + +// Other libraries and framework includes +#include "ForwardDecl.h" +#include "IDebugDelegate.h" +#include "lldb/lldb-forward.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Process.h" + +#include "llvm/Support/Mutex.h" + +#include "plugins/Process/Windows/Common/ProcessWindows.h" + +class ProcessMonitor; + +namespace lldb_private +{ +class HostProcess; +class ProcessWindowsData; + +class ProcessWindowsLive : public lldb_private::ProcessWindows, public lldb_private::IDebugDelegate +{ +public: + //------------------------------------------------------------------ + // Static functions. + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance(lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessWindowsLive(lldb::TargetSP target_sp, + lldb_private::Listener &listener); + + ~ProcessWindowsLive(); + + // lldb_private::Process overrides + lldb_private::ConstString GetPluginName() override; + uint32_t GetPluginVersion() override; + + lldb_private::Error EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; + lldb_private::Error DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; + + lldb_private::Error DoDetach(bool keep_stopped) override; + lldb_private::Error DoLaunch(lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; + lldb_private::Error DoAttachToProcessWithID(lldb::pid_t pid, + const lldb_private::ProcessAttachInfo &attach_info) override; + lldb_private::Error DoResume() override; + lldb_private::Error DoDestroy() override; + lldb_private::Error DoHalt(bool &caused_stop) override; + + void DidLaunch() override; + void DidAttach(lldb_private::ArchSpec &arch_spec) override; + + void RefreshStateAfterStop() override; + + bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; + bool + DestroyRequiresHalt() override + { + return false; + } + bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; + bool IsAlive() override; + + size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, lldb_private::Error &error) override; + size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error) override; + lldb_private::Error GetMemoryRegionInfo(lldb::addr_t vm_addr, lldb_private::MemoryRegionInfo &info) override; + + // IDebugDelegate overrides. + void OnExitProcess(uint32_t exit_code) override; + void OnDebuggerConnected(lldb::addr_t image_base) override; + ExceptionResult OnDebugException(bool first_chance, const lldb_private::ExceptionRecord &record) override; + void OnCreateThread(const lldb_private::HostThread &thread) override; + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; + void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override; + void OnUnloadDll(lldb::addr_t module_addr) override; + void OnDebugString(const std::string &string) override; + void OnDebuggerError(const lldb_private::Error &error, uint32_t type) override; + + private: + lldb_private::Error WaitForDebuggerConnection(lldb_private::DebuggerThreadSP debugger, + lldb_private::HostProcess &process); + + llvm::sys::Mutex m_mutex; + + // Data for the active debugging session. + std::unique_ptr m_session_data; +}; + +} + +#endif // liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ diff --git a/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp new file mode 100644 index 000000000000..52b32716e674 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp @@ -0,0 +1,147 @@ +//===-- TargetThreadWindowsLive.cpp------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Core/Logging.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/RegisterContext.h" + +#include "TargetThreadWindowsLive.h" +#include "ProcessWindows.h" +#include "ProcessWindowsLog.h" +#include "UnwindLLDB.h" + +#if defined(_WIN64) +#include "x64/RegisterContextWindowsLive_x64.h" +#else +#include "x86/RegisterContextWindowsLive_x86.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +TargetThreadWindowsLive::TargetThreadWindowsLive(ProcessWindows &process, const HostThread &thread) + : TargetThreadWindows(process, thread) + , m_host_thread(thread) +{ +} + +TargetThreadWindowsLive::~TargetThreadWindowsLive() +{ + DestroyThread(); +} + +void +TargetThreadWindowsLive::RefreshStateAfterStop() +{ + ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); + SetState(eStateStopped); + GetRegisterContext()->InvalidateIfNeeded(false); +} + +void +TargetThreadWindowsLive::WillResume(lldb::StateType resume_state) +{ +} + +void +TargetThreadWindowsLive::DidStop() +{ +} + +RegisterContextSP +TargetThreadWindowsLive::GetRegisterContext() +{ + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrameIndex(0); + + return m_reg_context_sp; +} + +RegisterContextSP +TargetThreadWindowsLive::CreateRegisterContextForFrame(StackFrame *frame) +{ + return CreateRegisterContextForFrameIndex(frame->GetConcreteFrameIndex()); +} + +RegisterContextSP +TargetThreadWindowsLive::CreateRegisterContextForFrameIndex(uint32_t idx) +{ + if (!m_reg_context_sp) + { + ArchSpec arch = HostInfo::GetArchitecture(); + switch (arch.GetMachine()) + { + case llvm::Triple::x86: +#if defined(_WIN64) + // FIXME: This is a Wow64 process, create a RegisterContextWindows_Wow64 +#else + m_reg_context_sp.reset(new RegisterContextWindowsLive_x86(*this, idx)); +#endif + break; + case llvm::Triple::x86_64: +#if defined(_WIN64) + m_reg_context_sp.reset(new RegisterContextWindowsLive_x64(*this, idx)); +#else + // LLDB is 32-bit, but the target process is 64-bit. We probably can't debug this. +#endif + default: + break; + } + } + return m_reg_context_sp; +} + +bool +TargetThreadWindowsLive::CalculateStopInfo() +{ + SetStopInfo(m_stop_info_sp); + return true; +} + +Unwind * +TargetThreadWindowsLive::GetUnwinder() +{ + // FIXME: Implement an unwinder based on the Windows unwinder exposed through DIA SDK. + if (m_unwinder_ap.get() == NULL) + m_unwinder_ap.reset(new UnwindLLDB(*this)); + return m_unwinder_ap.get(); +} + +bool +TargetThreadWindowsLive::DoResume() +{ + StateType resume_state = GetTemporaryResumeState(); + StateType current_state = GetState(); + if (resume_state == current_state) + return true; + + if (resume_state == eStateStepping) + { + uint32_t flags_index = GetRegisterContext()->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + uint64_t flags_value = GetRegisterContext()->ReadRegisterAsUnsigned(flags_index, 0); + flags_value |= 0x100; // Set the trap flag on the CPU + GetRegisterContext()->WriteRegisterFromUnsigned(flags_index, flags_value); + } + + if (resume_state == eStateStepping || resume_state == eStateRunning) + { + DWORD previous_suspend_count = 0; + HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); + do + { + previous_suspend_count = ::ResumeThread(thread_handle); + } while (previous_suspend_count > 0); + } + return true; +} diff --git a/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h new file mode 100644 index 000000000000..15262b9e9423 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h @@ -0,0 +1,55 @@ +//===-- TargetThreadWindowsLive.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_TargetThreadWindowsLive_H_ +#define liblldb_Plugins_Process_Windows_TargetThreadWindowsLive_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Thread.h" + +#include "Plugins/Process/Windows/Common/TargetThreadWindows.h" + +namespace lldb_private +{ +class ProcessWindows; +class HostThread; +class StackFrame; + +class TargetThreadWindowsLive : public lldb_private::TargetThreadWindows +{ + public: + TargetThreadWindowsLive(ProcessWindows &process, const HostThread &thread); + virtual ~TargetThreadWindowsLive(); + + // lldb_private::Thread overrides + void RefreshStateAfterStop() override; + void WillResume(lldb::StateType resume_state) override; + void DidStop() override; + lldb::RegisterContextSP GetRegisterContext() override; + lldb::RegisterContextSP CreateRegisterContextForFrame(StackFrame *frame) override; + bool CalculateStopInfo() override; + Unwind *GetUnwinder() override; + + bool DoResume(); + + HostThread + GetHostThread() const + { + return m_host_thread; + } + + private: + lldb::RegisterContextSP CreateRegisterContextForFrameIndex(uint32_t idx); + + HostThread m_host_thread; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp new file mode 100644 index 000000000000..e74647ab68fb --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp @@ -0,0 +1,171 @@ +//===-- RegisterContextWindowsLive_x64.cpp ----------------------*- 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-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "RegisterContextWindowsLive_x64.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextWindowsLive_x64::RegisterContextWindowsLive_x64(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows_x64(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsLive_x64::~RegisterContextWindowsLive_x64() +{ +} + + +bool +RegisterContextWindowsLive_x64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + reg_value.SetUInt64(m_context.Rax); + break; + case lldb_rbx_x86_64: + reg_value.SetUInt64(m_context.Rbx); + break; + case lldb_rcx_x86_64: + reg_value.SetUInt64(m_context.Rcx); + break; + case lldb_rdx_x86_64: + reg_value.SetUInt64(m_context.Rdx); + break; + case lldb_rdi_x86_64: + reg_value.SetUInt64(m_context.Rdi); + break; + case lldb_rsi_x86_64: + reg_value.SetUInt64(m_context.Rsi); + break; + case lldb_r8_x86_64: + reg_value.SetUInt64(m_context.R8); + break; + case lldb_r9_x86_64: + reg_value.SetUInt64(m_context.R9); + break; + case lldb_r10_x86_64: + reg_value.SetUInt64(m_context.R10); + break; + case lldb_r11_x86_64: + reg_value.SetUInt64(m_context.R11); + break; + case lldb_r12_x86_64: + reg_value.SetUInt64(m_context.R12); + break; + case lldb_r13_x86_64: + reg_value.SetUInt64(m_context.R13); + break; + case lldb_r14_x86_64: + reg_value.SetUInt64(m_context.R14); + break; + case lldb_r15_x86_64: + reg_value.SetUInt64(m_context.R15); + break; + case lldb_rbp_x86_64: + reg_value.SetUInt64(m_context.Rbp); + break; + case lldb_rsp_x86_64: + reg_value.SetUInt64(m_context.Rsp); + break; + case lldb_rip_x86_64: + reg_value.SetUInt64(m_context.Rip); + break; + case lldb_rflags_x86_64: + reg_value.SetUInt64(m_context.EFlags); + break; + } + return true; +} + +bool +RegisterContextWindowsLive_x64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + // Since we cannot only write a single register value to the inferior, we need to make sure + // our cached copy of the register values are fresh. Otherwise when writing EAX, for example, + // we may also overwrite some other register with a stale value. + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + m_context.Rax = reg_value.GetAsUInt64(); + break; + case lldb_rbx_x86_64: + m_context.Rbx = reg_value.GetAsUInt64(); + break; + case lldb_rcx_x86_64: + m_context.Rcx = reg_value.GetAsUInt64(); + break; + case lldb_rdx_x86_64: + m_context.Rdx = reg_value.GetAsUInt64(); + break; + case lldb_rdi_x86_64: + m_context.Rdi = reg_value.GetAsUInt64(); + break; + case lldb_rsi_x86_64: + m_context.Rsi = reg_value.GetAsUInt64(); + break; + case lldb_r8_x86_64: + m_context.R8 = reg_value.GetAsUInt64(); + break; + case lldb_r9_x86_64: + m_context.R9 = reg_value.GetAsUInt64(); + break; + case lldb_r10_x86_64: + m_context.R10 = reg_value.GetAsUInt64(); + break; + case lldb_r11_x86_64: + m_context.R11 = reg_value.GetAsUInt64(); + break; + case lldb_r12_x86_64: + m_context.R12 = reg_value.GetAsUInt64(); + break; + case lldb_r13_x86_64: + m_context.R13 = reg_value.GetAsUInt64(); + break; + case lldb_r14_x86_64: + m_context.R14 = reg_value.GetAsUInt64(); + break; + case lldb_r15_x86_64: + m_context.R15 = reg_value.GetAsUInt64(); + break; + case lldb_rbp_x86_64: + m_context.Rbp = reg_value.GetAsUInt64(); + break; + case lldb_rsp_x86_64: + m_context.Rsp = reg_value.GetAsUInt64(); + break; + case lldb_rip_x86_64: + m_context.Rip = reg_value.GetAsUInt64(); + break; + case lldb_rflags_x86_64: + m_context.EFlags = reg_value.GetAsUInt64(); + break; + } + + // Physically update the registers in the target process. + TargetThreadWindows &wthread = static_cast(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} diff --git a/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h new file mode 100644 index 000000000000..bd250a994d3a --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h @@ -0,0 +1,40 @@ +//===-- RegisterContextWindowsLive_x64.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsLive_x64_H_ +#define liblldb_RegisterContextWindowsLive_x64_H_ + +#include "lldb/lldb-forward.h" +#include "../../Common/x64/RegisterContextWindows_x64.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsLive_x64 : public RegisterContextWindows_x64 +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindowsLive_x64(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindowsLive_x64(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindowsLive_x64_H_ diff --git a/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp new file mode 100644 index 000000000000..f2decc72d16d --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp @@ -0,0 +1,100 @@ +//===-- RegisterContextWindowsLive_x86.cpp ------------------------*- 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-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "ProcessWindowsLog.h" +#include "RegisterContextWindowsLive_x86.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsLive_x86::RegisterContextWindowsLive_x86(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows_x86(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsLive_x86::~RegisterContextWindowsLive_x86() +{ +} + + +bool +RegisterContextWindowsLive_x86::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + // Since we cannot only write a single register value to the inferior, we need to make sure + // our cached copy of the register values are fresh. Otherwise when writing EAX, for example, + // we may also overwrite some other register with a stale value. + if (!CacheAllRegisterValues()) + return false; + + uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + switch (reg) + { + case lldb_eax_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EAX", reg_value.GetAsUInt32()); + m_context.Eax = reg_value.GetAsUInt32(); + break; + case lldb_ebx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBX", reg_value.GetAsUInt32()); + m_context.Ebx = reg_value.GetAsUInt32(); + break; + case lldb_ecx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ECX", reg_value.GetAsUInt32()); + m_context.Ecx = reg_value.GetAsUInt32(); + break; + case lldb_edx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDX", reg_value.GetAsUInt32()); + m_context.Edx = reg_value.GetAsUInt32(); + break; + case lldb_edi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDI", reg_value.GetAsUInt32()); + m_context.Edi = reg_value.GetAsUInt32(); + break; + case lldb_esi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESI", reg_value.GetAsUInt32()); + m_context.Esi = reg_value.GetAsUInt32(); + break; + case lldb_ebp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBP", reg_value.GetAsUInt32()); + m_context.Ebp = reg_value.GetAsUInt32(); + break; + case lldb_esp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESP", reg_value.GetAsUInt32()); + m_context.Esp = reg_value.GetAsUInt32(); + break; + case lldb_eip_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EIP", reg_value.GetAsUInt32()); + m_context.Eip = reg_value.GetAsUInt32(); + break; + case lldb_eflags_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EFLAGS", reg_value.GetAsUInt32()); + m_context.EFlags = reg_value.GetAsUInt32(); + break; + default: + WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to unknown register %u", reg_value.GetAsUInt32(), + reg); + } + + // Physically update the registers in the target process. + TargetThreadWindows &wthread = static_cast(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h new file mode 100644 index 000000000000..9554f017408a --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsLive_x86.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsLive_x86_H_ +#define liblldb_RegisterContextWindowsLive_x86_H_ + +#include "lldb/lldb-forward.h" +#include "../../Common/x86/RegisterContextWindows_x86.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsLive_x86 : public RegisterContextWindows_x86 +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindowsLive_x86(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindowsLive_x86(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsLive_x86_H_ diff --git a/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt b/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt new file mode 100644 index 000000000000..b43246b00351 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories(../../Utility) +include_directories(../Common) + +set(PROC_WINDOWS_MINIDUMP_SOURCES + ProcessWinMiniDump.cpp + ThreadWinMiniDump.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_MINIDUMP_SOURCES ${PROC_WINDOWS_MINIDUMP_SOURCES} + x86/RegisterContextWindowsMiniDump_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_MINIDUMP_SOURCES ${PROC_WINDOWS_MINIDUMP_SOURCES} + x64/RegisterContextWindowsMiniDump_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWinMiniDump + ${PROC_WINDOWS_MINIDUMP_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp new file mode 100644 index 000000000000..fbc96f085ed4 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp @@ -0,0 +1,550 @@ +//===-- ProcessWinMiniDump.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWinMiniDump.h" + +#include "lldb/Host/windows/windows.h" +#include + +#include +#include +#include +#include + +#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBAssert.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +#include "ExceptionRecord.h" +#include "ThreadWinMiniDump.h" + +using namespace lldb_private; + +namespace +{ + +// Getting a string out of a mini dump is a chore. You're usually given a +// relative virtual address (RVA), which points to a counted string that's in +// Windows Unicode (UTF-16). This wrapper handles all the redirection and +// returns a UTF-8 copy of the string. +std::string +GetMiniDumpString(const void *base_addr, const RVA rva) +{ + std::string result; + if (!base_addr) + { + return result; + } + auto md_string = reinterpret_cast(static_cast(base_addr) + rva); + auto source_start = reinterpret_cast(md_string->Buffer); + const auto source_length = ::wcslen(md_string->Buffer); + const auto source_end = source_start + source_length; + result.resize(4*source_length); // worst case length + auto result_start = reinterpret_cast(&result[0]); + const auto result_end = result_start + result.size(); + ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end, strictConversion); + const auto result_size = std::distance(reinterpret_cast(&result[0]), result_start); + result.resize(result_size); // shrink to actual length + return result; +} + +} // anonymous namespace + +// Encapsulates the private data for ProcessWinMiniDump. +// TODO(amccarth): Determine if we need a mutex for access. +class ProcessWinMiniDump::Data +{ +public: + Data(); + ~Data(); + + FileSpec m_core_file; + HANDLE m_dump_file; // handle to the open minidump file + HANDLE m_mapping; // handle to the file mapping for the minidump file + void * m_base_addr; // base memory address of the minidump + std::shared_ptr m_exception_sp; +}; + +ConstString +ProcessWinMiniDump::GetPluginNameStatic() +{ + static ConstString g_name("win-minidump"); + return g_name; +} + +const char * +ProcessWinMiniDump::GetPluginDescriptionStatic() +{ + return "Windows minidump plug-in."; +} + +void +ProcessWinMiniDump::Terminate() +{ + PluginManager::UnregisterPlugin(ProcessWinMiniDump::CreateInstance); +} + + +lldb::ProcessSP +ProcessWinMiniDump::CreateInstance(lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file) +{ + lldb::ProcessSP process_sp; + if (crash_file) + { + process_sp.reset(new ProcessWinMiniDump(target_sp, listener, *crash_file)); + } + return process_sp; +} + +bool +ProcessWinMiniDump::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + // TODO(amccarth): Eventually, this needs some actual logic. + return true; +} + +ProcessWinMiniDump::ProcessWinMiniDump(lldb::TargetSP target_sp, Listener &listener, + const FileSpec &core_file) : + ProcessWindows(target_sp, listener), + m_data_up(new Data) +{ + m_data_up->m_core_file = core_file; +} + +ProcessWinMiniDump::~ProcessWinMiniDump() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +ConstString +ProcessWinMiniDump::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWinMiniDump::GetPluginVersion() +{ + return 1; +} + + +Error +ProcessWinMiniDump::DoLoadCore() +{ + Error error; + + error = MapMiniDumpIntoMemory(m_data_up->m_core_file.GetCString()); + if (error.Fail()) + { + return error; + } + + GetTarget().SetArchitecture(DetermineArchitecture()); + ReadMiscInfo(); // notably for process ID + ReadModuleList(); + ReadExceptionRecord(); + + return error; + +} + +DynamicLoader * +ProcessWinMiniDump::GetDynamicLoader() +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, DynamicLoaderWindowsDYLD::GetPluginNameStatic().GetCString())); + return m_dyld_ap.get(); +} + +bool +ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + size_t size = 0; + auto thread_list_ptr = static_cast(FindDumpStream(ThreadListStream, &size)); + if (thread_list_ptr) + { + const ULONG32 thread_count = thread_list_ptr->NumberOfThreads; + for (ULONG32 i = 0; i < thread_count; ++i) { + const auto &mini_dump_thread = thread_list_ptr->Threads[i]; + auto thread_sp = std::make_shared(*this, mini_dump_thread.ThreadId); + if (mini_dump_thread.ThreadContext.DataSize >= sizeof(CONTEXT)) + { + const CONTEXT *context = reinterpret_cast(static_cast(m_data_up->m_base_addr) + mini_dump_thread.ThreadContext.Rva); + thread_sp->SetContext(context); + } + new_thread_list.AddThread(thread_sp); + } + } + + return new_thread_list.GetSize(false) > 0; +} + +void +ProcessWinMiniDump::RefreshStateAfterStop() +{ + if (!m_data_up) return; + if (!m_data_up->m_exception_sp) return; + + auto active_exception = m_data_up->m_exception_sp; + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex(active_exception->GetExceptionCode(), 8) + << " encountered at address " + << llvm::format_hex(active_exception->GetExceptionAddress(), 8); + m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); + auto stop_thread = m_thread_list.GetSelectedThread(); + auto stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); + stop_thread->SetStopInfo(stop_info); +} + +Error +ProcessWinMiniDump::DoDestroy() +{ + return Error(); +} + +bool +ProcessWinMiniDump::IsAlive() +{ + return true; +} + +bool +ProcessWinMiniDump::WarnBeforeDetach () const +{ + // Since this is post-mortem debugging, there's no need to warn the user + // that quitting the debugger will terminate the process. + return false; +} + +size_t +ProcessWinMiniDump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since we have it all cached our our dump file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t +ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // I don't have a sense of how frequently this is called or how many memory + // ranges a mini dump typically has, so I'm not sure if searching for the + // appropriate range linearly each time is stupid. Perhaps we should build + // an index for faster lookups. + Range range = {0}; + if (!FindMemoryRange(addr, &range)) + { + return 0; + } + + // There's at least some overlap between the beginning of the desired range + // (addr) and the current range. Figure out where the overlap begins and + // how much overlap there is, then copy it to the destination buffer. + lldbassert(range.start <= addr); + const size_t offset = addr - range.start; + lldbassert(offset < range.size); + const size_t overlap = std::min(size, range.size - offset); + std::memcpy(buf, range.ptr + offset, overlap); + return overlap; +} + +Error +ProcessWinMiniDump::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &info) +{ + Error error; + size_t size; + const auto list = reinterpret_cast(FindDumpStream(MemoryInfoListStream, &size)); + if (list == nullptr || size < sizeof(MINIDUMP_MEMORY_INFO_LIST)) + { + error.SetErrorString("the mini dump contains no memory range information"); + return error; + } + + if (list->SizeOfEntry < sizeof(MINIDUMP_MEMORY_INFO)) + { + error.SetErrorString("the entries in the mini dump memory info list are smaller than expected"); + return error; + } + + if (size < list->SizeOfHeader + list->SizeOfEntry * list->NumberOfEntries) + { + error.SetErrorString("the mini dump memory info list is incomplete"); + return error; + } + + for (int i = 0; i < list->NumberOfEntries; ++i) + { + const auto entry = reinterpret_cast(reinterpret_cast(list) + + list->SizeOfHeader + i * list->SizeOfEntry); + const auto head = entry->BaseAddress; + const auto tail = head + entry->RegionSize; + if (head <= load_addr && load_addr < tail) + { + info.SetReadable(IsPageReadable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetWritable(IsPageWritable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(IsPageExecutable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + return error; + } + } + // Note that the memory info list doesn't seem to contain ranges in kernel space, + // so if you're walking a stack that has kernel frames, the stack may appear + // truncated. + error.SetErrorString("address is not in a known range"); + return error; +} + +void +ProcessWinMiniDump::Clear() +{ + m_thread_list.Clear(); +} + +void +ProcessWinMiniDump::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +ArchSpec +ProcessWinMiniDump::GetArchitecture() +{ + // TODO + return ArchSpec(); +} + + +ProcessWinMiniDump::Data::Data() : + m_dump_file(INVALID_HANDLE_VALUE), + m_mapping(NULL), + m_base_addr(nullptr) +{ +} + +ProcessWinMiniDump::Data::~Data() +{ + if (m_base_addr) + { + ::UnmapViewOfFile(m_base_addr); + m_base_addr = nullptr; + } + if (m_mapping) + { + ::CloseHandle(m_mapping); + m_mapping = NULL; + } + if (m_dump_file != INVALID_HANDLE_VALUE) + { + ::CloseHandle(m_dump_file); + m_dump_file = INVALID_HANDLE_VALUE; + } +} + +bool +ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const +{ + size_t stream_size = 0; + auto mem_list_stream = static_cast(FindDumpStream(MemoryListStream, &stream_size)); + if (mem_list_stream) + { + for (ULONG32 i = 0; i < mem_list_stream->NumberOfMemoryRanges; ++i) { + const MINIDUMP_MEMORY_DESCRIPTOR &mem_desc = mem_list_stream->MemoryRanges[i]; + const MINIDUMP_LOCATION_DESCRIPTOR &loc_desc = mem_desc.Memory; + const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; + const size_t range_size = loc_desc.DataSize; + if (range_start <= addr && addr < range_start + range_size) + { + range_out->start = range_start; + range_out->size = range_size; + range_out->ptr = reinterpret_cast(m_data_up->m_base_addr) + loc_desc.Rva; + return true; + } + } + } + + // Some mini dumps have a Memory64ListStream that captures all the heap + // memory. We can't exactly use the same loop as above, because the mini + // dump uses slightly different data structures to describe those. + auto mem_list64_stream = static_cast(FindDumpStream(Memory64ListStream, &stream_size)); + if (mem_list64_stream) + { + size_t base_rva = mem_list64_stream->BaseRva; + for (ULONG32 i = 0; i < mem_list64_stream->NumberOfMemoryRanges; ++i) { + const MINIDUMP_MEMORY_DESCRIPTOR64 &mem_desc = mem_list64_stream->MemoryRanges[i]; + const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; + const size_t range_size = mem_desc.DataSize; + if (range_start <= addr && addr < range_start + range_size) + { + range_out->start = range_start; + range_out->size = range_size; + range_out->ptr = reinterpret_cast(m_data_up->m_base_addr) + base_rva; + return true; + } + base_rva += range_size; + } + } + + return false; +} + + +Error +ProcessWinMiniDump::MapMiniDumpIntoMemory(const char *file) +{ + Error error; + + m_data_up->m_dump_file = ::CreateFile(file, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (m_data_up->m_dump_file == INVALID_HANDLE_VALUE) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + m_data_up->m_mapping = ::CreateFileMapping(m_data_up->m_dump_file, NULL, + PAGE_READONLY, 0, 0, NULL); + if (m_data_up->m_mapping == NULL) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + m_data_up->m_base_addr = ::MapViewOfFile(m_data_up->m_mapping, FILE_MAP_READ, 0, 0, 0); + if (m_data_up->m_base_addr == NULL) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + return error; +} + + +ArchSpec +ProcessWinMiniDump::DetermineArchitecture() +{ + size_t size = 0; + auto system_info_ptr = static_cast(FindDumpStream(SystemInfoStream, &size)); + if (system_info_ptr) + { + switch (system_info_ptr->ProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_INTEL: + return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_I386, LLDB_INVALID_CPUTYPE); + case PROCESSOR_ARCHITECTURE_AMD64: + return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_AMD64, LLDB_INVALID_CPUTYPE); + default: + break; + } + } + + return ArchSpec(); // invalid or unknown +} + +void +ProcessWinMiniDump::ReadExceptionRecord() +{ + size_t size = 0; + auto exception_stream_ptr = static_cast(FindDumpStream(ExceptionStream, &size)); + if (exception_stream_ptr) + { + m_data_up->m_exception_sp.reset(new ExceptionRecord(exception_stream_ptr->ExceptionRecord, exception_stream_ptr->ThreadId)); + } +} + +void +ProcessWinMiniDump::ReadMiscInfo() +{ + size_t size = 0; + const auto misc_info_ptr = static_cast(FindDumpStream(MiscInfoStream, &size)); + if (!misc_info_ptr || size < sizeof(MINIDUMP_MISC_INFO)) { + return; + } + + if ((misc_info_ptr->Flags1 & MINIDUMP_MISC1_PROCESS_ID) != 0) { + // This misc info record has the process ID. + SetID(misc_info_ptr->ProcessId); + } +} + +void +ProcessWinMiniDump::ReadModuleList() +{ + size_t size = 0; + auto module_list_ptr = static_cast(FindDumpStream(ModuleListStream, &size)); + if (!module_list_ptr || module_list_ptr->NumberOfModules == 0) + { + return; + } + + for (ULONG32 i = 0; i < module_list_ptr->NumberOfModules; ++i) + { + const auto &module = module_list_ptr->Modules[i]; + const auto file_name = GetMiniDumpString(m_data_up->m_base_addr, module.ModuleNameRva); + ModuleSpec module_spec = FileSpec(file_name, true); + + lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec); + if (!module_sp) + { + continue; + } + bool load_addr_changed = false; + module_sp->SetLoadAddress(GetTarget(), module.BaseOfImage, false, load_addr_changed); + } +} + +void * +ProcessWinMiniDump::FindDumpStream(unsigned stream_number, size_t *size_out) const +{ + void *stream = nullptr; + *size_out = 0; + + assert(m_data_up != nullptr); + assert(m_data_up->m_base_addr != 0); + + MINIDUMP_DIRECTORY *dir = nullptr; + if (::MiniDumpReadDumpStream(m_data_up->m_base_addr, stream_number, &dir, nullptr, nullptr) && + dir != nullptr && dir->Location.DataSize > 0) + { + assert(dir->StreamType == stream_number); + *size_out = dir->Location.DataSize; + stream = static_cast(static_cast(m_data_up->m_base_addr) + dir->Location.Rva); + } + + return stream; +} diff --git a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h new file mode 100644 index 000000000000..12864be37127 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h @@ -0,0 +1,140 @@ +//===-- ProcessWinMiniDump.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessWinMiniDump_h_ +#define liblldb_ProcessWinMiniDump_h_ + +#include +#include + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +#include "Plugins/Process/Windows/Common/ProcessWindows.h" + +struct ThreadData; + +class ProcessWinMiniDump : public lldb_private::ProcessWindows +{ + public: + static lldb::ProcessSP + CreateInstance (lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + ProcessWinMiniDump(lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec &core_file); + + virtual + ~ProcessWinMiniDump(); + + bool + CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; + + lldb_private::Error + DoLoadCore() override; + + lldb_private::DynamicLoader * + GetDynamicLoader() override; + + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + + lldb_private::Error + DoDestroy() override; + + void + RefreshStateAfterStop() override; + + bool + IsAlive() override; + + bool + WarnBeforeDetach () const override; + + size_t + ReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + size_t + DoReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + lldb_private::ArchSpec + GetArchitecture(); + + lldb_private::Error + GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &range_info) override; + + protected: + void + Clear(); + + bool + UpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + private: + // Describes a range of memory captured in the mini dump. + struct Range { + lldb::addr_t start; // virtual address of the beginning of the range + size_t size; // size of the range in bytes + const uint8_t *ptr; // absolute pointer to the first byte of the range + }; + + // If the mini dump has a memory range that contains the desired address, it + // returns true with the details of the range in *range_out. Otherwise, it + // returns false. + bool + FindMemoryRange(lldb::addr_t addr, Range *range_out) const; + + lldb_private::Error + MapMiniDumpIntoMemory(const char *file); + + lldb_private::ArchSpec + DetermineArchitecture(); + + void + ReadExceptionRecord(); + + void + ReadMiscInfo(); + + void + ReadModuleList(); + + // A thin wrapper around WinAPI's MiniDumpReadDumpStream to avoid redundant + // checks. If there's a failure (e.g., if the requested stream doesn't exist), + // the function returns nullptr and sets *size_out to 0. + void * + FindDumpStream(unsigned stream_number, size_t *size_out) const; + + // Isolate the data to keep Windows-specific types out of this header. Can't + // use the typical pimpl idiom because the implementation of this class also + // needs access to public and protected members of the base class. + class Data; + std::unique_ptr m_data_up; +}; + +#endif // liblldb_ProcessWinMiniDump_h_ diff --git a/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp new file mode 100644 index 000000000000..ddcd15b1ae12 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp @@ -0,0 +1,104 @@ +//===-- ThreadWinMiniDump.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ThreadWinMiniDump.h" + +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/windows/windows.h" +#include + +#include "ProcessWinMiniDump.h" +#if defined(_WIN64) +#include "x64/RegisterContextWindowsMiniDump_x64.h" +#else +#include "x86/RegisterContextWindowsMiniDump_x86.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +// This is a minimal implementation in order to get something running. It will +// be fleshed out as more mini-dump functionality is added. + +class ThreadWinMiniDump::Data { + public: + Data() : m_context(nullptr) {} + const CONTEXT *m_context; +}; + +ThreadWinMiniDump::ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_data(new Data) +{ +} + +ThreadWinMiniDump::~ThreadWinMiniDump() +{ +} + +void +ThreadWinMiniDump::RefreshStateAfterStop() +{ +} + +lldb::RegisterContextSP +ThreadWinMiniDump::GetRegisterContext() +{ + if (m_reg_context_sp.get() == NULL) { + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + } + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadWinMiniDump::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) +{ + const uint32_t concrete_frame_idx = (frame) ? frame->GetConcreteFrameIndex() : 0; + RegisterContextSP reg_ctx_sp; + ArchSpec arch = HostInfo::GetArchitecture(); + switch (arch.GetMachine()) + { + case llvm::Triple::x86: +#if defined(_WIN64) + // FIXME: This is a Wow64 process, create a RegisterContextWindows_Wow64 +#else + reg_ctx_sp.reset(new RegisterContextWindowsMiniDump_x86(*this, concrete_frame_idx, m_data->m_context)); +#endif + break; + case llvm::Triple::x86_64: +#if defined(_WIN64) + reg_ctx_sp.reset(new RegisterContextWindowsMiniDump_x64(*this, concrete_frame_idx, m_data->m_context)); +#else + // LLDB is 32-bit, but the target process is 64-bit. We probably can't debug this. +#endif + default: + break; + } + return reg_ctx_sp; +} + +void +ThreadWinMiniDump::ClearStackFrames() +{ +} + +void +ThreadWinMiniDump::SetContext(const void *context) +{ + if (m_data) + { + m_data->m_context = static_cast(context); + } +} + +bool +ThreadWinMiniDump::CalculateStopInfo() +{ + return false; +} diff --git a/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h new file mode 100644 index 000000000000..c78925422102 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h @@ -0,0 +1,49 @@ +//===-- ThreadWinMiniDump.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadWinMiniDump_h_ +#define liblldb_ThreadWinMiniDump_h_ + +#include + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/Thread.h" + +class ThreadWinMiniDump : public lldb_private::Thread +{ +public: + ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid); + + virtual + ~ThreadWinMiniDump(); + + void + RefreshStateAfterStop() override; + + lldb::RegisterContextSP + GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + void + ClearStackFrames() override; + + void + SetContext(const void *context); + +protected: + lldb::RegisterContextSP m_reg_context_sp; + class Data; + std::unique_ptr m_data; // for WinAPI-specific data + + bool CalculateStopInfo() override; +}; + +#endif diff --git a/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp new file mode 100644 index 000000000000..41d9195f1eed --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp @@ -0,0 +1,47 @@ +//===-- RegisterContextWindowsMiniDump_x64.cpp ------------------*- 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-types.h" +#include "lldb/Host/windows/windows.h" + +#include "RegisterContextWindowsMiniDump_x64.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsMiniDump_x64::RegisterContextWindowsMiniDump_x64(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context) + : RegisterContextWindows_x64(thread, concrete_frame_idx) +{ + if (context) + { + m_context = *context; + m_context_stale = false; + } +} + +RegisterContextWindowsMiniDump_x64::~RegisterContextWindowsMiniDump_x64() +{ +} + +bool +RegisterContextWindowsMiniDump_x64::WriteRegister(const RegisterInfo * /* reg_info */, const RegisterValue & /* reg_value */) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump_x64::CacheAllRegisterValues() +{ + // Since this is post-mortem debugging, we either have the context or we don't. + return !m_context_stale; +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h new file mode 100644 index 000000000000..86d58046113f --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsMiniDump_x64.h --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsMiniDump_x64_H_ +#define liblldb_RegisterContextWindowsMiniDump_x64_H_ + +#include "lldb/lldb-forward.h" +#include "Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsMiniDump_x64 : public RegisterContextWindows_x64 +{ + public: + RegisterContextWindowsMiniDump_x64(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context); + + virtual ~RegisterContextWindowsMiniDump_x64(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + protected: + bool CacheAllRegisterValues() override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsMiniDump_x64_H_ diff --git a/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp new file mode 100644 index 000000000000..2c8a069c5376 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp @@ -0,0 +1,47 @@ +//===-- RegisterContextWindowsMiniDump_x86.cpp ------------------*- 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-types.h" +#include "lldb/Host/windows/windows.h" + +#include "RegisterContextWindowsMiniDump_x86.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsMiniDump_x86::RegisterContextWindowsMiniDump_x86(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context) + : RegisterContextWindows_x86(thread, concrete_frame_idx) +{ + if (context) + { + m_context = *context; + m_context_stale = false; + } +} + +RegisterContextWindowsMiniDump_x86::~RegisterContextWindowsMiniDump_x86() +{ +} + +bool +RegisterContextWindowsMiniDump_x86::WriteRegister(const RegisterInfo * /* reg_info */, const RegisterValue & /* reg_value */) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump_x86::CacheAllRegisterValues() +{ + // Since this is post-mortem debugging, we either have the context or we don't. + return !m_context_stale; +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h new file mode 100644 index 000000000000..d36e0cfd9e7b --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsMiniDump_x86.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsMiniDump_x86_H_ +#define liblldb_RegisterContextWindowsMiniDump_x86_H_ + +#include "lldb/lldb-forward.h" +#include "Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsMiniDump_x86 : public RegisterContextWindows_x86 +{ + public: + RegisterContextWindowsMiniDump_x86(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context); + + virtual ~RegisterContextWindowsMiniDump_x86(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + protected: + bool CacheAllRegisterValues() override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsMiniDump_x86_H_ diff --git a/source/Plugins/Process/elf-core/CMakeLists.txt b/source/Plugins/Process/elf-core/CMakeLists.txt new file mode 100644 index 000000000000..1a4dd7e9d333 --- /dev/null +++ b/source/Plugins/Process/elf-core/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessElfCore + ProcessElfCore.cpp + ThreadElfCore.cpp + RegisterContextPOSIXCore_arm.cpp + RegisterContextPOSIXCore_arm64.cpp + RegisterContextPOSIXCore_mips64.cpp + RegisterContextPOSIXCore_powerpc.cpp + RegisterContextPOSIXCore_x86_64.cpp + ) diff --git a/source/Plugins/Process/elf-core/Makefile b/source/Plugins/Process/elf-core/Makefile new file mode 100644 index 000000000000..8c5b3b800f5a --- /dev/null +++ b/source/Plugins/Process/elf-core/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/elf-core/Makefile -----------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessElfCore +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/gdb-remote/CMakeLists.txt b/source/Plugins/Process/gdb-remote/CMakeLists.txt new file mode 100644 index 000000000000..8dbfa453f2c2 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/CMakeLists.txt @@ -0,0 +1,16 @@ +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + include_directories(${LIBXML2_INCLUDE_DIR}) +endif() + +add_lldb_library(lldbPluginProcessGDBRemote + GDBRemoteCommunication.cpp + GDBRemoteCommunicationClient.cpp + GDBRemoteCommunicationServer.cpp + GDBRemoteCommunicationServerCommon.cpp + GDBRemoteCommunicationServerLLGS.cpp + GDBRemoteCommunicationServerPlatform.cpp + GDBRemoteRegisterContext.cpp + ProcessGDBRemote.cpp + ProcessGDBRemoteLog.cpp + ThreadGDBRemote.cpp + ) diff --git a/source/Plugins/Process/gdb-remote/Makefile b/source/Plugins/Process/gdb-remote/Makefile new file mode 100644 index 000000000000..8a9b61077875 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/gdb-remote/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessGDBRemote +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 2e7a5b5384f4..be380a442e3c 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -109,35 +109,35 @@ namespace { { "target-definition-file" , OptionValue::eTypeFileSpec , true, 0 , NULL, NULL, "The file that provides the description for remote target registers." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; - + enum { ePropertyPacketTimeout, ePropertyTargetDefinitionFile }; - + class PluginProperties : public Properties { public: - + static ConstString GetSettingName () { return ProcessGDBRemote::GetPluginNameStatic(); } - + PluginProperties() : Properties () { m_collection_sp.reset (new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } - + virtual ~PluginProperties() { } - + uint64_t GetPacketTimeout() { @@ -159,9 +159,9 @@ namespace { return m_collection_sp->GetPropertyAtIndexAsFileSpec (NULL, idx); } }; - + typedef std::shared_ptr ProcessKDPPropertiesSP; - + static const ProcessKDPPropertiesSP & GetGlobalPluginProperties() { @@ -170,7 +170,7 @@ namespace { g_settings_sp.reset (new PluginProperties ()); return g_settings_sp; } - + } // anonymous namespace end class ProcessGDBRemote::GDBLoadedModuleInfoList @@ -446,7 +446,7 @@ ProcessGDBRemote::~ProcessGDBRemote() // destruct this class, then Process::~Process() might have problems // trying to fully destroy the broadcaster. Finalize(); - + // The general Finalize is going to try to destroy the process and that SHOULD // shut down the async thread. However, if we don't kill it it will get stranded and // its connection will go away so when it wakes up it will crash. So kill it for sure here. @@ -587,7 +587,7 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout); } - // Register info search order: + // Register info search order: // 1 - Use the target definition python file if one is specified. // 2 - If the target definition doesn't have any of the info from the target.xml (registers) then proceed to read the target.xml. // 3 - Fall back on the qRegisterInfo packets. @@ -614,12 +614,12 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) if (GetGDBServerRegisterInfo ()) return; - + char packet[128]; uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; - response_type == StringExtractorGDBRemote::eResponse; + response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { const int packet_len = ::snprintf (packet, sizeof(packet), "qRegisterInfo%x", reg_num); @@ -831,7 +831,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error (WillLaunchOrAttach ()); - + if (error.Fail()) return error; @@ -845,7 +845,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) if (pid == LLDB_INVALID_PROCESS_ID) { // We don't have a valid process ID, so note that we are connected - // and could now request to launch or attach, or get remote process + // and could now request to launch or attach, or get remote process // listings... SetPrivateState (eStateConnected); } @@ -864,7 +864,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) HandleStopReplySequence(); Target &target = GetTarget(); - if (!target.GetArchitecture().IsValid()) + if (!target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) { @@ -1058,11 +1058,11 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) m_gdb_comm.SetDetachOnError (launch_flags & eLaunchFlagDetachOnError); m_gdb_comm.SendLaunchArchPacket (GetTarget().GetArchitecture().GetArchitectureName()); - + const char * launch_event_data = launch_info.GetLaunchEventData(); if (launch_event_data != NULL && *launch_event_data != '\0') m_gdb_comm.SendLaunchEventDataPacket (launch_event_data); - + if (working_dir) { m_gdb_comm.SetWorkingDir (working_dir); @@ -1134,7 +1134,7 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) } SetPrivateState (SetThreadStopInfo (response)); - + if (!disable_stdio) { if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) @@ -1152,8 +1152,8 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) { // Set our user ID to an invalid process ID. SetID(LLDB_INVALID_PROCESS_ID); - error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", - exe_module->GetFileSpec().GetFilename().AsCString(), + error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", + exe_module->GetFileSpec().GetFilename().AsCString(), exe_module->GetArchitecture().GetArchitectureName()); } return error; @@ -1167,7 +1167,7 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) Error error; // Only connect if we have a valid connect URL Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); - + if (connect_url && connect_url[0]) { if (log) @@ -1189,9 +1189,9 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) // If we were interrupted, don't keep retrying. break; } - + retry_count++; - + if (retry_count >= max_retry_count) break; @@ -1216,7 +1216,7 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the - // handshake with the remote GDB server and make sure that goes + // handshake with the remote GDB server and make sure that goes // alright. if (!m_gdb_comm.HandshakeWithServer (&error)) { @@ -1382,7 +1382,7 @@ ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const Process char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); - SetID (attach_pid); + SetID (attach_pid); m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet, packet_len)); } else @@ -1405,9 +1405,9 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const Pro if (error.Success()) { StreamString packet; - + m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); - + if (attach_info.GetWaitForLaunch()) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) @@ -1426,7 +1426,7 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const Pro packet.PutCString("vAttachName"); packet.PutChar(';'); packet.PutBytesAsRawHex8(process_name, strlen(process_name), endian::InlHostByteOrder(), endian::InlHostByteOrder()); - + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet.GetData(), packet.GetSize())); } @@ -1471,12 +1471,12 @@ ProcessGDBRemote::DoResume () Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::Resume()"); - + Listener listener ("gdb-remote.resume-packet-sent"); if (listener.StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { listener.StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); - + const size_t num_threads = GetThreadList().GetSize(); StreamString continue_packet; @@ -1496,7 +1496,7 @@ ProcessGDBRemote::DoResume () else { continue_packet.PutCString ("vCont"); - + if (!m_continue_c_tids.empty()) { if (m_gdb_comm.GetVContSupported ('c')) @@ -1504,10 +1504,10 @@ ProcessGDBRemote::DoResume () for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); } - else + else continue_packet_error = true; } - + if (!continue_packet_error && !m_continue_C_tids.empty()) { if (m_gdb_comm.GetVContSupported ('C')) @@ -1515,7 +1515,7 @@ ProcessGDBRemote::DoResume () for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } - else + else continue_packet_error = true; } @@ -1526,10 +1526,10 @@ ProcessGDBRemote::DoResume () for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); } - else + else continue_packet_error = true; } - + if (!continue_packet_error && !m_continue_S_tids.empty()) { if (m_gdb_comm.GetVContSupported ('S')) @@ -1540,14 +1540,14 @@ ProcessGDBRemote::DoResume () else continue_packet_error = true; } - + if (continue_packet_error) continue_packet.GetString().clear(); } } else continue_packet_error = true; - + if (continue_packet_error) { // Either no vCont support, or we tried to use part of the vCont @@ -1563,33 +1563,33 @@ ProcessGDBRemote::DoResume () { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); - continue_packet.PutChar ('c'); + continue_packet.PutChar ('c'); continue_packet_error = false; } else if (num_continue_c_tids == 1 && - num_continue_C_tids == 0 && - num_continue_s_tids == 0 && + num_continue_C_tids == 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { // Only one thread is continuing m_gdb_comm.SetCurrentThreadForRun (m_continue_c_tids.front()); - continue_packet.PutChar ('c'); + continue_packet.PutChar ('c'); continue_packet_error = false; } } if (continue_packet_error && num_continue_C_tids > 0) { - if ((num_continue_C_tids + num_continue_c_tids) == num_threads && - num_continue_C_tids > 0 && - num_continue_s_tids == 0 && + if ((num_continue_C_tids + num_continue_c_tids) == num_threads && + num_continue_C_tids > 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { - // More that one thread with a signal, yet we don't have + // More that one thread with a signal, yet we don't have // vCont support and we are being asked to resume each // thread with a signal, we need to make sure they are // all the same signal, or we can't issue the continue @@ -1641,13 +1641,13 @@ ProcessGDBRemote::DoResume () continue_packet_error = false; } else if (num_continue_c_tids == 0 && - num_continue_C_tids == 0 && - num_continue_s_tids == 1 && + num_continue_C_tids == 0 && + num_continue_s_tids == 1 && num_continue_S_tids == 0 ) { // Only one thread is stepping m_gdb_comm.SetCurrentThreadForRun (m_continue_s_tids.front()); - continue_packet.PutChar ('s'); + continue_packet.PutChar ('s'); continue_packet_error = false; } } @@ -1675,8 +1675,8 @@ ProcessGDBRemote::DoResume () } } else if (num_continue_c_tids == 0 && - num_continue_C_tids == 0 && - num_continue_s_tids == 0 && + num_continue_C_tids == 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 1 ) { // Only one thread is stepping with signal @@ -1704,7 +1704,7 @@ ProcessGDBRemote::DoResume () log->Printf ("ProcessGDBRemote::DoResume: Trying to resume but the async thread is dead."); return error; } - + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (continue_packet.GetData(), continue_packet.GetSize())); if (listener.WaitForEvent (&timeout, event_sp) == false) @@ -1890,7 +1890,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf ("ProcessGDBRemote::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); - + size_t num_thread_ids = m_thread_ids.size(); // The "m_thread_ids" thread ID list should always be updated after each stop // reply packet, but in case it isn't, update it here. @@ -1926,7 +1926,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new thread_sp->GetID()); } // The m_thread_pcs vector has pc values in big-endian order, not target-endian, unlike most - // of the register read/write packets in gdb-remote protocol. + // of the register read/write packets in gdb-remote protocol. // Early in the process startup, we may not yet have set the process ByteOrder so we ignore these; // they are a performance improvement over fetching thread register values individually, the // method we will fall back to if needed. @@ -1936,7 +1936,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new RegisterContextSP reg_ctx_sp (thread_sp->GetRegisterContext()); if (reg_ctx_sp) { - uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber + uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); if (pc_regnum != LLDB_INVALID_REGNUM) { @@ -1947,7 +1947,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new new_thread_list.AddThread(thread_sp); } } - + // Whatever that is left in old_thread_list_copy are not // present in new_thread_list. Remove non-existent threads from internal id table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); @@ -1960,7 +1960,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new m_thread_id_to_index_id_map.erase(old_thread_id); } } - + return true; } @@ -2658,10 +2658,10 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) if (tid == LLDB_INVALID_THREAD_ID) { - // A thread id may be invalid if the response is old style 'S' packet which does not provide the + // A thread id may be invalid if the response is old style 'S' packet which does not provide the // thread information. So update the thread list and choose the first one. UpdateThreadIDList (); - + if (!m_thread_ids.empty ()) { tid = m_thread_ids.front (); @@ -2742,7 +2742,7 @@ ProcessGDBRemote::RefreshStateAfterStop () // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); - + } Error @@ -2752,7 +2752,7 @@ ProcessGDBRemote::DoHalt (bool &caused_stop) bool timed_out = false; Mutex::Locker locker; - + if (m_public_state.GetValue() == eStateAttaching) { // We are being asked to halt during an attach. We need to just close @@ -2768,7 +2768,7 @@ ProcessGDBRemote::DoHalt (bool &caused_stop) else error.SetErrorString("unknown error sending interrupt packet"); } - + caused_stop = m_gdb_comm.GetInterruptWasSent (); } return error; @@ -2781,7 +2781,7 @@ ProcessGDBRemote::DoDetach(bool keep_stopped) Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); - + error = m_gdb_comm.Detach (keep_stopped); if (log) { @@ -2790,7 +2790,7 @@ ProcessGDBRemote::DoDetach(bool keep_stopped) else log->Printf ("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : ""); } - + if (!error.Success()) return error; @@ -2833,7 +2833,7 @@ ProcessGDBRemote::DoDestroy () if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { PlatformSP platform_sp = GetTarget().GetPlatform(); - + // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. if (platform_sp && platform_sp->GetName() @@ -2845,18 +2845,18 @@ ProcessGDBRemote::DoDestroy () log->PutCString ("ProcessGDBRemote::DoDestroy() - Tried resuming to destroy once already, not doing it again."); } else - { + { // At present, the plans are discarded and the breakpoints disabled Process::Destroy, // but we really need it to happen here and it doesn't matter if we do it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); - + bool stop_looks_like_crash = false; ThreadList &threads = GetThreadList(); - + { Mutex::Locker locker(threads.GetMutex()); - + size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { @@ -2877,21 +2877,21 @@ ProcessGDBRemote::DoDestroy () } } } - + if (stop_looks_like_crash) { if (log) log->PutCString ("ProcessGDBRemote::DoDestroy() - Stopped at a breakpoint, continue and then kill."); m_destroy_tried_resuming = true; - - // If we are going to run again before killing, it would be good to suspend all the threads + + // If we are going to run again before killing, it would be good to suspend all the threads // before resuming so they won't get into more trouble. Sadly, for the threads stopped with // the breakpoint or exception, the exception doesn't get cleared if it is suspended, so we do // have to run the risk of letting those threads proceed a bit. - + { Mutex::Locker locker(threads.GetMutex()); - + size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { @@ -2916,7 +2916,7 @@ ProcessGDBRemote::DoDestroy () } } } - + // Interrupt if our inferior is running... int exit_status = SIGABRT; std::string exit_string; @@ -3098,7 +3098,7 @@ ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &erro 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 + // 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; } @@ -3156,7 +3156,7 @@ ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Erro 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 + // 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; } @@ -3191,7 +3191,7 @@ ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &er { Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; - + LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { @@ -3222,7 +3222,7 @@ ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &er } break; } - + if (allocated_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat("unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString (permissions)); else @@ -3231,10 +3231,10 @@ ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &er } Error -ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, +ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, MemoryRegionInfo ®ion_info) { - + Error error (m_gdb_comm.GetMemoryRegionInfo (load_addr, region_info)); return error; } @@ -3242,7 +3242,7 @@ ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, Error ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num) { - + Error error (m_gdb_comm.GetWatchpointSupportInfo (num)); return error; } @@ -3257,13 +3257,13 @@ ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num, bool& after) Error ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) { - Error error; + Error error; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: - // We should never be deallocating memory without allocating memory + // We should never be deallocating memory without allocating memory // first so we should never get eLazyBoolCalculate error.SetErrorString ("tried to deallocate memory without ever allocating memory"); break; @@ -3272,7 +3272,7 @@ ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) if (!m_gdb_comm.DeallocateMemory (addr)) error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); break; - + case eLazyBoolNo: // Call munmap() to deallocate memory in the inferior.. { @@ -3448,7 +3448,7 @@ ProcessGDBRemote::DisableBreakpointSite (BreakpointSite *bp_site) stoppoint_type = eBreakpointHardware; else stoppoint_type = eBreakpointSoftware; - + if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr, bp_op_size)) error.SetErrorToGenericError(); } @@ -3554,7 +3554,7 @@ ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify) wp->SetEnabled(false, notify); return error; } - + if (wp->IsHardware()) { GDBStoppointType type = GetGDBStoppointType(wp); @@ -3565,7 +3565,7 @@ ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify) return error; } else - error.SetErrorString("sending gdb watchpoint packet failed"); + error.SetErrorString("sending gdb watchpoint packet failed"); } // TODO: clear software watchpoints if we implement them } @@ -3669,7 +3669,7 @@ ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) StartAsyncThread (); - + if (error.Fail()) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); @@ -3678,7 +3678,7 @@ ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info log->Printf("failed to start debugserver process: %s", error.AsCString()); return error; } - + if (m_gdb_comm.IsConnected()) { // Finish the connection process by doing the handshake without connecting (send NULL URL) @@ -3708,7 +3708,7 @@ ProcessGDBRemote::MonitorDebugserverProcess // The baton is a "ProcessGDBRemote *". Now this class might be gone // and might not exist anymore, so we need to carefully try to get the // target for this process first since we have a race condition when - // we are done running between getting the notice that the inferior + // we are done running between getting the notice that the inferior // process has died and the debugserver that was debugging this process. // In our test suite, we are also continually running process after // process, so we must be very careful to make sure: @@ -3738,7 +3738,7 @@ ProcessGDBRemote::MonitorDebugserverProcess ProcessSP process_sp (target_sp->GetProcessSP()); // Now we have a shared pointer to the process that can't go away on us // so we now make sure it was the same as the one passed in, and also make - // sure that our previous "process *" didn't get deleted and have a new + // sure that our previous "process *" didn't get deleted and have a new // "process *" created in its place with the same pointer. To verify this // we make sure the process has our debugserver process ID. If we pass all // of these tests, then we are sure that this process is the one we were @@ -3752,7 +3752,7 @@ ProcessGDBRemote::MonitorDebugserverProcess // If our process hasn't yet exited, debugserver might have died. // If the process did exit, the we are reaping it. const StateType state = process->GetState(); - + if (process->m_debugserver_pid != LLDB_INVALID_PROCESS_ID && state != eStateInvalid && state != eStateUnloaded && @@ -3828,7 +3828,7 @@ ProcessGDBRemote::StartAsyncThread () if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); - + Mutex::Locker start_locker(m_async_thread_state_mutex); if (!m_async_thread.IsJoinable()) { @@ -3855,7 +3855,7 @@ ProcessGDBRemote::StopAsyncThread () if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); - + // This will shut down the async thread. m_gdb_comm.Disconnect(); // Disconnect from the debug server. @@ -3972,7 +3972,7 @@ ProcessGDBRemote::AsyncThread (void *arg) process->SetLastStopPacket (response); process->ClearThreadIDList(); response.SetFilePos(1); - + int exit_status = response.GetHexU8(); const char *desc_cstr = NULL; StringExtractor extractor; @@ -4091,7 +4091,7 @@ ProcessGDBRemote::AsyncThread (void *arg) // { // return Host::ListProcessesMatchingName (name, matches, pids); // } -// else +// else // { // // FIXME: Implement talking to the remote debugserver. // return 0; @@ -4105,7 +4105,7 @@ ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { - // I don't think I have to do anything here, just make sure I notice the new thread when it starts to + // I don't think I have to do anything here, just make sure I notice the new thread when it starts to // run so I can stop it if that's what I want to do. Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log) @@ -4148,7 +4148,7 @@ ProcessGDBRemote::StartNoticingNewThreads() bool ProcessGDBRemote::StopNoticingNewThreads() -{ +{ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf ("Disabling new thread notification breakpoint."); @@ -4158,7 +4158,7 @@ ProcessGDBRemote::StopNoticingNewThreads() return true; } - + DynamicLoader * ProcessGDBRemote::GetDynamicLoader () { @@ -4172,9 +4172,9 @@ ProcessGDBRemote::SendEventData(const char *data) { int return_value; bool was_supported; - + Error error; - + return_value = m_gdb_comm.SendLaunchEventDataPacket (data, &was_supported); if (return_value != 0) { @@ -4284,8 +4284,8 @@ ProcessGDBRemote::GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_addres // Establish the largest memory read/write payloads we should use. // If the remote stub has a max packet size, stay under that size. -// -// If the remote stub's max packet size is crazy large, use a +// +// If the remote stub's max packet size is crazy large, use a // reasonable largeish default. // // If the remote stub doesn't advertise a max packet size, use a @@ -4398,7 +4398,7 @@ struct RegisterSetInfo }; typedef std::map RegisterSetMap; - + struct GdbServerTargetInfo { std::string arch; @@ -4407,13 +4407,13 @@ struct GdbServerTargetInfo RegisterSetMap reg_set_map; XMLNode feature_node; }; - + bool ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp) { if (!feature_node) return false; - + uint32_t cur_reg_num = 0; uint32_t reg_offset = 0; @@ -4443,7 +4443,7 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot NULL, NULL }; - + reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, ®_name, &alt_name, &set_name, &value_regs, &invalidate_regs, &encoding_set, &format_set, ®_info, &cur_reg_num, ®_offset](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") { @@ -4538,7 +4538,7 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot } return true; // Keep iterating through all attributes }); - + if (!gdb_type.empty() && !(encoding_set || format_set)) { if (gdb_type.find("int") == 0) @@ -4557,12 +4557,12 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot reg_info.encoding = eEncodingIEEE754; } } - + // Only update the register set name if we didn't get a "reg_set" attribute. // "set_name" will be empty if we didn't have a "reg_set" attribute. if (!set_name && !gdb_group.empty()) set_name.SetCString(gdb_group.c_str()); - + reg_info.byte_offset = reg_offset; assert (reg_info.byte_size != 0); reg_offset += reg_info.byte_size; @@ -4576,16 +4576,16 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } - + ++cur_reg_num; AugmentRegisterInfoViaABI (reg_info, reg_name, abi_sp); dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); - + return true; // Keep iterating through all "reg" elements }); return true; } - + } // namespace {} @@ -4617,14 +4617,14 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () { return false; } - + XMLDocument xml_document; if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) { GdbServerTargetInfo target_info; - + XMLNode target_node = xml_document.GetRootElement("target"); if (target_node) { @@ -4655,7 +4655,7 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () node.ForEachChildElementWithName("group", [&target_info](const XMLNode &node) -> bool { uint32_t set_id = UINT32_MAX; RegisterSetInfo set_info; - + node.ForEachAttribute([&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "id") set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); @@ -4663,7 +4663,7 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () set_info.name = ConstString(value); return true; // Keep iterating through all attributes }); - + if (set_id != UINT32_MAX) target_info.reg_set_map[set_id] = set_info; return true; // Keep iterating through all "group" elements @@ -4671,12 +4671,12 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () } return true; // Keep iterating through all children of the target_node }); - + if (feature_node) { ParseRegisters(feature_node, target_info, this->m_register_info, GetABI()); } - + for (const auto &include : target_info.includes) { // request register file @@ -4730,7 +4730,7 @@ ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) if (log) log->Printf ("parsing: %s", raw.c_str()); XMLDocument doc; - + if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Error (0, ErrorType::eErrorTypeGeneric); @@ -4750,7 +4750,7 @@ ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) GDBLoadedModuleInfoList::LoadedModuleInfo module; library.ForEachAttribute([log, &module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { - + if (name == "name") module.set_name (value.str()); else if (name == "lm") @@ -4770,7 +4770,7 @@ ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) // the memory address of the libraries PT_DYAMIC section. module.set_dynamic(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); } - + return true; // Keep iterating over all properties of "library" }); @@ -5075,7 +5075,7 @@ class CommandObjectProcessGDBRemoteSpeedTest: public CommandObjectParsed class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, @@ -5084,11 +5084,11 @@ class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed NULL) { } - + ~CommandObjectProcessGDBRemotePacketHistory () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { @@ -5115,7 +5115,7 @@ class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, @@ -5124,11 +5124,11 @@ class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed NULL) { } - + ~CommandObjectProcessGDBRemotePacketXferSize () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { @@ -5162,7 +5162,7 @@ class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { private: - + public: CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, @@ -5172,11 +5172,11 @@ class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed NULL) { } - + ~CommandObjectProcessGDBRemotePacketSend () { } - + bool DoExecute (Args& command, CommandReturnObject &result) override { @@ -5187,7 +5187,7 @@ class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed result.SetStatus (eReturnStatusFailed); return false; } - + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { @@ -5201,7 +5201,7 @@ class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); std::string &response_str = response.GetStringRef(); - + if (strstr(packet_cstr, "qGetProfileData") != NULL) { response_str = process->GetGDBRemote().HarmonizeThreadIdsForProfileData(process, response); @@ -5220,7 +5220,7 @@ class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { private: - + public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw (interpreter, @@ -5230,11 +5230,11 @@ class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw NULL) { } - + ~CommandObjectProcessGDBRemotePacketMonitor () { } - + bool DoExecute (const char *command, CommandReturnObject &result) override { @@ -5244,7 +5244,7 @@ class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw result.SetStatus (eReturnStatusFailed); return false; } - + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { @@ -5252,7 +5252,7 @@ class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw packet.PutCString("qRcmd,"); packet.PutBytesAsRawHex8(command, strlen(command)); const char *packet_cstr = packet.GetString().c_str(); - + bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); @@ -5260,7 +5260,7 @@ class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); const std::string &response_str = response.GetStringRef(); - + if (response_str.empty()) output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); else @@ -5273,7 +5273,7 @@ class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { private: - + public: CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, @@ -5287,10 +5287,10 @@ class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword LoadSubCommand ("xfer-size", CommandObjectSP (new CommandObjectProcessGDBRemotePacketXferSize (interpreter))); LoadSubCommand ("speed-test", CommandObjectSP (new CommandObjectProcessGDBRemoteSpeedTest (interpreter))); } - + ~CommandObjectProcessGDBRemotePacket () { - } + } }; class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword diff --git a/source/Plugins/Process/mach-core/CMakeLists.txt b/source/Plugins/Process/mach-core/CMakeLists.txt new file mode 100644 index 000000000000..ac54658cf4dc --- /dev/null +++ b/source/Plugins/Process/mach-core/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessMachCore + ProcessMachCore.cpp + ThreadMachCore.cpp + ) diff --git a/source/Plugins/Process/mach-core/Makefile b/source/Plugins/Process/mach-core/Makefile new file mode 100644 index 000000000000..6db849872267 --- /dev/null +++ b/source/Plugins/Process/mach-core/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/mach-core/Makefile -----------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessMachCore +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/source/Plugins/Process/mach-core/ProcessMachCore.cpp new file mode 100644 index 000000000000..b199ec606367 --- /dev/null +++ b/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -0,0 +1,520 @@ +//===-- ProcessMachCore.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 + +// C++ Includes +#include "llvm/Support/MathExtras.h" +#include + +// Other libraries and framework includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +// Project includes +#include "ProcessMachCore.h" +#include "ThreadMachCore.h" +#include "StopInfoMachException.h" + +// Needed for the plug-in names for the dynamic loaders. +#include "lldb/Utility/SafeMachO.h" + +#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" +#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" + +using namespace lldb; +using namespace lldb_private; + +ConstString +ProcessMachCore::GetPluginNameStatic() +{ + static ConstString g_name("mach-o-core"); + return g_name; +} + +const char * +ProcessMachCore::GetPluginDescriptionStatic() +{ + return "Mach-O core file debugging plug-in."; +} + +void +ProcessMachCore::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessMachCore::CreateInstance); +} + + +lldb::ProcessSP +ProcessMachCore::CreateInstance (lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file) +{ + lldb::ProcessSP process_sp; + if (crash_file) + { + const size_t header_size = sizeof(llvm::MachO::mach_header); + lldb::DataBufferSP data_sp (crash_file->ReadFileContents(0, header_size)); + if (data_sp && data_sp->GetByteSize() == header_size) + { + DataExtractor data(data_sp, lldb::eByteOrderLittle, 4); + + lldb::offset_t data_offset = 0; + llvm::MachO::mach_header mach_header; + if (ObjectFileMachO::ParseHeader(data, &data_offset, mach_header)) + { + if (mach_header.filetype == llvm::MachO::MH_CORE) + process_sp.reset(new ProcessMachCore (target_sp, listener, *crash_file)); + } + } + + } + return process_sp; +} + +bool +ProcessMachCore::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + if (!m_core_module_sp && m_core_file.Exists()) + { + // Don't add the Target's architecture to the ModuleSpec - we may be working + // with a core file that doesn't have the correct cpusubtype in the header + // but we should still try to use it - ModuleSpecList::FindMatchingModuleSpec + // enforces a strict arch mach. + ModuleSpec core_module_spec(m_core_file); + Error error (ModuleList::GetSharedModule (core_module_spec, + m_core_module_sp, + NULL, + NULL, + NULL)); + + if (m_core_module_sp) + { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile) + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// ProcessMachCore constructor +//---------------------------------------------------------------------- +ProcessMachCore::ProcessMachCore(lldb::TargetSP target_sp, Listener &listener, const FileSpec &core_file) : + Process (target_sp, listener), + m_core_aranges (), + m_core_module_sp (), + m_core_file (core_file), + m_dyld_addr (LLDB_INVALID_ADDRESS), + m_mach_kernel_addr (LLDB_INVALID_ADDRESS), + m_dyld_plugin_name () +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessMachCore::~ProcessMachCore() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +ConstString +ProcessMachCore::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessMachCore::GetPluginVersion() +{ + return 1; +} + +bool +ProcessMachCore::GetDynamicLoaderAddress (lldb::addr_t addr) +{ + llvm::MachO::mach_header header; + Error error; + if (DoReadMemory (addr, &header, sizeof(header), error) != sizeof(header)) + return false; + if (header.magic == llvm::MachO::MH_CIGAM || + header.magic == llvm::MachO::MH_CIGAM_64) + { + header.magic = llvm::ByteSwap_32(header.magic); + header.cputype = llvm::ByteSwap_32(header.cputype); + header.cpusubtype = llvm::ByteSwap_32(header.cpusubtype); + header.filetype = llvm::ByteSwap_32(header.filetype); + header.ncmds = llvm::ByteSwap_32(header.ncmds); + header.sizeofcmds = llvm::ByteSwap_32(header.sizeofcmds); + header.flags = llvm::ByteSwap_32(header.flags); + } + + // TODO: swap header if needed... + //printf("0x%16.16" PRIx64 ": magic = 0x%8.8x, file_type= %u\n", vaddr, header.magic, header.filetype); + if (header.magic == llvm::MachO::MH_MAGIC || + header.magic == llvm::MachO::MH_MAGIC_64) + { + // Check MH_EXECUTABLE to see if we can find the mach image + // that contains the shared library list. The dynamic loader + // (dyld) is what contains the list for user applications, + // and the mach kernel contains a global that has the list + // of kexts to load + switch (header.filetype) + { + case llvm::MachO::MH_DYLINKER: + //printf("0x%16.16" PRIx64 ": file_type = MH_DYLINKER\n", vaddr); + // Address of dyld "struct mach_header" in the core file + m_dyld_addr = addr; + return true; + + case llvm::MachO::MH_EXECUTE: + //printf("0x%16.16" PRIx64 ": file_type = MH_EXECUTE\n", vaddr); + // Check MH_EXECUTABLE file types to see if the dynamic link object flag + // is NOT set. If it isn't, then we have a mach_kernel. + if ((header.flags & llvm::MachO::MH_DYLDLINK) == 0) + { + // Address of the mach kernel "struct mach_header" in the core file. + m_mach_kernel_addr = addr; + return true; + } + break; + } + } + return false; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessMachCore::DoLoadCore () +{ + Error error; + if (!m_core_module_sp) + { + error.SetErrorString ("invalid core module"); + return error; + } + + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile == NULL) + { + error.SetErrorString ("invalid core object file"); + return error; + } + + if (core_objfile->GetNumThreadContexts() == 0) + { + error.SetErrorString ("core file doesn't contain any LC_THREAD load commands, or the LC_THREAD architecture is not supported in this lldb"); + return error; + } + + SectionList *section_list = core_objfile->GetSectionList(); + if (section_list == NULL) + { + error.SetErrorString ("core file has no sections"); + return error; + } + + const uint32_t num_sections = section_list->GetNumSections(0); + if (num_sections == 0) + { + error.SetErrorString ("core file has no sections"); + return error; + } + + SetCanJIT(false); + + llvm::MachO::mach_header header; + DataExtractor data (&header, + sizeof(header), + m_core_module_sp->GetArchitecture().GetByteOrder(), + m_core_module_sp->GetArchitecture().GetAddressByteSize()); + + bool ranges_are_sorted = true; + addr_t vm_addr = 0; + for (uint32_t i=0; iGetSectionAtIndex (i).get(); + if (section) + { + lldb::addr_t section_vm_addr = section->GetFileAddress(); + FileRange file_range (section->GetFileOffset(), section->GetFileSize()); + VMRangeToFileOffset::Entry range_entry (section_vm_addr, + section->GetByteSize(), + file_range); + + if (vm_addr > section_vm_addr) + ranges_are_sorted = false; + vm_addr = section->GetFileAddress(); + VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); +// printf ("LC_SEGMENT[%u] arange=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), frange=[0x%8.8x - 0x%8.8x)\n", +// i, +// range_entry.GetRangeBase(), +// range_entry.GetRangeEnd(), +// range_entry.data.GetRangeBase(), +// range_entry.data.GetRangeEnd()); + + if (last_entry && + last_entry->GetRangeEnd() == range_entry.GetRangeBase() && + last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase()) + { + last_entry->SetRangeEnd (range_entry.GetRangeEnd()); + last_entry->data.SetRangeEnd (range_entry.data.GetRangeEnd()); + //puts("combine"); + } + else + { + m_core_aranges.Append(range_entry); + } + } + } + if (!ranges_are_sorted) + { + m_core_aranges.Sort(); + } + + if (m_dyld_addr == LLDB_INVALID_ADDRESS || m_mach_kernel_addr == LLDB_INVALID_ADDRESS) + { + // We need to locate the main executable in the memory ranges + // we have in the core file. We need to search for both a user-process dyld binary + // and a kernel binary in memory; we must look at all the pages in the binary so + // we don't miss one or the other. Step through all memory segments searching for + // a kernel binary and for a user process dyld -- we'll decide which to prefer + // later if both are present. + + const size_t num_core_aranges = m_core_aranges.GetSize(); + for (size_t i = 0; + i < num_core_aranges && (m_dyld_addr == LLDB_INVALID_ADDRESS || m_mach_kernel_addr == LLDB_INVALID_ADDRESS); + ++i) + { + const VMRangeToFileOffset::Entry *entry = m_core_aranges.GetEntryAtIndex(i); + lldb::addr_t section_vm_addr_start = entry->GetRangeBase(); + lldb::addr_t section_vm_addr_end = entry->GetRangeEnd(); + for (lldb::addr_t section_vm_addr = section_vm_addr_start; + section_vm_addr < section_vm_addr_end; + section_vm_addr += 0x1000) + { + GetDynamicLoaderAddress (section_vm_addr); + } + } + } + + // If we found both a user-process dyld and a kernel binary, we need to decide + // which to prefer. + if (GetCorefilePreference() == eKernelCorefile) + { + if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) + { + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + } + else if (m_dyld_addr != LLDB_INVALID_ADDRESS) + { + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } + } + else + { + if (m_dyld_addr != LLDB_INVALID_ADDRESS) + { + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } + else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) + { + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + } + } + + // Even if the architecture is set in the target, we need to override + // it to match the core file which is always single arch. + ArchSpec arch (m_core_module_sp->GetArchitecture()); + if (arch.GetCore() == ArchSpec::eCore_x86_32_i486) + { + arch.SetTriple ("i386", GetTarget().GetPlatform().get()); + } + if (arch.IsValid()) + GetTarget().SetArchitecture(arch); + + return error; +} + +lldb_private::DynamicLoader * +ProcessMachCore::GetDynamicLoader () +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, m_dyld_plugin_name.IsEmpty() ? NULL : m_dyld_plugin_name.GetCString())); + return m_dyld_ap.get(); +} + +bool +ProcessMachCore::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + if (old_thread_list.GetSize(false) == 0) + { + // Make up the thread the first time this is called so we can setup our one and only + // core thread state. + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + + if (core_objfile) + { + const uint32_t num_threads = core_objfile->GetNumThreadContexts (); + for (lldb::tid_t tid = 0; tid < num_threads; ++tid) + { + ThreadSP thread_sp(new ThreadMachCore (*this, tid)); + new_thread_list.AddThread (thread_sp); + } + } + } + else + { + const uint32_t num_threads = old_thread_list.GetSize(false); + for (uint32_t i=0; i 0; +} + +void +ProcessMachCore::RefreshStateAfterStop () +{ + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); + //SetThreadStopInfo (m_last_stop_packet); +} + +Error +ProcessMachCore::DoDestroy () +{ + return Error(); +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessMachCore::IsAlive () +{ + return true; +} + +bool +ProcessMachCore::WarnBeforeDetach () const +{ + return false; +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessMachCore::ReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since in core files we have it all cached our our core file anyway. + return DoReadMemory (addr, buf, size, error); +} + +size_t +ProcessMachCore::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + + if (core_objfile) + { + const VMRangeToFileOffset::Entry *core_memory_entry = m_core_aranges.FindEntryThatContains (addr); + if (core_memory_entry) + { + const addr_t offset = addr - core_memory_entry->GetRangeBase(); + const addr_t bytes_left = core_memory_entry->GetRangeEnd() - addr; + size_t bytes_to_read = size; + if (bytes_to_read > bytes_left) + bytes_to_read = bytes_left; + return core_objfile->CopyData (core_memory_entry->data.GetRangeBase() + offset, bytes_to_read, buf); + } + else + { + error.SetErrorStringWithFormat ("core file does not contain 0x%" PRIx64, addr); + } + } + return 0; +} + +void +ProcessMachCore::Clear() +{ + m_thread_list.Clear(); +} + +void +ProcessMachCore::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +addr_t +ProcessMachCore::GetImageInfoAddress() +{ + // If we found both a user-process dyld and a kernel binary, we need to decide + // which to prefer. + if (GetCorefilePreference() == eKernelCorefile) + { + if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) + { + return m_mach_kernel_addr; + } + return m_dyld_addr; + } + else + { + if (m_dyld_addr != LLDB_INVALID_ADDRESS) + { + return m_dyld_addr; + } + return m_mach_kernel_addr; + } +} + + +lldb_private::ObjectFile * +ProcessMachCore::GetCoreObjectFile () +{ + return m_core_module_sp->GetObjectFile(); +} diff --git a/source/Plugins/Process/mach-core/ProcessMachCore.h b/source/Plugins/Process/mach-core/ProcessMachCore.h new file mode 100644 index 000000000000..2de0b772370c --- /dev/null +++ b/source/Plugins/Process/mach-core/ProcessMachCore.h @@ -0,0 +1,164 @@ +//===-- ProcessMachCore.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessMachCore_h_ +#define liblldb_ProcessMachCore_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +class ThreadKDP; + +class ProcessMachCore : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessMachCore(lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec &core_file); + + ~ProcessMachCore() override; + + static lldb::ProcessSP + CreateInstance (lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + bool + CanDebug (lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + lldb_private::Error + DoLoadCore () override; + + lldb_private::DynamicLoader * + GetDynamicLoader () override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + lldb_private::Error + DoDestroy () override; + + void + RefreshStateAfterStop() override; + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + bool + IsAlive () override; + + bool + WarnBeforeDetach () const override; + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + size_t + ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + lldb::addr_t + GetImageInfoAddress () override; + +protected: + friend class ThreadMachCore; + + void + Clear ( ); + + bool + UpdateThreadList (lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + lldb_private::ObjectFile * + GetCoreObjectFile (); +private: + bool + GetDynamicLoaderAddress (lldb::addr_t addr); + + typedef enum CorefilePreference { eUserProcessCorefile, eKernelCorefile } CorefilePreferences; + + //------------------------------------------------------------------ + /// If a core file can be interpreted multiple ways, this establishes + /// which style wins. + /// + /// If a core file contains both a kernel binary and a user-process + /// dynamic loader, lldb needs to pick one over the other. This could + /// be a kernel corefile that happens to have a coyp of dyld in its + /// memory. Or it could be a user process coredump of lldb while doing + /// kernel debugging - so a copy of the kernel is in its heap. This + /// should become a setting so it can be over-ridden when necessary. + //------------------------------------------------------------------ + CorefilePreference + GetCorefilePreference () + { + // For now, if both user process and kernel binaries a present, + // assume this is a kernel coredump which has a copy of a user + // process dyld in one of its pages. + return eKernelCorefile; + } + + //------------------------------------------------------------------ + // For ProcessMachCore only + //------------------------------------------------------------------ + typedef lldb_private::Range FileRange; + typedef lldb_private::RangeDataVector VMRangeToFileOffset; + + VMRangeToFileOffset m_core_aranges; + lldb::ModuleSP m_core_module_sp; + lldb_private::FileSpec m_core_file; + lldb::addr_t m_dyld_addr; + lldb::addr_t m_mach_kernel_addr; + lldb_private::ConstString m_dyld_plugin_name; + + DISALLOW_COPY_AND_ASSIGN (ProcessMachCore); +}; + +#endif // liblldb_ProcessMachCore_h_ diff --git a/source/Plugins/Process/mach-core/ThreadMachCore.cpp b/source/Plugins/Process/mach-core/ThreadMachCore.cpp new file mode 100644 index 000000000000..2720c910e4d1 --- /dev/null +++ b/source/Plugins/Process/mach-core/ThreadMachCore.cpp @@ -0,0 +1,132 @@ +//===-- ThreadMachCore.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadMachCore.h" + +#include "lldb/Utility/SafeMachO.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/State.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Breakpoint/Watchpoint.h" + +#include "ProcessMachCore.h" +//#include "RegisterContextKDP_arm.h" +//#include "RegisterContextKDP_i386.h" +//#include "RegisterContextKDP_x86_64.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadMachCore::ThreadMachCore (Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_thread_name (), + m_dispatch_queue_name (), + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS), + m_thread_reg_ctx_sp () +{ +} + +ThreadMachCore::~ThreadMachCore () +{ + DestroyThread(); +} + +const char * +ThreadMachCore::GetName () +{ + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + +void +ThreadMachCore::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The KDPRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + const bool force = false; + GetRegisterContext()->InvalidateIfNeeded (force); +} + +bool +ThreadMachCore::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +lldb::RegisterContextSP +ThreadMachCore::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadMachCore::CreateRegisterContextForFrame (StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex (); + + if (concrete_frame_idx == 0) + { + if (!m_thread_reg_ctx_sp) + { + ProcessSP process_sp (GetProcess()); + + ObjectFile *core_objfile = static_cast(process_sp.get())->GetCoreObjectFile (); + if (core_objfile) + m_thread_reg_ctx_sp = core_objfile->GetThreadContextAtIndex (GetID(), *this); + } + reg_ctx_sp = m_thread_reg_ctx_sp; + } + else + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame); + } + return reg_ctx_sp; +} + +bool +ThreadMachCore::CalculateStopInfo () +{ + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP)); + return true; + } + return false; +} + + diff --git a/source/Plugins/Process/mach-core/ThreadMachCore.h b/source/Plugins/Process/mach-core/ThreadMachCore.h new file mode 100644 index 000000000000..25973540db16 --- /dev/null +++ b/source/Plugins/Process/mach-core/ThreadMachCore.h @@ -0,0 +1,91 @@ +//===-- ThreadMachCore.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadMachCore_h_ +#define liblldb_ThreadMachCore_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" + +class ProcessMachCore; + +class ThreadMachCore : public lldb_private::Thread +{ +public: + ThreadMachCore (lldb_private::Process &process, + lldb::tid_t tid); + + ~ThreadMachCore() override; + + void + RefreshStateAfterStop() override; + + const char * + GetName() override; + + lldb::RegisterContextSP + GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + bool + ShouldStop (bool &step_more); + + const char * + GetBasicInfoAsString (); + + void + SetName(const char *name) override + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + + lldb::addr_t + GetThreadDispatchQAddr () + { + return m_thread_dispatch_qaddr; + } + + void + SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr) + { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + +protected: + friend class ProcessMachCore; + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + lldb::RegisterContextSP m_thread_reg_ctx_sp; + + //------------------------------------------------------------------ + // Protected member functions. + //------------------------------------------------------------------ + bool + CalculateStopInfo() override; +}; + +#endif // liblldb_ThreadMachCore_h_ diff --git a/source/Plugins/ScriptInterpreter/CMakeLists.txt b/source/Plugins/ScriptInterpreter/CMakeLists.txt new file mode 100644 index 000000000000..dc2a27d95a31 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(None) +add_subdirectory(Python) diff --git a/source/Plugins/ScriptInterpreter/None/CMakeLists.txt b/source/Plugins/ScriptInterpreter/None/CMakeLists.txt new file mode 100644 index 000000000000..5692d2f90710 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/None/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginScriptInterpreterNone + ScriptInterpreterNone.cpp + ) \ No newline at end of file diff --git a/source/Plugins/ScriptInterpreter/None/Makefile b/source/Plugins/ScriptInterpreter/None/Makefile new file mode 100644 index 000000000000..1e2523198520 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/None/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ScriptInterpreter/None/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginScriptInterpreterNone +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt new file mode 100644 index 000000000000..71f5807b6f62 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_library(lldbPluginScriptInterpreterPython + PythonDataObjects.cpp + PythonExceptionState.cpp + ScriptInterpreterPython.cpp + ) diff --git a/source/Plugins/ScriptInterpreter/Python/Makefile b/source/Plugins/ScriptInterpreter/Python/Makefile new file mode 100644 index 000000000000..cf605014d458 --- /dev/null +++ b/source/Plugins/ScriptInterpreter/Python/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/ScriptInterpreter/Python/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginScriptInterpreterPython +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolFile/CMakeLists.txt b/source/Plugins/SymbolFile/CMakeLists.txt new file mode 100644 index 000000000000..add6697389f9 --- /dev/null +++ b/source/Plugins/SymbolFile/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(DWARF) +add_subdirectory(Symtab) diff --git a/source/Plugins/SymbolFile/DWARF/CMakeLists.txt b/source/Plugins/SymbolFile/DWARF/CMakeLists.txt new file mode 100644 index 000000000000..b4658115dfeb --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/CMakeLists.txt @@ -0,0 +1,33 @@ +add_lldb_library(lldbPluginSymbolFileDWARF + DIERef.cpp + DWARFAbbreviationDeclaration.cpp + DWARFASTParserClang.cpp + DWARFASTParserGo.cpp + DWARFAttribute.cpp + DWARFCompileUnit.cpp + DWARFDataExtractor.cpp + DWARFDebugAbbrev.cpp + DWARFDebugAranges.cpp + DWARFDebugArangeSet.cpp + DWARFDebugInfo.cpp + DWARFDebugInfoEntry.cpp + DWARFDebugLine.cpp + DWARFDebugMacro.cpp + DWARFDebugMacinfo.cpp + DWARFDebugMacinfoEntry.cpp + DWARFDebugPubnames.cpp + DWARFDebugPubnamesSet.cpp + DWARFDebugRanges.cpp + DWARFDeclContext.cpp + DWARFDefines.cpp + DWARFDIE.cpp + DWARFDIECollection.cpp + DWARFFormValue.cpp + HashedNameToDIE.cpp + LogChannelDWARF.cpp + NameToDIE.cpp + SymbolFileDWARF.cpp + SymbolFileDWARFDwo.cpp + SymbolFileDWARFDebugMap.cpp + UniqueDWARFASTType.cpp + ) diff --git a/source/Plugins/SymbolFile/DWARF/Makefile b/source/Plugins/SymbolFile/DWARF/Makefile new file mode 100644 index 000000000000..509065650ab9 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SymbolFile/DWARF/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginSymbolFileDWARF +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp b/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp index 775bb6718b8a..fe02adbb6c87 100644 --- a/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp +++ b/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp @@ -69,7 +69,7 @@ NameToDIE::Dump (Stream *s) { const char *cstr = m_map.GetCStringAtIndex(i); const DIERef& die_ref = m_map.GetValueAtIndexUnchecked(i); - s->Printf("%p: {0x%8.8x/0x%8.8x} \"%s\"\n", cstr, die_ref.cu_offset, die_ref.die_offset, cstr); + s->Printf("%p: {0x%8.8x/0x%8.8x} \"%s\"\n", (const void*) cstr, die_ref.cu_offset, die_ref.die_offset, cstr); } } diff --git a/source/Plugins/SymbolFile/Symtab/CMakeLists.txt b/source/Plugins/SymbolFile/Symtab/CMakeLists.txt new file mode 100644 index 000000000000..20e406b08ab2 --- /dev/null +++ b/source/Plugins/SymbolFile/Symtab/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginSymbolFileSymtab + SymbolFileSymtab.cpp + ) diff --git a/source/Plugins/SymbolFile/Symtab/Makefile b/source/Plugins/SymbolFile/Symtab/Makefile new file mode 100644 index 000000000000..2c3dbb6d86ab --- /dev/null +++ b/source/Plugins/SymbolFile/Symtab/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SymbolFile/Symtab/Makefile -----------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginSymbolFileSymtab +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolVendor/CMakeLists.txt b/source/Plugins/SymbolVendor/CMakeLists.txt new file mode 100644 index 000000000000..94862d588727 --- /dev/null +++ b/source/Plugins/SymbolVendor/CMakeLists.txt @@ -0,0 +1,5 @@ +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(MacOSX) +endif() + +add_subdirectory(ELF) diff --git a/source/Plugins/SymbolVendor/ELF/CMakeLists.txt b/source/Plugins/SymbolVendor/ELF/CMakeLists.txt new file mode 100644 index 000000000000..cffc2ef74597 --- /dev/null +++ b/source/Plugins/SymbolVendor/ELF/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginSymbolVendorELF + SymbolVendorELF.cpp + ) diff --git a/source/Plugins/SymbolVendor/ELF/Makefile b/source/Plugins/SymbolVendor/ELF/Makefile new file mode 100644 index 000000000000..47c24a2bda34 --- /dev/null +++ b/source/Plugins/SymbolVendor/ELF/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SymbolVendor/ELF/Makefile ---------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginSymbolVendorELF +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolVendor/MacOSX/CMakeLists.txt b/source/Plugins/SymbolVendor/MacOSX/CMakeLists.txt new file mode 100644 index 000000000000..093766ac07d5 --- /dev/null +++ b/source/Plugins/SymbolVendor/MacOSX/CMakeLists.txt @@ -0,0 +1,5 @@ +include_directories(${LIBXML2_INCLUDE_DIR}) + +add_lldb_library(lldbPluginSymbolVendorMacOSX + SymbolVendorMacOSX.cpp + ) diff --git a/source/Plugins/SymbolVendor/MacOSX/Makefile b/source/Plugins/SymbolVendor/MacOSX/Makefile new file mode 100644 index 000000000000..9f71ad669aa0 --- /dev/null +++ b/source/Plugins/SymbolVendor/MacOSX/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SymbolVendor/MacOSX/Makefile ---------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginSymbolVendorMacOSX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp b/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp new file mode 100644 index 000000000000..7d21fbc0ede4 --- /dev/null +++ b/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp @@ -0,0 +1,251 @@ +//===-- 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 + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Host/XML.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SymbolVendorMacOSX constructor +//---------------------------------------------------------------------- +SymbolVendorMacOSX::SymbolVendorMacOSX(const lldb::ModuleSP &module_sp) : + SymbolVendor (module_sp) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SymbolVendorMacOSX::~SymbolVendorMacOSX() +{ +} + + +static bool +UUIDsMatch(Module *module, ObjectFile *ofile, lldb_private::Stream *feedback_strm) +{ + if (module && ofile) + { + // Make sure the UUIDs match + lldb_private::UUID dsym_uuid; + + if (!ofile->GetUUID(&dsym_uuid)) + { + if (feedback_strm) + { + feedback_strm->PutCString("warning: failed to get the uuid for object file: '"); + ofile->GetFileSpec().Dump(feedback_strm); + feedback_strm->PutCString("\n"); + } + return false; + } + + if (dsym_uuid == module->GetUUID()) + return true; + + // Emit some warning messages since the UUIDs do not match! + if (feedback_strm) + { + feedback_strm->PutCString("warning: UUID mismatch detected between modules:\n "); + module->GetUUID().Dump(feedback_strm); + feedback_strm->PutChar(' '); + module->GetFileSpec().Dump(feedback_strm); + feedback_strm->PutCString("\n "); + dsym_uuid.Dump(feedback_strm); + feedback_strm->PutChar(' '); + ofile->GetFileSpec().Dump(feedback_strm); + feedback_strm->EOL(); + } + } + return false; +} + +void +SymbolVendorMacOSX::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolVendorMacOSX::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +SymbolVendorMacOSX::GetPluginNameStatic() +{ + static ConstString g_name("macosx"); + return g_name; +} + +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 (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm) +{ + if (!module_sp) + return NULL; + + ObjectFile * obj_file = module_sp->GetObjectFile(); + if (!obj_file) + return NULL; + + static ConstString obj_file_macho("mach-o"); + ConstString obj_name = obj_file->GetPluginName(); + if (obj_name != obj_file_macho) + return NULL; + + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolVendorMacOSX::CreateInstance (module = %s)", + module_sp->GetFileSpec().GetPath().c_str()); + SymbolVendorMacOSX* symbol_vendor = new SymbolVendorMacOSX(module_sp); + if (symbol_vendor) + { + char path[PATH_MAX]; + path[0] = '\0'; + + // Try and locate the dSYM file on Mac OS X + Timer scoped_timer2 ("SymbolVendorMacOSX::CreateInstance () locate dSYM", + "SymbolVendorMacOSX::CreateInstance (module = %s) locate dSYM", + module_sp->GetFileSpec().GetPath().c_str()); + + // First check to see if the module has a symbol file in mind already. + // If it does, then we MUST use that. + FileSpec dsym_fspec (module_sp->GetSymbolFileFileSpec()); + + ObjectFileSP dsym_objfile_sp; + if (!dsym_fspec) + { + // No symbol file was specified in the module, lets try and find + // one ourselves. + FileSpec file_spec = obj_file->GetFileSpec(); + if (!file_spec) + file_spec = module_sp->GetFileSpec(); + + ModuleSpec module_spec(file_spec, module_sp->GetArchitecture()); + module_spec.GetUUID() = module_sp->GetUUID(); + dsym_fspec = Symbols::LocateExecutableSymbolFile (module_spec); + if (module_spec.GetSourceMappingList().GetSize()) + module_sp->GetSourceMappingList().Append (module_spec.GetSourceMappingList (), true); + } + + if (dsym_fspec) + { + DataBufferSP dsym_file_data_sp; + lldb::offset_t dsym_file_data_offset = 0; + dsym_objfile_sp = ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0, dsym_fspec.GetByteSize(), dsym_file_data_sp, dsym_file_data_offset); + if (UUIDsMatch(module_sp.get(), dsym_objfile_sp.get(), feedback_strm)) + { + // We need a XML parser if we hope to parse a plist... + if (XMLDocument::XMLEnabled()) + { + char dsym_path[PATH_MAX]; + if (module_sp->GetSourceMappingList().IsEmpty() && dsym_fspec.GetPath(dsym_path, sizeof(dsym_path))) + { + lldb_private::UUID dsym_uuid; + if (dsym_objfile_sp->GetUUID(&dsym_uuid)) + { + std::string uuid_str = dsym_uuid.GetAsString (); + if (!uuid_str.empty()) + { + char *resources = strstr (dsym_path, "/Contents/Resources/"); + if (resources) + { + char dsym_uuid_plist_path[PATH_MAX]; + resources[strlen("/Contents/Resources/")] = '\0'; + snprintf(dsym_uuid_plist_path, sizeof(dsym_uuid_plist_path), "%s%s.plist", dsym_path, uuid_str.c_str()); + FileSpec dsym_uuid_plist_spec(dsym_uuid_plist_path, false); + if (dsym_uuid_plist_spec.Exists()) + { + ApplePropertyList plist(dsym_uuid_plist_path); + if (plist) + { + std::string DBGBuildSourcePath; + std::string DBGSourcePath; + + plist.GetValueAsString("DBGBuildSourcePath", DBGBuildSourcePath); + plist.GetValueAsString("DBGSourcePath", DBGSourcePath); + if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) + { + if (DBGSourcePath[0] == '~') + { + FileSpec resolved_source_path(DBGSourcePath.c_str(), true); + DBGSourcePath = resolved_source_path.GetPath(); + } + module_sp->GetSourceMappingList().Append (ConstString(DBGBuildSourcePath), ConstString(DBGSourcePath), true); + } + } + } + } + } + } + } + } + + symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp); + 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), an executable with + // a dSYM that has a UUID that doesn't match. + symbol_vendor->AddSymbolFileRepresentation(obj_file->shared_from_this()); + } + return symbol_vendor; +} + + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +SymbolVendorMacOSX::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolVendorMacOSX::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h b/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h new file mode 100644 index 000000000000..31a842ade86f --- /dev/null +++ b/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h @@ -0,0 +1,58 @@ +//===-- 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 lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolVendor* + CreateInstance (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolVendorMacOSX (const lldb::ModuleSP &module_sp); + + virtual + ~SymbolVendorMacOSX(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolVendorMacOSX); +}; + +#endif // liblldb_SymbolVendorMacOSX_h_ diff --git a/source/Plugins/SystemRuntime/CMakeLists.txt b/source/Plugins/SystemRuntime/CMakeLists.txt new file mode 100644 index 000000000000..0955a9eb74c2 --- /dev/null +++ b/source/Plugins/SystemRuntime/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(MacOSX) diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.cpp b/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.cpp new file mode 100644 index 000000000000..2ca367c0cce8 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.cpp @@ -0,0 +1,383 @@ +//===-- AppleGetItemInfoHandler.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleGetItemInfoHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleGetItemInfoHandler::g_get_item_info_function_name = "__lldb_backtrace_recording_get_item_info"; +const char *AppleGetItemInfoHandler::g_get_item_info_function_code = " \n\ +extern \"C\" \n\ +{ \n\ + /* \n\ + * mach defines \n\ + */ \n\ + \n\ + typedef unsigned int uint32_t; \n\ + typedef unsigned long long uint64_t; \n\ + typedef uint32_t mach_port_t; \n\ + typedef mach_port_t vm_map_t; \n\ + typedef int kern_return_t; \n\ + typedef uint64_t mach_vm_address_t; \n\ + typedef uint64_t mach_vm_size_t; \n\ + \n\ + mach_port_t mach_task_self (); \n\ + kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\ + \n\ + /* \n\ + * libBacktraceRecording defines \n\ + */ \n\ + \n\ + typedef uint32_t queue_list_scope_t; \n\ + typedef void *dispatch_queue_t; \n\ + typedef void *introspection_dispatch_queue_info_t; \n\ + typedef void *introspection_dispatch_item_info_ref; \n\ + \n\ + extern uint64_t __introspection_dispatch_queue_item_get_info (introspection_dispatch_item_info_ref item_info_ref, \n\ + introspection_dispatch_item_info_ref *returned_queues_buffer, \n\ + uint64_t *returned_queues_buffer_size); \n\ + extern int printf(const char *format, ...); \n\ + \n\ + /* \n\ + * return type define \n\ + */ \n\ + \n\ + struct get_item_info_return_values \n\ + { \n\ + uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ \n\ + uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ \n\ + }; \n\ + \n\ + void __lldb_backtrace_recording_get_item_info \n\ + (struct get_item_info_return_values *return_buffer, \n\ + int debug, \n\ + uint64_t /* introspection_dispatch_item_info_ref item_info_ref */ item, \n\ + void *page_to_free, \n\ + uint64_t page_to_free_size) \n\ +{ \n\ + if (debug) \n\ + printf (\"entering get_item_info with args return_buffer == %p, debug == %d, item == 0x%llx, page_to_free == %p, page_to_free_size == 0x%llx\\n\", return_buffer, debug, item, page_to_free, page_to_free_size); \n\ + if (page_to_free != 0) \n\ + { \n\ + mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\ + } \n\ + \n\ + __introspection_dispatch_queue_item_get_info ((void*) item, \n\ + (void**)&return_buffer->item_info_buffer_ptr, \n\ + &return_buffer->item_info_buffer_size); \n\ +} \n\ +} \n\ +"; + +AppleGetItemInfoHandler::AppleGetItemInfoHandler (Process *process) : + m_process (process), + m_get_item_info_impl_code (), + m_get_item_info_function_mutex(), + m_get_item_info_return_buffer_addr (LLDB_INVALID_ADDRESS), + m_get_item_info_retbuffer_mutex() +{ +} + +AppleGetItemInfoHandler::~AppleGetItemInfoHandler () +{ +} + +void +AppleGetItemInfoHandler::Detach () +{ + + if (m_process && m_process->IsAlive() && m_get_item_info_return_buffer_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker locker; + locker.TryLock (m_get_item_info_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer + m_process->DeallocateMemory (m_get_item_info_return_buffer_addr); + } +} + +// Compile our __lldb_backtrace_recording_get_item_info() function (from the +// source above in g_get_item_info_function_code) if we don't find that function in the inferior +// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.) +// +// Insert the __lldb_backtrace_recording_get_item_info into the inferior process if needed. +// +// Write the get_item_info_arglist into the inferior's memory space to prepare for the call. +// +// Returns the address of the arguments written down in the inferior process, which can be used to +// make the function call. + +lldb::addr_t +AppleGetItemInfoHandler::SetupGetItemInfoFunction (Thread &thread, ValueList &get_item_info_arglist) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + StreamString errors; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + FunctionCaller *get_item_info_caller = nullptr; + + // Scope for mutex locker: + { + Mutex::Locker locker(m_get_item_info_function_mutex); + + // First stage is to make the UtilityFunction to hold our injected function: + + if (!m_get_item_info_impl_code.get()) + { + if (g_get_item_info_function_code != NULL) + { + Error error; + m_get_item_info_impl_code.reset(exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage (g_get_item_info_function_code, + eLanguageTypeObjC, + g_get_item_info_function_name, + error)); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to get utility function: %s.", error.AsCString()); + return args_addr; + } + + if (!m_get_item_info_impl_code->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install get-item-info introspection: %s.", errors.GetData()); + m_get_item_info_impl_code.reset(); + return args_addr; + } + } + else + { + if (log) + log->Printf("No get-item-info introspection code found."); + errors.Printf ("No get-item-info introspection code found."); + return LLDB_INVALID_ADDRESS; + } + + // Next make the runner function for our implementation utility function. + Error error; + + TypeSystem *type_system = thread.GetProcess()->GetTarget().GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC); + CompilerType get_item_info_return_type = type_system->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType(); + + get_item_info_caller = m_get_item_info_impl_code->MakeFunctionCaller(get_item_info_return_type, + get_item_info_arglist, + error); + if (error.Fail()) + { + if (log) + log->Printf ("Error Inserting get-item-info function: \"%s\".", error.AsCString()); + return args_addr; + } + } + else + { + // If it's already made, then we can just retrieve the caller: + get_item_info_caller = m_get_item_info_impl_code->GetFunctionCaller(); + if (!get_item_info_caller) + { + if (log) + log->Printf ("Failed to get get-item-info introspection caller."); + m_get_item_info_impl_code.reset(); + return args_addr; + } + } + } + + errors.Clear(); + + // Now write down the argument values for this particular call. This looks like it might be a race condition + // if other threads were calling into here, but actually it isn't because we allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!get_item_info_caller->WriteFunctionArguments (exe_ctx, args_addr, get_item_info_arglist, errors)) + { + if (log) + log->Printf ("Error writing get-item-info function arguments: \"%s\".", errors.GetData()); + return args_addr; + } + + return args_addr; +} + +AppleGetItemInfoHandler::GetItemInfoReturnInfo +AppleGetItemInfoHandler::GetItemInfo (Thread &thread, uint64_t item, addr_t page_to_free, uint64_t page_to_free_size, Error &error) +{ + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + ProcessSP process_sp (thread.CalculateProcess()); + TargetSP target_sp (thread.CalculateTarget()); + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + + GetItemInfoReturnInfo return_value; + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return_value.item_buffer_size = 0; + + error.Clear(); + + if (thread.SafeToCallFunctions() == false) + { + if (log) + log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID()); + error.SetErrorString ("Not safe to call functions on this thread."); + return return_value; + } + + // Set up the arguments for a call to + + // struct get_item_info_return_values + // { + // uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ + // uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ + // }; + // + // void __lldb_backtrace_recording_get_item_info + // (struct get_item_info_return_values *return_buffer, + // int debug, + // uint64_t item, + // void *page_to_free, + // uint64_t page_to_free_size) + + // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in + // the inferior process. + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Value return_buffer_ptr_value; + return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar); + return_buffer_ptr_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); + Value debug_value; + debug_value.SetValueType (Value::eValueTypeScalar); + debug_value.SetCompilerType (clang_int_type); + + CompilerType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); + Value item_value; + item_value.SetValueType (Value::eValueTypeScalar); + item_value.SetCompilerType (clang_uint64_type); + + Value page_to_free_value; + page_to_free_value.SetValueType (Value::eValueTypeScalar); + page_to_free_value.SetCompilerType (clang_void_ptr_type); + + Value page_to_free_size_value; + page_to_free_size_value.SetValueType (Value::eValueTypeScalar); + page_to_free_size_value.SetCompilerType (clang_uint64_type); + + + Mutex::Locker locker(m_get_item_info_retbuffer_mutex); + if (m_get_item_info_return_buffer_addr == LLDB_INVALID_ADDRESS) + { + addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error); + if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Failed to allocate memory for return buffer for get current queues func call"); + return return_value; + } + m_get_item_info_return_buffer_addr = bufaddr; + } + + ValueList argument_values; + + return_buffer_ptr_value.GetScalar() = m_get_item_info_return_buffer_addr; + argument_values.PushValue (return_buffer_ptr_value); + + debug_value.GetScalar() = 0; + argument_values.PushValue (debug_value); + + item_value.GetScalar() = item; + argument_values.PushValue (item_value); + + if (page_to_free != LLDB_INVALID_ADDRESS) + page_to_free_value.GetScalar() = page_to_free; + else + page_to_free_value.GetScalar() = 0; + argument_values.PushValue (page_to_free_value); + + page_to_free_size_value.GetScalar() = page_to_free_size; + argument_values.PushValue (page_to_free_size_value); + + addr_t args_addr = SetupGetItemInfoFunction (thread, argument_values); + + StreamString errors; + ExecutionContext exe_ctx; + EvaluateExpressionOptions options; + options.SetUnwindOnError (true); + options.SetIgnoreBreakpoints (true); + options.SetStopOthers (true); + options.SetTimeoutUsec(500000); + options.SetTryAllThreads (false); + thread.CalculateExecutionContext (exe_ctx); + + if (!m_get_item_info_impl_code) + { + error.SetErrorString ("Unable to compile function to call __introspection_dispatch_queue_item_get_info"); + return return_value; + } + + + ExpressionResults func_call_ret; + Value results; + FunctionCaller *func_caller = m_get_item_info_impl_code->GetFunctionCaller(); + if (!func_caller) + { + if (log) + log->Printf ("Could not retrieve function caller for __introspection_dispatch_queue_item_get_info."); + error.SetErrorString("Could not retrieve function caller for __introspection_dispatch_queue_item_get_info."); + return return_value; + } + + func_call_ret = func_caller->ExecuteFunction (exe_ctx, &args_addr, options, errors, results); + if (func_call_ret != eExpressionCompleted || !error.Success()) + { + if (log) + log->Printf ("Unable to call __introspection_dispatch_queue_item_get_info(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString("")); + error.SetErrorString ("Unable to call __introspection_dispatch_queue_get_item_info() for list of queues"); + return return_value; + } + + return_value.item_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_item_info_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error); + if (!error.Success() || return_value.item_buffer_ptr == LLDB_INVALID_ADDRESS) + { + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.item_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_item_info_return_buffer_addr + 8, 8, 0, error); + + if (!error.Success()) + { + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + if (log) + log->Printf ("AppleGetItemInfoHandler called __introspection_dispatch_queue_item_get_info (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64, page_to_free, page_to_free_size, return_value.item_buffer_ptr, return_value.item_buffer_size); + + + return return_value; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.h b/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.h new file mode 100644 index 000000000000..51182a624939 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetItemInfoHandler.h @@ -0,0 +1,117 @@ +//===-- AppleGetItemInfoHandler.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_AppleGetItemInfoHandler_h_ +#define lldb_AppleGetItemInfoHandler_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Error.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/CompilerType.h" + +// This class will insert a UtilityFunction into the inferior process for +// calling libBacktraceRecording's __introspection_dispatch_queue_item_get_info() +// function. The function in the inferior will return a struct by value +// with these members: +// +// struct get_item_info_return_values +// { +// introspection_dispatch_item_info_ref *item_buffer; +// uint64_t item_buffer_size; +// }; +// +// The item_buffer pointer is an address in the inferior program's address +// space (item_buffer_size in size) which must be mach_vm_deallocate'd by +// lldb. +// +// The AppleGetItemInfoHandler object should persist so that the UtilityFunction +// can be reused multiple times. + +namespace lldb_private +{ + +class AppleGetItemInfoHandler { +public: + + AppleGetItemInfoHandler (lldb_private::Process *process); + + ~AppleGetItemInfoHandler(); + + struct GetItemInfoReturnInfo + { + lldb::addr_t item_buffer_ptr; /* the address of the item buffer from libBacktraceRecording */ + lldb::addr_t item_buffer_size; /* the size of the item buffer from libBacktraceRecording */ + + GetItemInfoReturnInfo() : + item_buffer_ptr(LLDB_INVALID_ADDRESS), + item_buffer_size(0) + {} + }; + + //---------------------------------------------------------- + /// Get the information about a work item by calling + /// __introspection_dispatch_queue_item_get_info. If there's a page of + /// memory that needs to be freed, pass in the address and size and it will + /// be freed before getting the list of queues. + /// + /// @param [in] thread + /// The thread to run this plan on. + /// + /// @param [in] item + /// The introspection_dispatch_item_info_ref value for the item of interest. + /// + /// @param [in] page_to_free + /// An address of an inferior process vm page that needs to be deallocated, + /// LLDB_INVALID_ADDRESS if this is not needed. + /// + /// @param [in] page_to_free_size + /// The size of the vm page that needs to be deallocated if an address was + /// passed in to page_to_free. + /// + /// @param [out] error + /// This object will be updated with the error status / error string from any failures encountered. + /// + /// @returns + /// The result of the inferior function call execution. If there was a failure of any kind while getting + /// the information, the item_buffer_ptr value will be LLDB_INVALID_ADDRESS. + //---------------------------------------------------------- + GetItemInfoReturnInfo + GetItemInfo (Thread &thread, lldb::addr_t item, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Error &error); + + + void + Detach (); + +private: + + lldb::addr_t + SetupGetItemInfoFunction (Thread &thread, ValueList &get_item_info_arglist); + + static const char *g_get_item_info_function_name; + static const char *g_get_item_info_function_code; + + lldb_private::Process *m_process; + std::unique_ptr m_get_item_info_impl_code; + Mutex m_get_item_info_function_mutex; + + lldb::addr_t m_get_item_info_return_buffer_addr; + Mutex m_get_item_info_retbuffer_mutex; + +}; + +} // using namespace lldb_private + +#endif // lldb_AppleGetItemInfoHandler_h_ diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.cpp b/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.cpp new file mode 100644 index 000000000000..97699878f5ee --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.cpp @@ -0,0 +1,383 @@ +//===-- AppleGetPendingItemsHandler.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleGetPendingItemsHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleGetPendingItemsHandler::g_get_pending_items_function_name = "__lldb_backtrace_recording_get_pending_items"; +const char *AppleGetPendingItemsHandler::g_get_pending_items_function_code = " \n\ +extern \"C\" \n\ +{ \n\ + /* \n\ + * mach defines \n\ + */ \n\ + \n\ + typedef unsigned int uint32_t; \n\ + typedef unsigned long long uint64_t; \n\ + typedef uint32_t mach_port_t; \n\ + typedef mach_port_t vm_map_t; \n\ + typedef int kern_return_t; \n\ + typedef uint64_t mach_vm_address_t; \n\ + typedef uint64_t mach_vm_size_t; \n\ + \n\ + mach_port_t mach_task_self (); \n\ + kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\ + \n\ + /* \n\ + * libBacktraceRecording defines \n\ + */ \n\ + \n\ + typedef uint32_t queue_list_scope_t; \n\ + typedef void *dispatch_queue_t; \n\ + typedef void *introspection_dispatch_queue_info_t; \n\ + typedef void *introspection_dispatch_item_info_ref; \n\ + \n\ + extern uint64_t __introspection_dispatch_queue_get_pending_items (dispatch_queue_t queue, \n\ + introspection_dispatch_item_info_ref *returned_queues_buffer, \n\ + uint64_t *returned_queues_buffer_size); \n\ + extern int printf(const char *format, ...); \n\ + \n\ + /* \n\ + * return type define \n\ + */ \n\ + \n\ + struct get_pending_items_return_values \n\ + { \n\ + uint64_t pending_items_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ \n\ + uint64_t pending_items_buffer_size; /* the size of the items buffer from libBacktraceRecording */ \n\ + uint64_t count; /* the number of items included in the queues buffer */ \n\ + }; \n\ + \n\ + void __lldb_backtrace_recording_get_pending_items \n\ + (struct get_pending_items_return_values *return_buffer, \n\ + int debug, \n\ + uint64_t /* dispatch_queue_t */ queue, \n\ + void *page_to_free, \n\ + uint64_t page_to_free_size) \n\ +{ \n\ + if (debug) \n\ + printf (\"entering get_pending_items with args return_buffer == %p, debug == %d, queue == 0x%llx, page_to_free == %p, page_to_free_size == 0x%llx\\n\", return_buffer, debug, queue, page_to_free, page_to_free_size); \n\ + if (page_to_free != 0) \n\ + { \n\ + mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\ + } \n\ + \n\ + return_buffer->count = __introspection_dispatch_queue_get_pending_items ( \n\ + (void*) queue, \n\ + (void**)&return_buffer->pending_items_buffer_ptr, \n\ + &return_buffer->pending_items_buffer_size); \n\ + if (debug) \n\ + printf(\"result was count %lld\\n\", return_buffer->count); \n\ +} \n\ +} \n\ +"; + +AppleGetPendingItemsHandler::AppleGetPendingItemsHandler (Process *process) : + m_process (process), + m_get_pending_items_impl_code (), + m_get_pending_items_function_mutex(), + m_get_pending_items_return_buffer_addr (LLDB_INVALID_ADDRESS), + m_get_pending_items_retbuffer_mutex() +{ +} + +AppleGetPendingItemsHandler::~AppleGetPendingItemsHandler () +{ +} + +void +AppleGetPendingItemsHandler::Detach () +{ + + if (m_process && m_process->IsAlive() && m_get_pending_items_return_buffer_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker locker; + locker.TryLock (m_get_pending_items_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer + m_process->DeallocateMemory (m_get_pending_items_return_buffer_addr); + } +} + +// Compile our __lldb_backtrace_recording_get_pending_items() function (from the +// source above in g_get_pending_items_function_code) if we don't find that function in the inferior +// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.) +// +// Insert the __lldb_backtrace_recording_get_pending_items into the inferior process if needed. +// +// Write the get_pending_items_arglist into the inferior's memory space to prepare for the call. +// +// Returns the address of the arguments written down in the inferior process, which can be used to +// make the function call. + +lldb::addr_t +AppleGetPendingItemsHandler::SetupGetPendingItemsFunction (Thread &thread, ValueList &get_pending_items_arglist) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + StreamString errors; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + FunctionCaller *get_pending_items_caller = nullptr; + + // Scope for mutex locker: + { + Mutex::Locker locker(m_get_pending_items_function_mutex); + + // First stage is to make the ClangUtility to hold our injected function: + + if (!m_get_pending_items_impl_code.get()) + { + if (g_get_pending_items_function_code != NULL) + { + Error error; + m_get_pending_items_impl_code.reset (exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(g_get_pending_items_function_code, + eLanguageTypeObjC, + g_get_pending_items_function_name, + error)); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to get UtilityFunction for pending-items introspection: %s.", error.AsCString()); + return args_addr; + } + + if (!m_get_pending_items_impl_code->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install pending-items introspection: %s.", errors.GetData()); + m_get_pending_items_impl_code.reset(); + return args_addr; + } + } + else + { + if (log) + log->Printf("No pending-items introspection code found."); + return LLDB_INVALID_ADDRESS; + } + + // Next make the runner function for our implementation utility function. + Error error; + ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext(); + CompilerType get_pending_items_return_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + get_pending_items_caller = m_get_pending_items_impl_code->MakeFunctionCaller (get_pending_items_return_type, + get_pending_items_arglist, + error); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to install pending-items introspection function caller: %s.", error.AsCString()); + m_get_pending_items_impl_code.reset(); + return args_addr; + } + } + + } + + errors.Clear(); + + if (get_pending_items_caller == nullptr) + { + if (log) + log->Printf ("Failed to get get_pending_items_caller."); + return LLDB_INVALID_ADDRESS; + } + + // Now write down the argument values for this particular call. This looks like it might be a race condition + // if other threads were calling into here, but actually it isn't because we allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!get_pending_items_caller->WriteFunctionArguments (exe_ctx, args_addr, get_pending_items_arglist, errors)) + { + if (log) + log->Printf ("Error writing pending-items function arguments: \"%s\".", errors.GetData()); + return args_addr; + } + + return args_addr; +} + +AppleGetPendingItemsHandler::GetPendingItemsReturnInfo +AppleGetPendingItemsHandler::GetPendingItems (Thread &thread, addr_t queue, addr_t page_to_free, uint64_t page_to_free_size, Error &error) +{ + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + ProcessSP process_sp (thread.CalculateProcess()); + TargetSP target_sp (thread.CalculateTarget()); + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + + GetPendingItemsReturnInfo return_value; + return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS; + return_value.items_buffer_size = 0; + return_value.count = 0; + + error.Clear(); + + if (thread.SafeToCallFunctions() == false) + { + if (log) + log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID()); + error.SetErrorString ("Not safe to call functions on this thread."); + return return_value; + } + + // Set up the arguments for a call to + + // struct get_pending_items_return_values + // { + // uint64_t pending_items_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ + // uint64_t pending_items_buffer_size; /* the size of the items buffer from libBacktraceRecording */ + // uint64_t count; /* the number of items included in the queues buffer */ + // }; + // + // void __lldb_backtrace_recording_get_pending_items + // (struct get_pending_items_return_values *return_buffer, + // int debug, + // uint64_t /* dispatch_queue_t */ queue + // void *page_to_free, + // uint64_t page_to_free_size) + + // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in + // the inferior process. + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Value return_buffer_ptr_value; + return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar); + return_buffer_ptr_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); + Value debug_value; + debug_value.SetValueType (Value::eValueTypeScalar); + debug_value.SetCompilerType (clang_int_type); + + CompilerType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); + Value queue_value; + queue_value.SetValueType (Value::eValueTypeScalar); + queue_value.SetCompilerType (clang_uint64_type); + + Value page_to_free_value; + page_to_free_value.SetValueType (Value::eValueTypeScalar); + page_to_free_value.SetCompilerType (clang_void_ptr_type); + + Value page_to_free_size_value; + page_to_free_size_value.SetValueType (Value::eValueTypeScalar); + page_to_free_size_value.SetCompilerType (clang_uint64_type); + + + Mutex::Locker locker(m_get_pending_items_retbuffer_mutex); + if (m_get_pending_items_return_buffer_addr == LLDB_INVALID_ADDRESS) + { + addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error); + if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Failed to allocate memory for return buffer for get current queues func call"); + return return_value; + } + m_get_pending_items_return_buffer_addr = bufaddr; + } + + ValueList argument_values; + + return_buffer_ptr_value.GetScalar() = m_get_pending_items_return_buffer_addr; + argument_values.PushValue (return_buffer_ptr_value); + + debug_value.GetScalar() = 0; + argument_values.PushValue (debug_value); + + queue_value.GetScalar() = queue; + argument_values.PushValue (queue_value); + + if (page_to_free != LLDB_INVALID_ADDRESS) + page_to_free_value.GetScalar() = page_to_free; + else + page_to_free_value.GetScalar() = 0; + argument_values.PushValue (page_to_free_value); + + page_to_free_size_value.GetScalar() = page_to_free_size; + argument_values.PushValue (page_to_free_size_value); + + addr_t args_addr = SetupGetPendingItemsFunction (thread, argument_values); + + StreamString errors; + ExecutionContext exe_ctx; + FunctionCaller *get_pending_items_caller = m_get_pending_items_impl_code->GetFunctionCaller(); + + EvaluateExpressionOptions options; + options.SetUnwindOnError (true); + options.SetIgnoreBreakpoints (true); + options.SetStopOthers (true); + options.SetTimeoutUsec(500000); + options.SetTryAllThreads (false); + thread.CalculateExecutionContext (exe_ctx); + + if (get_pending_items_caller == NULL) + { + error.SetErrorString ("Unable to compile function to call __introspection_dispatch_queue_get_pending_items"); + return return_value; + } + + + ExpressionResults func_call_ret; + Value results; + func_call_ret = get_pending_items_caller->ExecuteFunction (exe_ctx, &args_addr, options, errors, results); + if (func_call_ret != eExpressionCompleted || !error.Success()) + { + if (log) + log->Printf ("Unable to call __introspection_dispatch_queue_get_pending_items(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString("")); + error.SetErrorString ("Unable to call __introspection_dispatch_queue_get_pending_items() for list of queues"); + return return_value; + } + + return_value.items_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_pending_items_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error); + if (!error.Success() || return_value.items_buffer_ptr == LLDB_INVALID_ADDRESS) + { + return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.items_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_pending_items_return_buffer_addr + 8, 8, 0, error); + + if (!error.Success()) + { + return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.count = m_process->ReadUnsignedIntegerFromMemory (m_get_pending_items_return_buffer_addr + 16, 8, 0, error); + if (!error.Success()) + { + return_value.items_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + if (log) + log->Printf ("AppleGetPendingItemsHandler called __introspection_dispatch_queue_get_pending_items (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64 ", count = %" PRId64, page_to_free, page_to_free_size, return_value.items_buffer_ptr, return_value.items_buffer_size, return_value.count); + + return return_value; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.h b/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.h new file mode 100644 index 000000000000..445c4a0fb82b --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetPendingItemsHandler.h @@ -0,0 +1,119 @@ +//===-- AppleGetPendingItemsHandler.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_AppleGetPendingItemsHandler_h_ +#define lldb_AppleGetPendingItemsHandler_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/CompilerType.h" + +// This class will insert a UtilityFunction into the inferior process for +// calling libBacktraceRecording's __introspection_dispatch_queue_get_pending_items() +// function. The function in the inferior will return a struct by value +// with these members: +// +// struct get_pending_items_return_values +// { +// introspection_dispatch_item_info_ref *items_buffer; +// uint64_t items_buffer_size; +// uint64_t count; +// }; +// +// The items_buffer pointer is an address in the inferior program's address +// space (items_buffer_size in size) which must be mach_vm_deallocate'd by +// lldb. count is the number of items that were stored in the buffer. +// +// The AppleGetPendingItemsHandler object should persist so that the UtilityFunction +// can be reused multiple times. + +namespace lldb_private +{ + +class AppleGetPendingItemsHandler { +public: + + AppleGetPendingItemsHandler (lldb_private::Process *process); + + ~AppleGetPendingItemsHandler(); + + struct GetPendingItemsReturnInfo + { + lldb::addr_t items_buffer_ptr; /* the address of the pending items buffer from libBacktraceRecording */ + lldb::addr_t items_buffer_size; /* the size of the pending items buffer from libBacktraceRecording */ + uint64_t count; /* the number of pending items included in the buffer */ + + GetPendingItemsReturnInfo () : + items_buffer_ptr(LLDB_INVALID_ADDRESS), + items_buffer_size(0), + count(0) + {} + }; + + //---------------------------------------------------------- + /// Get the list of pending items for a given queue via a call to + /// __introspection_dispatch_queue_get_pending_items. If there's a page of + /// memory that needs to be freed, pass in the address and size and it will + /// be freed before getting the list of queues. + /// + /// @param [in] thread + /// The thread to run this plan on. + /// + /// @param [in] queue + /// The dispatch_queue_t value for the queue of interest. + /// + /// @param [in] page_to_free + /// An address of an inferior process vm page that needs to be deallocated, + /// LLDB_INVALID_ADDRESS if this is not needed. + /// + /// @param [in] page_to_free_size + /// The size of the vm page that needs to be deallocated if an address was + /// passed in to page_to_free. + /// + /// @param [out] error + /// This object will be updated with the error status / error string from any failures encountered. + /// + /// @returns + /// The result of the inferior function call execution. If there was a failure of any kind while getting + /// the information, the items_buffer_ptr value will be LLDB_INVALID_ADDRESS. + //---------------------------------------------------------- + GetPendingItemsReturnInfo + GetPendingItems (Thread &thread, lldb::addr_t queue, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Error &error); + + + void + Detach (); + +private: + + lldb::addr_t + SetupGetPendingItemsFunction (Thread &thread, ValueList &get_pending_items_arglist); + + static const char *g_get_pending_items_function_name; + static const char *g_get_pending_items_function_code; + + lldb_private::Process *m_process; + std::unique_ptr m_get_pending_items_impl_code; + Mutex m_get_pending_items_function_mutex; + + lldb::addr_t m_get_pending_items_return_buffer_addr; + Mutex m_get_pending_items_retbuffer_mutex; + +}; + +} // using namespace lldb_private + +#endif // lldb_AppleGetPendingItemsHandler_h_ diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.cpp b/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.cpp new file mode 100644 index 000000000000..3370b3257a25 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.cpp @@ -0,0 +1,382 @@ +//===-- AppleGetQueuesHandler.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleGetQueuesHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleGetQueuesHandler::g_get_current_queues_function_name = "__lldb_backtrace_recording_get_current_queues"; +const char *AppleGetQueuesHandler::g_get_current_queues_function_code = " \n\ +extern \"C\" \n\ +{ \n\ + /* \n\ + * mach defines \n\ + */ \n\ + \n\ + typedef unsigned int uint32_t; \n\ + typedef unsigned long long uint64_t; \n\ + typedef uint32_t mach_port_t; \n\ + typedef mach_port_t vm_map_t; \n\ + typedef int kern_return_t; \n\ + typedef uint64_t mach_vm_address_t; \n\ + typedef uint64_t mach_vm_size_t; \n\ + \n\ + mach_port_t mach_task_self (); \n\ + kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\ + \n\ + /* \n\ + * libBacktraceRecording defines \n\ + */ \n\ + \n\ + typedef uint32_t queue_list_scope_t; \n\ + typedef void *introspection_dispatch_queue_info_t; \n\ + \n\ + extern uint64_t __introspection_dispatch_get_queues (queue_list_scope_t scope, \n\ + introspection_dispatch_queue_info_t *returned_queues_buffer, \n\ + uint64_t *returned_queues_buffer_size); \n\ + extern int printf(const char *format, ...); \n\ + \n\ + /* \n\ + * return type define \n\ + */ \n\ + \n\ + struct get_current_queues_return_values \n\ + { \n\ + uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */ \n\ + uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */ \n\ + uint64_t count; /* the number of queues included in the queues buffer */ \n\ + }; \n\ + \n\ + void __lldb_backtrace_recording_get_current_queues \n\ + (struct get_current_queues_return_values *return_buffer, \n\ + int debug, \n\ + void *page_to_free, \n\ + uint64_t page_to_free_size) \n\ +{ \n\ + if (debug) \n\ + printf (\"entering get_current_queues with args %p, %d, 0x%p, 0x%llx\\n\", return_buffer, debug, page_to_free, page_to_free_size); \n\ + if (page_to_free != 0) \n\ + { \n\ + mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\ + } \n\ + \n\ + return_buffer->count = __introspection_dispatch_get_queues ( \n\ + /* QUEUES_WITH_ANY_ITEMS */ 2, \n\ + (void**)&return_buffer->queues_buffer_ptr, \n\ + &return_buffer->queues_buffer_size); \n\ + if (debug) \n\ + printf(\"result was count %lld\\n\", return_buffer->count); \n\ +} \n\ +} \n\ +"; + +AppleGetQueuesHandler::AppleGetQueuesHandler (Process *process) : + m_process (process), + m_get_queues_impl_code_up (), + m_get_queues_function_mutex(), + m_get_queues_return_buffer_addr (LLDB_INVALID_ADDRESS), + m_get_queues_retbuffer_mutex() +{ +} + +AppleGetQueuesHandler::~AppleGetQueuesHandler () +{ +} + +void +AppleGetQueuesHandler::Detach () +{ + + if (m_process && m_process->IsAlive() && m_get_queues_return_buffer_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker locker; + locker.TryLock (m_get_queues_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer + m_process->DeallocateMemory (m_get_queues_return_buffer_addr); + } +} + +// Construct a CompilerType for the structure that g_get_current_queues_function_code will return by value +// so we can extract the fields after performing the function call. +// i.e. we are getting this struct returned to us: +// +// struct get_current_queues_return_values +// { +// introspection_dispatch_queue_info_t *queues_buffer; +// uint64_t queues_buffer_size; +// uint64_t count; +// }; + + +// Compile our __lldb_backtrace_recording_get_current_queues() function (from the +// source above in g_get_current_queues_function_code) if we don't find that function in the inferior +// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.) +// +// Insert the __lldb_backtrace_recording_get_current_queues into the inferior process if needed. +// +// Write the get_queues_arglist into the inferior's memory space to prepare for the call. +// +// Returns the address of the arguments written down in the inferior process, which can be used to +// make the function call. + +lldb::addr_t +AppleGetQueuesHandler::SetupGetQueuesFunction (Thread &thread, ValueList &get_queues_arglist) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + Address impl_code_address; + StreamString errors; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + + FunctionCaller *get_queues_caller = nullptr; + + // Scope for mutex locker: + { + Mutex::Locker locker(m_get_queues_function_mutex); + + // First stage is to make the ClangUtility to hold our injected function: + + if (!m_get_queues_impl_code_up.get()) + { + if (g_get_current_queues_function_code != NULL) + { + Error error; + m_get_queues_impl_code_up.reset (exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(g_get_current_queues_function_code, + eLanguageTypeC, + g_get_current_queues_function_name, + error)); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to get UtilityFunction for queues introspection: %s.", error.AsCString()); + return args_addr; + } + + if (!m_get_queues_impl_code_up->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install queues introspection: %s.", errors.GetData()); + m_get_queues_impl_code_up.reset(); + return args_addr; + } + } + else + { + if (log) + log->Printf("No queues introspection code found."); + errors.Printf ("No queues introspection code found."); + return LLDB_INVALID_ADDRESS; + } + } + + // Next make the runner function for our implementation utility function. + ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext(); + CompilerType get_queues_return_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Error error; + get_queues_caller = m_get_queues_impl_code_up->MakeFunctionCaller (get_queues_return_type, + get_queues_arglist, + error); + if (error.Fail()) + { + if (log) + log->Printf ("Could not get function caller for get-queues function: %s.", error.AsCString()); + return args_addr; + } + } + + errors.Clear(); + + // Now write down the argument values for this particular call. This looks like it might be a race condition + // if other threads were calling into here, but actually it isn't because we allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!get_queues_caller->WriteFunctionArguments (exe_ctx, args_addr, get_queues_arglist, errors)) + { + if (log) + log->Printf ("Error writing get-queues function arguments: \"%s\".", errors.GetData()); + return args_addr; + } + + return args_addr; +} + +AppleGetQueuesHandler::GetQueuesReturnInfo +AppleGetQueuesHandler::GetCurrentQueues (Thread &thread, addr_t page_to_free, uint64_t page_to_free_size, Error &error) +{ + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + ProcessSP process_sp (thread.CalculateProcess()); + TargetSP target_sp (thread.CalculateTarget()); + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + + GetQueuesReturnInfo return_value; + return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS; + return_value.queues_buffer_size = 0; + return_value.count = 0; + + error.Clear(); + + if (thread.SafeToCallFunctions() == false) + { + if (log) + log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID()); + error.SetErrorString ("Not safe to call functions on this thread."); + return return_value; + } + + // Set up the arguments for a call to + + // struct get_current_queues_return_values + // { + // uint64_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */ + // uint64_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */ + // uint64_t count; /* the number of queues included in the queues buffer */ + // }; + // + // void + // __lldb_backtrace_recording_get_current_queues + // (struct get_current_queues_return_values *return_buffer, + // void *page_to_free, + // uint64_t page_to_free_size); + + // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in + // the inferior process. + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Value return_buffer_ptr_value; + return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar); + return_buffer_ptr_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); + Value debug_value; + debug_value.SetValueType (Value::eValueTypeScalar); + debug_value.SetCompilerType (clang_int_type); + + Value page_to_free_value; + page_to_free_value.SetValueType (Value::eValueTypeScalar); + page_to_free_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); + Value page_to_free_size_value; + page_to_free_size_value.SetValueType (Value::eValueTypeScalar); + page_to_free_size_value.SetCompilerType (clang_uint64_type); + + + Mutex::Locker locker(m_get_queues_retbuffer_mutex); + if (m_get_queues_return_buffer_addr == LLDB_INVALID_ADDRESS) + { + addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error); + if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Failed to allocate memory for return buffer for get current queues func call"); + return return_value; + } + m_get_queues_return_buffer_addr = bufaddr; + } + + ValueList argument_values; + + return_buffer_ptr_value.GetScalar() = m_get_queues_return_buffer_addr; + argument_values.PushValue (return_buffer_ptr_value); + + debug_value.GetScalar() = 0; + argument_values.PushValue (debug_value); + + if (page_to_free != LLDB_INVALID_ADDRESS) + page_to_free_value.GetScalar() = page_to_free; + else + page_to_free_value.GetScalar() = 0; + argument_values.PushValue (page_to_free_value); + + page_to_free_size_value.GetScalar() = page_to_free_size; + argument_values.PushValue (page_to_free_size_value); + + addr_t args_addr = SetupGetQueuesFunction (thread, argument_values); + + if (!m_get_queues_impl_code_up) + { + error.SetErrorString ("Unable to compile __introspection_dispatch_get_queues."); + return return_value; + } + + FunctionCaller *get_queues_caller = m_get_queues_impl_code_up->GetFunctionCaller(); + + if (get_queues_caller == NULL) + { + error.SetErrorString ("Unable to get caller for call __introspection_dispatch_get_queues"); + return return_value; + } + + StreamString errors; + ExecutionContext exe_ctx; + EvaluateExpressionOptions options; + options.SetUnwindOnError (true); + options.SetIgnoreBreakpoints (true); + options.SetStopOthers (true); + options.SetTimeoutUsec(500000); + options.SetTryAllThreads (false); + thread.CalculateExecutionContext (exe_ctx); + + ExpressionResults func_call_ret; + Value results; + func_call_ret = get_queues_caller->ExecuteFunction (exe_ctx, &args_addr, options, errors, results); + if (func_call_ret != eExpressionCompleted || !error.Success()) + { + if (log) + log->Printf ("Unable to call introspection_get_dispatch_queues(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString("")); + error.SetErrorString ("Unable to call introspection_get_dispatch_queues() for list of queues"); + return return_value; + } + + return_value.queues_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error); + if (!error.Success() || return_value.queues_buffer_ptr == LLDB_INVALID_ADDRESS) + { + return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.queues_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 8, 8, 0, error); + + if (!error.Success()) + { + return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.count = m_process->ReadUnsignedIntegerFromMemory (m_get_queues_return_buffer_addr + 16, 8, 0, error); + if (!error.Success()) + { + return_value.queues_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + if (log) + log->Printf ("AppleGetQueuesHandler called __introspection_dispatch_get_queues (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64 ", count = %" PRId64, page_to_free, page_to_free_size, return_value.queues_buffer_ptr, return_value.queues_buffer_size, return_value.count); + + return return_value; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.h b/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.h new file mode 100644 index 000000000000..6f3df5f62807 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetQueuesHandler.h @@ -0,0 +1,116 @@ +//===-- AppleGetQueuesHandler.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_AppleGetQueuesHandler_h_ +#define lldb_AppleGetQueuesHandler_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/CompilerType.h" + +// This class will insert a UtilityFunction into the inferior process for +// calling libBacktraceRecording's introspection_get_dispatch_queues() +// function. The function in the inferior will return a struct by value +// with these members: +// +// struct get_current_queues_return_values +// { +// introspection_dispatch_queue_info_t *queues_buffer; +// uint64_t queues_buffer_size; +// uint64_t count; +// }; +// +// The queues_buffer pointer is an address in the inferior program's address +// space (queues_buffer_size in size) which must be mach_vm_deallocate'd by +// lldb. count is the number of queues that were stored in the buffer. +// +// The AppleGetQueuesHandler object should persist so that the UtilityFunction +// can be reused multiple times. + +namespace lldb_private +{ + +class AppleGetQueuesHandler { +public: + + AppleGetQueuesHandler (lldb_private::Process *process); + + ~AppleGetQueuesHandler(); + + struct GetQueuesReturnInfo + { + lldb::addr_t queues_buffer_ptr; /* the address of the queues buffer from libBacktraceRecording */ + lldb::addr_t queues_buffer_size; /* the size of the queues buffer from libBacktraceRecording */ + uint64_t count; /* the number of queues included in the queues buffer */ + + GetQueuesReturnInfo() : + queues_buffer_ptr(LLDB_INVALID_ADDRESS), + queues_buffer_size(0), + count(0) + {} + }; + + //---------------------------------------------------------- + /// Get the list of queues that exist (with any active or pending items) via + /// a call to introspection_get_dispatch_queues(). If there's a page of + /// memory that needs to be freed, pass in the address and size and it will + /// be freed before getting the list of queues. + /// + /// @param [in] thread + /// The thread to run this plan on. + /// + /// @param [in] page_to_free + /// An address of an inferior process vm page that needs to be deallocated, + /// LLDB_INVALID_ADDRESS if this is not needed. + /// + /// @param [in] page_to_free_size + /// The size of the vm page that needs to be deallocated if an address was + /// passed in to page_to_free. + /// + /// @param [out] error + /// This object will be updated with the error status / error string from any failures encountered. + /// + /// @returns + /// The result of the inferior function call execution. If there was a failure of any kind while getting + /// the information, the queues_buffer_ptr value will be LLDB_INVALID_ADDRESS. + //---------------------------------------------------------- + GetQueuesReturnInfo + GetCurrentQueues (Thread &thread, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Error &error); + + + void + Detach (); + +private: + + lldb::addr_t + SetupGetQueuesFunction (Thread &thread, ValueList &get_queues_arglist); + + static const char *g_get_current_queues_function_name; + static const char *g_get_current_queues_function_code; + + lldb_private::Process *m_process; + std::unique_ptr m_get_queues_impl_code_up; + Mutex m_get_queues_function_mutex; + + lldb::addr_t m_get_queues_return_buffer_addr; + Mutex m_get_queues_retbuffer_mutex; + +}; + +} // using namespace lldb_private + +#endif // lldb_AppleGetQueuesHandler_h_ diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp b/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp new file mode 100644 index 000000000000..ba03f51152c0 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.cpp @@ -0,0 +1,385 @@ +//===-- AppleGetThreadItemInfoHandler.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleGetThreadItemInfoHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/Expression.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleGetThreadItemInfoHandler::g_get_thread_item_info_function_name = "__lldb_backtrace_recording_get_thread_item_info"; +const char *AppleGetThreadItemInfoHandler::g_get_thread_item_info_function_code = " \n\ +extern \"C\" \n\ +{ \n\ + /* \n\ + * mach defines \n\ + */ \n\ + \n\ + typedef unsigned int uint32_t; \n\ + typedef unsigned long long uint64_t; \n\ + typedef uint32_t mach_port_t; \n\ + typedef mach_port_t vm_map_t; \n\ + typedef int kern_return_t; \n\ + typedef uint64_t mach_vm_address_t; \n\ + typedef uint64_t mach_vm_size_t; \n\ + \n\ + mach_port_t mach_task_self (); \n\ + kern_return_t mach_vm_deallocate (vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); \n\ + \n\ + typedef void *pthread_t; \n\ + extern int printf(const char *format, ...); \n\ + extern pthread_t pthread_self(void); \n\ + \n\ + /* \n\ + * libBacktraceRecording defines \n\ + */ \n\ + \n\ + typedef uint32_t queue_list_scope_t; \n\ + typedef void *dispatch_queue_t; \n\ + typedef void *introspection_dispatch_queue_info_t; \n\ + typedef void *introspection_dispatch_item_info_ref; \n\ + \n\ + extern void __introspection_dispatch_thread_get_item_info (uint64_t thread_id, \n\ + introspection_dispatch_item_info_ref *returned_queues_buffer, \n\ + uint64_t *returned_queues_buffer_size); \n\ + \n\ + /* \n\ + * return type define \n\ + */ \n\ + \n\ + struct get_thread_item_info_return_values \n\ + { \n\ + uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ \n\ + uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ \n\ + }; \n\ + \n\ + void __lldb_backtrace_recording_get_thread_item_info \n\ + (struct get_thread_item_info_return_values *return_buffer, \n\ + int debug, \n\ + uint64_t thread_id, \n\ + void *page_to_free, \n\ + uint64_t page_to_free_size) \n\ +{ \n\ + void *pthread_id = pthread_self (); \n\ + if (debug) \n\ + printf (\"entering get_thread_item_info with args return_buffer == %p, debug == %d, thread id == 0x%llx, page_to_free == %p, page_to_free_size == 0x%llx\\n\", return_buffer, debug, (uint64_t) thread_id, page_to_free, page_to_free_size); \n\ + if (page_to_free != 0) \n\ + { \n\ + mach_vm_deallocate (mach_task_self(), (mach_vm_address_t) page_to_free, (mach_vm_size_t) page_to_free_size); \n\ + } \n\ + \n\ + __introspection_dispatch_thread_get_item_info (thread_id, \n\ + (void**)&return_buffer->item_info_buffer_ptr, \n\ + &return_buffer->item_info_buffer_size); \n\ +} \n\ +} \n\ +"; + +AppleGetThreadItemInfoHandler::AppleGetThreadItemInfoHandler (Process *process) : + m_process (process), + m_get_thread_item_info_impl_code (), + m_get_thread_item_info_function_mutex(), + m_get_thread_item_info_return_buffer_addr (LLDB_INVALID_ADDRESS), + m_get_thread_item_info_retbuffer_mutex() +{ +} + +AppleGetThreadItemInfoHandler::~AppleGetThreadItemInfoHandler () +{ +} + +void +AppleGetThreadItemInfoHandler::Detach () +{ + + if (m_process && m_process->IsAlive() && m_get_thread_item_info_return_buffer_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker locker; + locker.TryLock (m_get_thread_item_info_retbuffer_mutex); // Even if we don't get the lock, deallocate the buffer + m_process->DeallocateMemory (m_get_thread_item_info_return_buffer_addr); + } +} + +// Compile our __lldb_backtrace_recording_get_thread_item_info() function (from the +// source above in g_get_thread_item_info_function_code) if we don't find that function in the inferior +// already with USE_BUILTIN_FUNCTION defined. (e.g. this would be the case for testing.) +// +// Insert the __lldb_backtrace_recording_get_thread_item_info into the inferior process if needed. +// +// Write the get_thread_item_info_arglist into the inferior's memory space to prepare for the call. +// +// Returns the address of the arguments written down in the inferior process, which can be used to +// make the function call. + +lldb::addr_t +AppleGetThreadItemInfoHandler::SetupGetThreadItemInfoFunction (Thread &thread, ValueList &get_thread_item_info_arglist) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + Address impl_code_address; + StreamString errors; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + FunctionCaller *get_thread_item_info_caller = nullptr; + + // Scope for mutex locker: + { + Mutex::Locker locker(m_get_thread_item_info_function_mutex); + + // First stage is to make the ClangUtility to hold our injected function: + + if (!m_get_thread_item_info_impl_code.get()) + { + Error error; + if (g_get_thread_item_info_function_code != NULL) + { + m_get_thread_item_info_impl_code.reset (exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage (g_get_thread_item_info_function_code, + eLanguageTypeC, + g_get_thread_item_info_function_name, + error)); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to get UtilityFunction for get-thread-item-info introspection: %s.", + error.AsCString()); + m_get_thread_item_info_impl_code.reset(); + return args_addr; + } + + if (!m_get_thread_item_info_impl_code->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install get-thread-item-info introspection: %s.", errors.GetData()); + m_get_thread_item_info_impl_code.reset(); + return args_addr; + } + } + else + { + if (log) + log->Printf("No get-thread-item-info introspection code found."); + errors.Printf ("No get-thread-item-info introspection code found."); + return LLDB_INVALID_ADDRESS; + } + + // Also make the FunctionCaller for this UtilityFunction: + + ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext(); + CompilerType get_thread_item_info_return_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + + get_thread_item_info_caller = m_get_thread_item_info_impl_code->MakeFunctionCaller (get_thread_item_info_return_type, + get_thread_item_info_arglist, + error); + if (error.Fail()) + { + if (log) + log->Printf ("Failed to install get-thread-item-info introspection caller: %s.", error.AsCString()); + m_get_thread_item_info_impl_code.reset(); + return args_addr; + } + + } + else + { + get_thread_item_info_caller = m_get_thread_item_info_impl_code->GetFunctionCaller(); + } + } + + errors.Clear(); + + // Now write down the argument values for this particular call. This looks like it might be a race condition + // if other threads were calling into here, but actually it isn't because we allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!get_thread_item_info_caller->WriteFunctionArguments (exe_ctx, args_addr, get_thread_item_info_arglist, errors)) + { + if (log) + log->Printf ("Error writing get-thread-item-info function arguments: \"%s\".", errors.GetData()); + return args_addr; + } + + return args_addr; +} + +AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo +AppleGetThreadItemInfoHandler::GetThreadItemInfo (Thread &thread, tid_t thread_id, addr_t page_to_free, uint64_t page_to_free_size, Error &error) +{ + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + ProcessSP process_sp (thread.CalculateProcess()); + TargetSP target_sp (thread.CalculateTarget()); + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + + GetThreadItemInfoReturnInfo return_value; + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return_value.item_buffer_size = 0; + + error.Clear(); + + if (thread.SafeToCallFunctions() == false) + { + if (log) + log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID()); + error.SetErrorString ("Not safe to call functions on this thread."); + return return_value; + } + + // Set up the arguments for a call to + + // struct get_thread_item_info_return_values + // { + // uint64_t item_info_buffer_ptr; /* the address of the items buffer from libBacktraceRecording */ + // uint64_t item_info_buffer_size; /* the size of the items buffer from libBacktraceRecording */ + // }; + // + // void __lldb_backtrace_recording_get_thread_item_info + // (struct get_thread_item_info_return_values *return_buffer, + // int debug, + // void *page_to_free, + // uint64_t page_to_free_size) + + // Where the return_buffer argument points to a 24 byte region of memory already allocated by lldb in + // the inferior process. + + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Value return_buffer_ptr_value; + return_buffer_ptr_value.SetValueType (Value::eValueTypeScalar); + return_buffer_ptr_value.SetCompilerType (clang_void_ptr_type); + + CompilerType clang_int_type = clang_ast_context->GetBasicType(eBasicTypeInt); + Value debug_value; + debug_value.SetValueType (Value::eValueTypeScalar); + debug_value.SetCompilerType (clang_int_type); + + CompilerType clang_uint64_type = clang_ast_context->GetBasicType(eBasicTypeUnsignedLongLong); + Value thread_id_value; + thread_id_value.SetValueType (Value::eValueTypeScalar); + thread_id_value.SetCompilerType (clang_uint64_type); + + Value page_to_free_value; + page_to_free_value.SetValueType (Value::eValueTypeScalar); + page_to_free_value.SetCompilerType (clang_void_ptr_type); + + Value page_to_free_size_value; + page_to_free_size_value.SetValueType (Value::eValueTypeScalar); + page_to_free_size_value.SetCompilerType (clang_uint64_type); + + + Mutex::Locker locker(m_get_thread_item_info_retbuffer_mutex); + if (m_get_thread_item_info_return_buffer_addr == LLDB_INVALID_ADDRESS) + { + addr_t bufaddr = process_sp->AllocateMemory (32, ePermissionsReadable | ePermissionsWritable, error); + if (!error.Success() || bufaddr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Failed to allocate memory for return buffer for get current queues func call"); + return return_value; + } + m_get_thread_item_info_return_buffer_addr = bufaddr; + } + + ValueList argument_values; + + return_buffer_ptr_value.GetScalar() = m_get_thread_item_info_return_buffer_addr; + argument_values.PushValue (return_buffer_ptr_value); + + debug_value.GetScalar() = 0; + argument_values.PushValue (debug_value); + + thread_id_value.GetScalar() = thread_id; + argument_values.PushValue (thread_id_value); + + if (page_to_free != LLDB_INVALID_ADDRESS) + page_to_free_value.GetScalar() = page_to_free; + else + page_to_free_value.GetScalar() = 0; + argument_values.PushValue (page_to_free_value); + + page_to_free_size_value.GetScalar() = page_to_free_size; + argument_values.PushValue (page_to_free_size_value); + + addr_t args_addr = SetupGetThreadItemInfoFunction (thread, argument_values); + + StreamString errors; + ExecutionContext exe_ctx; + EvaluateExpressionOptions options; + FunctionCaller *get_thread_item_info_caller = nullptr; + + options.SetUnwindOnError (true); + options.SetIgnoreBreakpoints (true); + options.SetStopOthers (true); + options.SetTimeoutUsec(500000); + options.SetTryAllThreads (false); + thread.CalculateExecutionContext (exe_ctx); + + if (!m_get_thread_item_info_impl_code) + { + error.SetErrorString ("Unable to compile function to call __introspection_dispatch_thread_get_item_info"); + return return_value; + } + + get_thread_item_info_caller = m_get_thread_item_info_impl_code->GetFunctionCaller(); + + if (!get_thread_item_info_caller) + { + error.SetErrorString ("Unable to compile function caller for __introspection_dispatch_thread_get_item_info"); + return return_value; + } + + ExpressionResults func_call_ret; + Value results; + func_call_ret = get_thread_item_info_caller->ExecuteFunction (exe_ctx, &args_addr, options, errors, results); + if (func_call_ret != eExpressionCompleted || !error.Success()) + { + if (log) + log->Printf ("Unable to call __introspection_dispatch_thread_get_item_info(), got ExpressionResults %d, error contains %s", func_call_ret, error.AsCString("")); + error.SetErrorString ("Unable to call __introspection_dispatch_thread_get_item_info() for list of queues"); + return return_value; + } + + return_value.item_buffer_ptr = m_process->ReadUnsignedIntegerFromMemory (m_get_thread_item_info_return_buffer_addr, 8, LLDB_INVALID_ADDRESS, error); + if (!error.Success() || return_value.item_buffer_ptr == LLDB_INVALID_ADDRESS) + { + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + return_value.item_buffer_size = m_process->ReadUnsignedIntegerFromMemory (m_get_thread_item_info_return_buffer_addr + 8, 8, 0, error); + + if (!error.Success()) + { + return_value.item_buffer_ptr = LLDB_INVALID_ADDRESS; + return return_value; + } + + if (log) + log->Printf ("AppleGetThreadItemInfoHandler called __introspection_dispatch_thread_get_item_info (page_to_free == 0x%" PRIx64 ", size = %" PRId64 "), returned page is at 0x%" PRIx64 ", size %" PRId64, page_to_free, page_to_free_size, return_value.item_buffer_ptr, return_value.item_buffer_size); + + return return_value; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.h b/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.h new file mode 100644 index 000000000000..c1798fb515b4 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/AppleGetThreadItemInfoHandler.h @@ -0,0 +1,113 @@ +//===-- AppleGetThreadItemInfoHandler.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_AppleGetThreadItemInfoHandler_h_ +#define lldb_AppleGetThreadItemInfoHandler_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/CompilerType.h" + +// This class will insert a UtilityFunction into the inferior process for +// calling libBacktraceRecording's __introspection_dispatch_thread_get_item_info() +// function. The function in the inferior will return a struct by value +// with these members: +// +// struct get_thread_item_info_return_values +// { +// introspection_dispatch_item_info_ref *item_buffer; +// uint64_t item_buffer_size; +// }; +// +// The item_buffer pointer is an address in the inferior program's address +// space (item_buffer_size in size) which must be mach_vm_deallocate'd by +// lldb. +// +// The AppleGetThreadItemInfoHandler object should persist so that the UtilityFunction +// can be reused multiple times. + +namespace lldb_private +{ + +class AppleGetThreadItemInfoHandler { +public: + + AppleGetThreadItemInfoHandler (lldb_private::Process *process); + + ~AppleGetThreadItemInfoHandler(); + + struct GetThreadItemInfoReturnInfo + { + lldb::addr_t item_buffer_ptr; /* the address of the item buffer from libBacktraceRecording */ + lldb::addr_t item_buffer_size; /* the size of the item buffer from libBacktraceRecording */ + + GetThreadItemInfoReturnInfo() : + item_buffer_ptr(LLDB_INVALID_ADDRESS), + item_buffer_size(0) + {} + }; + + //---------------------------------------------------------- + /// Get the information about a work item by calling + /// __introspection_dispatch_thread_get_item_info. If there's a page of + /// memory that needs to be freed, pass in the address and size and it will + /// be freed before getting the list of queues. + /// + /// @param [in] thread_id + /// The thread to get the extended backtrace for. + /// + /// @param [in] page_to_free + /// An address of an inferior process vm page that needs to be deallocated, + /// LLDB_INVALID_ADDRESS if this is not needed. + /// + /// @param [in] page_to_free_size + /// The size of the vm page that needs to be deallocated if an address was + /// passed in to page_to_free. + /// + /// @param [out] error + /// This object will be updated with the error status / error string from any failures encountered. + /// + /// @returns + /// The result of the inferior function call execution. If there was a failure of any kind while getting + /// the information, the item_buffer_ptr value will be LLDB_INVALID_ADDRESS. + //---------------------------------------------------------- + GetThreadItemInfoReturnInfo + GetThreadItemInfo (Thread &thread, lldb::tid_t thread_id, lldb::addr_t page_to_free, uint64_t page_to_free_size, lldb_private::Error &error); + + + void + Detach (); + +private: + + lldb::addr_t + SetupGetThreadItemInfoFunction (Thread &thread, ValueList &get_thread_item_info_arglist); + + static const char *g_get_thread_item_info_function_name; + static const char *g_get_thread_item_info_function_code; + + lldb_private::Process *m_process; + std::unique_ptr m_get_thread_item_info_impl_code; + Mutex m_get_thread_item_info_function_mutex; + + lldb::addr_t m_get_thread_item_info_return_buffer_addr; + Mutex m_get_thread_item_info_retbuffer_mutex; + +}; + +} // using namespace lldb_private + +#endif // lldb_AppleGetThreadItemInfoHandler_h_ diff --git a/source/Plugins/SystemRuntime/MacOSX/CMakeLists.txt b/source/Plugins/SystemRuntime/MacOSX/CMakeLists.txt new file mode 100644 index 000000000000..d1580cce20d6 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/CMakeLists.txt @@ -0,0 +1,7 @@ +add_lldb_library(lldbPluginSystemRuntimeMacOSX + AppleGetItemInfoHandler.cpp + AppleGetPendingItemsHandler.cpp + AppleGetQueuesHandler.cpp + AppleGetThreadItemInfoHandler.cpp + SystemRuntimeMacOSX.cpp + ) diff --git a/source/Plugins/SystemRuntime/MacOSX/Makefile b/source/Plugins/SystemRuntime/MacOSX/Makefile new file mode 100644 index 000000000000..eebfb52073d5 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/SystemRuntime/MacOSX ----*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginSystemRuntimeMacOSX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp b/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp new file mode 100644 index 000000000000..b11a06760325 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp @@ -0,0 +1,1010 @@ +//===-- SystemRuntimeMacOSX.cpp ---------------------------------*- 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/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "Plugins/Process/Utility/HistoryThread.h" +#include "lldb/Target/Queue.h" +#include "lldb/Target/QueueList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/ProcessStructReader.h" + +#include "SystemRuntimeMacOSX.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Create an instance of this class. This function is filled into +// the plugin info class that gets handed out by the plugin factory and +// allows the lldb to instantiate an instance of this class. +//---------------------------------------------------------------------- +SystemRuntime * +SystemRuntimeMacOSX::CreateInstance (Process* process) +{ + bool create = false; + if (!create) + { + create = true; + Module* exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) + { + create = (object_file->GetStrata() == ObjectFile::eStrataUser); + } + } + + if (create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + switch (triple_ref.getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + create = triple_ref.getVendor() == llvm::Triple::Apple; + break; + default: + create = false; + break; + } + } + } + + if (create) + return new SystemRuntimeMacOSX (process); + return NULL; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +SystemRuntimeMacOSX::SystemRuntimeMacOSX (Process* process) : + SystemRuntime(process), + m_break_id(LLDB_INVALID_BREAK_ID), + m_mutex(Mutex::eMutexTypeRecursive), + m_get_queues_handler(process), + m_get_pending_items_handler(process), + m_get_item_info_handler(process), + m_get_thread_item_info_handler(process), + m_page_to_free(LLDB_INVALID_ADDRESS), + m_page_to_free_size(0), + m_lib_backtrace_recording_info(), + m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS), + m_libdispatch_offsets(), + m_libpthread_layout_offsets_addr (LLDB_INVALID_ADDRESS), + m_libpthread_offsets(), + m_dispatch_tsd_indexes_addr (LLDB_INVALID_ADDRESS), + m_libdispatch_tsd_indexes(), + m_dispatch_voucher_offsets_addr (LLDB_INVALID_ADDRESS), + m_libdispatch_voucher_offsets() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SystemRuntimeMacOSX::~SystemRuntimeMacOSX() +{ + Clear (true); +} + +void +SystemRuntimeMacOSX::Detach () +{ + m_get_queues_handler.Detach(); + m_get_pending_items_handler.Detach(); + m_get_item_info_handler.Detach(); + m_get_thread_item_info_handler.Detach(); +} + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +SystemRuntimeMacOSX::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_break_id = LLDB_INVALID_BREAK_ID; +} + + +std::string +SystemRuntimeMacOSX::GetQueueNameFromThreadQAddress (addr_t dispatch_qaddr) +{ + std::string dispatch_queue_name; + if (dispatch_qaddr == LLDB_INVALID_ADDRESS || dispatch_qaddr == 0) + return ""; + + ReadLibdispatchOffsets (); + if (m_libdispatch_offsets.IsValid ()) + { + // dispatch_qaddr is from a thread_info(THREAD_IDENTIFIER_INFO) call for a thread - + // deref it to get the address of the dispatch_queue_t structure for this thread's + // queue. + Error error; + addr_t dispatch_queue_addr = m_process->ReadPointerFromMemory (dispatch_qaddr, error); + if (error.Success()) + { + if (m_libdispatch_offsets.dqo_version >= 4) + { + // libdispatch versions 4+, pointer to dispatch name is in the + // queue structure. + addr_t pointer_to_label_address = dispatch_queue_addr + m_libdispatch_offsets.dqo_label; + addr_t label_addr = m_process->ReadPointerFromMemory (pointer_to_label_address, error); + if (error.Success()) + { + m_process->ReadCStringFromMemory (label_addr, dispatch_queue_name, error); + } + } + else + { + // libdispatch versions 1-3, dispatch name is a fixed width char array + // in the queue structure. + addr_t label_addr = dispatch_queue_addr + m_libdispatch_offsets.dqo_label; + dispatch_queue_name.resize (m_libdispatch_offsets.dqo_label_size, '\0'); + size_t bytes_read = m_process->ReadMemory (label_addr, &dispatch_queue_name[0], m_libdispatch_offsets.dqo_label_size, error); + if (bytes_read < m_libdispatch_offsets.dqo_label_size) + dispatch_queue_name.erase (bytes_read); + } + } + } + return dispatch_queue_name; +} + +lldb::addr_t +SystemRuntimeMacOSX::GetLibdispatchQueueAddressFromThreadQAddress (addr_t dispatch_qaddr) +{ + addr_t libdispatch_queue_t_address = LLDB_INVALID_ADDRESS; + Error error; + libdispatch_queue_t_address = m_process->ReadPointerFromMemory (dispatch_qaddr, error); + if (!error.Success()) + { + libdispatch_queue_t_address = LLDB_INVALID_ADDRESS; + } + return libdispatch_queue_t_address; +} + +lldb::QueueKind +SystemRuntimeMacOSX::GetQueueKind (addr_t dispatch_queue_addr) +{ + if (dispatch_queue_addr == LLDB_INVALID_ADDRESS || dispatch_queue_addr == 0) + return eQueueKindUnknown; + + QueueKind kind = eQueueKindUnknown; + ReadLibdispatchOffsets (); + if (m_libdispatch_offsets.IsValid () && m_libdispatch_offsets.dqo_version >= 4) + { + Error error; + uint64_t width = m_process->ReadUnsignedIntegerFromMemory (dispatch_queue_addr + m_libdispatch_offsets.dqo_width, m_libdispatch_offsets.dqo_width_size, 0, error); + if (error.Success()) + { + if (width == 1) + { + kind = eQueueKindSerial; + } + if (width > 1) + { + kind = eQueueKindConcurrent; + } + } + } + return kind; +} + +void +SystemRuntimeMacOSX::AddThreadExtendedInfoPacketHints (lldb_private::StructuredData::ObjectSP dict_sp) +{ + StructuredData::Dictionary *dict = dict_sp->GetAsDictionary(); + if (dict) + { + ReadLibpthreadOffsets(); + if (m_libpthread_offsets.IsValid()) + { + dict->AddIntegerItem ("plo_pthread_tsd_base_offset", m_libpthread_offsets.plo_pthread_tsd_base_offset); + dict->AddIntegerItem ("plo_pthread_tsd_base_address_offset", m_libpthread_offsets.plo_pthread_tsd_base_address_offset); + dict->AddIntegerItem ("plo_pthread_tsd_entry_size", m_libpthread_offsets.plo_pthread_tsd_entry_size); + } + + ReadLibdispatchTSDIndexes (); + if (m_libdispatch_tsd_indexes.IsValid()) + { + dict->AddIntegerItem ("dti_queue_index", m_libdispatch_tsd_indexes.dti_queue_index); + dict->AddIntegerItem ("dti_voucher_index", m_libdispatch_tsd_indexes.dti_voucher_index); + dict->AddIntegerItem ("dti_qos_class_index", m_libdispatch_tsd_indexes.dti_qos_class_index); + } + } +} + +bool +SystemRuntimeMacOSX::SafeToCallFunctionsOnThisThread (ThreadSP thread_sp) +{ + if (thread_sp && thread_sp->GetStackFrameCount() > 0 && thread_sp->GetFrameWithConcreteFrameIndex(0)) + { + const SymbolContext sym_ctx (thread_sp->GetFrameWithConcreteFrameIndex(0)->GetSymbolContext (eSymbolContextSymbol)); + static ConstString g_select_symbol ("__select"); + if (sym_ctx.GetFunctionName() == g_select_symbol) + { + return false; + } + } + return true; +} + +lldb::queue_id_t +SystemRuntimeMacOSX::GetQueueIDFromThreadQAddress (lldb::addr_t dispatch_qaddr) +{ + queue_id_t queue_id = LLDB_INVALID_QUEUE_ID; + + if (dispatch_qaddr == LLDB_INVALID_ADDRESS || dispatch_qaddr == 0) + return queue_id; + + ReadLibdispatchOffsets (); + if (m_libdispatch_offsets.IsValid ()) + { + // dispatch_qaddr is from a thread_info(THREAD_IDENTIFIER_INFO) call for a thread - + // deref it to get the address of the dispatch_queue_t structure for this thread's + // queue. + Error error; + uint64_t dispatch_queue_addr = m_process->ReadPointerFromMemory (dispatch_qaddr, error); + if (error.Success()) + { + addr_t serialnum_address = dispatch_queue_addr + m_libdispatch_offsets.dqo_serialnum; + queue_id_t serialnum = m_process->ReadUnsignedIntegerFromMemory (serialnum_address, m_libdispatch_offsets.dqo_serialnum_size, LLDB_INVALID_QUEUE_ID, error); + if (error.Success()) + { + queue_id = serialnum; + } + } + } + + return queue_id; +} + + +void +SystemRuntimeMacOSX::ReadLibdispatchOffsetsAddress () +{ + if (m_dispatch_queue_offsets_addr != LLDB_INVALID_ADDRESS) + return; + + static ConstString g_dispatch_queue_offsets_symbol_name ("dispatch_queue_offsets"); + const Symbol *dispatch_queue_offsets_symbol = NULL; + + // libdispatch symbols were in libSystem.B.dylib up through Mac OS X 10.6 ("Snow Leopard") + ModuleSpec libSystem_module_spec (FileSpec("libSystem.B.dylib", false)); + ModuleSP module_sp(m_process->GetTarget().GetImages().FindFirstModule (libSystem_module_spec)); + if (module_sp) + dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); + + // libdispatch symbols are in their own dylib as of Mac OS X 10.7 ("Lion") and later + if (dispatch_queue_offsets_symbol == NULL) + { + ModuleSpec libdispatch_module_spec (FileSpec("libdispatch.dylib", false)); + module_sp = m_process->GetTarget().GetImages().FindFirstModule (libdispatch_module_spec); + if (module_sp) + dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); + } + if (dispatch_queue_offsets_symbol) + m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetLoadAddress(&m_process->GetTarget()); +} + +void +SystemRuntimeMacOSX::ReadLibdispatchOffsets () +{ + if (m_libdispatch_offsets.IsValid()) + return; + + ReadLibdispatchOffsetsAddress (); + + uint8_t memory_buffer[sizeof (struct LibdispatchOffsets)]; + DataExtractor data (memory_buffer, + sizeof(memory_buffer), + m_process->GetByteOrder(), + m_process->GetAddressByteSize()); + + Error error; + if (m_process->ReadMemory (m_dispatch_queue_offsets_addr, memory_buffer, sizeof(memory_buffer), error) == sizeof(memory_buffer)) + { + lldb::offset_t data_offset = 0; + + // The struct LibdispatchOffsets is a series of uint16_t's - extract them all + // in one big go. + data.GetU16 (&data_offset, &m_libdispatch_offsets.dqo_version, sizeof (struct LibdispatchOffsets) / sizeof (uint16_t)); + } +} + +void +SystemRuntimeMacOSX::ReadLibpthreadOffsetsAddress () +{ + if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) + return; + + static ConstString g_libpthread_layout_offsets_symbol_name ("pthread_layout_offsets"); + const Symbol *libpthread_layout_offsets_symbol = NULL; + + ModuleSpec libpthread_module_spec (FileSpec("libsystem_pthread.dylib", false)); + ModuleSP module_sp (m_process->GetTarget().GetImages().FindFirstModule (libpthread_module_spec)); + if (module_sp) + { + libpthread_layout_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType + (g_libpthread_layout_offsets_symbol_name, eSymbolTypeData); + if (libpthread_layout_offsets_symbol) + { + m_libpthread_layout_offsets_addr = libpthread_layout_offsets_symbol->GetLoadAddress(&m_process->GetTarget()); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibpthreadOffsets () +{ + if (m_libpthread_offsets.IsValid()) + return; + + ReadLibpthreadOffsetsAddress (); + + if (m_libpthread_layout_offsets_addr != LLDB_INVALID_ADDRESS) + { + uint8_t memory_buffer[sizeof (struct LibpthreadOffsets)]; + DataExtractor data (memory_buffer, + sizeof(memory_buffer), + m_process->GetByteOrder(), + m_process->GetAddressByteSize()); + Error error; + if (m_process->ReadMemory (m_libpthread_layout_offsets_addr, memory_buffer, sizeof(memory_buffer), error) == sizeof(memory_buffer)) + { + lldb::offset_t data_offset = 0; + + // The struct LibpthreadOffsets is a series of uint16_t's - extract them all + // in one big go. + data.GetU16 (&data_offset, &m_libpthread_offsets.plo_version, sizeof (struct LibpthreadOffsets) / sizeof (uint16_t)); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibdispatchTSDIndexesAddress () +{ + if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) + return; + + static ConstString g_libdispatch_tsd_indexes_symbol_name ("dispatch_tsd_indexes"); + const Symbol *libdispatch_tsd_indexes_symbol = NULL; + + ModuleSpec libpthread_module_spec (FileSpec("libdispatch.dylib", false)); + ModuleSP module_sp (m_process->GetTarget().GetImages().FindFirstModule (libpthread_module_spec)); + if (module_sp) + { + libdispatch_tsd_indexes_symbol = module_sp->FindFirstSymbolWithNameAndType + (g_libdispatch_tsd_indexes_symbol_name, eSymbolTypeData); + if (libdispatch_tsd_indexes_symbol) + { + m_dispatch_tsd_indexes_addr = libdispatch_tsd_indexes_symbol->GetLoadAddress(&m_process->GetTarget()); + } + } +} + +void +SystemRuntimeMacOSX::ReadLibdispatchTSDIndexes () +{ + if (m_libdispatch_tsd_indexes.IsValid()) + return; + + ReadLibdispatchTSDIndexesAddress (); + + if (m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) + { + + // We don't need to check the version number right now, it will be at least 2, but + // keep this code around to fetch just the version # for the future where we need + // to fetch alternate versions of the struct. +# if 0 + uint16_t dti_version = 2; + Address dti_struct_addr; + if (m_process->GetTarget().ResolveLoadAddress (m_dispatch_tsd_indexes_addr, dti_struct_addr)) + { + Error error; + uint16_t version = m_process->GetTarget().ReadUnsignedIntegerFromMemory (dti_struct_addr, false, 2, UINT16_MAX, error); + if (error.Success() && dti_version != UINT16_MAX) + { + dti_version = version; + } + } +#endif + + ClangASTContext *ast_ctx = m_process->GetTarget().GetScratchClangASTContext(); + if (ast_ctx->getASTContext() && m_dispatch_tsd_indexes_addr != LLDB_INVALID_ADDRESS) + { + CompilerType uint16 = ast_ctx->GetBuiltinTypeForEncodingAndBitSize (eEncodingUint, 16); + CompilerType dispatch_tsd_indexes_s = ast_ctx->CreateRecordType(nullptr, lldb::eAccessPublic, "__lldb_dispatch_tsd_indexes_s", clang::TTK_Struct, lldb::eLanguageTypeC); + + ClangASTContext::StartTagDeclarationDefinition(dispatch_tsd_indexes_s); + ClangASTContext::AddFieldToRecordType (dispatch_tsd_indexes_s, "dti_version", uint16, lldb::eAccessPublic, 0); + ClangASTContext::AddFieldToRecordType (dispatch_tsd_indexes_s, "dti_queue_index", uint16, lldb::eAccessPublic, 0); + ClangASTContext::AddFieldToRecordType (dispatch_tsd_indexes_s, "dti_voucher_index", uint16, lldb::eAccessPublic, 0); + ClangASTContext::AddFieldToRecordType (dispatch_tsd_indexes_s, "dti_qos_class_index", uint16, lldb::eAccessPublic, 0); + ClangASTContext::CompleteTagDeclarationDefinition(dispatch_tsd_indexes_s); + + ProcessStructReader struct_reader (m_process, m_dispatch_tsd_indexes_addr, dispatch_tsd_indexes_s); + + m_libdispatch_tsd_indexes.dti_version = struct_reader.GetField(ConstString("dti_version")); + m_libdispatch_tsd_indexes.dti_queue_index = struct_reader.GetField(ConstString("dti_queue_index")); + m_libdispatch_tsd_indexes.dti_voucher_index = struct_reader.GetField(ConstString("dti_voucher_index")); + m_libdispatch_tsd_indexes.dti_qos_class_index = struct_reader.GetField(ConstString("dti_qos_class_index")); + } + } +} + + +ThreadSP +SystemRuntimeMacOSX::GetExtendedBacktraceThread (ThreadSP real_thread, ConstString type) +{ + ThreadSP originating_thread_sp; + if (BacktraceRecordingHeadersInitialized() && type == ConstString ("libdispatch")) + { + Error error; + + // real_thread is either an actual, live thread (in which case we need to call into + // libBacktraceRecording to find its originator) or it is an extended backtrace itself, + // in which case we get the token from it and call into libBacktraceRecording to find + // the originator of that token. + + if (real_thread->GetExtendedBacktraceToken() != LLDB_INVALID_ADDRESS) + { + originating_thread_sp = GetExtendedBacktraceFromItemRef (real_thread->GetExtendedBacktraceToken()); + } + else + { + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + AppleGetThreadItemInfoHandler::GetThreadItemInfoReturnInfo ret = m_get_thread_item_info_handler.GetThreadItemInfo (*cur_thread_sp.get(), real_thread->GetID(), m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) + { + DataBufferHeap data (ret.item_buffer_size, 0); + if (m_process->ReadMemory (ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + ItemInfo item = ExtractItemInfoFromBuffer (extractor); + bool stop_id_is_valid = true; + if (item.stop_id == 0) + stop_id_is_valid = false; + originating_thread_sp.reset (new HistoryThread (*m_process, + item.enqueuing_thread_id, + item.enqueuing_callstack, + item.stop_id, + stop_id_is_valid)); + originating_thread_sp->SetExtendedBacktraceToken (item.item_that_enqueued_this); + originating_thread_sp->SetQueueName (item.enqueuing_queue_label.c_str()); + originating_thread_sp->SetQueueID (item.enqueuing_queue_serialnum); +// originating_thread_sp->SetThreadName (item.enqueuing_thread_label.c_str()); + } + m_page_to_free = ret.item_buffer_ptr; + m_page_to_free_size = ret.item_buffer_size; + } + } + } + return originating_thread_sp; +} + +ThreadSP +SystemRuntimeMacOSX::GetExtendedBacktraceFromItemRef (lldb::addr_t item_ref) +{ + ThreadSP return_thread_sp; + + AppleGetItemInfoHandler::GetItemInfoReturnInfo ret; + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + Error error; + ret = m_get_item_info_handler.GetItemInfo (*cur_thread_sp.get(), item_ref, m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) + { + DataBufferHeap data (ret.item_buffer_size, 0); + if (m_process->ReadMemory (ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + ItemInfo item = ExtractItemInfoFromBuffer (extractor); + bool stop_id_is_valid = true; + if (item.stop_id == 0) + stop_id_is_valid = false; + return_thread_sp.reset (new HistoryThread (*m_process, + item.enqueuing_thread_id, + item.enqueuing_callstack, + item.stop_id, + stop_id_is_valid)); + return_thread_sp->SetExtendedBacktraceToken (item.item_that_enqueued_this); + return_thread_sp->SetQueueName (item.enqueuing_queue_label.c_str()); + return_thread_sp->SetQueueID (item.enqueuing_queue_serialnum); +// return_thread_sp->SetThreadName (item.enqueuing_thread_label.c_str()); + + m_page_to_free = ret.item_buffer_ptr; + m_page_to_free_size = ret.item_buffer_size; + } + } + return return_thread_sp; +} + +ThreadSP +SystemRuntimeMacOSX::GetExtendedBacktraceForQueueItem (QueueItemSP queue_item_sp, ConstString type) +{ + ThreadSP extended_thread_sp; + if (type != ConstString("libdispatch")) + return extended_thread_sp; + + bool stop_id_is_valid = true; + if (queue_item_sp->GetStopID() == 0) + stop_id_is_valid = false; + + extended_thread_sp.reset (new HistoryThread (*m_process, + queue_item_sp->GetEnqueueingThreadID(), + queue_item_sp->GetEnqueueingBacktrace(), + queue_item_sp->GetStopID(), + stop_id_is_valid)); + extended_thread_sp->SetExtendedBacktraceToken (queue_item_sp->GetItemThatEnqueuedThis()); + extended_thread_sp->SetQueueName (queue_item_sp->GetQueueLabel().c_str()); + extended_thread_sp->SetQueueID (queue_item_sp->GetEnqueueingQueueID()); +// extended_thread_sp->SetThreadName (queue_item_sp->GetThreadLabel().c_str()); + + return extended_thread_sp; +} + +/* Returns true if we were able to get the version / offset information + * out of libBacktraceRecording. false means we were unable to retrieve + * this; the queue_info_version field will be 0. + */ + +bool +SystemRuntimeMacOSX::BacktraceRecordingHeadersInitialized () +{ + if (m_lib_backtrace_recording_info.queue_info_version != 0) + return true; + + addr_t queue_info_version_address = LLDB_INVALID_ADDRESS; + addr_t queue_info_data_offset_address = LLDB_INVALID_ADDRESS; + addr_t item_info_version_address = LLDB_INVALID_ADDRESS; + addr_t item_info_data_offset_address = LLDB_INVALID_ADDRESS; + Target &target = m_process->GetTarget(); + + + static ConstString introspection_dispatch_queue_info_version ("__introspection_dispatch_queue_info_version"); + SymbolContextList sc_list; + if (m_process->GetTarget().GetImages().FindSymbolsWithNameAndType (introspection_dispatch_queue_info_version, eSymbolTypeData, sc_list) > 0) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + AddressRange addr_range; + sc.GetAddressRange (eSymbolContextSymbol, 0, false, addr_range); + queue_info_version_address = addr_range.GetBaseAddress().GetLoadAddress(&target); + } + sc_list.Clear(); + + static ConstString introspection_dispatch_queue_info_data_offset ("__introspection_dispatch_queue_info_data_offset"); + if (m_process->GetTarget().GetImages().FindSymbolsWithNameAndType (introspection_dispatch_queue_info_data_offset, eSymbolTypeData, sc_list) > 0) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + AddressRange addr_range; + sc.GetAddressRange (eSymbolContextSymbol, 0, false, addr_range); + queue_info_data_offset_address = addr_range.GetBaseAddress().GetLoadAddress(&target); + } + sc_list.Clear(); + + static ConstString introspection_dispatch_item_info_version ("__introspection_dispatch_item_info_version"); + if (m_process->GetTarget().GetImages().FindSymbolsWithNameAndType (introspection_dispatch_item_info_version, eSymbolTypeData, sc_list) > 0) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + AddressRange addr_range; + sc.GetAddressRange (eSymbolContextSymbol, 0, false, addr_range); + item_info_version_address = addr_range.GetBaseAddress().GetLoadAddress(&target); + } + sc_list.Clear(); + + static ConstString introspection_dispatch_item_info_data_offset ("__introspection_dispatch_item_info_data_offset"); + if (m_process->GetTarget().GetImages().FindSymbolsWithNameAndType (introspection_dispatch_item_info_data_offset, eSymbolTypeData, sc_list) > 0) + { + SymbolContext sc; + sc_list.GetContextAtIndex (0, sc); + AddressRange addr_range; + sc.GetAddressRange (eSymbolContextSymbol, 0, false, addr_range); + item_info_data_offset_address = addr_range.GetBaseAddress().GetLoadAddress(&target); + } + + if (queue_info_version_address != LLDB_INVALID_ADDRESS + && queue_info_data_offset_address != LLDB_INVALID_ADDRESS + && item_info_version_address != LLDB_INVALID_ADDRESS + && item_info_data_offset_address != LLDB_INVALID_ADDRESS) + { + Error error; + m_lib_backtrace_recording_info.queue_info_version = m_process->ReadUnsignedIntegerFromMemory (queue_info_version_address, 2, 0, error); + if (error.Success()) + { + m_lib_backtrace_recording_info.queue_info_data_offset = m_process->ReadUnsignedIntegerFromMemory (queue_info_data_offset_address, 2, 0, error); + if (error.Success()) + { + m_lib_backtrace_recording_info.item_info_version = m_process->ReadUnsignedIntegerFromMemory (item_info_version_address, 2, 0, error); + if (error.Success()) + { + m_lib_backtrace_recording_info.item_info_data_offset = m_process->ReadUnsignedIntegerFromMemory (item_info_data_offset_address, 2, 0, error); + if (!error.Success()) + { + m_lib_backtrace_recording_info.queue_info_version = 0; + } + } + else + { + m_lib_backtrace_recording_info.queue_info_version = 0; + } + } + else + { + m_lib_backtrace_recording_info.queue_info_version = 0; + } + } + } + + return m_lib_backtrace_recording_info.queue_info_version != 0; +} + +const std::vector & +SystemRuntimeMacOSX::GetExtendedBacktraceTypes () +{ + if (m_types.size () == 0) + { + m_types.push_back(ConstString("libdispatch")); + // We could have pthread as another type in the future if we have a way of + // gathering that information & it's useful to distinguish between them. + } + return m_types; +} + +void +SystemRuntimeMacOSX::PopulateQueueList (lldb_private::QueueList &queue_list) +{ + if (BacktraceRecordingHeadersInitialized()) + { + AppleGetQueuesHandler::GetQueuesReturnInfo queue_info_pointer; + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + if (cur_thread_sp) + { + Error error; + queue_info_pointer = m_get_queues_handler.GetCurrentQueues (*cur_thread_sp.get(), m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (error.Success()) + { + + if (queue_info_pointer.count > 0 + && queue_info_pointer.queues_buffer_size > 0 + && queue_info_pointer.queues_buffer_ptr != 0 + && queue_info_pointer.queues_buffer_ptr != LLDB_INVALID_ADDRESS) + { + PopulateQueuesUsingLibBTR (queue_info_pointer.queues_buffer_ptr, queue_info_pointer.queues_buffer_size, queue_info_pointer.count, queue_list); + } + } + } + } + + // We either didn't have libBacktraceRecording (and need to create the queues list based on threads) + // or we did get the queues list from libBacktraceRecording but some special queues may not be + // included in its information. This is needed because libBacktraceRecording + // will only list queues with pending or running items by default - but the magic com.apple.main-thread + // queue on thread 1 is always around. + + for (ThreadSP thread_sp : m_process->Threads()) + { + if (thread_sp->GetQueueID() != LLDB_INVALID_QUEUE_ID) + { + if (queue_list.FindQueueByID (thread_sp->GetQueueID()).get() == NULL) + { + QueueSP queue_sp (new Queue(m_process->shared_from_this(), thread_sp->GetQueueID(), thread_sp->GetQueueName())); + queue_sp->SetKind (GetQueueKind (thread_sp->GetQueueLibdispatchQueueAddress())); + queue_sp->SetLibdispatchQueueAddress (thread_sp->GetQueueLibdispatchQueueAddress()); + queue_list.AddQueue (queue_sp); + } + } + } +} + +// Returns either an array of introspection_dispatch_item_info_ref's for the pending items on +// a queue or an array introspection_dispatch_item_info_ref's and code addresses for the +// pending items on a queue. The information about each of these pending items then needs to +// be fetched individually by passing the ref to libBacktraceRecording. + +SystemRuntimeMacOSX::PendingItemsForQueue +SystemRuntimeMacOSX::GetPendingItemRefsForQueue (lldb::addr_t queue) +{ + PendingItemsForQueue pending_item_refs; + AppleGetPendingItemsHandler::GetPendingItemsReturnInfo pending_items_pointer; + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + if (cur_thread_sp) + { + Error error; + pending_items_pointer = m_get_pending_items_handler.GetPendingItems (*cur_thread_sp.get(), queue, m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (error.Success()) + { + if (pending_items_pointer.count > 0 + && pending_items_pointer.items_buffer_size > 0 + && pending_items_pointer.items_buffer_ptr != 0 + && pending_items_pointer.items_buffer_ptr != LLDB_INVALID_ADDRESS) + { + DataBufferHeap data (pending_items_pointer.items_buffer_size, 0); + if (m_process->ReadMemory (pending_items_pointer.items_buffer_ptr, data.GetBytes(), pending_items_pointer.items_buffer_size, error)) + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + + // We either have an array of + // void* item_ref + // (old style) or we have a structure returned which looks like + // + // struct introspection_dispatch_pending_item_info_s { + // void *item_ref; + // void *function_or_block; + // }; + // + // struct introspection_dispatch_pending_items_array_s { + // uint32_t version; + // uint32_t size_of_item_info; + // introspection_dispatch_pending_item_info_s items[]; + // } + + offset_t offset = 0; + int i = 0; + uint32_t version = extractor.GetU32(&offset); + if (version == 1) + { + pending_item_refs.new_style = true; + uint32_t item_size = extractor.GetU32(&offset); + uint32_t start_of_array_offset = offset; + while (offset < pending_items_pointer.items_buffer_size && + static_cast(i) < pending_items_pointer.count) + { + offset = start_of_array_offset + (i * item_size); + ItemRefAndCodeAddress item; + item.item_ref = extractor.GetPointer (&offset); + item.code_address = extractor.GetPointer (&offset); + pending_item_refs.item_refs_and_code_addresses.push_back (item); + i++; + } + } + else + { + offset = 0; + pending_item_refs.new_style = false; + while (offset < pending_items_pointer.items_buffer_size && + static_cast(i) < pending_items_pointer.count) + { + ItemRefAndCodeAddress item; + item.item_ref = extractor.GetPointer (&offset); + item.code_address = LLDB_INVALID_ADDRESS; + pending_item_refs.item_refs_and_code_addresses.push_back (item); + i++; + } + } + } + m_page_to_free = pending_items_pointer.items_buffer_ptr; + m_page_to_free_size = pending_items_pointer.items_buffer_size; + } + } + } + return pending_item_refs; +} + + + +void +SystemRuntimeMacOSX::PopulatePendingItemsForQueue (Queue *queue) +{ + if (BacktraceRecordingHeadersInitialized()) + { + PendingItemsForQueue pending_item_refs = GetPendingItemRefsForQueue (queue->GetLibdispatchQueueAddress()); + for (ItemRefAndCodeAddress pending_item : pending_item_refs.item_refs_and_code_addresses) + { + Address addr; + m_process->GetTarget().ResolveLoadAddress (pending_item.code_address, addr); + QueueItemSP queue_item_sp (new QueueItem (queue->shared_from_this(), m_process->shared_from_this(), pending_item.item_ref, addr)); + queue->PushPendingQueueItem (queue_item_sp); + } + } +} + +void +SystemRuntimeMacOSX::CompleteQueueItem (QueueItem *queue_item, addr_t item_ref) +{ + AppleGetItemInfoHandler::GetItemInfoReturnInfo ret; + + ThreadSP cur_thread_sp (m_process->GetThreadList().GetSelectedThread()); + Error error; + ret = m_get_item_info_handler.GetItemInfo (*cur_thread_sp.get(), item_ref, m_page_to_free, m_page_to_free_size, error); + m_page_to_free = LLDB_INVALID_ADDRESS; + m_page_to_free_size = 0; + if (ret.item_buffer_ptr != 0 && ret.item_buffer_ptr != LLDB_INVALID_ADDRESS && ret.item_buffer_size > 0) + { + DataBufferHeap data (ret.item_buffer_size, 0); + if (m_process->ReadMemory (ret.item_buffer_ptr, data.GetBytes(), ret.item_buffer_size, error) && error.Success()) + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + ItemInfo item = ExtractItemInfoFromBuffer (extractor); + queue_item->SetItemThatEnqueuedThis (item.item_that_enqueued_this); + queue_item->SetEnqueueingThreadID (item.enqueuing_thread_id); + queue_item->SetEnqueueingQueueID (item.enqueuing_queue_serialnum); + queue_item->SetStopID (item.stop_id); + queue_item->SetEnqueueingBacktrace (item.enqueuing_callstack); + queue_item->SetThreadLabel (item.enqueuing_thread_label); + queue_item->SetQueueLabel (item.enqueuing_queue_label); + queue_item->SetTargetQueueLabel (item.target_queue_label); + } + m_page_to_free = ret.item_buffer_ptr; + m_page_to_free_size = ret.item_buffer_size; + } +} + +void +SystemRuntimeMacOSX::PopulateQueuesUsingLibBTR (lldb::addr_t queues_buffer, uint64_t queues_buffer_size, + uint64_t count, lldb_private::QueueList &queue_list) +{ + Error error; + DataBufferHeap data (queues_buffer_size, 0); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYSTEM_RUNTIME)); + if (m_process->ReadMemory (queues_buffer, data.GetBytes(), queues_buffer_size, error) == queues_buffer_size && error.Success()) + { + // We've read the information out of inferior memory; free it on the next call we make + m_page_to_free = queues_buffer; + m_page_to_free_size = queues_buffer_size; + + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), m_process->GetByteOrder(), m_process->GetAddressByteSize()); + offset_t offset = 0; + uint64_t queues_read = 0; + + // The information about the queues is stored in this format (v1): + // typedef struct introspection_dispatch_queue_info_s { + // uint32_t offset_to_next; + // dispatch_queue_t queue; + // uint64_t serialnum; // queue's serialnum in the process, as provided by libdispatch + // uint32_t running_work_items_count; + // uint32_t pending_work_items_count; + // + // char data[]; // Starting here, we have variable-length data: + // // char queue_label[]; + // } introspection_dispatch_queue_info_s; + + while (queues_read < count && offset < queues_buffer_size) + { + offset_t start_of_this_item = offset; + + uint32_t offset_to_next = extractor.GetU32 (&offset); + + offset += 4; // Skip over the 4 bytes of reserved space + addr_t queue = extractor.GetPointer (&offset); + uint64_t serialnum = extractor.GetU64 (&offset); + uint32_t running_work_items_count = extractor.GetU32 (&offset); + uint32_t pending_work_items_count = extractor.GetU32 (&offset); + + // Read the first field of the variable length data + offset = start_of_this_item + m_lib_backtrace_recording_info.queue_info_data_offset; + const char *queue_label = extractor.GetCStr (&offset); + if (queue_label == NULL) + queue_label = ""; + + offset_t start_of_next_item = start_of_this_item + offset_to_next; + offset = start_of_next_item; + + if (log) + log->Printf ("SystemRuntimeMacOSX::PopulateQueuesUsingLibBTR added queue with dispatch_queue_t 0x%" PRIx64 ", serial number 0x%" PRIx64 ", running items %d, pending items %d, name '%s'", queue, serialnum, running_work_items_count, pending_work_items_count, queue_label); + + QueueSP queue_sp (new Queue (m_process->shared_from_this(), serialnum, queue_label)); + queue_sp->SetNumRunningWorkItems (running_work_items_count); + queue_sp->SetNumPendingWorkItems (pending_work_items_count); + queue_sp->SetLibdispatchQueueAddress (queue); + queue_sp->SetKind (GetQueueKind (queue)); + queue_list.AddQueue (queue_sp); + queues_read++; + } + } +} + +SystemRuntimeMacOSX::ItemInfo +SystemRuntimeMacOSX::ExtractItemInfoFromBuffer (lldb_private::DataExtractor &extractor) +{ + ItemInfo item; + + offset_t offset = 0; + + item.item_that_enqueued_this = extractor.GetPointer (&offset); + item.function_or_block = extractor.GetPointer (&offset); + item.enqueuing_thread_id = extractor.GetU64 (&offset); + item.enqueuing_queue_serialnum = extractor.GetU64 (&offset); + item.target_queue_serialnum = extractor.GetU64 (&offset); + item.enqueuing_callstack_frame_count = extractor.GetU32 (&offset); + item.stop_id = extractor.GetU32 (&offset); + + offset = m_lib_backtrace_recording_info.item_info_data_offset; + + for (uint32_t i = 0; i < item.enqueuing_callstack_frame_count; i++) + { + item.enqueuing_callstack.push_back (extractor.GetPointer (&offset)); + } + item.enqueuing_thread_label = extractor.GetCStr (&offset); + item.enqueuing_queue_label = extractor.GetCStr (&offset); + item.target_queue_label = extractor.GetCStr (&offset); + + return item; +} + +void +SystemRuntimeMacOSX::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SystemRuntimeMacOSX::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +SystemRuntimeMacOSX::GetPluginNameStatic() +{ + static ConstString g_name("systemruntime-macosx"); + return g_name; +} + +const char * +SystemRuntimeMacOSX::GetPluginDescriptionStatic() +{ + return "System runtime plugin for Mac OS X native libraries."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +SystemRuntimeMacOSX::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SystemRuntimeMacOSX::GetPluginVersion() +{ + return 1; +} diff --git a/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h b/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h new file mode 100644 index 000000000000..5976bed57cf6 --- /dev/null +++ b/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h @@ -0,0 +1,336 @@ +//===-- SystemRuntimeMacOSX.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SystemRuntimeMacOSX_h_ +#define liblldb_SystemRuntimeMacOSX_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework include +// Project includes +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/QueueItem.h" + +#include "AppleGetItemInfoHandler.h" +#include "AppleGetQueuesHandler.h" +#include "AppleGetPendingItemsHandler.h" +#include "AppleGetThreadItemInfoHandler.h" + +class SystemRuntimeMacOSX : public lldb_private::SystemRuntime +{ +public: + SystemRuntimeMacOSX(lldb_private::Process *process); + + ~SystemRuntimeMacOSX() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SystemRuntime * + CreateInstance (lldb_private::Process *process); + + //------------------------------------------------------------------ + // instance methods + //------------------------------------------------------------------ + + void + Clear(bool clear_process); + + void + Detach() override; + + const std::vector & + GetExtendedBacktraceTypes() override; + + lldb::ThreadSP + GetExtendedBacktraceThread(lldb::ThreadSP thread, lldb_private::ConstString type) override; + + lldb::ThreadSP + GetExtendedBacktraceForQueueItem(lldb::QueueItemSP queue_item_sp, + lldb_private::ConstString type) override; + + lldb::ThreadSP + GetExtendedBacktraceFromItemRef (lldb::addr_t item_ref); + + void + PopulateQueueList(lldb_private::QueueList &queue_list) override; + + void + PopulateQueuesUsingLibBTR (lldb::addr_t queues_buffer, uint64_t queues_buffer_size, uint64_t count, lldb_private::QueueList &queue_list); + + void + PopulatePendingQueuesUsingLibBTR (lldb::addr_t items_buffer, uint64_t items_buffer_size, uint64_t count, lldb_private::Queue *queue); + + std::string + GetQueueNameFromThreadQAddress(lldb::addr_t dispatch_qaddr) override; + + lldb::queue_id_t + GetQueueIDFromThreadQAddress(lldb::addr_t dispatch_qaddr) override; + + lldb::addr_t + GetLibdispatchQueueAddressFromThreadQAddress(lldb::addr_t dispatch_qaddr) override; + + void + PopulatePendingItemsForQueue(lldb_private::Queue *queue) override; + + void + CompleteQueueItem(lldb_private::QueueItem *queue_item, lldb::addr_t item_ref) override; + + virtual lldb::QueueKind + GetQueueKind (lldb::addr_t dispatch_queue_addr); + + void + AddThreadExtendedInfoPacketHints(lldb_private::StructuredData::ObjectSP dict) override; + + bool + SafeToCallFunctionsOnThisThread(lldb::ThreadSP thread_sp) override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + lldb::user_id_t m_break_id; + mutable lldb_private::Mutex m_mutex; + +private: + struct libBacktraceRecording_info { + uint16_t queue_info_version; + uint16_t queue_info_data_offset; + uint16_t item_info_version; + uint16_t item_info_data_offset; + + libBacktraceRecording_info () : + queue_info_version(0), + queue_info_data_offset(0), + item_info_version(0), + item_info_data_offset(0) {} + }; + + // A structure which reflects the data recorded in the + // libBacktraceRecording introspection_dispatch_item_info_s. + struct ItemInfo { + lldb::addr_t item_that_enqueued_this; + lldb::addr_t function_or_block; + uint64_t enqueuing_thread_id; + uint64_t enqueuing_queue_serialnum; + uint64_t target_queue_serialnum; + uint32_t enqueuing_callstack_frame_count; + uint32_t stop_id; + std::vector enqueuing_callstack; + std::string enqueuing_thread_label; + std::string enqueuing_queue_label; + std::string target_queue_label; + }; + + // The offsets of different fields of the dispatch_queue_t structure in + // a thread/queue process. + // Based on libdispatch src/queue_private.h, struct dispatch_queue_offsets_s + // With dqo_version 1-3, the dqo_label field is a per-queue value and cannot be cached. + // With dqo_version 4 (Mac OS X 10.9 / iOS 7), dqo_label is a constant value that can be cached. + struct LibdispatchOffsets + { + uint16_t dqo_version; + uint16_t dqo_label; + uint16_t dqo_label_size; + uint16_t dqo_flags; + uint16_t dqo_flags_size; + uint16_t dqo_serialnum; + uint16_t dqo_serialnum_size; + uint16_t dqo_width; + uint16_t dqo_width_size; + uint16_t dqo_running; + uint16_t dqo_running_size; + + uint16_t dqo_suspend_cnt; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_suspend_cnt_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + + LibdispatchOffsets () + { + dqo_version = UINT16_MAX; + dqo_flags = UINT16_MAX; + dqo_serialnum = UINT16_MAX; + dqo_label = UINT16_MAX; + dqo_width = UINT16_MAX; + dqo_running = UINT16_MAX; + dqo_suspend_cnt = UINT16_MAX; + dqo_target_queue = UINT16_MAX; + dqo_target_queue = UINT16_MAX; + dqo_priority = UINT16_MAX; + } + + bool + IsValid () + { + return dqo_version != UINT16_MAX; + } + + bool + LabelIsValid () + { + return dqo_label != UINT16_MAX; + } + }; + + struct LibdispatchVoucherOffsets + { + uint16_t vo_version; + uint16_t vo_activity_ids_count; + uint16_t vo_activity_ids_count_size; + uint16_t vo_activity_ids_array; + uint16_t vo_activity_ids_array_entry_size; + + LibdispatchVoucherOffsets () : + vo_version (UINT16_MAX), + vo_activity_ids_count (UINT16_MAX), + vo_activity_ids_count_size (UINT16_MAX), + vo_activity_ids_array (UINT16_MAX), + vo_activity_ids_array_entry_size (UINT16_MAX) + { } + + bool IsValid () { return vo_version != UINT16_MAX; } + }; + + struct LibdispatchTSDIndexes + { + uint16_t dti_version; + uint64_t dti_queue_index; + uint64_t dti_voucher_index; + uint64_t dti_qos_class_index; + + LibdispatchTSDIndexes () : + dti_version (UINT16_MAX), + dti_queue_index (UINT64_MAX), + dti_voucher_index (UINT64_MAX), + dti_qos_class_index (UINT64_MAX) + { } + + bool IsValid () { return dti_version != UINT16_MAX; } + }; + + struct LibpthreadOffsets + { + uint16_t plo_version; + uint16_t plo_pthread_tsd_base_offset; + uint16_t plo_pthread_tsd_base_address_offset; + uint16_t plo_pthread_tsd_entry_size; + + LibpthreadOffsets () : + plo_version (UINT16_MAX), + plo_pthread_tsd_base_offset (UINT16_MAX), + plo_pthread_tsd_base_address_offset (UINT16_MAX), + plo_pthread_tsd_entry_size (UINT16_MAX) + { + } + + bool IsValid () + { + return plo_version != UINT16_MAX; + } + }; + + // The libBacktraceRecording function __introspection_dispatch_queue_get_pending_items has + // two forms. It can either return a simple array of item_refs (void *) size or it can return + // a header with uint32_t version, a uint32_t size of item, and then an array of item_refs (void*) + // and code addresses (void*) for all the pending blocks. + + struct ItemRefAndCodeAddress { + lldb::addr_t item_ref; + lldb::addr_t code_address; + }; + + struct PendingItemsForQueue { + bool new_style; // new-style means both item_refs and code_addresses avail + // old-style means only item_refs is filled in + std::vector item_refs_and_code_addresses; + }; + + bool + BacktraceRecordingHeadersInitialized (); + + void + ReadLibdispatchOffsetsAddress(); + + void + ReadLibdispatchOffsets (); + + void + ReadLibpthreadOffsetsAddress(); + + void + ReadLibpthreadOffsets (); + + void + ReadLibdispatchTSDIndexesAddress (); + + void + ReadLibdispatchTSDIndexes (); + + PendingItemsForQueue + GetPendingItemRefsForQueue (lldb::addr_t queue); + + ItemInfo + ExtractItemInfoFromBuffer (lldb_private::DataExtractor &extractor); + + lldb_private::AppleGetQueuesHandler m_get_queues_handler; + lldb_private::AppleGetPendingItemsHandler m_get_pending_items_handler; + lldb_private::AppleGetItemInfoHandler m_get_item_info_handler; + lldb_private::AppleGetThreadItemInfoHandler m_get_thread_item_info_handler; + + lldb::addr_t m_page_to_free; + uint64_t m_page_to_free_size; + libBacktraceRecording_info m_lib_backtrace_recording_info; + + lldb::addr_t m_dispatch_queue_offsets_addr; + struct LibdispatchOffsets m_libdispatch_offsets; + + lldb::addr_t m_libpthread_layout_offsets_addr; + struct LibpthreadOffsets m_libpthread_offsets; + + lldb::addr_t m_dispatch_tsd_indexes_addr; + struct LibdispatchTSDIndexes m_libdispatch_tsd_indexes; + + lldb::addr_t m_dispatch_voucher_offsets_addr; + struct LibdispatchVoucherOffsets m_libdispatch_voucher_offsets; + + DISALLOW_COPY_AND_ASSIGN (SystemRuntimeMacOSX); +}; + +#endif // liblldb_SystemRuntimeMacOSX_h_ diff --git a/source/Plugins/UnwindAssembly/CMakeLists.txt b/source/Plugins/UnwindAssembly/CMakeLists.txt new file mode 100644 index 000000000000..1723a0604580 --- /dev/null +++ b/source/Plugins/UnwindAssembly/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(InstEmulation) +add_subdirectory(x86) diff --git a/source/Plugins/UnwindAssembly/InstEmulation/CMakeLists.txt b/source/Plugins/UnwindAssembly/InstEmulation/CMakeLists.txt new file mode 100644 index 000000000000..21673160bf40 --- /dev/null +++ b/source/Plugins/UnwindAssembly/InstEmulation/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginUnwindAssemblyInstEmulation + UnwindAssemblyInstEmulation.cpp + ) diff --git a/source/Plugins/UnwindAssembly/InstEmulation/Makefile b/source/Plugins/UnwindAssembly/InstEmulation/Makefile new file mode 100644 index 000000000000..e006235864f1 --- /dev/null +++ b/source/Plugins/UnwindAssembly/InstEmulation/Makefile @@ -0,0 +1,14 @@ +##==- source/Plugins/UnwindAssembly/InstEmulation/Makefile -*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginUnwindAssemblyInstEmulation +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/UnwindAssembly/x86/CMakeLists.txt b/source/Plugins/UnwindAssembly/x86/CMakeLists.txt new file mode 100644 index 000000000000..6ae63891bcc3 --- /dev/null +++ b/source/Plugins/UnwindAssembly/x86/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginUnwindAssemblyX86 + UnwindAssembly-x86.cpp + ) diff --git a/source/Plugins/UnwindAssembly/x86/Makefile b/source/Plugins/UnwindAssembly/x86/Makefile new file mode 100644 index 000000000000..24419c044f04 --- /dev/null +++ b/source/Plugins/UnwindAssembly/x86/Makefile @@ -0,0 +1,14 @@ +##==-- source/Plugins/UnwindAssembly/x86/Makefile ----------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginUnwindAssemblyX86 +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Symbol/CMakeLists.txt b/source/Symbol/CMakeLists.txt new file mode 100644 index 000000000000..6372fffde1f1 --- /dev/null +++ b/source/Symbol/CMakeLists.txt @@ -0,0 +1,36 @@ +add_lldb_library(lldbSymbol + ArmUnwindInfo.cpp + Block.cpp + ClangASTContext.cpp + ClangASTImporter.cpp + ClangExternalASTSourceCallbacks.cpp + ClangExternalASTSourceCommon.cpp + CompilerDecl.cpp + CompilerDeclContext.cpp + CompilerType.cpp + CompileUnit.cpp + CompactUnwindInfo.cpp + DebugMacros.cpp + Declaration.cpp + DWARFCallFrameInfo.cpp + Function.cpp + FuncUnwinders.cpp + GoASTContext.cpp + LineEntry.cpp + LineTable.cpp + ObjectFile.cpp + Symbol.cpp + SymbolContext.cpp + SymbolFile.cpp + SymbolVendor.cpp + Symtab.cpp + Type.cpp + TypeList.cpp + TypeMap.cpp + TypeSystem.cpp + UnwindPlan.cpp + UnwindTable.cpp + Variable.cpp + VariableList.cpp + VerifyDecl.cpp + ) diff --git a/source/Symbol/Makefile b/source/Symbol/Makefile new file mode 100644 index 000000000000..ae0cef0e242a --- /dev/null +++ b/source/Symbol/Makefile @@ -0,0 +1,14 @@ +##===- source/Symbol/Makefile ------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbSymbol +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Target/CMakeLists.txt b/source/Target/CMakeLists.txt new file mode 100644 index 000000000000..f1c2a7e98007 --- /dev/null +++ b/source/Target/CMakeLists.txt @@ -0,0 +1,59 @@ +include_directories(../Plugins/Process/Utility) + +add_lldb_library(lldbTarget + ABI.cpp + CPPLanguageRuntime.cpp + ExecutionContext.cpp + FileAction.cpp + JITLoader.cpp + JITLoaderList.cpp + InstrumentationRuntime.cpp + InstrumentationRuntimeStopInfo.cpp + Language.cpp + LanguageRuntime.cpp + Memory.cpp + MemoryHistory.cpp + ObjCLanguageRuntime.cpp + OperatingSystem.cpp + PathMappingList.cpp + Platform.cpp + Process.cpp + ProcessInfo.cpp + ProcessLaunchInfo.cpp + Queue.cpp + QueueItem.cpp + QueueList.cpp + RegisterContext.cpp + SectionLoadHistory.cpp + SectionLoadList.cpp + StackFrame.cpp + StackFrameList.cpp + StackID.cpp + StopInfo.cpp + SystemRuntime.cpp + Target.cpp + TargetList.cpp + Thread.cpp + ThreadCollection.cpp + ThreadList.cpp + ThreadPlan.cpp + ThreadPlanBase.cpp + ThreadPlanCallFunction.cpp + ThreadPlanCallFunctionUsingABI.cpp + ThreadPlanCallUserExpression.cpp + ThreadPlanPython.cpp + ThreadPlanRunToAddress.cpp + ThreadPlanShouldStopHere.cpp + ThreadPlanStepInRange.cpp + ThreadPlanStepInstruction.cpp + ThreadPlanStepOut.cpp + ThreadPlanStepOverBreakpoint.cpp + ThreadPlanStepOverRange.cpp + ThreadPlanStepRange.cpp + ThreadPlanStepThrough.cpp + ThreadPlanStepUntil.cpp + ThreadPlanTracer.cpp + ThreadSpec.cpp + UnixSignals.cpp + UnwindAssembly.cpp + ) diff --git a/source/Target/Makefile b/source/Target/Makefile new file mode 100644 index 000000000000..0d4be5449ad3 --- /dev/null +++ b/source/Target/Makefile @@ -0,0 +1,14 @@ +##===- source/Target/Makefile ------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbTarget +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp index 6bc9f2b0c9d8..311c695860fe 100644 --- a/source/Target/Process.cpp +++ b/source/Target/Process.cpp @@ -3980,7 +3980,7 @@ Process::ShouldBroadcastEvent (Event *event_ptr) { Vote stop_vote = m_thread_list.ShouldReportStop (event_ptr); if (log) - log->Printf ("Process::ShouldBroadcastEvent: should_stop: %i state: %s was_restarted: %i stop_vote: %d.", + log->Printf ("Process::ShouldBroadcastEvent: should_resume: %i state: %s was_restarted: %i stop_vote: %d.", should_resume, StateAsCString(state), was_restarted, stop_vote); diff --git a/source/Utility/CMakeLists.txt b/source/Utility/CMakeLists.txt new file mode 100644 index 000000000000..aa4ad0301352 --- /dev/null +++ b/source/Utility/CMakeLists.txt @@ -0,0 +1,20 @@ +add_lldb_library(lldbUtility + ARM_DWARF_Registers.cpp + ARM64_DWARF_Registers.cpp + ConvertEnum.cpp + JSON.cpp + KQueue.cpp + LLDBAssert.cpp + ModuleCache.cpp + NameMatches.cpp + PseudoTerminal.cpp + Range.cpp + RegisterNumber.cpp + SharingPtr.cpp + StringExtractor.cpp + StringExtractorGDBRemote.cpp + StringLexer.cpp + TaskPool.cpp + TimeSpecTimeout.cpp + UriParser.cpp + ) diff --git a/source/Utility/Makefile b/source/Utility/Makefile new file mode 100644 index 000000000000..13bcd8376c2d --- /dev/null +++ b/source/Utility/Makefile @@ -0,0 +1,15 @@ +##===- source/Utility/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../.. +LIBRARYNAME := lldbUtility +BUILD_ARCHIVE = 1 +NO_PEDANTIC = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 000000000000..1397c664d758 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,77 @@ +function(add_python_test_target name test_script args comment) + set(PYTHON_TEST_COMMAND + ${PYTHON_EXECUTABLE} + ${test_script} + ${args} + ) + + add_custom_target(${name} + COMMAND ${PYTHON_TEST_COMMAND} ${ARG_DEFAULT_ARGS} + COMMENT "${comment}" + ) +endfunction() + +if ("${LLDB_TEST_COMPILER}" STREQUAL "") + string(REGEX REPLACE ".*ccache\ +" "" LLDB_TEST_COMPILER ${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}) +endif() + +# The default architecture with which to compile test executables is the default LLVM target +# architecture, which itself defaults to the host architecture. +string(TOLOWER "${LLVM_TARGET_ARCH}" LLDB_DEFAULT_TEST_ARCH) +if( LLDB_DEFAULT_TEST_ARCH STREQUAL "host" ) + string(REGEX MATCH "^[^-]*" LLDB_DEFAULT_TEST_ARCH ${LLVM_HOST_TRIPLE}) +endif () + +# Allow the user to override the default by setting LLDB_TEST_ARCH +set(LLDB_TEST_ARCH + ${LLDB_DEFAULT_TEST_ARCH} + CACHE STRING "Specify the architecture to run LLDB tests as (x86|x64). Determines whether tests are compiled with -m32 or -m64") + +# Users can override LLDB_TEST_USER_ARGS to specify arbitrary arguments to pass to the script +set(LLDB_TEST_USER_ARGS + -C ${LLDB_TEST_COMPILER} + CACHE STRING "Specify additional arguments to pass to test runner. For example: '-C gcc -C clang -A i386 -A x86_64'") + +set(LLDB_TEST_COMMON_ARGS + --arch=${LLDB_TEST_ARCH} + --executable + ${CMAKE_BINARY_DIR}/bin/lldb${CMAKE_EXECUTABLE_SUFFIX} + -s + ${CMAKE_BINARY_DIR}/lldb-test-traces + -u CXXFLAGS + -u CFLAGS + ) + +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + set(LLDB_TEST_DEBUG_TEST_CRASHES + 0 + CACHE BOOL "(Windows only) Enables debugging of tests in the test suite by showing the crash dialog when lldb crashes") + + set(LLDB_TEST_HIDE_CONSOLE_WINDOWS + 1 + CACHE BOOL "(Windows only) Hides the console window for an inferior when it is launched through the test suite") + + if (LLDB_TEST_DEBUG_TEST_CRASHES) + set(LLDB_TEST_COMMON_ARGS ${LLDB_TEST_COMMON_ARGS} --enable-crash-dialog) + endif() + + if (NOT LLDB_TEST_HIDE_CONSOLE_WINDOWS) + set(LLDB_TEST_COMMON_ARGS ${LLDB_TEST_COMMON_ARGS} --show-inferior-console) + endif() +endif() + +add_python_test_target(check-lldb-single + ${LLDB_SOURCE_DIR}/test/dotest.py + "--no-multiprocess;${LLDB_TEST_COMMON_ARGS};${LLDB_TEST_USER_ARGS}" + "Testing LLDB with args: ${LLDB_TEST_COMMON_ARGS};${LLDB_TEST_USER_ARGS}" + ) + +set(LLDB_DOTEST_ARGS -q;${LLDB_TEST_COMMON_ARGS};${LLDB_TEST_USER_ARGS}) + +# If tests crash cause LLDB to crash, or things are otherwise unstable, or if machine-parsable +# output is desired (i.e. in continuous integration contexts) check-lldb-single is a better target. +add_python_test_target(check-lldb + ${LLDB_SOURCE_DIR}/test/dotest.py + "${LLDB_DOTEST_ARGS}" + "Testing LLDB (parallel execution, with a separate subprocess per test)" + ) diff --git a/test/dotest.py b/test/dotest.py new file mode 100755 index 000000000000..1f9d52354f52 --- /dev/null +++ b/test/dotest.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +if __name__ == "__main__": + import use_lldb_suite + + import lldbsuite.test + lldbsuite.test.run_suite() diff --git a/test/testcases b/test/testcases new file mode 120000 index 000000000000..f4a13dceb79c --- /dev/null +++ b/test/testcases @@ -0,0 +1 @@ +../packages/Python/lldbsuite/test \ No newline at end of file diff --git a/test/use_lldb_suite.py b/test/use_lldb_suite.py new file mode 100644 index 000000000000..63a098cea220 --- /dev/null +++ b/test/use_lldb_suite.py @@ -0,0 +1,22 @@ +import inspect +import os +import sys + +def find_lldb_root(): + lldb_root = os.path.dirname(inspect.getfile(inspect.currentframe())) + while True: + lldb_root = os.path.dirname(lldb_root) + if lldb_root is None: + return None + + test_path = os.path.join(lldb_root, "use_lldb_suite_root.py") + if os.path.isfile(test_path): + return lldb_root + return None + +lldb_root = find_lldb_root() +if lldb_root is not None: + import imp + module = imp.find_module("use_lldb_suite_root", [lldb_root]) + if module is not None: + imp.load_module("use_lldb_suite_root", *module) diff --git a/third_party/Python/module/pexpect-2.4/ANSI.py b/third_party/Python/module/pexpect-2.4/ANSI.py new file mode 100644 index 000000000000..537017e90b29 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/ANSI.py @@ -0,0 +1,334 @@ +"""This implements an ANSI terminal emulator as a subclass of screen. + +$Id: ANSI.py 491 2007-12-16 20:04:57Z noah $ +""" +# references: +# http://www.retards.org/terminals/vt102.html +# http://vt100.net/docs/vt102-ug/contents.html +# http://vt100.net/docs/vt220-rm/ +# http://www.termsys.demon.co.uk/vtansi.htm + +import screen +import FSM +import copy +import string + +def Emit (fsm): + + screen = fsm.memory[0] + screen.write_ch(fsm.input_symbol) + +def StartNumber (fsm): + + fsm.memory.append (fsm.input_symbol) + +def BuildNumber (fsm): + + ns = fsm.memory.pop() + ns = ns + fsm.input_symbol + fsm.memory.append (ns) + +def DoBackOne (fsm): + + screen = fsm.memory[0] + screen.cursor_back () + +def DoBack (fsm): + + count = int(fsm.memory.pop()) + screen = fsm.memory[0] + screen.cursor_back (count) + +def DoDownOne (fsm): + + screen = fsm.memory[0] + screen.cursor_down () + +def DoDown (fsm): + + count = int(fsm.memory.pop()) + screen = fsm.memory[0] + screen.cursor_down (count) + +def DoForwardOne (fsm): + + screen = fsm.memory[0] + screen.cursor_forward () + +def DoForward (fsm): + + count = int(fsm.memory.pop()) + screen = fsm.memory[0] + screen.cursor_forward (count) + +def DoUpReverse (fsm): + + screen = fsm.memory[0] + screen.cursor_up_reverse() + +def DoUpOne (fsm): + + screen = fsm.memory[0] + screen.cursor_up () + +def DoUp (fsm): + + count = int(fsm.memory.pop()) + screen = fsm.memory[0] + screen.cursor_up (count) + +def DoHome (fsm): + + c = int(fsm.memory.pop()) + r = int(fsm.memory.pop()) + screen = fsm.memory[0] + screen.cursor_home (r,c) + +def DoHomeOrigin (fsm): + + c = 1 + r = 1 + screen = fsm.memory[0] + screen.cursor_home (r,c) + +def DoEraseDown (fsm): + + screen = fsm.memory[0] + screen.erase_down() + +def DoErase (fsm): + + arg = int(fsm.memory.pop()) + screen = fsm.memory[0] + if arg == 0: + screen.erase_down() + elif arg == 1: + screen.erase_up() + elif arg == 2: + screen.erase_screen() + +def DoEraseEndOfLine (fsm): + + screen = fsm.memory[0] + screen.erase_end_of_line() + +def DoEraseLine (fsm): + + screen = fsm.memory[0] + if arg == 0: + screen.end_of_line() + elif arg == 1: + screen.start_of_line() + elif arg == 2: + screen.erase_line() + +def DoEnableScroll (fsm): + + screen = fsm.memory[0] + screen.scroll_screen() + +def DoCursorSave (fsm): + + screen = fsm.memory[0] + screen.cursor_save_attrs() + +def DoCursorRestore (fsm): + + screen = fsm.memory[0] + screen.cursor_restore_attrs() + +def DoScrollRegion (fsm): + + screen = fsm.memory[0] + r2 = int(fsm.memory.pop()) + r1 = int(fsm.memory.pop()) + screen.scroll_screen_rows (r1,r2) + +def DoMode (fsm): + + screen = fsm.memory[0] + mode = fsm.memory.pop() # Should be 4 + # screen.setReplaceMode () + +def Log (fsm): + + screen = fsm.memory[0] + fsm.memory = [screen] + fout = open ('log', 'a') + fout.write (fsm.input_symbol + ',' + fsm.current_state + '\n') + fout.close() + +class term (screen.screen): + """This is a placeholder. + In theory I might want to add other terminal types. + """ + def __init__ (self, r=24, c=80): + screen.screen.__init__(self, r,c) + +class ANSI (term): + + """This class encapsulates a generic terminal. It filters a stream and + maintains the state of a screen object. """ + + def __init__ (self, r=24,c=80): + + term.__init__(self,r,c) + + #self.screen = screen (24,80) + self.state = FSM.FSM ('INIT',[self]) + self.state.set_default_transition (Log, 'INIT') + self.state.add_transition_any ('INIT', Emit, 'INIT') + self.state.add_transition ('\x1b', 'INIT', None, 'ESC') + self.state.add_transition_any ('ESC', Log, 'INIT') + self.state.add_transition ('(', 'ESC', None, 'G0SCS') + self.state.add_transition (')', 'ESC', None, 'G1SCS') + self.state.add_transition_list ('AB012', 'G0SCS', None, 'INIT') + self.state.add_transition_list ('AB012', 'G1SCS', None, 'INIT') + self.state.add_transition ('7', 'ESC', DoCursorSave, 'INIT') + self.state.add_transition ('8', 'ESC', DoCursorRestore, 'INIT') + self.state.add_transition ('M', 'ESC', DoUpReverse, 'INIT') + self.state.add_transition ('>', 'ESC', DoUpReverse, 'INIT') + self.state.add_transition ('<', 'ESC', DoUpReverse, 'INIT') + self.state.add_transition ('=', 'ESC', None, 'INIT') # Selects application keypad. + self.state.add_transition ('#', 'ESC', None, 'GRAPHICS_POUND') + self.state.add_transition_any ('GRAPHICS_POUND', None, 'INIT') + self.state.add_transition ('[', 'ESC', None, 'ELB') + # ELB means Escape Left Bracket. That is ^[[ + self.state.add_transition ('H', 'ELB', DoHomeOrigin, 'INIT') + self.state.add_transition ('D', 'ELB', DoBackOne, 'INIT') + self.state.add_transition ('B', 'ELB', DoDownOne, 'INIT') + self.state.add_transition ('C', 'ELB', DoForwardOne, 'INIT') + self.state.add_transition ('A', 'ELB', DoUpOne, 'INIT') + self.state.add_transition ('J', 'ELB', DoEraseDown, 'INIT') + self.state.add_transition ('K', 'ELB', DoEraseEndOfLine, 'INIT') + self.state.add_transition ('r', 'ELB', DoEnableScroll, 'INIT') + self.state.add_transition ('m', 'ELB', None, 'INIT') + self.state.add_transition ('?', 'ELB', None, 'MODECRAP') + self.state.add_transition_list (string.digits, 'ELB', StartNumber, 'NUMBER_1') + self.state.add_transition_list (string.digits, 'NUMBER_1', BuildNumber, 'NUMBER_1') + self.state.add_transition ('D', 'NUMBER_1', DoBack, 'INIT') + self.state.add_transition ('B', 'NUMBER_1', DoDown, 'INIT') + self.state.add_transition ('C', 'NUMBER_1', DoForward, 'INIT') + self.state.add_transition ('A', 'NUMBER_1', DoUp, 'INIT') + self.state.add_transition ('J', 'NUMBER_1', DoErase, 'INIT') + self.state.add_transition ('K', 'NUMBER_1', DoEraseLine, 'INIT') + self.state.add_transition ('l', 'NUMBER_1', DoMode, 'INIT') + ### It gets worse... the 'm' code can have infinite number of + ### number;number;number before it. I've never seen more than two, + ### but the specs say it's allowed. crap! + self.state.add_transition ('m', 'NUMBER_1', None, 'INIT') + ### LED control. Same problem as 'm' code. + self.state.add_transition ('q', 'NUMBER_1', None, 'INIT') + + # \E[?47h appears to be "switch to alternate screen" + # \E[?47l restores alternate screen... I think. + self.state.add_transition_list (string.digits, 'MODECRAP', StartNumber, 'MODECRAP_NUM') + self.state.add_transition_list (string.digits, 'MODECRAP_NUM', BuildNumber, 'MODECRAP_NUM') + self.state.add_transition ('l', 'MODECRAP_NUM', None, 'INIT') + self.state.add_transition ('h', 'MODECRAP_NUM', None, 'INIT') + +#RM Reset Mode Esc [ Ps l none + self.state.add_transition (';', 'NUMBER_1', None, 'SEMICOLON') + self.state.add_transition_any ('SEMICOLON', Log, 'INIT') + self.state.add_transition_list (string.digits, 'SEMICOLON', StartNumber, 'NUMBER_2') + self.state.add_transition_list (string.digits, 'NUMBER_2', BuildNumber, 'NUMBER_2') + self.state.add_transition_any ('NUMBER_2', Log, 'INIT') + self.state.add_transition ('H', 'NUMBER_2', DoHome, 'INIT') + self.state.add_transition ('f', 'NUMBER_2', DoHome, 'INIT') + self.state.add_transition ('r', 'NUMBER_2', DoScrollRegion, 'INIT') + ### It gets worse... the 'm' code can have infinite number of + ### number;number;number before it. I've never seen more than two, + ### but the specs say it's allowed. crap! + self.state.add_transition ('m', 'NUMBER_2', None, 'INIT') + ### LED control. Same problem as 'm' code. + self.state.add_transition ('q', 'NUMBER_2', None, 'INIT') + + def process (self, c): + + self.state.process(c) + + def process_list (self, l): + + self.write(l) + + def write (self, s): + + for c in s: + self.process(c) + + def flush (self): + + pass + + def write_ch (self, ch): + + """This puts a character at the current cursor position. cursor + position if moved forward with wrap-around, but no scrolling is done if + the cursor hits the lower-right corner of the screen. """ + + #\r and \n both produce a call to crlf(). + ch = ch[0] + + if ch == '\r': + # self.crlf() + return + if ch == '\n': + self.crlf() + return + if ch == chr(screen.BS): + self.cursor_back() + self.put_abs(self.cur_r, self.cur_c, ' ') + return + + if ch not in string.printable: + fout = open ('log', 'a') + fout.write ('Nonprint: ' + str(ord(ch)) + '\n') + fout.close() + return + self.put_abs(self.cur_r, self.cur_c, ch) + old_r = self.cur_r + old_c = self.cur_c + self.cursor_forward() + if old_c == self.cur_c: + self.cursor_down() + if old_r != self.cur_r: + self.cursor_home (self.cur_r, 1) + else: + self.scroll_up () + self.cursor_home (self.cur_r, 1) + self.erase_line() + +# def test (self): +# +# import sys +# write_text = 'I\'ve got a ferret sticking up my nose.\n' + \ +# '(He\'s got a ferret sticking up his nose.)\n' + \ +# 'How it got there I can\'t tell\n' + \ +# 'But now it\'s there it hurts like hell\n' + \ +# 'And what is more it radically affects my sense of smell.\n' + \ +# '(His sense of smell.)\n' + \ +# 'I can see a bare-bottomed mandril.\n' + \ +# '(Slyly eyeing his other nostril.)\n' + \ +# 'If it jumps inside there too I really don\'t know what to do\n' + \ +# 'I\'ll be the proud posessor of a kind of nasal zoo.\n' + \ +# '(A nasal zoo.)\n' + \ +# 'I\'ve got a ferret sticking up my nose.\n' + \ +# '(And what is worst of all it constantly explodes.)\n' + \ +# '"Ferrets don\'t explode," you say\n' + \ +# 'But it happened nine times yesterday\n' + \ +# 'And I should know for each time I was standing in the way.\n' + \ +# 'I\'ve got a ferret sticking up my nose.\n' + \ +# '(He\'s got a ferret sticking up his nose.)\n' + \ +# 'How it got there I can\'t tell\n' + \ +# 'But now it\'s there it hurts like hell\n' + \ +# 'And what is more it radically affects my sense of smell.\n' + \ +# '(His sense of smell.)' +# self.fill('.') +# self.cursor_home() +# for c in write_text: +# self.write_ch (c) +# print str(self) +# +#if __name__ == '__main__': +# t = ANSI(6,65) +# t.test() diff --git a/third_party/Python/module/pexpect-2.4/FSM.py b/third_party/Python/module/pexpect-2.4/FSM.py new file mode 100644 index 000000000000..751eb37e1304 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/FSM.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python + +"""This module implements a Finite State Machine (FSM). In addition to state +this FSM also maintains a user defined "memory". So this FSM can be used as a +Push-down Automata (PDA) since a PDA is a FSM + memory. + +The following describes how the FSM works, but you will probably also need to +see the example function to understand how the FSM is used in practice. + +You define an FSM by building tables of transitions. For a given input symbol +the process() method uses these tables to decide what action to call and what +the next state will be. The FSM has a table of transitions that associate: + + (input_symbol, current_state) --> (action, next_state) + +Where "action" is a function you define. The symbols and states can be any +objects. You use the add_transition() and add_transition_list() methods to add +to the transition table. The FSM also has a table of transitions that +associate: + + (current_state) --> (action, next_state) + +You use the add_transition_any() method to add to this transition table. The +FSM also has one default transition that is not associated with any specific +input_symbol or state. You use the set_default_transition() method to set the +default transition. + +When an action function is called it is passed a reference to the FSM. The +action function may then access attributes of the FSM such as input_symbol, +current_state, or "memory". The "memory" attribute can be any object that you +want to pass along to the action functions. It is not used by the FSM itself. +For parsing you would typically pass a list to be used as a stack. + +The processing sequence is as follows. The process() method is given an +input_symbol to process. The FSM will search the table of transitions that +associate: + + (input_symbol, current_state) --> (action, next_state) + +If the pair (input_symbol, current_state) is found then process() will call the +associated action function and then set the current state to the next_state. + +If the FSM cannot find a match for (input_symbol, current_state) it will then +search the table of transitions that associate: + + (current_state) --> (action, next_state) + +If the current_state is found then the process() method will call the +associated action function and then set the current state to the next_state. +Notice that this table lacks an input_symbol. It lets you define transitions +for a current_state and ANY input_symbol. Hence, it is called the "any" table. +Remember, it is always checked after first searching the table for a specific +(input_symbol, current_state). + +For the case where the FSM did not match either of the previous two cases the +FSM will try to use the default transition. If the default transition is +defined then the process() method will call the associated action function and +then set the current state to the next_state. This lets you define a default +transition as a catch-all case. You can think of it as an exception handler. +There can be only one default transition. + +Finally, if none of the previous cases are defined for an input_symbol and +current_state then the FSM will raise an exception. This may be desirable, but +you can always prevent this just by defining a default transition. + +Noah Spurrier 20020822 +""" + +class ExceptionFSM(Exception): + + """This is the FSM Exception class.""" + + def __init__(self, value): + self.value = value + + def __str__(self): + return `self.value` + +class FSM: + + """This is a Finite State Machine (FSM). + """ + + def __init__(self, initial_state, memory=None): + + """This creates the FSM. You set the initial state here. The "memory" + attribute is any object that you want to pass along to the action + functions. It is not used by the FSM. For parsing you would typically + pass a list to be used as a stack. """ + + # Map (input_symbol, current_state) --> (action, next_state). + self.state_transitions = {} + # Map (current_state) --> (action, next_state). + self.state_transitions_any = {} + self.default_transition = None + + self.input_symbol = None + self.initial_state = initial_state + self.current_state = self.initial_state + self.next_state = None + self.action = None + self.memory = memory + + def reset (self): + + """This sets the current_state to the initial_state and sets + input_symbol to None. The initial state was set by the constructor + __init__(). """ + + self.current_state = self.initial_state + self.input_symbol = None + + def add_transition (self, input_symbol, state, action=None, next_state=None): + + """This adds a transition that associates: + + (input_symbol, current_state) --> (action, next_state) + + The action may be set to None in which case the process() method will + ignore the action and only set the next_state. The next_state may be + set to None in which case the current state will be unchanged. + + You can also set transitions for a list of symbols by using + add_transition_list(). """ + + if next_state is None: + next_state = state + self.state_transitions[(input_symbol, state)] = (action, next_state) + + def add_transition_list (self, list_input_symbols, state, action=None, next_state=None): + + """This adds the same transition for a list of input symbols. + You can pass a list or a string. Note that it is handy to use + string.digits, string.whitespace, string.letters, etc. to add + transitions that match character classes. + + The action may be set to None in which case the process() method will + ignore the action and only set the next_state. The next_state may be + set to None in which case the current state will be unchanged. """ + + if next_state is None: + next_state = state + for input_symbol in list_input_symbols: + self.add_transition (input_symbol, state, action, next_state) + + def add_transition_any (self, state, action=None, next_state=None): + + """This adds a transition that associates: + + (current_state) --> (action, next_state) + + That is, any input symbol will match the current state. + The process() method checks the "any" state associations after it first + checks for an exact match of (input_symbol, current_state). + + The action may be set to None in which case the process() method will + ignore the action and only set the next_state. The next_state may be + set to None in which case the current state will be unchanged. """ + + if next_state is None: + next_state = state + self.state_transitions_any [state] = (action, next_state) + + def set_default_transition (self, action, next_state): + + """This sets the default transition. This defines an action and + next_state if the FSM cannot find the input symbol and the current + state in the transition list and if the FSM cannot find the + current_state in the transition_any list. This is useful as a final + fall-through state for catching errors and undefined states. + + The default transition can be removed by setting the attribute + default_transition to None. """ + + self.default_transition = (action, next_state) + + def get_transition (self, input_symbol, state): + + """This returns (action, next state) given an input_symbol and state. + This does not modify the FSM state, so calling this method has no side + effects. Normally you do not call this method directly. It is called by + process(). + + The sequence of steps to check for a defined transition goes from the + most specific to the least specific. + + 1. Check state_transitions[] that match exactly the tuple, + (input_symbol, state) + + 2. Check state_transitions_any[] that match (state) + In other words, match a specific state and ANY input_symbol. + + 3. Check if the default_transition is defined. + This catches any input_symbol and any state. + This is a handler for errors, undefined states, or defaults. + + 4. No transition was defined. If we get here then raise an exception. + """ + + if self.state_transitions.has_key((input_symbol, state)): + return self.state_transitions[(input_symbol, state)] + elif self.state_transitions_any.has_key (state): + return self.state_transitions_any[state] + elif self.default_transition is not None: + return self.default_transition + else: + raise ExceptionFSM ('Transition is undefined: (%s, %s).' % + (str(input_symbol), str(state)) ) + + def process (self, input_symbol): + + """This is the main method that you call to process input. This may + cause the FSM to change state and call an action. This method calls + get_transition() to find the action and next_state associated with the + input_symbol and current_state. If the action is None then the action + is not called and only the current state is changed. This method + processes one complete input symbol. You can process a list of symbols + (or a string) by calling process_list(). """ + + self.input_symbol = input_symbol + (self.action, self.next_state) = self.get_transition (self.input_symbol, self.current_state) + if self.action is not None: + self.action (self) + self.current_state = self.next_state + self.next_state = None + + def process_list (self, input_symbols): + + """This takes a list and sends each element to process(). The list may + be a string or any iterable object. """ + + for s in input_symbols: + self.process (s) + +############################################################################## +# The following is an example that demonstrates the use of the FSM class to +# process an RPN expression. Run this module from the command line. You will +# get a prompt > for input. Enter an RPN Expression. Numbers may be integers. +# Operators are * / + - Use the = sign to evaluate and print the expression. +# For example: +# +# 167 3 2 2 * * * 1 - = +# +# will print: +# +# 2003 +############################################################################## + +import sys, os, traceback, optparse, time, string + +# +# These define the actions. +# Note that "memory" is a list being used as a stack. +# + +def BeginBuildNumber (fsm): + fsm.memory.append (fsm.input_symbol) + +def BuildNumber (fsm): + s = fsm.memory.pop () + s = s + fsm.input_symbol + fsm.memory.append (s) + +def EndBuildNumber (fsm): + s = fsm.memory.pop () + fsm.memory.append (int(s)) + +def DoOperator (fsm): + ar = fsm.memory.pop() + al = fsm.memory.pop() + if fsm.input_symbol == '+': + fsm.memory.append (al + ar) + elif fsm.input_symbol == '-': + fsm.memory.append (al - ar) + elif fsm.input_symbol == '*': + fsm.memory.append (al * ar) + elif fsm.input_symbol == '/': + fsm.memory.append (al / ar) + +def DoEqual (fsm): + print str(fsm.memory.pop()) + +def Error (fsm): + print 'That does not compute.' + print str(fsm.input_symbol) + +def main(): + + """This is where the example starts and the FSM state transitions are + defined. Note that states are strings (such as 'INIT'). This is not + necessary, but it makes the example easier to read. """ + + f = FSM ('INIT', []) # "memory" will be used as a stack. + f.set_default_transition (Error, 'INIT') + f.add_transition_any ('INIT', None, 'INIT') + f.add_transition ('=', 'INIT', DoEqual, 'INIT') + f.add_transition_list (string.digits, 'INIT', BeginBuildNumber, 'BUILDING_NUMBER') + f.add_transition_list (string.digits, 'BUILDING_NUMBER', BuildNumber, 'BUILDING_NUMBER') + f.add_transition_list (string.whitespace, 'BUILDING_NUMBER', EndBuildNumber, 'INIT') + f.add_transition_list ('+-*/', 'INIT', DoOperator, 'INIT') + + print + print 'Enter an RPN Expression.' + print 'Numbers may be integers. Operators are * / + -' + print 'Use the = sign to evaluate and print the expression.' + print 'For example: ' + print ' 167 3 2 2 * * * 1 - =' + inputstr = raw_input ('> ') + f.process_list(inputstr) + +if __name__ == '__main__': + try: + start_time = time.time() + parser = optparse.OptionParser(formatter=optparse.TitledHelpFormatter(), usage=globals()['__doc__'], version='$Id: FSM.py 490 2007-12-07 15:46:24Z noah $') + parser.add_option ('-v', '--verbose', action='store_true', default=False, help='verbose output') + (options, args) = parser.parse_args() + if options.verbose: print time.asctime() + main() + if options.verbose: print time.asctime() + if options.verbose: print 'TOTAL TIME IN MINUTES:', + if options.verbose: print (time.time() - start_time) / 60.0 + sys.exit(0) + except KeyboardInterrupt, e: # Ctrl-C + raise e + except SystemExit, e: # sys.exit() + raise e + except Exception, e: + print 'ERROR, UNEXPECTED EXCEPTION' + print str(e) + traceback.print_exc() + os._exit(1) diff --git a/third_party/Python/module/pexpect-2.4/INSTALL b/third_party/Python/module/pexpect-2.4/INSTALL new file mode 100644 index 000000000000..509e925f9491 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/INSTALL @@ -0,0 +1,31 @@ +Installation +------------ +This is a standard Python Distutil distribution. To install simply run: + + python setup.py install + +This makes Pexpect available to any script on the machine. You need +root access to install it this way. If you do not have root access or +if you do not wish to install Pexpect so that is available to any script +then you can just copy the pexpect.py file to same directory as your script. + +Trouble on Debian and Ubuntu +---------------------------- +For some stupid reason Debian Linux does not include the distutils module +in the standard 'python' package. Instead, the distutils module is packaged +separately in the 'python-dev' package. So to add distutils back +into Python, simply use aptitude or apt-get to install 'python-dev'. +As root, run this command: + apt-get install python-dev +Why they do this is mysterious because: + - It breaks the Python model of "batteries included". + 'distutils' isn't an extra or optional module -- + it's parts of the Standard Python Library. + - The Debian 'python-dev' package is a microscopic 50K installed. + So what are they saving? + - Distutils is not only interesting to developers. Many non-development + oriented Python packages use 'distutils' to install applications. + - As far as I can tell, the package maintainers must go through + more trouble to remove 'distutils' from the standard Python + distribution than it would take just to leave it in. + diff --git a/third_party/Python/module/pexpect-2.4/LICENSE b/third_party/Python/module/pexpect-2.4/LICENSE new file mode 100644 index 000000000000..e61144381c73 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/LICENSE @@ -0,0 +1,21 @@ +Free, open source, and all that good stuff. +Pexpect Copyright (c) 2008 Noah Spurrier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/third_party/Python/module/pexpect-2.4/PKG-INFO b/third_party/Python/module/pexpect-2.4/PKG-INFO new file mode 100644 index 000000000000..71cb54999eef --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: pexpect +Version: 2.4 +Summary: Pexpect is a pure Python Expect. It allows easy control of other applications. +Home-page: http://pexpect.sourceforge.net/ +Author: Noah Spurrier +Author-email: noah@noah.org +License: MIT license +Description: UNKNOWN +Platform: UNIX diff --git a/third_party/Python/module/pexpect-2.4/README b/third_party/Python/module/pexpect-2.4/README new file mode 100644 index 000000000000..3101dc890167 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/README @@ -0,0 +1,45 @@ +Pexpect is a Pure Python Expect-like module + +Pexpect makes Python a better tool for controlling other applications. + +Pexpect is a pure Python module for spawning child applications; controlling +them; and responding to expected patterns in their output. Pexpect works like +Don Libes' Expect. Pexpect allows your script to spawn a child application and +control it as if a human were typing commands. + +Pexpect can be used for automating interactive applications such as ssh, ftp, +passwd, telnet, etc. It can be used to a automate setup scripts for +duplicating software package installations on different servers. It can be +used for automated software testing. Pexpect is in the spirit of Don Libes' +Expect, but Pexpect is pure Python. Unlike other Expect-like modules for +Python, Pexpect does not require TCL or Expect nor does it require C +extensions to be compiled. It should work on any platform that supports the +standard Python pty module. The Pexpect interface was designed to be easy to use. + +If you want to work with the development version of the source code then please +read the DEVELOPERS document in the root of the source code tree. + +Free, open source, and all that good stuff. +Pexpect Copyright (c) 2008 Noah Spurrier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +Noah Spurrier +http://pexpect.sourceforge.net/ + diff --git a/third_party/Python/module/pexpect-2.4/doc/clean.css b/third_party/Python/module/pexpect-2.4/doc/clean.css new file mode 100644 index 000000000000..e8d98ddb2e59 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/doc/clean.css @@ -0,0 +1,103 @@ + +body { + margin:0px; + padding:0px; + font-family:verdana, arial, helvetica, sans-serif; + color:#333; + background-color:white; + } +pre { + background: #eeeeee; + border: 1px solid #888888; + color: black; + padding: 1em; + white-space: pre; +} +h1 { + margin:5px 0px 5px 0px; + padding:0px; + font-size:20px; + line-height:28px; + font-weight:900; + color:#44f; + } +h2 { + margin:5px 0px 5px 0px; + padding:0px; + font-size:17px; + line-height:28px; + font-weight:900; + color:#226; + } +h3 { + margin:5px 0px 5px 0px; + padding:0px; + font-size:15px; + line-height:28px; + font-weight:900; + } +p +{ + margin:0px 0px 16px 0px; + font:11px/20px verdana, arial, helvetica, sans-serif; + padding:0px; +} +table +{ + font-size: 10pt; + color: #000000; +} +td{border:1px solid #999;} + +table.pymenu {color: #000000; background-color: #99ccff} +th.pymenu {color: #ffffff; background-color: #003366} + +.code +{ + font-family: "Lucida Console", monospace; font-weight: bold; + color: #007700; background-color: #eeeeee +} + +#Content>p {margin:0px;} +#Content>p+p {text-indent:30px;} + +a { + text-decoration:none; + font-weight:600; + font-family:verdana, arial, helvetica, sans-serif; + color: #900; +} +//a:link {color:#09c;} +//a x:visited {color:#07a;} +a:hover {background-color:#ee0;} + +#Header { + margin:10px 0px 10px 0px; + padding:10px 0px 10px 20px; + /* For IE5/Win's benefit height = [correct height] + [top padding] + [top and bottom border widths] */ + height:33px; /* 14px + 17px + 2px = 33px */ + border-style:solid; + border-color:black; + border-width:1px 0px; /* top and bottom borders: 1px; left and right borders: 0px */ + line-height:33px; + background-color:#eee; + height:66px; /* the correct height */ + } + +#Content { + margin:0px 210px 50px 10px; + padding:10px; + } + +#Menu { + position:absolute; + top:100px; + right:20px; + width:172px; + padding:10px; + background-color:#eee; + border:1px solid #999; // dashed #999; + line-height:17px; + width:150px; + font-size:11px; + } diff --git a/third_party/Python/module/pexpect-2.4/doc/email.png b/third_party/Python/module/pexpect-2.4/doc/email.png new file mode 100644 index 0000000000000000000000000000000000000000..511113815b180db1558feafff79f0a4b040a6da9 GIT binary patch literal 322 zcmV-I0lof-P)NLwXB@*TGEL~WX>lFk^=`ICL#a#>NK00 zjgmQh?P{!=9gd&dtl3UhAFX|Rbged}HJ+Y78mrYxJu$ml)}4NBt+Dh{;4L^-VExyR zX20v9-n-9_Jj|n>NoiE_A{S>g)^Jl+j{s*SX+<$KpDu>*NvGUprS8s{D%mcl5-&SO zVCBrxd}w$ypNR05)!L5UbV01e65?WL?b6JYCl{lcg^^x+YoOo63M(e;=l5800VieL Ue0PPCuK)l507*qoM6N<$f(NgTO8@`> literal 0 HcmV?d00001 diff --git a/third_party/Python/module/pexpect-2.4/doc/examples.html b/third_party/Python/module/pexpect-2.4/doc/examples.html new file mode 100644 index 000000000000..2884a5c6cdc4 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/doc/examples.html @@ -0,0 +1,135 @@ + + + +Pexpect - Examples + + + + + + + + +
+ +

hive.py

+

+This script creates SSH connections to a list of hosts that +you provide. Then you are given a command line prompt. Each +shell command that you enter is sent to all the hosts. The +response from each host is collected and printed. For example, +you could connect to a dozen different machines and reboot +them all at once. +

+ +

script.py

+

+ This implements a command similar to the classic BSD +"script" command. + This will start a subshell and log all input and +output to a file. + This demonstrates the interact() method of Pexpect. +

+ +

fix_cvs_files.py

+

+ This is for cleaning up binary files improperly +added to CVS. + This script scans the given path to find binary +files; + checks with CVS to see if the sticky options are set +to -kb; + finally if sticky options are not -kb then uses 'cvs +admin' to + set the -kb option. +

+ +

ftp.py

+

+ This demonstrates an FTP "bookmark". + This connects to an ftp site; does a few ftp stuff; +and then gives the user + interactive control over the session. In this case +the "bookmark" is to a + directory on the OpenBSD ftp server. It puts you in +the i386 packages + directory. You can easily modify this for other +sites. + This demonstrates the interact() method of Pexpect. +

+ +

monitor.py

+

+ This runs a sequence of commands on a remote host +using SSH. + It runs a simple system checks such as uptime and +free to monitor + the state of the remote host. +

+ +

passmass.py

+

+ This will login to each given server and change the +password of the + given user. This demonstrates scripting logins and +passwords. +

+ +

python.py

+

+ This starts the python interpreter and prints the +greeting message backwards. + It then gives the user iteractive control of Python. +It's pretty useless! +

+ +

rippy.py

+

+ This is a wizard for mencoder. It greatly simplifies +the process of + ripping a DVD to Divx (mpeg4) format. It can +transcode from any + video file to another. It has options for resampling +the audio stream; + removing interlace artifacts, fitting to a target +file size, etc. + There are lots of options, but the process is simple +and easy to use. +

+ +

sshls.py

+

+ This lists a directory on a remote machine. +

+

ssh_tunnel.py

+

+ This starts an SSH tunnel to a remote machine. It +monitors the connection + and restarts the tunnel if it goes down. +

+

uptime.py

+

+ This will run the uptime command and parse the +output into variables. + This demonstrates using a single regular expression +to match the output + of a command and capturing different variable in +match groups. + The grouping regular expression handles a wide variety of different +uptime formats. +
+ +

+ The Pexpect project page on SourceForge.net +

+
+ + + diff --git a/third_party/Python/module/pexpect-2.4/doc/index.html b/third_party/Python/module/pexpect-2.4/doc/index.html new file mode 100644 index 000000000000..725fe984ce66 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/doc/index.html @@ -0,0 +1,868 @@ + + + +Pexpect - a Pure Python Expect-like module + + + + + + + + +
+

Pexpect makes Python a better tool for controlling other +applications.

+

Pexpect is a pure Python module for spawning child applications; +controlling them; and responding to expected patterns in their output. +Pexpect works like Don Libes' Expect. Pexpect allows your script to +spawn a child application and control it as if a human were typing +commands.

+

Pexpect can be used for automating interactive applications such as +ssh, ftp, passwd, telnet, etc. It can be used to a automate setup +scripts for duplicating software package installations on different +servers. It can be used for automated software testing. Pexpect is in +the spirit of Don Libes' Expect, but Pexpect is pure Python. Unlike +other Expect-like modules for Python, Pexpect does not require TCL or +Expect nor does it require C extensions to be compiled. It should work +on any platform that supports the standard Python pty module. The +Pexpect interface was designed to be easy to use.

+ + + + + + + +
Send questions to:Click to send email.
+
+

License: MIT style

+

+Free, open source, and all that good stuff.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Pexpect Copyright (c) 2008 Noah Spurrier
+http://pexpect.sourceforge.net/ +

+ +
+

Download

+

Download the +current version here from the SourceForge site. Grab the current Pexpect tarball. +

+

Installing Pexpect

+

The Pexpect tarball is a standard Python Distutil distribution.

+
    +
  1. download pexpect-2.4.tar.gz
  2. +
  3. tar zxf pexpect-2.4.tar.gz
  4. +
  5. cd pexpect-2.4
  6. +
  7. python setup.py install do this as root
  8. +
+

Examples

+

+Under the pexpect-2.4 directory you should find +the examples directory. +This is the best way to learn to use Pexpect. +See the descriptions of Pexpect Examples. +

+

API Documentation

+

+

+pexpect This is the main module that you want.
+pxssh Pexpect SSH is an extension of 'pexpect.spawn' that specializes in SSH.
+
+the following are experimental extensions to Pexpect
+
+fdpexpect fdpexpect extension of 'pexpect.spawn' that uses an open file descriptor.
+SCREEN This represents a virtual 'screen'.
+ANSI This parses ANSI/VT-100 terminal escape codes.
+FSM This is a finite state machine used by ANSI.
+
+

+
+

Project Status

+

Automated pyunit tests reach over 80% +code coverage on pexpect.py. I regularly test on Linux and BSD +platforms. I try to test on Solaris and Irix. +

+
+

Requirements for use of Pexpect

+

Python

+
+

Pexpect was written and tested with Python 2.4. It should work on +earlier versions that have the pty module. I +sometimes even manually test it with Python 1.5.2, but I can't easily +run the PyUnit test framework against Python 1.5.2, so I have less +confidence in Pexpect on Python 1.5.2.

+
+

pty module

+
+

Any POSIX system (UNIX) with a working pty +module should be able to run Pexpect. The pty +module is part of the Standard Python Library, so if you are running on +a POSIX system you should have it. The pty +module does not run the same on all platforms. It should be solid on Linux +and BSD systems. I have taken effort to try to smooth the wrinkles out of the different platforms. To learn more +about the wrinkles see Bugs and Testing.

+
+

Pexpect does not currently work on the standard Windows Python (see +the pty requirement); however, it seems to work fine using Cygwin. It is possible to build +something like a pty for Windows, but it would have to use a different +technique that I am still investigating. I know it's possible because +Libes' Expect was ported to Windows. If you have any ideas or +skills to contribute in this area then I would really appreciate some +tips on how to approach this problem.

+
+

Overview

+

Pexpect can be used for automating interactive applications such as +ssh, ftp, mencoder, passwd, etc. The Pexpect interface was designed to be +easy to use. Here is an example of Pexpect in action:

+
+
# This connects to the openbsd ftp site and
# downloads the recursive directory listing.
import pexpect
child = pexpect.spawn ('ftp ftp.openbsd.org')
child.expect ('Name .*: ')
child.sendline ('anonymous')
child.expect ('Password:')
child.sendline ('noah@example.com')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')
+
+

Obviously you could write an ftp client using Python's own ftplib module, but this is just a demonstration. +You can use this technique with any application. This is especially +handy if you are writing automated test tools.

+ +

There are two important methods in Pexpect -- expect() +and send() (or sendline() +which is like send() with a linefeed). +The expect() method waits for the child application +to return a given string. The string you specify is a regular expression, so +you can match complicated patterns. The send() method +writes a string to the child application. From the child's point of +view it looks just like someone typed the text from a terminal. After +each call to expect() the before and after +properties will be set to the text printed by child application. The before property will contain all text up to +the expected string pattern. The after string +will contain the text that was matched by the expected pattern. +The match property is set to the re MatchObject. +

+ +

An example of Pexpect in action may make things more clear. This example uses +ftp to login to the OpenBSD site; list files +in a directory; and then pass interactive control of the ftp session to +the human user.

+
+
import pexpect
child = pexpect.spawn ('ftp ftp.openbsd.org')
child.expect ('Name .*: ')
child.sendline ('anonymous')
child.expect ('Password:')
child.sendline ('noah@example.com')
child.expect ('ftp> ')
child.sendline ('ls /pub/OpenBSD/')
child.expect ('ftp> ')
print child.before # Print the result of the ls command.
child.interact() # Give control of the child to the user.
+
+

Special EOF and TIMEOUT patterns

+

+There are two special patterns to match the End Of File or a Timeout condition. +You you can pass these patterns to expect(). +These patterns are not regular expressions. Use them like predefined constants. +

+

If the child has died and you have read all the child's output then ordinarily +expect() will raise an EOF +exception. You can read everything up to the EOF without generating an +exception by using the EOF pattern expect(pexpect.EOF). +In this case everything the child has output will be available in the before property.

+

The pattern given to expect() may be a +regular expression or it may also be a list of regular expressions. +This allows you to match multiple optional responses. The expect() +method returns the index of the pattern that was matched. For example, +say you wanted to login to a server. After entering a password you +could get various responses from the server -- your password could be +rejected; or you could be allowed in and asked for your terminal type; +or you could be let right in and given a command prompt. The following +code fragment gives an example of this:

+
+
child.expect('password:')
child.sendline (my_secret_password)
# We expect any of these three patterns...
i = child.expect (['Permission denied', 'Terminal type', '[#\$] '])
if i==0:
print 'Permission denied on host. Can't login'
child.kill(0)
elif i==2:
print 'Login OK... need to send terminal type.'
child.sendline('vt100')
child.expect ('[#\$] ')
elif i==3:
print 'Login OK.'
print 'Shell command prompt', child.after
+
+

If nothing matches an expected pattern then expect will eventually +raise a TIMEOUT exception. The default time is 30 seconds, but you can +change this by passing a timeout argument to expect():

+
+
# Wait no more than 2 minutes (120 seconds) for password prompt.
child.expect('password:', timeout=120)
+
+

Find the end of line -- CR/LF conventions
+Matching at the end of a line can be tricky
+$ regex pattern is useless.
+

+

Pexpect matches regular expressions a little differently than what +you might be used to. +

+

The $ pattern for end of line match is useless. +The $ matches the end of string, but Pexpect reads from the child +one character at a time, so each character looks like the end of a line. +Pexpect can't do a look-ahead into the child's output stream. +In general you would have this situation when using regular expressions +with any stream.
+Note, pexpect does have an internal buffer, so reads are faster +than one character at a time, but from the user's perspective the regex +patterns test happens one character at a time.

+

The best way to match the end of a line is to look for the +newline: "\r\n" (CR/LF). Yes, that does appear to be DOS-style. +It may surprise some UNIX people to learn that terminal TTY device drivers +(dumb, vt100, ANSI, xterm, etc.) all use the CR/LF combination to signify +the end of line. Pexpect uses a Pseudo-TTY device to talk to the child application, so +when the child app prints "\n" you actually see "\r\n". +

+

UNIX uses just linefeeds to end lines of text, but not when it +comes to TTY devices! TTY devices are more like the Windows world. +Each line of text end with a CR/LF combination. When you intercept data +from a UNIX command from a TTY device you will find that the TTY device +outputs a CR/LF combination. A UNIX command may only write a linefeed +(\n), but the TTY device driver converts it to CR/LF. This means that +your terminal will see lines end with CR/LF (hex 0D 0A). +Since Pexpect emulates a terminal, to match ends of lines you have to +expect the CR/LF combination.

+
+

child.expect ('\r\n')

+
+

If you just need to skip past a new line then expect +('\n') by itself will work, but if you are expecting a specific +pattern before the end of line then you need to explicitly look for the +\r. For example the following expects a word at the end of a line:

+
+

child.expect ('\w+\r\n')

+
+

But the following would both fail:

+
+

child.expect ('\w+\n')

+
+

And as explained before, trying to use '$' to match the end of line +would not work either:

+
+

child.expect ('\w+$')

+
+

So if you need to explicitly look for the END OF LINE, you want to +look for the CR/LF combination -- not just the LF and not the $ pattern.

+

This problem is not limited to Pexpect. This problem happens any +time you try to perform a regular expression match on a stream. Regular +expressions need to look ahead. With a stream it is hard to look ahead +because the process generating the stream may not be finished. There is no +way to know if the process has paused momentarily or is finished and +waiting for you. Pexpect must implicitly always +do a NON greedy match (minimal) at the end of a input {### already said +this}.

+

Pexpect compiles all regular expressions with the DOTALL flag. With +the DOTALL flag a "." will match a newline. See the Python documentation

+

Beware of + and * at the end of input.

+

Remember that any time you try to match a pattern that needs +look-ahead that you will always get a minimal match (non greedy). For +example, the following will always return just one character:

+
+

child.expect ('.+')

+
+

This example will match successfully, but will always return no +characters:

+
+

child.expect ('.*')

+
+

Generally any star * expression will match as little as possible

+

One thing you can do is to try to force a non-ambiguous character at +the end of your \d+ pattern. Expect that +character to delimit the string. For example, you might try making the +end of your pattrn be \D+ instead of \D*. That means number digits alone would not +satisfy the (\d+) pattern. You would need +some number(s) and at least one \D at the +end.

+

Matching groups

+

You can group regular expression using parenthesis. After a match, +the match parameter of the spawn object will +contain the Python Match object.

+

Examples

+

Using "match" and groups...

+

Debugging

+

If you get the string value of a pexpect.spawn object you will get +lots of useful debugging information. For debugging it's very useful to +use the following pattern:

+

try:
+    i = child.expect ([pattern1, pattern2, pattern3, +etc])
+except:
+    print "Exception was thrown"
+    print "debug information:"
+    print str(child)
+

+

It is also useful to log the child's input and out to a file or the +screen. The following will turn on logging and send output to stdout +(the screen).
+

+

    child = pexpect.spawn (foo)
+    child.logfile = sys.stdout
+
+

+
+

Exceptions

+

EOF

+

Note that two flavors of EOF Exception may be thrown. They are +virtually identical except for the message string. For practical +purposes you should have no need to distinguish between them, but they +do give a little extra information about what type of platform you are +running. The two messages are:

+
+

End Of File (EOF) in read(). Exception style platform.

+

End Of File (EOF) in read(). Empty string style +platform.

+
+

Some UNIX platforms will throw an exception when you try to read +from a file descriptor in the EOF state. Other UNIX platforms instead +quietly return an empty string to indicate that the EOF state has been +reached.

+

Expecting EOF

+

If you wish to read up to the end of the child's output without +generating an EOF exception then use the expect(pexpect.EOF) method.

+

TIMEOUT

+

The expect() and read() +methods will also timeout if the child does not generate any output for +a given amount of time. If this happens they will raise a TIMEOUT exception. You can have these method +ignore a timeout and block indefinitely by passing None for the timeout +parameter.

+
+

child.expect(pexpect.EOF, timeout=None)

+
+
+

FAQ

+

Q: Why don't shell pipe and redirect (| and >) work when I +spawn a command?

+

+ +A: Remember that Pexpect does NOT interpret shell meta characters such as +redirect, pipe, or wild cards (>, |, or *). That's done by a shell not the +command you are spawning. This is a common mistake. If you want to run a +command and pipe it through another command then you must also start a shell. +For example: + +

+    child = pexpect.spawn('/bin/sh -c "ls -l | grep LOG > log_list.txt"')
+    child.expect(pexpect.EOF)
+
+ +The second form of spawn (where you pass a list of arguments) is useful in +situations where you wish to spawn a command and pass it its own argument list. +This can make syntax more clear. For example, the following is equivalent to +the previous example: + +
+    shell_cmd = 'ls -l | grep LOG > log_list.txt'
+    child = pexpect.spawn ('/bin/sh', ['-c', shell_cmd])
+    child.expect (pexpect.EOF)
+
+ +

+

Q: Isn't there already a Python Expect?

+

A: Yes, there are several of them. They usually require you to +compile C. I wanted something that was pure Python and preferably a +single module that was simple to install. I also wanted something that +was easy to use. This pure Python expect only recently became possible +with the introduction of the pty module in the standard Python library. +Previously C extensions were required.

+ +

Q: The before and after properties sound weird.

+

Originally I was going to model Pexpect more after Expect, but then +I found that I could never remember how to get the context of the stuff +I was trying to parse. I hate having to read my own documentation. I +decided that it was easier for me to remember what before and after +was. It just so happens that this is how the -B and -A options in grep +works, so that made it even easier for me to remember. Whatever makes +my life easier is what's best.

+ +

Q: Why not just use Expect?

+

A: I love it. It's great. I has bailed me out of some real jams, but +I wanted something that would do 90% of what I need from Expect; be 10% +of the size; and allow me to write my code in Python instead of TCL. +Pexpect is not nearly as big as Expect, but Pexpect does everything I +have ever used Expect for. +

+ +

Q: Why not just use a pipe (popen())?

+

A: A pipe works fine for getting the output to non-interactive +programs. If you just want to get the output from ls, +uname, or ping +then this works. Pipes do not work very well for interactive programs +and pipes will almost certainly fail for most applications that ask for +passwords such as telnet, ftp, or ssh.

+

There are two reasons for this.

+

First an application may bypass stdout and print directly to its +controlling TTY. Something like SSH will do this when it asks you for a +password. This is why you cannot redirect the password prompt because +it does not go through stdout or stderr.

+

The second reason is because most applications are built using the C +Standard IO Library (anything that uses #include +<stdio.h>). One of the features of the stdio library is +that it buffers all input and output. Normally output is line +buffered when a program is printing to a TTY (your terminal +screen). Every time the program prints a line-feed the currently +buffered data will get printed to your screen. The problem comes when +you connect a pipe. The stdio library is smart and can tell that it is +printing to a pipe instead of a TTY. In that case it switches from line +buffer mode to block buffered. In this mode the +currently buffered data is flushed when the buffer is full. This causes +most interactive programs to deadlock. Block buffering is more +efficient when writing to disks and pipes. Take the situation where a +program prints a message "Enter your user name:\n" and then waits for +you type type something. In block buffered mode, the stdio library will +not put the message into the pipe even though a linefeed is printed. +The result is that you never receive the message, yet the child +application will sit and wait for you to type a response. Don't confuse +the stdio lib's buffer with the pipe's buffer. The pipe buffer is +another area that can cause problems. You could flush the input side of +a pipe, whereas you have no control over the stdio library buffer.

+

More information: the Standard IO library has three states for a +FILE *. These are: _IOFBF for block buffered; _IOLBF for line buffered; +and _IONBF for unbuffered. The STDIO lib will use block buffering when +talking to a block file descriptor such as a pipe. This is usually not +helpful for interactive programs. Short of recompiling your program to +include fflush() everywhere or recompiling a custom stdio library there +is not much a controlling application can do about this if talking over +a pipe.

+

The program may have put data in its output that remains unflushed +because the output buffer is not full; then the program will go and +deadlock while waiting for input -- because you never send it any +because you are still waiting for its output (still stuck in the +STDIO's output buffer).

+

The answer is to use a pseudo-tty. A TTY device will force line +buffering (as opposed to block buffering). Line buffering means that +you will get each line when the child program sends a line feed. This +corresponds to the way most interactive programs operate -- send a line +of output then wait for a line of input.

+

I put "answer" in quotes because it's ugly solution and because +there is no POSIX standard for pseudo-TTY devices (even though they +have a TTY standard...). What would make more sense to me would be to +have some way to set a mode on a file descriptor so that it will tell +the STDIO to be line-buffered. I have investigated, and I don't think +there is a way to set the buffered state of a child process. The STDIO +Library does not maintain any external state in the kernel or whatnot, +so I don't think there is any way for you to alter it. I'm not quite +sure how this line-buffered/block-buffered state change happens +internally in the STDIO library. I think the STDIO lib looks at the +file descriptor and decides to change behavior based on whether it's a +TTY or a block file (see isatty()).

+

I hope that this qualifies as helpful.

+ +

Don't use a pipe to control another application...

+

Pexpect may seem similar to os.popen() or +commands module. The main difference is that +Pexpect (like Expect) uses a pseudo-TTY to talk to the child +application. Most applications do no work well through the system() +call or through pipes. And probably all applications that ask a user to +type in a password will fail. These applications bypass the stdin and +read directly from the TTY device. Many applications do not explicitly +flush their output buffers. This causes deadlocks if you try to control +an interactive application using a pipe. What happens is that most UNIX +applications use the stdio (#include <stdio.h>) for input and +output. The stdio library behaves differently depending on where the +output is going. There is no way to control this behavior from the +client end.
+

+ +

Q: Can I do screen scraping with this thing?

+

A: That depends. If your application just does line-oriented output +then this is easy. If it does screen-oriented output then it may work, +but it could be hard. For example, trying to scrape data from the 'top' +command would be hard. The top command repaints the text window.

+

I am working on an ANSI / VT100 terminal emulator that will have +methods to get characters from an arbitrary X,Y coordinate of the +virtual screen. It works and you can play with it, but I have no +working examples at this time.

+
+

Bugs

+

Threads

+

On Linux (RH 8) you cannot spawn a child from a different thread and +pass the handle back to a worker thread. The child is successfully +spawned but you can't interact with it. The only way to make it work is +to spawn and interact with the child all in the same thread. [Adam +Kerrison]

+

Timing issue with send() and sendline()

+

This problem has been addressed and should not effect most users.

+

It is sometimes possible to read an echo of the string sent with send() and sendline(). +If you call sendline() and then immediately +call readline() you may get part of your +output echoed back. You may read back what you just wrote even if the +child application does not explicitly echo it. Timing is critical. This +could be a security issue when talking to an application that asks for +a password; otherwise, this does not seem like a big deal. But why +do TTYs do this?

+

People usually report this when they are trying to control SSH or +some other login. For example, if your code looks something like this:

+
child.expect ('[pP]assword:')
child.sendline (my_password)
+


+

+1. SSH prints "password:" prompt to the user.
+2. SSH turns off echo on the TTY device.
+3. SSH waits for user to enter a password.
+
+When scripting with Pexpect what can happen is that Pexpect will response to the "password:" prompt +before SSH has had time to turn off TTY echo. In other words, Pexpect sends the password between +steps 1. and 2., so the password gets echoed back to the TTY. I would call this an SSH bug. +

+

+Pexpect now automatically adds a short delay before sending data to a child process. +This more closely mimics what happens in the usual human-to-app interaction. +The delay can be tuned with the 'delaybeforesend' attribute of the spawn class. +In general, this fixes the problem for everyone and so this should not be an issue +for most users. For some applications you might with to turn it off. + child = pexpect.spawn ("ssh user@example.com") + child.delaybeforesend = 0 +

+


+

+

Try changing it to look like the following. I know that this fix +does not look correct, but it works. I have not figured out exactly +what is happening. You would think that the sleep should be after the +sendline(). The fact that the sleep helps when it's between the +expect() and the sendline() must be a clue.

+
child.expect ('[pP]assword:')
child.sendline (my_password)
+

Timing issue with isalive()

+

Reading the state of isalive() immediately after a child exits may +sometimes return 1. This is a race condition. The child has closed its +file descriptor, but has not yet fully exited before Pexpect's +isalive() executes. Addings a slight delay before the isalive() will +help. In the following example isalive() +sometimes returns 1:

+
+
child = pexpect.spawn('ls')
child.expect(pexpect.EOF)
print child.isalive()
+
+

But if there is any delay before the call to isalive() +then it will always return 0 as expected.

+
+
child = pexpect.spawn('ls')
child.expect(pexpect.EOF)
time.sleep(0.1)
print child.isalive()
+
+ +

Truncated output just before child exits

+

So far I have seen this only on older versions of Apple's MacOS X. +If the child application quits it may not flush its output buffer. This +means that your Pexpect application will receive an EOF even though it +should have received a little more data before the child died. This is +not generally a problem when talking to interactive child applications. +One example where it is a problem is when trying to read output from a +program like 'ls'. You may receive most of +the directory listing, but the last few lines will get lost before you +receive an EOF. The reason for this is that 'ls' +runs; completes its task; and then exits. The buffer is not flushed +before exit so the last few lines are lost. The following example +demonstrates the problem:

+

+
+
child = pexpect.spawn ('ls -l')
child.expect (pexpect.EOF)
print child.before
+
+

+ +

Controlling SSH on Solaris

+

Pexpect does not yet work perfectly on Solaris. +One common problem is that SSH sometimes will not allow TTY password +authentication. For example, you may expect SSH to ask you for a +password using code like this: +

+
child = pexpect.spawn ('ssh user@example.com')
child.expect ('assword')
child.sendline ('mypassword')
+You may see the following error come back from a spawned +child SSH: +

+
Permission denied (publickey,keyboard-interactive).
+

+This means that SSH thinks it can't access the TTY to ask you for your +password. +The only solution I have found is to use public key authentication with +SSH. +This bypasses the need for a password. I'm not happy with this +solution. +The problem is due to poor support for Solaris Pseudo TTYs in the +Python +Standard Library.

+
+

CHANGES

+

Current Release

+

Fixed OSError exception when a pexpect object is cleaned up. +Previously you might have seen this exception:

+
+
Exception exceptions.OSError: (10, 'No child processes') 
in <bound method spawn.__del__ of
<pexpect.spawn instance at 0xd248c>> ignored
+
+

You should not see that anymore. Thanks to Michael Surette.

+

Added support for buffering reads. This greatly improves speed when +trying to match long output from a child process. When you create an +instance of the spawn object you can then set a buffer size. For now +you MUST do the following to turn on buffering -- it may be on by +default in future version.

+
+
child = pexpect.spawn ('my_command')
child.maxread=1000 # Sets buffer to 1000 characters.
+
+
+

I made a subtle change to the way TIMEOUT and EOF exceptions behave. +Previously you could either expect these states in which case pexpect +will not raise an exception, or you could just let pexpect raise an +exception when these states were encountered. If you expected the +states then the 'before' property was set to everything before the +state was encountered, but if you let pexpect raise the exception then +'before' was not set. Now the 'before' property will get set either way +you choose to handle these states.

+

Older changes...

+

The spawn object now provides iterators for a file-like interface. +This makes Pexpect a more complete file-like object. You can now write +code like this:

+
+
child = pexpect.spawn ('ls -l')
for line in child:
print line
+
+

I added the attribute exitstatus. This +will give the exit code returned by the child process. This will be set +to None while the child is still alive. When +isalive() returns 0 then exitstatus +will be set.

+

I made a few more tweaks to isalive() so +that it will operate more consistently on different platforms. Solaris +is the most difficult to support.

+

 

+

You can now put TIMEOUT in a list of +expected patterns. This is just like putting EOF +in the pattern list. Expecting for a TIMEOUT +may not be used as often as EOF, but this +makes Pexpect more consitent.

+

Thanks to a suggestion and sample code from Chad J. Schroeder I +added the ability for Pexpect to operate on a file descriptor that is +already open. This means that Pexpect can be used to control streams +such as those from serial port devices. Now you just pass the integer +file descriptor as the "command" when contsructing a spawn open. For +example on a Linux box with a modem on ttyS1:

+
+
fd = os.open("/dev/ttyS1", os.O_RDWR|os.O_NONBLOCK|os.O_NOCTTY)
m = pexpect.spawn(fd) # Note integer fd is used instead of usual string.
m.send("+++") # Escape sequence
m.send("ATZ0\r") # Reset modem to profile 0
rval = m.expect(["OK", "ERROR"])
+
+

Pexpect now tests itself on Compile Farm!

+

I wrote a nice script that uses ssh to connect to each machine on +Source Forge's Compile Farm and then run the testall.py script for each +platform. The result of the test is then recorded for each platform. +Now it's easy to run regression tests across multiple platforms.

+

Pexpect is a file-like object

+

The spawn object now provides a file-like interface. It +supports most of the methods and attributes defined for Python File +Objects.

+

I changed write and writelines() so that they no longer return a +value. Use send() if you need that functionality. I did this to make +the Spawn object more closely match a file-like object.

+

read() was renamed to read_nonblocking(). I added a new read() +method that matches file-like object interface. In general, you should +not notice the difference except that read() no longer allows you to +directly set the timeout value. I hope this will not effect any +existing code. Switching to read_nonblocking() should fix existing code.

+

I changed the name of set_echo() to setecho().

+

I changed the name of send_eof() to sendeof().

+

I modified kill() so that it checks to +make sure the pid isalive().

+

I modified spawn() (really called from __spawn())so that it does not raise an expection +if setwinsize() fails. Some platforms such +as Cygwin do not like setwinsize. This was a constant problem and since +it is not a critical feature I decided to just silence the error. +Normally I don't like to do that, but in this case I'm making an +exception.

+

Added a method close() that does what you +think. It closes the file descriptor of the child application. It makes +no attempt to actually kill the child or wait for its status.

+

Add variables __version__ and __revision__ (from cvs) to the pexpect modules. +This is mainly helpful to me so that I can make sure that I'm testing +with the right version instead of one already installed.

+

Logging changes

+
+

log_open() and log_close() +have been removed. Now use setlog(). The setlog() method takes a file object. This is far +more flexible than the previous log method. Each time data is written +to the file object it will be flushed. To turn logging off simply call setlog() with None.

+
+

isalive changes

+
+

I renamed the isAlive() method to isalive() to match the more typical naming style +in Python. Also the technique used to detect child process status has +been drastically modified. Previously I did some funky stuff with +signals which caused indigestion in other Python modules on some +platforms. It's was a big headache. It still is, but I think it works +better now.

+
+

attribute name changes

+
+

The names of some attributes have been changed. This effects the +names of the attributes that are set after called the expect() method.

+ + + + + + + + + + + + + + + + + + + +
NEW NAMEOLD NAME
before
+ Everything before the match.
before
after
+ Everything after and including the first character of the +match
matched
match
+ This is the re MatchObject from the match.
+You can get groups() from this.
+See 'uptime.py' in the examples tar ball.
New -- Did not exist
+
+

EOF changes

+
+

The expect_eof() method is gone. You +can now simply use the expect() method to +look for EOF.

+

Was:

+
+

p.expect_eof ()

+
+

Now:

+
+

p.expect (pexpect.EOF)

+
+
+
+

TESTING

+

The following platforms have been tested:

+ +

 

+

TO DO

+

Add an option to add a delay after each expect() or before each +read()/readline() call to automatically avoid the echo +bug.

+

 

+
+
+ + + + + + +
Click to send email.
+
+ + + diff --git a/third_party/Python/module/pexpect-2.4/doc/index.template.html b/third_party/Python/module/pexpect-2.4/doc/index.template.html new file mode 100644 index 000000000000..ab63b2dec963 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/doc/index.template.html @@ -0,0 +1,868 @@ + + + +Pexpect - a Pure Python Expect-like module + + + + + + + + +
+

Pexpect makes Python a better tool for controlling other +applications.

+

Pexpect is a pure Python module for spawning child applications; +controlling them; and responding to expected patterns in their output. +Pexpect works like Don Libes' Expect. Pexpect allows your script to +spawn a child application and control it as if a human were typing +commands.

+

Pexpect can be used for automating interactive applications such as +ssh, ftp, passwd, telnet, etc. It can be used to a automate setup +scripts for duplicating software package installations on different +servers. It can be used for automated software testing. Pexpect is in +the spirit of Don Libes' Expect, but Pexpect is pure Python. Unlike +other Expect-like modules for Python, Pexpect does not require TCL or +Expect nor does it require C extensions to be compiled. It should work +on any platform that supports the standard Python pty module. The +Pexpect interface was designed to be easy to use.

+ + + + + + + +
Send questions to:Click to send email.
+
+

License: MIT style

+

+Free, open source, and all that good stuff.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Pexpect Copyright (c) 2008 Noah Spurrier
+http://pexpect.sourceforge.net/ +

+ +
+

Download

+

Download the +current version here from the SourceForge site. Grab the current Pexpect tarball. +

+

Installing Pexpect

+

The Pexpect tarball is a standard Python Distutil distribution.

+
    +
  1. download pexpect-VERSION.tar.gz
  2. +
  3. tar zxf pexpect-VERSION.tar.gz
  4. +
  5. cd pexpect-VERSION
  6. +
  7. python setup.py install do this as root
  8. +
+

Examples

+

+Under the pexpect-VERSION directory you should find +the examples directory. +This is the best way to learn to use Pexpect. +See the descriptions of Pexpect Examples. +

+

API Documentation

+

+

+pexpect This is the main module that you want.
+pxssh Pexpect SSH is an extension of 'pexpect.spawn' that specializes in SSH.
+
+the following are experimental extensions to Pexpect
+
+fdpexpect fdpexpect extension of 'pexpect.spawn' that uses an open file descriptor.
+SCREEN This represents a virtual 'screen'.
+ANSI This parses ANSI/VT-100 terminal escape codes.
+FSM This is a finite state machine used by ANSI.
+
+

+
+

Project Status

+

Automated pyunit tests reach over 80% +code coverage on pexpect.py. I regularly test on Linux and BSD +platforms. I try to test on Solaris and Irix. +

+
+

Requirements for use of Pexpect

+

Python

+
+

Pexpect was written and tested with Python 2.4. It should work on +earlier versions that have the pty module. I +sometimes even manually test it with Python 1.5.2, but I can't easily +run the PyUnit test framework against Python 1.5.2, so I have less +confidence in Pexpect on Python 1.5.2.

+
+

pty module

+
+

Any POSIX system (UNIX) with a working pty +module should be able to run Pexpect. The pty +module is part of the Standard Python Library, so if you are running on +a POSIX system you should have it. The pty +module does not run the same on all platforms. It should be solid on Linux +and BSD systems. I have taken effort to try to smooth the wrinkles out of the different platforms. To learn more +about the wrinkles see Bugs and Testing.

+
+

Pexpect does not currently work on the standard Windows Python (see +the pty requirement); however, it seems to work fine using Cygwin. It is possible to build +something like a pty for Windows, but it would have to use a different +technique that I am still investigating. I know it's possible because +Libes' Expect was ported to Windows. If you have any ideas or +skills to contribute in this area then I would really appreciate some +tips on how to approach this problem.

+
+

Overview

+

Pexpect can be used for automating interactive applications such as +ssh, ftp, mencoder, passwd, etc. The Pexpect interface was designed to be +easy to use. Here is an example of Pexpect in action:

+
+
# This connects to the openbsd ftp site and
# downloads the recursive directory listing.
import pexpect
child = pexpect.spawn ('ftp ftp.openbsd.org')
child.expect ('Name .*: ')
child.sendline ('anonymous')
child.expect ('Password:')
child.sendline ('noah@example.com')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')
+
+

Obviously you could write an ftp client using Python's own ftplib module, but this is just a demonstration. +You can use this technique with any application. This is especially +handy if you are writing automated test tools.

+ +

There are two important methods in Pexpect -- expect() +and send() (or sendline() +which is like send() with a linefeed). +The expect() method waits for the child application +to return a given string. The string you specify is a regular expression, so +you can match complicated patterns. The send() method +writes a string to the child application. From the child's point of +view it looks just like someone typed the text from a terminal. After +each call to expect() the before and after +properties will be set to the text printed by child application. The before property will contain all text up to +the expected string pattern. The after string +will contain the text that was matched by the expected pattern. +The match property is set to the re MatchObject. +

+ +

An example of Pexpect in action may make things more clear. This example uses +ftp to login to the OpenBSD site; list files +in a directory; and then pass interactive control of the ftp session to +the human user.

+
+
import pexpect
child = pexpect.spawn ('ftp ftp.openbsd.org')
child.expect ('Name .*: ')
child.sendline ('anonymous')
child.expect ('Password:')
child.sendline ('noah@example.com')
child.expect ('ftp> ')
child.sendline ('ls /pub/OpenBSD/')
child.expect ('ftp> ')
print child.before # Print the result of the ls command.
child.interact() # Give control of the child to the user.
+
+

Special EOF and TIMEOUT patterns

+

+There are two special patterns to match the End Of File or a Timeout condition. +You you can pass these patterns to expect(). +These patterns are not regular expressions. Use them like predefined constants. +

+

If the child has died and you have read all the child's output then ordinarily +expect() will raise an EOF +exception. You can read everything up to the EOF without generating an +exception by using the EOF pattern expect(pexpect.EOF). +In this case everything the child has output will be available in the before property.

+

The pattern given to expect() may be a +regular expression or it may also be a list of regular expressions. +This allows you to match multiple optional responses. The expect() +method returns the index of the pattern that was matched. For example, +say you wanted to login to a server. After entering a password you +could get various responses from the server -- your password could be +rejected; or you could be allowed in and asked for your terminal type; +or you could be let right in and given a command prompt. The following +code fragment gives an example of this:

+
+
child.expect('password:')
child.sendline (my_secret_password)
# We expect any of these three patterns...
i = child.expect (['Permission denied', 'Terminal type', '[#\$] '])
if i==0:
print 'Permission denied on host. Can't login'
child.kill(0)
elif i==2:
print 'Login OK... need to send terminal type.'
child.sendline('vt100')
child.expect ('[#\$] ')
elif i==3:
print 'Login OK.'
print 'Shell command prompt', child.after
+
+

If nothing matches an expected pattern then expect will eventually +raise a TIMEOUT exception. The default time is 30 seconds, but you can +change this by passing a timeout argument to expect():

+
+
# Wait no more than 2 minutes (120 seconds) for password prompt.
child.expect('password:', timeout=120)
+
+

Find the end of line -- CR/LF conventions
+Matching at the end of a line can be tricky
+$ regex pattern is useless.
+

+

Pexpect matches regular expressions a little differently than what +you might be used to. +

+

The $ pattern for end of line match is useless. +The $ matches the end of string, but Pexpect reads from the child +one character at a time, so each character looks like the end of a line. +Pexpect can't do a look-ahead into the child's output stream. +In general you would have this situation when using regular expressions +with any stream.
+Note, pexpect does have an internal buffer, so reads are faster +than one character at a time, but from the user's perspective the regex +patterns test happens one character at a time.

+

The best way to match the end of a line is to look for the +newline: "\r\n" (CR/LF). Yes, that does appear to be DOS-style. +It may surprise some UNIX people to learn that terminal TTY device drivers +(dumb, vt100, ANSI, xterm, etc.) all use the CR/LF combination to signify +the end of line. Pexpect uses a Pseudo-TTY device to talk to the child application, so +when the child app prints "\n" you actually see "\r\n". +

+

UNIX uses just linefeeds to end lines of text, but not when it +comes to TTY devices! TTY devices are more like the Windows world. +Each line of text end with a CR/LF combination. When you intercept data +from a UNIX command from a TTY device you will find that the TTY device +outputs a CR/LF combination. A UNIX command may only write a linefeed +(\n), but the TTY device driver converts it to CR/LF. This means that +your terminal will see lines end with CR/LF (hex 0D 0A). +Since Pexpect emulates a terminal, to match ends of lines you have to +expect the CR/LF combination.

+
+

child.expect ('\r\n')

+
+

If you just need to skip past a new line then expect +('\n') by itself will work, but if you are expecting a specific +pattern before the end of line then you need to explicitly look for the +\r. For example the following expects a word at the end of a line:

+
+

child.expect ('\w+\r\n')

+
+

But the following would both fail:

+
+

child.expect ('\w+\n')

+
+

And as explained before, trying to use '$' to match the end of line +would not work either:

+
+

child.expect ('\w+$')

+
+

So if you need to explicitly look for the END OF LINE, you want to +look for the CR/LF combination -- not just the LF and not the $ pattern.

+

This problem is not limited to Pexpect. This problem happens any +time you try to perform a regular expression match on a stream. Regular +expressions need to look ahead. With a stream it is hard to look ahead +because the process generating the stream may not be finished. There is no +way to know if the process has paused momentarily or is finished and +waiting for you. Pexpect must implicitly always +do a NON greedy match (minimal) at the end of a input {### already said +this}.

+

Pexpect compiles all regular expressions with the DOTALL flag. With +the DOTALL flag a "." will match a newline. See the Python documentation

+

Beware of + and * at the end of input.

+

Remember that any time you try to match a pattern that needs +look-ahead that you will always get a minimal match (non greedy). For +example, the following will always return just one character:

+
+

child.expect ('.+')

+
+

This example will match successfully, but will always return no +characters:

+
+

child.expect ('.*')

+
+

Generally any star * expression will match as little as possible

+

One thing you can do is to try to force a non-ambiguous character at +the end of your \d+ pattern. Expect that +character to delimit the string. For example, you might try making the +end of your pattrn be \D+ instead of \D*. That means number digits alone would not +satisfy the (\d+) pattern. You would need +some number(s) and at least one \D at the +end.

+

Matching groups

+

You can group regular expression using parenthesis. After a match, +the match parameter of the spawn object will +contain the Python Match object.

+

Examples

+

Using "match" and groups...

+

Debugging

+

If you get the string value of a pexpect.spawn object you will get +lots of useful debugging information. For debugging it's very useful to +use the following pattern:

+

try:
+    i = child.expect ([pattern1, pattern2, pattern3, +etc])
+except:
+    print "Exception was thrown"
+    print "debug information:"
+    print str(child)
+

+

It is also useful to log the child's input and out to a file or the +screen. The following will turn on logging and send output to stdout +(the screen).
+

+

    child = pexpect.spawn (foo)
+    child.logfile = sys.stdout
+
+

+
+

Exceptions

+

EOF

+

Note that two flavors of EOF Exception may be thrown. They are +virtually identical except for the message string. For practical +purposes you should have no need to distinguish between them, but they +do give a little extra information about what type of platform you are +running. The two messages are:

+
+

End Of File (EOF) in read(). Exception style platform.

+

End Of File (EOF) in read(). Empty string style +platform.

+
+

Some UNIX platforms will throw an exception when you try to read +from a file descriptor in the EOF state. Other UNIX platforms instead +quietly return an empty string to indicate that the EOF state has been +reached.

+

Expecting EOF

+

If you wish to read up to the end of the child's output without +generating an EOF exception then use the expect(pexpect.EOF) method.

+

TIMEOUT

+

The expect() and read() +methods will also timeout if the child does not generate any output for +a given amount of time. If this happens they will raise a TIMEOUT exception. You can have these method +ignore a timeout and block indefinitely by passing None for the timeout +parameter.

+
+

child.expect(pexpect.EOF, timeout=None)

+
+
+

FAQ

+

Q: Why don't shell pipe and redirect (| and >) work when I +spawn a command?

+

+ +A: Remember that Pexpect does NOT interpret shell meta characters such as +redirect, pipe, or wild cards (>, |, or *). That's done by a shell not the +command you are spawning. This is a common mistake. If you want to run a +command and pipe it through another command then you must also start a shell. +For example: + +

+    child = pexpect.spawn('/bin/sh -c "ls -l | grep LOG > log_list.txt"')
+    child.expect(pexpect.EOF)
+
+ +The second form of spawn (where you pass a list of arguments) is useful in +situations where you wish to spawn a command and pass it its own argument list. +This can make syntax more clear. For example, the following is equivalent to +the previous example: + +
+    shell_cmd = 'ls -l | grep LOG > log_list.txt'
+    child = pexpect.spawn ('/bin/sh', ['-c', shell_cmd])
+    child.expect (pexpect.EOF)
+
+ +

+

Q: Isn't there already a Python Expect?

+

A: Yes, there are several of them. They usually require you to +compile C. I wanted something that was pure Python and preferably a +single module that was simple to install. I also wanted something that +was easy to use. This pure Python expect only recently became possible +with the introduction of the pty module in the standard Python library. +Previously C extensions were required.

+ +

Q: The before and after properties sound weird.

+

Originally I was going to model Pexpect more after Expect, but then +I found that I could never remember how to get the context of the stuff +I was trying to parse. I hate having to read my own documentation. I +decided that it was easier for me to remember what before and after +was. It just so happens that this is how the -B and -A options in grep +works, so that made it even easier for me to remember. Whatever makes +my life easier is what's best.

+ +

Q: Why not just use Expect?

+

A: I love it. It's great. I has bailed me out of some real jams, but +I wanted something that would do 90% of what I need from Expect; be 10% +of the size; and allow me to write my code in Python instead of TCL. +Pexpect is not nearly as big as Expect, but Pexpect does everything I +have ever used Expect for. +

+ +

Q: Why not just use a pipe (popen())?

+

A: A pipe works fine for getting the output to non-interactive +programs. If you just want to get the output from ls, +uname, or ping +then this works. Pipes do not work very well for interactive programs +and pipes will almost certainly fail for most applications that ask for +passwords such as telnet, ftp, or ssh.

+

There are two reasons for this.

+

First an application may bypass stdout and print directly to its +controlling TTY. Something like SSH will do this when it asks you for a +password. This is why you cannot redirect the password prompt because +it does not go through stdout or stderr.

+

The second reason is because most applications are built using the C +Standard IO Library (anything that uses #include +<stdio.h>). One of the features of the stdio library is +that it buffers all input and output. Normally output is line +buffered when a program is printing to a TTY (your terminal +screen). Every time the program prints a line-feed the currently +buffered data will get printed to your screen. The problem comes when +you connect a pipe. The stdio library is smart and can tell that it is +printing to a pipe instead of a TTY. In that case it switches from line +buffer mode to block buffered. In this mode the +currently buffered data is flushed when the buffer is full. This causes +most interactive programs to deadlock. Block buffering is more +efficient when writing to disks and pipes. Take the situation where a +program prints a message "Enter your user name:\n" and then waits for +you type type something. In block buffered mode, the stdio library will +not put the message into the pipe even though a linefeed is printed. +The result is that you never receive the message, yet the child +application will sit and wait for you to type a response. Don't confuse +the stdio lib's buffer with the pipe's buffer. The pipe buffer is +another area that can cause problems. You could flush the input side of +a pipe, whereas you have no control over the stdio library buffer.

+

More information: the Standard IO library has three states for a +FILE *. These are: _IOFBF for block buffered; _IOLBF for line buffered; +and _IONBF for unbuffered. The STDIO lib will use block buffering when +talking to a block file descriptor such as a pipe. This is usually not +helpful for interactive programs. Short of recompiling your program to +include fflush() everywhere or recompiling a custom stdio library there +is not much a controlling application can do about this if talking over +a pipe.

+

The program may have put data in its output that remains unflushed +because the output buffer is not full; then the program will go and +deadlock while waiting for input -- because you never send it any +because you are still waiting for its output (still stuck in the +STDIO's output buffer).

+

The answer is to use a pseudo-tty. A TTY device will force line +buffering (as opposed to block buffering). Line buffering means that +you will get each line when the child program sends a line feed. This +corresponds to the way most interactive programs operate -- send a line +of output then wait for a line of input.

+

I put "answer" in quotes because it's ugly solution and because +there is no POSIX standard for pseudo-TTY devices (even though they +have a TTY standard...). What would make more sense to me would be to +have some way to set a mode on a file descriptor so that it will tell +the STDIO to be line-buffered. I have investigated, and I don't think +there is a way to set the buffered state of a child process. The STDIO +Library does not maintain any external state in the kernel or whatnot, +so I don't think there is any way for you to alter it. I'm not quite +sure how this line-buffered/block-buffered state change happens +internally in the STDIO library. I think the STDIO lib looks at the +file descriptor and decides to change behavior based on whether it's a +TTY or a block file (see isatty()).

+

I hope that this qualifies as helpful.

+ +

Don't use a pipe to control another application...

+

Pexpect may seem similar to os.popen() or +commands module. The main difference is that +Pexpect (like Expect) uses a pseudo-TTY to talk to the child +application. Most applications do no work well through the system() +call or through pipes. And probably all applications that ask a user to +type in a password will fail. These applications bypass the stdin and +read directly from the TTY device. Many applications do not explicitly +flush their output buffers. This causes deadlocks if you try to control +an interactive application using a pipe. What happens is that most UNIX +applications use the stdio (#include <stdio.h>) for input and +output. The stdio library behaves differently depending on where the +output is going. There is no way to control this behavior from the +client end.
+

+ +

Q: Can I do screen scraping with this thing?

+

A: That depends. If your application just does line-oriented output +then this is easy. If it does screen-oriented output then it may work, +but it could be hard. For example, trying to scrape data from the 'top' +command would be hard. The top command repaints the text window.

+

I am working on an ANSI / VT100 terminal emulator that will have +methods to get characters from an arbitrary X,Y coordinate of the +virtual screen. It works and you can play with it, but I have no +working examples at this time.

+
+

Bugs

+

Threads

+

On Linux (RH 8) you cannot spawn a child from a different thread and +pass the handle back to a worker thread. The child is successfully +spawned but you can't interact with it. The only way to make it work is +to spawn and interact with the child all in the same thread. [Adam +Kerrison]

+

Timing issue with send() and sendline()

+

This problem has been addressed and should not effect most users.

+

It is sometimes possible to read an echo of the string sent with send() and sendline(). +If you call sendline() and then immediately +call readline() you may get part of your +output echoed back. You may read back what you just wrote even if the +child application does not explicitly echo it. Timing is critical. This +could be a security issue when talking to an application that asks for +a password; otherwise, this does not seem like a big deal. But why +do TTYs do this?

+

People usually report this when they are trying to control SSH or +some other login. For example, if your code looks something like this:

+
child.expect ('[pP]assword:')
child.sendline (my_password)
+


+

+1. SSH prints "password:" prompt to the user.
+2. SSH turns off echo on the TTY device.
+3. SSH waits for user to enter a password.
+
+When scripting with Pexpect what can happen is that Pexpect will response to the "password:" prompt +before SSH has had time to turn off TTY echo. In other words, Pexpect sends the password between +steps 1. and 2., so the password gets echoed back to the TTY. I would call this an SSH bug. +

+

+Pexpect now automatically adds a short delay before sending data to a child process. +This more closely mimics what happens in the usual human-to-app interaction. +The delay can be tuned with the 'delaybeforesend' attribute of the spawn class. +In general, this fixes the problem for everyone and so this should not be an issue +for most users. For some applications you might with to turn it off. + child = pexpect.spawn ("ssh user@example.com") + child.delaybeforesend = 0 +

+


+

+

Try changing it to look like the following. I know that this fix +does not look correct, but it works. I have not figured out exactly +what is happening. You would think that the sleep should be after the +sendline(). The fact that the sleep helps when it's between the +expect() and the sendline() must be a clue.

+
child.expect ('[pP]assword:')
child.sendline (my_password)
+

Timing issue with isalive()

+

Reading the state of isalive() immediately after a child exits may +sometimes return 1. This is a race condition. The child has closed its +file descriptor, but has not yet fully exited before Pexpect's +isalive() executes. Addings a slight delay before the isalive() will +help. In the following example isalive() +sometimes returns 1:

+
+
child = pexpect.spawn('ls')
child.expect(pexpect.EOF)
print child.isalive()
+
+

But if there is any delay before the call to isalive() +then it will always return 0 as expected.

+
+
child = pexpect.spawn('ls')
child.expect(pexpect.EOF)
time.sleep(0.1)
print child.isalive()
+
+ +

Truncated output just before child exits

+

So far I have seen this only on older versions of Apple's MacOS X. +If the child application quits it may not flush its output buffer. This +means that your Pexpect application will receive an EOF even though it +should have received a little more data before the child died. This is +not generally a problem when talking to interactive child applications. +One example where it is a problem is when trying to read output from a +program like 'ls'. You may receive most of +the directory listing, but the last few lines will get lost before you +receive an EOF. The reason for this is that 'ls' +runs; completes its task; and then exits. The buffer is not flushed +before exit so the last few lines are lost. The following example +demonstrates the problem:

+

+
+
child = pexpect.spawn ('ls -l')
child.expect (pexpect.EOF)
print child.before
+
+

+ +

Controlling SSH on Solaris

+

Pexpect does not yet work perfectly on Solaris. +One common problem is that SSH sometimes will not allow TTY password +authentication. For example, you may expect SSH to ask you for a +password using code like this: +

+
child = pexpect.spawn ('ssh user@example.com')
child.expect ('assword')
child.sendline ('mypassword')
+You may see the following error come back from a spawned +child SSH: +

+
Permission denied (publickey,keyboard-interactive).
+

+This means that SSH thinks it can't access the TTY to ask you for your +password. +The only solution I have found is to use public key authentication with +SSH. +This bypasses the need for a password. I'm not happy with this +solution. +The problem is due to poor support for Solaris Pseudo TTYs in the +Python +Standard Library.

+
+

CHANGES

+

Current Release

+

Fixed OSError exception when a pexpect object is cleaned up. +Previously you might have seen this exception:

+
+
Exception exceptions.OSError: (10, 'No child processes') 
in <bound method spawn.__del__ of
<pexpect.spawn instance at 0xd248c>> ignored
+
+

You should not see that anymore. Thanks to Michael Surette.

+

Added support for buffering reads. This greatly improves speed when +trying to match long output from a child process. When you create an +instance of the spawn object you can then set a buffer size. For now +you MUST do the following to turn on buffering -- it may be on by +default in future version.

+
+
child = pexpect.spawn ('my_command')
child.maxread=1000 # Sets buffer to 1000 characters.
+
+
+

I made a subtle change to the way TIMEOUT and EOF exceptions behave. +Previously you could either expect these states in which case pexpect +will not raise an exception, or you could just let pexpect raise an +exception when these states were encountered. If you expected the +states then the 'before' property was set to everything before the +state was encountered, but if you let pexpect raise the exception then +'before' was not set. Now the 'before' property will get set either way +you choose to handle these states.

+

Older changes...

+

The spawn object now provides iterators for a file-like interface. +This makes Pexpect a more complete file-like object. You can now write +code like this:

+
+
child = pexpect.spawn ('ls -l')
for line in child:
print line
+
+

I added the attribute exitstatus. This +will give the exit code returned by the child process. This will be set +to None while the child is still alive. When +isalive() returns 0 then exitstatus +will be set.

+

I made a few more tweaks to isalive() so +that it will operate more consistently on different platforms. Solaris +is the most difficult to support.

+

 

+

You can now put TIMEOUT in a list of +expected patterns. This is just like putting EOF +in the pattern list. Expecting for a TIMEOUT +may not be used as often as EOF, but this +makes Pexpect more consitent.

+

Thanks to a suggestion and sample code from Chad J. Schroeder I +added the ability for Pexpect to operate on a file descriptor that is +already open. This means that Pexpect can be used to control streams +such as those from serial port devices. Now you just pass the integer +file descriptor as the "command" when contsructing a spawn open. For +example on a Linux box with a modem on ttyS1:

+
+
fd = os.open("/dev/ttyS1", os.O_RDWR|os.O_NONBLOCK|os.O_NOCTTY)
m = pexpect.spawn(fd) # Note integer fd is used instead of usual string.
m.send("+++") # Escape sequence
m.send("ATZ0\r") # Reset modem to profile 0
rval = m.expect(["OK", "ERROR"])
+
+

Pexpect now tests itself on Compile Farm!

+

I wrote a nice script that uses ssh to connect to each machine on +Source Forge's Compile Farm and then run the testall.py script for each +platform. The result of the test is then recorded for each platform. +Now it's easy to run regression tests across multiple platforms.

+

Pexpect is a file-like object

+

The spawn object now provides a file-like interface. It +supports most of the methods and attributes defined for Python File +Objects.

+

I changed write and writelines() so that they no longer return a +value. Use send() if you need that functionality. I did this to make +the Spawn object more closely match a file-like object.

+

read() was renamed to read_nonblocking(). I added a new read() +method that matches file-like object interface. In general, you should +not notice the difference except that read() no longer allows you to +directly set the timeout value. I hope this will not effect any +existing code. Switching to read_nonblocking() should fix existing code.

+

I changed the name of set_echo() to setecho().

+

I changed the name of send_eof() to sendeof().

+

I modified kill() so that it checks to +make sure the pid isalive().

+

I modified spawn() (really called from __spawn())so that it does not raise an expection +if setwinsize() fails. Some platforms such +as Cygwin do not like setwinsize. This was a constant problem and since +it is not a critical feature I decided to just silence the error. +Normally I don't like to do that, but in this case I'm making an +exception.

+

Added a method close() that does what you +think. It closes the file descriptor of the child application. It makes +no attempt to actually kill the child or wait for its status.

+

Add variables __version__ and __revision__ (from cvs) to the pexpect modules. +This is mainly helpful to me so that I can make sure that I'm testing +with the right version instead of one already installed.

+

Logging changes

+
+

log_open() and log_close() +have been removed. Now use setlog(). The setlog() method takes a file object. This is far +more flexible than the previous log method. Each time data is written +to the file object it will be flushed. To turn logging off simply call setlog() with None.

+
+

isalive changes

+
+

I renamed the isAlive() method to isalive() to match the more typical naming style +in Python. Also the technique used to detect child process status has +been drastically modified. Previously I did some funky stuff with +signals which caused indigestion in other Python modules on some +platforms. It's was a big headache. It still is, but I think it works +better now.

+
+

attribute name changes

+
+

The names of some attributes have been changed. This effects the +names of the attributes that are set after called the expect() method.

+ + + + + + + + + + + + + + + + + + + +
NEW NAMEOLD NAME
before
+ Everything before the match.
before
after
+ Everything after and including the first character of the +match
matched
match
+ This is the re MatchObject from the match.
+You can get groups() from this.
+See 'uptime.py' in the examples tar ball.
New -- Did not exist
+
+

EOF changes

+
+

The expect_eof() method is gone. You +can now simply use the expect() method to +look for EOF.

+

Was:

+
+

p.expect_eof ()

+
+

Now:

+
+

p.expect (pexpect.EOF)

+
+
+
+

TESTING

+

The following platforms have been tested:

+ +

 

+

TO DO

+

Add an option to add a delay after each expect() or before each +read()/readline() call to automatically avoid the echo +bug.

+

 

+
+
+ + + + + + +
Click to send email.
+
+ + + diff --git a/third_party/Python/module/pexpect-2.4/examples/README b/third_party/Python/module/pexpect-2.4/examples/README new file mode 100644 index 000000000000..8f2581e05e7c --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/README @@ -0,0 +1,72 @@ +This directory contains scripts that give examples of using Pexpect. + +hive.py + This script creates SSH connections to a list of hosts that + you provide. Then you are given a command line prompt. Each + shell command that you enter is sent to all the hosts. The + response from each host is collected and printed. For example, + you could connect to a dozen different machines and reboot + them all at once. + +script.py + This implements a command similar to the classic BSD "script" command. + This will start a subshell and log all input and output to a file. + This demonstrates the interact() method of Pexpect. + +fix_cvs_files.py + This is for cleaning up binary files improperly added to + CVS. This script scans the given path to find binary files; + checks with CVS to see if the sticky options are set to -kb; + finally if sticky options are not -kb then uses 'cvs admin' + to set the -kb option. + +ftp.py + This demonstrates an FTP "bookmark". + This connects to an ftp site; does a few ftp commands; and then gives the user + interactive control over the session. In this case the "bookmark" is to a + directory on the OpenBSD ftp server. It puts you in the i386 packages + directory. You can easily modify this for other sites. + This demonstrates the interact() method of Pexpect. + +monitor.py + This runs a sequence of system status commands on a remote host using SSH. + It runs a simple system checks such as uptime and free to monitor + the state of the remote host. + +passmass.py + This will login to a list of hosts and change the password of the + given user. This demonstrates scripting logins; although, you could + more easily do this using the pxssh subclass of Pexpect. + See also the "hive.py" example script for a more general example + of scripting a collection of servers. + +python.py + This starts the python interpreter and prints the greeting message backwards. + It then gives the user interactive control of Python. It's pretty useless! + +rippy.py + This is a wizard for mencoder. It greatly simplifies the process of + ripping a DVD to mpeg4 format (XviD, DivX). It can transcode from any + video file to another. It has options for resampling the audio stream; + removing interlace artifacts, fitting to a target file size, etc. + There are lots of options, but the process is simple and easy to use. + +sshls.py + This lists a directory on a remote machine. + +ssh_tunnel.py + This starts an SSH tunnel to a remote machine. It monitors the connection + and restarts the tunnel if it goes down. + +uptime.py + This will run the uptime command and parse the output into python variables. + This demonstrates using a single regular expression to match the output + of a command and capturing different variable in match groups. + The regular expression takes into account a wide variety of different + formats for uptime output. + +df.py + This collects filesystem capacity info using the 'df' command. + Tuples of filesystem name and percentage are stored in a list. + A simple report is printed. Filesystems over 95% capacity are highlighted. + diff --git a/third_party/Python/module/pexpect-2.4/examples/astat.py b/third_party/Python/module/pexpect-2.4/examples/astat.py new file mode 100644 index 000000000000..82fa3c68b705 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/astat.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +"""This runs Apache Status on the remote host and returns the number of requests per second. + +./astat.py [-s server_hostname] [-u username] [-p password] + -s : hostname of the remote server to login to. + -u : username to user for login. + -p : Password to user for login. + +Example: + This will print information about the given host: + ./astat.py -s www.example.com -u mylogin -p mypassword + +""" + +import os, sys, time, re, getopt, getpass +import traceback +import pexpect, pxssh + +def exit_with_usage(): + + print globals()['__doc__'] + os._exit(1) + +def main(): + + ###################################################################### + ## Parse the options, arguments, get ready, etc. + ###################################################################### + try: + optlist, args = getopt.getopt(sys.argv[1:], 'h?s:u:p:', ['help','h','?']) + except Exception, e: + print str(e) + exit_with_usage() + options = dict(optlist) + if len(args) > 1: + exit_with_usage() + + if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]: + print "Help:" + exit_with_usage() + + if '-s' in options: + hostname = options['-s'] + else: + hostname = raw_input('hostname: ') + if '-u' in options: + username = options['-u'] + else: + username = raw_input('username: ') + if '-p' in options: + password = options['-p'] + else: + password = getpass.getpass('password: ') + + # + # Login via SSH + # + p = pxssh.pxssh() + p.login(hostname, username, password) + p.sendline('apachectl status') + p.expect('([0-9]+\.[0-9]+)\s*requests/sec') + requests_per_second = p.match.groups()[0] + p.logout() + print requests_per_second + +if __name__ == "__main__": + try: + main() + except Exception, e: + print str(e) + traceback.print_exc() + os._exit(1) + diff --git a/third_party/Python/module/pexpect-2.4/examples/bd_client.py b/third_party/Python/module/pexpect-2.4/examples/bd_client.py new file mode 100644 index 000000000000..564739a0aad5 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/bd_client.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +"""This is a very simple client for the backdoor daemon. This is intended more +for testing rather than normal use. See bd_serv.py """ + +import socket +import sys, time, select + +def recv_wrapper(s): + r,w,e = select.select([s.fileno()],[],[], 2) + if not r: + return '' + #cols = int(s.recv(4)) + #rows = int(s.recv(4)) + cols = 80 + rows = 24 + packet_size = cols * rows * 2 # double it for good measure + return s.recv(packet_size) + +#HOST = '' #'localhost' # The remote host +#PORT = 1664 # The same port as used by the server +s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +s.connect(sys.argv[1])#(HOST, PORT)) +time.sleep(1) +#s.setblocking(0) +#s.send('COMMAND' + '\x01' + sys.argv[1]) +s.send(':sendline ' + sys.argv[2]) +print recv_wrapper(s) +s.close() +sys.exit() +#while True: +# data = recv_wrapper(s) +# if data == '': +# break +# sys.stdout.write (data) +# sys.stdout.flush() +#s.close() + diff --git a/third_party/Python/module/pexpect-2.4/examples/bd_serv.py b/third_party/Python/module/pexpect-2.4/examples/bd_serv.py new file mode 100644 index 000000000000..b7def9e14022 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/bd_serv.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python + +"""Back door shell server + +This exposes an shell terminal on a socket. + + --hostname : sets the remote host name to open an ssh connection to. + --username : sets the user name to login with + --password : (optional) sets the password to login with + --port : set the local port for the server to listen on + --watch : show the virtual screen after each client request +""" + +# Having the password on the command line is not a good idea, but +# then this entire project is probably not the most security concious thing +# I've ever built. This should be considered an experimental tool -- at best. +import pxssh, pexpect, ANSI +import time, sys, os, getopt, getpass, traceback, threading, socket + +def exit_with_usage(exit_code=1): + + print globals()['__doc__'] + os._exit(exit_code) + +class roller (threading.Thread): + + """This runs a function in a loop in a thread.""" + + def __init__(self, interval, function, args=[], kwargs={}): + + """The interval parameter defines time between each call to the function. + """ + + threading.Thread.__init__(self) + self.interval = interval + self.function = function + self.args = args + self.kwargs = kwargs + self.finished = threading.Event() + + def cancel(self): + + """Stop the roller.""" + + self.finished.set() + + def run(self): + + while not self.finished.isSet(): + # self.finished.wait(self.interval) + self.function(*self.args, **self.kwargs) + +def endless_poll (child, prompt, screen, refresh_timeout=0.1): + + """This keeps the screen updated with the output of the child. This runs in + a separate thread. See roller(). """ + + #child.logfile_read = screen + try: + s = child.read_nonblocking(4000, 0.1) + screen.write(s) + except: + pass + #while True: + # #child.prompt (timeout=refresh_timeout) + # try: + # #child.read_nonblocking(1,timeout=refresh_timeout) + # child.read_nonblocking(4000, 0.1) + # except: + # pass + +def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): + + '''This forks the current process into a daemon. Almost none of this is + necessary (or advisable) if your daemon is being started by inetd. In that + case, stdin, stdout and stderr are all set up for you to refer to the + network connection, and the fork()s and session manipulation should not be + done (to avoid confusing inetd). Only the chdir() and umask() steps remain + as useful. + + References: + UNIX Programming FAQ + 1.7 How do I get my program to act like a daemon? + http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 + + Advanced Programming in the Unix Environment + W. Richard Stevens, 1992, Addison-Wesley, ISBN 0-201-56317-7. + + The stdin, stdout, and stderr arguments are file names that will be opened + and be used to replace the standard file descriptors in sys.stdin, + sys.stdout, and sys.stderr. These arguments are optional and default to + /dev/null. Note that stderr is opened unbuffered, so if it shares a file + with stdout then interleaved output may not appear in the order that you + expect. ''' + + # Do first fork. + try: + pid = os.fork() + if pid > 0: + sys.exit(0) # Exit first parent. + except OSError, e: + sys.stderr.write ("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror) ) + sys.exit(1) + + # Decouple from parent environment. + os.chdir("/") + os.umask(0) + os.setsid() + + # Do second fork. + try: + pid = os.fork() + if pid > 0: + sys.exit(0) # Exit second parent. + except OSError, e: + sys.stderr.write ("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror) ) + sys.exit(1) + + # Now I am a daemon! + + # Redirect standard file descriptors. + si = open(stdin, 'r') + so = open(stdout, 'a+') + se = open(stderr, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + # I now return as the daemon + return 0 + +def add_cursor_blink (response, row, col): + + i = (row-1) * 80 + col + return response[:i]+''+response[i:] + +def main (): + + try: + optlist, args = getopt.getopt(sys.argv[1:], 'h?d', ['help','h','?', 'hostname=', 'username=', 'password=', 'port=', 'watch']) + except Exception, e: + print str(e) + exit_with_usage() + + command_line_options = dict(optlist) + options = dict(optlist) + # There are a million ways to cry for help. These are but a few of them. + if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]: + exit_with_usage(0) + + hostname = "127.0.0.1" + port = 1664 + username = os.getenv('USER') + password = "" + daemon_mode = False + if '-d' in options: + daemon_mode = True + if '--watch' in options: + watch_mode = True + else: + watch_mode = False + if '--hostname' in options: + hostname = options['--hostname'] + if '--port' in options: + port = int(options['--port']) + if '--username' in options: + username = options['--username'] + print "Login for %s@%s:%s" % (username, hostname, port) + if '--password' in options: + password = options['--password'] + else: + password = getpass.getpass('password: ') + + if daemon_mode: + print "daemonizing server" + daemonize() + #daemonize('/dev/null','/tmp/daemon.log','/tmp/daemon.log') + + sys.stdout.write ('server started with pid %d\n' % os.getpid() ) + + virtual_screen = ANSI.ANSI (24,80) + child = pxssh.pxssh() + child.login (hostname, username, password) + print 'created shell. command line prompt is', child.PROMPT + #child.sendline ('stty -echo') + #child.setecho(False) + virtual_screen.write (child.before) + virtual_screen.write (child.after) + + if os.path.exists("/tmp/mysock"): os.remove("/tmp/mysock") + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + localhost = '127.0.0.1' + s.bind('/tmp/mysock') + os.chmod('/tmp/mysock',0777) + print 'Listen' + s.listen(1) + print 'Accept' + #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + #localhost = '127.0.0.1' + #s.bind((localhost, port)) + #print 'Listen' + #s.listen(1) + + r = roller (0.01, endless_poll, (child, child.PROMPT, virtual_screen)) + r.start() + print "screen poll updater started in background thread" + sys.stdout.flush() + + try: + while True: + conn, addr = s.accept() + print 'Connected by', addr + data = conn.recv(1024) + if data[0]!=':': + cmd = ':sendline' + arg = data.strip() + else: + request = data.split(' ', 1) + if len(request)>1: + cmd = request[0].strip() + arg = request[1].strip() + else: + cmd = request[0].strip() + if cmd == ':exit': + r.cancel() + break + elif cmd == ':sendline': + child.sendline (arg) + #child.prompt(timeout=2) + time.sleep(0.2) + shell_window = str(virtual_screen) + elif cmd == ':send' or cmd==':xsend': + if cmd==':xsend': + arg = arg.decode("hex") + child.send (arg) + time.sleep(0.2) + shell_window = str(virtual_screen) + elif cmd == ':cursor': + shell_window = '%x%x' % (virtual_screen.cur_r, virtual_screen.cur_c) + elif cmd == ':refresh': + shell_window = str(virtual_screen) + + response = [] + response.append (shell_window) + #response = add_cursor_blink (response, row, col) + sent = conn.send('\n'.join(response)) + if watch_mode: print '\n'.join(response) + if sent < len (response): + print "Sent is too short. Some data was cut off." + conn.close() + finally: + r.cancel() + print "cleaning up socket" + s.close() + if os.path.exists("/tmp/mysock"): os.remove("/tmp/mysock") + print "done!" + +def pretty_box (rows, cols, s): + + """This puts an ASCII text box around the given string, s. + """ + + top_bot = '+' + '-'*cols + '+\n' + return top_bot + '\n'.join(['|'+line+'|' for line in s.split('\n')]) + '\n' + top_bot + +def error_response (msg): + + response = [] + response.append ("""All commands start with : +:{REQUEST} {ARGUMENT} +{REQUEST} may be one of the following: + :sendline: Run the ARGUMENT followed by a line feed. + :send : send the characters in the ARGUMENT without a line feed. + :refresh : Use to catch up the screen with the shell if state gets out of sync. +Example: + :sendline ls -l +You may also leave off :command and it will be assumed. +Example: + ls -l +is equivalent to: + :sendline ls -l +""") + response.append (msg) + return '\n'.join(response) + +def parse_host_connect_string (hcs): + + """This parses a host connection string in the form + username:password@hostname:port. All fields are options expcet hostname. A + dictionary is returned with all four keys. Keys that were not included are + set to empty strings ''. Note that if your password has the '@' character + then you must backslash escape it. """ + + if '@' in hcs: + p = re.compile (r'(?P[^@:]*)(:?)(?P.*)(?!\\)@(?P[^:]*):?(?P[0-9]*)') + else: + p = re.compile (r'(?P)(?P)(?P[^:]*):?(?P[0-9]*)') + m = p.search (hcs) + d = m.groupdict() + d['password'] = d['password'].replace('\\@','@') + return d + +if __name__ == "__main__": + + try: + start_time = time.time() + print time.asctime() + main() + print time.asctime() + print "TOTAL TIME IN MINUTES:", + print (time.time() - start_time) / 60.0 + except Exception, e: + print str(e) + tb_dump = traceback.format_exc() + print str(tb_dump) + diff --git a/third_party/Python/module/pexpect-2.4/examples/cgishell.cgi b/third_party/Python/module/pexpect-2.4/examples/cgishell.cgi new file mode 100644 index 000000000000..1e3affc1cab6 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/cgishell.cgi @@ -0,0 +1,762 @@ +#!/usr/bin/python +##!/usr/bin/env python +"""CGI shell server + +This exposes a shell terminal on a web page. +It uses AJAX to send keys and receive screen updates. +The client web browser needs nothing but CSS and Javascript. + + --hostname : sets the remote host name to open an ssh connection to. + --username : sets the user name to login with + --password : (optional) sets the password to login with + --port : set the local port for the server to listen on + --watch : show the virtual screen after each client request + +This project is probably not the most security concious thing I've ever built. +This should be considered an experimental tool -- at best. +""" +import sys,os +sys.path.insert (0,os.getcwd()) # let local modules precede any installed modules +import socket, random, string, traceback, cgi, time, getopt, getpass, threading, resource, signal +import pxssh, pexpect, ANSI + +def exit_with_usage(exit_code=1): + print globals()['__doc__'] + os._exit(exit_code) + +def client (command, host='localhost', port=-1): + """This sends a request to the server and returns the response. + If port <= 0 then host is assumed to be the filename of a Unix domain socket. + If port > 0 then host is an inet hostname. + """ + if port <= 0: + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(host) + else: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, port)) + s.send(command) + data = s.recv (2500) + s.close() + return data + +def server (hostname, username, password, socket_filename='/tmp/server_sock', daemon_mode = True, verbose=False): + """This starts and services requests from a client. + If daemon_mode is True then this forks off a separate daemon process and returns the daemon's pid. + If daemon_mode is False then this does not return until the server is done. + """ + if daemon_mode: + mypid_name = '/tmp/%d.pid' % os.getpid() + daemon_pid = daemonize(daemon_pid_filename=mypid_name) + time.sleep(1) + if daemon_pid != 0: + os.unlink(mypid_name) + return daemon_pid + + virtual_screen = ANSI.ANSI (24,80) + child = pxssh.pxssh() + try: + child.login (hostname, username, password, login_naked=True) + except: + return + if verbose: print 'login OK' + virtual_screen.write (child.before) + virtual_screen.write (child.after) + + if os.path.exists(socket_filename): os.remove(socket_filename) + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.bind(socket_filename) + os.chmod(socket_filename, 0777) + if verbose: print 'Listen' + s.listen(1) + + r = roller (endless_poll, (child, child.PROMPT, virtual_screen)) + r.start() + if verbose: print "started screen-poll-updater in background thread" + sys.stdout.flush() + try: + while True: + conn, addr = s.accept() + if verbose: print 'Connected by', addr + data = conn.recv(1024) + request = data.split(' ', 1) + if len(request)>1: + cmd = request[0].strip() + arg = request[1].strip() + else: + cmd = request[0].strip() + arg = '' + + if cmd == 'exit': + r.cancel() + break + elif cmd == 'sendline': + child.sendline (arg) + time.sleep(0.1) + shell_window = str(virtual_screen) + elif cmd == 'send' or cmd=='xsend': + if cmd=='xsend': + arg = arg.decode("hex") + child.send (arg) + time.sleep(0.1) + shell_window = str(virtual_screen) + elif cmd == 'cursor': + shell_window = '%x,%x' % (virtual_screen.cur_r, virtual_screen.cur_c) + elif cmd == 'refresh': + shell_window = str(virtual_screen) + elif cmd == 'hash': + shell_window = str(hash(str(virtual_screen))) + + response = [] + response.append (shell_window) + if verbose: print '\n'.join(response) + sent = conn.send('\n'.join(response)) + if sent < len (response): + if verbose: print "Sent is too short. Some data was cut off." + conn.close() + except e: + pass + r.cancel() + if verbose: print "cleaning up socket" + s.close() + if os.path.exists(socket_filename): os.remove(socket_filename) + if verbose: print "server done!" + +class roller (threading.Thread): + """This class continuously loops a function in a thread. + This is basically a thin layer around Thread with a + while loop and a cancel. + """ + def __init__(self, function, args=[], kwargs={}): + threading.Thread.__init__(self) + self.function = function + self.args = args + self.kwargs = kwargs + self.finished = threading.Event() + def cancel(self): + """Stop the roller.""" + self.finished.set() + def run(self): + while not self.finished.isSet(): + self.function(*self.args, **self.kwargs) + +def endless_poll (child, prompt, screen, refresh_timeout=0.1): + """This keeps the screen updated with the output of the child. + This will be run in a separate thread. See roller class. + """ + #child.logfile_read = screen + try: + s = child.read_nonblocking(4000, 0.1) + screen.write(s) + except: + pass + +def daemonize (stdin=None, stdout=None, stderr=None, daemon_pid_filename=None): + """This runs the current process in the background as a daemon. + The arguments stdin, stdout, stderr allow you to set the filename that the daemon reads and writes to. + If they are set to None then all stdio for the daemon will be directed to /dev/null. + If daemon_pid_filename is set then the pid of the daemon will be written to it as plain text + and the pid will be returned. If daemon_pid_filename is None then this will return None. + """ + UMASK = 0 + WORKINGDIR = "/" + MAXFD = 1024 + + # The stdio file descriptors are redirected to /dev/null by default. + if hasattr(os, "devnull"): + DEVNULL = os.devnull + else: + DEVNULL = "/dev/null" + if stdin is None: stdin = DEVNULL + if stdout is None: stdout = DEVNULL + if stderr is None: stderr = DEVNULL + + try: + pid = os.fork() + except OSError, e: + raise Exception, "%s [%d]" % (e.strerror, e.errno) + + if pid != 0: # The first child. + os.waitpid(pid,0) + if daemon_pid_filename is not None: + daemon_pid = int(file(daemon_pid_filename,'r').read()) + return daemon_pid + else: + return None + + # first child + os.setsid() + signal.signal(signal.SIGHUP, signal.SIG_IGN) + + try: + pid = os.fork() # fork second child + except OSError, e: + raise Exception, "%s [%d]" % (e.strerror, e.errno) + + if pid != 0: + if daemon_pid_filename is not None: + file(daemon_pid_filename,'w').write(str(pid)) + os._exit(0) # exit parent (the first child) of the second child. + + # second child + os.chdir(WORKINGDIR) + os.umask(UMASK) + + maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] + if maxfd == resource.RLIM_INFINITY: + maxfd = MAXFD + + # close all file descriptors + for fd in xrange(0, maxfd): + try: + os.close(fd) + except OSError: # fd wasn't open to begin with (ignored) + pass + + os.open (DEVNULL, os.O_RDWR) # standard input + + # redirect standard file descriptors + si = open(stdin, 'r') + so = open(stdout, 'a+') + se = open(stderr, 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + return 0 + +def client_cgi (): + """This handles the request if this script was called as a cgi. + """ + sys.stderr = sys.stdout + ajax_mode = False + TITLE="Shell" + SHELL_OUTPUT="" + SID="NOT" + print "Content-type: text/html;charset=utf-8\r\n" + try: + form = cgi.FieldStorage() + if form.has_key('ajax'): + ajax_mode = True + ajax_cmd = form['ajax'].value + SID=form['sid'].value + if ajax_cmd == 'send': + command = 'xsend' + arg = form['arg'].value.encode('hex') + result = client (command + ' ' + arg, '/tmp/'+SID) + print result + elif ajax_cmd == 'refresh': + command = 'refresh' + result = client (command, '/tmp/'+SID) + print result + elif ajax_cmd == 'cursor': + command = 'cursor' + result = client (command, '/tmp/'+SID) + print result + elif ajax_cmd == 'exit': + command = 'exit' + result = client (command, '/tmp/'+SID) + print result + elif ajax_cmd == 'hash': + command = 'hash' + result = client (command, '/tmp/'+SID) + print result + elif not form.has_key('sid'): + SID=random_sid() + print LOGIN_HTML % locals(); + else: + SID=form['sid'].value + if form.has_key('start_server'): + USERNAME = form['username'].value + PASSWORD = form['password'].value + dpid = server ('127.0.0.1', USERNAME, PASSWORD, '/tmp/'+SID) + SHELL_OUTPUT="daemon pid: " + str(dpid) + else: + if form.has_key('cli'): + command = 'sendline ' + form['cli'].value + else: + command = 'sendline' + SHELL_OUTPUT = client (command, '/tmp/'+SID) + print CGISH_HTML % locals() + except: + tb_dump = traceback.format_exc() + if ajax_mode: + print str(tb_dump) + else: + SHELL_OUTPUT=str(tb_dump) + print CGISH_HTML % locals() + +def server_cli(): + """This is the command line interface to starting the server. + This handles things if the script was not called as a CGI + (if you run it from the command line). + """ + try: + optlist, args = getopt.getopt(sys.argv[1:], 'h?d', ['help','h','?', 'hostname=', 'username=', 'password=', 'port=', 'watch']) + except Exception, e: + print str(e) + exit_with_usage() + + command_line_options = dict(optlist) + options = dict(optlist) + # There are a million ways to cry for help. These are but a few of them. + if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]: + exit_with_usage(0) + + hostname = "127.0.0.1" + #port = 1664 + username = os.getenv('USER') + password = "" + daemon_mode = False + if '-d' in options: + daemon_mode = True + if '--watch' in options: + watch_mode = True + else: + watch_mode = False + if '--hostname' in options: + hostname = options['--hostname'] + if '--port' in options: + port = int(options['--port']) + if '--username' in options: + username = options['--username'] + if '--password' in options: + password = options['--password'] + else: + password = getpass.getpass('password: ') + + server (hostname, username, password, '/tmp/mysock', daemon_mode) + +def random_sid (): + a=random.randint(0,65535) + b=random.randint(0,65535) + return '%04x%04x.sid' % (a,b) + +def parse_host_connect_string (hcs): + """This parses a host connection string in the form + username:password@hostname:port. All fields are options expcet hostname. A + dictionary is returned with all four keys. Keys that were not included are + set to empty strings ''. Note that if your password has the '@' character + then you must backslash escape it. + """ + if '@' in hcs: + p = re.compile (r'(?P[^@:]*)(:?)(?P.*)(?!\\)@(?P[^:]*):?(?P[0-9]*)') + else: + p = re.compile (r'(?P)(?P)(?P[^:]*):?(?P[0-9]*)') + m = p.search (hcs) + d = m.groupdict() + d['password'] = d['password'].replace('\\@','@') + return d + +def pretty_box (s, rows=24, cols=80): + """This puts an ASCII text box around the given string. + """ + top_bot = '+' + '-'*cols + '+\n' + return top_bot + '\n'.join(['|'+line+'|' for line in s.split('\n')]) + '\n' + top_bot + +def main (): + if os.getenv('REQUEST_METHOD') is None: + server_cli() + else: + client_cgi() + +# It's mostly HTML and Javascript from here on out. +CGISH_HTML=""" + + +%(TITLE)s %(SID)s + + + + + + + + +
+ + +

+ + + + +
+ + + + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ +
+
+ + +""" + +LOGIN_HTML=""" + +Shell Login + + + + + +
+ + +username:
+password:
+ +
+
+ + +""" + +if __name__ == "__main__": + try: + main() + except Exception, e: + print str(e) + tb_dump = traceback.format_exc() + print str(tb_dump) + diff --git a/third_party/Python/module/pexpect-2.4/examples/chess.py b/third_party/Python/module/pexpect-2.4/examples/chess.py new file mode 100644 index 000000000000..8c32cf798f2a --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/chess.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +'''This demonstrates controlling a screen oriented application (curses). +It starts two instances of gnuchess and then pits them against each other. +''' + +import pexpect +import string +import ANSI + +REGEX_MOVE = '(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)' +REGEX_MOVE_PART = '(?:[0-9]|\x1b\[C)(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)' + +class Chess: + + def __init__(self, engine = "/usr/local/bin/gnuchess -a -h 1"): + self.child = pexpect.spawn (engine) + self.term = ANSI.ANSI () + + self.child.expect ('Chess') + if self.child.after != 'Chess': + raise IOError, 'incompatible chess program' + self.term.process_list (self.before) + self.term.process_list (self.after) + self.last_computer_move = '' + def read_until_cursor (self, r,c) + while 1: + self.child.read(1, 60) + self.term.process (c) + if self.term.cur_r == r and self.term.cur_c == c: + return 1 + + def do_first_move (self, move): + self.child.expect ('Your move is') + self.child.sendline (move) + self.term.process_list (self.before) + self.term.process_list (self.after) + return move + + def do_move (self, move): + read_until_cursor (19,60) + #self.child.expect ('\[19;60H') + self.child.sendline (move) + print 'do_move' move + return move + + def get_first_computer_move (self): + self.child.expect ('My move is') + self.child.expect (REGEX_MOVE) +# print '', self.child.after + return self.child.after + + def get_computer_move (self): + print 'Here' + i = self.child.expect (['\[17;59H', '\[17;58H']) + print i + if i == 0: + self.child.expect (REGEX_MOVE) + if len(self.child.after) < 4: + self.child.after = self.child.after + self.last_computer_move[3] + if i == 1: + self.child.expect (REGEX_MOVE_PART) + self.child.after = self.last_computer_move[0] + self.child.after + print '', self.child.after + self.last_computer_move = self.child.after + return self.child.after + + def switch (self): + self.child.sendline ('switch') + + def set_depth (self, depth): + self.child.sendline ('depth') + self.child.expect ('depth=') + self.child.sendline ('%d' % depth) + + def quit(self): + self.child.sendline ('quit') +import sys, os +print 'Starting...' +white = Chess() +white.child.echo = 1 +white.child.expect ('Your move is') +white.set_depth(2) +white.switch() + +move_white = white.get_first_computer_move() +print 'first move white:', move_white + +white.do_move ('e7e5') +move_white = white.get_computer_move() +print 'move white:', move_white +white.do_move ('f8c5') +move_white = white.get_computer_move() +print 'move white:', move_white +white.do_move ('b8a6') +move_white = white.get_computer_move() +print 'move white:', move_white + +sys.exit(1) + + + +black = Chess() +white = Chess() +white.child.expect ('Your move is') +white.switch() + +move_white = white.get_first_computer_move() +print 'first move white:', move_white + +black.do_first_move (move_white) +move_black = black.get_first_computer_move() +print 'first move black:', move_black + +white.do_move (move_black) + +done = 0 +while not done: + move_white = white.get_computer_move() + print 'move white:', move_white + + black.do_move (move_white) + move_black = black.get_computer_move() + print 'move black:', move_black + + white.do_move (move_black) + print 'tail of loop' + +g.quit() + + diff --git a/third_party/Python/module/pexpect-2.4/examples/chess2.py b/third_party/Python/module/pexpect-2.4/examples/chess2.py new file mode 100644 index 000000000000..c62d5ce37a06 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/chess2.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +'''This demonstrates controlling a screen oriented application (curses). +It starts two instances of gnuchess and then pits them against each other. +''' + +import pexpect +import string +import ANSI +import sys, os, time + +class Chess: + + def __init__(self, engine = "/usr/local/bin/gnuchess -a -h 1"): + self.child = pexpect.spawn (engine) + self.term = ANSI.ANSI () + + #self.child.expect ('Chess') + #if self.child.after != 'Chess': + # raise IOError, 'incompatible chess program' + #self.term.process_list (self.child.before) + #self.term.process_list (self.child.after) + + self.last_computer_move = '' + + def read_until_cursor (self, r,c, e=0): + '''Eventually something like this should move into the screen class or + a subclass. Maybe a combination of pexpect and screen... + ''' + fout = open ('log','a') + while self.term.cur_r != r or self.term.cur_c != c: + try: + k = self.child.read(1, 10) + except Exception, e: + print 'EXCEPTION, (r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c) + sys.stdout.flush() + self.term.process (k) + fout.write ('(r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c)) + fout.flush() + if e: + sys.stdout.write (k) + sys.stdout.flush() + if self.term.cur_r == r and self.term.cur_c == c: + fout.close() + return 1 + print 'DIDNT EVEN HIT.' + fout.close() + return 1 + + def expect_region (self): + '''This is another method that would be moved into the + screen class. + ''' + pass + def do_scan (self): + fout = open ('log','a') + while 1: + c = self.child.read(1,10) + self.term.process (c) + fout.write ('(r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c)) + fout.flush() + sys.stdout.write (c) + sys.stdout.flush() + + def do_move (self, move, e = 0): + time.sleep(1) + self.read_until_cursor (19,60, e) + self.child.sendline (move) + + def wait (self, color): + while 1: + r = self.term.get_region (14,50,14,60)[0] + r = r.strip() + if r == color: + return + time.sleep (1) + + def parse_computer_move (self, s): + i = s.find ('is: ') + cm = s[i+3:i+9] + return cm + def get_computer_move (self, e = 0): + time.sleep(1) + self.read_until_cursor (19,60, e) + time.sleep(1) + r = self.term.get_region (17,50,17,62)[0] + cm = self.parse_computer_move (r) + return cm + + def switch (self): + print 'switching' + self.child.sendline ('switch') + + def set_depth (self, depth): + self.child.sendline ('depth') + self.child.expect ('depth=') + self.child.sendline ('%d' % depth) + + def quit(self): + self.child.sendline ('quit') + +def LOG (s): + print s + sys.stdout.flush () + fout = open ('moves.log', 'a') + fout.write (s + '\n') + fout.close() + +print 'Starting...' + +black = Chess() +white = Chess() +white.read_until_cursor (19,60,1) +white.switch() + +done = 0 +while not done: + white.wait ('Black') + move_white = white.get_computer_move(1) + LOG ( 'move white:'+ move_white ) + + black.do_move (move_white) + black.wait ('White') + move_black = black.get_computer_move() + LOG ( 'move black:'+ move_black ) + + white.do_move (move_black, 1) + +g.quit() + + diff --git a/third_party/Python/module/pexpect-2.4/examples/chess3.py b/third_party/Python/module/pexpect-2.4/examples/chess3.py new file mode 100644 index 000000000000..44044420a1c9 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/chess3.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +'''This demonstrates controlling a screen oriented application (curses). +It starts two instances of gnuchess and then pits them against each other. +''' + +import pexpect +import string +import ANSI + +REGEX_MOVE = '(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)' +REGEX_MOVE_PART = '(?:[0-9]|\x1b\[C)(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)' + +class Chess: + + def __init__(self, engine = "/usr/local/bin/gnuchess -a -h 1"): + self.child = pexpect.spawn (engine) + self.term = ANSI.ANSI () + +# self.child.expect ('Chess') + # if self.child.after != 'Chess': + # raise IOError, 'incompatible chess program' + # self.term.process_list (self.before) + # self.term.process_list (self.after) + self.last_computer_move = '' + def read_until_cursor (self, r,c): + fout = open ('log','a') + while 1: + k = self.child.read(1, 10) + self.term.process (k) + fout.write ('(r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c)) + fout.flush() + if self.term.cur_r == r and self.term.cur_c == c: + fout.close() + return 1 + sys.stdout.write (k) + sys.stdout.flush() + + def do_scan (self): + fout = open ('log','a') + while 1: + c = self.child.read(1,10) + self.term.process (c) + fout.write ('(r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c)) + fout.flush() + sys.stdout.write (c) + sys.stdout.flush() + + def do_move (self, move): + self.read_until_cursor (19,60) + self.child.sendline (move) + return move + + def get_computer_move (self): + print 'Here' + i = self.child.expect (['\[17;59H', '\[17;58H']) + print i + if i == 0: + self.child.expect (REGEX_MOVE) + if len(self.child.after) < 4: + self.child.after = self.child.after + self.last_computer_move[3] + if i == 1: + self.child.expect (REGEX_MOVE_PART) + self.child.after = self.last_computer_move[0] + self.child.after + print '', self.child.after + self.last_computer_move = self.child.after + return self.child.after + + def switch (self): + self.child.sendline ('switch') + + def set_depth (self, depth): + self.child.sendline ('depth') + self.child.expect ('depth=') + self.child.sendline ('%d' % depth) + + def quit(self): + self.child.sendline ('quit') +import sys, os +print 'Starting...' +white = Chess() +white.do_move('b2b4') +white.read_until_cursor (19,60) +c1 = white.term.get_abs(17,58) +c2 = white.term.get_abs(17,59) +c3 = white.term.get_abs(17,60) +c4 = white.term.get_abs(17,61) +fout = open ('log','a') +fout.write ('Computer:%s%s%s%s\n' %(c1,c2,c3,c4)) +fout.close() +white.do_move('c2c4') +white.read_until_cursor (19,60) +c1 = white.term.get_abs(17,58) +c2 = white.term.get_abs(17,59) +c3 = white.term.get_abs(17,60) +c4 = white.term.get_abs(17,61) +fout = open ('log','a') +fout.write ('Computer:%s%s%s%s\n' %(c1,c2,c3,c4)) +fout.close() +white.do_scan () + +#white.do_move ('b8a6') +#move_white = white.get_computer_move() +#print 'move white:', move_white + +sys.exit(1) + + + +black = Chess() +white = Chess() +white.child.expect ('Your move is') +white.switch() + +move_white = white.get_first_computer_move() +print 'first move white:', move_white + +black.do_first_move (move_white) +move_black = black.get_first_computer_move() +print 'first move black:', move_black + +white.do_move (move_black) + +done = 0 +while not done: + move_white = white.get_computer_move() + print 'move white:', move_white + + black.do_move (move_white) + move_black = black.get_computer_move() + print 'move black:', move_black + + white.do_move (move_black) + print 'tail of loop' + +g.quit() + + diff --git a/third_party/Python/module/pexpect-2.4/examples/df.py b/third_party/Python/module/pexpect-2.4/examples/df.py new file mode 100644 index 000000000000..64bbf93a1b29 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/df.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +"""This collects filesystem capacity info using the 'df' command. Tuples of +filesystem name and percentage are stored in a list. A simple report is +printed. Filesystems over 95% capacity are highlighted. Note that this does not +parse filesystem names after the first space, so names with spaces in them will +be truncated. This will produce ambiguous results for automount filesystems on +Apple OSX. """ + +import pexpect + +child = pexpect.spawn ('df') + +# parse 'df' output into a list. +pattern = "\n(\S+).*?([0-9]+)%" +filesystem_list = [] +for dummy in range (0, 1000): + i = child.expect ([pattern, pexpect.EOF]) + if i == 0: + filesystem_list.append (child.match.groups()) + else: + break + +# Print report +print +for m in filesystem_list: + s = "Filesystem %s is at %s%%" % (m[0], m[1]) + # highlight filesystems over 95% capacity + if int(m[1]) > 95: + s = '! ' + s + else: + s = ' ' + s + print s + diff --git a/third_party/Python/module/pexpect-2.4/examples/fix_cvs_files.py b/third_party/Python/module/pexpect-2.4/examples/fix_cvs_files.py new file mode 100644 index 000000000000..e75a1496abdd --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/fix_cvs_files.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +"""This is for cleaning up binary files improperly added to CVS. This script +scans the given path to find binary files; checks with CVS to see if the sticky +options are set to -kb; finally if sticky options are not -kb then uses 'cvs +admin' to set the -kb option. + +This script ignores CVS directories, symbolic links, and files not known under +CVS control (cvs status is 'Unknown'). + +Run this on a CHECKED OUT module sandbox, not on the repository itself. After +if fixes the sticky options on any files you should manually do a 'cvs commit' +to accept the changes. Then be sure to have all users do a 'cvs up -A' to +update the Sticky Option status. + +Noah Spurrier +20030426 +""" + +import os, sys, time +import pexpect + +VERBOSE = 1 + +def is_binary (filename): + + """Assume that any file with a character where the 8th bit is set is + binary. """ + + fin = open(filename, 'rb') + wholething = fin.read() + fin.close() + for c in wholething: + if ord(c) & 0x80: + return 1 + return 0 + +def is_kb_sticky (filename): + + """This checks if 'cvs status' reports '-kb' for Sticky options. If the + Sticky Option status is '-ks' then this returns 1. If the status is + 'Unknown' then it returns 1. Otherwise 0 is returned. """ + + try: + s = pexpect.spawn ('cvs status %s' % filename) + i = s.expect (['Sticky Options:\s*(.*)\r\n', 'Status: Unknown']) + if i==1 and VERBOSE: + print 'File not part of CVS repository:', filename + return 1 # Pretend it's OK. + if s.match.group(1) == '-kb': + return 1 + s = None + except: + print 'Something went wrong trying to run external cvs command.' + print ' cvs status %s' % filename + print 'The cvs command returned:' + print s.before + return 0 + +def cvs_admin_kb (filename): + + """This uses 'cvs admin' to set the '-kb' sticky option. """ + + s = pexpect.run ('cvs admin -kb %s' % filename) + # There is a timing issue. If I run 'cvs admin' too quickly + # cvs sometimes has trouble obtaining the directory lock. + time.sleep(1) + +def walk_and_clean_cvs_binaries (arg, dirname, names): + + """This contains the logic for processing files. This is the os.path.walk + callback. This skips dirnames that end in CVS. """ + + if len(dirname)>3 and dirname[-3:]=='CVS': + return + for n in names: + fullpath = os.path.join (dirname, n) + if os.path.isdir(fullpath) or os.path.islink(fullpath): + continue + if is_binary(fullpath): + if not is_kb_sticky (fullpath): + if VERBOSE: print fullpath + cvs_admin_kb (fullpath) + +def main (): + + if len(sys.argv) == 1: + root = '.' + else: + root = sys.argv[1] + os.path.walk (root, walk_and_clean_cvs_binaries, None) + +if __name__ == '__main__': + main () + diff --git a/third_party/Python/module/pexpect-2.4/examples/ftp.py b/third_party/Python/module/pexpect-2.4/examples/ftp.py new file mode 100644 index 000000000000..89a502e1b8f4 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/ftp.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +"""This demonstrates an FTP "bookmark". This connects to an ftp site; does a +few ftp stuff; and then gives the user interactive control over the session. In +this case the "bookmark" is to a directory on the OpenBSD ftp server. It puts +you in the i386 packages directory. You can easily modify this for other sites. +""" + +import pexpect +import sys + +child = pexpect.spawn('ftp ftp.openbsd.org') +child.expect('(?i)name .*: ') +child.sendline('anonymous') +child.expect('(?i)password') +child.sendline('pexpect@sourceforge.net') +child.expect('ftp> ') +child.sendline('cd /pub/OpenBSD/3.7/packages/i386') +child.expect('ftp> ') +child.sendline('bin') +child.expect('ftp> ') +child.sendline('prompt') +child.expect('ftp> ') +child.sendline('pwd') +child.expect('ftp> ') +print("Escape character is '^]'.\n") +sys.stdout.write (child.after) +sys.stdout.flush() +child.interact() # Escape character defaults to ^] +# At this point this script blocks until the user presses the escape character +# or until the child exits. The human user and the child should be talking +# to each other now. + +# At this point the script is running again. +print 'Left interactve mode.' + +# The rest is not strictly necessary. This just demonstrates a few functions. +# This makes sure the child is dead; although it would be killed when Python exits. +if child.isalive(): + child.sendline('bye') # Try to ask ftp child to exit. + child.close() +# Print the final state of the child. Normally isalive() should be FALSE. +if child.isalive(): + print 'Child did not exit gracefully.' +else: + print 'Child exited gracefully.' + diff --git a/third_party/Python/module/pexpect-2.4/examples/hive.py b/third_party/Python/module/pexpect-2.4/examples/hive.py new file mode 100644 index 000000000000..fcb75bcac67f --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/hive.py @@ -0,0 +1,437 @@ +#!/usr/bin/env python + +"""hive -- Hive Shell + +This lets you ssh to a group of servers and control them as if they were one. +Each command you enter is sent to each host in parallel. The response of each +host is collected and printed. In normal synchronous mode Hive will wait for +each host to return the shell command line prompt. The shell prompt is used to +sync output. + +Example: + + $ hive.py --sameuser --samepass host1.example.com host2.example.net + username: myusername + password: + connecting to host1.example.com - OK + connecting to host2.example.net - OK + targetting hosts: 192.168.1.104 192.168.1.107 + CMD (? for help) > uptime + ======================================================================= + host1.example.com + ----------------------------------------------------------------------- + uptime + 23:49:55 up 74 days, 5:14, 2 users, load average: 0.15, 0.05, 0.01 + ======================================================================= + host2.example.net + ----------------------------------------------------------------------- + uptime + 23:53:02 up 1 day, 13:36, 2 users, load average: 0.50, 0.40, 0.46 + ======================================================================= + +Other Usage Examples: + +1. You will be asked for your username and password for each host. + + hive.py host1 host2 host3 ... hostN + +2. You will be asked once for your username and password. + This will be used for each host. + + hive.py --sameuser --samepass host1 host2 host3 ... hostN + +3. Give a username and password on the command-line: + + hive.py user1:pass2@host1 user2:pass2@host2 ... userN:passN@hostN + +You can use an extended host notation to specify username, password, and host +instead of entering auth information interactively. Where you would enter a +host name use this format: + + username:password@host + +This assumes that ':' is not part of the password. If your password contains a +':' then you can use '\\:' to indicate a ':' and '\\\\' to indicate a single +'\\'. Remember that this information will appear in the process listing. Anyone +on your machine can see this auth information. This is not secure. + +This is a crude script that begs to be multithreaded. But it serves its +purpose. + +Noah Spurrier + +$Id: hive.py 509 2008-01-05 21:27:47Z noah $ +""" + +# TODO add feature to support username:password@host combination +# TODO add feature to log each host output in separate file + +import sys, os, re, optparse, traceback, types, time, getpass +import pexpect, pxssh +import readline, atexit + +#histfile = os.path.join(os.environ["HOME"], ".hive_history") +#try: +# readline.read_history_file(histfile) +#except IOError: +# pass +#atexit.register(readline.write_history_file, histfile) + +CMD_HELP="""Hive commands are preceded by a colon : (just think of vi). + +:target name1 name2 name3 ... + + set list of hosts to target commands + +:target all + + reset list of hosts to target all hosts in the hive. + +:to name command + + send a command line to the named host. This is similar to :target, but + sends only one command and does not change the list of targets for future + commands. + +:sync + + set mode to wait for shell prompts after commands are run. This is the + default. When Hive first logs into a host it sets a special shell prompt + pattern that it can later look for to synchronize output of the hosts. If + you 'su' to another user then it can upset the synchronization. If you need + to run something like 'su' then use the following pattern: + + CMD (? for help) > :async + CMD (? for help) > sudo su - root + CMD (? for help) > :prompt + CMD (? for help) > :sync + +:async + + set mode to not expect command line prompts (see :sync). Afterwards + commands are send to target hosts, but their responses are not read back + until :sync is run. This is useful to run before commands that will not + return with the special shell prompt pattern that Hive uses to synchronize. + +:refresh + + refresh the display. This shows the last few lines of output from all hosts. + This is similar to resync, but does not expect the promt. This is useful + for seeing what hosts are doing during long running commands. + +:resync + + This is similar to :sync, but it does not change the mode. It looks for the + prompt and thus consumes all input from all targetted hosts. + +:prompt + + force each host to reset command line prompt to the special pattern used to + synchronize all the hosts. This is useful if you 'su' to a different user + where Hive would not know the prompt to match. + +:send my text + + This will send the 'my text' wihtout a line feed to the targetted hosts. + This output of the hosts is not automatically synchronized. + +:control X + + This will send the given control character to the targetted hosts. + For example, ":control c" will send ASCII 3. + +:exit + + This will exit the hive shell. + +""" + +def login (args, cli_username=None, cli_password=None): + + # I have to keep a separate list of host names because Python dicts are not ordered. + # I want to keep the same order as in the args list. + host_names = [] + hive_connect_info = {} + hive = {} + # build up the list of connection information (hostname, username, password, port) + for host_connect_string in args: + hcd = parse_host_connect_string (host_connect_string) + hostname = hcd['hostname'] + port = hcd['port'] + if port == '': + port = None + if len(hcd['username']) > 0: + username = hcd['username'] + elif cli_username is not None: + username = cli_username + else: + username = raw_input('%s username: ' % hostname) + if len(hcd['password']) > 0: + password = hcd['password'] + elif cli_password is not None: + password = cli_password + else: + password = getpass.getpass('%s password: ' % hostname) + host_names.append(hostname) + hive_connect_info[hostname] = (hostname, username, password, port) + # build up the list of hive connections using the connection information. + for hostname in host_names: + print 'connecting to', hostname + try: + fout = file("log_"+hostname, "w") + hive[hostname] = pxssh.pxssh() + hive[hostname].login(*hive_connect_info[hostname]) + print hive[hostname].before + hive[hostname].logfile = fout + print '- OK' + except Exception, e: + print '- ERROR', + print str(e) + print 'Skipping', hostname + hive[hostname] = None + return host_names, hive + +def main (): + + global options, args, CMD_HELP + + if options.sameuser: + cli_username = raw_input('username: ') + else: + cli_username = None + + if options.samepass: + cli_password = getpass.getpass('password: ') + else: + cli_password = None + + host_names, hive = login(args, cli_username, cli_password) + + synchronous_mode = True + target_hostnames = host_names[:] + print 'targetting hosts:', ' '.join(target_hostnames) + while True: + cmd = raw_input('CMD (? for help) > ') + cmd = cmd.strip() + if cmd=='?' or cmd==':help' or cmd==':h': + print CMD_HELP + continue + elif cmd==':refresh': + refresh (hive, target_hostnames, timeout=0.5) + for hostname in target_hostnames: + if hive[hostname] is None: + print '/=============================================================================' + print '| ' + hostname + ' is DEAD' + print '\\-----------------------------------------------------------------------------' + else: + print '/=============================================================================' + print '| ' + hostname + print '\\-----------------------------------------------------------------------------' + print hive[hostname].before + print '==============================================================================' + continue + elif cmd==':resync': + resync (hive, target_hostnames, timeout=0.5) + for hostname in target_hostnames: + if hive[hostname] is None: + print '/=============================================================================' + print '| ' + hostname + ' is DEAD' + print '\\-----------------------------------------------------------------------------' + else: + print '/=============================================================================' + print '| ' + hostname + print '\\-----------------------------------------------------------------------------' + print hive[hostname].before + print '==============================================================================' + continue + elif cmd==':sync': + synchronous_mode = True + resync (hive, target_hostnames, timeout=0.5) + continue + elif cmd==':async': + synchronous_mode = False + continue + elif cmd==':prompt': + for hostname in target_hostnames: + try: + if hive[hostname] is not None: + hive[hostname].set_unique_prompt() + except Exception, e: + print "Had trouble communicating with %s, so removing it from the target list." % hostname + print str(e) + hive[hostname] = None + continue + elif cmd[:5] == ':send': + cmd, txt = cmd.split(None,1) + for hostname in target_hostnames: + try: + if hive[hostname] is not None: + hive[hostname].send(txt) + except Exception, e: + print "Had trouble communicating with %s, so removing it from the target list." % hostname + print str(e) + hive[hostname] = None + continue + elif cmd[:3] == ':to': + cmd, hostname, txt = cmd.split(None,2) + if hive[hostname] is None: + print '/=============================================================================' + print '| ' + hostname + ' is DEAD' + print '\\-----------------------------------------------------------------------------' + continue + try: + hive[hostname].sendline (txt) + hive[hostname].prompt(timeout=2) + print '/=============================================================================' + print '| ' + hostname + print '\\-----------------------------------------------------------------------------' + print hive[hostname].before + except Exception, e: + print "Had trouble communicating with %s, so removing it from the target list." % hostname + print str(e) + hive[hostname] = None + continue + elif cmd[:7] == ':expect': + cmd, pattern = cmd.split(None,1) + print 'looking for', pattern + try: + for hostname in target_hostnames: + if hive[hostname] is not None: + hive[hostname].expect(pattern) + print hive[hostname].before + except Exception, e: + print "Had trouble communicating with %s, so removing it from the target list." % hostname + print str(e) + hive[hostname] = None + continue + elif cmd[:7] == ':target': + target_hostnames = cmd.split()[1:] + if len(target_hostnames) == 0 or target_hostnames[0] == all: + target_hostnames = host_names[:] + print 'targetting hosts:', ' '.join(target_hostnames) + continue + elif cmd == ':exit' or cmd == ':q' or cmd == ':quit': + break + elif cmd[:8] == ':control' or cmd[:5] == ':ctrl' : + cmd, c = cmd.split(None,1) + if ord(c)-96 < 0 or ord(c)-96 > 255: + print '/=============================================================================' + print '| Invalid character. Must be [a-zA-Z], @, [, ], \\, ^, _, or ?' + print '\\-----------------------------------------------------------------------------' + continue + for hostname in target_hostnames: + try: + if hive[hostname] is not None: + hive[hostname].sendcontrol(c) + except Exception, e: + print "Had trouble communicating with %s, so removing it from the target list." % hostname + print str(e) + hive[hostname] = None + continue + elif cmd == ':esc': + for hostname in target_hostnames: + if hive[hostname] is not None: + hive[hostname].send(chr(27)) + continue + # + # Run the command on all targets in parallel + # + for hostname in target_hostnames: + try: + if hive[hostname] is not None: + hive[hostname].sendline (cmd) + except Exception, e: + print "Had trouble communicating with %s, so removing it from the target list." % hostname + print str(e) + hive[hostname] = None + + # + # print the response for each targeted host. + # + if synchronous_mode: + for hostname in target_hostnames: + try: + if hive[hostname] is None: + print '/=============================================================================' + print '| ' + hostname + ' is DEAD' + print '\\-----------------------------------------------------------------------------' + else: + hive[hostname].prompt(timeout=2) + print '/=============================================================================' + print '| ' + hostname + print '\\-----------------------------------------------------------------------------' + print hive[hostname].before + except Exception, e: + print "Had trouble communicating with %s, so removing it from the target list." % hostname + print str(e) + hive[hostname] = None + print '==============================================================================' + +def refresh (hive, hive_names, timeout=0.5): + + """This waits for the TIMEOUT on each host. + """ + + # TODO This is ideal for threading. + for hostname in hive_names: + hive[hostname].expect([pexpect.TIMEOUT,pexpect.EOF],timeout=timeout) + +def resync (hive, hive_names, timeout=2, max_attempts=5): + + """This waits for the shell prompt for each host in an effort to try to get + them all to the same state. The timeout is set low so that hosts that are + already at the prompt will not slow things down too much. If a prompt match + is made for a hosts then keep asking until it stops matching. This is a + best effort to consume all input if it printed more than one prompt. It's + kind of kludgy. Note that this will always introduce a delay equal to the + timeout for each machine. So for 10 machines with a 2 second delay you will + get AT LEAST a 20 second delay if not more. """ + + # TODO This is ideal for threading. + for hostname in hive_names: + for attempts in xrange(0, max_attempts): + if not hive[hostname].prompt(timeout=timeout): + break + +def parse_host_connect_string (hcs): + + """This parses a host connection string in the form + username:password@hostname:port. All fields are options expcet hostname. A + dictionary is returned with all four keys. Keys that were not included are + set to empty strings ''. Note that if your password has the '@' character + then you must backslash escape it. """ + + if '@' in hcs: + p = re.compile (r'(?P[^@:]*)(:?)(?P.*)(?!\\)@(?P[^:]*):?(?P[0-9]*)') + else: + p = re.compile (r'(?P)(?P)(?P[^:]*):?(?P[0-9]*)') + m = p.search (hcs) + d = m.groupdict() + d['password'] = d['password'].replace('\\@','@') + return d + +if __name__ == '__main__': + try: + start_time = time.time() + parser = optparse.OptionParser(formatter=optparse.TitledHelpFormatter(), usage=globals()['__doc__'], version='$Id: hive.py 509 2008-01-05 21:27:47Z noah $',conflict_handler="resolve") + parser.add_option ('-v', '--verbose', action='store_true', default=False, help='verbose output') + parser.add_option ('--samepass', action='store_true', default=False, help='Use same password for each login.') + parser.add_option ('--sameuser', action='store_true', default=False, help='Use same username for each login.') + (options, args) = parser.parse_args() + if len(args) < 1: + parser.error ('missing argument') + if options.verbose: print time.asctime() + main() + if options.verbose: print time.asctime() + if options.verbose: print 'TOTAL TIME IN MINUTES:', + if options.verbose: print (time.time() - start_time) / 60.0 + sys.exit(0) + except KeyboardInterrupt, e: # Ctrl-C + raise e + except SystemExit, e: # sys.exit() + raise e + except Exception, e: + print 'ERROR, UNEXPECTED EXCEPTION' + print str(e) + traceback.print_exc() + os._exit(1) diff --git a/third_party/Python/module/pexpect-2.4/examples/monitor.py b/third_party/Python/module/pexpect-2.4/examples/monitor.py new file mode 100644 index 000000000000..e31b51b6d210 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/monitor.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python + +""" This runs a sequence of commands on a remote host using SSH. It runs a +simple system checks such as uptime and free to monitor the state of the remote +host. + +./monitor.py [-s server_hostname] [-u username] [-p password] + -s : hostname of the remote server to login to. + -u : username to user for login. + -p : Password to user for login. + +Example: + This will print information about the given host: + ./monitor.py -s www.example.com -u mylogin -p mypassword + +It works like this: + Login via SSH (This is the hardest part). + Run and parse 'uptime'. + Run 'iostat'. + Run 'vmstat'. + Run 'netstat' + Run 'free'. + Exit the remote host. +""" + +import os, sys, time, re, getopt, getpass +import traceback +import pexpect + +# +# Some constants. +# +COMMAND_PROMPT = '[#$] ' ### This is way too simple for industrial use -- we will change is ASAP. +TERMINAL_PROMPT = '(?i)terminal type\?' +TERMINAL_TYPE = 'vt100' +# This is the prompt we get if SSH does not have the remote host's public key stored in the cache. +SSH_NEWKEY = '(?i)are you sure you want to continue connecting' + +def exit_with_usage(): + + print globals()['__doc__'] + os._exit(1) + +def main(): + + global COMMAND_PROMPT, TERMINAL_PROMPT, TERMINAL_TYPE, SSH_NEWKEY + ###################################################################### + ## Parse the options, arguments, get ready, etc. + ###################################################################### + try: + optlist, args = getopt.getopt(sys.argv[1:], 'h?s:u:p:', ['help','h','?']) + except Exception, e: + print str(e) + exit_with_usage() + options = dict(optlist) + if len(args) > 1: + exit_with_usage() + + if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]: + print "Help:" + exit_with_usage() + + if '-s' in options: + host = options['-s'] + else: + host = raw_input('hostname: ') + if '-u' in options: + user = options['-u'] + else: + user = raw_input('username: ') + if '-p' in options: + password = options['-p'] + else: + password = getpass.getpass('password: ') + + # + # Login via SSH + # + child = pexpect.spawn('ssh -l %s %s'%(user, host)) + i = child.expect([pexpect.TIMEOUT, SSH_NEWKEY, COMMAND_PROMPT, '(?i)password']) + if i == 0: # Timeout + print 'ERROR! could not login with SSH. Here is what SSH said:' + print child.before, child.after + print str(child) + sys.exit (1) + if i == 1: # In this case SSH does not have the public key cached. + child.sendline ('yes') + child.expect ('(?i)password') + if i == 2: + # This may happen if a public key was setup to automatically login. + # But beware, the COMMAND_PROMPT at this point is very trivial and + # could be fooled by some output in the MOTD or login message. + pass + if i == 3: + child.sendline(password) + # Now we are either at the command prompt or + # the login process is asking for our terminal type. + i = child.expect ([COMMAND_PROMPT, TERMINAL_PROMPT]) + if i == 1: + child.sendline (TERMINAL_TYPE) + child.expect (COMMAND_PROMPT) + # + # Set command prompt to something more unique. + # + COMMAND_PROMPT = "\[PEXPECT\]\$ " + child.sendline ("PS1='[PEXPECT]\$ '") # In case of sh-style + i = child.expect ([pexpect.TIMEOUT, COMMAND_PROMPT], timeout=10) + if i == 0: + print "# Couldn't set sh-style prompt -- trying csh-style." + child.sendline ("set prompt='[PEXPECT]\$ '") + i = child.expect ([pexpect.TIMEOUT, COMMAND_PROMPT], timeout=10) + if i == 0: + print "Failed to set command prompt using sh or csh style." + print "Response was:" + print child.before + sys.exit (1) + + # Now we should be at the command prompt and ready to run some commands. + print '---------------------------------------' + print 'Report of commands run on remote host.' + print '---------------------------------------' + + # Run uname. + child.sendline ('uname -a') + child.expect (COMMAND_PROMPT) + print child.before + if 'linux' in child.before.lower(): + LINUX_MODE = 1 + else: + LINUX_MODE = 0 + + # Run and parse 'uptime'. + child.sendline ('uptime') + child.expect('up\s+(.*?),\s+([0-9]+) users?,\s+load averages?: ([0-9]+\.[0-9][0-9]),?\s+([0-9]+\.[0-9][0-9]),?\s+([0-9]+\.[0-9][0-9])') + duration, users, av1, av5, av15 = child.match.groups() + days = '0' + hours = '0' + mins = '0' + if 'day' in duration: + child.match = re.search('([0-9]+)\s+day',duration) + days = str(int(child.match.group(1))) + if ':' in duration: + child.match = re.search('([0-9]+):([0-9]+)',duration) + hours = str(int(child.match.group(1))) + mins = str(int(child.match.group(2))) + if 'min' in duration: + child.match = re.search('([0-9]+)\s+min',duration) + mins = str(int(child.match.group(1))) + print + print 'Uptime: %s days, %s users, %s (1 min), %s (5 min), %s (15 min)' % ( + duration, users, av1, av5, av15) + child.expect (COMMAND_PROMPT) + + # Run iostat. + child.sendline ('iostat') + child.expect (COMMAND_PROMPT) + print child.before + + # Run vmstat. + child.sendline ('vmstat') + child.expect (COMMAND_PROMPT) + print child.before + + # Run free. + if LINUX_MODE: + child.sendline ('free') # Linux systems only. + child.expect (COMMAND_PROMPT) + print child.before + + # Run df. + child.sendline ('df') + child.expect (COMMAND_PROMPT) + print child.before + + # Run lsof. + child.sendline ('lsof') + child.expect (COMMAND_PROMPT) + print child.before + +# # Run netstat +# child.sendline ('netstat') +# child.expect (COMMAND_PROMPT) +# print child.before + +# # Run MySQL show status. +# child.sendline ('mysql -p -e "SHOW STATUS;"') +# child.expect (PASSWORD_PROMPT_MYSQL) +# child.sendline (password_mysql) +# child.expect (COMMAND_PROMPT) +# print +# print child.before + + # Now exit the remote host. + child.sendline ('exit') + index = child.expect([pexpect.EOF, "(?i)there are stopped jobs"]) + if index==1: + child.sendline("exit") + child.expect(EOF) + +if __name__ == "__main__": + + try: + main() + except Exception, e: + print str(e) + traceback.print_exc() + os._exit(1) + diff --git a/third_party/Python/module/pexpect-2.4/examples/passmass.py b/third_party/Python/module/pexpect-2.4/examples/passmass.py new file mode 100644 index 000000000000..b1e17b9cb036 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/passmass.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +"""Change passwords on the named machines. passmass host1 host2 host3 . . . +Note that login shell prompt on remote machine must end in # or $. """ + +import pexpect +import sys, getpass + +USAGE = '''passmass host1 host2 host3 . . .''' +COMMAND_PROMPT = '[$#] ' +TERMINAL_PROMPT = r'Terminal type\?' +TERMINAL_TYPE = 'vt100' +SSH_NEWKEY = r'Are you sure you want to continue connecting \(yes/no\)\?' + +def login(host, user, password): + + child = pexpect.spawn('ssh -l %s %s'%(user, host)) + fout = file ("LOG.TXT","wb") + child.setlog (fout) + + i = child.expect([pexpect.TIMEOUT, SSH_NEWKEY, '[Pp]assword: ']) + if i == 0: # Timeout + print 'ERROR!' + print 'SSH could not login. Here is what SSH said:' + print child.before, child.after + sys.exit (1) + if i == 1: # SSH does not have the public key. Just accept it. + child.sendline ('yes') + child.expect ('[Pp]assword: ') + child.sendline(password) + # Now we are either at the command prompt or + # the login process is asking for our terminal type. + i = child.expect (['Permission denied', TERMINAL_PROMPT, COMMAND_PROMPT]) + if i == 0: + print 'Permission denied on host:', host + sys.exit (1) + if i == 1: + child.sendline (TERMINAL_TYPE) + child.expect (COMMAND_PROMPT) + return child + +# (current) UNIX password: +def change_password(child, user, oldpassword, newpassword): + + child.sendline('passwd') + i = child.expect(['[Oo]ld [Pp]assword', '.current.*password', '[Nn]ew [Pp]assword']) + # Root does not require old password, so it gets to bypass the next step. + if i == 0 or i == 1: + child.sendline(oldpassword) + child.expect('[Nn]ew [Pp]assword') + child.sendline(newpassword) + i = child.expect(['[Nn]ew [Pp]assword', '[Rr]etype', '[Rr]e-enter']) + if i == 0: + print 'Host did not like new password. Here is what it said...' + print child.before + child.send (chr(3)) # Ctrl-C + child.sendline('') # This should tell remote passwd command to quit. + return + child.sendline(newpassword) + +def main(): + + if len(sys.argv) <= 1: + print USAGE + return 1 + + user = raw_input('Username: ') + password = getpass.getpass('Current Password: ') + newpassword = getpass.getpass('New Password: ') + newpasswordconfirm = getpass.getpass('Confirm New Password: ') + if newpassword != newpasswordconfirm: + print 'New Passwords do not match.' + return 1 + + for host in sys.argv[1:]: + child = login(host, user, password) + if child == None: + print 'Could not login to host:', host + continue + print 'Changing password on host:', host + change_password(child, user, password, newpassword) + child.expect(COMMAND_PROMPT) + child.sendline('exit') + +if __name__ == '__main__': + try: + main() + except pexpect.ExceptionPexpect, e: + print str(e) + diff --git a/third_party/Python/module/pexpect-2.4/examples/python.py b/third_party/Python/module/pexpect-2.4/examples/python.py new file mode 100644 index 000000000000..d8c986652e6d --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/python.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +"""This starts the python interpreter; captures the startup message; then gives +the user interactive control over the session. Why? For fun... """ + +# Don't do this unless you like being John Malkovich +# c = pexpect.spawn ('/usr/bin/env python ./python.py') + +import pexpect +c = pexpect.spawn ('/usr/bin/env python') +c.expect ('>>>') +print 'And now for something completely different...' +f = lambda s:s and f(s[1:])+s[0] # Makes a function to reverse a string. +print f(c.before) +print 'Yes, it\'s python, but it\'s backwards.' +print +print 'Escape character is \'^]\'.' +print c.after, +c.interact() +c.kill(1) +print 'is alive:', c.isalive() + diff --git a/third_party/Python/module/pexpect-2.4/examples/rippy.py b/third_party/Python/module/pexpect-2.4/examples/rippy.py new file mode 100644 index 000000000000..e89c314c323c --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/rippy.py @@ -0,0 +1,993 @@ +#!/usr/bin/env python + +"""Rippy! + +This script helps to convert video from one format to another. +This is useful for ripping DVD to mpeg4 video (XviD, DivX). + +Features: + * automatic crop detection + * mp3 audio compression with resampling options + * automatic bitrate calculation based on desired target size + * optional interlace removal, b/w video optimization, video scaling + +Run the script with no arguments to start with interactive prompts: + rippy.py +Run the script with the filename of a config to start automatic mode: + rippy.py rippy.conf + +After Rippy is finished it saves the current configuation in a file called +'rippy.conf' in the local directoy. This can be used to rerun process using the +exact same settings by passing the filename of the conf file as an argument to +Rippy. Rippy will read the options from the file instead of asking you for +options interactively. So if you run rippy with 'dry_run=1' then you can run +the process again later using the 'rippy.conf' file. Don't forget to edit +'rippy.conf' to set 'dry_run=0'! + +If you run rippy with 'dry_run' and 'verbose' true then the output generated is +valid command line commands. you could (in theory) cut-and-paste the commands +to a shell prompt. You will need to tweak some values such as crop area and bit +rate because these cannot be calculated in a dry run. This is useful if you +want to get an idea of what Rippy plans to do. + +For all the trouble that Rippy goes through to calculate the best bitrate for a +desired target video size it sometimes fails to get it right. Sometimes the +final video size will differ more than you wanted from the desired size, but if +you are really motivated and have a lot of time on your hands then you can run +Rippy again with a manually calculated bitrate. After all compression is done +the first time Rippy will recalculate the bitrate to give you the nearly exact +bitrate that would have worked. You can then edit the 'rippy.conf' file; set +the video_bitrate with this revised bitrate; and then run Rippy all over again. +There is nothing like 4-pass video compression to get it right! Actually, this +could be done in three passes since I don't need to do the second pass +compression before I calculate the revised bitrate. I'm also considering an +enhancement where Rippy would compress ten spread out chunks, 1-minute in +length to estimate the bitrate. + +Free, open source, and all that good stuff. +Rippy Copyright (c) 2006 Noah Spurrier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +Noah Spurrier +$Id: rippy.py 517 2008-08-18 22:23:56Z noah $ +""" + +import sys, os, re, math, stat, getopt, traceback, types, time +import pexpect + +__version__ = '1.2' +__revision__ = '$Revision: 11 $' +__all__ = ['main', __version__, __revision__] + +GLOBAL_LOGFILE_NAME = "rippy_%d.log" % os.getpid() +GLOBAL_LOGFILE = open (GLOBAL_LOGFILE_NAME, "wb") + +############################################################################### +# This giant section defines the prompts and defaults used in interactive mode. +############################################################################### +# Python dictionaries are unordered, so +# I have this list that maintains the order of the keys. +prompts_key_order = ( +'verbose_flag', +'dry_run_flag', +'video_source_filename', +'video_chapter', +'video_final_filename', +'video_length', +'video_aspect_ratio', +'video_scale', +'video_encode_passes', +'video_codec', +'video_fourcc_override', +'video_bitrate', +'video_bitrate_overhead', +'video_target_size', +'video_deinterlace_flag', +'video_crop_area', +'video_gray_flag', +'subtitle_id', +'audio_id', +'audio_codec', +'audio_raw_filename', +'audio_volume_boost', +'audio_sample_rate', +'audio_bitrate', +#'audio_lowpass_filter', +'delete_tmp_files_flag' +) +# +# The 'prompts' dictionary holds all the messages shown to the user in +# interactive mode. The 'prompts' dictionary schema is defined as follows: +# prompt_key : ( default value, prompt string, help string, level of difficulty (0,1,2) ) +# +prompts = { +'video_source_filename':("dvd://1", 'video source filename?', """This is the filename of the video that you want to convert from. +It can be any file that mencoder supports. +You can also choose a DVD device using the dvd://1 syntax. +Title 1 is usually the main title on a DVD.""",0), +'video_chapter':("none",'video chapter?',"""This is the chapter number. Usually disks such as TV series seasons will be divided into chapters. Maybe be set to none.""",0), +'video_final_filename':("video_final.avi", "video final filename?", """This is the name of the final video.""",0), +'audio_raw_filename':("audiodump.wav", "audio raw filename?", """This is the audio raw PCM filename. This is prior to compression. +Note that mplayer automatically names this audiodump.wav, so don't change this.""",1000), +#'audio_compressed_filename':("audiodump.mp3","Audio compressed filename?", """This is the name of the compressed audio that will be mixed +#into the final video. Normally you don't need to change this.""",2), +'video_length':("none","video length in seconds?","""This sets the length of the video in seconds. This is used to estimate the +bitrate for a target video file size. Set to 'calc' to have Rippy calculate +the length. Set to 'none' if you don't want rippy to estimate the bitrate -- +you will have to manually specify bitrate.""",1), +'video_aspect_ratio':("calc","aspect ratio?","""This sets the aspect ratio of the video. Most DVDs are 16/9 or 4/3.""",1), +'video_scale':("none","video scale?","""This scales the video to the given output size. The default is to do no scaling. +You may type in a resolution such as 320x240 or you may use presets. + qntsc: 352x240 (NTSC quarter screen) + qpal: 352x288 (PAL quarter screen) + ntsc: 720x480 (standard NTSC) + pal: 720x576 (standard PAL) + sntsc: 640x480 (square pixel NTSC) + spal: 768x576 (square pixel PAL)""",1), +'video_codec':("mpeg4","video codec?","""This is the video compression to use. This is passed directly to mencoder, so +any format that it recognizes should work. For XviD or DivX use mpeg4. +Almost all MS Windows systems support wmv2 out of the box. +Some common codecs include: +mjpeg, h263, h263p, h264, mpeg4, msmpeg4, wmv1, wmv2, mpeg1video, mpeg2video, huffyuv, ffv1. +""",2), +'audio_codec':("mp3","audio codec?","""This is the audio compression to use. This is passed directly to mencoder, so +any format that it recognizes will work. +Some common codecs include: +mp3, mp2, aac, pcm +See mencoder manual for details.""",2), +'video_fourcc_override':("XVID","force fourcc code?","""This forces the fourcc codec to the given value. XVID is safest for Windows. +The following are common fourcc values: + FMP4 - This is the mencoder default. This is the "real" value. + XVID - used by Xvid (safest) + DX50 - + MP4S - Microsoft""",2), +'video_encode_passes':("1","number of encode passes?","""This sets how many passes to use to encode the video. You can choose 1 or 2. +Using two pases takes twice as long as one pass, but produces a better +quality video. I found that the improvement is not that impressive.""",1), +'verbose_flag':("Y","verbose output?","""This sets verbose output. If true then all commands and arguments are printed +before they are run. This is useful to see exactly how commands are run.""",1), +'dry_run_flag':("N","dry run?","""This sets 'dry run' mode. If true then commands are not run. This is useful +if you want to see what would the script would do.""",1), +'video_bitrate':("calc","video bitrate?","""This sets the video bitrate. This overrides video_target_size. +Set to 'calc' to automatically estimate the bitrate based on the +video final target size. If you set video_length to 'none' then +you will have to specify this video_bitrate.""",1), +'video_target_size':("737280000","video final target size?","""This sets the target video size that you want to end up with. +This is over-ridden by video_bitrate. In other words, if you specify +video_bitrate then video_target_size is ignored. +Due to the unpredictable nature of VBR compression the final video size +may not exactly match. The following are common CDR sizes: + 180MB CDR (21 minutes) holds 193536000 bytes + 550MB CDR (63 minutes) holds 580608000 bytes + 650MB CDR (74 minutes) holds 681984000 bytes + 700MB CDR (80 minutes) holds 737280000 bytes""",0), +'video_bitrate_overhead':("1.0","bitrate overhead factor?","""Adjust this value if you want to leave more room for +other files such as subtitle files. +If you specify video_bitrate then this value is ignored.""",2), +'video_crop_area':("detect","crop area?","""This sets the crop area to remove black bars from the top or sides of the video. +This helps save space. Set to 'detect' to automatically detect the crop area. +Set to 'none' to not crop the video. Normally you don't need to change this.""",1), +'video_deinterlace_flag':("N","is the video interlaced?","""This sets the deinterlace flag. If set then mencoder will be instructed +to filter out interlace artifacts (using '-vf pp=md').""",1), +'video_gray_flag':("N","is the video black and white (gray)?","""This improves output for black and white video.""",1), +'subtitle_id':("None","Subtitle ID stream?","""This selects the subtitle stream to extract from the source video. +Normally, 0 is the English subtitle stream for a DVD. +Subtitles IDs with higher numbers may be other languages.""",1), +'audio_id':("128","audio ID stream?","""This selects the audio stream to extract from the source video. +If your source is a VOB file (DVD) then stream IDs start at 128. +Normally, 128 is the main audio track for a DVD. +Tracks with higher numbers may be other language dubs or audio commentary.""",1), +'audio_sample_rate':("32000","audio sample rate (Hz) 48000, 44100, 32000, 24000, 12000","""This sets the rate at which the compressed audio will be resampled. +DVD audio is 48 kHz whereas music CDs use 44.1 kHz. The higher the sample rate +the more space the audio track will take. That will leave less space for video. +32 kHz is a good trade-off if you are trying to fit a video onto a CD.""",1), +'audio_bitrate':("96","audio bitrate (kbit/s) 192, 128, 96, 64?","""This sets the bitrate for MP3 audio compression. +The higher the bitrate the more space the audio track will take. +That will leave less space for video. Most people find music to be acceptable +at 128 kBitS. 96 kBitS is a good trade-off if you are trying to fit a video onto a CD.""",1), +'audio_volume_boost':("none","volume dB boost?","""Many DVDs have very low audio volume. This sets an audio volume boost in Decibels. +Values of 6 to 10 usually adjust quiet DVDs to a comfortable level.""",1), +#'audio_lowpass_filter':("16","audio lowpass filter (kHz)?","""This sets the low-pass filter for the audio. +#Normally this should be half of the audio sample rate. +#This improves audio compression and quality. +#Normally you don't need to change this.""",1), +'delete_tmp_files_flag':("N","delete temporary files when finished?","""If Y then %s, audio_raw_filename, and 'divx2pass.log' will be deleted at the end."""%GLOBAL_LOGFILE_NAME,1) +} + +############################################################################## +# This is the important convert control function +############################################################################## +def convert (options): + """This is the heart of it all -- this performs an end-to-end conversion of + a video from one format to another. It requires a dictionary of options. + The conversion process will also add some keys to the dictionary + such as length of the video and crop area. The dictionary is returned. + This options dictionary could be used again to repeat the convert process + (it is also saved to rippy.conf as text). + """ + if options['subtitle_id'] is not None: + print "# extract subtitles" + apply_smart (extract_subtitles, options) + else: + print "# do not extract subtitles." + + # Optimization + # I really only need to calculate the exact video length if the user + # selected 'calc' for video_bitrate + # or + # selected 'detect' for video_crop_area. + if options['video_bitrate']=='calc' or options['video_crop_area']=='detect': + # As strange as it seems, the only reliable way to calculate the length + # of a video (in seconds) is to extract the raw, uncompressed PCM audio stream + # and then calculate the length of that. This is because MP4 video is VBR, so + # you cannot get exact time based on compressed size. + if options['video_length']=='calc': + print "# extract PCM raw audio to %s" % (options['audio_raw_filename']) + apply_smart (extract_audio, options) + options['video_length'] = apply_smart (get_length, options) + print "# Length of raw audio file : %d seconds (%0.2f minutes)" % (options['video_length'], float(options['video_length'])/60.0) + if options['video_bitrate']=='calc': + options['video_bitrate'] = options['video_bitrate_overhead'] * apply_smart (calc_video_bitrate, options) + print "# video bitrate : " + str(options['video_bitrate']) + if options['video_crop_area']=='detect': + options['video_crop_area'] = apply_smart (crop_detect, options) + print "# crop area : " + str(options['video_crop_area']) + print "# compression estimate" + print apply_smart (compression_estimate, options) + + print "# compress video" + apply_smart (compress_video, options) + 'audio_volume_boost', + + print "# delete temporary files:", + if options['delete_tmp_files_flag']: + print "yes" + apply_smart (delete_tmp_files, options) + else: + print "no" + + # Finish by saving options to rippy.conf and + # calclating if final_size is less than target_size. + o = ["# options used to create video\n"] + video_actual_size = get_filesize (options['video_final_filename']) + if options['video_target_size'] != 'none': + revised_bitrate = calculate_revised_bitrate (options['video_bitrate'], options['video_target_size'], video_actual_size) + o.append("# revised video_bitrate : %d\n" % revised_bitrate) + for k,v in options.iteritems(): + o.append (" %30s : %s\n" % (k, v)) + print '# '.join(o) + fout = open("rippy.conf","wb").write(''.join(o)) + print "# final actual video size = %d" % video_actual_size + if options['video_target_size'] != 'none': + if video_actual_size > options['video_target_size']: + print "# FINAL VIDEO SIZE IS GREATER THAN DESIRED TARGET" + print "# final video size is %d bytes over target size" % (video_actual_size - options['video_target_size']) + else: + print "# final video size is %d bytes under target size" % (options['video_target_size'] - video_actual_size) + print "# If you want to run the entire compression process all over again" + print "# to get closer to the target video size then trying using a revised" + print "# video_bitrate of %d" % revised_bitrate + + return options + +############################################################################## + +def exit_with_usage(exit_code=1): + print globals()['__doc__'] + print 'version:', globals()['__version__'] + sys.stdout.flush() + os._exit(exit_code) + +def check_missing_requirements (): + """This list of missing requirements (mencoder, mplayer, lame, and mkvmerge). + Returns None if all requirements are in the execution path. + """ + missing = [] + if pexpect.which("mencoder") is None: + missing.append("mencoder") + if pexpect.which("mplayer") is None: + missing.append("mplayer") + cmd = "mencoder -oac help" + (command_output, exitstatus) = run(cmd) + ar = re.findall("(mp3lame)", command_output) + if len(ar)==0: + missing.append("Mencoder was not compiled with mp3lame support.") + + #if pexpect.which("lame") is None: + # missing.append("lame") + #if pexpect.which("mkvmerge") is None: + # missing.append("mkvmerge") + if len(missing)==0: + return None + return missing + +def input_option (message, default_value="", help=None, level=0, max_level=0): + """This is a fancy raw_input function. + If the user enters '?' then the contents of help is printed. + + The 'level' and 'max_level' are used to adjust which advanced options + are printed. 'max_level' is the level of options that the user wants + to see. 'level' is the level of difficulty for this particular option. + If this level is <= the max_level the user wants then the + message is printed and user input is allowed; otherwise, the + default value is returned automatically without user input. + """ + if default_value != '': + message = "%s [%s] " % (message, default_value) + if level > max_level: + return default_value + while 1: + user_input = raw_input (message) + if user_input=='?': + print help + elif user_input=='': + return default_value + else: + break + return user_input + +def progress_callback (d=None): + """This callback simply prints a dot to show activity. + This is used when running external commands with pexpect.run. + """ + sys.stdout.write (".") + sys.stdout.flush() + +def run(cmd): + global GLOBAL_LOGFILE + print >>GLOBAL_LOGFILE, cmd + (command_output, exitstatus) = pexpect.run(cmd, events={pexpect.TIMEOUT:progress_callback}, timeout=5, withexitstatus=True, logfile=GLOBAL_LOGFILE) + if exitstatus != 0: + print "RUN FAILED. RETURNED EXIT STATUS:", exitstatus + print >>GLOBAL_LOGFILE, "RUN FAILED. RETURNED EXIT STATUS:", exitstatus + return (command_output, exitstatus) + +def apply_smart (func, args): + """This is similar to func(**args), but this won't complain about + extra keys in 'args'. This ignores keys in 'args' that are + not required by 'func'. This passes None to arguments that are + not defined in 'args'. That's fine for arguments with a default valeue, but + that's a bug for required arguments. I should probably raise a TypeError. + The func parameter can be a function reference or a string. + If it is a string then it is converted to a function reference. + """ + if type(func) is type(''): + if func in globals(): + func = globals()[func] + else: + raise NameError("name '%s' is not defined" % func) + if hasattr(func,'im_func'): # Handle case when func is a class method. + func = func.im_func + argcount = func.func_code.co_argcount + required_args = dict([(k,args.get(k)) for k in func.func_code.co_varnames[:argcount]]) + return func(**required_args) + +def count_unique (items): + """This takes a list and returns a sorted list of tuples with a count of each unique item in the list. + Example 1: + count_unique(['a','b','c','a','c','c','a','c','c']) + returns: + [(5,'c'), (3,'a'), (1,'b')] + Example 2 -- get the most frequent item in a list: + count_unique(['a','b','c','a','c','c','a','c','c'])[0][1] + returns: + 'c' + """ + stats = {} + for i in items: + if i in stats: + stats[i] = stats[i] + 1 + else: + stats[i] = 1 + stats = [(v, k) for k, v in stats.items()] + stats.sort() + stats.reverse() + return stats + +def calculate_revised_bitrate (video_bitrate, video_target_size, video_actual_size): + """This calculates a revised video bitrate given the video_bitrate used, + the actual size that resulted, and the video_target_size. + This can be used if you want to compress the video all over again in an + attempt to get closer to the video_target_size. + """ + return int(math.floor(video_bitrate * (float(video_target_size) / float(video_actual_size)))) + +def get_aspect_ratio (video_source_filename): + """This returns the aspect ratio of the original video. + This is usualy 1.78:1(16/9) or 1.33:1(4/3). + This function is very lenient. It basically guesses 16/9 whenever + it cannot figure out the aspect ratio. + """ + cmd = "mplayer '%s' -vo png -ao null -frames 1" % video_source_filename + (command_output, exitstatus) = run(cmd) + ar = re.findall("Movie-Aspect is ([0-9]+\.?[0-9]*:[0-9]+\.?[0-9]*)", command_output) + if len(ar)==0: + return '16/9' + if ar[0] == '1.78:1': + return '16/9' + if ar[0] == '1.33:1': + return '4/3' + return '16/9' + #idh = re.findall("ID_VIDEO_HEIGHT=([0-9]+)", command_output) + #if len(idw)==0 or len(idh)==0: + # print 'WARNING!' + # print 'Could not get aspect ration. Assuming 1.78:1 (16/9).' + # return 1.78 + #return float(idw[0])/float(idh[0]) +#ID_VIDEO_WIDTH=720 +#ID_VIDEO_HEIGHT=480 +#Movie-Aspect is 1.78:1 - prescaling to correct movie aspect. + + +def get_aid_list (video_source_filename): + """This returns a list of audio ids in the source video file. + TODO: Also extract ID_AID_nnn_LANG to associate language. Not all DVDs include this. + """ + cmd = "mplayer '%s' -vo null -ao null -frames 0 -identify" % video_source_filename + (command_output, exitstatus) = run(cmd) + idl = re.findall("ID_AUDIO_ID=([0-9]+)", command_output) + idl.sort() + return idl + +def get_sid_list (video_source_filename): + """This returns a list of subtitle ids in the source video file. + TODO: Also extract ID_SID_nnn_LANG to associate language. Not all DVDs include this. + """ + cmd = "mplayer '%s' -vo null -ao null -frames 0 -identify" % video_source_filename + (command_output, exitstatus) = run(cmd) + idl = re.findall("ID_SUBTITLE_ID=([0-9]+)", command_output) + idl.sort() + return idl + +def extract_audio (video_source_filename, audio_id=128, verbose_flag=0, dry_run_flag=0): + """This extracts the given audio_id track as raw uncompressed PCM from the given source video. + Note that mplayer always saves this to audiodump.wav. + At this time there is no way to set the output audio name. + """ + #cmd = "mplayer %(video_source_filename)s -vc null -vo null -aid %(audio_id)s -ao pcm:fast -noframedrop" % locals() + cmd = "mplayer -quiet '%(video_source_filename)s' -vc dummy -vo null -aid %(audio_id)s -ao pcm:fast -noframedrop" % locals() + if verbose_flag: print cmd + if not dry_run_flag: + run(cmd) + print + +def extract_subtitles (video_source_filename, subtitle_id=0, verbose_flag=0, dry_run_flag=0): + """This extracts the given subtitle_id track as VOBSUB format from the given source video. + """ + cmd = "mencoder -quiet '%(video_source_filename)s' -o /dev/null -nosound -ovc copy -vobsubout subtitles -vobsuboutindex 0 -sid %(subtitle_id)s" % locals() + if verbose_flag: print cmd + if not dry_run_flag: + run(cmd) + print + +def get_length (audio_raw_filename): + """This attempts to get the length of the media file (length is time in seconds). + This should not be confused with size (in bytes) of the file data. + This is best used on a raw PCM AUDIO file because mplayer cannot get an accurate + time for many compressed video and audio formats -- notably MPEG4 and MP3. + Weird... + This returns -1 if it cannot get the length of the given file. + """ + cmd = "mplayer %s -vo null -ao null -frames 0 -identify" % audio_raw_filename + (command_output, exitstatus) = run(cmd) + idl = re.findall("ID_LENGTH=([0-9.]*)", command_output) + idl.sort() + if len(idl) != 1: + print "ERROR: cannot get length of raw audio file." + print "command_output of mplayer identify:" + print command_output + print "parsed command_output:" + print str(idl) + return -1 + return float(idl[0]) + +def get_filesize (filename): + """This returns the number of bytes a file takes on storage.""" + return os.stat(filename)[stat.ST_SIZE] + +def calc_video_bitrate (video_target_size, audio_bitrate, video_length, extra_space=0, dry_run_flag=0): + """This gives an estimate of the video bitrate necessary to + fit the final target size. This will take into account room to + fit the audio and extra space if given (for container overhead or whatnot). + video_target_size is in bytes, + audio_bitrate is bits per second (96, 128, 256, etc.) ASSUMING CBR, + video_length is in seconds, + extra_space is in bytes. + a 180MB CDR (21 minutes) holds 193536000 bytes. + a 550MB CDR (63 minutes) holds 580608000 bytes. + a 650MB CDR (74 minutes) holds 681984000 bytes. + a 700MB CDR (80 minutes) holds 737280000 bytes. + """ + if dry_run_flag: + return -1 + if extra_space is None: extra_space = 0 + #audio_size = os.stat(audio_compressed_filename)[stat.ST_SIZE] + audio_size = (audio_bitrate * video_length * 1000) / 8.0 + video_target_size = video_target_size - audio_size - extra_space + return (int)(calc_video_kbitrate (video_target_size, video_length)) + +def calc_video_kbitrate (target_size, length_secs): + """Given a target byte size free for video data, this returns the bitrate in kBit/S. + For mencoder vbitrate 1 kBit = 1000 Bits -- not 1024 bits. + target_size = bitrate * 1000 * length_secs / 8 + target_size = bitrate * 125 * length_secs + bitrate = target_size/(125*length_secs) + """ + return int(target_size / (125.0 * length_secs)) + +def crop_detect (video_source_filename, video_length, dry_run_flag=0): + """This attempts to figure out the best crop for the given video file. + Basically it runs crop detect for 10 seconds on five different places in the video. + It picks the crop area that was most often detected. + """ + skip = int(video_length/9) # offset to skip (-ss option in mencoder) + sample_length = 10 + cmd1 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, skip, sample_length) + cmd2 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 2*skip, sample_length) + cmd3 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 4*skip, sample_length) + cmd4 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 6*skip, sample_length) + cmd5 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 8*skip, sample_length) + if dry_run_flag: + return "0:0:0:0" + (command_output1, exitstatus1) = run(cmd1) + (command_output2, exitstatus2) = run(cmd2) + (command_output3, exitstatus3) = run(cmd3) + (command_output4, exitstatus4) = run(cmd4) + (command_output5, exitstatus5) = run(cmd5) + idl = re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output1) + idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output2) + idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output3) + idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output4) + idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output5) + items_count = count_unique(idl) + return items_count[0][1] + + +def build_compression_command (video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None, seek_skip=None, seek_length=None, video_chapter=None): +#Notes:For DVD, VCD, and SVCD use acodec=mp2 and vcodec=mpeg2video: +#mencoder movie.avi -o movie.VOB -ovc lavc -oac lavc -lavcopts acodec=mp2:abitrate=224:vcodec=mpeg2video:vbitrate=2000 + + # + # build video filter (-vf) argument + # + video_filter = '' + if video_crop_area and video_crop_area.lower()!='none': + video_filter = video_filter + 'crop=%s' % video_crop_area + if video_deinterlace_flag: + if video_filter != '': + video_filter = video_filter + ',' + video_filter = video_filter + 'pp=md' + if video_scale and video_scale.lower()!='none': + if video_filter != '': + video_filter = video_filter + ',' + video_filter = video_filter + 'scale=%s' % video_scale + # optional video rotation -- were you holding your camera sideways? + #if video_filter != '': + # video_filter = video_filter + ',' + #video_filter = video_filter + 'rotate=2' + if video_filter != '': + video_filter = '-vf ' + video_filter + + # + # build chapter argument + # + if video_chapter is not None: + chapter = '-chapter %d-%d' %(video_chapter,video_chapter) + else: + chapter = '' +# chapter = '-chapter 2-2' + + # + # build audio_filter argument + # + audio_filter = '' + if audio_sample_rate: + if audio_filter != '': + audio_filter = audio_filter + ',' + audio_filter = audio_filter + 'lavcresample=%s' % audio_sample_rate + if audio_volume_boost is not None: + if audio_filter != '': + audio_filter = audio_filter + ',' + audio_filter = audio_filter + 'volume=%0.1f:1'%audio_volume_boost + if audio_filter != '': + audio_filter = '-af ' + audio_filter + # + #if audio_sample_rate: + # audio_filter = ('-srate %d ' % audio_sample_rate) + audio_filter + + # + # build lavcopts argument + # + #lavcopts = '-lavcopts vcodec=%s:vbitrate=%d:mbd=2:aspect=%s:acodec=%s:abitrate=%d:vpass=1' % (video_codec,video_bitrate,audio_codec,audio_bitrate) + lavcopts = '-lavcopts vcodec=%(video_codec)s:vbitrate=%(video_bitrate)d:mbd=2:aspect=%(video_aspect_ratio)s:acodec=%(audio_codec)s:abitrate=%(audio_bitrate)d:vpass=1' % (locals()) + if video_gray_flag: + lavcopts = lavcopts + ':gray' + + seek_filter = '' + if seek_skip is not None: + seek_filter = '-ss %s' % (str(seek_skip)) + if seek_length is not None: + seek_filter = seek_filter + ' -endpos %s' % (str(seek_length)) + +# cmd = "mencoder -quiet -info comment='Arkivist' '%(video_source_filename)s' %(seek_filter)s %(chapter)s -aid %(audio_id)s -o '%(video_final_filename)s' -ffourcc %(video_fourcc_override)s -ovc lavc -oac lavc %(lavcopts)s %(video_filter)s %(audio_filter)s" % locals() + cmd = "mencoder -quiet -info comment='Arkivist' '%(video_source_filename)s' %(seek_filter)s %(chapter)s -aid %(audio_id)s -o '%(video_final_filename)s' -ffourcc %(video_fourcc_override)s -ovc lavc -oac mp3lame %(lavcopts)s %(video_filter)s %(audio_filter)s" % locals() + return cmd + +def compression_estimate (video_length, video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None): + """This attempts to figure out the best compression ratio for a given set of compression options. + """ + # TODO Need to account for AVI overhead. + skip = int(video_length/9) # offset to skip (-ss option in mencoder) + sample_length = 10 + cmd1 = build_compression_command (video_source_filename, "compression_test_1.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip, sample_length) + cmd2 = build_compression_command (video_source_filename, "compression_test_2.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*2, sample_length) + cmd3 = build_compression_command (video_source_filename, "compression_test_3.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*4, sample_length) + cmd4 = build_compression_command (video_source_filename, "compression_test_4.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*6, sample_length) + cmd5 = build_compression_command (video_source_filename, "compression_test_5.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*8, sample_length) + run(cmd1) + run(cmd2) + run(cmd3) + run(cmd4) + run(cmd5) + size = get_filesize ("compression_test_1.avi")+get_filesize ("compression_test_2.avi")+get_filesize ("compression_test_3.avi")+get_filesize ("compression_test_4.avi")+get_filesize ("compression_test_5.avi") + return (size / 5.0) + +def compress_video (video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None, seek_skip=None, seek_length=None, video_chapter=None, verbose_flag=0, dry_run_flag=0): + """This compresses the video and audio of the given source video filename to the transcoded filename. + This does a two-pass compression (I'm assuming mpeg4, I should probably make this smarter for other formats). + """ + # + # do the first pass video compression + # + #cmd = "mencoder -quiet '%(video_source_filename)s' -ss 65 -endpos 20 -aid %(audio_id)s -o '%(video_final_filename)s' -ffourcc %(video_fourcc_override)s -ovc lavc -oac lavc %(lavcopts)s %(video_filter)s %(audio_filter)s" % locals() + + cmd = build_compression_command (video_source_filename, video_final_filename, video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, seek_skip, seek_length, video_chapter) + if verbose_flag: print cmd + if not dry_run_flag: + run(cmd) + print + + # If not doing two passes then return early. + if video_encode_passes!='2': + return + + if verbose_flag: + video_actual_size = get_filesize (video_final_filename) + if video_actual_size > video_target_size: + print "=======================================================" + print "WARNING!" + print "First pass compression resulted in" + print "actual file size greater than target size." + print "Second pass will be too big." + print "=======================================================" + + # + # do the second pass video compression + # + cmd = cmd.replace ('vpass=1', 'vpass=2') + if verbose_flag: print cmd + if not dry_run_flag: + run(cmd) + print + return + +def compress_audio (audio_raw_filename, audio_compressed_filename, audio_lowpass_filter=None, audio_sample_rate=None, audio_bitrate=None, verbose_flag=0, dry_run_flag=0): + """This is depricated. + This compresses the raw audio file to the compressed audio filename. + """ + cmd = 'lame -h --athaa-sensitivity 1' # --cwlimit 11" + if audio_lowpass_filter: + cmd = cmd + ' --lowpass ' + audio_lowpass_filter + if audio_bitrate: + #cmd = cmd + ' --abr ' + audio_bitrate + cmd = cmd + ' --cbr -b ' + audio_bitrate + if audio_sample_rate: + cmd = cmd + ' --resample ' + audio_sample_rate + cmd = cmd + ' ' + audio_raw_filename + ' ' + audio_compressed_filename + if verbose_flag: print cmd + if not dry_run_flag: + (command_output, exitstatus) = run(cmd) + print + if exitstatus != 0: + raise Exception('ERROR: lame failed to compress raw audio file.') + +def mux (video_final_filename, video_transcoded_filename, audio_compressed_filename, video_container_format, verbose_flag=0, dry_run_flag=0): + """This is depricated. I used to use a three-pass encoding where I would mix the audio track separately, but + this never worked very well (loss of audio sync).""" + if video_container_format.lower() == 'mkv': # Matroska + mux_mkv (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag, dry_run_flag) + if video_container_format.lower() == 'avi': + mux_avi (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag, dry_run_flag) + +def mux_mkv (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag=0, dry_run_flag=0): + """This is depricated.""" + cmd = 'mkvmerge -o %s --noaudio %s %s' % (video_final_filename, video_transcoded_filename, audio_compressed_filename) + if verbose_flag: print cmd + if not dry_run_flag: + run(cmd) + print + +def mux_avi (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag=0, dry_run_flag=0): + """This is depricated.""" + pass +# cmd = "mencoder -quiet -oac copy -ovc copy -o '%s' -audiofile %s '%s'" % (video_final_filename, audio_compressed_filename, video_transcoded_filename) +# if verbose_flag: print cmd +# if not dry_run_flag: +# run(cmd) +# print + +def delete_tmp_files (audio_raw_filename, verbose_flag=0, dry_run_flag=0): + global GLOBAL_LOGFILE_NAME + file_list = ' '.join([GLOBAL_LOGFILE_NAME, 'divx2pass.log', audio_raw_filename ]) + cmd = 'rm -f ' + file_list + if verbose_flag: print cmd + if not dry_run_flag: + run(cmd) + print + +############################################################################## +# This is the interactive Q&A that is used if a conf file was not given. +############################################################################## +def interactive_convert (): + + global prompts, prompts_key_order + + print globals()['__doc__'] + print + print "==============================================" + print " Enter '?' at any question to get extra help." + print "==============================================" + print + + # Ask for the level of options the user wants. + # A lot of code just to print a string! + level_sort = {0:'', 1:'', 2:''} + for k in prompts: + level = prompts[k][3] + if level < 0 or level > 2: + continue + level_sort[level] += " " + prompts[k][1] + "\n" + level_sort_string = "This sets the level for advanced options prompts. Set 0 for simple, 1 for advanced, or 2 for expert.\n" + level_sort_string += "[0] Basic options:\n" + str(level_sort[0]) + "\n" + level_sort_string += "[1] Advanced options:\n" + str(level_sort[1]) + "\n" + level_sort_string += "[2] Expert options:\n" + str(level_sort[2]) + c = input_option("Prompt level (0, 1, or 2)?", "1", level_sort_string) + max_prompt_level = int(c) + + options = {} + for k in prompts_key_order: + if k == 'video_aspect_ratio': + guess_aspect = get_aspect_ratio(options['video_source_filename']) + options[k] = input_option (prompts[k][1], guess_aspect, prompts[k][2], prompts[k][3], max_prompt_level) + elif k == 'audio_id': + aid_list = get_aid_list (options['video_source_filename']) + default_id = '128' + if max_prompt_level>=prompts[k][3]: + if len(aid_list) > 1: + print "This video has more than one audio stream. The following stream audio IDs were found:" + for aid in aid_list: + print " " + aid + default_id = aid_list[0] + else: + print "WARNING!" + print "Rippy was unable to get the list of audio streams from this video." + print "If reading directly from a DVD then the DVD device might be busy." + print "Using a default setting of stream id 128 (main audio on most DVDs)." + default_id = '128' + options[k] = input_option (prompts[k][1], default_id, prompts[k][2], prompts[k][3], max_prompt_level) + elif k == 'subtitle_id': + sid_list = get_sid_list (options['video_source_filename']) + default_id = 'None' + if max_prompt_level>=prompts[k][3]: + if len(sid_list) > 0: + print "This video has one or more subtitle streams. The following stream subtitle IDs were found:" + for sid in sid_list: + print " " + sid + #default_id = sid_list[0] + default_id = prompts[k][0] + else: + print "WARNING!" + print "Unable to get the list of subtitle streams from this video. It may have none." + print "Setting default to None." + default_id = 'None' + options[k] = input_option (prompts[k][1], default_id, prompts[k][2], prompts[k][3], max_prompt_level) + elif k == 'audio_lowpass_filter': + lowpass_default = "%.1f" % (math.floor(float(options['audio_sample_rate']) / 2.0)) + options[k] = input_option (prompts[k][1], lowpass_default, prompts[k][2], prompts[k][3], max_prompt_level) + elif k == 'video_bitrate': + if options['video_length'].lower() == 'none': + options[k] = input_option (prompts[k][1], '1000', prompts[k][2], prompts[k][3], max_prompt_level) + else: + options[k] = input_option (prompts[k][1], prompts[k][0], prompts[k][2], prompts[k][3], max_prompt_level) + else: + # don't bother asking for video_target_size or video_bitrate_overhead if video_bitrate was set + if (k=='video_target_size' or k=='video_bitrate_overhead') and options['video_bitrate']!='calc': + continue + # don't bother with crop area if video length is none + if k == 'video_crop_area' and options['video_length'].lower() == 'none': + options['video_crop_area'] = 'none' + continue + options[k] = input_option (prompts[k][1], prompts[k][0], prompts[k][2], prompts[k][3], max_prompt_level) + + #options['video_final_filename'] = options['video_final_filename'] + "." + options['video_container_format'] + + print "==========================================================================" + print "Ready to Rippy!" + print + print "The following options will be used:" + for k,v in options.iteritems(): + print "%27s : %s" % (k, v) + + print + c = input_option("Continue?", "Y") + c = c.strip().lower() + if c[0] != 'y': + print "Exiting..." + os._exit(1) + return options + +def clean_options (d): + """This validates and cleans up the options dictionary. + After reading options interactively or from a conf file + we need to make sure that the values make sense and are + converted to the correct type. + 1. Any key with "_flag" in it becomes a boolean True or False. + 2. Values are normalized ("No", "None", "none" all become "none"; + "Calcluate", "c", "CALC" all become "calc"). + 3. Certain values are converted from string to int. + 4. Certain combinations of options are invalid or override each other. + This is a rather annoying function, but then so it most cleanup work. + """ + for k in d: + d[k] = d[k].strip() + # convert all flag options to 0 or 1 + if '_flag' in k: + if type(d[k]) is types.StringType: + if d[k].strip().lower()[0] in 'yt1': #Yes, True, 1 + d[k] = 1 + else: + d[k] = 0 + d['video_bitrate'] = d['video_bitrate'].lower() + if d['video_bitrate'][0]=='c': + d['video_bitrate']='calc' + else: + d['video_bitrate'] = int(float(d['video_bitrate'])) + try: + d['video_target_size'] = int(d['video_target_size']) + # shorthand magic numbers get automatically expanded + if d['video_target_size'] == 180: + d['video_target_size'] = 193536000 + elif d['video_target_size'] == 550: + d['video_target_size'] = 580608000 + elif d['video_target_size'] == 650: + d['video_target_size'] = 681984000 + elif d['video_target_size'] == 700: + d['video_target_size'] = 737280000 + except: + d['video_target_size'] = 'none' + + try: + d['video_chapter'] = int(d['video_chapter']) + except: + d['video_chapter'] = None + + try: + d['subtitle_id'] = int(d['subtitle_id']) + except: + d['subtitle_id'] = None + + try: + d['video_bitrate_overhead'] = float(d['video_bitrate_overhead']) + except: + d['video_bitrate_overhead'] = -1.0 + + d['audio_bitrate'] = int(d['audio_bitrate']) + d['audio_sample_rate'] = int(d['audio_sample_rate']) + d['audio_volume_boost'] = d['audio_volume_boost'].lower() + if d['audio_volume_boost'][0]=='n': + d['audio_volume_boost'] = None + else: + d['audio_volume_boost'] = d['audio_volume_boost'].replace('db','') + d['audio_volume_boost'] = float(d['audio_volume_boost']) + +# assert (d['video_bitrate']=='calc' and d['video_target_size']!='none') +# or (d['video_bitrate']!='calc' and d['video_target_size']=='none') + + d['video_scale'] = d['video_scale'].lower() + if d['video_scale'][0]=='n': + d['video_scale']='none' + else: + al = re.findall("([0-9]+).*?([0-9]+)", d['video_scale']) + d['video_scale']=al[0][0]+':'+al[0][1] + d['video_crop_area'] = d['video_crop_area'].lower() + if d['video_crop_area'][0]=='n': + d['video_crop_area']='none' + d['video_length'] = d['video_length'].lower() + if d['video_length'][0]=='c': + d['video_length']='calc' + elif d['video_length'][0]=='n': + d['video_length']='none' + else: + d['video_length'] = int(float(d['video_length'])) + if d['video_length']==0: + d['video_length'] = 'none' + assert (not (d['video_length']=='none' and d['video_bitrate']=='calc')) + return d + +def main (): + try: + optlist, args = getopt.getopt(sys.argv[1:], 'h?', ['help','h','?']) + except Exception, e: + print str(e) + exit_with_usage() + command_line_options = dict(optlist) + # There are a million ways to cry for help. These are but a few of them. + if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]: + exit_with_usage(0) + + missing = check_missing_requirements() + if missing is not None: + print + print "==========================================================================" + print "ERROR!" + print "Some required external commands are missing." + print "please install the following packages:" + print str(missing) + print "==========================================================================" + print + c = input_option("Continue?", "Y") + c = c.strip().lower() + if c[0] != 'y': + print "Exiting..." + os._exit(1) + + if len(args) > 0: + # cute one-line string-to-dictionary parser (two-lines if you count this comment): + options = dict(re.findall('([^: \t\n]*)\s*:\s*(".*"|[^ \t\n]*)', file(args[0]).read())) + options = clean_options(options) + convert (options) + else: + options = interactive_convert () + options = clean_options(options) + convert (options) + print "# Done!" + +if __name__ == "__main__": + try: + start_time = time.time() + print time.asctime() + main() + print time.asctime() + print "TOTAL TIME IN MINUTES:", + print (time.time() - start_time) / 60.0 + except Exception, e: + tb_dump = traceback.format_exc() + print "==========================================================================" + print "ERROR -- Unexpected exception in script." + print str(e) + print str(tb_dump) + print "==========================================================================" + print >>GLOBAL_LOGFILE, "==========================================================================" + print >>GLOBAL_LOGFILE, "ERROR -- Unexpected exception in script." + print >>GLOBAL_LOGFILE, str(e) + print >>GLOBAL_LOGFILE, str(tb_dump) + print >>GLOBAL_LOGFILE, "==========================================================================" + exit_with_usage(3) + diff --git a/third_party/Python/module/pexpect-2.4/examples/script.py b/third_party/Python/module/pexpect-2.4/examples/script.py new file mode 100644 index 000000000000..908b91241a63 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/script.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +"""This spawns a sub-shell (bash) and gives the user interactive control. The +entire shell session is logged to a file called script.log. This behaves much +like the classic BSD command 'script'. + +./script.py [-a] [-c command] {logfilename} + + logfilename : This is the name of the log file. Default is script.log. + -a : Append to log file. Default is to overwrite log file. + -c : spawn command. Default is to spawn the sh shell. + +Example: + + This will start a bash shell and append to the log named my_session.log: + + ./script.py -a -c bash my_session.log + +""" + +import os, sys, time, getopt +import signal, fcntl, termios, struct +import traceback +import pexpect + +global_pexpect_instance = None # Used by signal handler + +def exit_with_usage(): + + print globals()['__doc__'] + os._exit(1) + +def main(): + + ###################################################################### + # Parse the options, arguments, get ready, etc. + ###################################################################### + try: + optlist, args = getopt.getopt(sys.argv[1:], 'h?ac:', ['help','h','?']) + except Exception, e: + print str(e) + exit_with_usage() + options = dict(optlist) + if len(args) > 1: + exit_with_usage() + + if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]: + print "Help:" + exit_with_usage() + + if len(args) == 1: + script_filename = args[0] + else: + script_filename = "script.log" + if '-a' in options: + fout = file (script_filename, "ab") + else: + fout = file (script_filename, "wb") + if '-c' in options: + command = options['-c'] + else: + command = "sh" + + # Begin log with date/time in the form CCCCyymm.hhmmss + fout.write ('# %4d%02d%02d.%02d%02d%02d \n' % time.localtime()[:-3]) + + ###################################################################### + # Start the interactive session + ###################################################################### + p = pexpect.spawn(command) + p.logfile = fout + global global_pexpect_instance + global_pexpect_instance = p + signal.signal(signal.SIGWINCH, sigwinch_passthrough) + + print "Script recording started. Type ^] (ASCII 29) to escape from the script shell." + p.interact(chr(29)) + fout.close() + return 0 + +def sigwinch_passthrough (sig, data): + + # Check for buggy platforms (see pexpect.setwinsize()). + if 'TIOCGWINSZ' in dir(termios): + TIOCGWINSZ = termios.TIOCGWINSZ + else: + TIOCGWINSZ = 1074295912 # assume + s = struct.pack ("HHHH", 0, 0, 0, 0) + a = struct.unpack ('HHHH', fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ , s)) + global global_pexpect_instance + global_pexpect_instance.setwinsize(a[0],a[1]) + +if __name__ == "__main__": + try: + main() + except SystemExit, e: + raise e + except Exception, e: + print "ERROR" + print str(e) + traceback.print_exc() + os._exit(1) + diff --git a/third_party/Python/module/pexpect-2.4/examples/ssh_session.py b/third_party/Python/module/pexpect-2.4/examples/ssh_session.py new file mode 100644 index 000000000000..4d0e228a7e33 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/ssh_session.py @@ -0,0 +1,94 @@ +# +# Eric S. Raymond +# +# Greatly modified by Nigel W. Moriarty +# April 2003 +# +from pexpect import * +import os, sys +import getpass +import time + +class ssh_session: + + "Session with extra state including the password to be used." + + def __init__(self, user, host, password=None, verbose=0): + + self.user = user + self.host = host + self.verbose = verbose + self.password = password + self.keys = [ + 'authenticity', + 'assword:', + '@@@@@@@@@@@@', + 'Command not found.', + EOF, + ] + + self.f = open('ssh.out','w') + + def __repr__(self): + + outl = 'class :'+self.__class__.__name__ + for attr in self.__dict__: + if attr == 'password': + outl += '\n\t'+attr+' : '+'*'*len(self.password) + else: + outl += '\n\t'+attr+' : '+str(getattr(self, attr)) + return outl + + def __exec(self, command): + + "Execute a command on the remote host. Return the output." + child = spawn(command, + #timeout=10, + ) + if self.verbose: + sys.stderr.write("-> " + command + "\n") + seen = child.expect(self.keys) + self.f.write(str(child.before) + str(child.after)+'\n') + if seen == 0: + child.sendline('yes') + seen = child.expect(self.keys) + if seen == 1: + if not self.password: + self.password = getpass.getpass('Remote password: ') + child.sendline(self.password) + child.readline() + time.sleep(5) + # Added to allow the background running of remote process + if not child.isalive(): + seen = child.expect(self.keys) + if seen == 2: + lines = child.readlines() + self.f.write(lines) + if self.verbose: + sys.stderr.write("<- " + child.before + "|\n") + try: + self.f.write(str(child.before) + str(child.after)+'\n') + except: + pass + self.f.close() + return child.before + + def ssh(self, command): + + return self.__exec("ssh -l %s %s \"%s\"" \ + % (self.user,self.host,command)) + + def scp(self, src, dst): + + return self.__exec("scp %s %s@%s:%s" \ + % (src, session.user, session.host, dst)) + + def exists(self, file): + + "Retrieve file permissions of specified remote file." + seen = self.ssh("/bin/ls -ld %s" % file) + if string.find(seen, "No such file") > -1: + return None # File doesn't exist + else: + return seen.split()[0] # Return permission field of listing. + diff --git a/third_party/Python/module/pexpect-2.4/examples/ssh_tunnel.py b/third_party/Python/module/pexpect-2.4/examples/ssh_tunnel.py new file mode 100644 index 000000000000..3c8bc09514b4 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/ssh_tunnel.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +"""This starts an SSH tunnel to a given host. If the SSH process ever dies then +this script will detect that and restart it. I use this under Cygwin to keep +open encrypted tunnels to port 25 (SMTP), port 143 (IMAP4), and port 110 +(POP3). I set my mail client to talk to localhost and I keep this script +running in the background. + +Note that this is a rather stupid script at the moment because it just looks to +see if any ssh process is running. It should really make sure that our specific +ssh process is running. The problem is that ssh is missing a very useful +feature. It has no way to report the process id of the background daemon that +it creates with the -f command. This would be a really useful script if I could +figure a way around this problem. """ + +import pexpect +import getpass +import time + +# SMTP:25 IMAP4:143 POP3:110 +tunnel_command = 'ssh -C -N -f -L 25:127.0.0.1:25 -L 143:127.0.0.1:143 -L 110:127.0.0.1:110 %(user)@%(host)' +host = raw_input('Hostname: ') +user = raw_input('Username: ') +X = getpass.getpass('Password: ') + +def get_process_info (): + + # This seems to work on both Linux and BSD, but should otherwise be considered highly UNportable. + + ps = pexpect.run ('ps ax -O ppid') + pass +def start_tunnel (): + try: + ssh_tunnel = pexpect.spawn (tunnel_command % globals()) + ssh_tunnel.expect ('password:') + time.sleep (0.1) + ssh_tunnel.sendline (X) + time.sleep (60) # Cygwin is slow to update process status. + ssh_tunnel.expect (pexpect.EOF) + + except Exception, e: + print str(e) + +def main (): + + while True: + ps = pexpect.spawn ('ps') + time.sleep (1) + index = ps.expect (['/usr/bin/ssh', pexpect.EOF, pexpect.TIMEOUT]) + if index == 2: + print 'TIMEOUT in ps command...' + print str(ps) + time.sleep (13) + if index == 1: + print time.asctime(), + print 'restarting tunnel' + start_tunnel () + time.sleep (11) + print 'tunnel OK' + else: + # print 'tunnel OK' + time.sleep (7) + +if __name__ == '__main__': + main () + +# This was for older SSH versions that didn't have -f option +#tunnel_command = 'ssh -C -n -L 25:%(host)s:25 -L 110:%(host)s:110 %(user)s@%(host)s -f nothing.sh' +#nothing_script = """#!/bin/sh +#while true; do sleep 53; done +#""" + diff --git a/third_party/Python/module/pexpect-2.4/examples/sshls.py b/third_party/Python/module/pexpect-2.4/examples/sshls.py new file mode 100644 index 000000000000..ef1ab9c23cd5 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/sshls.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +"""This runs 'ls -l' on a remote host using SSH. At the prompts enter hostname, +user, and password. + +$Id: sshls.py 489 2007-11-28 23:40:34Z noah $ +""" + +import pexpect +import getpass, os + +def ssh_command (user, host, password, command): + + """This runs a command on the remote host. This could also be done with the +pxssh class, but this demonstrates what that class does at a simpler level. +This returns a pexpect.spawn object. This handles the case when you try to +connect to a new host and ssh asks you if you want to accept the public key +fingerprint and continue connecting. """ + + ssh_newkey = 'Are you sure you want to continue connecting' + child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command)) + i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: ']) + if i == 0: # Timeout + print 'ERROR!' + print 'SSH could not login. Here is what SSH said:' + print child.before, child.after + return None + if i == 1: # SSH does not have the public key. Just accept it. + child.sendline ('yes') + child.expect ('password: ') + i = child.expect([pexpect.TIMEOUT, 'password: ']) + if i == 0: # Timeout + print 'ERROR!' + print 'SSH could not login. Here is what SSH said:' + print child.before, child.after + return None + child.sendline(password) + return child + +def main (): + + host = raw_input('Hostname: ') + user = raw_input('User: ') + password = getpass.getpass('Password: ') + child = ssh_command (user, host, password, '/bin/ls -l') + child.expect(pexpect.EOF) + print child.before + +if __name__ == '__main__': + try: + main() + except Exception, e: + print str(e) + traceback.print_exc() + os._exit(1) + diff --git a/third_party/Python/module/pexpect-2.4/examples/table_test.html b/third_party/Python/module/pexpect-2.4/examples/table_test.html new file mode 100644 index 000000000000..5dba0ecf0c89 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/table_test.html @@ -0,0 +1,106 @@ + + + +TEST + + + + + +
+ +
+ + + + + + + + + + + + + +
/home/noah/ 
+ + + + \ No newline at end of file diff --git a/third_party/Python/module/pexpect-2.4/examples/topip.py b/third_party/Python/module/pexpect-2.4/examples/topip.py new file mode 100644 index 000000000000..5bd63e2ef224 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/topip.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python + +""" This runs netstat on a local or remote server. It calculates some simple +statistical information on the number of external inet connections. It groups +by IP address. This can be used to detect if one IP address is taking up an +excessive number of connections. It can also send an email alert if a given IP +address exceeds a threshold between runs of the script. This script can be used +as a drop-in Munin plugin or it can be used stand-alone from cron. I used this +on a busy web server that would sometimes get hit with denial of service +attacks. This made it easy to see if a script was opening many multiple +connections. A typical browser would open fewer than 10 connections at once. A +script might open over 100 simultaneous connections. + +./topip.py [-s server_hostname] [-u username] [-p password] {-a from_addr,to_addr} {-n N} {-v} {--ipv6} + + -s : hostname of the remote server to login to. + -u : username to user for login. + -p : password to user for login. + -n : print stddev for the the number of the top 'N' ipaddresses. + -v : verbose - print stats and list of top ipaddresses. + -a : send alert if stddev goes over 20. + -l : to log message to /var/log/topip.log + --ipv6 : this parses netstat output that includes ipv6 format. + Note that this actually only works with ipv4 addresses, but for versions of + netstat that print in ipv6 format. + --stdev=N : Where N is an integer. This sets the trigger point for alerts and logs. + Default is to trigger if max value is above 5 standard deviations. + +Example: + + This will print stats for the top IP addresses connected to the given host: + + ./topip.py -s www.example.com -u mylogin -p mypassword -n 10 -v + + This will send an alert email if the maxip goes over the stddev trigger value and + the the current top ip is the same as the last top ip (/tmp/topip.last): + + ./topip.py -s www.example.com -u mylogin -p mypassword -n 10 -v -a alert@example.com,user@example.com + + This will print the connection stats for the localhost in Munin format: + + ./topip.py + +Noah Spurrier + +$Id: topip.py 489 2007-11-28 23:40:34Z noah $ +""" + +import pexpect, pxssh # See http://pexpect.sourceforge.net/ +import os, sys, time, re, getopt, pickle, getpass, smtplib +import traceback +from pprint import pprint + +TOPIP_LOG_FILE = '/var/log/topip.log' +TOPIP_LAST_RUN_STATS = '/var/run/topip.last' + +def exit_with_usage(): + + print globals()['__doc__'] + os._exit(1) + +def stats(r): + + """This returns a dict of the median, average, standard deviation, min and max of the given sequence. + + >>> from topip import stats + >>> print stats([5,6,8,9]) + {'med': 8, 'max': 9, 'avg': 7.0, 'stddev': 1.5811388300841898, 'min': 5} + >>> print stats([1000,1006,1008,1014]) + {'med': 1008, 'max': 1014, 'avg': 1007.0, 'stddev': 5.0, 'min': 1000} + >>> print stats([1,3,4,5,18,16,4,3,3,5,13]) + {'med': 4, 'max': 18, 'avg': 6.8181818181818183, 'stddev': 5.6216817577237475, 'min': 1} + >>> print stats([1,3,4,5,18,16,4,3,3,5,13,14,5,6,7,8,7,6,6,7,5,6,4,14,7]) + {'med': 6, 'max': 18, 'avg': 7.0800000000000001, 'stddev': 4.3259218670706474, 'min': 1} + """ + + total = sum(r) + avg = float(total)/float(len(r)) + sdsq = sum([(i-avg)**2 for i in r]) + s = list(r) + s.sort() + return dict(zip(['med', 'avg', 'stddev', 'min', 'max'] , (s[len(s)//2], avg, (sdsq/len(r))**.5, min(r), max(r)))) + +def send_alert (message, subject, addr_from, addr_to, smtp_server='localhost'): + + """This sends an email alert. + """ + + message = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % (addr_from, addr_to, subject) + message + server = smtplib.SMTP(smtp_server) + server.sendmail(addr_from, addr_to, message) + server.quit() + +def main(): + + ###################################################################### + ## Parse the options, arguments, etc. + ###################################################################### + try: + optlist, args = getopt.getopt(sys.argv[1:], 'h?valqs:u:p:n:', ['help','h','?','ipv6','stddev=']) + except Exception, e: + print str(e) + exit_with_usage() + options = dict(optlist) + + munin_flag = False + if len(args) > 0: + if args[0] == 'config': + print 'graph_title Netstat Connections per IP' + print 'graph_vlabel Socket connections per IP' + print 'connections_max.label max' + print 'connections_max.info Maximum number of connections per IP' + print 'connections_avg.label avg' + print 'connections_avg.info Average number of connections per IP' + print 'connections_stddev.label stddev' + print 'connections_stddev.info Standard deviation' + return 0 + elif args[0] != '': + print args, len(args) + return 0 + exit_with_usage() + if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]: + print 'Help:' + exit_with_usage() + if '-s' in options: + hostname = options['-s'] + else: + # if host was not specified then assume localhost munin plugin. + munin_flag = True + hostname = 'localhost' + # If localhost then don't ask for username/password. + if hostname != 'localhost' and hostname != '127.0.0.1': + if '-u' in options: + username = options['-u'] + else: + username = raw_input('username: ') + if '-p' in options: + password = options['-p'] + else: + password = getpass.getpass('password: ') + else: + use_localhost = True + + if '-l' in options: + log_flag = True + else: + log_flag = False + if '-n' in options: + average_n = int(options['-n']) + else: + average_n = None + if '-v' in options: + verbose = True + else: + verbose = False + if '-a' in options: + alert_flag = True + (alert_addr_from, alert_addr_to) = tuple(options['-a'].split(',')) + else: + alert_flag = False + if '--ipv6' in options: + ipv6_flag = True + else: + ipv6_flag = False + if '--stddev' in options: + stddev_trigger = float(options['--stddev']) + else: + stddev_trigger = 5 + + if ipv6_flag: + netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+::ffff:(\S+):(\S+)\s+.*?\r' + else: + netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(?:::ffff:)*(\S+):(\S+)\s+.*?\r' + #netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+):(\S+)\s+.*?\r' + + # run netstat (either locally or via SSH). + if use_localhost: + p = pexpect.spawn('netstat -n -t') + PROMPT = pexpect.TIMEOUT + else: + p = pxssh.pxssh() + p.login(hostname, username, password) + p.sendline('netstat -n -t') + PROMPT = p.PROMPT + + # loop through each matching netstat_pattern and put the ip address in the list. + ip_list = {} + try: + while 1: + i = p.expect([PROMPT, netstat_pattern]) + if i == 0: + break + k = p.match.groups()[4] + if k in ip_list: + ip_list[k] = ip_list[k] + 1 + else: + ip_list[k] = 1 + except: + pass + + # remove a few common, uninteresting addresses from the dictionary. + ip_list = dict([ (key,value) for key,value in ip_list.items() if '192.168.' not in key]) + ip_list = dict([ (key,value) for key,value in ip_list.items() if '127.0.0.1' not in key]) + + # sort dict by value (count) + #ip_list = sorted(ip_list.iteritems(),lambda x,y:cmp(x[1], y[1]),reverse=True) + ip_list = ip_list.items() + if len(ip_list) < 1: + if verbose: print 'Warning: no networks connections worth looking at.' + return 0 + ip_list.sort(lambda x,y:cmp(y[1],x[1])) + + # generate some stats for the ip addresses found. + if average_n <= 1: + average_n = None + s = stats(zip(*ip_list[0:average_n])[1]) # The * unary operator treats the list elements as arguments + s['maxip'] = ip_list[0] + + # print munin-style or verbose results for the stats. + if munin_flag: + print 'connections_max.value', s['max'] + print 'connections_avg.value', s['avg'] + print 'connections_stddev.value', s['stddev'] + return 0 + if verbose: + pprint (s) + print + pprint (ip_list[0:average_n]) + + # load the stats from the last run. + try: + last_stats = pickle.load(file(TOPIP_LAST_RUN_STATS)) + except: + last_stats = {'maxip':None} + + if s['maxip'][1] > (s['stddev'] * stddev_trigger) and s['maxip']==last_stats['maxip']: + if verbose: print 'The maxip has been above trigger for two consecutive samples.' + if alert_flag: + if verbose: print 'SENDING ALERT EMAIL' + send_alert(str(s), 'ALERT on %s' % hostname, alert_addr_from, alert_addr_to) + if log_flag: + if verbose: print 'LOGGING THIS EVENT' + fout = file(TOPIP_LOG_FILE,'a') + #dts = time.strftime('%Y:%m:%d:%H:%M:%S', time.localtime()) + dts = time.asctime() + fout.write ('%s - %d connections from %s\n' % (dts,s['maxip'][1],str(s['maxip'][0]))) + fout.close() + + # save state to TOPIP_LAST_RUN_STATS + try: + pickle.dump(s, file(TOPIP_LAST_RUN_STATS,'w')) + os.chmod (TOPIP_LAST_RUN_STATS, 0664) + except: + pass + # p.logout() + +if __name__ == '__main__': + try: + main() + sys.exit(0) + except SystemExit, e: + raise e + except Exception, e: + print str(e) + traceback.print_exc() + os._exit(1) + diff --git a/third_party/Python/module/pexpect-2.4/examples/uptime.py b/third_party/Python/module/pexpect-2.4/examples/uptime.py new file mode 100644 index 000000000000..f5018dfe0c18 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/examples/uptime.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +"""This displays uptime information using uptime. This is redundant, +but it demonstrates expecting for a regular expression that uses subgroups. + +$Id: uptime.py 489 2007-11-28 23:40:34Z noah $ +""" + +import pexpect +import re + +# There are many different styles of uptime results. I try to parse them all. Yeee! +# Examples from different machines: +# [x86] Linux 2.4 (Redhat 7.3) +# 2:06pm up 63 days, 18 min, 3 users, load average: 0.32, 0.08, 0.02 +# [x86] Linux 2.4.18-14 (Redhat 8.0) +# 3:07pm up 29 min, 1 user, load average: 2.44, 2.51, 1.57 +# [PPC - G4] MacOS X 10.1 SERVER Edition +# 2:11PM up 3 days, 13:50, 3 users, load averages: 0.01, 0.00, 0.00 +# [powerpc] Darwin v1-58.corefa.com 8.2.0 Darwin Kernel Version 8.2.0 +# 10:35 up 18:06, 4 users, load averages: 0.52 0.47 0.36 +# [Sparc - R220] Sun Solaris (8) +# 2:13pm up 22 min(s), 1 user, load average: 0.02, 0.01, 0.01 +# [x86] Linux 2.4.18-14 (Redhat 8) +# 11:36pm up 4 days, 17:58, 1 user, load average: 0.03, 0.01, 0.00 +# AIX jwdir 2 5 0001DBFA4C00 +# 09:43AM up 23:27, 1 user, load average: 0.49, 0.32, 0.23 +# OpenBSD box3 2.9 GENERIC#653 i386 +# 6:08PM up 4 days, 22:26, 1 user, load averages: 0.13, 0.09, 0.08 + +# This parses uptime output into the major groups using regex group matching. +p = pexpect.spawn ('uptime') +p.expect('up\s+(.*?),\s+([0-9]+) users?,\s+load averages?: ([0-9]+\.[0-9][0-9]),?\s+([0-9]+\.[0-9][0-9]),?\s+([0-9]+\.[0-9][0-9])') +duration, users, av1, av5, av15 = p.match.groups() + +# The duration is a little harder to parse because of all the different +# styles of uptime. I'm sure there is a way to do this all at once with +# one single regex, but I bet it would be hard to read and maintain. +# If anyone wants to send me a version using a single regex I'd be happy to see it. +days = '0' +hours = '0' +mins = '0' +if 'day' in duration: + p.match = re.search('([0-9]+)\s+day',duration) + days = str(int(p.match.group(1))) +if ':' in duration: + p.match = re.search('([0-9]+):([0-9]+)',duration) + hours = str(int(p.match.group(1))) + mins = str(int(p.match.group(2))) +if 'min' in duration: + p.match = re.search('([0-9]+)\s+min',duration) + mins = str(int(p.match.group(1))) + +# Print the parsed fields in CSV format. +print 'days, hours, minutes, users, cpu avg 1 min, cpu avg 5 min, cpu avg 15 min' +print '%s, %s, %s, %s, %s, %s, %s' % (days, hours, mins, users, av1, av5, av15) + diff --git a/third_party/Python/module/pexpect-2.4/fdpexpect.py b/third_party/Python/module/pexpect-2.4/fdpexpect.py new file mode 100644 index 000000000000..0ece98e6b941 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/fdpexpect.py @@ -0,0 +1,82 @@ +"""This is like pexpect, but will work on any file descriptor that you pass it. +So you are reponsible for opening and close the file descriptor. + +$Id: fdpexpect.py 505 2007-12-26 21:33:50Z noah $ +""" + +from pexpect import * +import os + +__all__ = ['fdspawn'] + +class fdspawn (spawn): + + """This is like pexpect.spawn but allows you to supply your own open file + descriptor. For example, you could use it to read through a file looking + for patterns, or to control a modem or serial device. """ + + def __init__ (self, fd, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None): + + """This takes a file descriptor (an int) or an object that support the + fileno() method (returning an int). All Python file-like objects + support fileno(). """ + + ### TODO: Add better handling of trying to use fdspawn in place of spawn + ### TODO: (overload to allow fdspawn to also handle commands as spawn does. + + if type(fd) != type(0) and hasattr(fd, 'fileno'): + fd = fd.fileno() + + if type(fd) != type(0): + raise ExceptionPexpect ('The fd argument is not an int. If this is a command string then maybe you want to use pexpect.spawn.') + + try: # make sure fd is a valid file descriptor + os.fstat(fd) + except OSError: + raise ExceptionPexpect, 'The fd argument is not a valid file descriptor.' + + self.args = None + self.command = None + spawn.__init__(self, None, args, timeout, maxread, searchwindowsize, logfile) + self.child_fd = fd + self.own_fd = False + self.closed = False + self.name = '' % fd + + def __del__ (self): + + return + + def close (self): + + if self.child_fd == -1: + return + if self.own_fd: + self.close (self) + else: + self.flush() + os.close(self.child_fd) + self.child_fd = -1 + self.closed = True + + def isalive (self): + + """This checks if the file descriptor is still valid. If os.fstat() + does not raise an exception then we assume it is alive. """ + + if self.child_fd == -1: + return False + try: + os.fstat(self.child_fd) + return True + except: + return False + + def terminate (self, force=False): + + raise ExceptionPexpect ('This method is not valid for file descriptors.') + + def kill (self, sig): + + return + diff --git a/third_party/Python/module/pexpect-2.4/pexpect.py b/third_party/Python/module/pexpect-2.4/pexpect.py new file mode 100644 index 000000000000..0bb0a84c1415 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/pexpect.py @@ -0,0 +1,1844 @@ +"""Pexpect is a Python module for spawning child applications and controlling +them automatically. Pexpect can be used for automating interactive applications +such as ssh, ftp, passwd, telnet, etc. It can be used to a automate setup +scripts for duplicating software package installations on different servers. It +can be used for automated software testing. Pexpect is in the spirit of Don +Libes' Expect, but Pexpect is pure Python. Other Expect-like modules for Python +require TCL and Expect or require C extensions to be compiled. Pexpect does not +use C, Expect, or TCL extensions. It should work on any platform that supports +the standard Python pty module. The Pexpect interface focuses on ease of use so +that simple tasks are easy. + +There are two main interfaces to Pexpect -- the function, run() and the class, +spawn. You can call the run() function to execute a command and return the +output. This is a handy replacement for os.system(). + +For example:: + + pexpect.run('ls -la') + +The more powerful interface is the spawn class. You can use this to spawn an +external child command and then interact with the child by sending lines and +expecting responses. + +For example:: + + child = pexpect.spawn('scp foo myname@host.example.com:.') + child.expect ('Password:') + child.sendline (mypassword) + +This works even for commands that ask for passwords or other input outside of +the normal stdio streams. + +Credits: Noah Spurrier, Richard Holden, Marco Molteni, Kimberley Burchett, +Robert Stone, Hartmut Goebel, Chad Schroeder, Erick Tryzelaar, Dave Kirby, Ids +vander Molen, George Todd, Noel Taylor, Nicolas D. Cesar, Alexander Gattin, +Jacques-Etienne Baudoux, Geoffrey Marshall, Francisco Lourenco, Glen Mabey, +Karthik Gurusamy, Fernando Perez, Corey Minyard, Jon Cohen, Guillaume +Chazarain, Andrew Ryan, Nick Craig-Wood, Andrew Stone, Jorgen Grahn, John +Spiegel, Jan Grant (Let me know if I forgot anyone.) + +Free, open source, and all that good stuff. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Pexpect Copyright (c) 2008 Noah Spurrier +http://pexpect.sourceforge.net/ + +$Id: pexpect.py 516 2008-05-23 20:46:01Z noah $ +""" + +try: + import os, sys, time + import select + import string + import re + import struct + import resource + import types + import pty + import tty + import termios + import fcntl + import errno + import traceback + import signal +except ImportError, e: + raise ImportError (str(e) + """ + +A critical module was not found. Probably this operating system does not +support it. Pexpect is intended for UNIX-like operating systems.""") + +__version__ = '2.4' +__revision__ = '$Revision: 516 $' +__all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'run', 'which', + 'split_command_line', '__version__', '__revision__'] + +# Exception classes used by this module. +class ExceptionPexpect(Exception): + + """Base class for all exceptions raised by this module. + """ + + def __init__(self, value): + + self.value = value + + def __str__(self): + + return str(self.value) + + def get_trace(self): + + """This returns an abbreviated stack trace with lines that only concern + the caller. In other words, the stack trace inside the Pexpect module + is not included. """ + + tblist = traceback.extract_tb(sys.exc_info()[2]) + #tblist = filter(self.__filter_not_pexpect, tblist) + tblist = [item for item in tblist if self.__filter_not_pexpect(item)] + tblist = traceback.format_list(tblist) + return ''.join(tblist) + + def __filter_not_pexpect(self, trace_list_item): + + """This returns True if list item 0 the string 'pexpect.py' in it. """ + + if trace_list_item[0].find('pexpect.py') == -1: + return True + else: + return False + +class EOF(ExceptionPexpect): + + """Raised when EOF is read from a child. This usually means the child has exited.""" + +class TIMEOUT(ExceptionPexpect): + + """Raised when a read time exceeds the timeout. """ + +##class TIMEOUT_PATTERN(TIMEOUT): +## """Raised when the pattern match time exceeds the timeout. +## This is different than a read TIMEOUT because the child process may +## give output, thus never give a TIMEOUT, but the output +## may never match a pattern. +## """ +##class MAXBUFFER(ExceptionPexpect): +## """Raised when a scan buffer fills before matching an expected pattern.""" + +def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None): + + """ + This function runs the given command; waits for it to finish; then + returns all output as a string. STDERR is included in output. If the full + path to the command is not given then the path is searched. + + Note that lines are terminated by CR/LF (\\r\\n) combination even on + UNIX-like systems because this is the standard for pseudo ttys. If you set + 'withexitstatus' to true, then run will return a tuple of (command_output, + exitstatus). If 'withexitstatus' is false then this returns just + command_output. + + The run() function can often be used instead of creating a spawn instance. + For example, the following code uses spawn:: + + from pexpect import * + child = spawn('scp foo myname@host.example.com:.') + child.expect ('(?i)password') + child.sendline (mypassword) + + The previous code can be replace with the following:: + + from pexpect import * + run ('scp foo myname@host.example.com:.', events={'(?i)password': mypassword}) + + Examples + ======== + + Start the apache daemon on the local machine:: + + from pexpect import * + run ("/usr/local/apache/bin/apachectl start") + + Check in a file using SVN:: + + from pexpect import * + run ("svn ci -m 'automatic commit' my_file.py") + + Run a command and capture exit status:: + + from pexpect import * + (command_output, exitstatus) = run ('ls -l /bin', withexitstatus=1) + + Tricky Examples + =============== + + The following will run SSH and execute 'ls -l' on the remote machine. The + password 'secret' will be sent if the '(?i)password' pattern is ever seen:: + + run ("ssh username@machine.example.com 'ls -l'", events={'(?i)password':'secret\\n'}) + + This will start mencoder to rip a video from DVD. This will also display + progress ticks every 5 seconds as it runs. For example:: + + from pexpect import * + def print_ticks(d): + print d['event_count'], + run ("mencoder dvd://1 -o video.avi -oac copy -ovc copy", events={TIMEOUT:print_ticks}, timeout=5) + + The 'events' argument should be a dictionary of patterns and responses. + Whenever one of the patterns is seen in the command out run() will send the + associated response string. Note that you should put newlines in your + string if Enter is necessary. The responses may also contain callback + functions. Any callback is function that takes a dictionary as an argument. + The dictionary contains all the locals from the run() function, so you can + access the child spawn object or any other variable defined in run() + (event_count, child, and extra_args are the most useful). A callback may + return True to stop the current run process otherwise run() continues until + the next event. A callback may also return a string which will be sent to + the child. 'extra_args' is not used by directly run(). It provides a way to + pass data to a callback function through run() through the locals + dictionary passed to a callback. """ + + if timeout == -1: + child = spawn(command, maxread=2000, logfile=logfile, cwd=cwd, env=env) + else: + child = spawn(command, timeout=timeout, maxread=2000, logfile=logfile, cwd=cwd, env=env) + if events is not None: + patterns = events.keys() + responses = events.values() + else: + patterns=None # We assume that EOF or TIMEOUT will save us. + responses=None + child_result_list = [] + event_count = 0 + while 1: + try: + index = child.expect (patterns) + if type(child.after) in types.StringTypes: + child_result_list.append(child.before + child.after) + else: # child.after may have been a TIMEOUT or EOF, so don't cat those. + child_result_list.append(child.before) + if type(responses[index]) in types.StringTypes: + child.send(responses[index]) + elif type(responses[index]) is types.FunctionType: + callback_result = responses[index](locals()) + sys.stdout.flush() + if type(callback_result) in types.StringTypes: + child.send(callback_result) + elif callback_result: + break + else: + raise TypeError ('The callback must be a string or function type.') + event_count = event_count + 1 + except TIMEOUT, e: + child_result_list.append(child.before) + break + except EOF, e: + child_result_list.append(child.before) + break + child_result = ''.join(child_result_list) + if withexitstatus: + child.close() + return (child_result, child.exitstatus) + else: + return child_result + +class spawn (object): + + """This is the main class interface for Pexpect. Use this class to start + and control child applications. """ + + def __init__(self, command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None): + + """This is the constructor. The command parameter may be a string that + includes a command and any arguments to the command. For example:: + + child = pexpect.spawn ('/usr/bin/ftp') + child = pexpect.spawn ('/usr/bin/ssh user@example.com') + child = pexpect.spawn ('ls -latr /tmp') + + You may also construct it with a list of arguments like so:: + + child = pexpect.spawn ('/usr/bin/ftp', []) + child = pexpect.spawn ('/usr/bin/ssh', ['user@example.com']) + child = pexpect.spawn ('ls', ['-latr', '/tmp']) + + After this the child application will be created and will be ready to + talk to. For normal use, see expect() and send() and sendline(). + + Remember that Pexpect does NOT interpret shell meta characters such as + redirect, pipe, or wild cards (>, |, or *). This is a common mistake. + If you want to run a command and pipe it through another command then + you must also start a shell. For example:: + + child = pexpect.spawn('/bin/bash -c "ls -l | grep LOG > log_list.txt"') + child.expect(pexpect.EOF) + + The second form of spawn (where you pass a list of arguments) is useful + in situations where you wish to spawn a command and pass it its own + argument list. This can make syntax more clear. For example, the + following is equivalent to the previous example:: + + shell_cmd = 'ls -l | grep LOG > log_list.txt' + child = pexpect.spawn('/bin/bash', ['-c', shell_cmd]) + child.expect(pexpect.EOF) + + The maxread attribute sets the read buffer size. This is maximum number + of bytes that Pexpect will try to read from a TTY at one time. Setting + the maxread size to 1 will turn off buffering. Setting the maxread + value higher may help performance in cases where large amounts of + output are read back from the child. This feature is useful in + conjunction with searchwindowsize. + + The searchwindowsize attribute sets the how far back in the incomming + seach buffer Pexpect will search for pattern matches. Every time + Pexpect reads some data from the child it will append the data to the + incomming buffer. The default is to search from the beginning of the + imcomming buffer each time new data is read from the child. But this is + very inefficient if you are running a command that generates a large + amount of data where you want to match The searchwindowsize does not + effect the size of the incomming data buffer. You will still have + access to the full buffer after expect() returns. + + The logfile member turns on or off logging. All input and output will + be copied to the given file object. Set logfile to None to stop + logging. This is the default. Set logfile to sys.stdout to echo + everything to standard output. The logfile is flushed after each write. + + Example log input and output to a file:: + + child = pexpect.spawn('some_command') + fout = file('mylog.txt','w') + child.logfile = fout + + Example log to stdout:: + + child = pexpect.spawn('some_command') + child.logfile = sys.stdout + + The logfile_read and logfile_send members can be used to separately log + the input from the child and output sent to the child. Sometimes you + don't want to see everything you write to the child. You only want to + log what the child sends back. For example:: + + child = pexpect.spawn('some_command') + child.logfile_read = sys.stdout + + To separately log output sent to the child use logfile_send:: + + self.logfile_send = fout + + The delaybeforesend helps overcome a weird behavior that many users + were experiencing. The typical problem was that a user would expect() a + "Password:" prompt and then immediately call sendline() to send the + password. The user would then see that their password was echoed back + to them. Passwords don't normally echo. The problem is caused by the + fact that most applications print out the "Password" prompt and then + turn off stdin echo, but if you send your password before the + application turned off echo, then you get your password echoed. + Normally this wouldn't be a problem when interacting with a human at a + real keyboard. If you introduce a slight delay just before writing then + this seems to clear up the problem. This was such a common problem for + many users that I decided that the default pexpect behavior should be + to sleep just before writing to the child application. 1/20th of a + second (50 ms) seems to be enough to clear up the problem. You can set + delaybeforesend to 0 to return to the old behavior. Most Linux machines + don't like this to be below 0.03. I don't know why. + + Note that spawn is clever about finding commands on your path. + It uses the same logic that "which" uses to find executables. + + If you wish to get the exit status of the child you must call the + close() method. The exit or signal status of the child will be stored + in self.exitstatus or self.signalstatus. If the child exited normally + then exitstatus will store the exit return code and signalstatus will + be None. If the child was terminated abnormally with a signal then + signalstatus will store the signal value and exitstatus will be None. + If you need more detail you can also read the self.status member which + stores the status returned by os.waitpid. You can interpret this using + os.WIFEXITED/os.WEXITSTATUS or os.WIFSIGNALED/os.TERMSIG. """ + + self.STDIN_FILENO = pty.STDIN_FILENO + self.STDOUT_FILENO = pty.STDOUT_FILENO + self.STDERR_FILENO = pty.STDERR_FILENO + self.stdin = sys.stdin + self.stdout = sys.stdout + self.stderr = sys.stderr + + self.searcher = None + self.ignorecase = False + self.before = None + self.after = None + self.match = None + self.match_index = None + self.terminated = True + self.exitstatus = None + self.signalstatus = None + self.status = None # status returned by os.waitpid + self.flag_eof = False + self.pid = None + self.child_fd = -1 # initially closed + self.timeout = timeout + self.delimiter = EOF + self.logfile = logfile + self.logfile_read = None # input from child (read_nonblocking) + self.logfile_send = None # output to send (send, sendline) + self.maxread = maxread # max bytes to read at one time into buffer + self.buffer = '' # This is the read buffer. See maxread. + self.searchwindowsize = searchwindowsize # Anything before searchwindowsize point is preserved, but not searched. + # Most Linux machines don't like delaybeforesend to be below 0.03 (30 ms). + self.delaybeforesend = 0.05 # Sets sleep time used just before sending data to child. Time in seconds. + self.delayafterclose = 0.1 # Sets delay in close() method to allow kernel time to update process status. Time in seconds. + self.delayafterterminate = 0.1 # Sets delay in terminate() method to allow kernel time to update process status. Time in seconds. + self.softspace = False # File-like object. + self.name = '<' + repr(self) + '>' # File-like object. + self.encoding = None # File-like object. + self.closed = True # File-like object. + self.cwd = cwd + self.env = env + self.__irix_hack = (sys.platform.lower().find('irix')>=0) # This flags if we are running on irix + # Solaris uses internal __fork_pty(). All others use pty.fork(). + if (sys.platform.lower().find('solaris')>=0) or (sys.platform.lower().find('sunos5')>=0): + self.use_native_pty_fork = False + else: + self.use_native_pty_fork = True + + + # allow dummy instances for subclasses that may not use command or args. + if command is None: + self.command = None + self.args = None + self.name = '' + else: + self._spawn (command, args) + + def __del__(self): + + """This makes sure that no system resources are left open. Python only + garbage collects Python objects. OS file descriptors are not Python + objects, so they must be handled explicitly. If the child file + descriptor was opened outside of this class (passed to the constructor) + then this does not close it. """ + + if not self.closed: + # It is possible for __del__ methods to execute during the + # teardown of the Python VM itself. Thus self.close() may + # trigger an exception because os.close may be None. + # -- Fernando Perez + try: + self.close() + except: + pass + + def __str__(self): + + """This returns a human-readable string that represents the state of + the object. """ + + s = [] + s.append(repr(self)) + s.append('version: ' + __version__ + ' (' + __revision__ + ')') + s.append('command: ' + str(self.command)) + s.append('args: ' + str(self.args)) + s.append('searcher: ' + str(self.searcher)) + s.append('buffer (last 100 chars): ' + str(self.buffer)[-100:]) + s.append('before (last 100 chars): ' + str(self.before)[-100:]) + s.append('after: ' + str(self.after)) + s.append('match: ' + str(self.match)) + s.append('match_index: ' + str(self.match_index)) + s.append('exitstatus: ' + str(self.exitstatus)) + s.append('flag_eof: ' + str(self.flag_eof)) + s.append('pid: ' + str(self.pid)) + s.append('child_fd: ' + str(self.child_fd)) + s.append('closed: ' + str(self.closed)) + s.append('timeout: ' + str(self.timeout)) + s.append('delimiter: ' + str(self.delimiter)) + s.append('logfile: ' + str(self.logfile)) + s.append('logfile_read: ' + str(self.logfile_read)) + s.append('logfile_send: ' + str(self.logfile_send)) + s.append('maxread: ' + str(self.maxread)) + s.append('ignorecase: ' + str(self.ignorecase)) + s.append('searchwindowsize: ' + str(self.searchwindowsize)) + s.append('delaybeforesend: ' + str(self.delaybeforesend)) + s.append('delayafterclose: ' + str(self.delayafterclose)) + s.append('delayafterterminate: ' + str(self.delayafterterminate)) + return '\n'.join(s) + + def _spawn(self,command,args=[]): + + """This starts the given command in a child process. This does all the + fork/exec type of stuff for a pty. This is called by __init__. If args + is empty then command will be parsed (split on spaces) and args will be + set to parsed arguments. """ + + # The pid and child_fd of this object get set by this method. + # Note that it is difficult for this method to fail. + # You cannot detect if the child process cannot start. + # So the only way you can tell if the child process started + # or not is to try to read from the file descriptor. If you get + # EOF immediately then it means that the child is already dead. + # That may not necessarily be bad because you may haved spawned a child + # that performs some task; creates no stdout output; and then dies. + + # If command is an int type then it may represent a file descriptor. + if type(command) == type(0): + raise ExceptionPexpect ('Command is an int type. If this is a file descriptor then maybe you want to use fdpexpect.fdspawn which takes an existing file descriptor instead of a command string.') + + if type (args) != type([]): + raise TypeError ('The argument, args, must be a list.') + + if args == []: + self.args = split_command_line(command) + self.command = self.args[0] + else: + self.args = args[:] # work with a copy + self.args.insert (0, command) + self.command = command + + command_with_path = which(self.command) + if command_with_path is None: + raise ExceptionPexpect ('The command was not found or was not executable: %s.' % self.command) + self.command = command_with_path + self.args[0] = self.command + + self.name = '<' + ' '.join (self.args) + '>' + + assert self.pid is None, 'The pid member should be None.' + assert self.command is not None, 'The command member should not be None.' + + if self.use_native_pty_fork: + try: + self.pid, self.child_fd = pty.fork() + except OSError, e: + raise ExceptionPexpect('Error! pty.fork() failed: ' + str(e)) + else: # Use internal __fork_pty + self.pid, self.child_fd = self.__fork_pty() + + if self.pid == 0: # Child + try: + self.child_fd = sys.stdout.fileno() # used by setwinsize() + self.setwinsize(24, 80) + except: + # Some platforms do not like setwinsize (Cygwin). + # This will cause problem when running applications that + # are very picky about window size. + # This is a serious limitation, but not a show stopper. + pass + # Do not allow child to inherit open file descriptors from parent. + max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0] + for i in range (3, max_fd): + try: + os.close (i) + except OSError: + pass + + # I don't know why this works, but ignoring SIGHUP fixes a + # problem when trying to start a Java daemon with sudo + # (specifically, Tomcat). + signal.signal(signal.SIGHUP, signal.SIG_IGN) + + if self.cwd is not None: + os.chdir(self.cwd) + if self.env is None: + os.execv(self.command, self.args) + else: + os.execvpe(self.command, self.args, self.env) + + # Parent + self.terminated = False + self.closed = False + + def __fork_pty(self): + + """This implements a substitute for the forkpty system call. This + should be more portable than the pty.fork() function. Specifically, + this should work on Solaris. + + Modified 10.06.05 by Geoff Marshall: Implemented __fork_pty() method to + resolve the issue with Python's pty.fork() not supporting Solaris, + particularly ssh. Based on patch to posixmodule.c authored by Noah + Spurrier:: + + http://mail.python.org/pipermail/python-dev/2003-May/035281.html + + """ + + parent_fd, child_fd = os.openpty() + if parent_fd < 0 or child_fd < 0: + raise ExceptionPexpect, "Error! Could not open pty with os.openpty()." + + pid = os.fork() + if pid < 0: + raise ExceptionPexpect, "Error! Failed os.fork()." + elif pid == 0: + # Child. + os.close(parent_fd) + self.__pty_make_controlling_tty(child_fd) + + os.dup2(child_fd, 0) + os.dup2(child_fd, 1) + os.dup2(child_fd, 2) + + if child_fd > 2: + os.close(child_fd) + else: + # Parent. + os.close(child_fd) + + return pid, parent_fd + + def __pty_make_controlling_tty(self, tty_fd): + + """This makes the pseudo-terminal the controlling tty. This should be + more portable than the pty.fork() function. Specifically, this should + work on Solaris. """ + + child_name = os.ttyname(tty_fd) + + # Disconnect from controlling tty if still connected. + try: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY); + if fd >= 0: + os.close(fd) + except: + # We are already disconnected. Perhaps we are running inside cron. + pass + + os.setsid() + + # Verify we are disconnected from controlling tty + try: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY); + if fd >= 0: + os.close(fd) + raise ExceptionPexpect, "Error! We are not disconnected from a controlling tty." + except: + # Good! We are disconnected from a controlling tty. + pass + + # Verify we can open child pty. + fd = os.open(child_name, os.O_RDWR); + if fd < 0: + raise ExceptionPexpect, "Error! Could not open child pty, " + child_name + else: + os.close(fd) + + # Verify we now have a controlling tty. + fd = os.open("/dev/tty", os.O_WRONLY) + if fd < 0: + raise ExceptionPexpect, "Error! Could not open controlling tty, /dev/tty" + else: + os.close(fd) + + def fileno (self): # File-like object. + + """This returns the file descriptor of the pty for the child. + """ + + return self.child_fd + + def close (self, force=True): # File-like object. + + """This closes the connection with the child application. Note that + calling close() more than once is valid. This emulates standard Python + behavior with files. Set force to True if you want to make sure that + the child is terminated (SIGKILL is sent if the child ignores SIGHUP + and SIGINT). """ + + if not self.closed: + self.flush() + os.close (self.child_fd) + time.sleep(self.delayafterclose) # Give kernel time to update process status. + if self.isalive(): + if not self.terminate(force): + raise ExceptionPexpect ('close() could not terminate the child using terminate()') + self.child_fd = -1 + self.closed = True + #self.pid = None + + def flush (self): # File-like object. + + """This does nothing. It is here to support the interface for a + File-like object. """ + + pass + + def isatty (self): # File-like object. + + """This returns True if the file descriptor is open and connected to a + tty(-like) device, else False. """ + + return os.isatty(self.child_fd) + + def waitnoecho (self, timeout=-1): + + """This waits until the terminal ECHO flag is set False. This returns + True if the echo mode is off. This returns False if the ECHO flag was + not set False before the timeout. This can be used to detect when the + child is waiting for a password. Usually a child application will turn + off echo mode when it is waiting for the user to enter a password. For + example, instead of expecting the "password:" prompt you can wait for + the child to set ECHO off:: + + p = pexpect.spawn ('ssh user@example.com') + p.waitnoecho() + p.sendline(mypassword) + + If timeout is None then this method to block forever until ECHO flag is + False. + + """ + + if timeout == -1: + timeout = self.timeout + if timeout is not None: + end_time = time.time() + timeout + while True: + if not self.getecho(): + return True + if timeout < 0 and timeout is not None: + return False + if timeout is not None: + timeout = end_time - time.time() + time.sleep(0.1) + + def getecho (self): + + """This returns the terminal echo mode. This returns True if echo is + on or False if echo is off. Child applications that are expecting you + to enter a password often set ECHO False. See waitnoecho(). """ + + attr = termios.tcgetattr(self.child_fd) + if attr[3] & termios.ECHO: + return True + return False + + def setecho (self, state): + + """This sets the terminal echo mode on or off. Note that anything the + child sent before the echo will be lost, so you should be sure that + your input buffer is empty before you call setecho(). For example, the + following will work as expected:: + + p = pexpect.spawn('cat') + p.sendline ('1234') # We will see this twice (once from tty echo and again from cat). + p.expect (['1234']) + p.expect (['1234']) + p.setecho(False) # Turn off tty echo + p.sendline ('abcd') # We will set this only once (echoed by cat). + p.sendline ('wxyz') # We will set this only once (echoed by cat) + p.expect (['abcd']) + p.expect (['wxyz']) + + The following WILL NOT WORK because the lines sent before the setecho + will be lost:: + + p = pexpect.spawn('cat') + p.sendline ('1234') # We will see this twice (once from tty echo and again from cat). + p.setecho(False) # Turn off tty echo + p.sendline ('abcd') # We will set this only once (echoed by cat). + p.sendline ('wxyz') # We will set this only once (echoed by cat) + p.expect (['1234']) + p.expect (['1234']) + p.expect (['abcd']) + p.expect (['wxyz']) + """ + + self.child_fd + attr = termios.tcgetattr(self.child_fd) + if state: + attr[3] = attr[3] | termios.ECHO + else: + attr[3] = attr[3] & ~termios.ECHO + # I tried TCSADRAIN and TCSAFLUSH, but these were inconsistent + # and blocked on some platforms. TCSADRAIN is probably ideal if it worked. + termios.tcsetattr(self.child_fd, termios.TCSANOW, attr) + + def read_nonblocking (self, size = 1, timeout = -1): + + """This reads at most size characters from the child application. It + includes a timeout. If the read does not complete within the timeout + period then a TIMEOUT exception is raised. If the end of file is read + then an EOF exception will be raised. If a log file was set using + setlog() then all data will also be written to the log file. + + If timeout is None then the read may block indefinitely. If timeout is -1 + then the self.timeout value is used. If timeout is 0 then the child is + polled and if there was no data immediately ready then this will raise + a TIMEOUT exception. + + The timeout refers only to the amount of time to read at least one + character. This is not effected by the 'size' parameter, so if you call + read_nonblocking(size=100, timeout=30) and only one character is + available right away then one character will be returned immediately. + It will not wait for 30 seconds for another 99 characters to come in. + + This is a wrapper around os.read(). It uses select.select() to + implement the timeout. """ + + if self.closed: + raise ValueError ('I/O operation on closed file in read_nonblocking().') + + if timeout == -1: + timeout = self.timeout + + # Note that some systems such as Solaris do not give an EOF when + # the child dies. In fact, you can still try to read + # from the child_fd -- it will block forever or until TIMEOUT. + # For this case, I test isalive() before doing any reading. + # If isalive() is false, then I pretend that this is the same as EOF. + if not self.isalive(): + r,w,e = self.__select([self.child_fd], [], [], 0) # timeout of 0 means "poll" + if not r: + self.flag_eof = True + raise EOF ('End Of File (EOF) in read_nonblocking(). Braindead platform.') + elif self.__irix_hack: + # This is a hack for Irix. It seems that Irix requires a long delay before checking isalive. + # This adds a 2 second delay, but only when the child is terminated. + r, w, e = self.__select([self.child_fd], [], [], 2) + if not r and not self.isalive(): + self.flag_eof = True + raise EOF ('End Of File (EOF) in read_nonblocking(). Pokey platform.') + + r,w,e = self.__select([self.child_fd], [], [], timeout) + + if not r: + if not self.isalive(): + # Some platforms, such as Irix, will claim that their processes are alive; + # then timeout on the select; and then finally admit that they are not alive. + self.flag_eof = True + raise EOF ('End of File (EOF) in read_nonblocking(). Very pokey platform.') + else: + raise TIMEOUT ('Timeout exceeded in read_nonblocking().') + + if self.child_fd in r: + try: + s = os.read(self.child_fd, size) + except OSError, e: # Linux does this + self.flag_eof = True + raise EOF ('End Of File (EOF) in read_nonblocking(). Exception style platform.') + if s == '': # BSD style + self.flag_eof = True + raise EOF ('End Of File (EOF) in read_nonblocking(). Empty string style platform.') + + if self.logfile is not None: + self.logfile.write (s) + self.logfile.flush() + if self.logfile_read is not None: + self.logfile_read.write (s) + self.logfile_read.flush() + + return s + + raise ExceptionPexpect ('Reached an unexpected state in read_nonblocking().') + + def read (self, size = -1): # File-like object. + + """This reads at most "size" bytes from the file (less if the read hits + EOF before obtaining size bytes). If the size argument is negative or + omitted, read all data until EOF is reached. The bytes are returned as + a string object. An empty string is returned when EOF is encountered + immediately. """ + + if size == 0: + return '' + if size < 0: + self.expect (self.delimiter) # delimiter default is EOF + return self.before + + # I could have done this more directly by not using expect(), but + # I deliberately decided to couple read() to expect() so that + # I would catch any bugs early and ensure consistant behavior. + # It's a little less efficient, but there is less for me to + # worry about if I have to later modify read() or expect(). + # Note, it's OK if size==-1 in the regex. That just means it + # will never match anything in which case we stop only on EOF. + cre = re.compile('.{%d}' % size, re.DOTALL) + index = self.expect ([cre, self.delimiter]) # delimiter default is EOF + if index == 0: + return self.after ### self.before should be ''. Should I assert this? + return self.before + + def readline (self, size = -1): # File-like object. + + """This reads and returns one entire line. A trailing newline is kept + in the string, but may be absent when a file ends with an incomplete + line. Note: This readline() looks for a \\r\\n pair even on UNIX + because this is what the pseudo tty device returns. So contrary to what + you may expect you will receive the newline as \\r\\n. An empty string + is returned when EOF is hit immediately. Currently, the size argument is + mostly ignored, so this behavior is not standard for a file-like + object. If size is 0 then an empty string is returned. """ + + if size == 0: + return '' + index = self.expect (['\r\n', self.delimiter]) # delimiter default is EOF + if index == 0: + return self.before + '\r\n' + else: + return self.before + + def __iter__ (self): # File-like object. + + """This is to support iterators over a file-like object. + """ + + return self + + def next (self): # File-like object. + + """This is to support iterators over a file-like object. + """ + + result = self.readline() + if result == "": + raise StopIteration + return result + + def readlines (self, sizehint = -1): # File-like object. + + """This reads until EOF using readline() and returns a list containing + the lines thus read. The optional "sizehint" argument is ignored. """ + + lines = [] + while True: + line = self.readline() + if not line: + break + lines.append(line) + return lines + + def write(self, s): # File-like object. + + """This is similar to send() except that there is no return value. + """ + + self.send (s) + + def writelines (self, sequence): # File-like object. + + """This calls write() for each element in the sequence. The sequence + can be any iterable object producing strings, typically a list of + strings. This does not add line separators There is no return value. + """ + + for s in sequence: + self.write (s) + + def send(self, s): + + """This sends a string to the child process. This returns the number of + bytes written. If a log file was set then the data is also written to + the log. """ + + time.sleep(self.delaybeforesend) + if self.logfile is not None: + self.logfile.write (s) + self.logfile.flush() + if self.logfile_send is not None: + self.logfile_send.write (s) + self.logfile_send.flush() + c = os.write(self.child_fd, s) + return c + + def sendline(self, s=''): + + """This is like send(), but it adds a line feed (os.linesep). This + returns the number of bytes written. """ + + n = self.send(s) + n = n + self.send (os.linesep) + return n + + def sendcontrol(self, char): + + """This sends a control character to the child such as Ctrl-C or + Ctrl-D. For example, to send a Ctrl-G (ASCII 7):: + + child.sendcontrol('g') + + See also, sendintr() and sendeof(). + """ + + char = char.lower() + a = ord(char) + if a>=97 and a<=122: + a = a - ord('a') + 1 + return self.send (chr(a)) + d = {'@':0, '`':0, + '[':27, '{':27, + '\\':28, '|':28, + ']':29, '}': 29, + '^':30, '~':30, + '_':31, + '?':127} + if char not in d: + return 0 + return self.send (chr(d[char])) + + def sendeof(self): + + """This sends an EOF to the child. This sends a character which causes + the pending parent output buffer to be sent to the waiting child + program without waiting for end-of-line. If it is the first character + of the line, the read() in the user program returns 0, which signifies + end-of-file. This means to work as expected a sendeof() has to be + called at the beginning of a line. This method does not send a newline. + It is the responsibility of the caller to ensure the eof is sent at the + beginning of a line. """ + + ### Hmmm... how do I send an EOF? + ###C if ((m = write(pty, *buf, p - *buf)) < 0) + ###C return (errno == EWOULDBLOCK) ? n : -1; + #fd = sys.stdin.fileno() + #old = termios.tcgetattr(fd) # remember current state + #attr = termios.tcgetattr(fd) + #attr[3] = attr[3] | termios.ICANON # ICANON must be set to recognize EOF + #try: # use try/finally to ensure state gets restored + # termios.tcsetattr(fd, termios.TCSADRAIN, attr) + # if hasattr(termios, 'CEOF'): + # os.write (self.child_fd, '%c' % termios.CEOF) + # else: + # # Silly platform does not define CEOF so assume CTRL-D + # os.write (self.child_fd, '%c' % 4) + #finally: # restore state + # termios.tcsetattr(fd, termios.TCSADRAIN, old) + if hasattr(termios, 'VEOF'): + char = termios.tcgetattr(self.child_fd)[6][termios.VEOF] + else: + # platform does not define VEOF so assume CTRL-D + char = chr(4) + self.send(char) + + def sendintr(self): + + """This sends a SIGINT to the child. It does not require + the SIGINT to be the first character on a line. """ + + if hasattr(termios, 'VINTR'): + char = termios.tcgetattr(self.child_fd)[6][termios.VINTR] + else: + # platform does not define VINTR so assume CTRL-C + char = chr(3) + self.send (char) + + def eof (self): + + """This returns True if the EOF exception was ever raised. + """ + + return self.flag_eof + + def terminate(self, force=False): + + """This forces a child process to terminate. It starts nicely with + SIGHUP and SIGINT. If "force" is True then moves onto SIGKILL. This + returns True if the child was terminated. This returns False if the + child could not be terminated. """ + + if not self.isalive(): + return True + try: + self.kill(signal.SIGHUP) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + self.kill(signal.SIGCONT) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + self.kill(signal.SIGINT) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + if force: + self.kill(signal.SIGKILL) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + else: + return False + return False + except OSError, e: + # I think there are kernel timing issues that sometimes cause + # this to happen. I think isalive() reports True, but the + # process is dead to the kernel. + # Make one last attempt to see if the kernel is up to date. + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + else: + return False + + def wait(self): + + """This waits until the child exits. This is a blocking call. This will + not read any data from the child, so this will block forever if the + child has unread output and has terminated. In other words, the child + may have printed output then called exit(); but, technically, the child + is still alive until its output is read. """ + + if self.isalive(): + pid, status = os.waitpid(self.pid, 0) + else: + raise ExceptionPexpect ('Cannot wait for dead child process.') + self.exitstatus = os.WEXITSTATUS(status) + if os.WIFEXITED (status): + self.status = status + self.exitstatus = os.WEXITSTATUS(status) + self.signalstatus = None + self.terminated = True + elif os.WIFSIGNALED (status): + self.status = status + self.exitstatus = None + self.signalstatus = os.WTERMSIG(status) + self.terminated = True + elif os.WIFSTOPPED (status): + raise ExceptionPexpect ('Wait was called for a child process that is stopped. This is not supported. Is some other process attempting job control with our child pid?') + return self.exitstatus + + def isalive(self): + + """This tests if the child process is running or not. This is + non-blocking. If the child was terminated then this will read the + exitstatus or signalstatus of the child. This returns True if the child + process appears to be running or False if not. It can take literally + SECONDS for Solaris to return the right status. """ + + if self.terminated: + return False + + if self.flag_eof: + # This is for Linux, which requires the blocking form of waitpid to get + # status of a defunct process. This is super-lame. The flag_eof would have + # been set in read_nonblocking(), so this should be safe. + waitpid_options = 0 + else: + waitpid_options = os.WNOHANG + + try: + pid, status = os.waitpid(self.pid, waitpid_options) + except OSError, e: # No child processes + if e[0] == errno.ECHILD: + raise ExceptionPexpect ('isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?') + else: + raise e + + # I have to do this twice for Solaris. I can't even believe that I figured this out... + # If waitpid() returns 0 it means that no child process wishes to + # report, and the value of status is undefined. + if pid == 0: + try: + pid, status = os.waitpid(self.pid, waitpid_options) ### os.WNOHANG) # Solaris! + except OSError, e: # This should never happen... + if e[0] == errno.ECHILD: + raise ExceptionPexpect ('isalive() encountered condition that should never happen. There was no child process. Did someone else call waitpid() on our process?') + else: + raise e + + # If pid is still 0 after two calls to waitpid() then + # the process really is alive. This seems to work on all platforms, except + # for Irix which seems to require a blocking call on waitpid or select, so I let read_nonblocking + # take care of this situation (unfortunately, this requires waiting through the timeout). + if pid == 0: + return True + + if pid == 0: + return True + + if os.WIFEXITED (status): + self.status = status + self.exitstatus = os.WEXITSTATUS(status) + self.signalstatus = None + self.terminated = True + elif os.WIFSIGNALED (status): + self.status = status + self.exitstatus = None + self.signalstatus = os.WTERMSIG(status) + self.terminated = True + elif os.WIFSTOPPED (status): + raise ExceptionPexpect ('isalive() encountered condition where child process is stopped. This is not supported. Is some other process attempting job control with our child pid?') + return False + + def kill(self, sig): + + """This sends the given signal to the child application. In keeping + with UNIX tradition it has a misleading name. It does not necessarily + kill the child unless you send the right signal. """ + + # Same as os.kill, but the pid is given for you. + if self.isalive(): + os.kill(self.pid, sig) + + def compile_pattern_list(self, patterns): + + """This compiles a pattern-string or a list of pattern-strings. + Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or a list of + those. Patterns may also be None which results in an empty list (you + might do this if waiting for an EOF or TIMEOUT condition without + expecting any pattern). + + This is used by expect() when calling expect_list(). Thus expect() is + nothing more than:: + + cpl = self.compile_pattern_list(pl) + return self.expect_list(cpl, timeout) + + If you are using expect() within a loop it may be more + efficient to compile the patterns first and then call expect_list(). + This avoid calls in a loop to compile_pattern_list():: + + cpl = self.compile_pattern_list(my_pattern) + while some_condition: + ... + i = self.expect_list(clp, timeout) + ... + """ + + if patterns is None: + return [] + if type(patterns) is not types.ListType: + patterns = [patterns] + + compile_flags = re.DOTALL # Allow dot to match \n + if self.ignorecase: + compile_flags = compile_flags | re.IGNORECASE + compiled_pattern_list = [] + for p in patterns: + if type(p) in types.StringTypes: + compiled_pattern_list.append(re.compile(p, compile_flags)) + elif p is EOF: + compiled_pattern_list.append(EOF) + elif p is TIMEOUT: + compiled_pattern_list.append(TIMEOUT) + elif type(p) is type(re.compile('')): + compiled_pattern_list.append(p) + else: + raise TypeError ('Argument must be one of StringTypes, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p))) + + return compiled_pattern_list + + def expect(self, pattern, timeout = -1, searchwindowsize=-1): + + """This seeks through the stream until a pattern is matched. The + pattern is overloaded and may take several types. The pattern can be a + StringType, EOF, a compiled re, or a list of any of those types. + Strings will be compiled to re types. This returns the index into the + pattern list. If the pattern was not a list this returns index 0 on a + successful match. This may raise exceptions for EOF or TIMEOUT. To + avoid the EOF or TIMEOUT exceptions add EOF or TIMEOUT to the pattern + list. That will cause expect to match an EOF or TIMEOUT condition + instead of raising an exception. + + If you pass a list of patterns and more than one matches, the first match + in the stream is chosen. If more than one pattern matches at that point, + the leftmost in the pattern list is chosen. For example:: + + # the input is 'foobar' + index = p.expect (['bar', 'foo', 'foobar']) + # returns 1 ('foo') even though 'foobar' is a "better" match + + Please note, however, that buffering can affect this behavior, since + input arrives in unpredictable chunks. For example:: + + # the input is 'foobar' + index = p.expect (['foobar', 'foo']) + # returns 0 ('foobar') if all input is available at once, + # but returs 1 ('foo') if parts of the final 'bar' arrive late + + After a match is found the instance attributes 'before', 'after' and + 'match' will be set. You can see all the data read before the match in + 'before'. You can see the data that was matched in 'after'. The + re.MatchObject used in the re match will be in 'match'. If an error + occurred then 'before' will be set to all the data read so far and + 'after' and 'match' will be None. + + If timeout is -1 then timeout will be set to the self.timeout value. + + A list entry may be EOF or TIMEOUT instead of a string. This will + catch these exceptions and return the index of the list entry instead + of raising the exception. The attribute 'after' will be set to the + exception type. The attribute 'match' will be None. This allows you to + write code like this:: + + index = p.expect (['good', 'bad', pexpect.EOF, pexpect.TIMEOUT]) + if index == 0: + do_something() + elif index == 1: + do_something_else() + elif index == 2: + do_some_other_thing() + elif index == 3: + do_something_completely_different() + + instead of code like this:: + + try: + index = p.expect (['good', 'bad']) + if index == 0: + do_something() + elif index == 1: + do_something_else() + except EOF: + do_some_other_thing() + except TIMEOUT: + do_something_completely_different() + + These two forms are equivalent. It all depends on what you want. You + can also just expect the EOF if you are waiting for all output of a + child to finish. For example:: + + p = pexpect.spawn('/bin/ls') + p.expect (pexpect.EOF) + print p.before + + If you are trying to optimize for speed then see expect_list(). + """ + + compiled_pattern_list = self.compile_pattern_list(pattern) + return self.expect_list(compiled_pattern_list, timeout, searchwindowsize) + + def expect_list(self, pattern_list, timeout = -1, searchwindowsize = -1): + + """This takes a list of compiled regular expressions and returns the + index into the pattern_list that matched the child output. The list may + also contain EOF or TIMEOUT (which are not compiled regular + expressions). This method is similar to the expect() method except that + expect_list() does not recompile the pattern list on every call. This + may help if you are trying to optimize for speed, otherwise just use + the expect() method. This is called by expect(). If timeout==-1 then + the self.timeout value is used. If searchwindowsize==-1 then the + self.searchwindowsize value is used. """ + + return self.expect_loop(searcher_re(pattern_list), timeout, searchwindowsize) + + def expect_exact(self, pattern_list, timeout = -1, searchwindowsize = -1): + + """This is similar to expect(), but uses plain string matching instead + of compiled regular expressions in 'pattern_list'. The 'pattern_list' + may be a string; a list or other sequence of strings; or TIMEOUT and + EOF. + + This call might be faster than expect() for two reasons: string + searching is faster than RE matching and it is possible to limit the + search to just the end of the input buffer. + + This method is also useful when you don't want to have to worry about + escaping regular expression characters that you want to match.""" + + if type(pattern_list) in types.StringTypes or pattern_list in (TIMEOUT, EOF): + pattern_list = [pattern_list] + return self.expect_loop(searcher_string(pattern_list), timeout, searchwindowsize) + + def expect_loop(self, searcher, timeout = -1, searchwindowsize = -1): + + """This is the common loop used inside expect. The 'searcher' should be + an instance of searcher_re or searcher_string, which describes how and what + to search for in the input. + + See expect() for other arguments, return value and exceptions. """ + + self.searcher = searcher + + if timeout == -1: + timeout = self.timeout + if timeout is not None: + end_time = time.time() + timeout + if searchwindowsize == -1: + searchwindowsize = self.searchwindowsize + + try: + incoming = self.buffer + freshlen = len(incoming) + while True: # Keep reading until exception or return. + index = searcher.search(incoming, freshlen, searchwindowsize) + if index >= 0: + self.buffer = incoming[searcher.end : ] + self.before = incoming[ : searcher.start] + self.after = incoming[searcher.start : searcher.end] + self.match = searcher.match + self.match_index = index + return self.match_index + # No match at this point + if timeout < 0 and timeout is not None: + raise TIMEOUT ('Timeout exceeded in expect_any().') + # Still have time left, so read more data + c = self.read_nonblocking (self.maxread, timeout) + freshlen = len(c) + time.sleep (0.0001) + incoming = incoming + c + if timeout is not None: + timeout = end_time - time.time() + except EOF, e: + self.buffer = '' + self.before = incoming + self.after = EOF + index = searcher.eof_index + if index >= 0: + self.match = EOF + self.match_index = index + return self.match_index + else: + self.match = None + self.match_index = None + raise EOF (str(e) + '\n' + str(self)) + except TIMEOUT, e: + self.buffer = incoming + self.before = incoming + self.after = TIMEOUT + index = searcher.timeout_index + if index >= 0: + self.match = TIMEOUT + self.match_index = index + return self.match_index + else: + self.match = None + self.match_index = None + raise TIMEOUT (str(e) + '\n' + str(self)) + except: + self.before = incoming + self.after = None + self.match = None + self.match_index = None + raise + + def getwinsize(self): + + """This returns the terminal window size of the child tty. The return + value is a tuple of (rows, cols). """ + + TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912L) + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(self.fileno(), TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] + + def setwinsize(self, r, c): + + """This sets the terminal window size of the child tty. This will cause + a SIGWINCH signal to be sent to the child. This does not change the + physical window size. It changes the size reported to TTY-aware + applications like vi or curses -- applications that respond to the + SIGWINCH signal. """ + + # Some very old platforms have a bug that causes the value for + # termios.TIOCSWINSZ to be truncated. There was a hack here to work + # around this, but it caused problems with newer platforms so has been + # removed. For details see https://github.com/pexpect/pexpect/issues/39 + TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561) + # Note, assume ws_xpixel and ws_ypixel are zero. + s = struct.pack('HHHH', r, c, 0, 0) + fcntl.ioctl(self.fileno(), TIOCSWINSZ, s) + + def interact(self, escape_character = chr(29), input_filter = None, output_filter = None): + + """This gives control of the child process to the interactive user (the + human at the keyboard). Keystrokes are sent to the child process, and + the stdout and stderr output of the child process is printed. This + simply echos the child stdout and child stderr to the real stdout and + it echos the real stdin to the child stdin. When the user types the + escape_character this method will stop. The default for + escape_character is ^]. This should not be confused with ASCII 27 -- + the ESC character. ASCII 29 was chosen for historical merit because + this is the character used by 'telnet' as the escape character. The + escape_character will not be sent to the child process. + + You may pass in optional input and output filter functions. These + functions should take a string and return a string. The output_filter + will be passed all the output from the child process. The input_filter + will be passed all the keyboard input from the user. The input_filter + is run BEFORE the check for the escape_character. + + Note that if you change the window size of the parent the SIGWINCH + signal will not be passed through to the child. If you want the child + window size to change when the parent's window size changes then do + something like the following example:: + + import pexpect, struct, fcntl, termios, signal, sys + def sigwinch_passthrough (sig, data): + s = struct.pack("HHHH", 0, 0, 0, 0) + a = struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ , s)) + global p + p.setwinsize(a[0],a[1]) + p = pexpect.spawn('/bin/bash') # Note this is global and used in sigwinch_passthrough. + signal.signal(signal.SIGWINCH, sigwinch_passthrough) + p.interact() + """ + + # Flush the buffer. + self.stdout.write (self.buffer) + self.stdout.flush() + self.buffer = '' + mode = tty.tcgetattr(self.STDIN_FILENO) + tty.setraw(self.STDIN_FILENO) + try: + self.__interact_copy(escape_character, input_filter, output_filter) + finally: + tty.tcsetattr(self.STDIN_FILENO, tty.TCSAFLUSH, mode) + + def __interact_writen(self, fd, data): + + """This is used by the interact() method. + """ + + while data != '' and self.isalive(): + n = os.write(fd, data) + data = data[n:] + + def __interact_read(self, fd): + + """This is used by the interact() method. + """ + + return os.read(fd, 1000) + + def __interact_copy(self, escape_character = None, input_filter = None, output_filter = None): + + """This is used by the interact() method. + """ + + while self.isalive(): + r,w,e = self.__select([self.child_fd, self.STDIN_FILENO], [], []) + if self.child_fd in r: + data = self.__interact_read(self.child_fd) + if output_filter: data = output_filter(data) + if self.logfile is not None: + self.logfile.write (data) + self.logfile.flush() + os.write(self.STDOUT_FILENO, data) + if self.STDIN_FILENO in r: + data = self.__interact_read(self.STDIN_FILENO) + if input_filter: data = input_filter(data) + i = data.rfind(escape_character) + if i != -1: + data = data[:i] + self.__interact_writen(self.child_fd, data) + break + self.__interact_writen(self.child_fd, data) + + def __select (self, iwtd, owtd, ewtd, timeout=None): + + """This is a wrapper around select.select() that ignores signals. If + select.select raises a select.error exception and errno is an EINTR + error then it is ignored. Mainly this is used to ignore sigwinch + (terminal resize). """ + + # if select() is interrupted by a signal (errno==EINTR) then + # we loop back and enter the select() again. + if timeout is not None: + end_time = time.time() + timeout + while True: + try: + return select.select (iwtd, owtd, ewtd, timeout) + except select.error, e: + if e[0] == errno.EINTR: + # if we loop back we have to subtract the amount of time we already waited. + if timeout is not None: + timeout = end_time - time.time() + if timeout < 0: + return ([],[],[]) + else: # something else caused the select.error, so this really is an exception + raise + +############################################################################## +# The following methods are no longer supported or allowed. + + def setmaxread (self, maxread): + + """This method is no longer supported or allowed. I don't like getters + and setters without a good reason. """ + + raise ExceptionPexpect ('This method is no longer supported or allowed. Just assign a value to the maxread member variable.') + + def setlog (self, fileobject): + + """This method is no longer supported or allowed. + """ + + raise ExceptionPexpect ('This method is no longer supported or allowed. Just assign a value to the logfile member variable.') + +############################################################################## +# End of spawn class +############################################################################## + +class searcher_string (object): + + """This is a plain string search helper for the spawn.expect_any() method. + + Attributes: + + eof_index - index of EOF, or -1 + timeout_index - index of TIMEOUT, or -1 + + After a successful match by the search() method the following attributes + are available: + + start - index into the buffer, first byte of match + end - index into the buffer, first byte after match + match - the matching string itself + """ + + def __init__(self, strings): + + """This creates an instance of searcher_string. This argument 'strings' + may be a list; a sequence of strings; or the EOF or TIMEOUT types. """ + + self.eof_index = -1 + self.timeout_index = -1 + self._strings = [] + for n, s in zip(range(len(strings)), strings): + if s is EOF: + self.eof_index = n + continue + if s is TIMEOUT: + self.timeout_index = n + continue + self._strings.append((n, s)) + + def __str__(self): + + """This returns a human-readable string that represents the state of + the object.""" + + ss = [ (ns[0],' %d: "%s"' % ns) for ns in self._strings ] + ss.append((-1,'searcher_string:')) + if self.eof_index >= 0: + ss.append ((self.eof_index,' %d: EOF' % self.eof_index)) + if self.timeout_index >= 0: + ss.append ((self.timeout_index,' %d: TIMEOUT' % self.timeout_index)) + ss.sort() + ss = zip(*ss)[1] + return '\n'.join(ss) + + def search(self, buffer, freshlen, searchwindowsize=None): + + """This searches 'buffer' for the first occurence of one of the search + strings. 'freshlen' must indicate the number of bytes at the end of + 'buffer' which have not been searched before. It helps to avoid + searching the same, possibly big, buffer over and over again. + + See class spawn for the 'searchwindowsize' argument. + + If there is a match this returns the index of that string, and sets + 'start', 'end' and 'match'. Otherwise, this returns -1. """ + + absurd_match = len(buffer) + first_match = absurd_match + + # 'freshlen' helps a lot here. Further optimizations could + # possibly include: + # + # using something like the Boyer-Moore Fast String Searching + # Algorithm; pre-compiling the search through a list of + # strings into something that can scan the input once to + # search for all N strings; realize that if we search for + # ['bar', 'baz'] and the input is '...foo' we need not bother + # rescanning until we've read three more bytes. + # + # Sadly, I don't know enough about this interesting topic. /grahn + + for index, s in self._strings: + if searchwindowsize is None: + # the match, if any, can only be in the fresh data, + # or at the very end of the old data + offset = -(freshlen+len(s)) + else: + # better obey searchwindowsize + offset = -searchwindowsize + n = buffer.find(s, offset) + if n >= 0 and n < first_match: + first_match = n + best_index, best_match = index, s + if first_match == absurd_match: + return -1 + self.match = best_match + self.start = first_match + self.end = self.start + len(self.match) + return best_index + +class searcher_re (object): + + """This is regular expression string search helper for the + spawn.expect_any() method. + + Attributes: + + eof_index - index of EOF, or -1 + timeout_index - index of TIMEOUT, or -1 + + After a successful match by the search() method the following attributes + are available: + + start - index into the buffer, first byte of match + end - index into the buffer, first byte after match + match - the re.match object returned by a succesful re.search + + """ + + def __init__(self, patterns): + + """This creates an instance that searches for 'patterns' Where + 'patterns' may be a list or other sequence of compiled regular + expressions, or the EOF or TIMEOUT types.""" + + self.eof_index = -1 + self.timeout_index = -1 + self._searches = [] + for n, s in zip(range(len(patterns)), patterns): + if s is EOF: + self.eof_index = n + continue + if s is TIMEOUT: + self.timeout_index = n + continue + self._searches.append((n, s)) + + def __str__(self): + + """This returns a human-readable string that represents the state of + the object.""" + + ss = [ (n,' %d: re.compile("%s")' % (n,str(s.pattern))) for n,s in self._searches] + ss.append((-1,'searcher_re:')) + if self.eof_index >= 0: + ss.append ((self.eof_index,' %d: EOF' % self.eof_index)) + if self.timeout_index >= 0: + ss.append ((self.timeout_index,' %d: TIMEOUT' % self.timeout_index)) + ss.sort() + ss = zip(*ss)[1] + return '\n'.join(ss) + + def search(self, buffer, freshlen, searchwindowsize=None): + + """This searches 'buffer' for the first occurence of one of the regular + expressions. 'freshlen' must indicate the number of bytes at the end of + 'buffer' which have not been searched before. + + See class spawn for the 'searchwindowsize' argument. + + If there is a match this returns the index of that string, and sets + 'start', 'end' and 'match'. Otherwise, returns -1.""" + + absurd_match = len(buffer) + first_match = absurd_match + # 'freshlen' doesn't help here -- we cannot predict the + # length of a match, and the re module provides no help. + if searchwindowsize is None: + searchstart = 0 + else: + searchstart = max(0, len(buffer)-searchwindowsize) + for index, s in self._searches: + match = s.search(buffer, searchstart) + if match is None: + continue + n = match.start() + if n < first_match: + first_match = n + the_match = match + best_index = index + if first_match == absurd_match: + return -1 + self.start = first_match + self.match = the_match + self.end = self.match.end() + return best_index + +def which (filename): + + """This takes a given filename; tries to find it in the environment path; + then checks if it is executable. This returns the full path to the filename + if found and executable. Otherwise this returns None.""" + + # Special case where filename already contains a path. + if os.path.dirname(filename) != '': + if os.access (filename, os.X_OK): + return filename + + if not os.environ.has_key('PATH') or os.environ['PATH'] == '': + p = os.defpath + else: + p = os.environ['PATH'] + + # Oddly enough this was the one line that made Pexpect + # incompatible with Python 1.5.2. + #pathlist = p.split (os.pathsep) + pathlist = string.split (p, os.pathsep) + + for path in pathlist: + f = os.path.join(path, filename) + if os.access(f, os.X_OK): + return f + return None + +def split_command_line(command_line): + + """This splits a command line into a list of arguments. It splits arguments + on spaces, but handles embedded quotes, doublequotes, and escaped + characters. It's impossible to do this with a regular expression, so I + wrote a little state machine to parse the command line. """ + + arg_list = [] + arg = '' + + # Constants to name the states we can be in. + state_basic = 0 + state_esc = 1 + state_singlequote = 2 + state_doublequote = 3 + state_whitespace = 4 # The state of consuming whitespace between commands. + state = state_basic + + for c in command_line: + if state == state_basic or state == state_whitespace: + if c == '\\': # Escape the next character + state = state_esc + elif c == r"'": # Handle single quote + state = state_singlequote + elif c == r'"': # Handle double quote + state = state_doublequote + elif c.isspace(): + # Add arg to arg_list if we aren't in the middle of whitespace. + if state == state_whitespace: + None # Do nothing. + else: + arg_list.append(arg) + arg = '' + state = state_whitespace + else: + arg = arg + c + state = state_basic + elif state == state_esc: + arg = arg + c + state = state_basic + elif state == state_singlequote: + if c == r"'": + state = state_basic + else: + arg = arg + c + elif state == state_doublequote: + if c == r'"': + state = state_basic + else: + arg = arg + c + + if arg != '': + arg_list.append(arg) + return arg_list + +# vi:ts=4:sw=4:expandtab:ft=python: diff --git a/third_party/Python/module/pexpect-2.4/pxssh.py b/third_party/Python/module/pexpect-2.4/pxssh.py new file mode 100644 index 000000000000..04ba25cbff5b --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/pxssh.py @@ -0,0 +1,311 @@ +"""This class extends pexpect.spawn to specialize setting up SSH connections. +This adds methods for login, logout, and expecting the shell prompt. + +$Id: pxssh.py 513 2008-02-09 18:26:13Z noah $ +""" + +from pexpect import * +import pexpect +import time + +__all__ = ['ExceptionPxssh', 'pxssh'] + +# Exception classes used by this module. +class ExceptionPxssh(ExceptionPexpect): + """Raised for pxssh exceptions. + """ + +class pxssh (spawn): + + """This class extends pexpect.spawn to specialize setting up SSH + connections. This adds methods for login, logout, and expecting the shell + prompt. It does various tricky things to handle many situations in the SSH + login process. For example, if the session is your first login, then pxssh + automatically accepts the remote certificate; or if you have public key + authentication setup then pxssh won't wait for the password prompt. + + pxssh uses the shell prompt to synchronize output from the remote host. In + order to make this more robust it sets the shell prompt to something more + unique than just $ or #. This should work on most Borne/Bash or Csh style + shells. + + Example that runs a few commands on a remote server and prints the result:: + + import pxssh + import getpass + try: + s = pxssh.pxssh() + hostname = raw_input('hostname: ') + username = raw_input('username: ') + password = getpass.getpass('password: ') + s.login (hostname, username, password) + s.sendline ('uptime') # run a command + s.prompt() # match the prompt + print s.before # print everything before the prompt. + s.sendline ('ls -l') + s.prompt() + print s.before + s.sendline ('df') + s.prompt() + print s.before + s.logout() + except pxssh.ExceptionPxssh, e: + print "pxssh failed on login." + print str(e) + + Note that if you have ssh-agent running while doing development with pxssh + then this can lead to a lot of confusion. Many X display managers (xdm, + gdm, kdm, etc.) will automatically start a GUI agent. You may see a GUI + dialog box popup asking for a password during development. You should turn + off any key agents during testing. The 'force_password' attribute will turn + off public key authentication. This will only work if the remote SSH server + is configured to allow password logins. Example of using 'force_password' + attribute:: + + s = pxssh.pxssh() + s.force_password = True + hostname = raw_input('hostname: ') + username = raw_input('username: ') + password = getpass.getpass('password: ') + s.login (hostname, username, password) + """ + + def __init__ (self, timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None): + spawn.__init__(self, None, timeout=timeout, maxread=maxread, searchwindowsize=searchwindowsize, logfile=logfile, cwd=cwd, env=env) + + self.name = '' + + #SUBTLE HACK ALERT! Note that the command to set the prompt uses a + #slightly different string than the regular expression to match it. This + #is because when you set the prompt the command will echo back, but we + #don't want to match the echoed command. So if we make the set command + #slightly different than the regex we eliminate the problem. To make the + #set command different we add a backslash in front of $. The $ doesn't + #need to be escaped, but it doesn't hurt and serves to make the set + #prompt command different than the regex. + + # used to match the command-line prompt + self.UNIQUE_PROMPT = "\[PEXPECT\][\$\#] " + self.PROMPT = self.UNIQUE_PROMPT + + # used to set shell command-line prompt to UNIQUE_PROMPT. + self.PROMPT_SET_SH = "PS1='[PEXPECT]\$ '" + self.PROMPT_SET_CSH = "set prompt='[PEXPECT]\$ '" + self.SSH_OPTS = "-o'RSAAuthentication=no' -o 'PubkeyAuthentication=no'" + # Disabling X11 forwarding gets rid of the annoying SSH_ASKPASS from + # displaying a GUI password dialog. I have not figured out how to + # disable only SSH_ASKPASS without also disabling X11 forwarding. + # Unsetting SSH_ASKPASS on the remote side doesn't disable it! Annoying! + #self.SSH_OPTS = "-x -o'RSAAuthentication=no' -o 'PubkeyAuthentication=no'" + self.force_password = False + self.auto_prompt_reset = True + + def levenshtein_distance(self, a,b): + + """This calculates the Levenshtein distance between a and b. + """ + + n, m = len(a), len(b) + if n > m: + a,b = b,a + n,m = m,n + current = range(n+1) + for i in range(1,m+1): + previous, current = current, [i]+[0]*n + for j in range(1,n+1): + add, delete = previous[j]+1, current[j-1]+1 + change = previous[j-1] + if a[j-1] != b[i-1]: + change = change + 1 + current[j] = min(add, delete, change) + return current[n] + + def sync_original_prompt (self): + + """This attempts to find the prompt. Basically, press enter and record + the response; press enter again and record the response; if the two + responses are similar then assume we are at the original prompt. This + is a slow function. It can take over 10 seconds. """ + + # All of these timing pace values are magic. + # I came up with these based on what seemed reliable for + # connecting to a heavily loaded machine I have. + # If latency is worse than these values then this will fail. + + try: + self.read_nonblocking(size=10000,timeout=1) # GAS: Clear out the cache before getting the prompt + except TIMEOUT: + pass + time.sleep(0.1) + self.sendline() + time.sleep(0.5) + x = self.read_nonblocking(size=1000,timeout=1) + time.sleep(0.1) + self.sendline() + time.sleep(0.5) + a = self.read_nonblocking(size=1000,timeout=1) + time.sleep(0.1) + self.sendline() + time.sleep(0.5) + b = self.read_nonblocking(size=1000,timeout=1) + ld = self.levenshtein_distance(a,b) + len_a = len(a) + if len_a == 0: + return False + if float(ld)/len_a < 0.4: + return True + return False + + ### TODO: This is getting messy and I'm pretty sure this isn't perfect. + ### TODO: I need to draw a flow chart for this. + def login (self,server,username,password='',terminal_type='ansi',original_prompt=r"[#$]",login_timeout=10,port=None,auto_prompt_reset=True): + + """This logs the user into the given server. It uses the + 'original_prompt' to try to find the prompt right after login. When it + finds the prompt it immediately tries to reset the prompt to something + more easily matched. The default 'original_prompt' is very optimistic + and is easily fooled. It's more reliable to try to match the original + prompt as exactly as possible to prevent false matches by server + strings such as the "Message Of The Day". On many systems you can + disable the MOTD on the remote server by creating a zero-length file + called "~/.hushlogin" on the remote server. If a prompt cannot be found + then this will not necessarily cause the login to fail. In the case of + a timeout when looking for the prompt we assume that the original + prompt was so weird that we could not match it, so we use a few tricks + to guess when we have reached the prompt. Then we hope for the best and + blindly try to reset the prompt to something more unique. If that fails + then login() raises an ExceptionPxssh exception. + + In some situations it is not possible or desirable to reset the + original prompt. In this case, set 'auto_prompt_reset' to False to + inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh + uses a unique prompt in the prompt() method. If the original prompt is + not reset then this will disable the prompt() method unless you + manually set the PROMPT attribute. """ + + ssh_options = '-q' + if self.force_password: + ssh_options = ssh_options + ' ' + self.SSH_OPTS + if port is not None: + ssh_options = ssh_options + ' -p %s'%(str(port)) + cmd = "ssh %s -l %s %s" % (ssh_options, username, server) + + # This does not distinguish between a remote server 'password' prompt + # and a local ssh 'passphrase' prompt (for unlocking a private key). + spawn._spawn(self, cmd) + i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT, "(?i)connection closed by remote host"], timeout=login_timeout) + + # First phase + if i==0: + # New certificate -- always accept it. + # This is what you get if SSH does not have the remote host's + # public key stored in the 'known_hosts' cache. + self.sendline("yes") + i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) + if i==2: # password or passphrase + self.sendline(password) + i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) + if i==4: + self.sendline(terminal_type) + i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) + + # Second phase + if i==0: + # This is weird. This should not happen twice in a row. + self.close() + raise ExceptionPxssh ('Weird error. Got "are you sure" prompt twice.') + elif i==1: # can occur if you have a public key pair set to authenticate. + ### TODO: May NOT be OK if expect() got tricked and matched a false prompt. + pass + elif i==2: # password prompt again + # For incorrect passwords, some ssh servers will + # ask for the password again, others return 'denied' right away. + # If we get the password prompt again then this means + # we didn't get the password right the first time. + self.close() + raise ExceptionPxssh ('password refused') + elif i==3: # permission denied -- password was bad. + self.close() + raise ExceptionPxssh ('permission denied') + elif i==4: # terminal type again? WTF? + self.close() + raise ExceptionPxssh ('Weird error. Got "terminal type" prompt twice.') + elif i==5: # Timeout + #This is tricky... I presume that we are at the command-line prompt. + #It may be that the shell prompt was so weird that we couldn't match + #it. Or it may be that we couldn't log in for some other reason. I + #can't be sure, but it's safe to guess that we did login because if + #I presume wrong and we are not logged in then this should be caught + #later when I try to set the shell prompt. + pass + elif i==6: # Connection closed by remote host + self.close() + raise ExceptionPxssh ('connection closed') + else: # Unexpected + self.close() + raise ExceptionPxssh ('unexpected login response') + if not self.sync_original_prompt(): + self.close() + raise ExceptionPxssh ('could not synchronize with original prompt') + # We appear to be in. + # set shell prompt to something unique. + if auto_prompt_reset: + if not self.set_unique_prompt(): + self.close() + raise ExceptionPxssh ('could not set shell prompt\n'+self.before) + return True + + def logout (self): + + """This sends exit to the remote shell. If there are stopped jobs then + this automatically sends exit twice. """ + + self.sendline("exit") + index = self.expect([EOF, "(?i)there are stopped jobs"]) + if index==1: + self.sendline("exit") + self.expect(EOF) + self.close() + + def prompt (self, timeout=20): + + """This matches the shell prompt. This is little more than a short-cut + to the expect() method. This returns True if the shell prompt was + matched. This returns False if there was a timeout. Note that if you + called login() with auto_prompt_reset set to False then you should have + manually set the PROMPT attribute to a regex pattern for matching the + prompt. """ + + i = self.expect([self.PROMPT, TIMEOUT], timeout=timeout) + if i==1: + return False + return True + + def set_unique_prompt (self): + + """This sets the remote prompt to something more unique than # or $. + This makes it easier for the prompt() method to match the shell prompt + unambiguously. This method is called automatically by the login() + method, but you may want to call it manually if you somehow reset the + shell prompt. For example, if you 'su' to a different user then you + will need to manually reset the prompt. This sends shell commands to + the remote host to set the prompt, so this assumes the remote host is + ready to receive commands. + + Alternatively, you may use your own prompt pattern. Just set the PROMPT + attribute to a regular expression that matches it. In this case you + should call login() with auto_prompt_reset=False; then set the PROMPT + attribute. After that the prompt() method will try to match your prompt + pattern.""" + + self.sendline ("unset PROMPT_COMMAND") + self.sendline (self.PROMPT_SET_SH) # sh-style + i = self.expect ([TIMEOUT, self.PROMPT], timeout=10) + if i == 0: # csh-style + self.sendline (self.PROMPT_SET_CSH) + i = self.expect ([TIMEOUT, self.PROMPT], timeout=10) + if i == 0: + return False + return True + +# vi:ts=4:sw=4:expandtab:ft=python: diff --git a/third_party/Python/module/pexpect-2.4/screen.py b/third_party/Python/module/pexpect-2.4/screen.py new file mode 100644 index 000000000000..13699f93b5b5 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/screen.py @@ -0,0 +1,380 @@ +"""This implements a virtual screen. This is used to support ANSI terminal +emulation. The screen representation and state is implemented in this class. +Most of the methods are inspired by ANSI screen control codes. The ANSI class +extends this class to add parsing of ANSI escape codes. + +$Id: screen.py 486 2007-07-13 01:04:16Z noah $ +""" + +import copy + +NUL = 0 # Fill character; ignored on input. +ENQ = 5 # Transmit answerback message. +BEL = 7 # Ring the bell. +BS = 8 # Move cursor left. +HT = 9 # Move cursor to next tab stop. +LF = 10 # Line feed. +VT = 11 # Same as LF. +FF = 12 # Same as LF. +CR = 13 # Move cursor to left margin or newline. +SO = 14 # Invoke G1 character set. +SI = 15 # Invoke G0 character set. +XON = 17 # Resume transmission. +XOFF = 19 # Halt transmission. +CAN = 24 # Cancel escape sequence. +SUB = 26 # Same as CAN. +ESC = 27 # Introduce a control sequence. +DEL = 127 # Fill character; ignored on input. +SPACE = chr(32) # Space or blank character. + +def constrain (n, min, max): + + """This returns a number, n constrained to the min and max bounds. """ + + if n < min: + return min + if n > max: + return max + return n + +class screen: + + """This object maintains the state of a virtual text screen as a + rectangluar array. This maintains a virtual cursor position and handles + scrolling as characters are added. This supports most of the methods needed + by an ANSI text screen. Row and column indexes are 1-based (not zero-based, + like arrays). """ + + def __init__ (self, r=24,c=80): + + """This initializes a blank scree of the given dimentions.""" + + self.rows = r + self.cols = c + self.cur_r = 1 + self.cur_c = 1 + self.cur_saved_r = 1 + self.cur_saved_c = 1 + self.scroll_row_start = 1 + self.scroll_row_end = self.rows + self.w = [ [SPACE] * self.cols for c in range(self.rows)] + + def __str__ (self): + + """This returns a printable representation of the screen. The end of + each screen line is terminated by a newline. """ + + return '\n'.join ([ ''.join(c) for c in self.w ]) + + def dump (self): + + """This returns a copy of the screen as a string. This is similar to + __str__ except that lines are not terminated with line feeds. """ + + return ''.join ([ ''.join(c) for c in self.w ]) + + def pretty (self): + + """This returns a copy of the screen as a string with an ASCII text box + around the screen border. This is similar to __str__ except that it + adds a box. """ + + top_bot = '+' + '-'*self.cols + '+\n' + return top_bot + '\n'.join(['|'+line+'|' for line in str(self).split('\n')]) + '\n' + top_bot + + def fill (self, ch=SPACE): + + self.fill_region (1,1,self.rows,self.cols, ch) + + def fill_region (self, rs,cs, re,ce, ch=SPACE): + + rs = constrain (rs, 1, self.rows) + re = constrain (re, 1, self.rows) + cs = constrain (cs, 1, self.cols) + ce = constrain (ce, 1, self.cols) + if rs > re: + rs, re = re, rs + if cs > ce: + cs, ce = ce, cs + for r in range (rs, re+1): + for c in range (cs, ce + 1): + self.put_abs (r,c,ch) + + def cr (self): + + """This moves the cursor to the beginning (col 1) of the current row. + """ + + self.cursor_home (self.cur_r, 1) + + def lf (self): + + """This moves the cursor down with scrolling. + """ + + old_r = self.cur_r + self.cursor_down() + if old_r == self.cur_r: + self.scroll_up () + self.erase_line() + + def crlf (self): + + """This advances the cursor with CRLF properties. + The cursor will line wrap and the screen may scroll. + """ + + self.cr () + self.lf () + + def newline (self): + + """This is an alias for crlf(). + """ + + self.crlf() + + def put_abs (self, r, c, ch): + + """Screen array starts at 1 index.""" + + r = constrain (r, 1, self.rows) + c = constrain (c, 1, self.cols) + ch = str(ch)[0] + self.w[r-1][c-1] = ch + + def put (self, ch): + + """This puts a characters at the current cursor position. + """ + + self.put_abs (self.cur_r, self.cur_c, ch) + + def insert_abs (self, r, c, ch): + + """This inserts a character at (r,c). Everything under + and to the right is shifted right one character. + The last character of the line is lost. + """ + + r = constrain (r, 1, self.rows) + c = constrain (c, 1, self.cols) + for ci in range (self.cols, c, -1): + self.put_abs (r,ci, self.get_abs(r,ci-1)) + self.put_abs (r,c,ch) + + def insert (self, ch): + + self.insert_abs (self.cur_r, self.cur_c, ch) + + def get_abs (self, r, c): + + r = constrain (r, 1, self.rows) + c = constrain (c, 1, self.cols) + return self.w[r-1][c-1] + + def get (self): + + self.get_abs (self.cur_r, self.cur_c) + + def get_region (self, rs,cs, re,ce): + + """This returns a list of lines representing the region. + """ + + rs = constrain (rs, 1, self.rows) + re = constrain (re, 1, self.rows) + cs = constrain (cs, 1, self.cols) + ce = constrain (ce, 1, self.cols) + if rs > re: + rs, re = re, rs + if cs > ce: + cs, ce = ce, cs + sc = [] + for r in range (rs, re+1): + line = '' + for c in range (cs, ce + 1): + ch = self.get_abs (r,c) + line = line + ch + sc.append (line) + return sc + + def cursor_constrain (self): + + """This keeps the cursor within the screen area. + """ + + self.cur_r = constrain (self.cur_r, 1, self.rows) + self.cur_c = constrain (self.cur_c, 1, self.cols) + + def cursor_home (self, r=1, c=1): # [{ROW};{COLUMN}H + + self.cur_r = r + self.cur_c = c + self.cursor_constrain () + + def cursor_back (self,count=1): # [{COUNT}D (not confused with down) + + self.cur_c = self.cur_c - count + self.cursor_constrain () + + def cursor_down (self,count=1): # [{COUNT}B (not confused with back) + + self.cur_r = self.cur_r + count + self.cursor_constrain () + + def cursor_forward (self,count=1): # [{COUNT}C + + self.cur_c = self.cur_c + count + self.cursor_constrain () + + def cursor_up (self,count=1): # [{COUNT}A + + self.cur_r = self.cur_r - count + self.cursor_constrain () + + def cursor_up_reverse (self): # M (called RI -- Reverse Index) + + old_r = self.cur_r + self.cursor_up() + if old_r == self.cur_r: + self.scroll_up() + + def cursor_force_position (self, r, c): # [{ROW};{COLUMN}f + + """Identical to Cursor Home.""" + + self.cursor_home (r, c) + + def cursor_save (self): # [s + + """Save current cursor position.""" + + self.cursor_save_attrs() + + def cursor_unsave (self): # [u + + """Restores cursor position after a Save Cursor.""" + + self.cursor_restore_attrs() + + def cursor_save_attrs (self): # 7 + + """Save current cursor position.""" + + self.cur_saved_r = self.cur_r + self.cur_saved_c = self.cur_c + + def cursor_restore_attrs (self): # 8 + + """Restores cursor position after a Save Cursor.""" + + self.cursor_home (self.cur_saved_r, self.cur_saved_c) + + def scroll_constrain (self): + + """This keeps the scroll region within the screen region.""" + + if self.scroll_row_start <= 0: + self.scroll_row_start = 1 + if self.scroll_row_end > self.rows: + self.scroll_row_end = self.rows + + def scroll_screen (self): # [r + + """Enable scrolling for entire display.""" + + self.scroll_row_start = 1 + self.scroll_row_end = self.rows + + def scroll_screen_rows (self, rs, re): # [{start};{end}r + + """Enable scrolling from row {start} to row {end}.""" + + self.scroll_row_start = rs + self.scroll_row_end = re + self.scroll_constrain() + + def scroll_down (self): # D + + """Scroll display down one line.""" + + # Screen is indexed from 1, but arrays are indexed from 0. + s = self.scroll_row_start - 1 + e = self.scroll_row_end - 1 + self.w[s+1:e+1] = copy.deepcopy(self.w[s:e]) + + def scroll_up (self): # M + + """Scroll display up one line.""" + + # Screen is indexed from 1, but arrays are indexed from 0. + s = self.scroll_row_start - 1 + e = self.scroll_row_end - 1 + self.w[s:e] = copy.deepcopy(self.w[s+1:e+1]) + + def erase_end_of_line (self): # [0K -or- [K + + """Erases from the current cursor position to the end of the current + line.""" + + self.fill_region (self.cur_r, self.cur_c, self.cur_r, self.cols) + + def erase_start_of_line (self): # [1K + + """Erases from the current cursor position to the start of the current + line.""" + + self.fill_region (self.cur_r, 1, self.cur_r, self.cur_c) + + def erase_line (self): # [2K + + """Erases the entire current line.""" + + self.fill_region (self.cur_r, 1, self.cur_r, self.cols) + + def erase_down (self): # [0J -or- [J + + """Erases the screen from the current line down to the bottom of the + screen.""" + + self.erase_end_of_line () + self.fill_region (self.cur_r + 1, 1, self.rows, self.cols) + + def erase_up (self): # [1J + + """Erases the screen from the current line up to the top of the + screen.""" + + self.erase_start_of_line () + self.fill_region (self.cur_r-1, 1, 1, self.cols) + + def erase_screen (self): # [2J + + """Erases the screen with the background color.""" + + self.fill () + + def set_tab (self): # H + + """Sets a tab at the current position.""" + + pass + + def clear_tab (self): # [g + + """Clears tab at the current position.""" + + pass + + def clear_all_tabs (self): # [3g + + """Clears all tabs.""" + + pass + +# Insert line Esc [ Pn L +# Delete line Esc [ Pn M +# Delete character Esc [ Pn P +# Scrolling region Esc [ Pn(top);Pn(bot) r + diff --git a/third_party/Python/module/pexpect-2.4/setup.py b/third_party/Python/module/pexpect-2.4/setup.py new file mode 100644 index 000000000000..81a442bed355 --- /dev/null +++ b/third_party/Python/module/pexpect-2.4/setup.py @@ -0,0 +1,39 @@ +''' +$Revision: 485 $ +$Date: 2007-07-12 15:23:15 -0700 (Thu, 12 Jul 2007) $ +''' +from distutils.core import setup +setup (name='pexpect', + version='2.4', + py_modules=['pexpect', 'pxssh', 'fdpexpect', 'FSM', 'screen', 'ANSI'], + description='Pexpect is a pure Python Expect. It allows easy control of other applications.', + author='Noah Spurrier', + author_email='noah@noah.org', + url='http://pexpect.sourceforge.net/', + license='MIT license', + platforms='UNIX', +) + +# classifiers = [ +# 'Development Status :: 4 - Beta', +# 'Environment :: Console', +# 'Environment :: Console (Text Based)', +# 'Intended Audience :: Developers', +# 'Intended Audience :: System Administrators', +# 'Intended Audience :: Quality Engineers', +# 'License :: OSI Approved :: Python Software Foundation License', +# 'Operating System :: POSIX', +# 'Operating System :: MacOS :: MacOS X', +# 'Programming Language :: Python', +# 'Topic :: Software Development', +# 'Topic :: Software Development :: Libraries :: Python Modules', +# 'Topic :: Software Development :: Quality Assurance', +# 'Topic :: Software Development :: Testing', +# 'Topic :: System, System :: Archiving :: Packaging, System :: Installation/Setup', +# 'Topic :: System :: Shells', +# 'Topic :: System :: Software Distribution', +# 'Topic :: Terminals, Utilities', +# ], + + + diff --git a/third_party/Python/module/progress/progress.py b/third_party/Python/module/progress/progress.py new file mode 100644 index 000000000000..734627d4b16e --- /dev/null +++ b/third_party/Python/module/progress/progress.py @@ -0,0 +1,154 @@ +#!/usr/bin/python + +from __future__ import print_function + +import use_lldb_suite +import six + +import sys +import time + +class ProgressBar(object): + """ProgressBar class holds the options of the progress bar. + The options are: + start State from which start the progress. For example, if start is + 5 and the end is 10, the progress of this state is 50% + end State in which the progress has terminated. + width -- + fill String to use for "filled" used to represent the progress + blank String to use for "filled" used to represent remaining space. + format Format + incremental + """ + light_block = six.unichr(0x2591).encode("utf-8") + solid_block = six.unichr(0x2588).encode("utf-8") + solid_right_arrow = six.unichr(0x25BA).encode("utf-8") + + def __init__(self, + start=0, + end=10, + width=12, + fill=six.unichr(0x25C9).encode("utf-8"), + blank=six.unichr(0x25CC).encode("utf-8"), + marker=six.unichr(0x25CE).encode("utf-8"), + format='[%(fill)s%(marker)s%(blank)s] %(progress)s%%', + incremental=True): + super(ProgressBar, self).__init__() + + self.start = start + self.end = end + self.width = width + self.fill = fill + self.blank = blank + self.marker = marker + self.format = format + self.incremental = incremental + self.step = 100 / float(width) #fix + self.reset() + + def __add__(self, increment): + increment = self._get_progress(increment) + if 100 > self.progress + increment: + self.progress += increment + else: + self.progress = 100 + return self + + def complete(self): + self.progress = 100 + return self + + def __str__(self): + progressed = int(self.progress / self.step) #fix + fill = progressed * self.fill + blank = (self.width - progressed) * self.blank + return self.format % {'fill': fill, 'blank': blank, 'marker': self.marker, 'progress': int(self.progress)} + + __repr__ = __str__ + + def _get_progress(self, increment): + return float(increment * 100) / self.end + + def reset(self): + """Resets the current progress to the start point""" + self.progress = self._get_progress(self.start) + return self + + +class AnimatedProgressBar(ProgressBar): + """Extends ProgressBar to allow you to use it straighforward on a script. + Accepts an extra keyword argument named `stdout` (by default use sys.stdout) + and may be any file-object to which send the progress status. + """ + def __init__(self, + start=0, + end=10, + width=12, + fill=six.unichr(0x25C9).encode("utf-8"), + blank=six.unichr(0x25CC).encode("utf-8"), + marker=six.unichr(0x25CE).encode("utf-8"), + format='[%(fill)s%(marker)s%(blank)s] %(progress)s%%', + incremental=True, + stdout=sys.stdout): + super(AnimatedProgressBar, self).__init__(start,end,width,fill,blank,marker,format,incremental) + self.stdout = stdout + + def show_progress(self): + if hasattr(self.stdout, 'isatty') and self.stdout.isatty(): + self.stdout.write('\r') + else: + self.stdout.write('\n') + self.stdout.write(str(self)) + self.stdout.flush() + +class ProgressWithEvents(AnimatedProgressBar): + """Extends AnimatedProgressBar to allow you to track a set of events that + cause the progress to move. For instance, in a deletion progress bar, you + can track files that were nuked and files that the user doesn't have access to + """ + def __init__(self, + start=0, + end=10, + width=12, + fill=six.unichr(0x25C9).encode("utf-8"), + blank=six.unichr(0x25CC).encode("utf-8"), + marker=six.unichr(0x25CE).encode("utf-8"), + format='[%(fill)s%(marker)s%(blank)s] %(progress)s%%', + incremental=True, + stdout=sys.stdout): + super(ProgressWithEvents, self).__init__(start,end,width,fill,blank,marker,format,incremental,stdout) + self.events = {} + + def add_event(self,event): + if event in self.events: + self.events[event] += 1 + else: + self.events[event] = 1 + + def show_progress(self): + isatty = hasattr(self.stdout, 'isatty') and self.stdout.isatty() + if isatty: + self.stdout.write('\r') + else: + self.stdout.write('\n') + self.stdout.write(str(self)) + if len(self.events) == 0: + return + self.stdout.write('\n') + for key in list(self.events.keys()): + self.stdout.write(str(key) + ' = ' + str(self.events[key]) + ' ') + if isatty: + self.stdout.write('\033[1A') + self.stdout.flush() + + +if __name__ == '__main__': + p = AnimatedProgressBar(end=200, width=200) + + while True: + p + 5 + p.show_progress() + time.sleep(0.3) + if p.progress == 100: + break + print() #new line \ No newline at end of file diff --git a/third_party/Python/module/six/LICENSE b/third_party/Python/module/six/LICENSE new file mode 100644 index 000000000000..e558f9d494ab --- /dev/null +++ b/third_party/Python/module/six/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2010-2015 Benjamin Peterson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/Python/module/six/six.py b/third_party/Python/module/six/six.py new file mode 100644 index 000000000000..190c0239cd7d --- /dev/null +++ b/third_party/Python/module/six/six.py @@ -0,0 +1,868 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2015 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/third_party/Python/module/unittest2/unittest2/__init__.py b/third_party/Python/module/unittest2/unittest2/__init__.py new file mode 100644 index 000000000000..8ec6c7a478a6 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/__init__.py @@ -0,0 +1,78 @@ +""" +unittest2 + +unittest2 is a backport of the new features added to the unittest testing +framework in Python 2.7. It is tested to run on Python 2.4 - 2.6. + +To use unittest2 instead of unittest simply replace ``import unittest`` with +``import unittest2``. + + +Copyright (c) 1999-2003 Steve Purcell +Copyright (c) 2003-2010 Python Software Foundation +This module is free software, and you may redistribute it and/or modify +it under the same terms as Python itself, so long as this copyright message +and disclaimer are retained in their original form. + +IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +""" + +import sys + +if sys.version_info[0] >= 3: + # Python 3 doesn't have the builtin `cmp` function anymore + cmp_ = lambda x, y: (x > y) - (x < y) +else: + cmp_ = cmp + +reversed_cmp_ = lambda x, y: -cmp_(x,y) + +__all__ = ['TestResult', 'TestCase', 'TestSuite', + 'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main', + 'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless', + 'expectedFailure', 'TextTestResult', '__version__', 'collector'] + +__version__ = '0.5.1' + +# Expose obsolete functions for backwards compatibility +__all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases']) + + +from unittest2.collector import collector +from unittest2.result import TestResult +from unittest2.case import ( + TestCase, FunctionTestCase, SkipTest, skip, skipIf, + skipUnless, expectedFailure +) +from unittest2.suite import BaseTestSuite, TestSuite +from unittest2.loader import ( + TestLoader, defaultTestLoader, makeSuite, getTestCaseNames, + findTestCases +) +from unittest2.main import TestProgram, main, main_ +from unittest2.runner import TextTestRunner, TextTestResult + +try: + from unittest2.signals import ( + installHandler, registerResult, removeResult, removeHandler + ) +except ImportError: + # Compatibility with platforms that don't have the signal module + pass +else: + __all__.extend(['installHandler', 'registerResult', 'removeResult', + 'removeHandler']) + +# deprecated +_TextTestResult = TextTestResult + +__unittest = True diff --git a/third_party/Python/module/unittest2/unittest2/__main__.py b/third_party/Python/module/unittest2/unittest2/__main__.py new file mode 100644 index 000000000000..04ed982df0fb --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/__main__.py @@ -0,0 +1,10 @@ +"""Main entry point""" + +import sys +if sys.argv[0].endswith("__main__.py"): + sys.argv[0] = "unittest2" + +__unittest = True + +from unittest2.main import main_ +main_() diff --git a/third_party/Python/module/unittest2/unittest2/case.py b/third_party/Python/module/unittest2/unittest2/case.py new file mode 100644 index 000000000000..de7cb6a71e98 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/case.py @@ -0,0 +1,1119 @@ +"""Test case implementation""" + +import sys +import difflib +import pprint +import re +import unittest +import warnings + +import six + +from unittest2 import result +from unittest2.util import ( + safe_repr, safe_str, strclass, + unorderable_list_difference +) + +from unittest2.compatibility import wraps + +__unittest = True + + +DIFF_OMITTED = ('\nDiff is %s characters long. ' + 'Set self.maxDiff to None to see it.') + +class SkipTest(Exception): + """ + Raise this exception in a test to skip it. + + Usually you can use TestResult.skip() or one of the skipping decorators + instead of raising this directly. + """ + +class _ExpectedFailure(Exception): + """ + Raise this when a test is expected to fail. + + This is an implementation detail. + """ + + def __init__(self, exc_info, bugnumber=None): + # can't use super because Python 2.4 exceptions are old style + Exception.__init__(self) + self.exc_info = exc_info + self.bugnumber = bugnumber + +class _UnexpectedSuccess(Exception): + """ + The test was supposed to fail, but it didn't! + """ + + def __init__(self, exc_info, bugnumber=None): + # can't use super because Python 2.4 exceptions are old style + Exception.__init__(self) + self.exc_info = exc_info + self.bugnumber = bugnumber + +def _id(obj): + return obj + +def skip(reason): + """ + Unconditionally skip a test. + """ + def decorator(test_item): + if not (isinstance(test_item, type) and issubclass(test_item, TestCase)): + @wraps(test_item) + def skip_wrapper(*args, **kwargs): + raise SkipTest(reason) + test_item = skip_wrapper + + test_item.__unittest_skip__ = True + test_item.__unittest_skip_why__ = reason + return test_item + return decorator + +def skipIf(condition, reason): + """ + Skip a test if the condition is true. + """ + if condition: + return skip(reason) + return _id + +def skipUnless(condition, reason): + """ + Skip a test unless the condition is true. + """ + if not condition: + return skip(reason) + return _id + +def expectedFailure(bugnumber=None): + if callable(bugnumber): + @wraps(bugnumber) + def expectedFailure_easy_wrapper(*args, **kwargs): + try: + bugnumber(*args, **kwargs) + except Exception: + raise _ExpectedFailure(sys.exc_info(),None) + raise _UnexpectedSuccess(sys.exc_info(),None) + return expectedFailure_easy_wrapper + else: + def expectedFailure_impl(func): + @wraps(func) + def wrapper(*args, **kwargs): + try: + func(*args, **kwargs) + except Exception: + raise _ExpectedFailure(sys.exc_info(),bugnumber) + raise _UnexpectedSuccess(sys.exc_info(),bugnumber) + return wrapper + return expectedFailure_impl + +class _AssertRaisesContext(object): + """A context manager used to implement TestCase.assertRaises* methods.""" + + def __init__(self, expected, test_case, expected_regexp=None): + self.expected = expected + self.failureException = test_case.failureException + self.expected_regexp = expected_regexp + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + if exc_type is None: + try: + exc_name = self.expected.__name__ + except AttributeError: + exc_name = str(self.expected) + raise self.failureException( + "%s not raised" % (exc_name,)) + if not issubclass(exc_type, self.expected): + # let unexpected exceptions pass through + return False + self.exception = exc_value # store for later retrieval + if self.expected_regexp is None: + return True + + expected_regexp = self.expected_regexp + if isinstance(expected_regexp, six.string_types): + expected_regexp = re.compile(expected_regexp) + if not expected_regexp.search(str(exc_value)): + raise self.failureException('"%s" does not match "%s"' % + (expected_regexp.pattern, str(exc_value))) + return True + + +class _TypeEqualityDict(object): + + def __init__(self, testcase): + self.testcase = testcase + self._store = {} + + def __setitem__(self, key, value): + self._store[key] = value + + def __getitem__(self, key): + value = self._store[key] + if isinstance(value, six.string_types): + return getattr(self.testcase, value) + return value + + def get(self, key, default=None): + if key in self._store: + return self[key] + return default + + +class TestCase(unittest.TestCase): + """A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + """ + + # This attribute determines which exception will be raised when + # the instance's assertion methods fail; test methods raising this + # exception will be deemed to have 'failed' rather than 'errored' + + failureException = AssertionError + + # This attribute sets the maximum length of a diff in failure messages + # by assert methods using difflib. It is looked up as an instance attribute + # so can be configured by individual tests if required. + + maxDiff = 80*8 + + # This attribute determines whether long messages (including repr of + # objects used in assert methods) will be printed on failure in *addition* + # to any explicit message passed. + + longMessage = True + + # Attribute used by TestSuite for classSetUp + + _classSetupFailed = False + + def __init__(self, methodName='runTest'): + """Create an instance of the class that will use the named test + method when executed. Raises a ValueError if the instance does + not have a method with the specified name. + """ + self._testMethodName = methodName + self._resultForDoCleanups = None + try: + testMethod = getattr(self, methodName) + except AttributeError: + raise ValueError("no such test method in %s: %s" % \ + (self.__class__, methodName)) + self._testMethodDoc = testMethod.__doc__ + self._cleanups = [] + + # Map types to custom assertEqual functions that will compare + # instances of said type in more detail to generate a more useful + # error message. + self._type_equality_funcs = _TypeEqualityDict(self) + self.addTypeEqualityFunc(dict, 'assertDictEqual') + self.addTypeEqualityFunc(list, 'assertListEqual') + self.addTypeEqualityFunc(tuple, 'assertTupleEqual') + self.addTypeEqualityFunc(set, 'assertSetEqual') + self.addTypeEqualityFunc(frozenset, 'assertSetEqual') + if six.PY2: + self.addTypeEqualityFunc(unicode, 'assertMultiLineEqual') + else: + self.addTypeEqualityFunc(str, 'assertMultiLineEqual') + + def addTypeEqualityFunc(self, typeobj, function): + """Add a type specific assertEqual style function to compare a type. + + This method is for use by TestCase subclasses that need to register + their own type equality functions to provide nicer error messages. + + Args: + typeobj: The data type to call this function on when both values + are of the same type in assertEqual(). + function: The callable taking two arguments and an optional + msg= argument that raises self.failureException with a + useful error message when the two arguments are not equal. + """ + self._type_equality_funcs[typeobj] = function + + def addCleanup(self, function, *args, **kwargs): + """Add a function, with arguments, to be called when the test is + completed. Functions added are called on a LIFO basis and are + called after tearDown on test failure or success. + + Cleanup items are called even if setUp fails (unlike tearDown).""" + self._cleanups.append((function, args, kwargs)) + + def setUp(self): + "Hook method for setting up the test fixture before exercising it." + + @classmethod + def setUpClass(cls): + "Hook method for setting up class fixture before running tests in the class." + + @classmethod + def tearDownClass(cls): + "Hook method for deconstructing the class fixture after running all tests in the class." + + def tearDown(self): + "Hook method for deconstructing the test fixture after testing it." + + def countTestCases(self): + return 1 + + def defaultTestResult(self): + return result.TestResult() + + def shortDescription(self): + """Returns a one-line description of the test, or None if no + description has been provided. + + The default implementation of this method returns the first line of + the specified test method's docstring. + """ + doc = self._testMethodDoc + return doc and doc.split("\n")[0].strip() or None + + + def id(self): + return "%s.%s" % (strclass(self.__class__), self._testMethodName) + + def __eq__(self, other): + if type(self) is not type(other): + return NotImplemented + + return self._testMethodName == other._testMethodName + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((type(self), self._testMethodName)) + + def __str__(self): + return "%s (%s)" % (self._testMethodName, strclass(self.__class__)) + + def __repr__(self): + return "<%s testMethod=%s>" % \ + (strclass(self.__class__), self._testMethodName) + + def _addSkip(self, result, reason): + addSkip = getattr(result, 'addSkip', None) + if addSkip is not None: + addSkip(self, reason) + else: + warnings.warn("Use of a TestResult without an addSkip method is deprecated", + DeprecationWarning, 2) + result.addSuccess(self) + + def run(self, result=None): + orig_result = result + if result is None: + result = self.defaultTestResult() + startTestRun = getattr(result, 'startTestRun', None) + if startTestRun is not None: + startTestRun() + + self._resultForDoCleanups = result + result.startTest(self) + + testMethod = getattr(self, self._testMethodName) + + if (getattr(self.__class__, "__unittest_skip__", False) or + getattr(testMethod, "__unittest_skip__", False)): + # If the class or method was skipped. + try: + skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') + or getattr(testMethod, '__unittest_skip_why__', '')) + self._addSkip(result, skip_why) + finally: + result.stopTest(self) + return + try: + success = False + try: + self.setUp() + except SkipTest as e: + self._addSkip(result, str(e)) + except Exception: + result.addError(self, sys.exc_info()) + else: + success = self.runMethod(testMethod, result) + + try: + self.tearDown() + except Exception: + result.addCleanupError(self, sys.exc_info()) + success = False + + self.dumpSessionInfo() + + cleanUpSuccess = self.doCleanups() + success = success and cleanUpSuccess + if success: + result.addSuccess(self) + finally: + result.stopTest(self) + if orig_result is None: + stopTestRun = getattr(result, 'stopTestRun', None) + if stopTestRun is not None: + stopTestRun() + + def runMethod(self, testMethod, result): + """Runs the test method and catches any exception that might be thrown. + + This is factored out of TestCase.run() to ensure that any exception + thrown during the test goes out of scope before tearDown. Otherwise, an + exception could hold references to Python objects that are bound to + SB objects and prevent them from being deleted in time. + """ + try: + testMethod() + except self.failureException: + result.addFailure(self, sys.exc_info()) + except _ExpectedFailure as e: + addExpectedFailure = getattr(result, 'addExpectedFailure', None) + if addExpectedFailure is not None: + addExpectedFailure(self, e.exc_info, e.bugnumber) + else: + warnings.warn("Use of a TestResult without an addExpectedFailure method is deprecated", + DeprecationWarning) + result.addSuccess(self) + except _UnexpectedSuccess as x: + addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None) + if addUnexpectedSuccess is not None: + addUnexpectedSuccess(self, x.bugnumber) + else: + warnings.warn("Use of a TestResult without an addUnexpectedSuccess method is deprecated", + DeprecationWarning) + result.addFailure(self, sys.exc_info()) + except SkipTest as e: + self._addSkip(result, str(e)) + except Exception: + result.addError(self, sys.exc_info()) + else: + return True + return False + + def doCleanups(self): + """Execute all cleanup functions. Normally called for you after + tearDown.""" + result = self._resultForDoCleanups + ok = True + while self._cleanups: + function, args, kwargs = self._cleanups.pop(-1) + try: + function(*args, **kwargs) + except Exception: + ok = False + result.addError(self, sys.exc_info()) + return ok + + def __call__(self, *args, **kwds): + return self.run(*args, **kwds) + + def debug(self): + """Run the test without collecting errors in a TestResult""" + self.setUp() + getattr(self, self._testMethodName)() + self.tearDown() + while self._cleanups: + function, args, kwargs = self._cleanups.pop(-1) + function(*args, **kwargs) + + def skipTest(self, reason): + """Skip this test.""" + raise SkipTest(reason) + + def fail(self, msg=None): + """Fail immediately, with the given message.""" + raise self.failureException(msg) + + def assertFalse(self, expr, msg=None): + "Fail the test if the expression is true." + if expr: + msg = self._formatMessage(msg, "%s is not False" % safe_repr(expr)) + raise self.failureException(msg) + + def assertTrue(self, expr, msg=None): + """Fail the test unless the expression is true.""" + if not expr: + msg = self._formatMessage(msg, "%s is not True" % safe_repr(expr)) + raise self.failureException(msg) + + def _formatMessage(self, msg, standardMsg): + """Honour the longMessage attribute when generating failure messages. + If longMessage is False this means: + * Use only an explicit message if it is provided + * Otherwise use the standard message for the assert + + If longMessage is True: + * Use the standard message + * If an explicit message is provided, plus ' : ' and the explicit message + """ + if not self.longMessage: + return msg or standardMsg + if msg is None: + return standardMsg + try: + return '%s : %s' % (standardMsg, msg) + except UnicodeDecodeError: + return '%s : %s' % (safe_str(standardMsg), safe_str(msg)) + + + def assertRaises(self, excClass, callableObj=None, *args, **kwargs): + """Fail unless an exception of class excClass is thrown + by callableObj when invoked with arguments args and keyword + arguments kwargs. If a different type of exception is + thrown, it will not be caught, and the test case will be + deemed to have suffered an error, exactly as for an + unexpected exception. + + If called with callableObj omitted or None, will return a + context object used like this:: + + with self.assertRaises(SomeException): + do_something() + + The context manager keeps a reference to the exception as + the 'exception' attribute. This allows you to inspect the + exception after the assertion:: + + with self.assertRaises(SomeException) as cm: + do_something() + the_exception = cm.exception + self.assertEqual(the_exception.error_code, 3) + """ + if callableObj is None: + return _AssertRaisesContext(excClass, self) + try: + callableObj(*args, **kwargs) + except excClass: + return + + if hasattr(excClass,'__name__'): + excName = excClass.__name__ + else: + excName = str(excClass) + raise self.failureException("%s not raised" % excName) + + def _getAssertEqualityFunc(self, first, second): + """Get a detailed comparison function for the types of the two args. + + Returns: A callable accepting (first, second, msg=None) that will + raise a failure exception if first != second with a useful human + readable error message for those types. + """ + # + # NOTE(gregory.p.smith): I considered isinstance(first, type(second)) + # and vice versa. I opted for the conservative approach in case + # subclasses are not intended to be compared in detail to their super + # class instances using a type equality func. This means testing + # subtypes won't automagically use the detailed comparison. Callers + # should use their type specific assertSpamEqual method to compare + # subclasses if the detailed comparison is desired and appropriate. + # See the discussion in http://bugs.python.org/issue2578. + # + if type(first) is type(second): + asserter = self._type_equality_funcs.get(type(first)) + if asserter is not None: + return asserter + + return self._baseAssertEqual + + def _baseAssertEqual(self, first, second, msg=None): + """The default assertEqual implementation, not type specific.""" + if not first == second: + standardMsg = '%s != %s' % (safe_repr(first), safe_repr(second)) + msg = self._formatMessage(msg, standardMsg) + raise self.failureException(msg) + + def assertEqual(self, first, second, msg=None): + """Fail if the two objects are unequal as determined by the '==' + operator. + """ + assertion_func = self._getAssertEqualityFunc(first, second) + assertion_func(first, second, msg=msg) + + def assertNotEqual(self, first, second, msg=None): + """Fail if the two objects are equal as determined by the '==' + operator. + """ + if not first != second: + msg = self._formatMessage(msg, '%s == %s' % (safe_repr(first), + safe_repr(second))) + raise self.failureException(msg) + + def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None): + """Fail if the two objects are unequal as determined by their + difference rounded to the given number of decimal places + (default 7) and comparing to zero, or by comparing that the + between the two objects is more than the given delta. + + Note that decimal places (from zero) are usually not the same + as significant digits (measured from the most signficant digit). + + If the two objects compare equal then they will automatically + compare almost equal. + """ + if first == second: + # shortcut + return + if delta is not None and places is not None: + raise TypeError("specify delta or places not both") + + if delta is not None: + if abs(first - second) <= delta: + return + + standardMsg = '%s != %s within %s delta' % (safe_repr(first), + safe_repr(second), + safe_repr(delta)) + else: + if places is None: + places = 7 + + if round(abs(second-first), places) == 0: + return + + standardMsg = '%s != %s within %r places' % (safe_repr(first), + safe_repr(second), + places) + msg = self._formatMessage(msg, standardMsg) + raise self.failureException(msg) + + def assertNotAlmostEqual(self, first, second, places=None, msg=None, delta=None): + """Fail if the two objects are equal as determined by their + difference rounded to the given number of decimal places + (default 7) and comparing to zero, or by comparing that the + between the two objects is less than the given delta. + + Note that decimal places (from zero) are usually not the same + as significant digits (measured from the most signficant digit). + + Objects that are equal automatically fail. + """ + if delta is not None and places is not None: + raise TypeError("specify delta or places not both") + if delta is not None: + if not (first == second) and abs(first - second) > delta: + return + standardMsg = '%s == %s within %s delta' % (safe_repr(first), + safe_repr(second), + safe_repr(delta)) + else: + if places is None: + places = 7 + if not (first == second) and round(abs(second-first), places) != 0: + return + standardMsg = '%s == %s within %r places' % (safe_repr(first), + safe_repr(second), + places) + + msg = self._formatMessage(msg, standardMsg) + raise self.failureException(msg) + + # Synonyms for assertion methods + + # The plurals are undocumented. Keep them that way to discourage use. + # Do not add more. Do not remove. + # Going through a deprecation cycle on these would annoy many people. + assertEquals = assertEqual + assertNotEquals = assertNotEqual + assertAlmostEquals = assertAlmostEqual + assertNotAlmostEquals = assertNotAlmostEqual + assert_ = assertTrue + + # These fail* assertion method names are pending deprecation and will + # be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578 + def _deprecate(original_func): + def deprecated_func(*args, **kwargs): + warnings.warn( + ('Please use %s instead.' % original_func.__name__), + PendingDeprecationWarning, 2) + return original_func(*args, **kwargs) + return deprecated_func + + failUnlessEqual = _deprecate(assertEqual) + failIfEqual = _deprecate(assertNotEqual) + failUnlessAlmostEqual = _deprecate(assertAlmostEqual) + failIfAlmostEqual = _deprecate(assertNotAlmostEqual) + failUnless = _deprecate(assertTrue) + failUnlessRaises = _deprecate(assertRaises) + failIf = _deprecate(assertFalse) + + def assertSequenceEqual(self, seq1, seq2, + msg=None, seq_type=None, max_diff=80*8): + """An equality assertion for ordered sequences (like lists and tuples). + + For the purposes of this function, a valid ordered sequence type is one + which can be indexed, has a length, and has an equality operator. + + Args: + seq1: The first sequence to compare. + seq2: The second sequence to compare. + seq_type: The expected datatype of the sequences, or None if no + datatype should be enforced. + msg: Optional message to use on failure instead of a list of + differences. + max_diff: Maximum size off the diff, larger diffs are not shown + """ + if seq_type is not None: + seq_type_name = seq_type.__name__ + if not isinstance(seq1, seq_type): + raise self.failureException('First sequence is not a %s: %s' + % (seq_type_name, safe_repr(seq1))) + if not isinstance(seq2, seq_type): + raise self.failureException('Second sequence is not a %s: %s' + % (seq_type_name, safe_repr(seq2))) + else: + seq_type_name = "sequence" + + differing = None + try: + len1 = len(seq1) + except (TypeError, NotImplementedError): + differing = 'First %s has no length. Non-sequence?' % ( + seq_type_name) + + if differing is None: + try: + len2 = len(seq2) + except (TypeError, NotImplementedError): + differing = 'Second %s has no length. Non-sequence?' % ( + seq_type_name) + + if differing is None: + if seq1 == seq2: + return + + seq1_repr = repr(seq1) + seq2_repr = repr(seq2) + if len(seq1_repr) > 30: + seq1_repr = seq1_repr[:30] + '...' + if len(seq2_repr) > 30: + seq2_repr = seq2_repr[:30] + '...' + elements = (seq_type_name.capitalize(), seq1_repr, seq2_repr) + differing = '%ss differ: %s != %s\n' % elements + + for i in xrange(min(len1, len2)): + try: + item1 = seq1[i] + except (TypeError, IndexError, NotImplementedError): + differing += ('\nUnable to index element %d of first %s\n' % + (i, seq_type_name)) + break + + try: + item2 = seq2[i] + except (TypeError, IndexError, NotImplementedError): + differing += ('\nUnable to index element %d of second %s\n' % + (i, seq_type_name)) + break + + if item1 != item2: + differing += ('\nFirst differing element %d:\n%s\n%s\n' % + (i, item1, item2)) + break + else: + if (len1 == len2 and seq_type is None and + type(seq1) != type(seq2)): + # The sequences are the same, but have differing types. + return + + if len1 > len2: + differing += ('\nFirst %s contains %d additional ' + 'elements.\n' % (seq_type_name, len1 - len2)) + try: + differing += ('First extra element %d:\n%s\n' % + (len2, seq1[len2])) + except (TypeError, IndexError, NotImplementedError): + differing += ('Unable to index element %d ' + 'of first %s\n' % (len2, seq_type_name)) + elif len1 < len2: + differing += ('\nSecond %s contains %d additional ' + 'elements.\n' % (seq_type_name, len2 - len1)) + try: + differing += ('First extra element %d:\n%s\n' % + (len1, seq2[len1])) + except (TypeError, IndexError, NotImplementedError): + differing += ('Unable to index element %d ' + 'of second %s\n' % (len1, seq_type_name)) + standardMsg = differing + diffMsg = '\n' + '\n'.join( + difflib.ndiff(pprint.pformat(seq1).splitlines(), + pprint.pformat(seq2).splitlines())) + + standardMsg = self._truncateMessage(standardMsg, diffMsg) + msg = self._formatMessage(msg, standardMsg) + self.fail(msg) + + def _truncateMessage(self, message, diff): + max_diff = self.maxDiff + if max_diff is None or len(diff) <= max_diff: + return message + diff + return message + (DIFF_OMITTED % len(diff)) + + def assertListEqual(self, list1, list2, msg=None): + """A list-specific equality assertion. + + Args: + list1: The first list to compare. + list2: The second list to compare. + msg: Optional message to use on failure instead of a list of + differences. + + """ + self.assertSequenceEqual(list1, list2, msg, seq_type=list) + + def assertTupleEqual(self, tuple1, tuple2, msg=None): + """A tuple-specific equality assertion. + + Args: + tuple1: The first tuple to compare. + tuple2: The second tuple to compare. + msg: Optional message to use on failure instead of a list of + differences. + """ + self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple) + + def assertSetEqual(self, set1, set2, msg=None): + """A set-specific equality assertion. + + Args: + set1: The first set to compare. + set2: The second set to compare. + msg: Optional message to use on failure instead of a list of + differences. + + assertSetEqual uses ducktyping to support + different types of sets, and is optimized for sets specifically + (parameters must support a difference method). + """ + try: + difference1 = set1.difference(set2) + except TypeError as e: + self.fail('invalid type when attempting set difference: %s' % e) + except AttributeError as e: + self.fail('first argument does not support set difference: %s' % e) + + try: + difference2 = set2.difference(set1) + except TypeError as e: + self.fail('invalid type when attempting set difference: %s' % e) + except AttributeError as e: + self.fail('second argument does not support set difference: %s' % e) + + if not (difference1 or difference2): + return + + lines = [] + if difference1: + lines.append('Items in the first set but not the second:') + for item in difference1: + lines.append(repr(item)) + if difference2: + lines.append('Items in the second set but not the first:') + for item in difference2: + lines.append(repr(item)) + + standardMsg = '\n'.join(lines) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertIn(self, member, container, msg=None): + """Just like self.assertTrue(a in b), but with a nicer default message.""" + if member not in container: + standardMsg = '%s not found in %s' % (safe_repr(member), + safe_repr(container)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotIn(self, member, container, msg=None): + """Just like self.assertTrue(a not in b), but with a nicer default message.""" + if member in container: + standardMsg = '%s unexpectedly found in %s' % (safe_repr(member), + safe_repr(container)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertIs(self, expr1, expr2, msg=None): + """Just like self.assertTrue(a is b), but with a nicer default message.""" + if expr1 is not expr2: + standardMsg = '%s is not %s' % (safe_repr(expr1), safe_repr(expr2)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertIsNot(self, expr1, expr2, msg=None): + """Just like self.assertTrue(a is not b), but with a nicer default message.""" + if expr1 is expr2: + standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertDictEqual(self, d1, d2, msg=None): + self.assert_(isinstance(d1, dict), 'First argument is not a dictionary') + self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary') + + if d1 != d2: + standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True)) + diff = ('\n' + '\n'.join(difflib.ndiff( + pprint.pformat(d1).splitlines(), + pprint.pformat(d2).splitlines()))) + standardMsg = self._truncateMessage(standardMsg, diff) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertDictContainsSubset(self, expected, actual, msg=None): + """Checks whether actual is a superset of expected.""" + missing = [] + mismatched = [] + for key, value in expected.iteritems(): + if key not in actual: + missing.append(key) + elif value != actual[key]: + mismatched.append('%s, expected: %s, actual: %s' % + (safe_repr(key), safe_repr(value), + safe_repr(actual[key]))) + + if not (missing or mismatched): + return + + standardMsg = '' + if missing: + standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in + missing) + if mismatched: + if standardMsg: + standardMsg += '; ' + standardMsg += 'Mismatched values: %s' % ','.join(mismatched) + + self.fail(self._formatMessage(msg, standardMsg)) + + def assertItemsEqual(self, expected_seq, actual_seq, msg=None): + """An unordered sequence specific comparison. It asserts that + expected_seq and actual_seq contain the same elements. It is + the equivalent of:: + + self.assertEqual(sorted(expected_seq), sorted(actual_seq)) + + Raises with an error message listing which elements of expected_seq + are missing from actual_seq and vice versa if any. + + Asserts that each element has the same count in both sequences. + Example: + - [0, 1, 1] and [1, 0, 1] compare equal. + - [0, 0, 1] and [0, 1] compare unequal. + """ + try: + expected = sorted(expected_seq) + actual = sorted(actual_seq) + except TypeError: + # Unsortable items (example: set(), complex(), ...) + expected = list(expected_seq) + actual = list(actual_seq) + missing, unexpected = unorderable_list_difference( + expected, actual, ignore_duplicate=False + ) + else: + return self.assertSequenceEqual(expected, actual, msg=msg) + + errors = [] + if missing: + errors.append('Expected, but missing:\n %s' % + safe_repr(missing)) + if unexpected: + errors.append('Unexpected, but present:\n %s' % + safe_repr(unexpected)) + if errors: + standardMsg = '\n'.join(errors) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertMultiLineEqual(self, first, second, msg=None): + """Assert that two multi-line strings are equal.""" + self.assert_(isinstance(first, six.string_types), ( + 'First argument is not a string')) + self.assert_(isinstance(second, six.string_types), ( + 'Second argument is not a string')) + + if first != second: + standardMsg = '%s != %s' % (safe_repr(first, True), safe_repr(second, True)) + diff = '\n' + ''.join(difflib.ndiff(first.splitlines(True), + second.splitlines(True))) + standardMsg = self._truncateMessage(standardMsg, diff) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertLess(self, a, b, msg=None): + """Just like self.assertTrue(a < b), but with a nicer default message.""" + if not a < b: + standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertLessEqual(self, a, b, msg=None): + """Just like self.assertTrue(a <= b), but with a nicer default message.""" + if not a <= b: + standardMsg = '%s not less than or equal to %s' % (safe_repr(a), safe_repr(b)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertGreater(self, a, b, msg=None): + """Just like self.assertTrue(a > b), but with a nicer default message.""" + if not a > b: + standardMsg = '%s not greater than %s' % (safe_repr(a), safe_repr(b)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertGreaterEqual(self, a, b, msg=None): + """Just like self.assertTrue(a >= b), but with a nicer default message.""" + if not a >= b: + standardMsg = '%s not greater than or equal to %s' % (safe_repr(a), safe_repr(b)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertIsNone(self, obj, msg=None): + """Same as self.assertTrue(obj is None), with a nicer default message.""" + if obj is not None: + standardMsg = '%s is not None' % (safe_repr(obj),) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertIsNotNone(self, obj, msg=None): + """Included for symmetry with assertIsNone.""" + if obj is None: + standardMsg = 'unexpectedly None' + self.fail(self._formatMessage(msg, standardMsg)) + + def assertIsInstance(self, obj, cls, msg=None): + """Same as self.assertTrue(isinstance(obj, cls)), with a nicer + default message.""" + if not isinstance(obj, cls): + standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotIsInstance(self, obj, cls, msg=None): + """Included for symmetry with assertIsInstance.""" + if isinstance(obj, cls): + standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertRaisesRegexp(self, expected_exception, expected_regexp, + callable_obj=None, *args, **kwargs): + """Asserts that the message in a raised exception matches a regexp. + + Args: + expected_exception: Exception class expected to be raised. + expected_regexp: Regexp (re pattern object or string) expected + to be found in error message. + callable_obj: Function to be called. + args: Extra args. + kwargs: Extra kwargs. + """ + if callable_obj is None: + return _AssertRaisesContext(expected_exception, self, expected_regexp) + try: + callable_obj(*args, **kwargs) + except expected_exception as exc_value: + if isinstance(expected_regexp, six.string_types): + expected_regexp = re.compile(expected_regexp) + if not expected_regexp.search(str(exc_value)): + raise self.failureException('"%s" does not match "%s"' % + (expected_regexp.pattern, str(exc_value))) + else: + if hasattr(expected_exception, '__name__'): + excName = expected_exception.__name__ + else: + excName = str(expected_exception) + raise self.failureException("%s not raised" % excName) + + + def assertRegexpMatches(self, text, expected_regexp, msg=None): + """Fail the test unless the text matches the regular expression.""" + if isinstance(expected_regexp, six.string_types): + expected_regexp = re.compile(expected_regexp) + if not expected_regexp.search(text): + msg = msg or "Regexp didn't match" + msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text) + raise self.failureException(msg) + + def assertNotRegexpMatches(self, text, unexpected_regexp, msg=None): + """Fail the test if the text matches the regular expression.""" + if isinstance(unexpected_regexp, six.string_types): + unexpected_regexp = re.compile(unexpected_regexp) + match = unexpected_regexp.search(text) + if match: + msg = msg or "Regexp matched" + msg = '%s: %r matches %r in %r' % (msg, + text[match.start():match.end()], + unexpected_regexp.pattern, + text) + raise self.failureException(msg) + +class FunctionTestCase(TestCase): + """A test case that wraps a test function. + + This is useful for slipping pre-existing test functions into the + unittest framework. Optionally, set-up and tidy-up functions can be + supplied. As with TestCase, the tidy-up ('tearDown') function will + always be called if the set-up ('setUp') function ran successfully. + """ + + def __init__(self, testFunc, setUp=None, tearDown=None, description=None): + super(FunctionTestCase, self).__init__() + self._setUpFunc = setUp + self._tearDownFunc = tearDown + self._testFunc = testFunc + self._description = description + + def setUp(self): + if self._setUpFunc is not None: + self._setUpFunc() + + def tearDown(self): + if self._tearDownFunc is not None: + self._tearDownFunc() + + def runTest(self): + self._testFunc() + + def id(self): + return self._testFunc.__name__ + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + + return self._setUpFunc == other._setUpFunc and \ + self._tearDownFunc == other._tearDownFunc and \ + self._testFunc == other._testFunc and \ + self._description == other._description + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((type(self), self._setUpFunc, self._tearDownFunc, + self._testFunc, self._description)) + + def __str__(self): + return "%s (%s)" % (strclass(self.__class__), + self._testFunc.__name__) + + def __repr__(self): + return "<%s testFunc=%s>" % (strclass(self.__class__), + self._testFunc) + + def shortDescription(self): + if self._description is not None: + return self._description + doc = self._testFunc.__doc__ + return doc and doc.split("\n")[0].strip() or None diff --git a/third_party/Python/module/unittest2/unittest2/collector.py b/third_party/Python/module/unittest2/unittest2/collector.py new file mode 100644 index 000000000000..28ff3f89ce7d --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/collector.py @@ -0,0 +1,9 @@ +import os +import sys +from unittest2.loader import defaultTestLoader + +def collector(): + # import __main__ triggers code re-execution + __main__ = sys.modules['__main__'] + setupDir = os.path.abspath(os.path.dirname(__main__.__file__)) + return defaultTestLoader.discover(setupDir) diff --git a/third_party/Python/module/unittest2/unittest2/compatibility.py b/third_party/Python/module/unittest2/unittest2/compatibility.py new file mode 100644 index 000000000000..b8f15dd14288 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/compatibility.py @@ -0,0 +1,64 @@ +import os +import sys + +try: + from functools import wraps +except ImportError: + # only needed for Python 2.4 + def wraps(_): + def _wraps(func): + return func + return _wraps + +__unittest = True + +def _relpath_nt(path, start=os.path.curdir): + """Return a relative version of a path""" + + if not path: + raise ValueError("no path specified") + start_list = os.path.abspath(start).split(os.path.sep) + path_list = os.path.abspath(path).split(os.path.sep) + if start_list[0].lower() != path_list[0].lower(): + unc_path, rest = os.path.splitunc(path) + unc_start, rest = os.path.splitunc(start) + if bool(unc_path) ^ bool(unc_start): + raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" + % (path, start)) + else: + raise ValueError("path is on drive %s, start on drive %s" + % (path_list[0], start_list[0])) + # Work out how much of the filepath is shared by start and path. + for i in range(min(len(start_list), len(path_list))): + if start_list[i].lower() != path_list[i].lower(): + break + else: + i += 1 + + rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return os.path.curdir + return os.path.join(*rel_list) + +# default to posixpath definition +def _relpath_posix(path, start=os.path.curdir): + """Return a relative version of a path""" + + if not path: + raise ValueError("no path specified") + + start_list = os.path.abspath(start).split(os.path.sep) + path_list = os.path.abspath(path).split(os.path.sep) + + # Work out how much of the filepath is shared by start and path. + i = len(os.path.commonprefix([start_list, path_list])) + + rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return os.path.curdir + return os.path.join(*rel_list) + +if os.path is sys.modules.get('ntpath'): + relpath = _relpath_nt +else: + relpath = _relpath_posix diff --git a/third_party/Python/module/unittest2/unittest2/loader.py b/third_party/Python/module/unittest2/unittest2/loader.py new file mode 100644 index 000000000000..fe86db1e475e --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/loader.py @@ -0,0 +1,314 @@ +"""Loading unittests.""" + +import functools +import os +import re +import sys +import traceback +import types +import unittest + +from fnmatch import fnmatch + +from unittest2 import case, suite, cmp_ + +try: + from os.path import relpath +except ImportError: + from unittest2.compatibility import relpath + +__unittest = True + +# what about .pyc or .pyo (etc) +# we would need to avoid loading the same tests multiple times +# from '.py', '.pyc' *and* '.pyo' +VALID_MODULE_NAME = re.compile(r'[_a-z]\w*\.py$', re.IGNORECASE) + + +def _make_failed_import_test(name, suiteClass): + message = 'Failed to import test module: %s' % name + if hasattr(traceback, 'format_exc'): + # Python 2.3 compatibility + # format_exc returns two frames of discover.py as well + message += '\n%s' % traceback.format_exc() + return _make_failed_test('ModuleImportFailure', name, ImportError(message), + suiteClass) + +def _make_failed_load_tests(name, exception, suiteClass): + return _make_failed_test('LoadTestsFailure', name, exception, suiteClass) + +def _make_failed_test(classname, methodname, exception, suiteClass): + def testFailure(self): + raise exception + attrs = {methodname: testFailure} + TestClass = type(classname, (case.TestCase,), attrs) + return suiteClass((TestClass(methodname),)) + + +class TestLoader(unittest.TestLoader): + """ + This class is responsible for loading tests according to various criteria + and returning them wrapped in a TestSuite + """ + + def __init__(self): + self.testMethodPrefix = 'test' + self.sortTestMethodsUsing = cmp_ + self.suiteClass = suite.TestSuite + self._top_level_dir = None + + def loadTestsFromTestCase(self, testCaseClass): + """Return a suite of all tests cases contained in testCaseClass""" + if issubclass(testCaseClass, suite.TestSuite): + raise TypeError("Test cases should not be derived from TestSuite." + " Maybe you meant to derive from TestCase?") + testCaseNames = self.getTestCaseNames(testCaseClass) + if not testCaseNames and hasattr(testCaseClass, 'runTest'): + testCaseNames = ['runTest'] + loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames)) + return loaded_suite + + def loadTestsFromModule(self, module, use_load_tests=True): + """Return a suite of all tests cases contained in the given module""" + tests = [] + for name in dir(module): + obj = getattr(module, name) + if isinstance(obj, type) and issubclass(obj, unittest.TestCase): + tests.append(self.loadTestsFromTestCase(obj)) + + load_tests = getattr(module, 'load_tests', None) + tests = self.suiteClass(tests) + if use_load_tests and load_tests is not None: + try: + return load_tests(self, tests, None) + except Exception as e: + return _make_failed_load_tests(module.__name__, e, + self.suiteClass) + return tests + + def loadTestsFromName(self, name, module=None): + """Return a suite of all tests cases given a string specifier. + + The name may resolve either to a module, a test case class, a + test method within a test case class, or a callable object which + returns a TestCase or TestSuite instance. + + The method optionally resolves the names relative to a given module. + """ + parts = name.split('.') + if module is None: + parts_copy = parts[:] + while parts_copy: + try: + module = __import__('.'.join(parts_copy)) + break + except ImportError: + del parts_copy[-1] + if not parts_copy: + raise + parts = parts[1:] + obj = module + for part in parts: + parent, obj = obj, getattr(obj, part) + + if isinstance(obj, types.ModuleType): + return self.loadTestsFromModule(obj) + elif isinstance(obj, type) and issubclass(obj, unittest.TestCase): + return self.loadTestsFromTestCase(obj) + elif (isinstance(obj, types.UnboundMethodType) and + isinstance(parent, type) and + issubclass(parent, case.TestCase)): + return self.suiteClass([parent(obj.__name__)]) + elif isinstance(obj, unittest.TestSuite): + return obj + elif hasattr(obj, '__call__'): + test = obj() + if isinstance(test, unittest.TestSuite): + return test + elif isinstance(test, unittest.TestCase): + return self.suiteClass([test]) + else: + raise TypeError("calling %s returned %s, not a test" % + (obj, test)) + else: + raise TypeError("don't know how to make test from: %s" % obj) + + def loadTestsFromNames(self, names, module=None): + """Return a suite of all tests cases found using the given sequence + of string specifiers. See 'loadTestsFromName()'. + """ + suites = [self.loadTestsFromName(name, module) for name in names] + return self.suiteClass(suites) + + def getTestCaseNames(self, testCaseClass): + """Return a sorted sequence of method names found within testCaseClass + """ + def isTestMethod(attrname, testCaseClass=testCaseClass, + prefix=self.testMethodPrefix): + return attrname.startswith(prefix) and \ + hasattr(getattr(testCaseClass, attrname), '__call__') + testFnNames = list(filter(isTestMethod, dir(testCaseClass))) + if self.sortTestMethodsUsing: + testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing)) + return testFnNames + + def discover(self, start_dir, pattern='test*.py', top_level_dir=None): + """Find and return all test modules from the specified start + directory, recursing into subdirectories to find them. Only test files + that match the pattern will be loaded. (Using shell style pattern + matching.) + + All test modules must be importable from the top level of the project. + If the start directory is not the top level directory then the top + level directory must be specified separately. + + If a test package name (directory with '__init__.py') matches the + pattern then the package will be checked for a 'load_tests' function. If + this exists then it will be called with loader, tests, pattern. + + If load_tests exists then discovery does *not* recurse into the package, + load_tests is responsible for loading all tests in the package. + + The pattern is deliberately not stored as a loader attribute so that + packages can continue discovery themselves. top_level_dir is stored so + load_tests does not need to pass this argument in to loader.discover(). + """ + set_implicit_top = False + if top_level_dir is None and self._top_level_dir is not None: + # make top_level_dir optional if called from load_tests in a package + top_level_dir = self._top_level_dir + elif top_level_dir is None: + set_implicit_top = True + top_level_dir = start_dir + + top_level_dir = os.path.abspath(top_level_dir) + + if not top_level_dir in sys.path: + # all test modules must be importable from the top level directory + # should we *unconditionally* put the start directory in first + # in sys.path to minimise likelihood of conflicts between installed + # modules and development versions? + sys.path.insert(0, top_level_dir) + self._top_level_dir = top_level_dir + + is_not_importable = False + if os.path.isdir(os.path.abspath(start_dir)): + start_dir = os.path.abspath(start_dir) + if start_dir != top_level_dir: + is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py')) + else: + # support for discovery from dotted module names + try: + __import__(start_dir) + except ImportError: + is_not_importable = True + else: + the_module = sys.modules[start_dir] + top_part = start_dir.split('.')[0] + start_dir = os.path.abspath(os.path.dirname((the_module.__file__))) + if set_implicit_top: + self._top_level_dir = os.path.abspath(os.path.dirname(os.path.dirname(sys.modules[top_part].__file__))) + sys.path.remove(top_level_dir) + + if is_not_importable: + raise ImportError('Start directory is not importable: %r' % start_dir) + + tests = list(self._find_tests(start_dir, pattern)) + return self.suiteClass(tests) + + def _get_name_from_path(self, path): + path = os.path.splitext(os.path.normpath(path))[0] + + _relpath = relpath(path, self._top_level_dir) + assert not os.path.isabs(_relpath), "Path must be within the project" + assert not _relpath.startswith('..'), "Path must be within the project" + + name = _relpath.replace(os.path.sep, '.') + return name + + def _get_module_from_name(self, name): + __import__(name) + return sys.modules[name] + + def _match_path(self, path, full_path, pattern): + # override this method to use alternative matching strategy + return fnmatch(path, pattern) + + def _find_tests(self, start_dir, pattern): + """Used by discovery. Yields test suites it loads.""" + paths = os.listdir(start_dir) + + for path in paths: + full_path = os.path.join(start_dir, path) + if os.path.isfile(full_path): + if not VALID_MODULE_NAME.match(path): + # valid Python identifiers only + continue + if not self._match_path(path, full_path, pattern): + continue + # if the test file matches, load it + name = self._get_name_from_path(full_path) + try: + module = self._get_module_from_name(name) + except: + yield _make_failed_import_test(name, self.suiteClass) + else: + mod_file = os.path.abspath(getattr(module, '__file__', full_path)) + realpath = os.path.splitext(mod_file)[0] + fullpath_noext = os.path.splitext(full_path)[0] + if realpath.lower() != fullpath_noext.lower(): + module_dir = os.path.dirname(realpath) + mod_name = os.path.splitext(os.path.basename(full_path))[0] + expected_dir = os.path.dirname(full_path) + msg = ("%r module incorrectly imported from %r. Expected %r. " + "Is this module globally installed?") + raise ImportError(msg % (mod_name, module_dir, expected_dir)) + yield self.loadTestsFromModule(module) + elif os.path.isdir(full_path): + if not os.path.isfile(os.path.join(full_path, '__init__.py')): + continue + + load_tests = None + tests = None + if fnmatch(path, pattern): + # only check load_tests if the package directory itself matches the filter + name = self._get_name_from_path(full_path) + package = self._get_module_from_name(name) + load_tests = getattr(package, 'load_tests', None) + tests = self.loadTestsFromModule(package, use_load_tests=False) + + if load_tests is None: + if tests is not None: + # tests loaded from package file + yield tests + # recurse into the package + for test in self._find_tests(full_path, pattern): + yield test + else: + try: + yield load_tests(self, tests, pattern) + except Exception as e: + yield _make_failed_load_tests(package.__name__, e, + self.suiteClass) + +defaultTestLoader = TestLoader() + + +def _makeLoader(prefix, sortUsing, suiteClass=None): + loader = TestLoader() + loader.sortTestMethodsUsing = sortUsing + loader.testMethodPrefix = prefix + if suiteClass: + loader.suiteClass = suiteClass + return loader + +def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp_): + return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass) + +def makeSuite(testCaseClass, prefix='test', sortUsing=cmp_, + suiteClass=suite.TestSuite): + return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass) + +def findTestCases(module, prefix='test', sortUsing=cmp_, + suiteClass=suite.TestSuite): + return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module) diff --git a/third_party/Python/module/unittest2/unittest2/main.py b/third_party/Python/module/unittest2/unittest2/main.py new file mode 100644 index 000000000000..d74b01f9ac23 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/main.py @@ -0,0 +1,242 @@ +"""Unittest main program""" + +import sys +import os +import types +import six + +from unittest2 import loader, runner +try: + from unittest2.signals import installHandler +except ImportError: + installHandler = None + +__unittest = True + +FAILFAST = " -f, --failfast Stop on first failure\n" +CATCHBREAK = " -c, --catch Catch control-C and display results\n" +BUFFEROUTPUT = " -b, --buffer Buffer stdout and stderr during test runs\n" + +USAGE_AS_MAIN = """\ +Usage: %(progName)s [options] [tests] + +Options: + -h, --help Show this message + -v, --verbose Verbose output + -q, --quiet Minimal output +%(failfast)s%(catchbreak)s%(buffer)s +Examples: + %(progName)s test_module - run tests from test_module + %(progName)s test_module.TestClass - run tests from + test_module.TestClass + %(progName)s test_module.TestClass.test_method - run specified test method + +[tests] can be a list of any number of test modules, classes and test +methods. + +Alternative Usage: %(progName)s discover [options] + +Options: + -v, --verbose Verbose output +%(failfast)s%(catchbreak)s%(buffer)s -s directory Directory to start discovery ('.' default) + -p pattern Pattern to match test files ('test*.py' default) + -t directory Top level directory of project (default to + start directory) + +For test discovery all test modules must be importable from the top +level directory of the project. +""" + +USAGE_FROM_MODULE = """\ +Usage: %(progName)s [options] [test] [...] + +Options: + -h, --help Show this message + -v, --verbose Verbose output + -q, --quiet Minimal output +%(failfast)s%(catchbreak)s%(buffer)s +Examples: + %(progName)s - run default set of tests + %(progName)s MyTestSuite - run suite 'MyTestSuite' + %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething + %(progName)s MyTestCase - run all 'test*' test methods + in MyTestCase +""" + + +class TestProgram(object): + """A command-line program that runs a set of tests; this is primarily + for making test modules conveniently executable. + """ + USAGE = USAGE_FROM_MODULE + + # defaults for testing + failfast = catchbreak = buffer = progName = None + + def __init__(self, module='__main__', defaultTest=None, + argv=None, testRunner=None, + testLoader=loader.defaultTestLoader, exit=True, + verbosity=1, failfast=None, catchbreak=None, buffer=None): + if isinstance(module, six.string_types): + self.module = __import__(module) + for part in module.split('.')[1:]: + self.module = getattr(self.module, part) + else: + self.module = module + if argv is None: + argv = sys.argv + + self.exit = exit + self.verbosity = verbosity + self.failfast = failfast + self.catchbreak = catchbreak + self.buffer = buffer + self.defaultTest = defaultTest + self.testRunner = testRunner + self.testLoader = testLoader + self.progName = os.path.basename(argv[0]) + self.parseArgs(argv) + self.runTests() + + def usageExit(self, msg=None): + if msg: + print(msg) + usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '', + 'buffer': ''} + if self.failfast != False: + usage['failfast'] = FAILFAST + if self.catchbreak != False and installHandler is not None: + usage['catchbreak'] = CATCHBREAK + if self.buffer != False: + usage['buffer'] = BUFFEROUTPUT + print(self.USAGE % usage) + sys.exit(2) + + def parseArgs(self, argv): + if len(argv) > 1 and argv[1].lower() == 'discover': + self._do_discovery(argv[2:]) + return + + import getopt + long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer'] + try: + options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts) + for opt, value in options: + if opt in ('-h','-H','--help'): + self.usageExit() + if opt in ('-q','--quiet'): + self.verbosity = 0 + if opt in ('-v','--verbose'): + self.verbosity = 2 + if opt in ('-f','--failfast'): + if self.failfast is None: + self.failfast = True + # Should this raise an exception if -f is not valid? + if opt in ('-c','--catch'): + if self.catchbreak is None and installHandler is not None: + self.catchbreak = True + # Should this raise an exception if -c is not valid? + if opt in ('-b','--buffer'): + if self.buffer is None: + self.buffer = True + # Should this raise an exception if -b is not valid? + if len(args) == 0 and self.defaultTest is None: + # createTests will load tests from self.module + self.testNames = None + elif len(args) > 0: + self.testNames = args + if __name__ == '__main__': + # to support python -m unittest ... + self.module = None + else: + self.testNames = (self.defaultTest,) + self.createTests() + except getopt.error as msg: + self.usageExit(msg) + + def createTests(self): + if self.testNames is None: + self.test = self.testLoader.loadTestsFromModule(self.module) + else: + self.test = self.testLoader.loadTestsFromNames(self.testNames, + self.module) + + def _do_discovery(self, argv, Loader=loader.TestLoader): + # handle command line args for test discovery + self.progName = '%s discover' % self.progName + import optparse + parser = optparse.OptionParser() + parser.prog = self.progName + parser.add_option('-v', '--verbose', dest='verbose', default=False, + help='Verbose output', action='store_true') + if self.failfast != False: + parser.add_option('-f', '--failfast', dest='failfast', default=False, + help='Stop on first fail or error', + action='store_true') + if self.catchbreak != False and installHandler is not None: + parser.add_option('-c', '--catch', dest='catchbreak', default=False, + help='Catch ctrl-C and display results so far', + action='store_true') + if self.buffer != False: + parser.add_option('-b', '--buffer', dest='buffer', default=False, + help='Buffer stdout and stderr during tests', + action='store_true') + parser.add_option('-s', '--start-directory', dest='start', default='.', + help="Directory to start discovery ('.' default)") + parser.add_option('-p', '--pattern', dest='pattern', default='test*.py', + help="Pattern to match tests ('test*.py' default)") + parser.add_option('-t', '--top-level-directory', dest='top', default=None, + help='Top level directory of project (defaults to start directory)') + + options, args = parser.parse_args(argv) + if len(args) > 3: + self.usageExit() + + for name, value in zip(('start', 'pattern', 'top'), args): + setattr(options, name, value) + + # only set options from the parsing here + # if they weren't set explicitly in the constructor + if self.failfast is None: + self.failfast = options.failfast + if self.catchbreak is None and installHandler is not None: + self.catchbreak = options.catchbreak + if self.buffer is None: + self.buffer = options.buffer + + if options.verbose: + self.verbosity = 2 + + start_dir = options.start + pattern = options.pattern + top_level_dir = options.top + + loader = Loader() + self.test = loader.discover(start_dir, pattern, top_level_dir) + + def runTests(self): + if self.catchbreak: + installHandler() + if self.testRunner is None: + self.testRunner = runner.TextTestRunner + if isinstance(self.testRunner, (type, types.ClassType)): + try: + testRunner = self.testRunner(verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer) + except TypeError: + # didn't accept the verbosity, buffer or failfast arguments + testRunner = self.testRunner() + else: + # it is assumed to be a TestRunner instance + testRunner = self.testRunner + self.result = testRunner.run(self.test) + if self.exit: + sys.exit(not self.result.wasSuccessful()) + +main = TestProgram + +def main_(): + TestProgram.USAGE = USAGE_AS_MAIN + main(module=None) + diff --git a/third_party/Python/module/unittest2/unittest2/result.py b/third_party/Python/module/unittest2/unittest2/result.py new file mode 100644 index 000000000000..2e4994d1d825 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/result.py @@ -0,0 +1,195 @@ +"""Test result object""" + +import use_lldb_suite + +import sys +import traceback +import unittest + +from six import StringIO as SixStringIO + +from unittest2 import util +from unittest2.compatibility import wraps + +__unittest = True + +def failfast(method): + @wraps(method) + def inner(self, *args, **kw): + if getattr(self, 'failfast', False): + self.stop() + return method(self, *args, **kw) + return inner + + +STDOUT_LINE = '\nStdout:\n%s' +STDERR_LINE = '\nStderr:\n%s' + +class TestResult(unittest.TestResult): + """Holder for test result information. + + Test results are automatically managed by the TestCase and TestSuite + classes, and do not need to be explicitly manipulated by writers of tests. + + Each instance holds the total number of tests run, and collections of + failures and errors that occurred among those test runs. The collections + contain tuples of (testcase, exceptioninfo), where exceptioninfo is the + formatted traceback of the error that occurred. + """ + _previousTestClass = None + _moduleSetUpFailed = False + + def __init__(self): + self.failfast = False + self.failures = [] + self.passes = [] + self.errors = [] + self.cleanup_errors = [] + self.testsRun = 0 + self.skipped = [] + self.expectedFailures = [] + self.unexpectedSuccesses = [] + self.shouldStop = False + self.buffer = False + self._stdout_buffer = None + self._stderr_buffer = None + self._original_stdout = sys.stdout + self._original_stderr = sys.stderr + self._mirrorOutput = False + + def startTest(self, test): + "Called when the given test is about to be run" + self.testsRun += 1 + self._mirrorOutput = False + if self.buffer: + if self._stderr_buffer is None: + self._stderr_buffer = SixStringIO() + self._stdout_buffer = SixStringIO() + sys.stdout = self._stdout_buffer + sys.stderr = self._stderr_buffer + + def startTestRun(self): + """Called once before any tests are executed. + + See startTest for a method called before each test. + """ + + def stopTest(self, test): + """Called when the given test has been run""" + if self.buffer: + if self._mirrorOutput: + output = sys.stdout.getvalue() + error = sys.stderr.getvalue() + if output: + if not output.endswith('\n'): + output += '\n' + self._original_stdout.write(STDOUT_LINE % output) + if error: + if not error.endswith('\n'): + error += '\n' + self._original_stderr.write(STDERR_LINE % error) + + sys.stdout = self._original_stdout + sys.stderr = self._original_stderr + self._stdout_buffer.seek(0) + self._stdout_buffer.truncate() + self._stderr_buffer.seek(0) + self._stderr_buffer.truncate() + self._mirrorOutput = False + + + def stopTestRun(self): + """Called once after all tests are executed. + + See stopTest for a method called after each test. + """ + + @failfast + def addError(self, test, err): + """Called when an error has occurred. 'err' is a tuple of values as + returned by sys.exc_info(). + """ + self.errors.append((test, self._exc_info_to_string(err, test))) + self._mirrorOutput = True + + def addCleanupError(self, test, err): + """Called when an error has occurred during cleanup. 'err' is a tuple of + values as returned by sys.exc_info(). + """ + self.cleanup_errors.append((test, self._exc_info_to_string(err, test))) + self._mirrorOutput = True + + @failfast + def addFailure(self, test, err): + """Called when an error has occurred. 'err' is a tuple of values as + returned by sys.exc_info().""" + self.failures.append((test, self._exc_info_to_string(err, test))) + self._mirrorOutput = True + + def addSuccess(self, test): + "Called when a test has completed successfully" + self.passes.append(test) + pass + + def addSkip(self, test, reason): + """Called when a test is skipped.""" + self.skipped.append((test, reason)) + + def addExpectedFailure(self, test, err, bugnumber): + """Called when an expected failure/error occured.""" + self.expectedFailures.append( + (test, self._exc_info_to_string(err, test))) + + @failfast + def addUnexpectedSuccess(self, test, bugnumber): + """Called when a test was expected to fail, but succeed.""" + self.unexpectedSuccesses.append(test) + + def wasSuccessful(self): + "Tells whether or not this result was a success" + return (len(self.failures) + len(self.errors) == 0) + + def stop(self): + "Indicates that the tests should be aborted" + self.shouldStop = True + + def _exc_info_to_string(self, err, test): + """Converts a sys.exc_info()-style tuple of values into a string.""" + exctype, value, tb = err + # Skip test runner traceback levels + while tb and self._is_relevant_tb_level(tb): + tb = tb.tb_next + if exctype is test.failureException: + # Skip assert*() traceback levels + length = self._count_relevant_tb_levels(tb) + msgLines = traceback.format_exception(exctype, value, tb, length) + else: + msgLines = traceback.format_exception(exctype, value, tb) + + if self.buffer: + output = sys.stdout.getvalue() + error = sys.stderr.getvalue() + if output: + if not output.endswith('\n'): + output += '\n' + msgLines.append(STDOUT_LINE % output) + if error: + if not error.endswith('\n'): + error += '\n' + msgLines.append(STDERR_LINE % error) + return ''.join(msgLines) + + def _is_relevant_tb_level(self, tb): + return '__unittest' in tb.tb_frame.f_globals + + def _count_relevant_tb_levels(self, tb): + length = 0 + while tb and not self._is_relevant_tb_level(tb): + length += 1 + tb = tb.tb_next + return length + + def __repr__(self): + return "<%s run=%i errors=%i failures=%i>" % \ + (util.strclass(self.__class__), self.testsRun, len(self.errors), + len(self.failures)) diff --git a/third_party/Python/module/unittest2/unittest2/runner.py b/third_party/Python/module/unittest2/unittest2/runner.py new file mode 100644 index 000000000000..db0f89d26ec4 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/runner.py @@ -0,0 +1,203 @@ +"""Running tests""" + +import sys +import time +import unittest +import progress + +from unittest2 import result + +try: + from unittest2.signals import registerResult +except ImportError: + def registerResult(_): + pass + +__unittest = True + + +class _WritelnDecorator(object): + """Used to decorate file-like objects with a handy 'writeln' method""" + def __init__(self,stream): + self.stream = stream + + def __getattr__(self, attr): + if attr in ('stream', '__getstate__'): + raise AttributeError(attr) + return getattr(self.stream,attr) + + def writeln(self, arg=None): + if arg: + self.write(arg) + self.write('\n') # text-mode streams translate to \r\n if needed + + +class TextTestResult(result.TestResult): + """A test result class that can print formatted text results to a stream. + + Used by TextTestRunner. + """ + separator1 = '=' * 70 + separator2 = '-' * 70 + + def __init__(self, stream, descriptions, verbosity): + super(TextTestResult, self).__init__() + self.stream = stream + self.showAll = verbosity > 1 + self.dots = verbosity == 1 + self.descriptions = descriptions + self.progressbar = None + + if self.dots: + self.stream.writeln(".=success F=fail E=error s=skipped x=expected-fail u=unexpected-success"); + self.stream.writeln(""); + self.stream.flush() + + def getDescription(self, test): + doc_first_line = test.shortDescription() + if self.descriptions and doc_first_line: + return '\n'.join((str(test), doc_first_line)) + else: + return str(test) + + def startTest(self, test): + super(TextTestResult, self).startTest(test) + if self.showAll: + self.stream.write(self.getDescription(test)) + self.stream.write(" ... ") + self.stream.flush() + + def newTestResult(self,test,result_short,result_long): + if self.showAll: + self.stream.writeln(result_long) + elif self.progressbar: + self.progressbar.__add__(1) + self.progressbar.add_event(result_short) + self.progressbar.show_progress() + elif self.dots: + self.stream.write(result_short) + self.stream.flush() + + def addSuccess(self, test): + super(TextTestResult, self).addSuccess(test) + if self.progressbar: + self.newTestResult(test,"ok","ok") + else: + self.newTestResult(test,".","ok") + + def addError(self, test, err): + super(TextTestResult, self).addError(test, err) + self.newTestResult(test,"E","ERROR") + + def addFailure(self, test, err): + super(TextTestResult, self).addFailure(test, err) + self.newTestResult(test,"F","FAILURE") + + def addSkip(self, test, reason): + super(TextTestResult, self).addSkip(test, reason) + self.newTestResult(test,"s","skipped %r" % (reason,)) + + def addExpectedFailure(self, test, err, bugnumber): + super(TextTestResult, self).addExpectedFailure(test, err, bugnumber) + self.newTestResult(test,"x","expected failure") + + def addUnexpectedSuccess(self, test, bugnumber): + super(TextTestResult, self).addUnexpectedSuccess(test, bugnumber) + self.newTestResult(test,"u","unexpected success") + + def printErrors(self): + if self.progressbar: + self.progressbar.complete() + self.progressbar.show_progress() + if self.dots or self.showAll: + self.stream.writeln() + self.printErrorList('ERROR', self.errors) + self.printErrorList('FAIL', self.failures) + + def printErrorList(self, flavour, errors): + for test, err in errors: + self.stream.writeln(self.separator1) + self.stream.writeln("%s: %s" % (flavour, self.getDescription(test))) + self.stream.writeln(self.separator2) + self.stream.writeln("%s" % err) + + def stopTestRun(self): + super(TextTestResult, self).stopTestRun() + self.printErrors() + + +class TextTestRunner(unittest.TextTestRunner): + """A test runner class that displays results in textual form. + + It prints out the names of tests as they are run, errors as they + occur, and a summary of the results at the end of the test run. + """ + resultclass = TextTestResult + + def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1, + failfast=False, buffer=False, resultclass=None): + self.stream = _WritelnDecorator(stream) + self.descriptions = descriptions + self.verbosity = verbosity + self.failfast = failfast + self.buffer = buffer + if resultclass is not None: + self.resultclass = resultclass + + def _makeResult(self): + return self.resultclass(self.stream, self.descriptions, self.verbosity) + + def run(self, test): + "Run the given test case or test suite." + result = self._makeResult() + result.failfast = self.failfast + result.buffer = self.buffer + registerResult(result) + + startTime = time.time() + startTestRun = getattr(result, 'startTestRun', None) + if startTestRun is not None: + startTestRun() + try: + test(result) + finally: + stopTestRun = getattr(result, 'stopTestRun', None) + if stopTestRun is not None: + stopTestRun() + else: + result.printErrors() + stopTime = time.time() + timeTaken = stopTime - startTime + if hasattr(result, 'separator2'): + self.stream.writeln(result.separator2) + run = result.testsRun + self.stream.writeln("Ran %d test%s in %.3fs" % + (run, run != 1 and "s" or "", timeTaken)) + self.stream.writeln() + + expectedFails = unexpectedSuccesses = skipped = passed = failed = errored = 0 + try: + results = map(len, (result.expectedFailures, + result.unexpectedSuccesses, + result.skipped, + result.passes, + result.failures, + result.errors)) + expectedFails, unexpectedSuccesses, skipped, passed, failed, errored = results + except AttributeError: + pass + infos = [] + infos.append("%d passes" % passed) + infos.append("%d failures" % failed) + infos.append("%d errors" % errored) + infos.append("%d skipped" % skipped) + infos.append("%d expected failures" % expectedFails) + infos.append("%d unexpected successes" % unexpectedSuccesses) + self.stream.write("RESULT: ") + if not result.wasSuccessful(): + self.stream.write("FAILED") + else: + self.stream.write("PASSED") + + self.stream.writeln(" (%s)" % (", ".join(infos),)) + return result diff --git a/third_party/Python/module/unittest2/unittest2/signals.py b/third_party/Python/module/unittest2/unittest2/signals.py new file mode 100644 index 000000000000..e40328dea7a9 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/signals.py @@ -0,0 +1,57 @@ +import signal +import weakref + +from unittest2.compatibility import wraps + +__unittest = True + + +class _InterruptHandler(object): + def __init__(self, default_handler): + self.called = False + self.default_handler = default_handler + + def __call__(self, signum, frame): + installed_handler = signal.getsignal(signal.SIGINT) + if installed_handler is not self: + # if we aren't the installed handler, then delegate immediately + # to the default handler + self.default_handler(signum, frame) + + if self.called: + self.default_handler(signum, frame) + self.called = True + for result in _results.keys(): + result.stop() + +_results = weakref.WeakKeyDictionary() +def registerResult(result): + _results[result] = 1 + +def removeResult(result): + return bool(_results.pop(result, None)) + +_interrupt_handler = None +def installHandler(): + global _interrupt_handler + if _interrupt_handler is None: + default_handler = signal.getsignal(signal.SIGINT) + _interrupt_handler = _InterruptHandler(default_handler) + signal.signal(signal.SIGINT, _interrupt_handler) + + +def removeHandler(method=None): + if method is not None: + @wraps(method) + def inner(*args, **kwargs): + initial = signal.getsignal(signal.SIGINT) + removeHandler() + try: + return method(*args, **kwargs) + finally: + signal.signal(signal.SIGINT, initial) + return inner + + global _interrupt_handler + if _interrupt_handler is not None: + signal.signal(signal.SIGINT, _interrupt_handler.default_handler) diff --git a/third_party/Python/module/unittest2/unittest2/suite.py b/third_party/Python/module/unittest2/unittest2/suite.py new file mode 100644 index 000000000000..9fda66aa09e5 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/suite.py @@ -0,0 +1,288 @@ +"""TestSuite""" + +import sys +import unittest +from unittest2 import case, util +import six + +__unittest = True + + +class BaseTestSuite(unittest.TestSuite): + """A simple test suite that doesn't provide class or module shared fixtures. + """ + def __init__(self, tests=()): + self._tests = [] + self.addTests(tests) + + def __repr__(self): + return "<%s tests=%s>" % (util.strclass(self.__class__), list(self)) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return list(self) == list(other) + + def __ne__(self, other): + return not self == other + + # Can't guarantee hash invariant, so flag as unhashable + __hash__ = None + + def __iter__(self): + return iter(self._tests) + + def countTestCases(self): + cases = 0 + for test in self: + cases += test.countTestCases() + return cases + + def addTest(self, test): + # sanity checks + if not hasattr(test, '__call__'): + raise TypeError("%r is not callable" % (repr(test),)) + if isinstance(test, type) and issubclass(test, + (case.TestCase, TestSuite)): + raise TypeError("TestCases and TestSuites must be instantiated " + "before passing them to addTest()") + self._tests.append(test) + + def addTests(self, tests): + if isinstance(tests, six.string_types): + raise TypeError("tests must be an iterable of tests, not a string") + for test in tests: + self.addTest(test) + + def run(self, result): + for test in self: + if result.shouldStop: + break + test(result) + return result + + def __call__(self, *args, **kwds): + return self.run(*args, **kwds) + + def debug(self): + """Run the tests without collecting errors in a TestResult""" + for test in self: + test.debug() + + +class TestSuite(BaseTestSuite): + """A test suite is a composite test consisting of a number of TestCases. + + For use, create an instance of TestSuite, then add test case instances. + When all tests have been added, the suite can be passed to a test + runner, such as TextTestRunner. It will run the individual test cases + in the order in which they were added, aggregating the results. When + subclassing, do not forget to call the base class constructor. + """ + + + def run(self, result): + self._wrapped_run(result) + self._tearDownPreviousClass(None, result) + self._handleModuleTearDown(result) + return result + + def debug(self): + """Run the tests without collecting errors in a TestResult""" + debug = _DebugResult() + self._wrapped_run(debug, True) + self._tearDownPreviousClass(None, debug) + self._handleModuleTearDown(debug) + + ################################ + # private methods + def _wrapped_run(self, result, debug=False): + for test in self: + if result.shouldStop: + break + + if _isnotsuite(test): + self._tearDownPreviousClass(test, result) + self._handleModuleFixture(test, result) + self._handleClassSetUp(test, result) + result._previousTestClass = test.__class__ + + if (getattr(test.__class__, '_classSetupFailed', False) or + getattr(result, '_moduleSetUpFailed', False)): + continue + + if hasattr(test, '_wrapped_run'): + test._wrapped_run(result, debug) + elif not debug: + test(result) + else: + test.debug() + + def _handleClassSetUp(self, test, result): + previousClass = getattr(result, '_previousTestClass', None) + currentClass = test.__class__ + if currentClass == previousClass: + return + if result._moduleSetUpFailed: + return + if getattr(currentClass, "__unittest_skip__", False): + return + + try: + currentClass._classSetupFailed = False + except TypeError: + # test may actually be a function + # so its class will be a builtin-type + pass + + setUpClass = getattr(currentClass, 'setUpClass', None) + if setUpClass is not None: + try: + setUpClass() + except Exception as e: + if isinstance(result, _DebugResult): + raise + currentClass._classSetupFailed = True + className = util.strclass(currentClass) + errorName = 'setUpClass (%s)' % className + self._addClassOrModuleLevelException(result, e, errorName) + + def _get_previous_module(self, result): + previousModule = None + previousClass = getattr(result, '_previousTestClass', None) + if previousClass is not None: + previousModule = previousClass.__module__ + return previousModule + + + def _handleModuleFixture(self, test, result): + previousModule = self._get_previous_module(result) + currentModule = test.__class__.__module__ + if currentModule == previousModule: + return + + self._handleModuleTearDown(result) + + + result._moduleSetUpFailed = False + try: + module = sys.modules[currentModule] + except KeyError: + return + setUpModule = getattr(module, 'setUpModule', None) + if setUpModule is not None: + try: + setUpModule() + except Exception as e: + if isinstance(result, _DebugResult): + raise + result._moduleSetUpFailed = True + errorName = 'setUpModule (%s)' % currentModule + self._addClassOrModuleLevelException(result, e, errorName) + + def _addClassOrModuleLevelException(self, result, exception, errorName): + error = _ErrorHolder(errorName) + addSkip = getattr(result, 'addSkip', None) + if addSkip is not None and isinstance(exception, case.SkipTest): + addSkip(error, str(exception)) + else: + result.addError(error, sys.exc_info()) + + def _handleModuleTearDown(self, result): + previousModule = self._get_previous_module(result) + if previousModule is None: + return + if result._moduleSetUpFailed: + return + + try: + module = sys.modules[previousModule] + except KeyError: + return + + tearDownModule = getattr(module, 'tearDownModule', None) + if tearDownModule is not None: + try: + tearDownModule() + except Exception as e: + if isinstance(result, _DebugResult): + raise + errorName = 'tearDownModule (%s)' % previousModule + self._addClassOrModuleLevelException(result, e, errorName) + + def _tearDownPreviousClass(self, test, result): + previousClass = getattr(result, '_previousTestClass', None) + currentClass = test.__class__ + if currentClass == previousClass: + return + if getattr(previousClass, '_classSetupFailed', False): + return + if getattr(result, '_moduleSetUpFailed', False): + return + if getattr(previousClass, "__unittest_skip__", False): + return + + tearDownClass = getattr(previousClass, 'tearDownClass', None) + if tearDownClass is not None: + try: + tearDownClass() + except Exception as e: + if isinstance(result, _DebugResult): + raise + className = util.strclass(previousClass) + errorName = 'tearDownClass (%s)' % className + self._addClassOrModuleLevelException(result, e, errorName) + + +class _ErrorHolder(object): + """ + Placeholder for a TestCase inside a result. As far as a TestResult + is concerned, this looks exactly like a unit test. Used to insert + arbitrary errors into a test suite run. + """ + # Inspired by the ErrorHolder from Twisted: + # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py + + # attribute used by TestResult._exc_info_to_string + failureException = None + + def __init__(self, description): + self.description = description + + def id(self): + return self.description + + def shortDescription(self): + return None + + def __repr__(self): + return "" % (self.description,) + + def __str__(self): + return self.id() + + def run(self, result): + # could call result.addError(...) - but this test-like object + # shouldn't be run anyway + pass + + def __call__(self, result): + return self.run(result) + + def countTestCases(self): + return 0 + +def _isnotsuite(test): + "A crude way to tell apart testcases and suites with duck-typing" + try: + iter(test) + except TypeError: + return True + return False + + +class _DebugResult(object): + "Used by the TestSuite to hold previous class when running in debug." + _previousTestClass = None + _moduleSetUpFailed = False + shouldStop = False diff --git a/third_party/Python/module/unittest2/unittest2/test/__init__.py b/third_party/Python/module/unittest2/unittest2/test/__init__.py new file mode 100644 index 000000000000..4287ca861797 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/__init__.py @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/third_party/Python/module/unittest2/unittest2/test/dummy.py b/third_party/Python/module/unittest2/unittest2/test/dummy.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/third_party/Python/module/unittest2/unittest2/test/support.py b/third_party/Python/module/unittest2/unittest2/test/support.py new file mode 100644 index 000000000000..eec6eec77907 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/support.py @@ -0,0 +1,177 @@ +import sys +import warnings + +import unittest2 + + +def resultFactory(*_): + return unittest2.TestResult() + +class OldTestResult(object): + """An object honouring TestResult before startTestRun/stopTestRun.""" + + def __init__(self, *_): + self.failures = [] + self.errors = [] + self.testsRun = 0 + self.shouldStop = False + + def startTest(self, test): + pass + + def stopTest(self, test): + pass + + def addError(self, test, err): + self.errors.append((test, err)) + + def addFailure(self, test, err): + self.failures.append((test, err)) + + def addSuccess(self, test): + pass + + def wasSuccessful(self): + return True + + def printErrors(self): + pass + +class LoggingResult(unittest2.TestResult): + def __init__(self, log): + self._events = log + super(LoggingResult, self).__init__() + + def startTest(self, test): + self._events.append('startTest') + super(LoggingResult, self).startTest(test) + + def startTestRun(self): + self._events.append('startTestRun') + super(LoggingResult, self).startTestRun() + + def stopTest(self, test): + self._events.append('stopTest') + super(LoggingResult, self).stopTest(test) + + def stopTestRun(self): + self._events.append('stopTestRun') + super(LoggingResult, self).stopTestRun() + + def addFailure(self, *args): + self._events.append('addFailure') + super(LoggingResult, self).addFailure(*args) + + def addSuccess(self, *args): + self._events.append('addSuccess') + super(LoggingResult, self).addSuccess(*args) + + def addError(self, *args): + self._events.append('addError') + super(LoggingResult, self).addError(*args) + + def addSkip(self, *args): + self._events.append('addSkip') + super(LoggingResult, self).addSkip(*args) + + def addExpectedFailure(self, *args): + self._events.append('addExpectedFailure') + super(LoggingResult, self).addExpectedFailure(*args) + + def addUnexpectedSuccess(self, *args): + self._events.append('addUnexpectedSuccess') + super(LoggingResult, self).addUnexpectedSuccess(*args) + + +class EqualityMixin(object): + """Used as a mixin for TestCase""" + + # Check for a valid __eq__ implementation + def test_eq(self): + for obj_1, obj_2 in self.eq_pairs: + self.assertEqual(obj_1, obj_2) + self.assertEqual(obj_2, obj_1) + + # Check for a valid __ne__ implementation + def test_ne(self): + for obj_1, obj_2 in self.ne_pairs: + self.assertNotEqual(obj_1, obj_2) + self.assertNotEqual(obj_2, obj_1) + +class HashingMixin(object): + """Used as a mixin for TestCase""" + + # Check for a valid __hash__ implementation + def test_hash(self): + for obj_1, obj_2 in self.eq_pairs: + try: + if not hash(obj_1) == hash(obj_2): + self.fail("%r and %r do not hash equal" % (obj_1, obj_2)) + except KeyboardInterrupt: + raise + except Exception as e: + self.fail("Problem hashing %r and %r: %s" % (obj_1, obj_2, e)) + + for obj_1, obj_2 in self.ne_pairs: + try: + if hash(obj_1) == hash(obj_2): + self.fail("%s and %s hash equal, but shouldn't" % + (obj_1, obj_2)) + except KeyboardInterrupt: + raise + except Exception as e: + self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e)) + + + +# copied from Python 2.6 +try: + from warnings import catch_warnings +except ImportError: + class catch_warnings(object): + def __init__(self, record=False, module=None): + self._record = record + self._module = sys.modules['warnings'] + self._entered = False + + def __repr__(self): + args = [] + if self._record: + args.append("record=True") + name = type(self).__name__ + return "%s(%s)" % (name, ", ".join(args)) + + def __enter__(self): + if self._entered: + raise RuntimeError("Cannot enter %r twice" % self) + self._entered = True + self._filters = self._module.filters + self._module.filters = self._filters[:] + self._showwarning = self._module.showwarning + if self._record: + log = [] + def showwarning(*args, **kwargs): + log.append(WarningMessage(*args, **kwargs)) + self._module.showwarning = showwarning + return log + else: + return None + + def __exit__(self, *exc_info): + if not self._entered: + raise RuntimeError("Cannot exit %r without entering first" % self) + self._module.filters = self._filters + self._module.showwarning = self._showwarning + + class WarningMessage(object): + _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", + "line") + def __init__(self, message, category, filename, lineno, file=None, + line=None): + local_values = locals() + for attr in self._WARNING_DETAILS: + setattr(self, attr, local_values[attr]) + self._category_name = None + if category.__name__: + self._category_name = category.__name__ + diff --git a/third_party/Python/module/unittest2/unittest2/test/test_assertions.py b/third_party/Python/module/unittest2/unittest2/test/test_assertions.py new file mode 100644 index 000000000000..d71326b218a9 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_assertions.py @@ -0,0 +1,254 @@ +import datetime + +import unittest2 + + +class Test_Assertions(unittest2.TestCase): + def test_AlmostEqual(self): + self.assertAlmostEqual(1.00000001, 1.0) + self.assertNotAlmostEqual(1.0000001, 1.0) + self.assertRaises(self.failureException, + self.assertAlmostEqual, 1.0000001, 1.0) + self.assertRaises(self.failureException, + self.assertNotAlmostEqual, 1.00000001, 1.0) + + self.assertAlmostEqual(1.1, 1.0, places=0) + self.assertRaises(self.failureException, + self.assertAlmostEqual, 1.1, 1.0, places=1) + + self.assertAlmostEqual(0, .1+.1j, places=0) + self.assertNotAlmostEqual(0, .1+.1j, places=1) + self.assertRaises(self.failureException, + self.assertAlmostEqual, 0, .1+.1j, places=1) + self.assertRaises(self.failureException, + self.assertNotAlmostEqual, 0, .1+.1j, places=0) + + try: + self.assertAlmostEqual(float('inf'), float('inf')) + self.assertRaises(self.failureException, self.assertNotAlmostEqual, + float('inf'), float('inf')) + except ValueError: + # float('inf') is invalid on Windows in Python 2.4 / 2.5 + x = object() + self.assertAlmostEqual(x, x) + self.assertRaises(self.failureException, self.assertNotAlmostEqual, + x, x) + + + def test_AmostEqualWithDelta(self): + self.assertAlmostEqual(1.1, 1.0, delta=0.5) + self.assertAlmostEqual(1.0, 1.1, delta=0.5) + self.assertNotAlmostEqual(1.1, 1.0, delta=0.05) + self.assertNotAlmostEqual(1.0, 1.1, delta=0.05) + + self.assertRaises(self.failureException, self.assertAlmostEqual, + 1.1, 1.0, delta=0.05) + self.assertRaises(self.failureException, self.assertNotAlmostEqual, + 1.1, 1.0, delta=0.5) + + self.assertRaises(TypeError, self.assertAlmostEqual, + 1.1, 1.0, places=2, delta=2) + self.assertRaises(TypeError, self.assertNotAlmostEqual, + 1.1, 1.0, places=2, delta=2) + + first = datetime.datetime.now() + second = first + datetime.timedelta(seconds=10) + self.assertAlmostEqual(first, second, + delta=datetime.timedelta(seconds=20)) + self.assertNotAlmostEqual(first, second, + delta=datetime.timedelta(seconds=5)) + + def testAssertNotRegexpMatches(self): + self.assertNotRegexpMatches('Ala ma kota', r'r+') + try: + self.assertNotRegexpMatches('Ala ma kota', r'k.t', 'Message') + except self.failureException as e: + self.assertIn("'kot'", e.args[0]) + self.assertIn('Message', e.args[0]) + else: + self.fail('assertNotRegexpMatches should have failed.') + + +class TestLongMessage(unittest2.TestCase): + """Test that the individual asserts honour longMessage. + This actually tests all the message behaviour for + asserts that use longMessage.""" + + def setUp(self): + class TestableTestFalse(unittest2.TestCase): + longMessage = False + failureException = self.failureException + + def testTest(self): + pass + + class TestableTestTrue(unittest2.TestCase): + longMessage = True + failureException = self.failureException + + def testTest(self): + pass + + self.testableTrue = TestableTestTrue('testTest') + self.testableFalse = TestableTestFalse('testTest') + + def testDefault(self): + self.assertTrue(unittest2.TestCase.longMessage) + + def test_formatMsg(self): + self.assertEquals(self.testableFalse._formatMessage(None, "foo"), "foo") + self.assertEquals(self.testableFalse._formatMessage("foo", "bar"), "foo") + + self.assertEquals(self.testableTrue._formatMessage(None, "foo"), "foo") + self.assertEquals(self.testableTrue._formatMessage("foo", "bar"), "bar : foo") + + # This blows up if _formatMessage uses string concatenation + self.testableTrue._formatMessage(object(), 'foo') + + def assertMessages(self, methodName, args, errors): + def getMethod(i): + useTestableFalse = i < 2 + if useTestableFalse: + test = self.testableFalse + else: + test = self.testableTrue + return getattr(test, methodName) + + for i, expected_regexp in enumerate(errors): + testMethod = getMethod(i) + kwargs = {} + withMsg = i % 2 + if withMsg: + kwargs = {"msg": "oops"} + + self.assertRaisesRegexp(self.failureException, + expected_regexp, + lambda: testMethod(*args, **kwargs)) + + def testAssertTrue(self): + self.assertMessages('assertTrue', (False,), + ["^False is not True$", "^oops$", "^False is not True$", + "^False is not True : oops$"]) + + def testAssertFalse(self): + self.assertMessages('assertFalse', (True,), + ["^True is not False$", "^oops$", "^True is not False$", + "^True is not False : oops$"]) + + def testNotEqual(self): + self.assertMessages('assertNotEqual', (1, 1), + ["^1 == 1$", "^oops$", "^1 == 1$", + "^1 == 1 : oops$"]) + + def testAlmostEqual(self): + self.assertMessages('assertAlmostEqual', (1, 2), + ["^1 != 2 within 7 places$", "^oops$", + "^1 != 2 within 7 places$", "^1 != 2 within 7 places : oops$"]) + + def testNotAlmostEqual(self): + self.assertMessages('assertNotAlmostEqual', (1, 1), + ["^1 == 1 within 7 places$", "^oops$", + "^1 == 1 within 7 places$", "^1 == 1 within 7 places : oops$"]) + + def test_baseAssertEqual(self): + self.assertMessages('_baseAssertEqual', (1, 2), + ["^1 != 2$", "^oops$", "^1 != 2$", "^1 != 2 : oops$"]) + + def testAssertSequenceEqual(self): + # Error messages are multiline so not testing on full message + # assertTupleEqual and assertListEqual delegate to this method + self.assertMessages('assertSequenceEqual', ([], [None]), + ["\+ \[None\]$", "^oops$", r"\+ \[None\]$", + r"\+ \[None\] : oops$"]) + + def testAssertSetEqual(self): + self.assertMessages('assertSetEqual', (set(), set([None])), + ["None$", "^oops$", "None$", + "None : oops$"]) + + def testAssertIn(self): + self.assertMessages('assertIn', (None, []), + ['^None not found in \[\]$', "^oops$", + '^None not found in \[\]$', + '^None not found in \[\] : oops$']) + + def testAssertNotIn(self): + self.assertMessages('assertNotIn', (None, [None]), + ['^None unexpectedly found in \[None\]$', "^oops$", + '^None unexpectedly found in \[None\]$', + '^None unexpectedly found in \[None\] : oops$']) + + def testAssertDictEqual(self): + self.assertMessages('assertDictEqual', ({}, {'key': 'value'}), + [r"\+ \{'key': 'value'\}$", "^oops$", + "\+ \{'key': 'value'\}$", + "\+ \{'key': 'value'\} : oops$"]) + + def testAssertDictContainsSubset(self): + self.assertMessages('assertDictContainsSubset', ({'key': 'value'}, {}), + ["^Missing: 'key'$", "^oops$", + "^Missing: 'key'$", + "^Missing: 'key' : oops$"]) + + def testAssertItemsEqual(self): + self.assertMessages('assertItemsEqual', ([], [None]), + [r"\[None\]$", "^oops$", + r"\[None\]$", + r"\[None\] : oops$"]) + + def testAssertMultiLineEqual(self): + self.assertMessages('assertMultiLineEqual', ("", "foo"), + [r"\+ foo$", "^oops$", + r"\+ foo$", + r"\+ foo : oops$"]) + + def testAssertLess(self): + self.assertMessages('assertLess', (2, 1), + ["^2 not less than 1$", "^oops$", + "^2 not less than 1$", "^2 not less than 1 : oops$"]) + + def testAssertLessEqual(self): + self.assertMessages('assertLessEqual', (2, 1), + ["^2 not less than or equal to 1$", "^oops$", + "^2 not less than or equal to 1$", + "^2 not less than or equal to 1 : oops$"]) + + def testAssertGreater(self): + self.assertMessages('assertGreater', (1, 2), + ["^1 not greater than 2$", "^oops$", + "^1 not greater than 2$", + "^1 not greater than 2 : oops$"]) + + def testAssertGreaterEqual(self): + self.assertMessages('assertGreaterEqual', (1, 2), + ["^1 not greater than or equal to 2$", "^oops$", + "^1 not greater than or equal to 2$", + "^1 not greater than or equal to 2 : oops$"]) + + def testAssertIsNone(self): + self.assertMessages('assertIsNone', ('not None',), + ["^'not None' is not None$", "^oops$", + "^'not None' is not None$", + "^'not None' is not None : oops$"]) + + def testAssertIsNotNone(self): + self.assertMessages('assertIsNotNone', (None,), + ["^unexpectedly None$", "^oops$", + "^unexpectedly None$", + "^unexpectedly None : oops$"]) + + def testAssertIs(self): + self.assertMessages('assertIs', (None, 'foo'), + ["^None is not 'foo'$", "^oops$", + "^None is not 'foo'$", + "^None is not 'foo' : oops$"]) + + def testAssertIsNot(self): + self.assertMessages('assertIsNot', (None, None), + ["^unexpectedly identical: None$", "^oops$", + "^unexpectedly identical: None$", + "^unexpectedly identical: None : oops$"]) + + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_break.py b/third_party/Python/module/unittest2/unittest2/test/test_break.py new file mode 100644 index 000000000000..1f7864b7a415 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_break.py @@ -0,0 +1,260 @@ +import gc +import os +import weakref + +from cStringIO import StringIO + +try: + import signal +except ImportError: + signal = None + +import unittest2 + + +class TestBreak(unittest2.TestCase): + + def setUp(self): + self._default_handler = signal.getsignal(signal.SIGINT) + + def tearDown(self): + signal.signal(signal.SIGINT, self._default_handler) + unittest2.signals._results = weakref.WeakKeyDictionary() + unittest2.signals._interrupt_handler = None + + + def testInstallHandler(self): + default_handler = signal.getsignal(signal.SIGINT) + unittest2.installHandler() + self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler) + + try: + pid = os.getpid() + os.kill(pid, signal.SIGINT) + except KeyboardInterrupt: + self.fail("KeyboardInterrupt not handled") + + self.assertTrue(unittest2.signals._interrupt_handler.called) + + def testRegisterResult(self): + result = unittest2.TestResult() + unittest2.registerResult(result) + + for ref in unittest2.signals._results: + if ref is result: + break + elif ref is not result: + self.fail("odd object in result set") + else: + self.fail("result not found") + + + def testInterruptCaught(self): + default_handler = signal.getsignal(signal.SIGINT) + + result = unittest2.TestResult() + unittest2.installHandler() + unittest2.registerResult(result) + + self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler) + + def test(result): + pid = os.getpid() + os.kill(pid, signal.SIGINT) + result.breakCaught = True + self.assertTrue(result.shouldStop) + + try: + test(result) + except KeyboardInterrupt: + self.fail("KeyboardInterrupt not handled") + self.assertTrue(result.breakCaught) + + + def testSecondInterrupt(self): + result = unittest2.TestResult() + unittest2.installHandler() + unittest2.registerResult(result) + + def test(result): + pid = os.getpid() + os.kill(pid, signal.SIGINT) + result.breakCaught = True + self.assertTrue(result.shouldStop) + os.kill(pid, signal.SIGINT) + self.fail("Second KeyboardInterrupt not raised") + + try: + test(result) + except KeyboardInterrupt: + pass + else: + self.fail("Second KeyboardInterrupt not raised") + self.assertTrue(result.breakCaught) + + + def testTwoResults(self): + unittest2.installHandler() + + result = unittest2.TestResult() + unittest2.registerResult(result) + new_handler = signal.getsignal(signal.SIGINT) + + result2 = unittest2.TestResult() + unittest2.registerResult(result2) + self.assertEqual(signal.getsignal(signal.SIGINT), new_handler) + + result3 = unittest2.TestResult() + + def test(result): + pid = os.getpid() + os.kill(pid, signal.SIGINT) + + try: + test(result) + except KeyboardInterrupt: + self.fail("KeyboardInterrupt not handled") + + self.assertTrue(result.shouldStop) + self.assertTrue(result2.shouldStop) + self.assertFalse(result3.shouldStop) + + + def testHandlerReplacedButCalled(self): + # If our handler has been replaced (is no longer installed) but is + # called by the *new* handler, then it isn't safe to delay the + # SIGINT and we should immediately delegate to the default handler + unittest2.installHandler() + + handler = signal.getsignal(signal.SIGINT) + def new_handler(frame, signum): + handler(frame, signum) + signal.signal(signal.SIGINT, new_handler) + + try: + pid = os.getpid() + os.kill(pid, signal.SIGINT) + except KeyboardInterrupt: + pass + else: + self.fail("replaced but delegated handler doesn't raise interrupt") + + def testRunner(self): + # Creating a TextTestRunner with the appropriate argument should + # register the TextTestResult it creates + runner = unittest2.TextTestRunner(stream=StringIO()) + + result = runner.run(unittest2.TestSuite()) + self.assertIn(result, unittest2.signals._results) + + def testWeakReferences(self): + # Calling registerResult on a result should not keep it alive + result = unittest2.TestResult() + unittest2.registerResult(result) + + ref = weakref.ref(result) + del result + + # For non-reference counting implementations + gc.collect();gc.collect() + self.assertIsNone(ref()) + + + def testRemoveResult(self): + result = unittest2.TestResult() + unittest2.registerResult(result) + + unittest2.installHandler() + self.assertTrue(unittest2.removeResult(result)) + + # Should this raise an error instead? + self.assertFalse(unittest2.removeResult(unittest2.TestResult())) + + try: + pid = os.getpid() + os.kill(pid, signal.SIGINT) + except KeyboardInterrupt: + pass + + self.assertFalse(result.shouldStop) + + def testMainInstallsHandler(self): + failfast = object() + test = object() + verbosity = object() + result = object() + default_handler = signal.getsignal(signal.SIGINT) + + class FakeRunner(object): + initArgs = [] + runArgs = [] + def __init__(self, *args, **kwargs): + self.initArgs.append((args, kwargs)) + def run(self, test): + self.runArgs.append(test) + return result + + class Program(unittest2.TestProgram): + def __init__(self, catchbreak): + self.exit = False + self.verbosity = verbosity + self.failfast = failfast + self.catchbreak = catchbreak + self.testRunner = FakeRunner + self.test = test + self.result = None + + p = Program(False) + p.runTests() + + self.assertEqual(FakeRunner.initArgs, [((), {'verbosity': verbosity, + 'failfast': failfast, + 'buffer': None})]) + self.assertEqual(FakeRunner.runArgs, [test]) + self.assertEqual(p.result, result) + + self.assertEqual(signal.getsignal(signal.SIGINT), default_handler) + + FakeRunner.initArgs = [] + FakeRunner.runArgs = [] + p = Program(True) + p.runTests() + + self.assertEqual(FakeRunner.initArgs, [((), {'verbosity': verbosity, + 'failfast': failfast, + 'buffer': None})]) + self.assertEqual(FakeRunner.runArgs, [test]) + self.assertEqual(p.result, result) + + self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler) + + + def testRemoveHandler(self): + default_handler = signal.getsignal(signal.SIGINT) + unittest2.installHandler() + unittest2.removeHandler() + self.assertEqual(signal.getsignal(signal.SIGINT), default_handler) + + # check that calling removeHandler multiple times has no ill-effect + unittest2.removeHandler() + self.assertEqual(signal.getsignal(signal.SIGINT), default_handler) + + def testRemoveHandlerAsDecorator(self): + default_handler = signal.getsignal(signal.SIGINT) + unittest2.installHandler() + + @unittest2.removeHandler + def test(): + self.assertEqual(signal.getsignal(signal.SIGINT), default_handler) + + test() + self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler) + + +# Should also skip some tests on Jython +skipper = unittest2.skipUnless(hasattr(os, 'kill') and signal is not None, + "test uses os.kill(...) and the signal module") +TestBreak = skipper(TestBreak) + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_case.py b/third_party/Python/module/unittest2/unittest2/test/test_case.py new file mode 100644 index 000000000000..5f310ecb046e --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_case.py @@ -0,0 +1,1066 @@ +import difflib +import pprint +import re +import six + +from copy import deepcopy + +import unittest2 + +from unittest2.test.support import ( + OldTestResult, EqualityMixin, HashingMixin, LoggingResult +) + + +class MyException(Exception): + pass + + +class Test(object): + "Keep these TestCase classes out of the main namespace" + + class Foo(unittest2.TestCase): + def runTest(self): pass + def test1(self): pass + + class Bar(Foo): + def test2(self): pass + + class LoggingTestCase(unittest2.TestCase): + """A test case which logs its calls.""" + + def __init__(self, events): + super(Test.LoggingTestCase, self).__init__('test') + self.events = events + + def setUp(self): + self.events.append('setUp') + + def test(self): + self.events.append('test') + + def tearDown(self): + self.events.append('tearDown') + + + +class TestCleanUp(unittest2.TestCase): + + def testCleanUp(self): + class TestableTest(unittest2.TestCase): + def testNothing(self): + pass + + test = TestableTest('testNothing') + self.assertEqual(test._cleanups, []) + + cleanups = [] + + def cleanup1(*args, **kwargs): + cleanups.append((1, args, kwargs)) + + def cleanup2(*args, **kwargs): + cleanups.append((2, args, kwargs)) + + test.addCleanup(cleanup1, 1, 2, 3, four='hello', five='goodbye') + test.addCleanup(cleanup2) + + self.assertEqual(test._cleanups, + [(cleanup1, (1, 2, 3), dict(four='hello', five='goodbye')), + (cleanup2, (), {})]) + + result = test.doCleanups() + self.assertTrue(result) + + self.assertEqual(cleanups, [(2, (), {}), (1, (1, 2, 3), dict(four='hello', five='goodbye'))]) + + def testCleanUpWithErrors(self): + class TestableTest(unittest2.TestCase): + def testNothing(self): + pass + + class MockResult(object): + errors = [] + def addError(self, test, exc_info): + self.errors.append((test, exc_info)) + + result = MockResult() + test = TestableTest('testNothing') + test._resultForDoCleanups = result + + exc1 = Exception('foo') + exc2 = Exception('bar') + def cleanup1(): + raise exc1 + + def cleanup2(): + raise exc2 + + test.addCleanup(cleanup1) + test.addCleanup(cleanup2) + + self.assertFalse(test.doCleanups()) + + (test1, (Type1, instance1, _)), (test2, (Type2, instance2, _)) = reversed(MockResult.errors) + self.assertEqual((test1, Type1, instance1), (test, Exception, exc1)) + self.assertEqual((test2, Type2, instance2), (test, Exception, exc2)) + + def testCleanupInRun(self): + blowUp = False + ordering = [] + + class TestableTest(unittest2.TestCase): + def setUp(self): + ordering.append('setUp') + if blowUp: + raise Exception('foo') + + def testNothing(self): + ordering.append('test') + + def tearDown(self): + ordering.append('tearDown') + + test = TestableTest('testNothing') + + def cleanup1(): + ordering.append('cleanup1') + def cleanup2(): + ordering.append('cleanup2') + test.addCleanup(cleanup1) + test.addCleanup(cleanup2) + + def success(some_test): + self.assertEqual(some_test, test) + ordering.append('success') + + result = unittest2.TestResult() + result.addSuccess = success + + test.run(result) + self.assertEqual(ordering, ['setUp', 'test', 'tearDown', + 'cleanup2', 'cleanup1', 'success']) + + blowUp = True + ordering = [] + test = TestableTest('testNothing') + test.addCleanup(cleanup1) + test.run(result) + self.assertEqual(ordering, ['setUp', 'cleanup1']) + + def testTestCaseDebugExecutesCleanups(self): + ordering = [] + + class TestableTest(unittest2.TestCase): + def setUp(self): + ordering.append('setUp') + self.addCleanup(cleanup1) + + def testNothing(self): + ordering.append('test') + + def tearDown(self): + ordering.append('tearDown') + + test = TestableTest('testNothing') + + def cleanup1(): + ordering.append('cleanup1') + test.addCleanup(cleanup2) + def cleanup2(): + ordering.append('cleanup2') + + test.debug() + self.assertEqual(ordering, ['setUp', 'test', 'tearDown', 'cleanup1', 'cleanup2']) + + +class Test_TestCase(unittest2.TestCase, EqualityMixin, HashingMixin): + + ### Set up attributes used by inherited tests + ################################################################ + + # Used by HashingMixin.test_hash and EqualityMixin.test_eq + eq_pairs = [(Test.Foo('test1'), Test.Foo('test1'))] + + # Used by EqualityMixin.test_ne + ne_pairs = [(Test.Foo('test1'), Test.Foo('runTest')), + (Test.Foo('test1'), Test.Bar('test1')), + (Test.Foo('test1'), Test.Bar('test2'))] + + ################################################################ + ### /Set up attributes used by inherited tests + + + # "class TestCase([methodName])" + # ... + # "Each instance of TestCase will run a single test method: the + # method named methodName." + # ... + # "methodName defaults to "runTest"." + # + # Make sure it really is optional, and that it defaults to the proper + # thing. + def test_init__no_test_name(self): + class Test(unittest2.TestCase): + def runTest(self): raise MyException() + def test(self): pass + + self.assertEqual(Test().id()[-13:], '.Test.runTest') + + # "class TestCase([methodName])" + # ... + # "Each instance of TestCase will run a single test method: the + # method named methodName." + def test_init__test_name__valid(self): + class Test(unittest2.TestCase): + def runTest(self): raise MyException() + def test(self): pass + + self.assertEqual(Test('test').id()[-10:], '.Test.test') + + # "class unittest2.TestCase([methodName])" + # ... + # "Each instance of TestCase will run a single test method: the + # method named methodName." + def test_init__test_name__invalid(self): + class Test(unittest2.TestCase): + def runTest(self): raise MyException() + def test(self): pass + + try: + Test('testfoo') + except ValueError: + pass + else: + self.fail("Failed to raise ValueError") + + # "Return the number of tests represented by the this test object. For + # TestCase instances, this will always be 1" + def test_countTestCases(self): + class Foo(unittest2.TestCase): + def test(self): pass + + self.assertEqual(Foo('test').countTestCases(), 1) + + # "Return the default type of test result object to be used to run this + # test. For TestCase instances, this will always be + # unittest2.TestResult; subclasses of TestCase should + # override this as necessary." + def test_defaultTestResult(self): + class Foo(unittest2.TestCase): + def runTest(self): + pass + + result = Foo().defaultTestResult() + self.assertEqual(type(result), unittest2.TestResult) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if setUp() raises + # an exception. + def test_run_call_order__error_in_setUp(self): + events = [] + result = LoggingResult(events) + + class Foo(Test.LoggingTestCase): + def setUp(self): + super(Foo, self).setUp() + raise RuntimeError('raised by Foo.setUp') + + Foo(events).run(result) + expected = ['startTest', 'setUp', 'addError', 'stopTest'] + self.assertEqual(events, expected) + + # "With a temporary result stopTestRun is called when setUp errors. + def test_run_call_order__error_in_setUp_default_result(self): + events = [] + + class Foo(Test.LoggingTestCase): + def defaultTestResult(self): + return LoggingResult(self.events) + + def setUp(self): + super(Foo, self).setUp() + raise RuntimeError('raised by Foo.setUp') + + Foo(events).run() + expected = ['startTestRun', 'startTest', 'setUp', 'addError', + 'stopTest', 'stopTestRun'] + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if the test raises + # an error (as opposed to a failure). + def test_run_call_order__error_in_test(self): + events = [] + result = LoggingResult(events) + + class Foo(Test.LoggingTestCase): + def test(self): + super(Foo, self).test() + raise RuntimeError('raised by Foo.test') + + expected = ['startTest', 'setUp', 'test', 'addError', 'tearDown', + 'stopTest'] + Foo(events).run(result) + self.assertEqual(events, expected) + + # "With a default result, an error in the test still results in stopTestRun + # being called." + def test_run_call_order__error_in_test_default_result(self): + events = [] + + class Foo(Test.LoggingTestCase): + def defaultTestResult(self): + return LoggingResult(self.events) + + def test(self): + super(Foo, self).test() + raise RuntimeError('raised by Foo.test') + + expected = ['startTestRun', 'startTest', 'setUp', 'test', 'addError', + 'tearDown', 'stopTest', 'stopTestRun'] + Foo(events).run() + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if the test signals + # a failure (as opposed to an error). + def test_run_call_order__failure_in_test(self): + events = [] + result = LoggingResult(events) + + class Foo(Test.LoggingTestCase): + def test(self): + super(Foo, self).test() + self.fail('raised by Foo.test') + + expected = ['startTest', 'setUp', 'test', 'addFailure', 'tearDown', + 'stopTest'] + Foo(events).run(result) + self.assertEqual(events, expected) + + # "When a test fails with a default result stopTestRun is still called." + def test_run_call_order__failure_in_test_default_result(self): + + class Foo(Test.LoggingTestCase): + def defaultTestResult(self): + return LoggingResult(self.events) + def test(self): + super(Foo, self).test() + self.fail('raised by Foo.test') + + expected = ['startTestRun', 'startTest', 'setUp', 'test', 'addFailure', + 'tearDown', 'stopTest', 'stopTestRun'] + events = [] + Foo(events).run() + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if tearDown() raises + # an exception. + def test_run_call_order__error_in_tearDown(self): + events = [] + result = LoggingResult(events) + + class Foo(Test.LoggingTestCase): + def tearDown(self): + super(Foo, self).tearDown() + raise RuntimeError('raised by Foo.tearDown') + + Foo(events).run(result) + expected = ['startTest', 'setUp', 'test', 'tearDown', 'addError', + 'stopTest'] + self.assertEqual(events, expected) + + # "When tearDown errors with a default result stopTestRun is still called." + def test_run_call_order__error_in_tearDown_default_result(self): + + class Foo(Test.LoggingTestCase): + def defaultTestResult(self): + return LoggingResult(self.events) + def tearDown(self): + super(Foo, self).tearDown() + raise RuntimeError('raised by Foo.tearDown') + + events = [] + Foo(events).run() + expected = ['startTestRun', 'startTest', 'setUp', 'test', 'tearDown', + 'addError', 'stopTest', 'stopTestRun'] + self.assertEqual(events, expected) + + # "TestCase.run() still works when the defaultTestResult is a TestResult + # that does not support startTestRun and stopTestRun. + def test_run_call_order_default_result(self): + + class Foo(unittest2.TestCase): + def defaultTestResult(self): + return OldTestResult() + def test(self): + pass + + Foo('test').run() + + # "This class attribute gives the exception raised by the test() method. + # If a test framework needs to use a specialized exception, possibly to + # carry additional information, it must subclass this exception in + # order to ``play fair'' with the framework. The initial value of this + # attribute is AssertionError" + def test_failureException__default(self): + class Foo(unittest2.TestCase): + def test(self): + pass + + self.assertTrue(Foo('test').failureException is AssertionError) + + # "This class attribute gives the exception raised by the test() method. + # If a test framework needs to use a specialized exception, possibly to + # carry additional information, it must subclass this exception in + # order to ``play fair'' with the framework." + # + # Make sure TestCase.run() respects the designated failureException + def test_failureException__subclassing__explicit_raise(self): + events = [] + result = LoggingResult(events) + + class Foo(unittest2.TestCase): + def test(self): + raise RuntimeError() + + failureException = RuntimeError + + self.assertTrue(Foo('test').failureException is RuntimeError) + + + Foo('test').run(result) + expected = ['startTest', 'addFailure', 'stopTest'] + self.assertEqual(events, expected) + + # "This class attribute gives the exception raised by the test() method. + # If a test framework needs to use a specialized exception, possibly to + # carry additional information, it must subclass this exception in + # order to ``play fair'' with the framework." + # + # Make sure TestCase.run() respects the designated failureException + def test_failureException__subclassing__implicit_raise(self): + events = [] + result = LoggingResult(events) + + class Foo(unittest2.TestCase): + def test(self): + self.fail("foo") + + failureException = RuntimeError + + self.assertTrue(Foo('test').failureException is RuntimeError) + + + Foo('test').run(result) + expected = ['startTest', 'addFailure', 'stopTest'] + self.assertEqual(events, expected) + + # "The default implementation does nothing." + def test_setUp(self): + class Foo(unittest2.TestCase): + def runTest(self): + pass + + # ... and nothing should happen + Foo().setUp() + + # "The default implementation does nothing." + def test_tearDown(self): + class Foo(unittest2.TestCase): + def runTest(self): + pass + + # ... and nothing should happen + Foo().tearDown() + + # "Return a string identifying the specific test case." + # + # Because of the vague nature of the docs, I'm not going to lock this + # test down too much. Really all that can be asserted is that the id() + # will be a string (either 8-byte or unicode -- again, because the docs + # just say "string") + def test_id(self): + class Foo(unittest2.TestCase): + def runTest(self): + pass + + self.assertIsInstance(Foo().id(), six.string_types) + + # "If result is omitted or None, a temporary result object is created + # and used, but is not made available to the caller. As TestCase owns the + # temporary result startTestRun and stopTestRun are called. + + def test_run__uses_defaultTestResult(self): + events = [] + + class Foo(unittest2.TestCase): + def test(self): + events.append('test') + + def defaultTestResult(self): + return LoggingResult(events) + + # Make run() find a result object on its own + Foo('test').run() + + expected = ['startTestRun', 'startTest', 'test', 'addSuccess', + 'stopTest', 'stopTestRun'] + self.assertEqual(events, expected) + + def testShortDescriptionWithoutDocstring(self): + self.assertIsNone(self.shortDescription()) + + def testShortDescriptionWithOneLineDocstring(self): + """Tests shortDescription() for a method with a docstring.""" + self.assertEqual( + self.shortDescription(), + 'Tests shortDescription() for a method with a docstring.') + + def testShortDescriptionWithMultiLineDocstring(self): + """Tests shortDescription() for a method with a longer docstring. + + This method ensures that only the first line of a docstring is + returned used in the short description, no matter how long the + whole thing is. + """ + self.assertEqual( + self.shortDescription(), + 'Tests shortDescription() for a method with a longer ' + 'docstring.') + + def testAddTypeEqualityFunc(self): + class SadSnake(object): + """Dummy class for test_addTypeEqualityFunc.""" + s1, s2 = SadSnake(), SadSnake() + self.assertNotEqual(s1, s2) + def AllSnakesCreatedEqual(a, b, msg=None): + return type(a) is type(b) is SadSnake + self.addTypeEqualityFunc(SadSnake, AllSnakesCreatedEqual) + self.assertEqual(s1, s2) + # No this doesn't clean up and remove the SadSnake equality func + # from this TestCase instance but since its a local nothing else + # will ever notice that. + + def testAssertIs(self): + thing = object() + self.assertIs(thing, thing) + self.assertRaises(self.failureException, self.assertIs, thing, object()) + + def testAssertIsNot(self): + thing = object() + self.assertIsNot(thing, object()) + self.assertRaises(self.failureException, self.assertIsNot, thing, thing) + + def testAssertIsInstance(self): + thing = [] + self.assertIsInstance(thing, list) + self.assertRaises(self.failureException, self.assertIsInstance, + thing, dict) + + def testAssertNotIsInstance(self): + thing = [] + self.assertNotIsInstance(thing, dict) + self.assertRaises(self.failureException, self.assertNotIsInstance, + thing, list) + + def testAssertIn(self): + animals = {'monkey': 'banana', 'cow': 'grass', 'seal': 'fish'} + + self.assertIn('a', 'abc') + self.assertIn(2, [1, 2, 3]) + self.assertIn('monkey', animals) + + self.assertNotIn('d', 'abc') + self.assertNotIn(0, [1, 2, 3]) + self.assertNotIn('otter', animals) + + self.assertRaises(self.failureException, self.assertIn, 'x', 'abc') + self.assertRaises(self.failureException, self.assertIn, 4, [1, 2, 3]) + self.assertRaises(self.failureException, self.assertIn, 'elephant', + animals) + + self.assertRaises(self.failureException, self.assertNotIn, 'c', 'abc') + self.assertRaises(self.failureException, self.assertNotIn, 1, [1, 2, 3]) + self.assertRaises(self.failureException, self.assertNotIn, 'cow', + animals) + + def testAssertDictContainsSubset(self): + self.assertDictContainsSubset({}, {}) + self.assertDictContainsSubset({}, {'a': 1}) + self.assertDictContainsSubset({'a': 1}, {'a': 1}) + self.assertDictContainsSubset({'a': 1}, {'a': 1, 'b': 2}) + self.assertDictContainsSubset({'a': 1, 'b': 2}, {'a': 1, 'b': 2}) + + self.assertRaises(unittest2.TestCase.failureException, + self.assertDictContainsSubset, {'a': 2}, {'a': 1}, + '.*Mismatched values:.*') + + self.assertRaises(unittest2.TestCase.failureException, + self.assertDictContainsSubset, {'c': 1}, {'a': 1}, + '.*Missing:.*') + + self.assertRaises(unittest2.TestCase.failureException, + self.assertDictContainsSubset, {'a': 1, 'c': 1}, + {'a': 1}, '.*Missing:.*') + + self.assertRaises(unittest2.TestCase.failureException, + self.assertDictContainsSubset, {'a': 1, 'c': 1}, + {'a': 1}, '.*Missing:.*Mismatched values:.*') + + self.assertRaises(self.failureException, + self.assertDictContainsSubset, {1: "one"}, {}) + + def testAssertEqual(self): + equal_pairs = [ + ((), ()), + ({}, {}), + ([], []), + (set(), set()), + (frozenset(), frozenset())] + for a, b in equal_pairs: + # This mess of try excepts is to test the assertEqual behavior + # itself. + try: + self.assertEqual(a, b) + except self.failureException: + self.fail('assertEqual(%r, %r) failed' % (a, b)) + try: + self.assertEqual(a, b, msg='foo') + except self.failureException: + self.fail('assertEqual(%r, %r) with msg= failed' % (a, b)) + try: + self.assertEqual(a, b, 'foo') + except self.failureException: + self.fail('assertEqual(%r, %r) with third parameter failed' % + (a, b)) + + unequal_pairs = [ + ((), []), + ({}, set()), + (set([4,1]), frozenset([4,2])), + (frozenset([4,5]), set([2,3])), + (set([3,4]), set([5,4]))] + for a, b in unequal_pairs: + self.assertRaises(self.failureException, self.assertEqual, a, b) + self.assertRaises(self.failureException, self.assertEqual, a, b, + 'foo') + self.assertRaises(self.failureException, self.assertEqual, a, b, + msg='foo') + + def testEquality(self): + self.assertListEqual([], []) + self.assertTupleEqual((), ()) + self.assertSequenceEqual([], ()) + + a = [0, 'a', []] + b = [] + self.assertRaises(unittest2.TestCase.failureException, + self.assertListEqual, a, b) + self.assertRaises(unittest2.TestCase.failureException, + self.assertListEqual, tuple(a), tuple(b)) + self.assertRaises(unittest2.TestCase.failureException, + self.assertSequenceEqual, a, tuple(b)) + + b.extend(a) + self.assertListEqual(a, b) + self.assertTupleEqual(tuple(a), tuple(b)) + self.assertSequenceEqual(a, tuple(b)) + self.assertSequenceEqual(tuple(a), b) + + self.assertRaises(self.failureException, self.assertListEqual, + a, tuple(b)) + self.assertRaises(self.failureException, self.assertTupleEqual, + tuple(a), b) + self.assertRaises(self.failureException, self.assertListEqual, None, b) + self.assertRaises(self.failureException, self.assertTupleEqual, None, + tuple(b)) + self.assertRaises(self.failureException, self.assertSequenceEqual, + None, tuple(b)) + self.assertRaises(self.failureException, self.assertListEqual, 1, 1) + self.assertRaises(self.failureException, self.assertTupleEqual, 1, 1) + self.assertRaises(self.failureException, self.assertSequenceEqual, + 1, 1) + + self.assertDictEqual({}, {}) + + c = { 'x': 1 } + d = {} + self.assertRaises(unittest2.TestCase.failureException, + self.assertDictEqual, c, d) + + d.update(c) + self.assertDictEqual(c, d) + + d['x'] = 0 + self.assertRaises(unittest2.TestCase.failureException, + self.assertDictEqual, c, d, 'These are unequal') + + self.assertRaises(self.failureException, self.assertDictEqual, None, d) + self.assertRaises(self.failureException, self.assertDictEqual, [], d) + self.assertRaises(self.failureException, self.assertDictEqual, 1, 1) + + def testAssertItemsEqual(self): + self.assertItemsEqual([1, 2, 3], [3, 2, 1]) + self.assertItemsEqual(['foo', 'bar', 'baz'], ['bar', 'baz', 'foo']) + self.assertRaises(self.failureException, self.assertItemsEqual, + [10], [10, 11]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [10, 11], [10]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [10, 11, 10], [10, 11]) + + # Test that sequences of unhashable objects can be tested for sameness: + self.assertItemsEqual([[1, 2], [3, 4]], [[3, 4], [1, 2]]) + + self.assertItemsEqual([{'a': 1}, {'b': 2}], [{'b': 2}, {'a': 1}]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [[1]], [[2]]) + + # Test unsortable objects + self.assertItemsEqual([2j, None], [None, 2j]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [2j, None], [None, 3j]) + + def testAssertSetEqual(self): + set1 = set() + set2 = set() + self.assertSetEqual(set1, set2) + + self.assertRaises(self.failureException, self.assertSetEqual, None, set2) + self.assertRaises(self.failureException, self.assertSetEqual, [], set2) + self.assertRaises(self.failureException, self.assertSetEqual, set1, None) + self.assertRaises(self.failureException, self.assertSetEqual, set1, []) + + set1 = set(['a']) + set2 = set() + self.assertRaises(self.failureException, self.assertSetEqual, set1, set2) + + set1 = set(['a']) + set2 = set(['a']) + self.assertSetEqual(set1, set2) + + set1 = set(['a']) + set2 = set(['a', 'b']) + self.assertRaises(self.failureException, self.assertSetEqual, set1, set2) + + set1 = set(['a']) + set2 = frozenset(['a', 'b']) + self.assertRaises(self.failureException, self.assertSetEqual, set1, set2) + + set1 = set(['a', 'b']) + set2 = frozenset(['a', 'b']) + self.assertSetEqual(set1, set2) + + set1 = set() + set2 = "foo" + self.assertRaises(self.failureException, self.assertSetEqual, set1, set2) + self.assertRaises(self.failureException, self.assertSetEqual, set2, set1) + + # make sure any string formatting is tuple-safe + set1 = set([(0, 1), (2, 3)]) + set2 = set([(4, 5)]) + self.assertRaises(self.failureException, self.assertSetEqual, set1, set2) + + def testInequality(self): + # Try ints + self.assertGreater(2, 1) + self.assertGreaterEqual(2, 1) + self.assertGreaterEqual(1, 1) + self.assertLess(1, 2) + self.assertLessEqual(1, 2) + self.assertLessEqual(1, 1) + self.assertRaises(self.failureException, self.assertGreater, 1, 2) + self.assertRaises(self.failureException, self.assertGreater, 1, 1) + self.assertRaises(self.failureException, self.assertGreaterEqual, 1, 2) + self.assertRaises(self.failureException, self.assertLess, 2, 1) + self.assertRaises(self.failureException, self.assertLess, 1, 1) + self.assertRaises(self.failureException, self.assertLessEqual, 2, 1) + + # Try Floats + self.assertGreater(1.1, 1.0) + self.assertGreaterEqual(1.1, 1.0) + self.assertGreaterEqual(1.0, 1.0) + self.assertLess(1.0, 1.1) + self.assertLessEqual(1.0, 1.1) + self.assertLessEqual(1.0, 1.0) + self.assertRaises(self.failureException, self.assertGreater, 1.0, 1.1) + self.assertRaises(self.failureException, self.assertGreater, 1.0, 1.0) + self.assertRaises(self.failureException, self.assertGreaterEqual, 1.0, 1.1) + self.assertRaises(self.failureException, self.assertLess, 1.1, 1.0) + self.assertRaises(self.failureException, self.assertLess, 1.0, 1.0) + self.assertRaises(self.failureException, self.assertLessEqual, 1.1, 1.0) + + # Try Strings + self.assertGreater('bug', 'ant') + self.assertGreaterEqual('bug', 'ant') + self.assertGreaterEqual('ant', 'ant') + self.assertLess('ant', 'bug') + self.assertLessEqual('ant', 'bug') + self.assertLessEqual('ant', 'ant') + self.assertRaises(self.failureException, self.assertGreater, 'ant', 'bug') + self.assertRaises(self.failureException, self.assertGreater, 'ant', 'ant') + self.assertRaises(self.failureException, self.assertGreaterEqual, 'ant', 'bug') + self.assertRaises(self.failureException, self.assertLess, 'bug', 'ant') + self.assertRaises(self.failureException, self.assertLess, 'ant', 'ant') + self.assertRaises(self.failureException, self.assertLessEqual, 'bug', 'ant') + + # Try Unicode + self.assertGreater(u'bug', u'ant') + self.assertGreaterEqual(u'bug', u'ant') + self.assertGreaterEqual(u'ant', u'ant') + self.assertLess(u'ant', u'bug') + self.assertLessEqual(u'ant', u'bug') + self.assertLessEqual(u'ant', u'ant') + self.assertRaises(self.failureException, self.assertGreater, u'ant', u'bug') + self.assertRaises(self.failureException, self.assertGreater, u'ant', u'ant') + self.assertRaises(self.failureException, self.assertGreaterEqual, u'ant', + u'bug') + self.assertRaises(self.failureException, self.assertLess, u'bug', u'ant') + self.assertRaises(self.failureException, self.assertLess, u'ant', u'ant') + self.assertRaises(self.failureException, self.assertLessEqual, u'bug', u'ant') + + # Try Mixed String/Unicode + self.assertGreater('bug', u'ant') + self.assertGreater(u'bug', 'ant') + self.assertGreaterEqual('bug', u'ant') + self.assertGreaterEqual(u'bug', 'ant') + self.assertGreaterEqual('ant', u'ant') + self.assertGreaterEqual(u'ant', 'ant') + self.assertLess('ant', u'bug') + self.assertLess(u'ant', 'bug') + self.assertLessEqual('ant', u'bug') + self.assertLessEqual(u'ant', 'bug') + self.assertLessEqual('ant', u'ant') + self.assertLessEqual(u'ant', 'ant') + self.assertRaises(self.failureException, self.assertGreater, 'ant', u'bug') + self.assertRaises(self.failureException, self.assertGreater, u'ant', 'bug') + self.assertRaises(self.failureException, self.assertGreater, 'ant', u'ant') + self.assertRaises(self.failureException, self.assertGreater, u'ant', 'ant') + self.assertRaises(self.failureException, self.assertGreaterEqual, 'ant', + u'bug') + self.assertRaises(self.failureException, self.assertGreaterEqual, u'ant', + 'bug') + self.assertRaises(self.failureException, self.assertLess, 'bug', u'ant') + self.assertRaises(self.failureException, self.assertLess, u'bug', 'ant') + self.assertRaises(self.failureException, self.assertLess, 'ant', u'ant') + self.assertRaises(self.failureException, self.assertLess, u'ant', 'ant') + self.assertRaises(self.failureException, self.assertLessEqual, 'bug', u'ant') + self.assertRaises(self.failureException, self.assertLessEqual, u'bug', 'ant') + + def testAssertMultiLineEqual(self): + sample_text = """\ +http://www.python.org/doc/2.3/lib/module-unittest.html +test case + A test case is the smallest unit of testing. [...] +""" + revised_sample_text = """\ +http://www.python.org/doc/2.4.1/lib/module-unittest.html +test case + A test case is the smallest unit of testing. [...] You may provide your + own implementation that does not subclass from TestCase, of course. +""" + sample_text_error = """\ +- http://www.python.org/doc/2.3/lib/module-unittest.html +? ^ ++ http://www.python.org/doc/2.4.1/lib/module-unittest.html +? ^^^ + test case +- A test case is the smallest unit of testing. [...] ++ A test case is the smallest unit of testing. [...] You may provide your +? +++++++++++++++++++++ ++ own implementation that does not subclass from TestCase, of course. +""" + self.maxDiff = None + for type_changer in (lambda x: x, lambda x: x.decode('utf8')): + try: + self.assertMultiLineEqual(type_changer(sample_text), + type_changer(revised_sample_text)) + except self.failureException as e: + # need to remove the first line of the error message + error = str(e).encode('utf8').split('\n', 1)[1] + + # assertMultiLineEqual is hooked up as the default for + # unicode strings - so we can't use it for this check + self.assertTrue(sample_text_error == error) + + def testAssertSequenceEqualMaxDiff(self): + self.assertEqual(self.maxDiff, 80*8) + seq1 = 'a' + 'x' * 80**2 + seq2 = 'b' + 'x' * 80**2 + diff = '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(), + pprint.pformat(seq2).splitlines())) + # the +1 is the leading \n added by assertSequenceEqual + omitted = unittest2.case.DIFF_OMITTED % (len(diff) + 1,) + + self.maxDiff = len(diff)//2 + try: + self.assertSequenceEqual(seq1, seq2) + except self.failureException as e: + msg = e.args[0] + else: + self.fail('assertSequenceEqual did not fail.') + self.assertTrue(len(msg) < len(diff)) + self.assertIn(omitted, msg) + + self.maxDiff = len(diff) * 2 + try: + self.assertSequenceEqual(seq1, seq2) + except self.failureException as e: + msg = e.args[0] + else: + self.fail('assertSequenceEqual did not fail.') + self.assertTrue(len(msg) > len(diff)) + self.assertNotIn(omitted, msg) + + self.maxDiff = None + try: + self.assertSequenceEqual(seq1, seq2) + except self.failureException as e: + msg = e.args[0] + else: + self.fail('assertSequenceEqual did not fail.') + self.assertTrue(len(msg) > len(diff)) + self.assertNotIn(omitted, msg) + + def testTruncateMessage(self): + self.maxDiff = 1 + message = self._truncateMessage('foo', 'bar') + omitted = unittest2.case.DIFF_OMITTED % len('bar') + self.assertEqual(message, 'foo' + omitted) + + self.maxDiff = None + message = self._truncateMessage('foo', 'bar') + self.assertEqual(message, 'foobar') + + self.maxDiff = 4 + message = self._truncateMessage('foo', 'bar') + self.assertEqual(message, 'foobar') + + def testAssertDictEqualTruncates(self): + test = unittest2.TestCase('assertEqual') + def truncate(msg, diff): + return 'foo' + test._truncateMessage = truncate + try: + test.assertDictEqual({}, {1: 0}) + except self.failureException as e: + self.assertEqual(str(e), 'foo') + else: + self.fail('assertDictEqual did not fail') + + def testAssertMultiLineEqualTruncates(self): + test = unittest2.TestCase('assertEqual') + def truncate(msg, diff): + return 'foo' + test._truncateMessage = truncate + try: + test.assertMultiLineEqual('foo', 'bar') + except self.failureException as e: + self.assertEqual(str(e), 'foo') + else: + self.fail('assertMultiLineEqual did not fail') + + def testAssertIsNone(self): + self.assertIsNone(None) + self.assertRaises(self.failureException, self.assertIsNone, False) + self.assertIsNotNone('DjZoPloGears on Rails') + self.assertRaises(self.failureException, self.assertIsNotNone, None) + + def testAssertRegexpMatches(self): + self.assertRegexpMatches('asdfabasdf', r'ab+') + self.assertRaises(self.failureException, self.assertRegexpMatches, + 'saaas', r'aaaa') + + def testAssertRaisesRegexp(self): + class ExceptionMock(Exception): + pass + + def Stub(): + raise ExceptionMock('We expect') + + self.assertRaisesRegexp(ExceptionMock, re.compile('expect$'), Stub) + self.assertRaisesRegexp(ExceptionMock, 'expect$', Stub) + self.assertRaisesRegexp(ExceptionMock, u'expect$', Stub) + + def testAssertNotRaisesRegexp(self): + self.assertRaisesRegexp( + self.failureException, '^Exception not raised$', + self.assertRaisesRegexp, Exception, re.compile('x'), + lambda: None) + self.assertRaisesRegexp( + self.failureException, '^Exception not raised$', + self.assertRaisesRegexp, Exception, 'x', + lambda: None) + self.assertRaisesRegexp( + self.failureException, '^Exception not raised$', + self.assertRaisesRegexp, Exception, u'x', + lambda: None) + + def testAssertRaisesRegexpMismatch(self): + def Stub(): + raise Exception('Unexpected') + + self.assertRaisesRegexp( + self.failureException, + r'"\^Expected\$" does not match "Unexpected"', + self.assertRaisesRegexp, Exception, '^Expected$', + Stub) + self.assertRaisesRegexp( + self.failureException, + r'"\^Expected\$" does not match "Unexpected"', + self.assertRaisesRegexp, Exception, u'^Expected$', + Stub) + self.assertRaisesRegexp( + self.failureException, + r'"\^Expected\$" does not match "Unexpected"', + self.assertRaisesRegexp, Exception, + re.compile('^Expected$'), Stub) + + + def testSynonymAssertMethodNames(self): + """Test undocumented method name synonyms. + + Please do not use these methods names in your own code. + + This test confirms their continued existence and functionality + in order to avoid breaking existing code. + """ + self.assertNotEquals(3, 5) + self.assertEquals(3, 3) + self.assertAlmostEquals(2.0, 2.0) + self.assertNotAlmostEquals(3.0, 5.0) + self.assert_(True) + + def testDeepcopy(self): + # Issue: 5660 + class TestableTest(unittest2.TestCase): + def testNothing(self): + pass + + test = TestableTest('testNothing') + + # This shouldn't blow up + deepcopy(test) + + +if __name__ == "__main__": + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_discovery.py b/third_party/Python/module/unittest2/unittest2/test/test_discovery.py new file mode 100644 index 000000000000..f78c4d826b8d --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_discovery.py @@ -0,0 +1,371 @@ +import os +import re +import sys + +import unittest2 + + +class TestDiscovery(unittest2.TestCase): + + # Heavily mocked tests so I can avoid hitting the filesystem + def test_get_name_from_path(self): + loader = unittest2.TestLoader() + + loader._top_level_dir = '/foo' + name = loader._get_name_from_path('/foo/bar/baz.py') + self.assertEqual(name, 'bar.baz') + + if not __debug__: + # asserts are off + return + + self.assertRaises(AssertionError, + loader._get_name_from_path, + '/bar/baz.py') + + def test_find_tests(self): + loader = unittest2.TestLoader() + + original_listdir = os.listdir + def restore_listdir(): + os.listdir = original_listdir + original_isfile = os.path.isfile + def restore_isfile(): + os.path.isfile = original_isfile + original_isdir = os.path.isdir + def restore_isdir(): + os.path.isdir = original_isdir + + path_lists = [['test1.py', 'test2.py', 'not_a_test.py', 'test_dir', + 'test.foo', 'test-not-a-module.py', 'another_dir'], + ['test3.py', 'test4.py', ]] + os.listdir = lambda path: path_lists.pop(0) + self.addCleanup(restore_listdir) + + def isdir(path): + return path.endswith('dir') + os.path.isdir = isdir + self.addCleanup(restore_isdir) + + def isfile(path): + # another_dir is not a package and so shouldn't be recursed into + return not path.endswith('dir') and not 'another_dir' in path + os.path.isfile = isfile + self.addCleanup(restore_isfile) + + loader._get_module_from_name = lambda path: path + ' module' + loader.loadTestsFromModule = lambda module: module + ' tests' + + top_level = os.path.abspath('/foo') + loader._top_level_dir = top_level + suite = list(loader._find_tests(top_level, 'test*.py')) + + expected = [name + ' module tests' for name in + ('test1', 'test2')] + expected.extend([('test_dir.%s' % name) + ' module tests' for name in + ('test3', 'test4')]) + self.assertEqual(suite, expected) + + def test_find_tests_with_package(self): + loader = unittest2.TestLoader() + + original_listdir = os.listdir + def restore_listdir(): + os.listdir = original_listdir + original_isfile = os.path.isfile + def restore_isfile(): + os.path.isfile = original_isfile + original_isdir = os.path.isdir + def restore_isdir(): + os.path.isdir = original_isdir + + directories = ['a_directory', 'test_directory', 'test_directory2'] + path_lists = [directories, [], [], []] + os.listdir = lambda path: path_lists.pop(0) + self.addCleanup(restore_listdir) + + os.path.isdir = lambda path: True + self.addCleanup(restore_isdir) + + os.path.isfile = lambda path: os.path.basename(path) not in directories + self.addCleanup(restore_isfile) + + class Module(object): + paths = [] + load_tests_args = [] + + def __init__(self, path): + self.path = path + self.paths.append(path) + if os.path.basename(path) == 'test_directory': + def load_tests(loader, tests, pattern): + self.load_tests_args.append((loader, tests, pattern)) + return 'load_tests' + self.load_tests = load_tests + + def __eq__(self, other): + return self.path == other.path + + # Silence py3k warning + __hash__ = None + + loader._get_module_from_name = lambda name: Module(name) + def loadTestsFromModule(module, use_load_tests): + if use_load_tests: + raise self.failureException('use_load_tests should be False for packages') + return module.path + ' module tests' + loader.loadTestsFromModule = loadTestsFromModule + + loader._top_level_dir = '/foo' + # this time no '.py' on the pattern so that it can match + # a test package + suite = list(loader._find_tests('/foo', 'test*')) + + # We should have loaded tests from the test_directory package by calling load_tests + # and directly from the test_directory2 package + self.assertEqual(suite, + ['load_tests', 'test_directory2' + ' module tests']) + self.assertEqual(Module.paths, ['test_directory', 'test_directory2']) + + # load_tests should have been called once with loader, tests and pattern + self.assertEqual(Module.load_tests_args, + [(loader, 'test_directory' + ' module tests', 'test*')]) + + def test_discover(self): + loader = unittest2.TestLoader() + + original_isfile = os.path.isfile + original_isdir = os.path.isdir + def restore_isfile(): + os.path.isfile = original_isfile + + os.path.isfile = lambda path: False + self.addCleanup(restore_isfile) + + orig_sys_path = sys.path[:] + def restore_path(): + sys.path[:] = orig_sys_path + self.addCleanup(restore_path) + + full_path = os.path.abspath(os.path.normpath('/foo')) + self.assertRaises(ImportError, + loader.discover, + '/foo/bar', top_level_dir='/foo') + + self.assertEqual(loader._top_level_dir, full_path) + self.assertIn(full_path, sys.path) + + os.path.isfile = lambda path: True + os.path.isdir = lambda path: True + + def restore_isdir(): + os.path.isdir = original_isdir + self.addCleanup(restore_isdir) + + _find_tests_args = [] + def _find_tests(start_dir, pattern): + _find_tests_args.append((start_dir, pattern)) + return ['tests'] + loader._find_tests = _find_tests + loader.suiteClass = str + + suite = loader.discover('/foo/bar/baz', 'pattern', '/foo/bar') + + top_level_dir = os.path.abspath(os.path.normpath('/foo/bar')) + start_dir = os.path.abspath(os.path.normpath('/foo/bar/baz')) + self.assertEqual(suite, "['tests']") + self.assertEqual(loader._top_level_dir, top_level_dir) + self.assertEqual(_find_tests_args, [(start_dir, 'pattern')]) + self.assertIn(top_level_dir, sys.path) + + def test_discover_with_modules_that_fail_to_import(self): + loader = unittest2.TestLoader() + + listdir = os.listdir + os.listdir = lambda _: ['test_this_does_not_exist.py'] + isfile = os.path.isfile + os.path.isfile = lambda _: True + orig_sys_path = sys.path[:] + def restore(): + os.path.isfile = isfile + os.listdir = listdir + sys.path[:] = orig_sys_path + self.addCleanup(restore) + + suite = loader.discover('.') + self.assertIn(os.getcwd(), sys.path) + self.assertEqual(suite.countTestCases(), 1) + test = list(list(suite)[0])[0] # extract test from suite + + self.assertRaises(ImportError, + lambda: test.test_this_does_not_exist()) + + def test_command_line_handling_parseArgs(self): + # Haha - take that uninstantiable class + program = object.__new__(unittest2.TestProgram) + + args = [] + def do_discovery(argv): + args.extend(argv) + program._do_discovery = do_discovery + program.parseArgs(['something', 'discover']) + self.assertEqual(args, []) + + program.parseArgs(['something', 'discover', 'foo', 'bar']) + self.assertEqual(args, ['foo', 'bar']) + + def test_command_line_handling_do_discovery_too_many_arguments(self): + class Stop(Exception): + pass + def usageExit(): + raise Stop + + program = object.__new__(unittest2.TestProgram) + program.usageExit = usageExit + + self.assertRaises(Stop, + # too many args + lambda: program._do_discovery(['one', 'two', 'three', 'four'])) + + + def test_command_line_handling_do_discovery_calls_loader(self): + program = object.__new__(unittest2.TestProgram) + + class Loader(object): + args = [] + def discover(self, start_dir, pattern, top_level_dir): + self.args.append((start_dir, pattern, top_level_dir)) + return 'tests' + + program._do_discovery(['-v'], Loader=Loader) + self.assertEqual(program.verbosity, 2) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('.', 'test*.py', None)]) + + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery(['--verbose'], Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('.', 'test*.py', None)]) + + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery([], Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('.', 'test*.py', None)]) + + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery(['fish'], Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('fish', 'test*.py', None)]) + + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery(['fish', 'eggs'], Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('fish', 'eggs', None)]) + + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery(['fish', 'eggs', 'ham'], Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('fish', 'eggs', 'ham')]) + + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery(['-s', 'fish'], Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('fish', 'test*.py', None)]) + + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery(['-t', 'fish'], Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('.', 'test*.py', 'fish')]) + + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery(['-p', 'fish'], Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('.', 'fish', None)]) + self.assertFalse(program.failfast) + self.assertFalse(program.catchbreak) + + args = ['-p', 'eggs', '-s', 'fish', '-v', '-f'] + try: + import signal + except ImportError: + signal = None + else: + args.append('-c') + Loader.args = [] + program = object.__new__(unittest2.TestProgram) + program._do_discovery(args, Loader=Loader) + self.assertEqual(program.test, 'tests') + self.assertEqual(Loader.args, [('fish', 'eggs', None)]) + self.assertEqual(program.verbosity, 2) + self.assertTrue(program.failfast) + if signal is not None: + self.assertTrue(program.catchbreak) + + def test_detect_module_clash(self): + class Module(object): + __file__ = 'bar/foo.py' + sys.modules['foo'] = Module + full_path = os.path.abspath('foo') + original_listdir = os.listdir + original_isfile = os.path.isfile + original_isdir = os.path.isdir + + def cleanup(): + os.listdir = original_listdir + os.path.isfile = original_isfile + os.path.isdir = original_isdir + del sys.modules['foo'] + if full_path in sys.path: + sys.path.remove(full_path) + self.addCleanup(cleanup) + + def listdir(_): + return ['foo.py'] + def isfile(_): + return True + def isdir(_): + return True + os.listdir = listdir + os.path.isfile = isfile + os.path.isdir = isdir + + loader = unittest2.TestLoader() + + mod_dir = os.path.abspath('bar') + expected_dir = os.path.abspath('foo') + msg = re.escape(r"'foo' module incorrectly imported from %r. Expected %r. " + "Is this module globally installed?" % (mod_dir, expected_dir)) + self.assertRaisesRegexp( + ImportError, '^%s$' % msg, loader.discover, + start_dir='foo', pattern='foo.py' + ) + self.assertEqual(sys.path[0], full_path) + + + def test_discovery_from_dotted_path(self): + loader = unittest2.TestLoader() + + tests = [self] + expectedPath = os.path.abspath(os.path.dirname(unittest2.test.__file__)) + + self.wasRun = False + def _find_tests(start_dir, pattern): + self.wasRun = True + self.assertEqual(start_dir, expectedPath) + return tests + loader._find_tests = _find_tests + suite = loader.discover('unittest2.test') + self.assertTrue(self.wasRun) + self.assertEqual(suite._tests, tests) + + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_functiontestcase.py b/third_party/Python/module/unittest2/unittest2/test/test_functiontestcase.py new file mode 100644 index 000000000000..263aed5a9bf6 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_functiontestcase.py @@ -0,0 +1,150 @@ +import unittest2 +import six + +from unittest2.test.support import LoggingResult + + +class Test_FunctionTestCase(unittest2.TestCase): + + # "Return the number of tests represented by the this test object. For + # unittest2.TestCase instances, this will always be 1" + def test_countTestCases(self): + test = unittest2.FunctionTestCase(lambda: None) + + self.assertEqual(test.countTestCases(), 1) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if setUp() raises + # an exception. + def test_run_call_order__error_in_setUp(self): + events = [] + result = LoggingResult(events) + + def setUp(): + events.append('setUp') + raise RuntimeError('raised by setUp') + + def test(): + events.append('test') + + def tearDown(): + events.append('tearDown') + + expected = ['startTest', 'setUp', 'addError', 'stopTest'] + unittest2.FunctionTestCase(test, setUp, tearDown).run(result) + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if the test raises + # an error (as opposed to a failure). + def test_run_call_order__error_in_test(self): + events = [] + result = LoggingResult(events) + + def setUp(): + events.append('setUp') + + def test(): + events.append('test') + raise RuntimeError('raised by test') + + def tearDown(): + events.append('tearDown') + + expected = ['startTest', 'setUp', 'test', 'addError', 'tearDown', + 'stopTest'] + unittest2.FunctionTestCase(test, setUp, tearDown).run(result) + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if the test signals + # a failure (as opposed to an error). + def test_run_call_order__failure_in_test(self): + events = [] + result = LoggingResult(events) + + def setUp(): + events.append('setUp') + + def test(): + events.append('test') + self.fail('raised by test') + + def tearDown(): + events.append('tearDown') + + expected = ['startTest', 'setUp', 'test', 'addFailure', 'tearDown', + 'stopTest'] + unittest2.FunctionTestCase(test, setUp, tearDown).run(result) + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if tearDown() raises + # an exception. + def test_run_call_order__error_in_tearDown(self): + events = [] + result = LoggingResult(events) + + def setUp(): + events.append('setUp') + + def test(): + events.append('test') + + def tearDown(): + events.append('tearDown') + raise RuntimeError('raised by tearDown') + + expected = ['startTest', 'setUp', 'test', 'tearDown', 'addError', + 'stopTest'] + unittest2.FunctionTestCase(test, setUp, tearDown).run(result) + self.assertEqual(events, expected) + + # "Return a string identifying the specific test case." + # + # Because of the vague nature of the docs, I'm not going to lock this + # test down too much. Really all that can be asserted is that the id() + # will be a string (either 8-byte or unicode -- again, because the docs + # just say "string") + def test_id(self): + test = unittest2.FunctionTestCase(lambda: None) + + self.assertIsInstance(test.id(), six.string_types) + + # "Returns a one-line description of the test, or None if no description + # has been provided. The default implementation of this method returns + # the first line of the test method's docstring, if available, or None." + def test_shortDescription__no_docstring(self): + test = unittest2.FunctionTestCase(lambda: None) + + self.assertEqual(test.shortDescription(), None) + + # "Returns a one-line description of the test, or None if no description + # has been provided. The default implementation of this method returns + # the first line of the test method's docstring, if available, or None." + def test_shortDescription__singleline_docstring(self): + desc = "this tests foo" + test = unittest2.FunctionTestCase(lambda: None, description=desc) + + self.assertEqual(test.shortDescription(), "this tests foo") + + + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_loader.py b/third_party/Python/module/unittest2/unittest2/test/test_loader.py new file mode 100644 index 000000000000..3a61f33e3191 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_loader.py @@ -0,0 +1,1271 @@ +import sys +import types + +import unittest2 + + +class Test_TestLoader(unittest2.TestCase): + + ### Tests for TestLoader.loadTestsFromTestCase + ################################################################ + + # "Return a suite of all tests cases contained in the TestCase-derived + # class testCaseClass" + def test_loadTestsFromTestCase(self): + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests = unittest2.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest2.TestLoader() + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # "Return a suite of all tests cases contained in the TestCase-derived + # class testCaseClass" + # + # Make sure it does the right thing even if no tests were found + def test_loadTestsFromTestCase__no_matches(self): + class Foo(unittest2.TestCase): + def foo_bar(self): pass + + empty_suite = unittest2.TestSuite() + + loader = unittest2.TestLoader() + self.assertEqual(loader.loadTestsFromTestCase(Foo), empty_suite) + + # "Return a suite of all tests cases contained in the TestCase-derived + # class testCaseClass" + # + # What happens if loadTestsFromTestCase() is given an object + # that isn't a subclass of TestCase? Specifically, what happens + # if testCaseClass is a subclass of TestSuite? + # + # This is checked for specifically in the code, so we better add a + # test for it. + def test_loadTestsFromTestCase__TestSuite_subclass(self): + class NotATestCase(unittest2.TestSuite): + pass + + loader = unittest2.TestLoader() + try: + loader.loadTestsFromTestCase(NotATestCase) + except TypeError: + pass + else: + self.fail('Should raise TypeError') + + # "Return a suite of all tests cases contained in the TestCase-derived + # class testCaseClass" + # + # Make sure loadTestsFromTestCase() picks up the default test method + # name (as specified by TestCase), even though the method name does + # not match the default TestLoader.testMethodPrefix string + def test_loadTestsFromTestCase__default_method_name(self): + class Foo(unittest2.TestCase): + def runTest(self): + pass + + loader = unittest2.TestLoader() + # This has to be false for the test to succeed + self.assertFalse('runTest'.startswith(loader.testMethodPrefix)) + + suite = loader.loadTestsFromTestCase(Foo) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [Foo('runTest')]) + + ################################################################ + ### /Tests for TestLoader.loadTestsFromTestCase + + ### Tests for TestLoader.loadTestsFromModule + ################################################################ + + # "This method searches `module` for classes derived from TestCase" + def test_loadTestsFromModule__TestCase_subclass(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, loader.suiteClass) + + expected = [loader.suiteClass([MyTestCase('test')])] + self.assertEqual(list(suite), expected) + + # "This method searches `module` for classes derived from TestCase" + # + # What happens if no tests are found (no TestCase instances)? + def test_loadTestsFromModule__no_TestCase_instances(self): + m = types.ModuleType('m') + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # "This method searches `module` for classes derived from TestCase" + # + # What happens if no tests are found (TestCases instances, but no tests)? + def test_loadTestsFromModule__no_TestCase_tests(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [loader.suiteClass()]) + + # "This method searches `module` for classes derived from TestCase"s + # + # What happens if loadTestsFromModule() is given something other + # than a module? + # + # XXX Currently, it succeeds anyway. This flexibility + # should either be documented or loadTestsFromModule() should + # raise a TypeError + # + # XXX Certain people are using this behaviour. We'll add a test for it + def test_loadTestsFromModule__not_a_module(self): + class MyTestCase(unittest2.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromModule(NotAModule) + + reference = [unittest2.TestSuite([MyTestCase('test')])] + self.assertEqual(list(suite), reference) + + + # Check that loadTestsFromModule honors (or not) a module + # with a load_tests function. + def test_loadTestsFromModule__load_tests(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + load_tests_args = [] + def load_tests(loader, tests, pattern): + self.assertIsInstance(tests, unittest2.TestSuite) + load_tests_args.extend((loader, tests, pattern)) + return tests + m.load_tests = load_tests + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, unittest2.TestSuite) + self.assertEquals(load_tests_args, [loader, suite, None]) + + load_tests_args = [] + suite = loader.loadTestsFromModule(m, use_load_tests=False) + self.assertEquals(load_tests_args, []) + + def test_loadTestsFromModule__faulty_load_tests(self): + m = types.ModuleType('m') + + def load_tests(loader, tests, pattern): + raise TypeError('some failure') + m.load_tests = load_tests + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromModule(m) + self.assertIsInstance(suite, unittest2.TestSuite) + self.assertEqual(suite.countTestCases(), 1) + test = list(suite)[0] + + self.assertRaisesRegexp(TypeError, "some failure", test.m) + + + ################################################################ + ### /Tests for TestLoader.loadTestsFromModule() + + ### Tests for TestLoader.loadTestsFromName() + ################################################################ + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Is ValueError raised in response to an empty name? + def test_loadTestsFromName__empty_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromName('') + except ValueError as e: + self.assertEqual(str(e), "Empty module name") + else: + self.fail("TestLoader.loadTestsFromName failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the name contains invalid characters? + def test_loadTestsFromName__malformed_name(self): + loader = unittest2.TestLoader() + + # XXX Should this raise ValueError or ImportError? + try: + loader.loadTestsFromName('abc () //') + except ValueError: + pass + except ImportError: + pass + else: + self.fail("TestLoader.loadTestsFromName failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve ... to a + # module" + # + # What happens when a module by that name can't be found? + def test_loadTestsFromName__unknown_module_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromName('sdasfasfasdf') + except ImportError as e: + self.assertEqual(str(e), "No module named sdasfasfasdf") + else: + self.fail("TestLoader.loadTestsFromName failed to raise ImportError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the module is found, but the attribute can't? + def test_loadTestsFromName__unknown_attr_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromName('unittest2.sdasfasfasdf') + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when we provide the module, but the attribute can't be + # found? + def test_loadTestsFromName__relative_unknown_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromName('sdasfasfasdf', unittest2) + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromName raise ValueError when passed an empty + # name relative to a provided module? + # + # XXX Should probably raise a ValueError instead of an AttributeError + def test_loadTestsFromName__relative_empty_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromName('', unittest2) + except AttributeError: + pass + else: + self.fail("Failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when an impossible name is given, relative to the provided + # `module`? + def test_loadTestsFromName__relative_malformed_name(self): + loader = unittest2.TestLoader() + + # XXX Should this raise AttributeError or ValueError? + try: + loader.loadTestsFromName('abc () //', unittest2) + except ValueError: + pass + except AttributeError: + pass + else: + self.fail("TestLoader.loadTestsFromName failed to raise ValueError") + + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromName raise TypeError when the `module` argument + # isn't a module object? + # + # XXX Accepts the not-a-module object, ignorning the object's type + # This should raise an exception or the method name should be changed + # + # XXX Some people are relying on this, so keep it for now + def test_loadTestsFromName__relative_not_a_module(self): + class MyTestCase(unittest2.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromName('test_2', NotAModule) + + reference = [MyTestCase('test')] + self.assertEqual(list(suite), reference) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does it raise an exception if the name resolves to an invalid + # object? + def test_loadTestsFromName__relative_bad_object(self): + m = types.ModuleType('m') + m.testcase_1 = object() + + loader = unittest2.TestLoader() + try: + loader.loadTestsFromName('testcase_1', m) + except TypeError: + pass + else: + self.fail("Should have raised TypeError") + + # "The specifier name is a ``dotted name'' that may + # resolve either to ... a test case class" + def test_loadTestsFromName__relative_TestCase_subclass(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromName('testcase_1', m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + def test_loadTestsFromName__relative_TestSuite(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testsuite = unittest2.TestSuite([MyTestCase('test')]) + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromName('testsuite', m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a test method within a test case class" + def test_loadTestsFromName__relative_testmethod(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromName('testcase_1.test', m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does loadTestsFromName() raise the proper exception when trying to + # resolve "a test method within a test case class" that doesn't exist + # for the given name (relative to a provided module)? + def test_loadTestsFromName__relative_invalid_testmethod(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + try: + loader.loadTestsFromName('testcase_1.testfoo', m) + except AttributeError as e: + self.assertEqual(str(e), "type object 'MyTestCase' has no attribute 'testfoo'") + else: + self.fail("Failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a ... TestSuite instance" + def test_loadTestsFromName__callable__TestSuite(self): + m = types.ModuleType('m') + testcase_1 = unittest2.FunctionTestCase(lambda: None) + testcase_2 = unittest2.FunctionTestCase(lambda: None) + def return_TestSuite(): + return unittest2.TestSuite([testcase_1, testcase_2]) + m.return_TestSuite = return_TestSuite + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromName('return_TestSuite', m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [testcase_1, testcase_2]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase ... instance" + def test_loadTestsFromName__callable__TestCase_instance(self): + m = types.ModuleType('m') + testcase_1 = unittest2.FunctionTestCase(lambda: None) + def return_TestCase(): + return testcase_1 + m.return_TestCase = return_TestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromName('return_TestCase', m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [testcase_1]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase ... instance" + #***************************************************************** + #Override the suiteClass attribute to ensure that the suiteClass + #attribute is used + def test_loadTestsFromName__callable__TestCase_instance_ProperSuiteClass(self): + class SubTestSuite(unittest2.TestSuite): + pass + m = types.ModuleType('m') + testcase_1 = unittest2.FunctionTestCase(lambda: None) + def return_TestCase(): + return testcase_1 + m.return_TestCase = return_TestCase + + loader = unittest2.TestLoader() + loader.suiteClass = SubTestSuite + suite = loader.loadTestsFromName('return_TestCase', m) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [testcase_1]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a test method within a test case class" + #***************************************************************** + #Override the suiteClass attribute to ensure that the suiteClass + #attribute is used + def test_loadTestsFromName__relative_testmethod_ProperSuiteClass(self): + class SubTestSuite(unittest2.TestSuite): + pass + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + loader.suiteClass=SubTestSuite + suite = loader.loadTestsFromName('testcase_1.test', m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # What happens if the callable returns something else? + def test_loadTestsFromName__callable__wrong_type(self): + m = types.ModuleType('m') + def return_wrong(): + return 6 + m.return_wrong = return_wrong + + loader = unittest2.TestLoader() + try: + loader.loadTestsFromName('return_wrong', m) + except TypeError: + pass + else: + self.fail("TestLoader.loadTestsFromName failed to raise TypeError") + + # "The specifier can refer to modules and packages which have not been + # imported; they will be imported as a side-effect" + def test_loadTestsFromName__module_not_loaded(self): + # We're going to try to load this module as a side-effect, so it + # better not be loaded before we try. + # + module_name = 'unittest2.test.dummy' + sys.modules.pop(module_name, None) + + loader = unittest2.TestLoader() + try: + suite = loader.loadTestsFromName(module_name) + + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # module should now be loaded, thanks to loadTestsFromName() + self.assertIn(module_name, sys.modules) + finally: + if module_name in sys.modules: + del sys.modules[module_name] + + ################################################################ + ### Tests for TestLoader.loadTestsFromName() + + ### Tests for TestLoader.loadTestsFromNames() + ################################################################ + + # "Similar to loadTestsFromName(), but takes a sequence of names rather + # than a single name." + # + # What happens if that sequence of names is empty? + def test_loadTestsFromNames__empty_name_list(self): + loader = unittest2.TestLoader() + + suite = loader.loadTestsFromNames([]) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # "Similar to loadTestsFromName(), but takes a sequence of names rather + # than a single name." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens if that sequence of names is empty? + # + # XXX Should this raise a ValueError or just return an empty TestSuite? + def test_loadTestsFromNames__relative_empty_name_list(self): + loader = unittest2.TestLoader() + + suite = loader.loadTestsFromNames([], unittest2) + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), []) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Is ValueError raised in response to an empty name? + def test_loadTestsFromNames__empty_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromNames(['']) + except ValueError as e: + self.assertEqual(str(e), "Empty module name") + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when presented with an impossible module name? + def test_loadTestsFromNames__malformed_name(self): + loader = unittest2.TestLoader() + + # XXX Should this raise ValueError or ImportError? + try: + loader.loadTestsFromNames(['abc () //']) + except ValueError: + pass + except ImportError: + pass + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when no module can be found for the given name? + def test_loadTestsFromNames__unknown_module_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromNames(['sdasfasfasdf']) + except ImportError as e: + self.assertEqual(str(e), "No module named sdasfasfasdf") + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ImportError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the module can be found, but not the attribute? + def test_loadTestsFromNames__unknown_attr_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromNames(['unittest2.sdasfasfasdf', 'unittest2']) + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromNames failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when given an unknown attribute on a specified `module` + # argument? + def test_loadTestsFromNames__unknown_name_relative_1(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromNames(['sdasfasfasdf'], unittest2) + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # Do unknown attributes (relative to a provided module) still raise an + # exception even in the presence of valid attribute names? + def test_loadTestsFromNames__unknown_name_relative_2(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromNames(['TestCase', 'sdasfasfasdf'], unittest2) + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when faced with the empty string? + # + # XXX This currently raises AttributeError, though ValueError is probably + # more appropriate + def test_loadTestsFromNames__relative_empty_name(self): + loader = unittest2.TestLoader() + + try: + loader.loadTestsFromNames([''], unittest2) + except AttributeError: + pass + else: + self.fail("Failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when presented with an impossible attribute name? + def test_loadTestsFromNames__relative_malformed_name(self): + loader = unittest2.TestLoader() + + # XXX Should this raise AttributeError or ValueError? + try: + loader.loadTestsFromNames(['abc () //'], unittest2) + except AttributeError: + pass + except ValueError: + pass + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ValueError") + + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromNames() make sure the provided `module` is in fact + # a module? + # + # XXX This validation is currently not done. This flexibility should + # either be documented or a TypeError should be raised. + def test_loadTestsFromNames__relative_not_a_module(self): + class MyTestCase(unittest2.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromNames(['test_2'], NotAModule) + + reference = [unittest2.TestSuite([MyTestCase('test')])] + self.assertEqual(list(suite), reference) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does it raise an exception if the name resolves to an invalid + # object? + def test_loadTestsFromNames__relative_bad_object(self): + m = types.ModuleType('m') + m.testcase_1 = object() + + loader = unittest2.TestLoader() + try: + loader.loadTestsFromNames(['testcase_1'], m) + except TypeError: + pass + else: + self.fail("Should have raised TypeError") + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a test case class" + def test_loadTestsFromNames__relative_TestCase_subclass(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromNames(['testcase_1'], m) + self.assertIsInstance(suite, loader.suiteClass) + + expected = loader.suiteClass([MyTestCase('test')]) + self.assertEqual(list(suite), [expected]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a TestSuite instance" + def test_loadTestsFromNames__relative_TestSuite(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testsuite = unittest2.TestSuite([MyTestCase('test')]) + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromNames(['testsuite'], m) + self.assertIsInstance(suite, loader.suiteClass) + + self.assertEqual(list(suite), [m.testsuite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to ... a + # test method within a test case class" + def test_loadTestsFromNames__relative_testmethod(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromNames(['testcase_1.test'], m) + self.assertIsInstance(suite, loader.suiteClass) + + ref_suite = unittest2.TestSuite([MyTestCase('test')]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to ... a + # test method within a test case class" + # + # Does the method gracefully handle names that initially look like they + # resolve to "a test method within a test case class" but don't? + def test_loadTestsFromNames__relative_invalid_testmethod(self): + m = types.ModuleType('m') + class MyTestCase(unittest2.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest2.TestLoader() + try: + loader.loadTestsFromNames(['testcase_1.testfoo'], m) + except AttributeError as e: + self.assertEqual(str(e), "type object 'MyTestCase' has no attribute 'testfoo'") + else: + self.fail("Failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a ... TestSuite instance" + def test_loadTestsFromNames__callable__TestSuite(self): + m = types.ModuleType('m') + testcase_1 = unittest2.FunctionTestCase(lambda: None) + testcase_2 = unittest2.FunctionTestCase(lambda: None) + def return_TestSuite(): + return unittest2.TestSuite([testcase_1, testcase_2]) + m.return_TestSuite = return_TestSuite + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromNames(['return_TestSuite'], m) + self.assertIsInstance(suite, loader.suiteClass) + + expected = unittest2.TestSuite([testcase_1, testcase_2]) + self.assertEqual(list(suite), [expected]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase ... instance" + def test_loadTestsFromNames__callable__TestCase_instance(self): + m = types.ModuleType('m') + testcase_1 = unittest2.FunctionTestCase(lambda: None) + def return_TestCase(): + return testcase_1 + m.return_TestCase = return_TestCase + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromNames(['return_TestCase'], m) + self.assertIsInstance(suite, loader.suiteClass) + + ref_suite = unittest2.TestSuite([testcase_1]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # Are staticmethods handled correctly? + def test_loadTestsFromNames__callable__call_staticmethod(self): + m = types.ModuleType('m') + class Test1(unittest2.TestCase): + def test(self): + pass + + testcase_1 = Test1('test') + class Foo(unittest2.TestCase): + @staticmethod + def foo(): + return testcase_1 + m.Foo = Foo + + loader = unittest2.TestLoader() + suite = loader.loadTestsFromNames(['Foo.foo'], m) + self.assertIsInstance(suite, loader.suiteClass) + + ref_suite = unittest2.TestSuite([testcase_1]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # What happens when the callable returns something else? + def test_loadTestsFromNames__callable__wrong_type(self): + m = types.ModuleType('m') + def return_wrong(): + return 6 + m.return_wrong = return_wrong + + loader = unittest2.TestLoader() + try: + loader.loadTestsFromNames(['return_wrong'], m) + except TypeError: + pass + else: + self.fail("TestLoader.loadTestsFromNames failed to raise TypeError") + + # "The specifier can refer to modules and packages which have not been + # imported; they will be imported as a side-effect" + def test_loadTestsFromNames__module_not_loaded(self): + # We're going to try to load this module as a side-effect, so it + # better not be loaded before we try. + # + module_name = 'unittest2.test.dummy' + sys.modules.pop(module_name, None) + + loader = unittest2.TestLoader() + try: + suite = loader.loadTestsFromNames([module_name]) + + self.assertIsInstance(suite, loader.suiteClass) + self.assertEqual(list(suite), [unittest2.TestSuite()]) + + # module should now be loaded, thanks to loadTestsFromName() + self.assertIn(module_name, sys.modules) + finally: + if module_name in sys.modules: + del sys.modules[module_name] + + ################################################################ + ### /Tests for TestLoader.loadTestsFromNames() + + ### Tests for TestLoader.getTestCaseNames() + ################################################################ + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Test.foobar is defined to make sure getTestCaseNames() respects + # loader.testMethodPrefix + def test_getTestCaseNames(self): + class Test(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foobar(self): pass + + loader = unittest2.TestLoader() + + self.assertEqual(loader.getTestCaseNames(Test), ['test_1', 'test_2']) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Does getTestCaseNames() behave appropriately if no tests are found? + def test_getTestCaseNames__no_tests(self): + class Test(unittest2.TestCase): + def foobar(self): pass + + loader = unittest2.TestLoader() + + self.assertEqual(loader.getTestCaseNames(Test), []) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Are not-TestCases handled gracefully? + # + # XXX This should raise a TypeError, not return a list + # + # XXX It's too late in the 2.5 release cycle to fix this, but it should + # probably be revisited for 2.6 + def test_getTestCaseNames__not_a_TestCase(self): + class BadCase(int): + def test_foo(self): + pass + + loader = unittest2.TestLoader() + names = loader.getTestCaseNames(BadCase) + + self.assertEqual(names, ['test_foo']) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Make sure inherited names are handled. + # + # TestP.foobar is defined to make sure getTestCaseNames() respects + # loader.testMethodPrefix + def test_getTestCaseNames__inheritance(self): + class TestP(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foobar(self): pass + + class TestC(TestP): + def test_1(self): pass + def test_3(self): pass + + loader = unittest2.TestLoader() + + names = ['test_1', 'test_2', 'test_3'] + self.assertEqual(loader.getTestCaseNames(TestC), names) + + ################################################################ + ### /Tests for TestLoader.getTestCaseNames() + + ### Tests for TestLoader.testMethodPrefix + ################################################################ + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromTestCase(self): + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests_1 = unittest2.TestSuite([Foo('foo_bar')]) + tests_2 = unittest2.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest2.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromModule(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = [unittest2.TestSuite([Foo('foo_bar')])] + tests_2 = [unittest2.TestSuite([Foo('test_1'), Foo('test_2')])] + + loader = unittest2.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(list(loader.loadTestsFromModule(m)), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(list(loader.loadTestsFromModule(m)), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromName(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = unittest2.TestSuite([Foo('foo_bar')]) + tests_2 = unittest2.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest2.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromName('Foo', m), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromName('Foo', m), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromNames(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = unittest2.TestSuite([unittest2.TestSuite([Foo('foo_bar')])]) + tests_2 = unittest2.TestSuite([Foo('test_1'), Foo('test_2')]) + tests_2 = unittest2.TestSuite([tests_2]) + + loader = unittest2.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests_2) + + # "The default value is 'test'" + def test_testMethodPrefix__default_value(self): + loader = unittest2.TestLoader() + self.assertTrue(loader.testMethodPrefix == 'test') + + ################################################################ + ### /Tests for TestLoader.testMethodPrefix + + ### Tests for TestLoader.sortTestMethodsUsing + ################################################################ + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromTestCase(self): + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest2.TestLoader() + loader.sortTestMethodsUsing = unittest2.reversed_cmp_ + + tests = loader.suiteClass([Foo('test_2'), Foo('test_1')]) + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromModule(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest2.TestLoader() + loader.sortTestMethodsUsing = unittest2.reversed_cmp_ + + tests = [loader.suiteClass([Foo('test_2'), Foo('test_1')])] + self.assertEqual(list(loader.loadTestsFromModule(m)), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromName(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest2.TestLoader() + loader.sortTestMethodsUsing = unittest2.reversed_cmp_ + + tests = loader.suiteClass([Foo('test_2'), Foo('test_1')]) + self.assertEqual(loader.loadTestsFromName('Foo', m), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromNames(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest2.TestLoader() + loader.sortTestMethodsUsing = unittest2.reversed_cmp_ + + tests = [loader.suiteClass([Foo('test_2'), Foo('test_1')])] + self.assertEqual(list(loader.loadTestsFromNames(['Foo'], m)), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames()" + # + # Does it actually affect getTestCaseNames()? + def test_sortTestMethodsUsing__getTestCaseNames(self): + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest2.TestLoader() + loader.sortTestMethodsUsing = unittest2.reversed_cmp_ + + test_names = ['test_2', 'test_1'] + self.assertEqual(loader.getTestCaseNames(Foo), test_names) + + # "The default value is the built-in cmp() function" + def test_sortTestMethodsUsing__default_value(self): + loader = unittest2.TestLoader() + self.assertTrue(loader.sortTestMethodsUsing is unittest2.cmp_) + + # "it can be set to None to disable the sort." + # + # XXX How is this different from reassigning cmp? Are the tests returned + # in a random order or something? This behaviour should die + def test_sortTestMethodsUsing__None(self): + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest2.TestLoader() + loader.sortTestMethodsUsing = None + + test_names = ['test_2', 'test_1'] + self.assertEqual(set(loader.getTestCaseNames(Foo)), set(test_names)) + + ################################################################ + ### /Tests for TestLoader.sortTestMethodsUsing + + ### Tests for TestLoader.suiteClass + ################################################################ + + # "Callable object that constructs a test suite from a list of tests." + def test_suiteClass__loadTestsFromTestCase(self): + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests = [Foo('test_1'), Foo('test_2')] + + loader = unittest2.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromModule(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [[Foo('test_1'), Foo('test_2')]] + + loader = unittest2.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromModule(m), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromName(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [Foo('test_1'), Foo('test_2')] + + loader = unittest2.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromName('Foo', m), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromNames(self): + m = types.ModuleType('m') + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [[Foo('test_1'), Foo('test_2')]] + + loader = unittest2.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests) + + # "The default value is the TestSuite class" + def test_suiteClass__default_value(self): + loader = unittest2.TestLoader() + self.assertTrue(loader.suiteClass is unittest2.TestSuite) + + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_new_tests.py b/third_party/Python/module/unittest2/unittest2/test/test_new_tests.py new file mode 100644 index 000000000000..cc96bcb6a058 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_new_tests.py @@ -0,0 +1,46 @@ +from cStringIO import StringIO + +import unittest +import unittest2 + +from unittest2.test.support import resultFactory + + +class TestUnittest(unittest2.TestCase): + + def assertIsSubclass(self, actual, klass): + self.assertTrue(issubclass(actual, klass), "Not a subclass.") + + def testInheritance(self): + self.assertIsSubclass(unittest2.TestCase, unittest.TestCase) + self.assertIsSubclass(unittest2.TestResult, unittest.TestResult) + self.assertIsSubclass(unittest2.TestSuite, unittest.TestSuite) + self.assertIsSubclass(unittest2.TextTestRunner, unittest.TextTestRunner) + self.assertIsSubclass(unittest2.TestLoader, unittest.TestLoader) + self.assertIsSubclass(unittest2.TextTestResult, unittest.TestResult) + + def test_new_runner_old_case(self): + runner = unittest2.TextTestRunner(resultclass=resultFactory, + stream=StringIO()) + class Test(unittest.TestCase): + def testOne(self): + pass + suite = unittest2.TestSuite((Test('testOne'),)) + result = runner.run(suite) + self.assertEqual(result.testsRun, 1) + self.assertEqual(len(result.errors), 0) + + def test_old_runner_new_case(self): + runner = unittest.TextTestRunner(stream=StringIO()) + class Test(unittest2.TestCase): + def testOne(self): + self.assertDictEqual({}, {}) + + suite = unittest.TestSuite((Test('testOne'),)) + result = runner.run(suite) + self.assertEqual(result.testsRun, 1) + self.assertEqual(len(result.errors), 0) + + +if __name__ == '__main__': + unittest2.main() \ No newline at end of file diff --git a/third_party/Python/module/unittest2/unittest2/test/test_program.py b/third_party/Python/module/unittest2/unittest2/test/test_program.py new file mode 100644 index 000000000000..56077a6bb3a7 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_program.py @@ -0,0 +1,239 @@ +from cStringIO import StringIO + +import sys +import unittest2 + +hasInstallHandler = hasattr(unittest2, 'installHandler') + +class Test_TestProgram(unittest2.TestCase): + + # Horrible white box test + def testNoExit(self): + result = object() + test = object() + + class FakeRunner(object): + def run(self, test): + self.test = test + return result + + runner = FakeRunner() + + oldParseArgs = unittest2.TestProgram.parseArgs + def restoreParseArgs(): + unittest2.TestProgram.parseArgs = oldParseArgs + unittest2.TestProgram.parseArgs = lambda *args: None + self.addCleanup(restoreParseArgs) + + def removeTest(): + del unittest2.TestProgram.test + unittest2.TestProgram.test = test + self.addCleanup(removeTest) + + program = unittest2.TestProgram(testRunner=runner, exit=False, verbosity=2) + + self.assertEqual(program.result, result) + self.assertEqual(runner.test, test) + self.assertEqual(program.verbosity, 2) + + class FooBar(unittest2.TestCase): + def testPass(self): + assert True + def testFail(self): + assert False + + class FooBarLoader(unittest2.TestLoader): + """Test loader that returns a suite containing FooBar.""" + def loadTestsFromModule(self, module): + return self.suiteClass( + [self.loadTestsFromTestCase(Test_TestProgram.FooBar)]) + + + def test_NonExit(self): + program = unittest2.main(exit=False, + argv=["foobar"], + testRunner=unittest2.TextTestRunner(stream=StringIO()), + testLoader=self.FooBarLoader()) + self.assertTrue(hasattr(program, 'result')) + + + def test_Exit(self): + self.assertRaises( + SystemExit, + unittest2.main, + argv=["foobar"], + testRunner=unittest2.TextTestRunner(stream=StringIO()), + exit=True, + testLoader=self.FooBarLoader()) + + + def test_ExitAsDefault(self): + self.assertRaises( + SystemExit, + unittest2.main, + argv=["foobar"], + testRunner=unittest2.TextTestRunner(stream=StringIO()), + testLoader=self.FooBarLoader()) + + +class InitialisableProgram(unittest2.TestProgram): + exit = False + result = None + verbosity = 1 + defaultTest = None + testRunner = None + testLoader = unittest2.defaultTestLoader + progName = 'test' + test = 'test' + def __init__(self, *args): + pass + +RESULT = object() + +class FakeRunner(object): + initArgs = None + test = None + raiseError = False + + def __init__(self, **kwargs): + FakeRunner.initArgs = kwargs + if FakeRunner.raiseError: + FakeRunner.raiseError = False + raise TypeError + + def run(self, test): + FakeRunner.test = test + return RESULT + +class TestCommandLineArgs(unittest2.TestCase): + + def setUp(self): + self.program = InitialisableProgram() + self.program.createTests = lambda: None + FakeRunner.initArgs = None + FakeRunner.test = None + FakeRunner.raiseError = False + + def testHelpAndUnknown(self): + program = self.program + def usageExit(msg=None): + program.msg = msg + program.exit = True + program.usageExit = usageExit + + for opt in '-h', '-H', '--help': + program.exit = False + program.parseArgs([None, opt]) + self.assertTrue(program.exit) + self.assertIsNone(program.msg) + + program.parseArgs([None, '-$']) + self.assertTrue(program.exit) + self.assertIsNotNone(program.msg) + + def testVerbosity(self): + program = self.program + + for opt in '-q', '--quiet': + program.verbosity = 1 + program.parseArgs([None, opt]) + self.assertEqual(program.verbosity, 0) + + for opt in '-v', '--verbose': + program.verbosity = 1 + program.parseArgs([None, opt]) + self.assertEqual(program.verbosity, 2) + + def testBufferCatchFailfast(self): + program = self.program + for arg, attr in (('buffer', 'buffer'), ('failfast', 'failfast'), + ('catch', 'catchbreak')): + if attr == 'catch' and not hasInstallHandler: + continue + + short_opt = '-%s' % arg[0] + long_opt = '--%s' % arg + for opt in short_opt, long_opt: + setattr(program, attr, None) + + program.parseArgs([None, opt]) + self.assertTrue(getattr(program, attr)) + + for opt in short_opt, long_opt: + not_none = object() + setattr(program, attr, not_none) + + program.parseArgs([None, opt]) + self.assertEqual(getattr(program, attr), not_none) + + def testRunTestsRunnerClass(self): + program = self.program + + program.testRunner = FakeRunner + program.verbosity = 'verbosity' + program.failfast = 'failfast' + program.buffer = 'buffer' + + program.runTests() + + self.assertEqual(FakeRunner.initArgs, {'verbosity': 'verbosity', + 'failfast': 'failfast', + 'buffer': 'buffer'}) + self.assertEqual(FakeRunner.test, 'test') + self.assertIs(program.result, RESULT) + + def testRunTestsRunnerInstance(self): + program = self.program + + program.testRunner = FakeRunner() + FakeRunner.initArgs = None + + program.runTests() + + # A new FakeRunner should not have been instantiated + self.assertIsNone(FakeRunner.initArgs) + + self.assertEqual(FakeRunner.test, 'test') + self.assertIs(program.result, RESULT) + + def testRunTestsOldRunnerClass(self): + program = self.program + + FakeRunner.raiseError = True + program.testRunner = FakeRunner + program.verbosity = 'verbosity' + program.failfast = 'failfast' + program.buffer = 'buffer' + program.test = 'test' + + program.runTests() + + # If initialising raises a type error it should be retried + # without the new keyword arguments + self.assertEqual(FakeRunner.initArgs, {}) + self.assertEqual(FakeRunner.test, 'test') + self.assertIs(program.result, RESULT) + + def testCatchBreakInstallsHandler(self): + module = sys.modules['unittest2.main'] + original = module.installHandler + def restore(): + module.installHandler = original + self.addCleanup(restore) + + self.installed = False + def fakeInstallHandler(): + self.installed = True + module.installHandler = fakeInstallHandler + + program = self.program + program.catchbreak = True + + program.testRunner = FakeRunner + + program.runTests() + self.assertTrue(self.installed) + + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_result.py b/third_party/Python/module/unittest2/unittest2/test/test_result.py new file mode 100644 index 000000000000..69b31d542eae --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_result.py @@ -0,0 +1,418 @@ +from __future__ import print_function + +import sys +import textwrap +from StringIO import StringIO + +import unittest2 + + +class Test_TestResult(unittest2.TestCase): + # Note: there are not separate tests for TestResult.wasSuccessful(), + # TestResult.errors, TestResult.failures, TestResult.testsRun or + # TestResult.shouldStop because these only have meaning in terms of + # other TestResult methods. + # + # Accordingly, tests for the aforenamed attributes are incorporated + # in with the tests for the defining methods. + ################################################################ + + def test_init(self): + result = unittest2.TestResult() + + self.assertTrue(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 0) + self.assertEqual(result.shouldStop, False) + self.assertIsNone(result._stdout_buffer) + self.assertIsNone(result._stderr_buffer) + + # "This method can be called to signal that the set of tests being + # run should be aborted by setting the TestResult's shouldStop + # attribute to True." + def test_stop(self): + result = unittest2.TestResult() + + result.stop() + + self.assertEqual(result.shouldStop, True) + + # "Called when the test case test is about to be run. The default + # implementation simply increments the instance's testsRun counter." + def test_startTest(self): + class Foo(unittest2.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + + result = unittest2.TestResult() + + result.startTest(test) + + self.assertTrue(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + result.stopTest(test) + + # "Called after the test case test has been executed, regardless of + # the outcome. The default implementation does nothing." + def test_stopTest(self): + class Foo(unittest2.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + + result = unittest2.TestResult() + + result.startTest(test) + + self.assertTrue(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + result.stopTest(test) + + # Same tests as above; make sure nothing has changed + self.assertTrue(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + # "Called before and after tests are run. The default implementation does nothing." + def test_startTestRun_stopTestRun(self): + result = unittest2.TestResult() + result.startTestRun() + result.stopTestRun() + + # "addSuccess(test)" + # ... + # "Called when the test case test succeeds" + # ... + # "wasSuccessful() - Returns True if all tests run so far have passed, + # otherwise returns False" + # ... + # "testsRun - The total number of tests run so far." + # ... + # "errors - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test which raised an + # unexpected exception. Contains formatted + # tracebacks instead of sys.exc_info() results." + # ... + # "failures - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test where a failure was + # explicitly signalled using the TestCase.fail*() or TestCase.assert*() + # methods. Contains formatted tracebacks instead + # of sys.exc_info() results." + def test_addSuccess(self): + class Foo(unittest2.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + + result = unittest2.TestResult() + + result.startTest(test) + result.addSuccess(test) + result.stopTest(test) + + self.assertTrue(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + # "addFailure(test, err)" + # ... + # "Called when the test case test signals a failure. err is a tuple of + # the form returned by sys.exc_info(): (type, value, traceback)" + # ... + # "wasSuccessful() - Returns True if all tests run so far have passed, + # otherwise returns False" + # ... + # "testsRun - The total number of tests run so far." + # ... + # "errors - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test which raised an + # unexpected exception. Contains formatted + # tracebacks instead of sys.exc_info() results." + # ... + # "failures - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test where a failure was + # explicitly signalled using the TestCase.fail*() or TestCase.assert*() + # methods. Contains formatted tracebacks instead + # of sys.exc_info() results." + def test_addFailure(self): + class Foo(unittest2.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + try: + test.fail("foo") + except: + exc_info_tuple = sys.exc_info() + + result = unittest2.TestResult() + + result.startTest(test) + result.addFailure(test, exc_info_tuple) + result.stopTest(test) + + self.assertFalse(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 1) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + test_case, formatted_exc = result.failures[0] + self.assertTrue(test_case is test) + self.assertIsInstance(formatted_exc, str) + + # "addError(test, err)" + # ... + # "Called when the test case test raises an unexpected exception err + # is a tuple of the form returned by sys.exc_info(): + # (type, value, traceback)" + # ... + # "wasSuccessful() - Returns True if all tests run so far have passed, + # otherwise returns False" + # ... + # "testsRun - The total number of tests run so far." + # ... + # "errors - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test which raised an + # unexpected exception. Contains formatted + # tracebacks instead of sys.exc_info() results." + # ... + # "failures - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test where a failure was + # explicitly signalled using the TestCase.fail*() or TestCase.assert*() + # methods. Contains formatted tracebacks instead + # of sys.exc_info() results." + def test_addError(self): + class Foo(unittest2.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + try: + raise TypeError() + except: + exc_info_tuple = sys.exc_info() + + result = unittest2.TestResult() + + result.startTest(test) + result.addError(test, exc_info_tuple) + result.stopTest(test) + + self.assertFalse(result.wasSuccessful()) + self.assertEqual(len(result.errors), 1) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + test_case, formatted_exc = result.errors[0] + self.assertTrue(test_case is test) + self.assertIsInstance(formatted_exc, str) + + def testGetDescriptionWithoutDocstring(self): + result = unittest2.TextTestResult(None, True, 1) + self.assertEqual( + result.getDescription(self), + 'testGetDescriptionWithoutDocstring (' + __name__ + + '.Test_TestResult)') + + def testGetDescriptionWithOneLineDocstring(self): + """Tests getDescription() for a method with a docstring.""" + result = unittest2.TextTestResult(None, True, 1) + self.assertEqual( + result.getDescription(self), + ('testGetDescriptionWithOneLineDocstring ' + '(' + __name__ + '.Test_TestResult)\n' + 'Tests getDescription() for a method with a docstring.')) + + def testGetDescriptionWithMultiLineDocstring(self): + """Tests getDescription() for a method with a longer docstring. + The second line of the docstring. + """ + result = unittest2.TextTestResult(None, True, 1) + self.assertEqual( + result.getDescription(self), + ('testGetDescriptionWithMultiLineDocstring ' + '(' + __name__ + '.Test_TestResult)\n' + 'Tests getDescription() for a method with a longer ' + 'docstring.')) + + def testStackFrameTrimming(self): + class Frame(object): + class tb_frame(object): + f_globals = {} + result = unittest2.TestResult() + self.assertFalse(result._is_relevant_tb_level(Frame)) + + Frame.tb_frame.f_globals['__unittest'] = True + self.assertTrue(result._is_relevant_tb_level(Frame)) + + def testFailFast(self): + result = unittest2.TestResult() + result._exc_info_to_string = lambda *_: '' + result.failfast = True + result.addError(None, None) + self.assertTrue(result.shouldStop) + + result = unittest2.TestResult() + result._exc_info_to_string = lambda *_: '' + result.failfast = True + result.addFailure(None, None) + self.assertTrue(result.shouldStop) + + result = unittest2.TestResult() + result._exc_info_to_string = lambda *_: '' + result.failfast = True + result.addUnexpectedSuccess(None) + self.assertTrue(result.shouldStop) + + def testFailFastSetByRunner(self): + runner = unittest2.TextTestRunner(stream=StringIO(), failfast=True) + self.testRan = False + def test(result): + self.testRan = True + self.assertTrue(result.failfast) + runner.run(test) + self.assertTrue(self.testRan) + + +class TestOutputBuffering(unittest2.TestCase): + + def setUp(self): + self._real_out = sys.stdout + self._real_err = sys.stderr + + def tearDown(self): + sys.stdout = self._real_out + sys.stderr = self._real_err + + def testBufferOutputOff(self): + real_out = self._real_out + real_err = self._real_err + + result = unittest2.TestResult() + self.assertFalse(result.buffer) + + self.assertIs(real_out, sys.stdout) + self.assertIs(real_err, sys.stderr) + + result.startTest(self) + + self.assertIs(real_out, sys.stdout) + self.assertIs(real_err, sys.stderr) + + def testBufferOutputStartTestAddSuccess(self): + real_out = self._real_out + real_err = self._real_err + + result = unittest2.TestResult() + self.assertFalse(result.buffer) + + result.buffer = True + + self.assertIs(real_out, sys.stdout) + self.assertIs(real_err, sys.stderr) + + result.startTest(self) + + self.assertIsNot(real_out, sys.stdout) + self.assertIsNot(real_err, sys.stderr) + self.assertIsInstance(sys.stdout, StringIO) + self.assertIsInstance(sys.stderr, StringIO) + self.assertIsNot(sys.stdout, sys.stderr) + + out_stream = sys.stdout + err_stream = sys.stderr + + result._original_stdout = StringIO() + result._original_stderr = StringIO() + + print('foo') + print('bar', file=sys.stderr) + + self.assertEqual(out_stream.getvalue(), 'foo\n') + self.assertEqual(err_stream.getvalue(), 'bar\n') + + self.assertEqual(result._original_stdout.getvalue(), '') + self.assertEqual(result._original_stderr.getvalue(), '') + + result.addSuccess(self) + result.stopTest(self) + + self.assertIs(sys.stdout, result._original_stdout) + self.assertIs(sys.stderr, result._original_stderr) + + self.assertEqual(result._original_stdout.getvalue(), '') + self.assertEqual(result._original_stderr.getvalue(), '') + + self.assertEqual(out_stream.getvalue(), '') + self.assertEqual(err_stream.getvalue(), '') + + + def getStartedResult(self): + result = unittest2.TestResult() + result.buffer = True + result.startTest(self) + return result + + def testBufferOutputAddErrorOrFailure(self): + for message_attr, add_attr, include_error in [ + ('errors', 'addError', True), + ('failures', 'addFailure', False), + ('errors', 'addError', True), + ('failures', 'addFailure', False) + ]: + result = self.getStartedResult() + result._original_stderr = StringIO() + result._original_stdout = StringIO() + + print('foo') + if include_error: + print('bar', file=sys.stderr) + + addFunction = getattr(result, add_attr) + addFunction(self, (None, None, None)) + result.stopTest(self) + + result_list = getattr(result, message_attr) + self.assertEqual(len(result_list), 1) + + test, message = result_list[0] + expectedOutMessage = textwrap.dedent(""" + Stdout: + foo + """) + expectedErrMessage = '' + if include_error: + expectedErrMessage = textwrap.dedent(""" + Stderr: + bar + """) + expectedFullMessage = 'None\n%s%s' % (expectedOutMessage, expectedErrMessage) + + self.assertIs(test, self) + self.assertEqual(result._original_stdout.getvalue(), expectedOutMessage) + self.assertEqual(result._original_stderr.getvalue(), expectedErrMessage) + self.assertMultiLineEqual(message, expectedFullMessage) + + + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_runner.py b/third_party/Python/module/unittest2/unittest2/test/test_runner.py new file mode 100644 index 000000000000..38b39efb0069 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_runner.py @@ -0,0 +1,129 @@ +import pickle + +from cStringIO import StringIO +from unittest2.test.support import LoggingResult, OldTestResult + +import unittest2 + + +class Test_TextTestRunner(unittest2.TestCase): + """Tests for TextTestRunner.""" + + def test_init(self): + runner = unittest2.TextTestRunner() + self.assertFalse(runner.failfast) + self.assertFalse(runner.buffer) + self.assertEqual(runner.verbosity, 1) + self.assertTrue(runner.descriptions) + self.assertEqual(runner.resultclass, unittest2.TextTestResult) + + + def testBufferAndFailfast(self): + class Test(unittest2.TestCase): + def testFoo(self): + pass + result = unittest2.TestResult() + runner = unittest2.TextTestRunner(stream=StringIO(), failfast=True, + buffer=True) + # Use our result object + runner._makeResult = lambda: result + runner.run(Test('testFoo')) + + self.assertTrue(result.failfast) + self.assertTrue(result.buffer) + + def testRunnerRegistersResult(self): + class Test(unittest2.TestCase): + def testFoo(self): + pass + originalRegisterResult = unittest2.runner.registerResult + def cleanup(): + unittest2.runner.registerResult = originalRegisterResult + self.addCleanup(cleanup) + + result = unittest2.TestResult() + runner = unittest2.TextTestRunner(stream=StringIO()) + # Use our result object + runner._makeResult = lambda: result + + self.wasRegistered = 0 + def fakeRegisterResult(thisResult): + self.wasRegistered += 1 + self.assertEqual(thisResult, result) + unittest2.runner.registerResult = fakeRegisterResult + + runner.run(unittest2.TestSuite()) + self.assertEqual(self.wasRegistered, 1) + + def test_works_with_result_without_startTestRun_stopTestRun(self): + class OldTextResult(OldTestResult): + def __init__(self, *_): + super(OldTextResult, self).__init__() + separator2 = '' + def printErrors(self): + pass + + runner = unittest2.TextTestRunner(stream=StringIO(), + resultclass=OldTextResult) + runner.run(unittest2.TestSuite()) + + def test_startTestRun_stopTestRun_called(self): + class LoggingTextResult(LoggingResult): + separator2 = '' + def printErrors(self): + pass + + class LoggingRunner(unittest2.TextTestRunner): + def __init__(self, events): + super(LoggingRunner, self).__init__(StringIO()) + self._events = events + + def _makeResult(self): + return LoggingTextResult(self._events) + + events = [] + runner = LoggingRunner(events) + runner.run(unittest2.TestSuite()) + expected = ['startTestRun', 'stopTestRun'] + self.assertEqual(events, expected) + + def test_pickle_unpickle(self): + # Issue #7197: a TextTestRunner should be (un)pickleable. This is + # required by test_multiprocessing under Windows (in verbose mode). + import StringIO + # cStringIO objects are not pickleable, but StringIO objects are. + stream = StringIO.StringIO("foo") + runner = unittest2.TextTestRunner(stream) + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(runner, protocol=protocol) + obj = pickle.loads(s) + # StringIO objects never compare equal, a cheap test instead. + self.assertEqual(obj.stream.getvalue(), stream.getvalue()) + + def test_resultclass(self): + def MockResultClass(*args): + return args + STREAM = object() + DESCRIPTIONS = object() + VERBOSITY = object() + runner = unittest2.TextTestRunner(STREAM, DESCRIPTIONS, VERBOSITY, + resultclass=MockResultClass) + self.assertEqual(runner.resultclass, MockResultClass) + + expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY) + self.assertEqual(runner._makeResult(), expectedresult) + + + def test_oldresult(self): + class Test(unittest2.TestCase): + def testFoo(self): + pass + runner = unittest2.TextTestRunner(resultclass=OldTestResult, + stream=StringIO()) + # This will raise an exception if TextTestRunner can't handle old + # test result objects + runner.run(Test('testFoo')) + + +if __name__ == '__main__': + unittest2.main() \ No newline at end of file diff --git a/third_party/Python/module/unittest2/unittest2/test/test_setups.py b/third_party/Python/module/unittest2/unittest2/test/test_setups.py new file mode 100644 index 000000000000..55c7f578df10 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_setups.py @@ -0,0 +1,502 @@ +import sys + +from cStringIO import StringIO + +import unittest2 +from unittest2.test.support import resultFactory + + +class TestSetups(unittest2.TestCase): + + def getRunner(self): + return unittest2.TextTestRunner(resultclass=resultFactory, + stream=StringIO()) + def runTests(self, *cases): + suite = unittest2.TestSuite() + for case in cases: + tests = unittest2.defaultTestLoader.loadTestsFromTestCase(case) + suite.addTests(tests) + + runner = self.getRunner() + + # creating a nested suite exposes some potential bugs + realSuite = unittest2.TestSuite() + realSuite.addTest(suite) + # adding empty suites to the end exposes potential bugs + suite.addTest(unittest2.TestSuite()) + realSuite.addTest(unittest2.TestSuite()) + return runner.run(realSuite) + + def test_setup_class(self): + class Test(unittest2.TestCase): + setUpCalled = 0 + @classmethod + def setUpClass(cls): + Test.setUpCalled += 1 + unittest2.TestCase.setUpClass() + def test_one(self): + pass + def test_two(self): + pass + + result = self.runTests(Test) + + self.assertEqual(Test.setUpCalled, 1) + self.assertEqual(result.testsRun, 2) + self.assertEqual(len(result.errors), 0) + + def test_teardown_class(self): + class Test(unittest2.TestCase): + tearDownCalled = 0 + @classmethod + def tearDownClass(cls): + Test.tearDownCalled += 1 + unittest2.TestCase.tearDownClass() + def test_one(self): + pass + def test_two(self): + pass + + result = self.runTests(Test) + + self.assertEqual(Test.tearDownCalled, 1) + self.assertEqual(result.testsRun, 2) + self.assertEqual(len(result.errors), 0) + + def test_teardown_class_two_classes(self): + class Test(unittest2.TestCase): + tearDownCalled = 0 + @classmethod + def tearDownClass(cls): + Test.tearDownCalled += 1 + unittest2.TestCase.tearDownClass() + def test_one(self): + pass + def test_two(self): + pass + + class Test2(unittest2.TestCase): + tearDownCalled = 0 + @classmethod + def tearDownClass(cls): + Test2.tearDownCalled += 1 + unittest2.TestCase.tearDownClass() + def test_one(self): + pass + def test_two(self): + pass + + result = self.runTests(Test, Test2) + + self.assertEqual(Test.tearDownCalled, 1) + self.assertEqual(Test2.tearDownCalled, 1) + self.assertEqual(result.testsRun, 4) + self.assertEqual(len(result.errors), 0) + + def test_error_in_setupclass(self): + class BrokenTest(unittest2.TestCase): + @classmethod + def setUpClass(cls): + raise TypeError('foo') + def test_one(self): + pass + def test_two(self): + pass + + result = self.runTests(BrokenTest) + + self.assertEqual(result.testsRun, 0) + self.assertEqual(len(result.errors), 1) + error, _ = result.errors[0] + self.assertEqual(str(error), + 'setUpClass (%s.BrokenTest)' % __name__) + + def test_error_in_teardown_class(self): + class Test(unittest2.TestCase): + tornDown = 0 + @classmethod + def tearDownClass(cls): + Test.tornDown += 1 + raise TypeError('foo') + def test_one(self): + pass + def test_two(self): + pass + + class Test2(unittest2.TestCase): + tornDown = 0 + @classmethod + def tearDownClass(cls): + Test2.tornDown += 1 + raise TypeError('foo') + def test_one(self): + pass + def test_two(self): + pass + + result = self.runTests(Test, Test2) + self.assertEqual(result.testsRun, 4) + self.assertEqual(len(result.errors), 2) + self.assertEqual(Test.tornDown, 1) + self.assertEqual(Test2.tornDown, 1) + + error, _ = result.errors[0] + self.assertEqual(str(error), + 'tearDownClass (%s.Test)' % __name__) + + def test_class_not_torndown_when_setup_fails(self): + class Test(unittest2.TestCase): + tornDown = False + @classmethod + def setUpClass(cls): + raise TypeError + @classmethod + def tearDownClass(cls): + Test.tornDown = True + raise TypeError('foo') + def test_one(self): + pass + + self.runTests(Test) + self.assertFalse(Test.tornDown) + + def test_class_not_setup_or_torndown_when_skipped(self): + class Test(unittest2.TestCase): + classSetUp = False + tornDown = False + @classmethod + def setUpClass(cls): + Test.classSetUp = True + @classmethod + def tearDownClass(cls): + Test.tornDown = True + def test_one(self): + pass + + Test = unittest2.skip("hop")(Test) + self.runTests(Test) + self.assertFalse(Test.classSetUp) + self.assertFalse(Test.tornDown) + + def test_setup_teardown_order_with_pathological_suite(self): + results = [] + + class Module1(object): + @staticmethod + def setUpModule(): + results.append('Module1.setUpModule') + @staticmethod + def tearDownModule(): + results.append('Module1.tearDownModule') + + class Module2(object): + @staticmethod + def setUpModule(): + results.append('Module2.setUpModule') + @staticmethod + def tearDownModule(): + results.append('Module2.tearDownModule') + + class Test1(unittest2.TestCase): + @classmethod + def setUpClass(cls): + results.append('setup 1') + @classmethod + def tearDownClass(cls): + results.append('teardown 1') + def testOne(self): + results.append('Test1.testOne') + def testTwo(self): + results.append('Test1.testTwo') + + class Test2(unittest2.TestCase): + @classmethod + def setUpClass(cls): + results.append('setup 2') + @classmethod + def tearDownClass(cls): + results.append('teardown 2') + def testOne(self): + results.append('Test2.testOne') + def testTwo(self): + results.append('Test2.testTwo') + + class Test3(unittest2.TestCase): + @classmethod + def setUpClass(cls): + results.append('setup 3') + @classmethod + def tearDownClass(cls): + results.append('teardown 3') + def testOne(self): + results.append('Test3.testOne') + def testTwo(self): + results.append('Test3.testTwo') + + Test1.__module__ = Test2.__module__ = 'Module' + Test3.__module__ = 'Module2' + sys.modules['Module'] = Module1 + sys.modules['Module2'] = Module2 + + first = unittest2.TestSuite((Test1('testOne'),)) + second = unittest2.TestSuite((Test1('testTwo'),)) + third = unittest2.TestSuite((Test2('testOne'),)) + fourth = unittest2.TestSuite((Test2('testTwo'),)) + fifth = unittest2.TestSuite((Test3('testOne'),)) + sixth = unittest2.TestSuite((Test3('testTwo'),)) + suite = unittest2.TestSuite((first, second, third, fourth, fifth, sixth)) + + runner = self.getRunner() + result = runner.run(suite) + self.assertEqual(result.testsRun, 6) + self.assertEqual(len(result.errors), 0) + + self.assertEqual(results, + ['Module1.setUpModule', 'setup 1', + 'Test1.testOne', 'Test1.testTwo', 'teardown 1', + 'setup 2', 'Test2.testOne', 'Test2.testTwo', + 'teardown 2', 'Module1.tearDownModule', + 'Module2.setUpModule', 'setup 3', + 'Test3.testOne', 'Test3.testTwo', + 'teardown 3', 'Module2.tearDownModule']) + + def test_setup_module(self): + class Module(object): + moduleSetup = 0 + @staticmethod + def setUpModule(): + Module.moduleSetup += 1 + + class Test(unittest2.TestCase): + def test_one(self): + pass + def test_two(self): + pass + Test.__module__ = 'Module' + sys.modules['Module'] = Module + + result = self.runTests(Test) + self.assertEqual(Module.moduleSetup, 1) + self.assertEqual(result.testsRun, 2) + self.assertEqual(len(result.errors), 0) + + def test_error_in_setup_module(self): + class Module(object): + moduleSetup = 0 + moduleTornDown = 0 + @staticmethod + def setUpModule(): + Module.moduleSetup += 1 + raise TypeError('foo') + @staticmethod + def tearDownModule(): + Module.moduleTornDown += 1 + + class Test(unittest2.TestCase): + classSetUp = False + classTornDown = False + @classmethod + def setUpClass(cls): + Test.classSetUp = True + @classmethod + def tearDownClass(cls): + Test.classTornDown = True + def test_one(self): + pass + def test_two(self): + pass + + class Test2(unittest2.TestCase): + def test_one(self): + pass + def test_two(self): + pass + Test.__module__ = 'Module' + Test2.__module__ = 'Module' + sys.modules['Module'] = Module + + result = self.runTests(Test, Test2) + self.assertEqual(Module.moduleSetup, 1) + self.assertEqual(Module.moduleTornDown, 0) + self.assertEqual(result.testsRun, 0) + self.assertFalse(Test.classSetUp) + self.assertFalse(Test.classTornDown) + self.assertEqual(len(result.errors), 1) + error, _ = result.errors[0] + self.assertEqual(str(error), 'setUpModule (Module)') + + def test_testcase_with_missing_module(self): + class Test(unittest2.TestCase): + def test_one(self): + pass + def test_two(self): + pass + Test.__module__ = 'Module' + sys.modules.pop('Module', None) + + result = self.runTests(Test) + self.assertEqual(result.testsRun, 2) + + def test_teardown_module(self): + class Module(object): + moduleTornDown = 0 + @staticmethod + def tearDownModule(): + Module.moduleTornDown += 1 + + class Test(unittest2.TestCase): + def test_one(self): + pass + def test_two(self): + pass + Test.__module__ = 'Module' + sys.modules['Module'] = Module + + result = self.runTests(Test) + self.assertEqual(Module.moduleTornDown, 1) + self.assertEqual(result.testsRun, 2) + self.assertEqual(len(result.errors), 0) + + def test_error_in_teardown_module(self): + class Module(object): + moduleTornDown = 0 + @staticmethod + def tearDownModule(): + Module.moduleTornDown += 1 + raise TypeError('foo') + + class Test(unittest2.TestCase): + classSetUp = False + classTornDown = False + @classmethod + def setUpClass(cls): + Test.classSetUp = True + @classmethod + def tearDownClass(cls): + Test.classTornDown = True + def test_one(self): + pass + def test_two(self): + pass + + class Test2(unittest2.TestCase): + def test_one(self): + pass + def test_two(self): + pass + Test.__module__ = 'Module' + Test2.__module__ = 'Module' + sys.modules['Module'] = Module + + result = self.runTests(Test, Test2) + self.assertEqual(Module.moduleTornDown, 1) + self.assertEqual(result.testsRun, 4) + self.assertTrue(Test.classSetUp) + self.assertTrue(Test.classTornDown) + self.assertEqual(len(result.errors), 1) + error, _ = result.errors[0] + self.assertEqual(str(error), 'tearDownModule (Module)') + + def test_skiptest_in_setupclass(self): + class Test(unittest2.TestCase): + @classmethod + def setUpClass(cls): + raise unittest2.SkipTest('foo') + def test_one(self): + pass + def test_two(self): + pass + + result = self.runTests(Test) + self.assertEqual(result.testsRun, 0) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.skipped), 1) + skipped = result.skipped[0][0] + self.assertEqual(str(skipped), 'setUpClass (%s.Test)' % __name__) + + def test_skiptest_in_setupmodule(self): + class Test(unittest2.TestCase): + def test_one(self): + pass + def test_two(self): + pass + + class Module(object): + @staticmethod + def setUpModule(): + raise unittest2.SkipTest('foo') + + Test.__module__ = 'Module' + sys.modules['Module'] = Module + + result = self.runTests(Test) + self.assertEqual(result.testsRun, 0) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.skipped), 1) + skipped = result.skipped[0][0] + self.assertEqual(str(skipped), 'setUpModule (Module)') + + def test_suite_debug_executes_setups_and_teardowns(self): + ordering = [] + + class Module(object): + @staticmethod + def setUpModule(): + ordering.append('setUpModule') + @staticmethod + def tearDownModule(): + ordering.append('tearDownModule') + + class Test(unittest2.TestCase): + @classmethod + def setUpClass(cls): + ordering.append('setUpClass') + @classmethod + def tearDownClass(cls): + ordering.append('tearDownClass') + def test_something(self): + ordering.append('test_something') + + Test.__module__ = 'Module' + sys.modules['Module'] = Module + + suite = unittest2.defaultTestLoader.loadTestsFromTestCase(Test) + suite.debug() + expectedOrder = ['setUpModule', 'setUpClass', 'test_something', 'tearDownClass', 'tearDownModule'] + self.assertEqual(ordering, expectedOrder) + + def test_suite_debug_propagates_exceptions(self): + class Module(object): + @staticmethod + def setUpModule(): + if phase == 0: + raise Exception('setUpModule') + @staticmethod + def tearDownModule(): + if phase == 1: + raise Exception('tearDownModule') + + class Test(unittest2.TestCase): + @classmethod + def setUpClass(cls): + if phase == 2: + raise Exception('setUpClass') + @classmethod + def tearDownClass(cls): + if phase == 3: + raise Exception('tearDownClass') + def test_something(self): + if phase == 4: + raise Exception('test_something') + + Test.__module__ = 'Module' + sys.modules['Module'] = Module + + _suite = unittest2.defaultTestLoader.loadTestsFromTestCase(Test) + suite = unittest2.TestSuite() + + # nesting a suite again exposes a bug in the initial implementation + suite.addTest(_suite) + messages = ('setUpModule', 'tearDownModule', 'setUpClass', 'tearDownClass', 'test_something') + for phase, msg in enumerate(messages): + self.assertRaisesRegexp(Exception, msg, suite.debug) diff --git a/third_party/Python/module/unittest2/unittest2/test/test_skipping.py b/third_party/Python/module/unittest2/unittest2/test/test_skipping.py new file mode 100644 index 000000000000..9555b9ff7700 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_skipping.py @@ -0,0 +1,143 @@ +from unittest2.test.support import LoggingResult + +import unittest2 + + +class Test_TestSkipping(unittest2.TestCase): + + def test_skipping(self): + class Foo(unittest2.TestCase): + def test_skip_me(self): + self.skipTest("skip") + events = [] + result = LoggingResult(events) + test = Foo("test_skip_me") + test.run(result) + self.assertEqual(events, ['startTest', 'addSkip', 'stopTest']) + self.assertEqual(result.skipped, [(test, "skip")]) + + # Try letting setUp skip the test now. + class Foo(unittest2.TestCase): + def setUp(self): + self.skipTest("testing") + def test_nothing(self): pass + events = [] + result = LoggingResult(events) + test = Foo("test_nothing") + test.run(result) + self.assertEqual(events, ['startTest', 'addSkip', 'stopTest']) + self.assertEqual(result.skipped, [(test, "testing")]) + self.assertEqual(result.testsRun, 1) + + def test_skipping_decorators(self): + op_table = ((unittest2.skipUnless, False, True), + (unittest2.skipIf, True, False)) + for deco, do_skip, dont_skip in op_table: + class Foo(unittest2.TestCase): + @deco(do_skip, "testing") + def test_skip(self): + pass + + @deco(dont_skip, "testing") + def test_dont_skip(self): + pass + + test_do_skip = Foo("test_skip") + test_dont_skip = Foo("test_dont_skip") + suite = unittest2.TestSuite([test_do_skip, test_dont_skip]) + events = [] + result = LoggingResult(events) + suite.run(result) + self.assertEqual(len(result.skipped), 1) + expected = ['startTest', 'addSkip', 'stopTest', + 'startTest', 'addSuccess', 'stopTest'] + self.assertEqual(events, expected) + self.assertEqual(result.testsRun, 2) + self.assertEqual(result.skipped, [(test_do_skip, "testing")]) + self.assertTrue(result.wasSuccessful()) + + def test_skip_class(self): + class Foo(unittest2.TestCase): + def test_1(self): + record.append(1) + + # was originally a class decorator... + Foo = unittest2.skip("testing")(Foo) + record = [] + result = unittest2.TestResult() + test = Foo("test_1") + suite = unittest2.TestSuite([test]) + suite.run(result) + self.assertEqual(result.skipped, [(test, "testing")]) + self.assertEqual(record, []) + + def test_expected_failure(self): + class Foo(unittest2.TestCase): + @unittest2.expectedFailure + def test_die(self): + self.fail("help me!") + events = [] + result = LoggingResult(events) + test = Foo("test_die") + test.run(result) + self.assertEqual(events, + ['startTest', 'addExpectedFailure', 'stopTest']) + self.assertEqual(result.expectedFailures[0][0], test) + self.assertTrue(result.wasSuccessful()) + + def test_unexpected_success(self): + class Foo(unittest2.TestCase): + @unittest2.expectedFailure + def test_die(self): + pass + events = [] + result = LoggingResult(events) + test = Foo("test_die") + test.run(result) + self.assertEqual(events, + ['startTest', 'addUnexpectedSuccess', 'stopTest']) + self.assertFalse(result.failures) + self.assertEqual(result.unexpectedSuccesses, [test]) + self.assertTrue(result.wasSuccessful()) + + def test_skip_doesnt_run_setup(self): + class Foo(unittest2.TestCase): + wasSetUp = False + wasTornDown = False + def setUp(self): + Foo.wasSetUp = True + def tornDown(self): + Foo.wasTornDown = True + @unittest2.skip('testing') + def test_1(self): + pass + + result = unittest2.TestResult() + test = Foo("test_1") + suite = unittest2.TestSuite([test]) + suite.run(result) + self.assertEqual(result.skipped, [(test, "testing")]) + self.assertFalse(Foo.wasSetUp) + self.assertFalse(Foo.wasTornDown) + + def test_decorated_skip(self): + def decorator(func): + def inner(*a): + return func(*a) + return inner + + class Foo(unittest2.TestCase): + @decorator + @unittest2.skip('testing') + def test_1(self): + pass + + result = unittest2.TestResult() + test = Foo("test_1") + suite = unittest2.TestSuite([test]) + suite.run(result) + self.assertEqual(result.skipped, [(test, "testing")]) + + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_suite.py b/third_party/Python/module/unittest2/unittest2/test/test_suite.py new file mode 100644 index 000000000000..64a72bbdf147 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_suite.py @@ -0,0 +1,341 @@ +from unittest2.test.support import EqualityMixin, LoggingResult + +import sys +import unittest2 + +class Test(object): + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + def test_3(self): pass + def runTest(self): pass + +def _mk_TestSuite(*names): + return unittest2.TestSuite(Test.Foo(n) for n in names) + + +class Test_TestSuite(unittest2.TestCase, EqualityMixin): + + ### Set up attributes needed by inherited tests + ################################################################ + + # Used by EqualityMixin.test_eq + eq_pairs = [(unittest2.TestSuite(), unittest2.TestSuite()), + (unittest2.TestSuite(), unittest2.TestSuite([])), + (_mk_TestSuite('test_1'), _mk_TestSuite('test_1'))] + + # Used by EqualityMixin.test_ne + ne_pairs = [(unittest2.TestSuite(), _mk_TestSuite('test_1')), + (unittest2.TestSuite([]), _mk_TestSuite('test_1')), + (_mk_TestSuite('test_1', 'test_2'), _mk_TestSuite('test_1', 'test_3')), + (_mk_TestSuite('test_1'), _mk_TestSuite('test_2'))] + + ################################################################ + ### /Set up attributes needed by inherited tests + + ### Tests for TestSuite.__init__ + ################################################################ + + # "class TestSuite([tests])" + # + # The tests iterable should be optional + def test_init__tests_optional(self): + suite = unittest2.TestSuite() + + self.assertEqual(suite.countTestCases(), 0) + + # "class TestSuite([tests])" + # ... + # "If tests is given, it must be an iterable of individual test cases + # or other test suites that will be used to build the suite initially" + # + # TestSuite should deal with empty tests iterables by allowing the + # creation of an empty suite + def test_init__empty_tests(self): + suite = unittest2.TestSuite([]) + + self.assertEqual(suite.countTestCases(), 0) + + # "class TestSuite([tests])" + # ... + # "If tests is given, it must be an iterable of individual test cases + # or other test suites that will be used to build the suite initially" + # + # TestSuite should allow any iterable to provide tests + def test_init__tests_from_any_iterable(self): + def tests(): + yield unittest2.FunctionTestCase(lambda: None) + yield unittest2.FunctionTestCase(lambda: None) + + suite_1 = unittest2.TestSuite(tests()) + self.assertEqual(suite_1.countTestCases(), 2) + + suite_2 = unittest2.TestSuite(suite_1) + self.assertEqual(suite_2.countTestCases(), 2) + + suite_3 = unittest2.TestSuite(set(suite_1)) + self.assertEqual(suite_3.countTestCases(), 2) + + # "class TestSuite([tests])" + # ... + # "If tests is given, it must be an iterable of individual test cases + # or other test suites that will be used to build the suite initially" + # + # Does TestSuite() also allow other TestSuite() instances to be present + # in the tests iterable? + def test_init__TestSuite_instances_in_tests(self): + def tests(): + ftc = unittest2.FunctionTestCase(lambda: None) + yield unittest2.TestSuite([ftc]) + yield unittest2.FunctionTestCase(lambda: None) + + suite = unittest2.TestSuite(tests()) + self.assertEqual(suite.countTestCases(), 2) + + ################################################################ + ### /Tests for TestSuite.__init__ + + # Container types should support the iter protocol + def test_iter(self): + test1 = unittest2.FunctionTestCase(lambda: None) + test2 = unittest2.FunctionTestCase(lambda: None) + suite = unittest2.TestSuite((test1, test2)) + + self.assertEqual(list(suite), [test1, test2]) + + # "Return the number of tests represented by the this test object. + # ...this method is also implemented by the TestSuite class, which can + # return larger [greater than 1] values" + # + # Presumably an empty TestSuite returns 0? + def test_countTestCases_zero_simple(self): + suite = unittest2.TestSuite() + + self.assertEqual(suite.countTestCases(), 0) + + # "Return the number of tests represented by the this test object. + # ...this method is also implemented by the TestSuite class, which can + # return larger [greater than 1] values" + # + # Presumably an empty TestSuite (even if it contains other empty + # TestSuite instances) returns 0? + def test_countTestCases_zero_nested(self): + class Test1(unittest2.TestCase): + def test(self): + pass + + suite = unittest2.TestSuite([unittest2.TestSuite()]) + + self.assertEqual(suite.countTestCases(), 0) + + # "Return the number of tests represented by the this test object. + # ...this method is also implemented by the TestSuite class, which can + # return larger [greater than 1] values" + def test_countTestCases_simple(self): + test1 = unittest2.FunctionTestCase(lambda: None) + test2 = unittest2.FunctionTestCase(lambda: None) + suite = unittest2.TestSuite((test1, test2)) + + self.assertEqual(suite.countTestCases(), 2) + + # "Return the number of tests represented by the this test object. + # ...this method is also implemented by the TestSuite class, which can + # return larger [greater than 1] values" + # + # Make sure this holds for nested TestSuite instances, too + def test_countTestCases_nested(self): + class Test1(unittest2.TestCase): + def test1(self): pass + def test2(self): pass + + test2 = unittest2.FunctionTestCase(lambda: None) + test3 = unittest2.FunctionTestCase(lambda: None) + child = unittest2.TestSuite((Test1('test2'), test2)) + parent = unittest2.TestSuite((test3, child, Test1('test1'))) + + self.assertEqual(parent.countTestCases(), 4) + + # "Run the tests associated with this suite, collecting the result into + # the test result object passed as result." + # + # And if there are no tests? What then? + def test_run__empty_suite(self): + events = [] + result = LoggingResult(events) + + suite = unittest2.TestSuite() + + suite.run(result) + + self.assertEqual(events, []) + + # "Note that unlike TestCase.run(), TestSuite.run() requires the + # "result object to be passed in." + def test_run__requires_result(self): + suite = unittest2.TestSuite() + + try: + suite.run() + except TypeError: + pass + else: + self.fail("Failed to raise TypeError") + + # "Run the tests associated with this suite, collecting the result into + # the test result object passed as result." + def test_run(self): + events = [] + result = LoggingResult(events) + + class LoggingCase(unittest2.TestCase): + def run(self, result): + events.append('run %s' % self._testMethodName) + + def test1(self): pass + def test2(self): pass + + tests = [LoggingCase('test1'), LoggingCase('test2')] + + unittest2.TestSuite(tests).run(result) + + self.assertEqual(events, ['run test1', 'run test2']) + + # "Add a TestCase ... to the suite" + def test_addTest__TestCase(self): + class Foo(unittest2.TestCase): + def test(self): pass + + test = Foo('test') + suite = unittest2.TestSuite() + + suite.addTest(test) + + self.assertEqual(suite.countTestCases(), 1) + self.assertEqual(list(suite), [test]) + + # "Add a ... TestSuite to the suite" + def test_addTest__TestSuite(self): + class Foo(unittest2.TestCase): + def test(self): pass + + suite_2 = unittest2.TestSuite([Foo('test')]) + + suite = unittest2.TestSuite() + suite.addTest(suite_2) + + self.assertEqual(suite.countTestCases(), 1) + self.assertEqual(list(suite), [suite_2]) + + # "Add all the tests from an iterable of TestCase and TestSuite + # instances to this test suite." + # + # "This is equivalent to iterating over tests, calling addTest() for + # each element" + def test_addTests(self): + class Foo(unittest2.TestCase): + def test_1(self): pass + def test_2(self): pass + + test_1 = Foo('test_1') + test_2 = Foo('test_2') + inner_suite = unittest2.TestSuite([test_2]) + + def gen(): + yield test_1 + yield test_2 + yield inner_suite + + suite_1 = unittest2.TestSuite() + suite_1.addTests(gen()) + + self.assertEqual(list(suite_1), list(gen())) + + # "This is equivalent to iterating over tests, calling addTest() for + # each element" + suite_2 = unittest2.TestSuite() + for t in gen(): + suite_2.addTest(t) + + self.assertEqual(suite_1, suite_2) + + # "Add all the tests from an iterable of TestCase and TestSuite + # instances to this test suite." + # + # What happens if it doesn't get an iterable? + def test_addTest__noniterable(self): + suite = unittest2.TestSuite() + + try: + suite.addTests(5) + except TypeError: + pass + else: + self.fail("Failed to raise TypeError") + + def test_addTest__noncallable(self): + suite = unittest2.TestSuite() + self.assertRaises(TypeError, suite.addTest, 5) + + def test_addTest__casesuiteclass(self): + suite = unittest2.TestSuite() + self.assertRaises(TypeError, suite.addTest, Test_TestSuite) + self.assertRaises(TypeError, suite.addTest, unittest2.TestSuite) + + def test_addTests__string(self): + suite = unittest2.TestSuite() + self.assertRaises(TypeError, suite.addTests, "foo") + + def test_function_in_suite(self): + def f(_): + pass + suite = unittest2.TestSuite() + suite.addTest(f) + + # when the bug is fixed this line will not crash + suite.run(unittest2.TestResult()) + + + def test_basetestsuite(self): + class Test(unittest2.TestCase): + wasSetUp = False + wasTornDown = False + @classmethod + def setUpClass(cls): + cls.wasSetUp = True + @classmethod + def tearDownClass(cls): + cls.wasTornDown = True + def testPass(self): + pass + def testFail(self): + fail + class Module(object): + wasSetUp = False + wasTornDown = False + @staticmethod + def setUpModule(): + Module.wasSetUp = True + @staticmethod + def tearDownModule(): + Module.wasTornDown = True + + Test.__module__ = 'Module' + sys.modules['Module'] = Module + self.addCleanup(sys.modules.pop, 'Module') + + suite = unittest2.BaseTestSuite() + suite.addTests([Test('testPass'), Test('testFail')]) + self.assertEqual(suite.countTestCases(), 2) + + result = unittest2.TestResult() + suite.run(result) + self.assertFalse(Module.wasSetUp) + self.assertFalse(Module.wasTornDown) + self.assertFalse(Test.wasSetUp) + self.assertFalse(Test.wasTornDown) + self.assertEqual(len(result.errors), 1) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 2) + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/test/test_unittest2_with.py b/third_party/Python/module/unittest2/unittest2/test/test_unittest2_with.py new file mode 100644 index 000000000000..ddb88ace82ae --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/test/test_unittest2_with.py @@ -0,0 +1,143 @@ +from __future__ import with_statement + +import unittest2 +from unittest2.test.support import OldTestResult, catch_warnings + +import warnings +# needed to enable the deprecation warnings +warnings.simplefilter('default') + +class TestWith(unittest2.TestCase): + """Tests that use the with statement live in this + module so that all other tests can be run with Python 2.4. + """ + + def testAssertRaisesExcValue(self): + class ExceptionMock(Exception): + pass + + def Stub(foo): + raise ExceptionMock(foo) + v = "particular value" + + ctx = self.assertRaises(ExceptionMock) + with ctx: + Stub(v) + e = ctx.exception + self.assertIsInstance(e, ExceptionMock) + self.assertEqual(e.args[0], v) + + + def test_assertRaises(self): + def _raise(e): + raise e + self.assertRaises(KeyError, _raise, KeyError) + self.assertRaises(KeyError, _raise, KeyError("key")) + try: + self.assertRaises(KeyError, lambda: None) + except self.failureException as e: + self.assertIn("KeyError not raised", e.args) + else: + self.fail("assertRaises() didn't fail") + try: + self.assertRaises(KeyError, _raise, ValueError) + except ValueError: + pass + else: + self.fail("assertRaises() didn't let exception pass through") + with self.assertRaises(KeyError) as cm: + try: + raise KeyError + except Exception as e: + raise + self.assertIs(cm.exception, e) + + with self.assertRaises(KeyError): + raise KeyError("key") + try: + with self.assertRaises(KeyError): + pass + except self.failureException as e: + self.assertIn("KeyError not raised", e.args) + else: + self.fail("assertRaises() didn't fail") + try: + with self.assertRaises(KeyError): + raise ValueError + except ValueError: + pass + else: + self.fail("assertRaises() didn't let exception pass through") + + def test_assert_dict_unicode_error(self): + with catch_warnings(record=True): + # This causes a UnicodeWarning due to its craziness + one = ''.join(chr(i) for i in range(255)) + # this used to cause a UnicodeDecodeError constructing the failure msg + with self.assertRaises(self.failureException): + self.assertDictContainsSubset({'foo': one}, {'foo': u'\uFFFD'}) + + def test_formatMessage_unicode_error(self): + with catch_warnings(record=True): + # This causes a UnicodeWarning due to its craziness + one = ''.join(chr(i) for i in range(255)) + # this used to cause a UnicodeDecodeError constructing msg + self._formatMessage(one, u'\uFFFD') + + def assertOldResultWarning(self, test, failures): + with catch_warnings(record=True) as log: + result = OldTestResult() + test.run(result) + self.assertEqual(len(result.failures), failures) + warning, = log + self.assertIs(warning.category, DeprecationWarning) + + def test_old_testresult(self): + class Test(unittest2.TestCase): + def testSkip(self): + self.skipTest('foobar') + @unittest2.expectedFailure + def testExpectedFail(self): + raise TypeError + @unittest2.expectedFailure + def testUnexpectedSuccess(self): + pass + + for test_name, should_pass in (('testSkip', True), + ('testExpectedFail', True), + ('testUnexpectedSuccess', False)): + test = Test(test_name) + self.assertOldResultWarning(test, int(not should_pass)) + + def test_old_testresult_setup(self): + class Test(unittest2.TestCase): + def setUp(self): + self.skipTest('no reason') + def testFoo(self): + pass + self.assertOldResultWarning(Test('testFoo'), 0) + + def test_old_testresult_class(self): + class Test(unittest2.TestCase): + def testFoo(self): + pass + Test = unittest2.skip('no reason')(Test) + self.assertOldResultWarning(Test('testFoo'), 0) + + def testPendingDeprecationMethodNames(self): + """Test fail* methods pending deprecation, they will warn in 3.2. + + Do not use these methods. They will go away in 3.3. + """ + with catch_warnings(record=True): + self.failIfEqual(3, 5) + self.failUnlessEqual(3, 3) + self.failUnlessAlmostEqual(2.0, 2.0) + self.failIfAlmostEqual(3.0, 5.0) + self.failUnless(True) + self.failUnlessRaises(TypeError, lambda _: 3.14 + u'spam') + self.failIf(False) + + +if __name__ == '__main__': + unittest2.main() diff --git a/third_party/Python/module/unittest2/unittest2/util.py b/third_party/Python/module/unittest2/unittest2/util.py new file mode 100644 index 000000000000..c45d008cc886 --- /dev/null +++ b/third_party/Python/module/unittest2/unittest2/util.py @@ -0,0 +1,99 @@ +"""Various utility functions.""" + +__unittest = True + + +_MAX_LENGTH = 80 +def safe_repr(obj, short=False): + try: + result = repr(obj) + except Exception: + result = object.__repr__(obj) + if not short or len(result) < _MAX_LENGTH: + return result + return result[:_MAX_LENGTH] + ' [truncated]...' + +def safe_str(obj): + try: + return str(obj) + except Exception: + return object.__str__(obj) + +def strclass(cls): + return "%s.%s" % (cls.__module__, cls.__name__) + +def sorted_list_difference(expected, actual): + """Finds elements in only one or the other of two, sorted input lists. + + Returns a two-element tuple of lists. The first list contains those + elements in the "expected" list but not in the "actual" list, and the + second contains those elements in the "actual" list but not in the + "expected" list. Duplicate elements in either input list are ignored. + """ + i = j = 0 + missing = [] + unexpected = [] + while True: + try: + e = expected[i] + a = actual[j] + if e < a: + missing.append(e) + i += 1 + while expected[i] == e: + i += 1 + elif e > a: + unexpected.append(a) + j += 1 + while actual[j] == a: + j += 1 + else: + i += 1 + try: + while expected[i] == e: + i += 1 + finally: + j += 1 + while actual[j] == a: + j += 1 + except IndexError: + missing.extend(expected[i:]) + unexpected.extend(actual[j:]) + break + return missing, unexpected + +def unorderable_list_difference(expected, actual, ignore_duplicate=False): + """Same behavior as sorted_list_difference but + for lists of unorderable items (like dicts). + + As it does a linear search per item (remove) it + has O(n*n) performance. + """ + missing = [] + unexpected = [] + while expected: + item = expected.pop() + try: + actual.remove(item) + except ValueError: + missing.append(item) + if ignore_duplicate: + for lst in expected, actual: + try: + while True: + lst.remove(item) + except ValueError: + pass + if ignore_duplicate: + while actual: + item = actual.pop() + unexpected.append(item) + try: + while True: + actual.remove(item) + except ValueError: + pass + return missing, unexpected + + # anything left in actual is unexpected + return missing, actual diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 000000000000..49af928c3815 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,12 @@ +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_subdirectory(darwin-debug) + add_subdirectory(debugserver) +endif() + add_subdirectory(argdumper) + add_subdirectory(driver) +if (NOT __ANDROID_NDK__) + add_subdirectory(lldb-mi) +endif() +if (LLDB_CAN_USE_LLDB_SERVER) + add_subdirectory(lldb-server) +endif() diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 000000000000..2588d87dc98a --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,30 @@ +##===- source/Makefile -------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := .. +include $(LLDB_LEVEL)/../../Makefile.config + +DIRS := + +# enable lldb-gdbserver for supported platforms +ifneq (,$(strip $(filter $(HOST_OS), FreeBSD Linux NetBSD GNU/kFreeBSD))) +DIRS += lldb-server +endif + +ifeq ($(HOST_OS),Darwin) +DIRS += debugserver +endif + +ifeq ($(ENABLE_WERROR),0) +DIRS += lldb-mi +endif + +DIRS += driver + +include $(LLDB_LEVEL)/Makefile diff --git a/tools/argdumper/CMakeLists.txt b/tools/argdumper/CMakeLists.txt new file mode 100644 index 000000000000..69bc97c7e518 --- /dev/null +++ b/tools/argdumper/CMakeLists.txt @@ -0,0 +1,8 @@ +add_lldb_executable(lldb-argdumper + argdumper.cpp + ) + +target_link_libraries(lldb-argdumper liblldb) + +install(TARGETS lldb-argdumper + RUNTIME DESTINATION bin) diff --git a/tools/darwin-debug/CMakeLists.txt b/tools/darwin-debug/CMakeLists.txt new file mode 100644 index 000000000000..352a573e25e1 --- /dev/null +++ b/tools/darwin-debug/CMakeLists.txt @@ -0,0 +1,6 @@ +add_lldb_executable(lldb-launcher + darwin-debug.cpp + ) + +install(TARGETS lldb-launcher + RUNTIME DESTINATION bin) diff --git a/tools/darwin-debug/darwin-debug.cpp b/tools/darwin-debug/darwin-debug.cpp new file mode 100644 index 000000000000..ca0a8d48328b --- /dev/null +++ b/tools/darwin-debug/darwin-debug.cpp @@ -0,0 +1,354 @@ +//===-- Launcher.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//---------------------------------------------------------------------- +// Darwin launch helper +// +// This program was written to allow programs to be launched in a new +// Terminal.app window and have the application be stopped for debugging +// at the program entry point. +// +// Although it uses posix_spawn(), it uses Darwin specific posix spawn +// attribute flags to accomplish its task. It uses an "exec only" flag +// which avoids forking this process, and it uses a "stop at entry" +// flag to stop the program at the entry point. +// +// Since it uses darwin specific flags this code should not be compiled +// on other systems. +//---------------------------------------------------------------------- +#if defined (__APPLE__) + +#include // for _NSGetEnviron() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +#endif + +#define streq(a,b) strcmp(a,b) == 0 + +static struct option g_long_options[] = +{ + { "arch", required_argument, NULL, 'a' }, + { "disable-aslr", no_argument, NULL, 'd' }, + { "no-env", no_argument, NULL, 'e' }, + { "help", no_argument, NULL, 'h' }, + { "setsid", no_argument, NULL, 's' }, + { "unix-socket", required_argument, NULL, 'u' }, + { "working-dir", required_argument, NULL, 'w' }, + { "env", required_argument, NULL, 'E' }, + { NULL, 0, NULL, 0 } +}; + +static void +usage() +{ + puts ( +"NAME\n" +" darwin-debug -- posix spawn a process that is stopped at the entry point\n" +" for debugging.\n" +"\n" +"SYNOPSIS\n" +" darwin-debug --unix-socket= [--arch=] [--working-dir=] [--disable-aslr] [--no-env] [--setsid] [--help] -- [ ....]\n" +"\n" +"DESCRIPTION\n" +" darwin-debug will exec itself into a child process that is\n" +" halted for debugging. It does this by using posix_spawn() along with\n" +" darwin specific posix_spawn flags that allows exec only (no fork), and\n" +" stop at the program entry point. Any program arguments are\n" +" passed on to the exec as the arguments for the new process. The current\n" +" environment will be passed to the new process unless the \"--no-env\"\n" +" option is used. A unix socket must be supplied using the\n" +" --unix-socket= option so the calling program can handshake with\n" +" this process and get its process id.\n" +"\n" +"EXAMPLE\n" +" darwin-debug --arch=i386 -- /bin/ls -al /tmp\n" +); + exit (1); +} + +static void +exit_with_errno (int err, const char *prefix) +{ + if (err) + { + fprintf (stderr, + "%s%s", + prefix ? prefix : "", + strerror(err)); + exit (err); + } +} + +pid_t +posix_spawn_for_debug +( + char *const *argv, + char *const *envp, + const char *working_dir, + cpu_type_t cpu_type, + int disable_aslr) +{ + pid_t pid = 0; + + const char *path = argv[0]; + + posix_spawnattr_t attr; + + exit_with_errno (::posix_spawnattr_init (&attr), "::posix_spawnattr_init (&attr) error: "); + + // Here we are using a darwin specific feature that allows us to exec only + // since we want this program to turn into the program we want to debug, + // and also have the new program start suspended (right at __dyld_start) + // so we can debug it + short flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETEXEC | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + + // Disable ASLR if we were asked to + if (disable_aslr) + flags |= _POSIX_SPAWN_DISABLE_ASLR; + + sigset_t no_signals; + sigset_t all_signals; + sigemptyset (&no_signals); + sigfillset (&all_signals); + ::posix_spawnattr_setsigmask(&attr, &no_signals); + ::posix_spawnattr_setsigdefault(&attr, &all_signals); + + // Set the flags we just made into our posix spawn attributes + exit_with_errno (::posix_spawnattr_setflags (&attr, flags), "::posix_spawnattr_setflags (&attr, flags) error: "); + + + // Another darwin specific thing here where we can select the architecture + // of the binary we want to re-exec as. + if (cpu_type != 0) + { + size_t ocount = 0; + exit_with_errno (::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), "posix_spawnattr_setbinpref_np () error: "); + } + + // I wish there was a posix_spawn flag to change the working directory of + // the inferior process we will spawn, but there currently isn't. If there + // ever is a better way to do this, we should use it. I would rather not + // manually fork, chdir in the child process, and then posix_spawn with exec + // as the whole reason for doing posix_spawn is to not hose anything up + // after the fork and prior to the exec... + if (working_dir) + ::chdir (working_dir); + + exit_with_errno (::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), "posix_spawn() error: "); + + // This code will only be reached if the posix_spawn exec failed... + ::posix_spawnattr_destroy (&attr); + + return pid; +} + + +int main (int argc, char *const *argv, char *const *envp, const char **apple) +{ +#if defined (DEBUG_LLDB_LAUNCHER) + const char *program_name = strrchr(apple[0], '/'); + + if (program_name) + program_name++; // Skip the last slash.. + else + program_name = apple[0]; + + printf("%s called with:\n", program_name); + for (int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h +#define CS_OPS_STATUS 0 /* return status */ +#define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */ +int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); + +/* Step through the process table, find a matching process name, return + the pid of that matched process. + If there are multiple processes with that name, issue a warning on stdout + and return the highest numbered process. + The proc_pidpath() call is used which gets the full process name including + directories to the executable and the full (longer than 16 character) + executable name. */ + +pid_t +get_pid_for_process_name (const char *procname) +{ + int process_count = proc_listpids (PROC_ALL_PIDS, 0, NULL, 0) / sizeof (pid_t); + if (process_count < 1) + { + printf ("Only found %d processes running!\n", process_count); + exit (1); + } + + // Allocate a few extra slots in case new processes are spawned + int all_pids_size = sizeof (pid_t) * (process_count + 3); + pid_t *all_pids = (pid_t *) malloc (all_pids_size); + + // re-set process_count in case the number of processes changed (got smaller; we won't do bigger) + process_count = proc_listpids (PROC_ALL_PIDS, 0, all_pids, all_pids_size) / sizeof (pid_t); + + int i; + pid_t highest_pid = 0; + int match_count = 0; + for (i = 1; i < process_count; i++) + { + char pidpath[PATH_MAX]; + int pidpath_len = proc_pidpath (all_pids[i], pidpath, sizeof (pidpath)); + if (pidpath_len == 0) + continue; + char *j = strrchr (pidpath, '/'); + if ((j == NULL && strcmp (procname, pidpath) == 0) + || (j != NULL && strcmp (j + 1, procname) == 0)) + { + match_count++; + if (all_pids[i] > highest_pid) + highest_pid = all_pids[i]; + } + } + free (all_pids); + + if (match_count == 0) + { + printf ("Did not find process '%s'.\n", procname); + exit (1); + } + if (match_count > 1) + { + printf ("Warning: More than one process '%s'!\n", procname); + printf (" defaulting to the highest-pid one, %d\n", highest_pid); + } + return highest_pid; +} + +/* Given a pid, get the full executable name (including directory + paths and the longer-than-16-chars executable name) and return + the basename of that (i.e. do not include the directory components). + This function mallocs the memory for the string it returns; + the caller must free this memory. */ + +const char * +get_process_name_for_pid (pid_t pid) +{ + char tmp_name[PATH_MAX]; + if (proc_pidpath (pid, tmp_name, sizeof (tmp_name)) == 0) + { + printf ("Could not find process with pid of %d\n", (int) pid); + exit (1); + } + if (strrchr (tmp_name, '/')) + return strdup (strrchr (tmp_name, '/') + 1); + else + return strdup (tmp_name); +} + +/* Get a struct kinfo_proc structure for a given pid. + Process name is required for error printing. + Gives you the current state of the process and whether it is being debugged by anyone. + memory is malloc()'ed for the returned struct kinfo_proc + and must be freed by the caller. */ + +struct kinfo_proc * +get_kinfo_proc_for_pid (pid_t pid, const char *process_name) +{ + struct kinfo_proc *kinfo = (struct kinfo_proc *) malloc (sizeof (struct kinfo_proc)); + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; + size_t len = sizeof (struct kinfo_proc); + if (sysctl (mib, sizeof (mib) / sizeof (mib[0]), kinfo, &len, NULL, 0) != 0) + { + free ((void *) kinfo); + printf ("Could not get kinfo_proc for pid %d\n", (int) pid); + exit (1); + } + return kinfo; +} + +/* Get the basic information (thread_basic_info_t) about a given + thread. + Gives you the suspend count; thread state; user time; system time; sleep time; etc. + The return value is a pointer to malloc'ed memory - it is the caller's + responsibility to free it. */ + +thread_basic_info_t +get_thread_basic_info (thread_t thread) +{ + kern_return_t kr; + integer_t *thinfo = (integer_t *) malloc (sizeof (integer_t) * THREAD_INFO_MAX); + mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX; + kr = thread_info (thread, THREAD_BASIC_INFO, + (thread_info_t) thinfo, &thread_info_count); + if (kr != KERN_SUCCESS) + { + printf ("Error - unable to get basic thread info for a thread\n"); + exit (1); + } + return (thread_basic_info_t) thinfo; +} + +/* Get the thread identifier info (thread_identifier_info_data_t) + about a given thread. + Gives you the system-wide unique thread number; the pthread identifier number +*/ + +thread_identifier_info_data_t +get_thread_identifier_info (thread_t thread) +{ + kern_return_t kr; + thread_identifier_info_data_t tident; + mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info (thread, THREAD_IDENTIFIER_INFO, + (thread_info_t) &tident, &tident_count); + if (kr != KERN_SUCCESS) + { + printf ("Error - unable to get thread ident for a thread\n"); + exit (1); + } + return tident; +} + + +/* Given a mach port # (in the examine-threads mach port namespace) for a thread, + find the mach port # in the inferior program's port namespace. + Sets inferior_port if successful. + Returns true if successful, false if unable to find the port number. */ + +bool +inferior_namespace_mach_port_num (task_t task, thread_t examine_threads_port, thread_t *inferior_port) +{ + kern_return_t retval; + mach_port_name_array_t names; + mach_msg_type_number_t nameslen; + mach_port_type_array_t types; + mach_msg_type_number_t typeslen; + + if (inferior_port == NULL) + return false; + + retval = mach_port_names (task, &names, &nameslen, &types, &typeslen); + if (retval != KERN_SUCCESS) + { + printf ("Error - unable to get mach port names for inferior.\n"); + return false; + } + int i = 0; + for (i = 0; i < nameslen; i++) + { + mach_port_t local_name; + mach_msg_type_name_t local_type; + retval = mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &local_name, &local_type); + if (retval == KERN_SUCCESS) + { + mach_port_deallocate (mach_task_self(), local_name); + if (local_name == examine_threads_port) + { + *inferior_port = names[i]; + vm_deallocate (mach_task_self (), (vm_address_t) names, nameslen * sizeof (mach_port_t)); + vm_deallocate (mach_task_self (), (vm_address_t) types, typeslen * sizeof (mach_port_t)); + return true; + } + } + } + vm_deallocate (mach_task_self (), (vm_address_t) names, nameslen * sizeof (mach_port_t)); + vm_deallocate (mach_task_self (), (vm_address_t) types, typeslen * sizeof (mach_port_t)); + return false; +} + +/* Get the current pc value for a given thread. */ + +uint64_t +get_current_pc (thread_t thread, int *wordsize) +{ + kern_return_t kr ; + +#if defined (__x86_64__) || defined (__i386__) + x86_thread_state_t gp_regs; + mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT; + kr = thread_get_state (thread, x86_THREAD_STATE, + (thread_state_t) &gp_regs, &gp_count); + if (kr != KERN_SUCCESS) + { + printf ("Error - unable to get registers for a thread\n"); + exit (1); + } + + if (gp_regs.tsh.flavor == x86_THREAD_STATE64) + { + *wordsize = 8; + return gp_regs.uts.ts64.__rip; + } + else + { + *wordsize = 4; + return gp_regs.uts.ts32.__eip; + } +#endif + +#if defined (__arm__) + arm_thread_state_t gp_regs; + mach_msg_type_number_t gp_count = ARM_THREAD_STATE_COUNT; + kr = thread_get_state (thread, ARM_THREAD_STATE, + (thread_state_t) &gp_regs, &gp_count); + if (kr != KERN_SUCCESS) + { + printf ("Error - unable to get registers for a thread\n"); + exit (1); + } + *wordsize = 4; + return gp_regs.__pc; +#endif + +#if defined (__arm64__) + arm_thread_state64_t gp_regs; + mach_msg_type_number_t gp_count = ARM_THREAD_STATE64_COUNT; + kr = thread_get_state (thread, ARM_THREAD_STATE64, + (thread_state_t) &gp_regs, &gp_count); + if (kr != KERN_SUCCESS) + { + printf ("Error - unable to get registers for a thread\n"); + exit (1); + } + *wordsize = 8; + return gp_regs.__pc; +#endif + +} + +/* Get the proc_threadinfo for a given thread. + Gives you the thread name, if set; current and max priorities. + Returns 1 if successful + Returns 0 if proc_pidinfo() failed +*/ + +int +get_proc_threadinfo (pid_t pid, uint64_t thread_handle, struct proc_threadinfo *pth) +{ + pth->pth_name[0] = '\0'; + int ret = proc_pidinfo (pid, PROC_PIDTHREADINFO, thread_handle, + pth, sizeof (struct proc_threadinfo)); + if (ret != 0) + return 1; + else + return 0; +} + +int +main (int argc, char **argv) +{ + kern_return_t kr; + task_t task; + pid_t pid = 0; + char *procname = NULL; + int arg_is_procname = 0; + int do_loop = 0; + int verbose = 0; + int resume_when_done = 0; + mach_port_t mytask = mach_task_self (); + + if (argc != 2 && argc != 3 && argc != 4 && argc != 5) + { + printf ("Usage: tdump [-l] [-v] [-r] pid/procname\n"); + exit (1); + } + + if (argc == 3 || argc == 4) + { + int i = 1; + while (i < argc - 1) + { + if (strcmp (argv[i], "-l") == 0) + do_loop = 1; + if (strcmp (argv[i], "-v") == 0) + verbose = 1; + if (strcmp (argv[i], "-r") == 0) + resume_when_done++; + i++; + } + } + + char *c = argv[argc - 1]; + if (*c == '\0') + { + printf ("Usage: tdump [-l] [-v] pid/procname\n"); + exit (1); + } + while (*c != '\0') + { + if (!isdigit (*c)) + { + arg_is_procname = 1; + procname = argv[argc - 1]; + break; + } + c++; + } + + if (arg_is_procname && procname) + { + pid = get_pid_for_process_name (procname); + } + else + { + errno = 0; + pid = (pid_t) strtol (argv[argc - 1], NULL, 10); + if (pid == 0 && errno == EINVAL) + { + printf ("Usage: tdump [-l] [-v] pid/procname\n"); + exit (1); + } + } + + const char *process_name = get_process_name_for_pid (pid); + + // At this point "pid" is the process id and "process_name" is the process name + // Now we have to get the process list from the kernel (which only has the truncated + // 16 char names) + + struct kinfo_proc *kinfo = get_kinfo_proc_for_pid (pid, process_name); + + printf ("pid %d (%s) is currently ", pid, process_name); + switch (kinfo->kp_proc.p_stat) { + case SIDL: printf ("being created by fork"); break; + case SRUN: printf ("runnable"); break; + case SSLEEP: printf ("sleeping on an address"); break; + case SSTOP: printf ("suspended"); break; + case SZOMB: printf ("zombie state - awaiting collection by parent"); break; + default: printf ("unknown"); + } + if (kinfo->kp_proc.p_flag & P_TRACED) + printf (" and is being debugged."); + free ((void *) kinfo); + + printf ("\n"); + + int csops_flags = 0; + if (csops (pid, CS_OPS_STATUS, &csops_flags, sizeof (csops_flags)) != -1 + && (csops_flags & CS_RESTRICT)) + { + printf ("pid %d (%s) is restricted so nothing can attach to it.\n", pid, process_name); + } + + kr = task_for_pid (mach_task_self (), pid, &task); + if (kr != KERN_SUCCESS) + { + printf ("Error - unable to task_for_pid()\n"); + exit (1); + } + + struct task_basic_info info; + unsigned int info_count = TASK_BASIC_INFO_COUNT; + + kr = task_info (task, TASK_BASIC_INFO, (task_info_t) &info, &info_count); + if (kr != KERN_SUCCESS) + { + printf ("Error - unable to call task_info.\n"); + exit (1); + } + printf ("Task suspend count: %d.\n", info.suspend_count); + + struct timespec *rqtp = (struct timespec *) malloc (sizeof (struct timespec)); + rqtp->tv_sec = 0; + rqtp->tv_nsec = 150000000; + + int loop_cnt = 1; + do + { + int i; + if (do_loop) + printf ("Iteration %d:\n", loop_cnt++); + thread_array_t thread_list; + mach_msg_type_number_t thread_count; + + kr = task_threads (task, &thread_list, &thread_count); + if (kr != KERN_SUCCESS) + { + printf ("Error - unable to get thread list\n"); + exit (1); + } + printf ("pid %d has %d threads\n", pid, thread_count); + if (verbose) + printf ("\n"); + + for (i = 0; i < thread_count; i++) + { + thread_basic_info_t basic_info = get_thread_basic_info (thread_list[i]); + + thread_identifier_info_data_t identifier_info = get_thread_identifier_info (thread_list[i]); + + int wordsize; + uint64_t pc = get_current_pc (thread_list[i], &wordsize); + + printf ("thread #%d, system-wide-unique-tid %lld, suspend count is %d, ", i, + identifier_info.thread_id, + basic_info->suspend_count); + if (wordsize == 8) + printf ("pc 0x%016llx, ", pc); + else + printf ("pc 0x%08llx, ", pc); + printf ("run state is "); + switch (basic_info->run_state) { + case TH_STATE_RUNNING: puts ("running"); break; + case TH_STATE_STOPPED: puts ("stopped"); break; + case TH_STATE_WAITING: puts ("waiting"); break; + case TH_STATE_UNINTERRUPTIBLE: puts ("uninterruptible"); break; + case TH_STATE_HALTED: puts ("halted"); break; + default: puts (""); + } + + printf (" pthread handle id 0x%llx (not the same value as pthread_self() returns)\n", (uint64_t) identifier_info.thread_handle); + + struct proc_threadinfo pth; + int proc_threadinfo_succeeded = get_proc_threadinfo (pid, identifier_info.thread_handle, &pth); + + if (proc_threadinfo_succeeded && pth.pth_name[0] != '\0') + printf (" thread name '%s'\n", pth.pth_name); + + printf (" libdispatch qaddr 0x%llx (not the same as the dispatch_queue_t token)\n", (uint64_t) identifier_info.dispatch_qaddr); + + if (verbose) + { + printf (" (examine-threads port namespace) mach port # 0x%4.4x\n", (int) thread_list[i]); + thread_t mach_port_inferior_namespace; + if (inferior_namespace_mach_port_num (task, thread_list[i], &mach_port_inferior_namespace)) + printf (" (inferior port namepsace) mach port # 0x%4.4x\n", (int) mach_port_inferior_namespace); + printf (" user %d.%06ds, system %d.%06ds", + basic_info->user_time.seconds, basic_info->user_time.microseconds, + basic_info->system_time.seconds, basic_info->system_time.microseconds); + if (basic_info->cpu_usage > 0) + { + float cpu_percentage = basic_info->cpu_usage / 10.0; + printf (", using %.1f%% cpu currently", cpu_percentage); + } + if (basic_info->sleep_time > 0) + printf (", this thread has slept for %d seconds", basic_info->sleep_time); + + printf ("\n "); + printf ("scheduling policy %d", basic_info->policy); + + if (basic_info->flags != 0) + { + printf (", flags %d", basic_info->flags); + if ((basic_info->flags | TH_FLAGS_SWAPPED) == TH_FLAGS_SWAPPED) + printf (" (thread is swapped out)"); + if ((basic_info->flags | TH_FLAGS_IDLE) == TH_FLAGS_IDLE) + printf (" (thread is idle)"); + } + if (proc_threadinfo_succeeded) + printf (", current pri %d, max pri %d", pth.pth_curpri, pth.pth_maxpriority); + + printf ("\n\n"); + } + + free ((void *) basic_info); + } + if (do_loop) + printf ("\n"); + vm_deallocate (mytask, (vm_address_t) thread_list, + thread_count * sizeof (thread_act_t)); + nanosleep (rqtp, NULL); + } while (do_loop); + + while (resume_when_done > 0) + { + kern_return_t err = task_resume (task); + if (err != KERN_SUCCESS) + printf ("Error resuming task: %d.", err); + resume_when_done--; + } + + vm_deallocate (mytask, (vm_address_t) task, sizeof (task_t)); + free ((void *) process_name); + + return 0; +} diff --git a/tools/debugserver/CMakeLists.txt b/tools/debugserver/CMakeLists.txt new file mode 100644 index 000000000000..d8414e5a2fe0 --- /dev/null +++ b/tools/debugserver/CMakeLists.txt @@ -0,0 +1,2 @@ +project(C CXX ASM-ATT) +add_subdirectory(source) diff --git a/tools/debugserver/Makefile b/tools/debugserver/Makefile new file mode 100644 index 000000000000..0284ea42f407 --- /dev/null +++ b/tools/debugserver/Makefile @@ -0,0 +1,13 @@ +##===- tools/debugserver/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LLDB_LEVEL := ../.. + +DIRS := source + +include $(LLDB_LEVEL)/Makefile diff --git a/tools/debugserver/debugnub-exports b/tools/debugserver/debugnub-exports new file mode 100644 index 000000000000..662bf9308a6f --- /dev/null +++ b/tools/debugserver/debugnub-exports @@ -0,0 +1,2 @@ +_DNB* +__DNB* diff --git a/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/tools/debugserver/debugserver.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..2f7a557bf0b9 --- /dev/null +++ b/tools/debugserver/debugserver.xcodeproj/project.pbxproj @@ -0,0 +1,1945 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; }; + 2660D9CE1192280900958FBD /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9CC1192280900958FBD /* StringExtractor.cpp */; }; + 266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.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.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.mm */; }; + 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.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */; }; + 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 */; settings = {ATTRIBUTES = (Required, ); }; }; + 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; }; + 456F67461AD46CE9002850C2 /* DNBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DE0C71334A0024798E /* DNBError.cpp */; }; + 456F67471AD46CE9002850C2 /* DNBThreadResumeActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */; }; + 456F67481AD46CE9002850C2 /* debugserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A02918114AB9240029C479 /* debugserver.cpp */; }; + 456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68F7E0D104EC800665A9E /* RNBContext.cpp */; }; + 456F674A1AD46CE9002850C2 /* RNBServices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EF8878A00D9C797C001831DA /* RNBServices.cpp */; }; + 456F674B1AD46CE9002850C2 /* RNBSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */; }; + 456F674C1AD46CE9002850C2 /* RNBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FD60D10574500665A9E /* RNBRemote.cpp */; }; + 456F674D1AD46CE9002850C2 /* dbgnub-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E80C71334A0024798E /* dbgnub-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + 456F674E1AD46CE9002850C2 /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EE0C71334A0024798E /* MachException.cpp */; }; + 456F674F1AD46CE9002850C2 /* MachProcess.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.mm */; }; + 456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F20C71334A0024798E /* MachThread.cpp */; }; + 456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F40C71334A0024798E /* MachThreadList.cpp */; }; + 456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F60C71334A0024798E /* MachVMMemory.cpp */; }; + 456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F80C71334A0024798E /* MachVMRegion.cpp */; }; + 456F67541AD46CE9002850C2 /* MachTask.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */; }; + 456F67551AD46CE9002850C2 /* DNB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D60C71334A0024798E /* DNB.cpp */; }; + 456F67561AD46CE9002850C2 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; }; + 456F67571AD46CE9002850C2 /* DNBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */; }; + 456F67581AD46CE9002850C2 /* DNBDataRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DB0C71334A0024798E /* DNBDataRef.cpp */; }; + 456F67591AD46CE9002850C2 /* DNBLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E00C71334A0024798E /* DNBLog.cpp */; }; + 456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */; }; + 456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FE0C71334A0024798E /* PThreadEvent.cpp */; }; + 456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */; }; + 456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C638010C71334A0024798E /* SysSignal.cpp */; }; + 456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */; }; + 456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */; }; + 456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */; }; + 456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */; }; + 456F67621AD46CE9002850C2 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; }; + 456F67631AD46CE9002850C2 /* CFData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */; }; + 456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; }; + 456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; }; + 456F67661AD46CE9002850C2 /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9CC1192280900958FBD /* StringExtractor.cpp */; }; + 456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; }; + 456F67681AD46CE9002850C2 /* HasAVX.s in Sources */ = {isa = PBXBuildFile; fileRef = 4971AE7113D10F4F00649E37 /* HasAVX.s */; }; + 456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */; }; + 456F676B1AD46CE9002850C2 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Required, ); }; }; + 4971AE7213D10F4F00649E37 /* HasAVX.s in Sources */ = {isa = PBXBuildFile; fileRef = 4971AE7113D10F4F00649E37 /* HasAVX.s */; }; + AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.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; }; + 26203D1C1641EFB200A662F7 /* com.apple.debugserver.applist.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.internal.plist; sourceTree = ""; }; + 26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.internal.plist; sourceTree = ""; }; + 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "debugserver-entitlements.plist"; sourceTree = ""; }; + 264D5D571293835600ED4C01 /* DNBArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArch.cpp; sourceTree = ""; }; + 264F679A1B2F9EB200140093 /* JSONGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSONGenerator.h; 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; }; + 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplARM64.cpp; sourceTree = ""; }; + 266B5ED01460A68200E43F0A /* DNBArchImplARM64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBArchImplARM64.h; sourceTree = ""; }; + 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 = ""; }; + 269E8DF8164B2ED200AD65F6 /* com.apple.debugserver.posix.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.posix.plist; 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 = ""; }; + 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.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MachTask.mm; 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; lineEnding = 0; path = DNBDefs.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 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 = ""; }; + 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.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = MachProcess.mm; 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 = ""; }; + 456F67721AD46CE9002850C2 /* debugserver */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = debugserver; sourceTree = BUILT_PRODUCTS_DIR; }; + 4971AE7013D10F4F00649E37 /* HasAVX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HasAVX.h; sourceTree = ""; }; + 4971AE7113D10F4F00649E37 /* HasAVX.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = HasAVX.s; sourceTree = ""; }; + 49F530111331519C008956F6 /* MachRegisterStatesI386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesI386.h; sourceTree = ""; }; + 49F5301213316D7F008956F6 /* MachRegisterStatesX86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesX86_64.h; sourceTree = ""; }; + 9457ECF61419864100DFE7D8 /* stack_logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stack_logging.h; sourceTree = ""; }; + AF0934BA18E12B92005A11FD /* Genealogy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Genealogy.h; sourceTree = ""; }; + AF0934BB18E12B92005A11FD /* GenealogySPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenealogySPI.h; sourceTree = ""; }; + AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "debugserver-macosx-entitlements.plist"; 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 = ""; }; + AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Genealogy.cpp; sourceTree = ""; }; + ED128B7918E1F163003F6A7B /* libpmenergy.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmenergy.dylib; path = usr/lib/libpmenergy.dylib; sourceTree = SDKROOT; }; + ED128B7A18E1F163003F6A7B /* libpmsample.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmsample.dylib; path = usr/lib/libpmsample.dylib; sourceTree = SDKROOT; }; + 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; + }; + 456F676A1AD46CE9002850C2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 456F676B1AD46CE9002850C2 /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* dbgnub */ = { + isa = PBXGroup; + children = ( + 26ACA3330D3E94F200A2120B /* Framework */, + 26C637D50C71334A0024798E /* source */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = dbgnub; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 26CE0594115C31C20022F371 /* debugserver */, + 456F67721AD46CE9002850C2 /* debugserver */, + ); + name = Products; + sourceTree = ""; + }; + 266B5ECE1460A68200E43F0A /* arm64 */ = { + isa = PBXGroup; + children = ( + 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */, + 266B5ED01460A68200E43F0A /* DNBArchImplARM64.h */, + ); + path = arm64; + sourceTree = ""; + }; + 2675D41C0CCEB6CF000F49AF /* arm */ = { + isa = PBXGroup; + children = ( + 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */, + 2675D4230CCEB705000F49AF /* DNBArchImpl.h */, + ); + name = arm; + sourceTree = ""; + }; + 26A028FE114AB6A60029C479 /* Resources */ = { + isa = PBXGroup; + children = ( + 269E8DF8164B2ED200AD65F6 /* com.apple.debugserver.posix.plist */, + 26203D1C1641EFB200A662F7 /* com.apple.debugserver.applist.internal.plist */, + 26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */, + 260FC7320E5B290400043FC9 /* debugnub-exports */, + 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */, + AF61C60418F75ABC00B48D9D /* debugserver-macosx-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 */, + 264D5D571293835600ED4C01 /* DNBArch.cpp */, + 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 */, + 264F679A1B2F9EB200140093 /* JSONGenerator.h */, + 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 = ""; + }; + 26ACA3330D3E94F200A2120B /* Framework */ = { + isa = PBXGroup; + children = ( + ED128B7918E1F163003F6A7B /* libpmenergy.dylib */, + ED128B7A18E1F163003F6A7B /* libpmsample.dylib */, + 26ACA3340D3E956300A2120B /* CoreFoundation.framework */, + ); + name = Framework; + sourceTree = ""; + }; + 26C637D50C71334A0024798E /* source */ = { + isa = PBXGroup; + children = ( + 26593A060D4931CC001C9FE3 /* ChangeLog */, + 26DEFD6C0D104C23008A5A07 /* debugserver */, + 26A028FF114AB6BB0029C479 /* libdebugnub */, + ); + indentWidth = 4; + path = source; + sourceTree = ""; + tabWidth = 4; + usesTabs = 0; + }; + 26C637E60C71334A0024798E /* MacOSX */ = { + isa = PBXGroup; + children = ( + AF0934BA18E12B92005A11FD /* Genealogy.h */, + AF0934BB18E12B92005A11FD /* GenealogySPI.h */, + 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */, + 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */, + 2695DE2D0D3EE55B007E4CA2 /* CFData.h */, + 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */, + 2695DD9A0D3EC160007E4CA2 /* CFString.h */, + 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */, + 26C637E70C71334A0024798E /* CFUtils.h */, + 2675D41C0CCEB6CF000F49AF /* arm */, + 266B5ECE1460A68200E43F0A /* arm64 */, + 26C637E90C71334A0024798E /* i386 */, + 26C637FA0C71334A0024798E /* ppc */, + 26CF99A11142EB7400011AAB /* x86_64 */, + 4971AE7013D10F4F00649E37 /* HasAVX.h */, + 4971AE7113D10F4F00649E37 /* HasAVX.s */, + 26C637E80C71334A0024798E /* dbgnub-mig.defs */, + AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */, + 26C637EF0C71334A0024798E /* MachException.h */, + 26C637EE0C71334A0024798E /* MachException.cpp */, + 26C637F10C71334A0024798E /* MachProcess.h */, + 26C637F00C71334A0024798E /* MachProcess.mm */, + 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.mm */, + 9457ECF61419864100DFE7D8 /* stack_logging.h */, + ); + path = MacOSX; + sourceTree = ""; + }; + 26C637E90C71334A0024798E /* i386 */ = { + isa = PBXGroup; + children = ( + 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */, + 26C637EB0C71334A0024798E /* DNBArchImplI386.h */, + 49F530111331519C008956F6 /* MachRegisterStatesI386.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 */, + 49F5301213316D7F008956F6 /* MachRegisterStatesX86_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 */, + 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 = ( + 26CE0591115C31C20022F371 /* Sources */, + 26CE0592115C31C20022F371 /* Frameworks */, + 4C3326CB18B2A2F600EB5DD7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = debugserver; + productName = "lldb-debugserver"; + productReference = 26CE0594115C31C20022F371 /* debugserver */; + productType = "com.apple.product-type.tool"; + }; + 456F67431AD46CE9002850C2 /* debugserver-mini */ = { + isa = PBXNativeTarget; + buildConfigurationList = 456F676D1AD46CE9002850C2 /* Build configuration list for PBXNativeTarget "debugserver-mini" */; + buildPhases = ( + 456F67451AD46CE9002850C2 /* Sources */, + 456F676A1AD46CE9002850C2 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "debugserver-mini"; + productName = "lldb-debugserver"; + productReference = 456F67721AD46CE9002850C2 /* debugserver */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0700; + LastUpgradeCheck = 0720; + }; + buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "debugserver" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 08FB7794FE84155DC02AAC07 /* dbgnub */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 26CE0593115C31C20022F371 /* debugserver */, + 456F67431AD46CE9002850C2 /* debugserver-mini */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4C3326CB18B2A2F600EB5DD7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/sh -x"; + shellScript = "if [ \"${CONFIGURATION}\" != BuildAndIntegration ]\nthen\n if [ -n \"${DEBUGSERVER_USE_FROM_SYSTEM}\" ]\n then\n\t\tditto \"${DEVELOPER_DIR}/../SharedFrameworks/LLDB.framework/Resources/debugserver\" \"${TARGET_BUILD_DIR}/${TARGET_NAME}\"\n elif [ \"${DEBUGSERVER_DISABLE_CODESIGN}\" == \"\" ]\n then\n codesign -f -s lldb_codesign \"${TARGET_BUILD_DIR}/${TARGET_NAME}\"\n fi\nfi\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.mm in Sources */, + 26CE05B2115C36360022F371 /* MachThread.cpp in Sources */, + 26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */, + 26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */, + 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */, + 26CE05B6115C36390022F371 /* MachTask.mm in Sources */, + 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */, + AFEC3364194A8B0B00FF05C6 /* Genealogy.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 */, + 264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */, + 4971AE7213D10F4F00649E37 /* HasAVX.s in Sources */, + 266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 456F67451AD46CE9002850C2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 456F67461AD46CE9002850C2 /* DNBError.cpp in Sources */, + 456F67471AD46CE9002850C2 /* DNBThreadResumeActions.cpp in Sources */, + 456F67481AD46CE9002850C2 /* debugserver.cpp in Sources */, + 456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */, + 456F674A1AD46CE9002850C2 /* RNBServices.cpp in Sources */, + 456F674B1AD46CE9002850C2 /* RNBSocket.cpp in Sources */, + 456F674C1AD46CE9002850C2 /* RNBRemote.cpp in Sources */, + 456F674D1AD46CE9002850C2 /* dbgnub-mig.defs in Sources */, + 456F674E1AD46CE9002850C2 /* MachException.cpp in Sources */, + 456F674F1AD46CE9002850C2 /* MachProcess.mm in Sources */, + 456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */, + 456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */, + 456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */, + 456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */, + 456F67541AD46CE9002850C2 /* MachTask.mm in Sources */, + 456F67551AD46CE9002850C2 /* DNB.cpp in Sources */, + 456F67561AD46CE9002850C2 /* Genealogy.cpp in Sources */, + 456F67571AD46CE9002850C2 /* DNBBreakpoint.cpp in Sources */, + 456F67581AD46CE9002850C2 /* DNBDataRef.cpp in Sources */, + 456F67591AD46CE9002850C2 /* DNBLog.cpp in Sources */, + 456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */, + 456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */, + 456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */, + 456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */, + 456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */, + 456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */, + 456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */, + 456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */, + 456F67621AD46CE9002850C2 /* CFString.cpp in Sources */, + 456F67631AD46CE9002850C2 /* CFData.cpp in Sources */, + 456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */, + 456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */, + 456F67661AD46CE9002850C2 /* StringExtractor.cpp in Sources */, + 456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */, + 456F67681AD46CE9002850C2 /* HasAVX.s in Sources */, + 456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB914F08733D8E0010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=iphoneos*]" = ( + arm64, + armv7, + ); + "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 350.99.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ""; + STRIP_INSTALLED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = Debug; + }; + 1DEB915008733D8E0010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=iphoneos*]" = ( + armv7, + armv7s, + ); + "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ""; + STRIPFLAGS = "-x"; + STRIP_STYLE = debugging; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = Release; + }; + 262419A11198A93E00067686 /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=iphoneos*]" = arm64; + "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + OTHER_CFLAGS = ""; + STRIPFLAGS = "-x"; + STRIP_STYLE = debugging; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = BuildAndIntegration; + }; + 262419A21198A93E00067686 /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_BUILDANDINTEGRATION; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_PATH = /usr/bin; + "INSTALL_PATH[sdk=iphoneos*]" = /Developer/usr/bin/; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx*]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx*]" = "-weak-lpmenergy -weak-lpmsample"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-framework", + Foundation, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + "SKIP_INSTALL[sdk=iphoneos*]" = NO; + STRIP_INSTALLED_PRODUCT = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = BuildAndIntegration; + }; + 26CE0596115C31C30022F371 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INSTALL_PATH = /usr/bin; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_BKS", + "-DWITH_FBS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-framework", + Foundation, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = Debug; + }; + 26CE0597115C31C30022F371 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_PATH = /usr/bin; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-framework", + Foundation, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = Release; + }; + 456F676E1AD46CE9002850C2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 350.99.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ""; + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + Foundation, + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx.internal; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + STRIP_INSTALLED_PRODUCT = NO; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + ZERO_LINK = NO; + }; + name = Debug; + }; + 456F676F1AD46CE9002850C2 /* DebugClang */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INSTALL_PATH = /usr/bin; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + Foundation, + "$(LLDB_COMPRESSION_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx.internal; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = DebugClang; + }; + 456F67701AD46CE9002850C2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_PATH = /usr/bin; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + Foundation, + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx.internal; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = Release; + }; + 456F67711AD46CE9002850C2 /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_BUILDANDINTEGRATION; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_PATH = /usr/bin; + "INSTALL_PATH[sdk=iphoneos*]" = /Developer/usr/bin/; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx*]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx*]" = "-weak-lpmenergy -weak-lpmsample"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + Foundation, + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + SDKROOT = macosx.internal; + SKIP_INSTALL = YES; + "SKIP_INSTALL[sdk=iphoneos*]" = NO; + STRIP_INSTALLED_PRODUCT = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = BuildAndIntegration; + }; + 4968B7A916657FAE00741ABB /* DebugClang */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=iphoneos*]" = ( + arm64, + armv7, + ); + "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 350.99.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx.internal; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + STRIP_INSTALLED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = DebugClang; + }; + 4968B7AA16657FAE00741ABB /* DebugClang */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INSTALL_PATH = /usr/bin; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-framework", + Foundation, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = DebugClang; + }; + 940AD5251B1FE3B10051E88F /* DebugPresubmission */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=iphoneos*]" = ( + arm64, + armv7, + ); + "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 350.99.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx.internal; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + STRIP_INSTALLED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = DebugPresubmission; + }; + 940AD5261B1FE3B10051E88F /* DebugPresubmission */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INSTALL_PATH = /usr/bin; + LLDB_COMPRESSION_CFLAGS = ""; + "LLDB_COMPRESSION_CFLAGS[sdk=appletvos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=iphoneos*]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1"; + "LLDB_COMPRESSION_CFLAGS[sdk=watchos*]" = "-DHAVE_LIBCOMPRESSION=1"; + LLDB_COMPRESSION_LDFLAGS = ""; + "LLDB_COMPRESSION_LDFLAGS[sdk=appletvos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=iphoneos*]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11internal]" = "-weak-lcompression"; + "LLDB_COMPRESSION_LDFLAGS[sdk=watchos*]" = "-weak-lcompression"; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-framework", + Foundation, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = DebugPresubmission; + }; + 940AD5271B1FE3B10051E88F /* DebugPresubmission */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INSTALL_PATH = /usr/bin; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DOS_OBJECT_USE_OBJC=0", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + Foundation, + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx.internal; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = DebugPresubmission; + }; + 94BA9B361B1A7C5700035A23 /* CustomSwift-Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "lldb-debugserver"; + }; + name = "CustomSwift-Debug"; + }; + 94BA9B371B1A7C5700035A23 /* CustomSwift-Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "lldb-debugserver"; + }; + name = "CustomSwift-Release"; + }; + 94D72C871ADF10AA00A3F718 /* CustomSwift-Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=iphoneos*]" = ( + arm64, + armv7, + ); + "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 350.99.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx.internal; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + STRIP_INSTALLED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = "CustomSwift-Debug"; + }; + 94D72C881ADF10AA00A3F718 /* CustomSwift-Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INSTALL_PATH = /usr/bin; + LLDB_COMPRESSION_CFLAGS = ""; + LLDB_COMPRESSION_LDFLAGS = ""; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_BKS", + "-DWITH_FBS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-framework", + Foundation, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = "CustomSwift-Debug"; + }; + 94D72C891ADF10B000A3F718 /* CustomSwift-Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=iphoneos*]" = ( + armv7, + armv7s, + ); + "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx.internal; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + STRIPFLAGS = "-x"; + STRIP_STYLE = debugging; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = "CustomSwift-Release"; + }; + 94D72C8A1ADF10B000A3F718 /* CustomSwift-Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 350.99.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = ( + "$(SDKROOT)/System/Library/PrivateFrameworks", + "$(SDKROOT)/Developer/Library/PrivateFrameworks", + ); + "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + INSTALL_PATH = /usr/bin; + LLDB_COMPRESSION_CFLAGS = ""; + LLDB_COMPRESSION_LDFLAGS = ""; + LLDB_DEBUGSERVER = 1; + LLDB_ENERGY_CFLAGS = ""; + "LLDB_ENERGY_CFLAGS[sdk=macosx.internal]" = "-DLLDB_ENERGY"; + LLDB_ENERGY_LFLAGS = ""; + "LLDB_ENERGY_LFLAGS[sdk=macosx.internal]" = "-weak-lpmenergy -weak-lpmsample"; + LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1"; + LLDB_ZLIB_LDFLAGS = "-lz"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = ( + "-Wparentheses", + "$(LLDB_ENERGY_CFLAGS)", + "-DDT_VARIANT_$(DT_VARIANT)", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wparentheses", + "-DWITH_LOCKDOWN", + "-DWITH_FBS", + "-DWITH_BKS", + "-DOS_OBJECT_USE_OBJC=0", + "$(LLDB_COMPRESSION_CFLAGS)", + "$(LLDB_ZLIB_CFLAGS)", + ); + "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)"; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-framework", + SpringBoardServices, + "-framework", + BackBoardServices, + "-framework", + Foundation, + "-llockdown", + "-framework", + FrontBoardServices, + "-framework", + MobileCoreServices, + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + "$(LLDB_ENERGY_LFLAGS)", + "$(LLDB_COMPRESSION_LDFLAGS)", + "$(LLDB_ZLIB_LDFLAGS)", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PRODUCT_NAME = debugserver; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=macosx*]" = ""; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=x86_64]" = macosx; + "SDKROOT[arch=x86_64h]" = macosx; + SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include"; + ZERO_LINK = NO; + }; + name = "CustomSwift-Release"; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "debugserver" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB914F08733D8E0010E9CD /* Debug */, + 94D72C871ADF10AA00A3F718 /* CustomSwift-Debug */, + 4968B7A916657FAE00741ABB /* DebugClang */, + 940AD5251B1FE3B10051E88F /* DebugPresubmission */, + 1DEB915008733D8E0010E9CD /* Release */, + 94D72C891ADF10B000A3F718 /* CustomSwift-Release */, + 262419A11198A93E00067686 /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; + 26CE05A4115C31ED0022F371 /* Build configuration list for PBXNativeTarget "debugserver" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 26CE0596115C31C30022F371 /* Debug */, + 94D72C881ADF10AA00A3F718 /* CustomSwift-Debug */, + 4968B7AA16657FAE00741ABB /* DebugClang */, + 940AD5261B1FE3B10051E88F /* DebugPresubmission */, + 26CE0597115C31C30022F371 /* Release */, + 94D72C8A1ADF10B000A3F718 /* CustomSwift-Release */, + 262419A21198A93E00067686 /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; + 456F676D1AD46CE9002850C2 /* Build configuration list for PBXNativeTarget "debugserver-mini" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 456F676E1AD46CE9002850C2 /* Debug */, + 456F676F1AD46CE9002850C2 /* DebugClang */, + 940AD5271B1FE3B10051E88F /* DebugPresubmission */, + 456F67701AD46CE9002850C2 /* Release */, + 456F67711AD46CE9002850C2 /* BuildAndIntegration */, + 94BA9B361B1A7C5700035A23 /* CustomSwift-Debug */, + 94BA9B371B1A7C5700035A23 /* CustomSwift-Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..c24931480d46 --- /dev/null +++ b/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme b/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme new file mode 100644 index 000000000000..2f27b5e4eff5 --- /dev/null +++ b/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/debugserver/resources/lldb-debugserver-Info.plist b/tools/debugserver/resources/lldb-debugserver-Info.plist new file mode 100644 index 000000000000..343325c2765c --- /dev/null +++ b/tools/debugserver/resources/lldb-debugserver-Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.debugserver + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + debugserver + CFBundleVersion + 2 + SecTaskAccess + + allowed + debug + + + diff --git a/tools/debugserver/scripts/diagnose-termination.d b/tools/debugserver/scripts/diagnose-termination.d new file mode 100644 index 000000000000..d216c9750033 --- /dev/null +++ b/tools/debugserver/scripts/diagnose-termination.d @@ -0,0 +1,18 @@ +fbt::exception_deliver:entry +{ + printf("pid %d got an exception of type %d\n", pid, arg1); + stack(); + ustack(); +} + +syscall::kill:entry +{ + printf("pid %d called kill(%d, %d)\n", pid, arg0, arg1); + ustack(); +} + +syscall::__pthread_kill:entry +{ + printf("pid %d called pthread_kill(%p, %d)\n", pid, arg0, arg1); + ustack(); +} diff --git a/tools/debugserver/source/ARM_DWARF_Registers.h b/tools/debugserver/source/ARM_DWARF_Registers.h new file mode 100644 index 000000000000..845260ba187c --- /dev/null +++ b/tools/debugserver/source/ARM_DWARF_Registers.h @@ -0,0 +1,209 @@ +//===-- 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 ARM_DWARF_Registers_h_ +#define 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, + + // Neon quadword registers + dwarf_q0 = 288, + dwarf_q1, + dwarf_q2, + dwarf_q3, + dwarf_q4, + dwarf_q5, + dwarf_q6, + dwarf_q7, + dwarf_q8, + dwarf_q9, + dwarf_q10, + dwarf_q11, + dwarf_q12, + dwarf_q13, + dwarf_q14, + dwarf_q15 +}; + +#endif // ARM_DWARF_Registers_h_ + diff --git a/tools/debugserver/source/ARM_ehframe_Registers.h b/tools/debugserver/source/ARM_ehframe_Registers.h new file mode 100644 index 000000000000..f6d93b3cee02 --- /dev/null +++ b/tools/debugserver/source/ARM_ehframe_Registers.h @@ -0,0 +1,35 @@ +//===-- ARM_ehframe_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_ehframe_Registers_h_ +#define utility_ARM_ehframe_Registers_h_ + +enum +{ + ehframe_r0 = 0, + ehframe_r1, + ehframe_r2, + ehframe_r3, + ehframe_r4, + ehframe_r5, + ehframe_r6, + ehframe_r7, + ehframe_r8, + ehframe_r9, + ehframe_r10, + ehframe_r11, + ehframe_r12, + ehframe_sp, + ehframe_lr, + ehframe_pc, + ehframe_cpsr +}; + +#endif // utility_ARM_ehframe_Registers_h_ + diff --git a/tools/debugserver/source/CMakeLists.txt b/tools/debugserver/source/CMakeLists.txt new file mode 100644 index 000000000000..94cef6c31203 --- /dev/null +++ b/tools/debugserver/source/CMakeLists.txt @@ -0,0 +1,62 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) +include_directories(${LLDB_SOURCE_DIR}/source) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + include_directories(MacOSX) + #include_directories(${CMAKE_CURRENT_BINARY_DIR}/MacOSX) + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/../resources/lldb-debugserver-Info.plist") +endif() + +check_cxx_compiler_flag("-Wno-gnu-zero-variadic-macro-arguments" + CXX_SUPPORTS_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS) +if (CXX_SUPPORTS_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments") +endif () + +check_cxx_compiler_flag("-Wno-zero-length-array" + CXX_SUPPORTS_NO_ZERO_LENGTH_ARRAY) +if (CXX_SUPPORTS_NO_ZERO_LENGTH_ARRAY) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-zero-length-array") +endif () + +check_cxx_compiler_flag("-Wno-extended-offsetof" + CXX_SUPPORTS_NO_EXTENDED_OFFSETOF) +if (CXX_SUPPORTS_NO_EXTENDED_OFFSETOF) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-extended-offsetof") +endif () + +if (NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_definitions( + -DDEBUGSERVER_VERSION_STR="${LLDB_VERSION}" + ) +endif () + +add_library(lldbDebugserverCommon + debugserver.cpp + DNBArch.cpp + DNBBreakpoint.cpp + DNB.cpp + DNBDataRef.cpp + DNBError.cpp + DNBLog.cpp + DNBRegisterInfo.cpp + DNBThreadResumeActions.cpp + libdebugserver.cpp + PseudoTerminal.cpp + PThreadEvent.cpp + PThreadMutex.cpp + RNBContext.cpp + RNBRemote.cpp + RNBServices.cpp + RNBSocket.cpp + SysSignal.cpp + TTYState.cpp + ) + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + find_library(COCOA_LIBRARY Cocoa) + target_link_libraries(lldbDebugserverCommon ${COCOA_LIBRARY}) + add_subdirectory(MacOSX) +endif() + diff --git a/tools/debugserver/source/ChangeLog b/tools/debugserver/source/ChangeLog new file mode 100644 index 000000000000..898f2fba7b04 --- /dev/null +++ b/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 function 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 abstracted 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, resume, 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 exception 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 comparisons 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 register 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/tools/debugserver/source/DNB.cpp b/tools/debugserver/source/DNB.cpp new file mode 100644 index 000000000000..03c85df441de --- /dev/null +++ b/tools/debugserver/source/DNB.cpp @@ -0,0 +1,1979 @@ +//===-- 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 +#include + +#if defined (__APPLE__) +#include +#include +#endif + +#define TRY_KQUEUE 1 + +#ifdef TRY_KQUEUE + #include + #include + #ifdef NOTE_EXIT_DETAIL + #define USE_KQUEUE + #endif +#endif + +#include "MacOSX/MachProcess.h" +#include "MacOSX/MachTask.h" +#include "MacOSX/Genealogy.h" +#include "MacOSX/ThreadInfo.h" +#include "CFString.h" +#include "DNBLog.h" +#include "DNBDataRef.h" +#include "DNBThreadResumeActions.h" +#include "DNBTimer.h" +#include "CFBundle.h" + + +typedef std::shared_ptr MachProcessSP; +typedef std::map ProcessMap; +typedef ProcessMap::iterator ProcessMapIter; +typedef ProcessMap::const_iterator ProcessMapConstIter; + +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; +} + +#ifdef USE_KQUEUE +void * +kqueue_thread (void *arg) +{ + int kq_id = (int) (intptr_t) arg; + +#if defined (__APPLE__) + pthread_setname_np ("kqueue thread"); +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + struct sched_param thread_param; + int thread_sched_policy; + if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0) + { + thread_param.sched_priority = 47; + pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); + } +#endif +#endif + + struct kevent death_event; + while (1) + { + int n_events = kevent (kq_id, NULL, 0, &death_event, 1, NULL); + if (n_events == -1) + { + if (errno == EINTR) + continue; + else + { + DNBLogError ("kqueue failed with error: (%d): %s", errno, strerror(errno)); + return NULL; + } + } + else if (death_event.flags & EV_ERROR) + { + int error_no = static_cast(death_event.data); + const char *error_str = strerror(error_no); + if (error_str == NULL) + error_str = "Unknown error"; + DNBLogError ("Failed to initialize kqueue event: (%d): %s", error_no, error_str ); + return NULL; + } + else + { + int status; + const pid_t pid = (pid_t)death_event.ident; + const pid_t child_pid = waitpid (pid, &status, 0); + + + bool exited = false; + int signal = 0; + int exit_status = 0; + if (WIFSTOPPED(status)) + { + signal = WSTOPSIG(status); + DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> STOPPED (signal = %i)", child_pid, signal); + } + else if (WIFEXITED(status)) + { + exit_status = WEXITSTATUS(status); + exited = true; + DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> EXITED (status = %i)", child_pid, exit_status); + } + else if (WIFSIGNALED(status)) + { + signal = WTERMSIG(status); + if (child_pid == abs(pid)) + { + DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> SIGNALED and EXITED (signal = %i)", child_pid, signal); + char exit_info[64]; + ::snprintf (exit_info, sizeof(exit_info), "Terminated due to signal %i", signal); + DNBProcessSetExitInfo (child_pid, exit_info); + exited = true; + exit_status = INT8_MAX; + } + else + { + DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> SIGNALED (signal = %i)", child_pid, signal); + } + } + + if (exited) + { + if (death_event.data & NOTE_EXIT_MEMORY) + DNBProcessSetExitInfo (child_pid, "Terminated due to memory issue"); + else if (death_event.data & NOTE_EXIT_DECRYPTFAIL) + DNBProcessSetExitInfo (child_pid, "Terminated due to decrypt failure"); + else if (death_event.data & NOTE_EXIT_CSERROR) + DNBProcessSetExitInfo (child_pid, "Terminated due to code signing error"); + + DNBLogThreadedIf(LOG_PROCESS, "waitpid_process_thread (): setting exit status for pid = %i to %i", child_pid, exit_status); + DNBProcessSetExitStatus (child_pid, status); + return NULL; + } + } + } +} + +static bool +spawn_kqueue_thread (pid_t pid) +{ + pthread_t thread; + int kq_id; + + kq_id = kqueue(); + if (kq_id == -1) + { + DNBLogError ("Could not get kqueue for pid = %i.", pid); + return false; + } + + struct kevent reg_event; + + EV_SET(®_event, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT|NOTE_EXITSTATUS|NOTE_EXIT_DETAIL, 0, NULL); + // Register the event: + int result = kevent (kq_id, ®_event, 1, NULL, 0, NULL); + if (result != 0) + { + DNBLogError ("Failed to register kqueue NOTE_EXIT event for pid %i, error: %d.", pid, result); + return false; + } + + int ret = ::pthread_create (&thread, NULL, kqueue_thread, (void *)(intptr_t)kq_id); + + // pthread_create returns 0 if successful + if (ret == 0) + { + ::pthread_detach (thread); + return true; + } + return false; +} +#endif // #if USE_KQUEUE + +static void * +waitpid_thread (void *arg) +{ + const pid_t pid = (pid_t)(intptr_t)arg; + int status; + +#if defined (__APPLE__) + pthread_setname_np ("waitpid thread"); +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + struct sched_param thread_param; + int thread_sched_policy; + if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0) + { + thread_param.sched_priority = 47; + pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); + } +#endif +#endif + + while (1) + { + pid_t child_pid = waitpid(pid, &status, 0); + DNBLogThreadedIf(LOG_PROCESS, "waitpid_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_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_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) +{ +#ifdef USE_KQUEUE + bool success = spawn_kqueue_thread (pid); + if (success) + return true; +#endif + + pthread_t thread; + int ret = ::pthread_create (&thread, NULL, waitpid_thread, (void *)(intptr_t)pid); + // pthread_create returns 0 if successful + if (ret == 0) + { + ::pthread_detach (thread); + return true; + } + return false; +} + +nub_process_t +DNBProcessLaunch (const char *path, + char const *argv[], + const char *envp[], + const char *working_directory, // NULL => don't change, non-NULL => set working directory for inferior to this + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + bool no_stdio, + nub_launch_flavor_t launch_flavor, + int disable_aslr, + const char *event_data, + char *err_str, + size_t err_len) +{ + DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv = %p, envp = %p, working_dir=%s, stdin=%s, stdout=%s, stderr=%s, no-stdio=%i, launch_flavor = %u, disable_aslr = %d, err = %p, err_len = %llu) called...", + __FUNCTION__, + path, + argv, + envp, + working_directory, + stdin_path, + stdout_path, + stderr_path, + no_stdio, + launch_flavor, + disable_aslr, + err_str, + (uint64_t)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, + working_directory, + stdin_path, + stdout_path, + stderr_path, + no_stdio, + launch_flavor, + disable_aslr, + event_data, + 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. + // Kill our process otherwise it will be stopped at the entry + // point and get reparented to someone else and never go away. + DNBLog ("Could not get task port for process, sending SIGKILL and exiting."); + kill (SIGKILL, pid); + + 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 + { + bool res = AddProcessToMap(pid, processSP); + UNUSED_IF_ASSERT_DISABLED(res); + assert(res && "Couldn't add process to map!"); + return pid; + } + } + } + return INVALID_NUB_PROCESS; +} + +// If there is one process with a given name, return the pid for that process. +nub_process_t +DNBProcessGetPIDByName (const char *name) +{ + std::vector matching_proc_infos; + size_t num_matching_proc_infos = GetAllInfosMatchingName(name, matching_proc_infos); + if (num_matching_proc_infos == 1) + { + return matching_proc_infos[0].kp_proc.p_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: %llu processes match '%s':\n", (uint64_t)num_matching_proc_infos, name); + size_t i; + for (i=0; i 0) + err_str[0] = '\0'; + + pid_t pid = INVALID_NUB_PROCESS; + 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) + { + bool res = AddProcessToMap(pid, processSP); + UNUSED_IF_ASSERT_DISABLED(res); + assert(res && "Couldn't add process to map!"); + 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; +} + +size_t +GetAllInfos (std::vector& proc_infos) +{ + size_t size = 0; + 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++; + + const size_t process_name_len = strlen(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 MAXCOMLEN) + { + // We found a matching process name whose first MAXCOMLEN + // characters match, but there is more to the name than + // this. We need to get the full process name. Use proc_pidpath, which will get + // us the full path to the executed process. + + char proc_path_buf[PATH_MAX]; + + int return_val = proc_pidpath (proc_infos[i].kp_proc.p_pid, proc_path_buf, PATH_MAX); + if (return_val > 0) + { + // Okay, now search backwards from that to see if there is a + // slash in the name. Note, even though we got all the args we don't care + // because the list data is just a bunch of concatenated null terminated strings + // so strrchr will start from the end of argv0. + + const char *argv_basename = strrchr(proc_path_buf, '/'); + if (argv_basename) + { + // Skip the '/' + ++argv_basename; + } + else + { + // We didn't find a directory delimiter in the process argv[0], just use what was in there + argv_basename = proc_path_buf; + } + + if (argv_basename) + { + if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0) + { + matching_proc_infos.push_back(proc_infos[i]); + } + } + } + } + else + { + // We found a matching process, add it to our list + matching_proc_infos.push_back(proc_infos[i]); + } + } + } + } + } + // return the newly added matches. + return matching_proc_infos.size(); +} + +nub_process_t +DNBProcessAttachWait (const char *waitfor_process_name, + nub_launch_flavor_t launch_flavor, + bool ignore_existing, + struct timespec *timeout_abstime, + useconds_t waitfor_interval, + char *err_str, + size_t err_len, + DNBShouldCancelCallback should_cancel_callback, + void *callback_data) +{ + DNBError prepare_error; + std::vector 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) + { + if (ignore_existing) + num_exclude_proc_infos = GetAllInfosMatchingName (waitfor_process_name, exclude_proc_infos); + else + num_exclude_proc_infos = 0; + } + + 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, launch_flavor); + 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, launch_flavor, success, prepare_error); + + return waitfor_pid; +} + +nub_bool_t +DNBProcessDetach (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + const bool remove = true; + DNBLogThreaded("Disabling breakpoints and watchpoints, and detaching from %d.", pid); + procSP->DisableAllBreakpoints(remove); + procSP->DisableAllWatchpoints (remove); + 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 +DNBProcessInterrupt(nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->Interrupt(); + return false; +} + +nub_bool_t +DNBProcessSendEvent (nub_process_t pid, const char *event) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + // FIXME: Do something with the error... + DNBError send_error; + return procSP->SendEvent (event, send_error); + } + 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 * +DNBProcessGetExitInfo (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetExitInfo(); + } + return NULL; +} + +nub_bool_t +DNBProcessSetExitInfo (nub_process_t pid, const char *info) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + procSP->SetExitInfo(info); + 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 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"; + } + return "nub_state_t ???"; +} + +Genealogy::ThreadActivitySP +DNBGetGenealogyInfoForThread (nub_process_t pid, nub_thread_t tid, bool &timed_out) +{ + Genealogy::ThreadActivitySP thread_activity_sp; + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + thread_activity_sp = procSP->GetGenealogyInfoForThread (tid, timed_out); + return thread_activity_sp; +} + +Genealogy::ProcessExecutableInfoSP +DNBGetGenealogyImageInfo (nub_process_t pid, size_t idx) +{ + Genealogy::ProcessExecutableInfoSP image_info_sp; + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + image_info_sp = procSP->GetGenealogyImageInfo (idx); + } + return image_info_sp; +} + +ThreadInfo::QoS +DNBGetRequestedQoSForThread (nub_process_t pid, nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetRequestedQoS (tid, tsd, dti_qos_class_index); + } + return ThreadInfo::QoS(); +} + +nub_addr_t +DNBGetPThreadT (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetPThreadT (tid); + } + return INVALID_NUB_ADDRESS; +} + +nub_addr_t +DNBGetDispatchQueueT (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetDispatchQueueT (tid); + } + return INVALID_NUB_ADDRESS; +} + +nub_addr_t +DNBGetTSDAddressForThread (nub_process_t pid, nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); + } + return INVALID_NUB_ADDRESS; +} + +JSONGenerator::ObjectSP +DNBGetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetLoadedDynamicLibrariesInfos (pid, image_list_address, image_count); + } + return JSONGenerator::ObjectSP(); +} + + + +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); +} + +// Breakpoints +nub_bool_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) != NULL; + return false; +} + +nub_bool_t +DNBBreakpointClear (nub_process_t pid, nub_addr_t addr) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->DisableBreakpoint(addr, true); + return false; // Failed +} + + +//---------------------------------------------------------------------- +// Watchpoints +//---------------------------------------------------------------------- +nub_bool_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) != NULL; + return false; +} + +nub_bool_t +DNBWatchpointClear (nub_process_t pid, nub_addr_t addr) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->DisableWatchpoint(addr, true); + return false; // Failed +} + +//---------------------------------------------------------------------- +// Return the number of supported hardware watchpoints. +//---------------------------------------------------------------------- +uint32_t +DNBWatchpointGetNumSupportedHWP (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetNumSupportedHardwareWatchpoints(); + return 0; +} + +//---------------------------------------------------------------------- +// 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; +} + +uint64_t +DNBProcessMemoryReadInteger (nub_process_t pid, nub_addr_t addr, nub_size_t integer_size, uint64_t fail_value) +{ + union Integers + { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + }; + + if (integer_size <= sizeof(uint64_t)) + { + Integers ints; + if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size) + { + switch (integer_size) + { + case 1: return ints.u8; + case 2: return ints.u16; + case 3: return ints.u32 & 0xffffffu; + case 4: return ints.u32; + case 5: return ints.u32 & 0x000000ffffffffffull; + case 6: return ints.u32 & 0x0000ffffffffffffull; + case 7: return ints.u32 & 0x00ffffffffffffffull; + case 8: return ints.u64; + } + } + } + return fail_value; + +} + +nub_addr_t +DNBProcessMemoryReadPointer (nub_process_t pid, nub_addr_t addr) +{ + cpu_type_t cputype = DNBProcessGetCPUType (pid); + if (cputype) + { + const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4; + return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0); + } + return 0; + +} + +std::string +DNBProcessMemoryReadCString (nub_process_t pid, nub_addr_t addr) +{ + std::string cstr; + char buffer[256]; + const nub_size_t max_buffer_cstr_length = sizeof(buffer)-1; + buffer[max_buffer_cstr_length] = '\0'; + nub_size_t length = 0; + nub_addr_t curr_addr = addr; + do + { + nub_size_t bytes_read = DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer); + if (bytes_read == 0) + break; + length = strlen(buffer); + cstr.append(buffer, length); + curr_addr += length; + } while (length == max_buffer_cstr_length); + return cstr; +} + +std::string +DNBProcessMemoryReadCStringFixed (nub_process_t pid, nub_addr_t addr, nub_size_t fixed_length) +{ + std::string cstr; + char buffer[fixed_length+1]; + buffer[fixed_length] = '\0'; + nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer); + if (bytes_read > 0) + cstr.assign(buffer); + return cstr; +} + + +//---------------------------------------------------------------------- +// 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; +} + +//---------------------------------------------------------------------- +// Find attributes of the memory region that contains ADDR for process PID, +// if possible, and return a string describing those attributes. +// +// Returns 1 if we could find attributes for this region and OUTBUF can +// be sent to the remote debugger. +// +// Returns 0 if we couldn't find the attributes for a region of memory at +// that address and OUTBUF should not be sent. +// +// Returns -1 if this platform cannot look up information about memory regions +// or if we do not yet have a valid launched process. +// +//---------------------------------------------------------------------- +int +DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->Task().GetMemoryRegionInfo (addr, region_info); + + return -1; +} + +std::string +DNBProcessGetProfileData (nub_process_t pid, DNBProfileDataScanType scanType) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->Task().GetProfileData(scanType); + + return std::string(""); +} + +nub_bool_t +DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type); + return true; + } + + return false; +} + +//---------------------------------------------------------------------- +// 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; +} + +//---------------------------------------------------------------------- +// Get the mach port number of the current thread. +//---------------------------------------------------------------------- +nub_thread_t +DNBProcessGetCurrentThreadMachPort (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetCurrentThreadMachPort(); + 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; +} + +//---------------------------------------------------------------------- +// Do whatever is needed to sync the thread's register state with it's kernel values. +//---------------------------------------------------------------------- +nub_bool_t +DNBProcessSyncThreadState (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->SyncThreadState (tid); + return false; + +} + +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; +} + +uint32_t +DNBGetRegisterCPUType() +{ + return DNBArchProtocol::GetRegisterCPUType (); + +} +//---------------------------------------------------------------------- +// Get the register set information for a specific thread. +//---------------------------------------------------------------------- +const DNBRegisterSetInfo * +DNBGetRegisterSetInfo (nub_size_t *num_reg_sets) +{ + return DNBArchProtocol::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; +} + +uint32_t +DNBThreadSaveRegisterState (nub_process_t pid, nub_thread_t tid) +{ + if (tid != INVALID_NUB_THREAD) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetThreadList().SaveRegisterState (tid); + } + return 0; +} +nub_bool_t +DNBThreadRestoreRegisterState (nub_process_t pid, nub_thread_t tid, uint32_t save_id) +{ + if (tid != INVALID_NUB_THREAD) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetThreadList().RestoreRegisterState (tid, save_id); + } + return false; +} + + + +//---------------------------------------------------------------------- +// 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 +DNBProcessGetAvailableProfileData (nub_process_t pid, char *buf, nub_size_t buf_size) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetAsyncProfileData (buf, buf_size); + return 0; +} + +nub_size_t +DNBProcessGetStopCount (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->StopCount(); + return 0; +} + +uint32_t +DNBProcessGetCPUType (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetCPUType (); + 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; + + struct stat path_stat; + if (::stat(path, &path_stat) == 0) + { + if ((path_stat.st_mode & S_IFMT) == S_IFDIR) + { + CFBundle bundle (path); + CFReleaser url(bundle.CopyExecutableURL ()); + if (url.get()) + { + if (::CFURLGetFileSystemRepresentation (url.get(), true, (UInt8*)resolved_path, resolved_path_size)) + return true; + } + } + } + + if (realpath(path, max_path)) + { + // Found the path relatively... + ::strncpy(resolved_path, max_path, resolved_path_size); + return strlen(resolved_path) + 1 < resolved_path_size; + } + else + { + // Not a relative path, check the PATH environment variable if the + const char *PATH = getenv("PATH"); + if (PATH) + { + const char *curr_path_start = PATH; + const char *curr_path_end; + while (curr_path_start && *curr_path_start) + { + curr_path_end = strchr(curr_path_start, ':'); + if (curr_path_end == NULL) + { + result.assign(curr_path_start); + curr_path_start = NULL; + } + else if (curr_path_end > curr_path_start) + { + size_t len = curr_path_end - curr_path_start; + result.assign(curr_path_start, len); + curr_path_start += len + 1; + } + else + break; + + result += '/'; + result += path; + struct stat s; + if (stat(result.c_str(), &s) == 0) + { + ::strncpy(resolved_path, result.c_str(), resolved_path_size); + return result.size() + 1 < resolved_path_size; + } + } + } + } + return false; +} + +bool +DNBGetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch) +{ + return MachProcess::GetOSVersionNumbers (major, minor, patch); +} + + +void +DNBInitialize() +{ + DNBLogThreadedIf (LOG_PROCESS, "DNBInitialize ()"); +#if defined (__i386__) || defined (__x86_64__) + DNBArchImplI386::Initialize(); + DNBArchImplX86_64::Initialize(); +#elif defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + DNBArchMachARM::Initialize(); + DNBArchMachARM64::Initialize(); +#endif +} + +void +DNBTerminate() +{ +} + +nub_bool_t +DNBSetArchitecture (const char *arch) +{ + if (arch && arch[0]) + { + if (strcasecmp (arch, "i386") == 0) + return DNBArchProtocol::SetArchitecture (CPU_TYPE_I386); + else if ((strcasecmp (arch, "x86_64") == 0) || (strcasecmp (arch, "x86_64h") == 0)) + return DNBArchProtocol::SetArchitecture (CPU_TYPE_X86_64); + else if (strstr (arch, "arm64") == arch || strstr (arch, "armv8") == arch || strstr (arch, "aarch64") == arch) + return DNBArchProtocol::SetArchitecture (CPU_TYPE_ARM64); + else if (strstr (arch, "arm") == arch) + return DNBArchProtocol::SetArchitecture (CPU_TYPE_ARM); + } + return false; +} diff --git a/tools/debugserver/source/DNB.h b/tools/debugserver/source/DNB.h new file mode 100644 index 000000000000..7b186d38b32a --- /dev/null +++ b/tools/debugserver/source/DNB.h @@ -0,0 +1,173 @@ +//===-- 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 "MacOSX/Genealogy.h" +#include "MacOSX/ThreadInfo.h" +#include "JSONGenerator.h" +#include "DNBDefs.h" +#include +#include + +#define DNB_EXPORT __attribute__((visibility("default"))) + +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 ((cpu_type_t) 12 | 0x01000000) +#endif + +typedef bool (*DNBShouldCancelCallback) (void *); + +void DNBInitialize (); +void DNBTerminate (); + +nub_bool_t DNBSetArchitecture (const char *arch); + +//---------------------------------------------------------------------- +// Process control +//---------------------------------------------------------------------- +nub_process_t DNBProcessLaunch (const char *path, + char const *argv[], + const char *envp[], + const char *working_directory, // NULL => don't change, non-NULL => set working directory for inferior to this + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + bool no_stdio, + nub_launch_flavor_t launch_flavor, + int disable_aslr, + const char *event_data, + char *err_str, + size_t err_len); + +nub_process_t DNBProcessGetPIDByName (const char *name); +nub_process_t DNBProcessAttach (nub_process_t pid, struct timespec *timeout, char *err_str, size_t err_len); +nub_process_t DNBProcessAttachByName (const char *name, struct timespec *timeout, char *err_str, size_t err_len); +nub_process_t DNBProcessAttachWait (const char *wait_name, nub_launch_flavor_t launch_flavor, bool ignore_existing, struct timespec *timeout, useconds_t interval, char *err_str, size_t err_len, DNBShouldCancelCallback should_cancel = NULL, void *callback_data = NULL); +// 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 DNBProcessInterrupt (nub_process_t pid) DNB_EXPORT; +nub_bool_t DNBProcessKill (nub_process_t pid) DNB_EXPORT; +nub_bool_t DNBProcessSendEvent (nub_process_t pid, const char *event) DNB_EXPORT; +nub_size_t DNBProcessMemoryRead (nub_process_t pid, nub_addr_t addr, nub_size_t size, void *buf) DNB_EXPORT; +uint64_t DNBProcessMemoryReadInteger (nub_process_t pid, nub_addr_t addr, nub_size_t integer_size, uint64_t fail_value) DNB_EXPORT; +nub_addr_t DNBProcessMemoryReadPointer (nub_process_t pid, nub_addr_t addr) DNB_EXPORT; +std::string DNBProcessMemoryReadCString (nub_process_t pid, nub_addr_t addr) DNB_EXPORT; +std::string DNBProcessMemoryReadCStringFixed (nub_process_t pid, nub_addr_t addr, nub_size_t fixed_length) 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; +int DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info) DNB_EXPORT; +std::string DNBProcessGetProfileData (nub_process_t pid, DNBProfileDataScanType scanType) DNB_EXPORT; +nub_bool_t DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) 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; +const char * DNBProcessGetExitInfo (nub_process_t pid) DNB_EXPORT; +nub_bool_t DNBProcessSetExitInfo (nub_process_t pid, const char *info) 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 DNBProcessGetCurrentThreadMachPort (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_bool_t DNBProcessSyncThreadState (nub_process_t pid, nub_thread_t tid) 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 DNBProcessGetAvailableProfileData (nub_process_t pid, char *buf, nub_size_t buf_size) DNB_EXPORT; +nub_size_t DNBProcessGetStopCount (nub_process_t pid) DNB_EXPORT; +uint32_t DNBProcessGetCPUType (nub_process_t pid) DNB_EXPORT; + +//---------------------------------------------------------------------- +// Process executable and arguments +//---------------------------------------------------------------------- +const char * DNBProcessGetExecutablePath (nub_process_t pid); +const char * DNBProcessGetArgumentAtIndex (nub_process_t pid, nub_size_t idx); +nub_size_t DNBProcessGetArgumentCount (nub_process_t pid); + +//---------------------------------------------------------------------- +// Process events +//---------------------------------------------------------------------- +nub_event_t DNBProcessWaitForEvents (nub_process_t pid, nub_event_t event_mask, bool wait_for_set, struct timespec* timeout); +void DNBProcessResetEvents (nub_process_t pid, nub_event_t event_mask); + +//---------------------------------------------------------------------- +// Thread functions +//---------------------------------------------------------------------- +const char * DNBThreadGetName (nub_process_t pid, nub_thread_t tid); +nub_bool_t DNBThreadGetIdentifierInfo (nub_process_t pid, nub_thread_t tid, thread_identifier_info_data_t *ident_info); +nub_state_t DNBThreadGetState (nub_process_t pid, nub_thread_t tid); +nub_bool_t DNBThreadGetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value); +nub_bool_t DNBThreadSetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value); +nub_size_t DNBThreadGetRegisterContext (nub_process_t pid, nub_thread_t tid, void *buf, size_t buf_len); +nub_size_t DNBThreadSetRegisterContext (nub_process_t pid, nub_thread_t tid, const void *buf, size_t buf_len); +uint32_t DNBThreadSaveRegisterState (nub_process_t pid, nub_thread_t tid); +nub_bool_t DNBThreadRestoreRegisterState (nub_process_t pid, nub_thread_t tid, uint32_t save_id); +nub_bool_t DNBThreadGetRegisterValueByName (nub_process_t pid, nub_thread_t tid, uint32_t set, const char *name, DNBRegisterValue *value); +nub_bool_t DNBThreadGetStopReason (nub_process_t pid, nub_thread_t tid, DNBThreadStopInfo *stop_info); +const char * DNBThreadGetInfo (nub_process_t pid, nub_thread_t tid); +Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread (nub_process_t pid, nub_thread_t tid, bool &timed_out); +Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo (nub_process_t pid, size_t idx); +ThreadInfo::QoS DNBGetRequestedQoSForThread (nub_process_t pid, nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index); +nub_addr_t DNBGetPThreadT (nub_process_t pid, nub_thread_t tid); +nub_addr_t DNBGetDispatchQueueT (nub_process_t pid, nub_thread_t tid); +nub_addr_t DNBGetTSDAddressForThread (nub_process_t pid, nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); +JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count); +// +//---------------------------------------------------------------------- +// Breakpoint functions +//---------------------------------------------------------------------- +nub_bool_t DNBBreakpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, nub_bool_t hardware); +nub_bool_t DNBBreakpointClear (nub_process_t pid, nub_addr_t addr); + +//---------------------------------------------------------------------- +// Watchpoint functions +//---------------------------------------------------------------------- +nub_bool_t DNBWatchpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, uint32_t watch_flags, nub_bool_t hardware); +nub_bool_t DNBWatchpointClear (nub_process_t pid, nub_addr_t addr); +uint32_t DNBWatchpointGetNumSupportedHWP (nub_process_t pid); + +uint32_t DNBGetRegisterCPUType (); +const DNBRegisterSetInfo * + DNBGetRegisterSetInfo (nub_size_t *num_reg_sets); +nub_bool_t DNBGetRegisterInfoByName (const char *reg_name, DNBRegisterInfo* info); + +//---------------------------------------------------------------------- +// Other static nub information calls. +//---------------------------------------------------------------------- +const char * DNBStateAsString (nub_state_t state); +nub_bool_t DNBResolveExecutablePath (const char *path, char *resolved_path, size_t resolved_path_size); +bool DNBGetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch); + +#endif diff --git a/tools/debugserver/source/DNBArch.cpp b/tools/debugserver/source/DNBArch.cpp new file mode 100644 index 000000000000..f17a719e92ec --- /dev/null +++ b/tools/debugserver/source/DNBArch.cpp @@ -0,0 +1,97 @@ +//===-- DNBArch.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/24/07. +// +//===----------------------------------------------------------------------===// + +#include "DNBArch.h" +#include +#include + +#include + +#include "DNBLog.h" + +typedef std::map CPUPluginInfoMap; + +static uint32_t g_current_cpu_type = 0; +CPUPluginInfoMap g_arch_plugins; + + +static const DNBArchPluginInfo * +GetArchInfo () +{ + CPUPluginInfoMap::const_iterator pos = g_arch_plugins.find(g_current_cpu_type); + if (pos != g_arch_plugins.end()) + return &pos->second; + return NULL; +} + + +uint32_t +DNBArchProtocol::GetArchitecture () +{ + return g_current_cpu_type; +} + +bool +DNBArchProtocol::SetArchitecture (uint32_t cpu_type) +{ + g_current_cpu_type = cpu_type; + bool result = g_arch_plugins.find(g_current_cpu_type) != g_arch_plugins.end(); + DNBLogThreadedIf (LOG_PROCESS, "DNBArchProtocol::SetDefaultArchitecture (cpu_type=0x%8.8x) => %i", cpu_type, result); + return result; +} + +void +DNBArchProtocol::RegisterArchPlugin (const DNBArchPluginInfo &arch_info) +{ + if (arch_info.cpu_type) + g_arch_plugins[arch_info.cpu_type] = arch_info; +} + +uint32_t +DNBArchProtocol::GetRegisterCPUType () +{ + const DNBArchPluginInfo *arch_info = GetArchInfo (); + if (arch_info) + return arch_info->cpu_type; + return 0; +} + +const DNBRegisterSetInfo * +DNBArchProtocol::GetRegisterSetInfo (nub_size_t *num_reg_sets) +{ + const DNBArchPluginInfo *arch_info = GetArchInfo (); + if (arch_info) + return arch_info->GetRegisterSetInfo (num_reg_sets); + *num_reg_sets = 0; + return NULL; +} + +DNBArchProtocol * +DNBArchProtocol::Create (MachThread *thread) +{ + const DNBArchPluginInfo *arch_info = GetArchInfo (); + if (arch_info) + return arch_info->Create (thread); + return NULL; + +} + +const uint8_t * +DNBArchProtocol::GetBreakpointOpcode (nub_size_t byte_size) +{ + const DNBArchPluginInfo *arch_info = GetArchInfo (); + if (arch_info) + return arch_info->GetBreakpointOpcode (byte_size); + return NULL; +} + diff --git a/tools/debugserver/source/DNBArch.h b/tools/debugserver/source/DNBArch.h new file mode 100644 index 000000000000..c07d3a67400d --- /dev/null +++ b/tools/debugserver/source/DNBArch.h @@ -0,0 +1,129 @@ +//===-- 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; +class MachThread; + +typedef DNBArchProtocol * (* DNBArchCallbackCreate)(MachThread *thread); +typedef const DNBRegisterSetInfo * (* DNBArchCallbackGetRegisterSetInfo)(nub_size_t *num_reg_sets); +typedef const uint8_t * (* DNBArchCallbackGetBreakpointOpcode)(nub_size_t byte_size); + +typedef struct DNBArchPluginInfoTag +{ + uint32_t cpu_type; + DNBArchCallbackCreate Create; + DNBArchCallbackGetRegisterSetInfo GetRegisterSetInfo; + DNBArchCallbackGetBreakpointOpcode GetBreakpointOpcode; +} DNBArchPluginInfo; + +class DNBArchProtocol +{ +public: + static DNBArchProtocol * + Create (MachThread *thread); + + static uint32_t + GetRegisterCPUType (); + + static const DNBRegisterSetInfo * + GetRegisterSetInfo (nub_size_t *num_reg_sets); + + static const uint8_t * + GetBreakpointOpcode (nub_size_t byte_size); + + static void + RegisterArchPlugin (const DNBArchPluginInfo &arch_info); + + static uint32_t + GetArchitecture (); + + static bool + SetArchitecture (uint32_t cpu_type); + + DNBArchProtocol () : + m_save_id(0) + { + + } + + virtual ~DNBArchProtocol () + { + + } + virtual bool GetRegisterValue (uint32_t set, uint32_t reg, DNBRegisterValue *value) = 0; + virtual bool SetRegisterValue (uint32_t set, uint32_t 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 uint32_t SaveRegisterState () = 0; + virtual bool RestoreRegisterState (uint32_t save_id) = 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 kern_return_t SetPC (uint64_t value) = 0; + 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, bool also_set_on_task) { return INVALID_NUB_HW_INDEX; } + virtual bool DisableHardwareBreakpoint (uint32_t hw_index) { return false; } + virtual bool DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task) { return false; } + virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr) { return INVALID_NUB_HW_INDEX; } + virtual bool StepNotComplete () { return false; } + +protected: + friend class MachThread; + + uint32_t GetNextRegisterStateSaveID () + { + return ++m_save_id; + } + + enum + { + Trans_Pending = 0, // Transaction is pending, and checkpoint state has been snapshotted. + Trans_Done = 1, // Transaction is done, the current state is committed, and checkpoint state is irrelevant. + Trans_Rolled_Back = 2 // Transaction is done, the current state has been rolled back to the checkpoint state. + }; + virtual bool StartTransForHWP() { return true; } + virtual bool RollbackTransForHWP() { return true; } + virtual bool FinishTransForHWP() { return true; } + + uint32_t m_save_id; // An always incrementing integer ID used with SaveRegisterState/RestoreRegisterState + +}; + + +#include "MacOSX/arm/DNBArchImpl.h" +#include "MacOSX/arm64/DNBArchImplARM64.h" +#include "MacOSX/i386/DNBArchImplI386.h" +#include "MacOSX/x86_64/DNBArchImplX86_64.h" +#include "MacOSX/ppc/DNBArchImpl.h" + +#endif diff --git a/tools/debugserver/source/DNBBreakpoint.cpp b/tools/debugserver/source/DNBBreakpoint.cpp new file mode 100644 index 000000000000..2645f173306b --- /dev/null +++ b/tools/debugserver/source/DNBBreakpoint.cpp @@ -0,0 +1,225 @@ +//===-- 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 "MachProcess.h" +#include +#include +#include +#include "DNBLog.h" + + +#pragma mark -- DNBBreakpoint +DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size, bool hardware) : + m_retain_count (1), + m_byte_size (static_cast(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) +{ +} + +DNBBreakpoint::~DNBBreakpoint() +{ +} + +void +DNBBreakpoint::Dump() const +{ + if (IsBreakpoint()) + { + DNBLog ("DNBBreakpoint addr = 0x%llx state = %s type = %s breakpoint hw_index = %i", + (uint64_t)m_addr, + m_enabled ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + GetHardwareIndex()); + } + else + { + DNBLog ("DNBBreakpoint addr = 0x%llx size = %llu state = %s type = %s watchpoint (%s%s) hw_index = %i", + (uint64_t)m_addr, + (uint64_t)m_byte_size, + m_enabled ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + m_watch_read ? "r" : "", + m_watch_write ? "w" : "", + GetHardwareIndex()); + } +} + +#pragma mark -- DNBBreakpointList + +DNBBreakpointList::DNBBreakpointList() +{ +} + +DNBBreakpointList::~DNBBreakpointList() +{ +} + + +DNBBreakpoint * +DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length, bool hardware) +{ + m_breakpoints.insert(std::make_pair(addr, DNBBreakpoint(addr, length, hardware))); + iterator pos = m_breakpoints.find (addr); + return &pos->second; +} + +bool +DNBBreakpointList::Remove (nub_addr_t addr) +{ + iterator pos = m_breakpoints.find(addr); + if (pos != m_breakpoints.end()) + { + m_breakpoints.erase(pos); + return true; + } + return false; +} + +DNBBreakpoint * +DNBBreakpointList::FindByAddress (nub_addr_t addr) +{ + iterator pos = m_breakpoints.find(addr); + if (pos != m_breakpoints.end()) + return &pos->second; + + return NULL; +} + +const DNBBreakpoint * +DNBBreakpointList::FindByAddress (nub_addr_t addr) const +{ + const_iterator pos = m_breakpoints.find(addr); + if (pos != m_breakpoints.end()) + return &pos->second; + + return NULL; +} + +// Finds the next breakpoint at an address greater than or equal to "addr" +size_t +DNBBreakpointList::FindBreakpointsThatOverlapRange (nub_addr_t addr, + nub_addr_t size, + std::vector &bps) +{ + bps.clear(); + iterator end = m_breakpoints.end(); + // Find the first breakpoint with an address >= to "addr" + iterator pos = m_breakpoints.lower_bound(addr); + if (pos != end) + { + if (pos != m_breakpoints.begin()) + { + // Watch out for a breakpoint at an address less than "addr" that might still overlap + iterator prev_pos = pos; + --prev_pos; + if (prev_pos->second.IntersectsRange (addr, size, NULL, NULL, NULL)) + bps.push_back (&pos->second); + + } + + while (pos != end) + { + // When we hit a breakpoint whose start address is greater than "addr + size" we are done. + // Do the math in a way that doesn't risk unsigned overflow with bad input. + if ((pos->second.Address() - addr) >= size) + break; + + // Check if this breakpoint overlaps, and if it does, add it to the list + if (pos->second.IntersectsRange (addr, size, NULL, NULL, NULL)) + { + bps.push_back (&pos->second); + ++pos; + } + } + } + return bps.size(); +} + +void +DNBBreakpointList::Dump() const +{ + const_iterator pos; + const_iterator end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + pos->second.Dump(); +} + +void +DNBBreakpointList::DisableAll () +{ + iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + pos->second.SetEnabled(false); +} + + +void +DNBBreakpointList::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, void *p) const +{ + uint8_t *buf = (uint8_t *)p; + const_iterator end = m_breakpoints.end(); + const_iterator pos = m_breakpoints.lower_bound(addr); + while (pos != end && (pos->first < (addr + size))) + { + nub_addr_t intersect_addr; + nub_size_t intersect_size; + nub_size_t opcode_offset; + const DNBBreakpoint &bp = pos->second; + 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); + } + ++pos; + } +} + +void +DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) +{ + iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + process->DisableBreakpoint(pos->second.Address(), false); +} + +void +DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) +{ + iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + process->DisableWatchpoint(pos->second.Address(), false); +} + +void +DNBBreakpointList::RemoveDisabled() +{ + iterator pos = m_breakpoints.begin(); + while (pos != m_breakpoints.end()) + { + if (!pos->second.IsEnabled()) + pos = m_breakpoints.erase(pos); + else + ++pos; + } +} diff --git a/tools/debugserver/source/DNBBreakpoint.h b/tools/debugserver/source/DNBBreakpoint.h new file mode 100644 index 000000000000..c764dbd6cf29 --- /dev/null +++ b/tools/debugserver/source/DNBBreakpoint.h @@ -0,0 +1,165 @@ +//===-- 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 +#include + +#include "DNBDefs.h" + +class MachProcess; + +class DNBBreakpoint +{ +public: + DNBBreakpoint(nub_addr_t m_addr, nub_size_t byte_size, bool hardware); + ~DNBBreakpoint(); + + 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(bool 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; } + void Dump() const; + uint32_t Retain () + { + return ++m_retain_count; + } + uint32_t Release () + { + if (m_retain_count == 0) + return 0; + return --m_retain_count; + } + +private: + uint32_t m_retain_count; // Each breakpoint is maintained by address and is ref counted in case multiple people set a breakpoint at the same address + uint32_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 +}; + + +class DNBBreakpointList +{ +public: + DNBBreakpointList(); + ~DNBBreakpointList(); + + DNBBreakpoint * Add (nub_addr_t addr, nub_size_t length, bool hardware); + bool Remove (nub_addr_t addr); + DNBBreakpoint * FindByAddress (nub_addr_t addr); + const DNBBreakpoint * FindByAddress (nub_addr_t addr) const; + + size_t FindBreakpointsThatOverlapRange (nub_addr_t addr, + nub_addr_t size, + std::vector &bps); + + void Dump () const; + + size_t Size() const { return m_breakpoints.size(); } + void DisableAll (); + + void RemoveTrapsFromBuffer (nub_addr_t addr, + nub_size_t size, + void *buf) const; + + void DisableAllBreakpoints (MachProcess *process); + void DisableAllWatchpoints(MachProcess *process); + void RemoveDisabled (); +protected: + typedef std::map collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + collection m_breakpoints; +}; + +#endif + diff --git a/tools/debugserver/source/DNBDataRef.cpp b/tools/debugserver/source/DNBDataRef.cpp new file mode 100644 index 000000000000..53e9881dfb94 --- /dev/null +++ b/tools/debugserver/source/DNBDataRef.cpp @@ -0,0 +1,386 @@ +//===-- 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); +} +//---------------------------------------------------------------------- +// 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 = 0; + 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'; + size_t 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/tools/debugserver/source/DNBDataRef.h b/tools/debugserver/source/DNBDataRef.h new file mode 100644 index 000000000000..d0c34ced6233 --- /dev/null +++ b/tools/debugserver/source/DNBDataRef.h @@ -0,0 +1,125 @@ +//===-- 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; + } + + size_t BytesLeft (size_t offset) const + { + const size_t size = GetSize(); + if (size > offset) + return size - offset; + return 0; + } + + bool ValidOffset(offset_t offset) const + { + return BytesLeft(offset) > 0; + } + bool ValidOffsetForDataOfSize(offset_t offset, uint32_t num_bytes) const + { + return num_bytes <= BytesLeft (offset); + } + 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/tools/debugserver/source/DNBDefs.h b/tools/debugserver/source/DNBDefs.h new file mode 100644 index 000000000000..e3757e903e59 --- /dev/null +++ b/tools/debugserver/source/DNBDefs.h @@ -0,0 +1,372 @@ +//===-- 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__) || defined (__arm64__) || defined (__aarch64__) + +//---------------------------------------------------------------------- +// 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_index_t; +typedef pid_t nub_process_t; +typedef uint64_t nub_thread_t; +typedef uint32_t nub_event_t; +typedef uint32_t nub_bool_t; + +#define INVALID_NUB_PROCESS ((nub_process_t)0) +#define INVALID_NUB_THREAD ((nub_thread_t)0) +#define INVALID_NUB_WATCH_ID ((nub_watch_t)0) +#define INVALID_NUB_HW_INDEX UINT32_MAX +#define INVALID_NUB_REGNUM UINT32_MAX +#define NUB_GENERIC_ERROR UINT32_MAX + +// 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 = 1, + eLaunchFlavorForkExec = 2, +#ifdef WITH_SPRINGBOARD + eLaunchFlavorSpringBoard = 3, +#endif +#ifdef WITH_BKS + eLaunchFlavorBKS = 4, +#endif +#ifdef WITH_FBS + eLaunchFlavorFBS = 5 +#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 + eEventProfileDataAvailable = 1 << 4, // Profile data ready for retrieval + kAllEventsMask = eEventProcessRunningStateChanged | + eEventProcessStoppedStateChanged | + eEventSharedLibsStateChange | + eEventStdioAvailable | + eEventProfileDataAvailable +}; + +#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 +#define GENERIC_REGNUM_ARG1 5 // The register that would contain pointer size or less argument 1 (if any) +#define GENERIC_REGNUM_ARG2 6 // The register that would contain pointer size or less argument 2 (if any) +#define GENERIC_REGNUM_ARG3 7 // The register that would contain pointer size or less argument 3 (if any) +#define GENERIC_REGNUM_ARG4 8 // The register that would contain pointer size or less argument 4 (if any) +#define GENERIC_REGNUM_ARG5 9 // The register that would contain pointer size or less argument 5 (if any) +#define GENERIC_REGNUM_ARG6 10 // The register that would contain pointer size or less argument 6 (if any) +#define GENERIC_REGNUM_ARG7 11 // The register that would contain pointer size or less argument 7 (if any) +#define GENERIC_REGNUM_ARG8 12 // The register that would contain pointer size or less argument 8 (if any) + +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_ehframe; // eh_frame 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_debugserver;// The debugserver register number we'll use over gdb-remote protocol (INVALID_NUB_REGNUM when none) + const char **value_regs; // If this register is a part of other registers, list the register names terminated by NULL + const char **update_regs; // If modifying this register will invalidate other registers, list the register names terminated by NULL +}; + +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, + eStopTypeExec +}; + +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[32]; + int16_t v_sint16[16]; + int32_t v_sint32[8]; + int64_t v_sint64[4]; + uint8_t v_uint8[32]; + uint16_t v_uint16[16]; + uint32_t v_uint32[8]; + uint64_t v_uint64[4]; + float v_float32[8]; + double v_float64[4]; + 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 identifier 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 +}; + +struct DNBRegionInfo +{ + nub_addr_t addr; + nub_addr_t size; + uint32_t permissions; +}; + +enum DNBProfileDataScanType +{ + eProfileHostCPU = (1 << 0), + eProfileCPU = (1 << 1), + + eProfileThreadsCPU = (1 << 2), // By default excludes eProfileThreadName and eProfileQueueName. + eProfileThreadName = (1 << 3), // Assume eProfileThreadsCPU, get thread name as well. + eProfileQueueName = (1 << 4), // Assume eProfileThreadsCPU, get queue name as well. + + eProfileHostMemory = (1 << 5), + + eProfileMemory = (1 << 6), // By default, excludes eProfileMemoryDirtyPage. + eProfileMemoryDirtyPage = (1 << 7), // Assume eProfileMemory, get Dirty Page size as well. + eProfileMemoryAnonymous = (1 << 8), // Assume eProfileMemory, get Anonymous memory as well. + + eProfileEnergy = (1 << 9), + + eProfileAll = 0xffffffff +}; + +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); + +#define UNUSED_IF_ASSERT_DISABLED(x) ((void)(x)) + +#endif // #ifndef __DNBDefs_h__ diff --git a/tools/debugserver/source/DNBError.cpp b/tools/debugserver/source/DNBError.cpp new file mode 100644 index 000000000000..c9d8ebd58d89 --- /dev/null +++ b/tools/debugserver/source/DNBError.cpp @@ -0,0 +1,128 @@ +//===-- 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" + +#ifdef WITH_SPRINGBOARD +#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; + +#ifdef WITH_SPRINGBOARD + case SpringBoard: + { + CFStringRef statusStr = SBSApplicationLaunchingErrorString (m_err); + if (CFString::UTF8 (statusStr, m_str) == NULL) + m_str.clear(); + } + break; +#endif +#ifdef WITH_BKS + case BackBoard: + { + // You have to call ObjC routines to get the error string from BackBoardServices. + // Not sure I want to make DNBError.cpp an .mm file. For now just make sure you + // pre-populate the error string when you make the DNBError of type BackBoard. + m_str.assign("Should have set BackBoard error when making the error string."); + } + break; +#endif +#ifdef WITH_FBS + case FrontBoard: + { + // You have to call ObjC routines to get the error string from FrontBoardServices. + // Not sure I want to make DNBError.cpp an .mm file. For now just make sure you + // pre-populate the error string when you make the DNBError of type FrontBoard. + m_str.assign("Should have set FrontBoard error when making the error string."); + } + 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/tools/debugserver/source/DNBError.h b/tools/debugserver/source/DNBError.h new file mode 100644 index 000000000000..274ae0d44773 --- /dev/null +++ b/tools/debugserver/source/DNBError.h @@ -0,0 +1,102 @@ +//===-- 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 = 1, + POSIX = 2 +#ifdef WITH_SPRINGBOARD + , SpringBoard = 3 +#endif +#ifdef WITH_BKS + , BackBoard = 4 +#endif +#ifdef WITH_FBS + , FrontBoard = 5 +#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/tools/debugserver/source/DNBLog.cpp b/tools/debugserver/source/DNBLog.cpp new file mode 100644 index 000000000000..18d8d2ad3a67 --- /dev/null +++ b/tools/debugserver/source/DNBLog.cpp @@ -0,0 +1,377 @@ +//===-- 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 +#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; +} + +DNBCallbackLog +DNBLogGetLogCallback () +{ + return g_log_callback; +} + +bool +DNBLogEnabled () +{ + return g_log_callback != NULL; +} + +bool +DNBLogEnabledForAny (uint32_t mask) +{ + if (g_log_callback) + return (g_log_bits & mask) != 0; + return false; +} +static inline void +_DNBLogVAPrintf(uint32_t flags, const char *format, va_list args) +{ + static PThreadMutex g_LogThreadedMutex(PTHREAD_MUTEX_RECURSIVE); + PTHREAD_MUTEX_LOCKER(locker, g_LogThreadedMutex); + + 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 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) + { + static struct timeval g_timeval = { 0 , 0 }; + static struct timeval tv; + static struct timeval delta; + gettimeofday(&tv, NULL); + if (g_timeval.tv_sec == 0) + { + delta.tv_sec = 0; + delta.tv_usec = 0; + } + else + { + timersub (&tv, &g_timeval, &delta); + } + g_timeval = tv; + + // Calling "mach_port_deallocate()" bumps the reference count on the thread + // port, so we need to deallocate it. mach_task_self() doesn't bump the ref + // count. + thread_port_t thread_self = mach_thread_self(); + + _DNBLog (DNBLOG_FLAG_THREADED, "%u +%lu.%06u sec [%4.4x/%4.4x]: %s", + ++g_message_id, + delta.tv_sec, + delta.tv_usec, + getpid(), + thread_self, + arg_msg); + + mach_port_deallocate(mach_task_self(), thread_self); + 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) + { + static struct timeval g_timeval = { 0 , 0 }; + static struct timeval tv; + static struct timeval delta; + gettimeofday(&tv, NULL); + if (g_timeval.tv_sec == 0) + { + delta.tv_sec = 0; + delta.tv_usec = 0; + } + else + { + timersub (&tv, &g_timeval, &delta); + } + g_timeval = tv; + + // Calling "mach_port_deallocate()" bumps the reference count on the thread + // port, so we need to deallocate it. mach_task_self() doesn't bump the ref + // count. + thread_port_t thread_self = mach_thread_self(); + + _DNBLog (DNBLOG_FLAG_THREADED, "%u +%lu.%06u sec [%4.4x/%4.4x]: %s", + ++g_message_id, + delta.tv_sec, + delta.tv_usec, + getpid(), + thread_self, + arg_msg); + + mach_port_deallocate(mach_task_self(), thread_self); + + 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/tools/debugserver/source/DNBLog.h b/tools/debugserver/source/DNBLog.h new file mode 100644 index 000000000000..01add065abcd --- /dev/null +++ b/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) + +void _DNBLog(uint32_t flags, const char *format, ...) __attribute__ ((format (printf, 2, 3))); +void _DNBLogDebug (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void _DNBLogDebugVerbose (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))) ; +void _DNBLogThreaded (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void _DNBLogThreadedIf (uint32_t mask, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +void _DNBLogError (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void _DNBLogFatalError (int err, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +void _DNBLogVerbose (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void _DNBLogWarning (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void _DNBLogWarningVerbose (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +bool DNBLogCheckLogBit (uint32_t bit); +uint32_t DNBLogSetLogMask (uint32_t mask); +uint32_t DNBLogGetLogMask (); +void DNBLogSetLogCallback (DNBCallbackLog callback, void *baton); +DNBCallbackLog DNBLogGetLogCallback (); +bool DNBLogEnabled (); +bool DNBLogEnabledForAny (uint32_t mask); +int DNBLogGetDebug (); +void DNBLogSetDebug (int g); +int DNBLogGetVerbose (); +void DNBLogSetVerbose (int g); + +#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 (DNBLogEnabledForAny(mask)) { _DNBLogThreaded(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/tools/debugserver/source/DNBRegisterInfo.cpp b/tools/debugserver/source/DNBRegisterInfo.cpp new file mode 100644 index 000000000000..acc7ba9946be --- /dev/null +++ b/tools/debugserver/source/DNBRegisterInfo.cpp @@ -0,0 +1,219 @@ +//===-- 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) + { + 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 (uint32_t i=0; i 0) + { + switch (info.format) + { + case VectorOfSInt8: + snprintf(str, sizeof(str), "%s", "sint8 { "); + pos = str + strlen(str); + for (uint32_t 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/tools/debugserver/source/DNBRuntimeAction.h b/tools/debugserver/source/DNBRuntimeAction.h new file mode 100644 index 000000000000..d77bda8c604d --- /dev/null +++ b/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/tools/debugserver/source/DNBThreadResumeActions.cpp b/tools/debugserver/source/DNBThreadResumeActions.cpp new file mode 100644 index 000000000000..b50dd0617843 --- /dev/null +++ b/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(); + } + + void + Clear() + { + m_actions.clear(); + m_signal_handled.clear(); + } + +protected: + std::vector m_actions; + mutable std::vector m_signal_handled; +}; + + +#endif // #ifndef __DNBThreadResumeActions_h__ diff --git a/tools/debugserver/source/DNBTimer.h b/tools/debugserver/source/DNBTimer.h new file mode 100644 index 000000000000..ca56e30c7090 --- /dev/null +++ b/tools/debugserver/source/DNBTimer.h @@ -0,0 +1,163 @@ +//===-- 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 "DNBDefs.h" +#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 (uint64_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::unique_ptr m_mutexAP; + struct timeval m_timeval; +}; + +#endif // #ifndef __DNBTimer_h__ diff --git a/tools/debugserver/source/JSONGenerator.h b/tools/debugserver/source/JSONGenerator.h new file mode 100644 index 000000000000..423b2bd57927 --- /dev/null +++ b/tools/debugserver/source/JSONGenerator.h @@ -0,0 +1,490 @@ +//===-- JSONGenerator.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __JSONGenerator_h_ +#define __JSONGenerator_h_ + +// C Includes +// C++ Includes + +#include +#include +#include +#include +#include + +//---------------------------------------------------------------------- +/// @class JSONGenerator JSONGenerator.h +/// @brief A class which can construct structured data for the sole purpose +/// of printing it in JSON format. +/// +/// A stripped down version of lldb's StructuredData objects which are much +/// general purpose. This variant is intended only for assembling information +/// and printing it as a JSON string. +//---------------------------------------------------------------------- + +class JSONGenerator +{ +public: + + class Object; + class Array; + class Integer; + class Float; + class Boolean; + class String; + class Dictionary; + class Generic; + + typedef std::shared_ptr ObjectSP; + typedef std::shared_ptr ArraySP; + typedef std::shared_ptr IntegerSP; + typedef std::shared_ptr FloatSP; + typedef std::shared_ptr BooleanSP; + typedef std::shared_ptr StringSP; + typedef std::shared_ptr DictionarySP; + typedef std::shared_ptr GenericSP; + + enum class Type + { + eTypeInvalid = -1, + eTypeNull = 0, + eTypeGeneric, + eTypeArray, + eTypeInteger, + eTypeFloat, + eTypeBoolean, + eTypeString, + eTypeDictionary + }; + + class Object : + public std::enable_shared_from_this + { + public: + + Object (Type t = Type::eTypeInvalid) : + m_type (t) + { + } + + virtual ~Object () + { + } + + virtual bool + IsValid() const + { + return true; + } + + virtual void + Clear () + { + m_type = Type::eTypeInvalid; + } + + Type + GetType () const + { + return m_type; + } + + void + SetType (Type t) + { + m_type = t; + } + + Array * + GetAsArray () + { + if (m_type == Type::eTypeArray) + return (Array *)this; + return NULL; + } + + Dictionary * + GetAsDictionary () + { + if (m_type == Type::eTypeDictionary) + return (Dictionary *)this; + return NULL; + } + + Integer * + GetAsInteger () + { + if (m_type == Type::eTypeInteger) + return (Integer *)this; + return NULL; + } + + Float * + GetAsFloat () + { + if (m_type == Type::eTypeFloat) + return (Float *)this; + return NULL; + } + + Boolean * + GetAsBoolean () + { + if (m_type == Type::eTypeBoolean) + return (Boolean *)this; + return NULL; + } + + String * + GetAsString () + { + if (m_type == Type::eTypeString) + return (String *)this; + return NULL; + } + + Generic * + GetAsGeneric() + { + if (m_type == Type::eTypeGeneric) + return (Generic *)this; + return NULL; + } + + virtual void + Dump (std::ostream &s) const = 0; + + private: + Type m_type; + }; + + class Array : public Object + { + public: + Array () : + Object (Type::eTypeArray) + { + } + + virtual + ~Array() + { + } + + void + AddItem(ObjectSP item) + { + m_items.push_back(item); + } + + void Dump(std::ostream &s) const override + { + s << "["; + const size_t arrsize = m_items.size(); + for (size_t i = 0; i < arrsize; ++i) + { + m_items[i]->Dump(s); + if (i + 1 < arrsize) + s << ","; + } + s << "]"; + } + + protected: + typedef std::vector collection; + collection m_items; + }; + + + class Integer : public Object + { + public: + Integer (uint64_t value = 0) : + Object (Type::eTypeInteger), + m_value (value) + { + } + + virtual ~Integer() + { + } + + void + SetValue (uint64_t value) + { + m_value = value; + } + + void Dump(std::ostream &s) const override + { + s << m_value; + } + + protected: + uint64_t m_value; + }; + + class Float : public Object + { + public: + Float (double d = 0.0) : + Object (Type::eTypeFloat), + m_value (d) + { + } + + virtual ~Float() + { + } + + void + SetValue (double value) + { + m_value = value; + } + + void Dump(std::ostream &s) const override + { + s << m_value; + } + + protected: + double m_value; + }; + + class Boolean : public Object + { + public: + Boolean (bool b = false) : + Object (Type::eTypeBoolean), + m_value (b) + { + } + + virtual ~Boolean() + { + } + + void + SetValue (bool value) + { + m_value = value; + } + + void Dump(std::ostream &s) const override + { + if (m_value == true) + s << "true"; + else + s << "false"; + } + + protected: + bool m_value; + }; + + + + class String : public Object + { + public: + String () : + Object (Type::eTypeString), + m_value () + { + } + + String (const std::string &s) : + Object (Type::eTypeString), + m_value (s) + { + } + + String (const std::string &&s) : + Object (Type::eTypeString), + m_value (s) + { + } + + void + SetValue (const std::string &string) + { + m_value = string; + } + + void Dump(std::ostream &s) const override + { + std::string quoted; + const size_t strsize = m_value.size(); + for (size_t i = 0; i < strsize ; ++i) + { + char ch = m_value[i]; + if (ch == '"') + quoted.push_back ('\\'); + quoted.push_back (ch); + } + s << '"' << quoted.c_str() << '"'; + } + + protected: + std::string m_value; + }; + + class Dictionary : public Object + { + public: + Dictionary () : + Object (Type::eTypeDictionary), + m_dict () + { + } + + virtual ~Dictionary() + { + } + + void + AddItem (std::string key, ObjectSP value) + { + m_dict.push_back(Pair(key, value)); + } + + void + AddIntegerItem (std::string key, uint64_t value) + { + AddItem (key, ObjectSP (new Integer(value))); + } + + void + AddFloatItem (std::string key, double value) + { + AddItem (key, ObjectSP (new Float(value))); + } + + void + AddStringItem (std::string key, std::string value) + { + AddItem (key, ObjectSP (new String(std::move(value)))); + } + + void + AddBytesAsHexASCIIString (std::string key, const uint8_t *src, size_t src_len) + { + if (src && src_len) + { + std::ostringstream strm; + for (size_t i = 0; i < src_len; i++) + strm << std::setfill('0') << std::hex << std::right << std::setw(2) << ((uint32_t)(src[i])); + AddItem (key, ObjectSP (new String(std::move(strm.str())))); + } + else + { + AddItem (key, ObjectSP (new String())); + } + } + + void + AddBooleanItem (std::string key, bool value) + { + AddItem (key, ObjectSP (new Boolean(value))); + } + + void Dump(std::ostream &s) const override + { + bool have_printed_one_elem = false; + s << "{"; + for (collection::const_iterator iter = m_dict.begin(); iter != m_dict.end(); ++iter) + { + if (have_printed_one_elem == false) + { + have_printed_one_elem = true; + } + else + { + s << ","; + } + s << "\"" << iter->first.c_str() << "\":"; + iter->second->Dump(s); + } + s << "}"; + } + + protected: + // Keep the dictionary as a vector so the dictionary doesn't reorder itself when you dump it + // We aren't accessing keys by name, so this won't affect performance + typedef std::pair Pair; + typedef std::vector collection; + collection m_dict; + }; + + class Null : public Object + { + public: + Null () : + Object (Type::eTypeNull) + { + } + + virtual ~Null() + { + } + + bool + IsValid() const override + { + return false; + } + + void Dump(std::ostream &s) const override + { + s << "null"; + } + + protected: + }; + + class Generic : public Object + { + public: + explicit Generic(void *object = nullptr) + : Object(Type::eTypeGeneric) + , m_object(object) + { + } + + void + SetValue(void *value) + { + m_object = value; + } + + void * + GetValue() const + { + return m_object; + } + + bool + IsValid() const override + { + return m_object != nullptr; + } + + void Dump(std::ostream &s) const override; + + private: + void *m_object; + }; + +}; // class JSONGenerator + + + +#endif // __JSONGenerator_h_ diff --git a/tools/debugserver/source/MacOSX/CFBundle.cpp b/tools/debugserver/source/MacOSX/CFBundle.cpp new file mode 100644 index 000000000000..fdcb7cc2fcb2 --- /dev/null +++ b/tools/debugserver/source/MacOSX/CFBundle.cpp @@ -0,0 +1,97 @@ +//===-- 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; +} + + +CFURLRef +CFBundle::CopyExecutableURL () const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return CFBundleCopyExecutableURL(bundle); + return NULL; +} diff --git a/tools/debugserver/source/MacOSX/CFBundle.h b/tools/debugserver/source/MacOSX/CFBundle.h new file mode 100644 index 000000000000..e08290add731 --- /dev/null +++ b/tools/debugserver/source/MacOSX/CFBundle.h @@ -0,0 +1,43 @@ +//===-- 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; + + CFURLRef + CopyExecutableURL () const; + +protected: + CFReleaser m_bundle_url; +}; + +#endif // #ifndef __CFBundle_h__ diff --git a/tools/debugserver/source/MacOSX/CFData.cpp b/tools/debugserver/source/MacOSX/CFData.cpp new file mode 100644 index 000000000000..94c93da544a4 --- /dev/null +++ b/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/tools/debugserver/source/MacOSX/CFData.h b/tools/debugserver/source/MacOSX/CFData.h new file mode 100644 index 000000000000..2c9d65d3af72 --- /dev/null +++ b/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/tools/debugserver/source/MacOSX/CFString.cpp b/tools/debugserver/source/MacOSX/CFString.cpp new file mode 100644 index 000000000000..819024ca3bcb --- /dev/null +++ b/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/tools/debugserver/source/MacOSX/CFString.h b/tools/debugserver/source/MacOSX/CFString.h new file mode 100644 index 000000000000..73945a28a658 --- /dev/null +++ b/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 = kCFStringEncodingUTF8); + CFString (const CFString& rhs); + CFString& operator= (const CFString& rhs); + virtual ~CFString (); + + const char * GetFileSystemRepresentation (std::string& str); + CFStringRef SetFileSystemRepresentation (const char *path); + CFStringRef SetFileSystemRepresentationFromCFType (CFTypeRef cf_type); + CFStringRef SetFileSystemRepresentationAndExpandTilde (const char *path); + const char * UTF8 (std::string& str); + CFIndex GetLength() const; + static const char *UTF8 (CFStringRef cf_str, std::string& str); + static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str); + static const char* GlobPath(const char* path, std::string &expanded_path); +}; + +#endif // #ifndef __CFString_h__ diff --git a/tools/debugserver/source/MacOSX/CFUtils.h b/tools/debugserver/source/MacOSX/CFUtils.h new file mode 100644 index 000000000000..afa984fa11cd --- /dev/null +++ b/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/tools/debugserver/source/MacOSX/CMakeLists.txt b/tools/debugserver/source/MacOSX/CMakeLists.txt new file mode 100644 index 000000000000..d319cb7b0bab --- /dev/null +++ b/tools/debugserver/source/MacOSX/CMakeLists.txt @@ -0,0 +1,89 @@ +#add_subdirectory(arm64) +#add_subdirectory(arm) +add_subdirectory(i386) +#add_subdirectory(ppc) +add_subdirectory(x86_64) + +include_directories(..) + +set(generated_mach_interfaces + ${CMAKE_CURRENT_BINARY_DIR}/mach_exc.h + ${CMAKE_CURRENT_BINARY_DIR}/mach_excServer.c + ${CMAKE_CURRENT_BINARY_DIR}/mach_excUser.c + ) +add_custom_command(OUTPUT ${generated_mach_interfaces} + COMMAND mig ${CMAKE_CURRENT_SOURCE_DIR}/dbgnub-mig.defs + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/dbgnub-mig.defs + ) + +set(DEBUGSERVER_VERS_GENERATED_FILE ${CMAKE_CURRENT_BINARY_DIR}/debugserver_vers.c) +set_source_files_properties(${DEBUGSERVER_VERS_GENERATED_FILE} PROPERTIES GENERATED 1) + +add_custom_command(OUTPUT ${DEBUGSERVER_VERS_GENERATED_FILE} + COMMAND ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl + ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj debugserver + > ${DEBUGSERVER_VERS_GENERATED_FILE} + DEPENDS ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl + ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj + ) + +set(DEBUGSERVER_USED_LIBS + lldbDebugserverCommon + lldbUtility + lldbDebugserverMacOSX_I386 + lldbDebugserverMacOSX_X86_64 + ) + +add_lldb_executable(debugserver + HasAVX.s + CFBundle.cpp + CFData.cpp + CFString.cpp + Genealogy.cpp + MachException.cpp + MachProcess.mm + MachTask.mm + MachThread.cpp + MachThreadList.cpp + MachVMMemory.cpp + MachVMRegion.cpp + ${generated_mach_interfaces} + ${DEBUGSERVER_VERS_GENERATED_FILE} + ) + +set_source_files_properties( + HasAVX.s + # Necessary since compilation will fail with stand-alone assembler + PROPERTIES LANGUAGE C COMPILE_FLAGS "-x assembler-with-cpp" + ) + +target_link_libraries(debugserver ${DEBUGSERVER_USED_LIBS}) + +# Sign the debugserver binary +set (CODESIGN_IDENTITY lldb_codesign) +execute_process( + COMMAND xcrun -f codesign_allocate + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CODESIGN_ALLOCATE + ) +# Older cmake versions don't support "-E env". +if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 3.2) + add_custom_command(TARGET debugserver + POST_BUILD + # Note: --entitlements option removed, as it causes errors when debugging. + # was: COMMAND CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --entitlements ${CMAKE_CURRENT_SOURCE_DIR}/../debugserver-entitlements.plist --force --sign ${CODESIGN_IDENTITY} debugserver + COMMAND CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${CODESIGN_IDENTITY} debugserver + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) +else() + add_custom_command(TARGET debugserver + POST_BUILD + # Note: --entitlements option removed (see comment above). + COMMAND ${CMAKE_COMMAND} -E env CODESIGN_ALLOCATE=${CODESIGN_ALLOCATE} codesign --force --sign ${CODESIGN_IDENTITY} debugserver + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) +endif() + +install(TARGETS debugserver + RUNTIME DESTINATION bin + ) diff --git a/tools/debugserver/source/MacOSX/Genealogy.cpp b/tools/debugserver/source/MacOSX/Genealogy.cpp new file mode 100644 index 000000000000..a5ee097aa2a6 --- /dev/null +++ b/tools/debugserver/source/MacOSX/Genealogy.cpp @@ -0,0 +1,300 @@ +///===-- Activity.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 "DNBDefs.h" +#include "Genealogy.h" +#include "GenealogySPI.h" +#include "MachThreadList.h" + +//--------------------------- +/// Constructor +//--------------------------- + +Genealogy::Genealogy () : + m_os_activity_diagnostic_for_pid (nullptr), + m_os_activity_iterate_processes (nullptr), + m_os_activity_iterate_breadcrumbs (nullptr), + m_os_activity_iterate_messages (nullptr), + m_os_activity_iterate_activities (nullptr), + m_os_trace_get_type (nullptr), + m_os_trace_copy_formatted_message (nullptr), + m_os_activity_for_thread (nullptr), + m_os_activity_for_task_thread (nullptr), + m_thread_activities(), + m_process_executable_infos(), + m_diagnosticd_call_timed_out(false) +{ + m_os_activity_diagnostic_for_pid = (bool (*)(pid_t, os_activity_t, uint32_t, os_diagnostic_block_t))dlsym (RTLD_DEFAULT, "os_activity_diagnostic_for_pid"); + m_os_activity_iterate_processes = (void (*)(os_activity_process_list_t, bool (^)(os_activity_process_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_processes"); + m_os_activity_iterate_breadcrumbs = (void (*)(os_activity_process_t, bool (^)(os_activity_breadcrumb_t))) dlsym (RTLD_DEFAULT, "os_activity_iterate_breadcrumbs"); + m_os_activity_iterate_messages = (void (*)(os_trace_message_list_t, os_activity_process_t, bool (^)(os_trace_message_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_messages"); + m_os_activity_iterate_activities = (void (*)(os_activity_list_t, os_activity_process_t, bool (^)(os_activity_entry_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_activities"); + m_os_trace_get_type = (uint8_t (*)(os_trace_message_t)) dlsym (RTLD_DEFAULT, "os_trace_get_type"); + m_os_trace_copy_formatted_message = (char *(*)(os_trace_message_t)) dlsym (RTLD_DEFAULT, "os_trace_copy_formatted_message"); + m_os_activity_for_thread = (os_activity_t (*)(os_activity_process_t, uint64_t)) dlsym (RTLD_DEFAULT, "os_activity_for_thread"); + m_os_activity_for_task_thread = (os_activity_t (*)(task_t, uint64_t)) dlsym (RTLD_DEFAULT, "os_activity_for_task_thread"); + m_os_activity_messages_for_thread = (os_trace_message_list_t (*) (os_activity_process_t process, os_activity_t activity, uint64_t thread_id)) dlsym (RTLD_DEFAULT, "os_activity_messages_for_thread"); +} + +Genealogy::ThreadActivitySP +Genealogy::GetGenealogyInfoForThread (pid_t pid, nub_thread_t tid, const MachThreadList &thread_list, task_t task, bool &timed_out) +{ + ThreadActivitySP activity; + // + // if we've timed out trying to get the activities, don't try again at this process stop. + // (else we'll need to hit the timeout for every thread we're asked about.) + // We'll try again at the next public stop. + + if (m_thread_activities.size() == 0 && m_diagnosticd_call_timed_out == false) + { + GetActivities(pid, thread_list, task); + } + std::map::const_iterator search; + search = m_thread_activities.find(tid); + if (search != m_thread_activities.end()) + { + activity = search->second; + } + timed_out = m_diagnosticd_call_timed_out; + return activity; +} + +void +Genealogy::Clear() +{ + m_thread_activities.clear(); + m_diagnosticd_call_timed_out = false; +} + +void +Genealogy::GetActivities(pid_t pid, const MachThreadList &thread_list, task_t task) +{ + if (m_os_activity_diagnostic_for_pid != nullptr + && m_os_activity_iterate_processes != nullptr + && m_os_activity_iterate_breadcrumbs != nullptr + && m_os_activity_iterate_messages != nullptr + && m_os_activity_iterate_activities != nullptr + && m_os_trace_get_type != nullptr + && m_os_trace_copy_formatted_message != nullptr + && (m_os_activity_for_thread != nullptr || m_os_activity_for_task_thread != nullptr) + ) + { + __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block BreadcrumbList breadcrumbs; + __block ActivityList activities; + __block MessageList messages; + __block std::map thread_activity_mapping; + + os_activity_diagnostic_flag_t flags = OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES | OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY; + if (m_os_activity_diagnostic_for_pid (pid, 0, flags, ^(os_activity_process_list_t processes, int error) + { + if (error == 0) + { + m_os_activity_iterate_processes (processes, ^bool(os_activity_process_t process_info) + { + if (pid == process_info->pid) + { + // Collect all the Breadcrumbs + m_os_activity_iterate_breadcrumbs (process_info, ^bool(os_activity_breadcrumb_t breadcrumb) + { + Breadcrumb bc; + bc.breadcrumb_id = breadcrumb->breadcrumb_id; + bc.activity_id = breadcrumb->activity_id; + bc.timestamp = breadcrumb->timestamp; + if (breadcrumb->name) + bc.name = breadcrumb->name; + breadcrumbs.push_back (bc); + return true; + }); + + // Collect all the Activites + m_os_activity_iterate_activities (process_info->activities, process_info, ^bool(os_activity_entry_t activity) + { + Activity ac; + ac.activity_start = activity->activity_start; + ac.activity_id = activity->activity_id; + ac.parent_id = activity->parent_id; + if (activity->activity_name) + ac.activity_name = activity->activity_name; + if (activity->reason) + ac.reason = activity->reason; + activities.push_back (ac); + return true; + }); + + + // Collect all the Messages -- messages not associated with any thread + m_os_activity_iterate_messages (process_info->messages, process_info, ^bool(os_trace_message_t trace_msg) + { + Message msg; + msg.timestamp = trace_msg->timestamp; + msg.trace_id = trace_msg->trace_id; + msg.thread = trace_msg->thread; + msg.type = m_os_trace_get_type (trace_msg); + msg.activity_id = 0; + if (trace_msg->image_uuid && trace_msg->image_path) + { + ProcessExecutableInfoSP process_info_sp (new ProcessExecutableInfo()); + uuid_copy (process_info_sp->image_uuid, trace_msg->image_uuid); + process_info_sp->image_path = trace_msg->image_path; + msg.process_info_index = AddProcessExecutableInfo (process_info_sp); + } + const char *message_text = m_os_trace_copy_formatted_message (trace_msg); + if (message_text) + msg.message = message_text; + messages.push_back (msg); + return true; + }); + + // Discover which activities are said to be running on threads currently + const nub_size_t num_threads = thread_list.NumThreads(); + for (nub_size_t i = 0; i < num_threads; ++i) + { + nub_thread_t thread_id = thread_list.ThreadIDAtIndex(i); + os_activity_t act = 0; + if (m_os_activity_for_task_thread != nullptr) + { + act = m_os_activity_for_task_thread (task, thread_id); + } + else if (m_os_activity_for_thread != nullptr) + { + act = m_os_activity_for_thread (process_info, thread_id); + } + if (act != 0) + thread_activity_mapping[thread_id] = act; + } + + // Collect all Messages -- messages associated with a thread + + // When there's no genealogy information, an early version of os_activity_messages_for_thread + // can crash in rare circumstances. Check to see if this process has any activities before + // making the call to get messages. + if (process_info->activities != nullptr && thread_activity_mapping.size() > 0) + { + std::map::const_iterator iter; + for (iter = thread_activity_mapping.begin(); iter != thread_activity_mapping.end(); ++iter) + { + nub_thread_t thread_id = iter->first; + os_activity_t act = iter->second; + os_trace_message_list_t this_thread_messages = m_os_activity_messages_for_thread (process_info, act, thread_id); + m_os_activity_iterate_messages (this_thread_messages, process_info, ^bool(os_trace_message_t trace_msg) + { + Message msg; + msg.timestamp = trace_msg->timestamp; + msg.trace_id = trace_msg->trace_id; + msg.thread = trace_msg->thread; + msg.type = m_os_trace_get_type (trace_msg); + msg.activity_id = act; + if (trace_msg->image_uuid && trace_msg->image_path) + { + ProcessExecutableInfoSP process_info_sp (new ProcessExecutableInfo()); + uuid_copy (process_info_sp->image_uuid, trace_msg->image_uuid); + process_info_sp->image_path = trace_msg->image_path; + msg.process_info_index = AddProcessExecutableInfo (process_info_sp); + } + const char *message_text = m_os_trace_copy_formatted_message (trace_msg); + if (message_text) + msg.message = message_text; + messages.push_back (msg); + return true; + }); + } + } + } + return true; + }); + } + dispatch_semaphore_signal(semaphore); + }) == true) + { + // Wait for the diagnosticd xpc calls to all finish up -- or half a second to elapse. + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2); + bool success = dispatch_semaphore_wait(semaphore, timeout) == 0; + if (!success) + { + m_diagnosticd_call_timed_out = true; + return; + } + } + + // breadcrumbs, activities, and messages have all now been filled in. + + std::map::const_iterator iter; + for (iter = thread_activity_mapping.begin(); iter != thread_activity_mapping.end(); ++iter) + { + nub_thread_t thread_id = iter->first; + uint64_t activity_id = iter->second; + ActivityList::const_iterator activity_search; + for (activity_search = activities.begin(); activity_search != activities.end(); ++activity_search) + { + if (activity_search->activity_id == activity_id) + { + ThreadActivitySP thread_activity_sp (new ThreadActivity()); + thread_activity_sp->current_activity = *activity_search; + + BreadcrumbList::const_iterator breadcrumb_search; + for (breadcrumb_search = breadcrumbs.begin(); breadcrumb_search != breadcrumbs.end(); ++breadcrumb_search) + { + if (breadcrumb_search->activity_id == activity_id) + { + thread_activity_sp->breadcrumbs.push_back (*breadcrumb_search); + } + } + MessageList::const_iterator message_search; + for (message_search = messages.begin(); message_search != messages.end(); ++message_search) + { + if (message_search->thread == thread_id) + { + thread_activity_sp->messages.push_back (*message_search); + } + } + + m_thread_activities[thread_id] = thread_activity_sp; + break; + } + } + } + } +} + +uint32_t +Genealogy::AddProcessExecutableInfo (ProcessExecutableInfoSP process_exe_info) +{ + const uint32_t info_size = static_cast(m_process_executable_infos.size()); + for (uint32_t idx = 0; idx < info_size; ++idx) + { + if (uuid_compare (m_process_executable_infos[idx]->image_uuid, process_exe_info->image_uuid) == 0) + { + return idx + 1; + } + } + m_process_executable_infos.push_back (process_exe_info); + return info_size + 1; +} + +Genealogy::ProcessExecutableInfoSP +Genealogy::GetProcessExecutableInfosAtIndex(size_t idx) +{ + ProcessExecutableInfoSP info_sp; + if (idx > 0) + { + idx--; + if (idx <= m_process_executable_infos.size()) + { + info_sp = m_process_executable_infos[idx]; + } + } + return info_sp; +} + diff --git a/tools/debugserver/source/MacOSX/Genealogy.h b/tools/debugserver/source/MacOSX/Genealogy.h new file mode 100644 index 000000000000..d39145a06f29 --- /dev/null +++ b/tools/debugserver/source/MacOSX/Genealogy.h @@ -0,0 +1,116 @@ +//===-- Activity.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __Genealogy_h__ +#define __Genealogy_h__ + +#include +#include +#include +#include +#include + +#include "GenealogySPI.h" +#include "MachThreadList.h" + +class Genealogy +{ +public: + + Genealogy (); + + ~Genealogy () + { + } + + void + Clear(); + + struct Breadcrumb + { + uint32_t breadcrumb_id; + uint64_t activity_id; + uint64_t timestamp; + std::string name; + }; + + struct Activity + { + uint64_t activity_start; + uint64_t activity_id; + uint64_t parent_id; + std::string activity_name; + std::string reason; + }; + + struct Message + { + uint64_t timestamp; + uint64_t activity_id; + uint64_t trace_id; + uint64_t thread; + uint8_t type; // OS_TRACE_TYPE_RELEASE, OS_TRACE_TYPE_DEBUG, OS_TRACE_TYPE_ERROR, OS_TRACE_TYPE_FAULT + uint32_t process_info_index; // index # of the image uuid/file path, 0 means unknown + std::string message; + }; + + typedef std::vector MessageList; + typedef std::vector BreadcrumbList; + typedef std::vector ActivityList; + + struct ThreadActivity + { + Activity current_activity; + MessageList messages; + BreadcrumbList breadcrumbs; // should be 0 or 1 breadcrumbs; no more than 1 BC for any given activity + }; + + typedef std::shared_ptr ThreadActivitySP; + + ThreadActivitySP + GetGenealogyInfoForThread (pid_t pid, nub_thread_t tid, const MachThreadList &thread_list, task_t task, bool &timed_out); + + struct ProcessExecutableInfo + { + std::string image_path; + uuid_t image_uuid; + }; + + typedef std::shared_ptr ProcessExecutableInfoSP; + + ProcessExecutableInfoSP + GetProcessExecutableInfosAtIndex(size_t idx); + + uint32_t + AddProcessExecutableInfo(ProcessExecutableInfoSP process_exe_info); + +private: + + void + GetActivities(pid_t pid, const MachThreadList &thread_list, task_t task); + + // the spi we need to call into libtrace - look them up via dlsym at runtime + bool (*m_os_activity_diagnostic_for_pid) (pid_t pid, os_activity_t activity, uint32_t flags, os_diagnostic_block_t block); + void (*m_os_activity_iterate_processes) (os_activity_process_list_t processes, bool (^iterator)(os_activity_process_t process_info)); + void (*m_os_activity_iterate_breadcrumbs) (os_activity_process_t process_info, bool (^iterator)(os_activity_breadcrumb_t breadcrumb)); + void (*m_os_activity_iterate_messages) (os_trace_message_list_t messages, os_activity_process_t process_info, bool (^iterator)(os_trace_message_t tracemsg)); + void (*m_os_activity_iterate_activities) (os_activity_list_t activities, os_activity_process_t process_info, bool (^iterator)(os_activity_entry_t activity)); + uint8_t (*m_os_trace_get_type) (os_trace_message_t trace_msg); + char * (*m_os_trace_copy_formatted_message) (os_trace_message_t trace_msg); + os_activity_t (*m_os_activity_for_thread) (os_activity_process_t process, uint64_t thread_id); + os_activity_t (*m_os_activity_for_task_thread) (task_t target, uint64_t thread_id); + os_trace_message_list_t (*m_os_activity_messages_for_thread) (os_activity_process_t process, os_activity_t activity, uint64_t thread_id); + + + std::map m_thread_activities; + std::vector m_process_executable_infos; + bool m_diagnosticd_call_timed_out; +}; + +#endif // __Genealogy_h__ diff --git a/tools/debugserver/source/MacOSX/GenealogySPI.h b/tools/debugserver/source/MacOSX/GenealogySPI.h new file mode 100644 index 000000000000..f84e930e8725 --- /dev/null +++ b/tools/debugserver/source/MacOSX/GenealogySPI.h @@ -0,0 +1,96 @@ +//===-- ActivitySPI.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +//===----------------------------------------------------------------------===// + +#ifndef __GenealogySPI_h__ +#define __GenealogySPI_h__ + +#include + +typedef void *os_activity_process_list_t; +typedef void *os_activity_list_t; +typedef void *os_trace_message_list_t; +typedef struct os_activity_watch_s *os_activity_watch_t; +typedef uint64_t os_activity_t; + +struct os_activity_breadcrumb_s { + uint32_t breadcrumb_id; + uint64_t activity_id; + uint64_t timestamp; + const char *name; +}; + +typedef struct os_activity_breadcrumb_s *os_activity_breadcrumb_t; + +typedef struct os_trace_message_s { + uint64_t trace_id; + uint64_t thread; + uint64_t timestamp; + uint32_t offset; + xpc_object_t __unsafe_unretained payload; + const uint8_t *image_uuid; + const char *image_path; + const char *format; + const void *buffer; + size_t bufferLen; +} *os_trace_message_t; + +typedef struct os_activity_process_s { + os_activity_process_list_t child_procs; + os_trace_message_list_t messages; + os_activity_list_t activities; + void *breadcrumbs; + uint64_t proc_id; + const uint8_t *image_uuid; + const char *image_path; + pid_t pid; +} *os_activity_process_t; + +typedef struct os_activity_entry_s { + uint64_t activity_start; + os_activity_t activity_id; + os_activity_t parent_id; + const char *activity_name; + const char *reason; + os_trace_message_list_t messages; +} *os_activity_entry_t; + +enum +{ + OS_ACTIVITY_DIAGNOSTIC_DEFAULT = 0x00000000, + OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY = 0x00000001, + OS_ACTIVITY_DIAGNOSTIC_SKIP_DECODE = 0x00000002, + OS_ACTIVITY_DIAGNOSTIC_FLATTENED = 0x00000004, + OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES = 0x00000008, + OS_ACTIVITY_DIAGNOSTIC_MAX = 0x0000000f +}; +typedef uint32_t os_activity_diagnostic_flag_t; + +enum +{ + OS_ACTIVITY_WATCH_DEFAULT = 0x00000000, + OS_ACTIVITY_WATCH_PROCESS_ONLY = 0x00000001, + OS_ACTIVITY_WATCH_SKIP_DECODE = 0x00000002, + OS_ACTIVITY_WATCH_PAYLOAD = 0x00000004, + OS_ACTIVITY_WATCH_ERRORS = 0x00000008, + OS_ACTIVITY_WATCH_FAULTS = 0x00000010, + OS_ACTIVITY_WATCH_MAX = 0x0000001f +}; +typedef uint32_t os_activity_watch_flag_t; + +// Return values from os_trace_get_type() +#define OS_TRACE_TYPE_RELEASE (1u << 0) +#define OS_TRACE_TYPE_DEBUG (1u << 1) +#define OS_TRACE_TYPE_ERROR ((1u << 6) | (1u << 0)) +#define OS_TRACE_TYPE_FAULT ((1u << 7) | (1u << 6) | (1u << 0)) + + +typedef void (^os_activity_watch_block_t)(os_activity_watch_t watch, os_activity_process_t process_info, bool canceled); +typedef void (^os_diagnostic_block_t)(os_activity_process_list_t processes, int error); + +#endif + diff --git a/tools/debugserver/source/MacOSX/HasAVX.h b/tools/debugserver/source/MacOSX/HasAVX.h new file mode 100644 index 000000000000..c7a50fa20b3e --- /dev/null +++ b/tools/debugserver/source/MacOSX/HasAVX.h @@ -0,0 +1,27 @@ +//===-- HasAVX.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef HasAVX_h +#define HasAVX_h + +#if defined (__i386__) || defined (__x86_64__) + +#ifdef __cplusplus +extern "C" { +#endif + +int HasAVX (); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/tools/debugserver/source/MacOSX/HasAVX.s b/tools/debugserver/source/MacOSX/HasAVX.s new file mode 100644 index 000000000000..b66ccf14dbee --- /dev/null +++ b/tools/debugserver/source/MacOSX/HasAVX.s @@ -0,0 +1,50 @@ +//===-- HasAVX.s ---------------------------------------*- x86 Assembly -*-===// +// +// 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__) + +.globl _HasAVX + +_HasAVX: +#if defined (__x86_64__) + pushq %rbp + movq %rsp, %rbp + pushq %rbx +#else + pushl %ebp + movl %esp, %ebp + pushl %ebx +#endif + mov $1, %eax + cpuid // clobbers ebx + and $0x018000000, %ecx + cmp $0x018000000, %ecx + jne not_supported + mov $0, %ecx +.byte 0x0f, 0x01, 0xd0 // xgetbv, for those assemblers that don't know it + and $0x06, %eax + cmp $0x06, %eax + jne not_supported + mov $1, %eax + jmp done +not_supported: + mov $0, %eax +done: +#if defined (__x86_64__) + popq %rbx + movq %rbp, %rsp + popq %rbp +#else + popl %ebx + movl %ebp, %esp + popl %ebp +#endif + ret // return + +#endif diff --git a/tools/debugserver/source/MacOSX/MachException.cpp b/tools/debugserver/source/MacOSX/MachException.cpp new file mode 100644 index 000000000000..b7245796ae4c --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachException.cpp @@ -0,0 +1,582 @@ +//===-- 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 = 0x%llx, exc_data_count = %d)", + __FUNCTION__, + exc_port, + exc_type, MachException::Name(exc_type), + (uint64_t)exc_data, + exc_data_count); + } + return KERN_FAILURE; +} + +extern "C" +kern_return_t +catch_mach_exception_raise_state_identity +( + mach_port_t exc_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exc_type, + mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count, + int * flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +) +{ + 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] = { 0x%llx, 0x%llx })", + __FUNCTION__, + exc_port, + thread_port, + task_port, + exc_type, MachException::Name(exc_type), + exc_data_count, + (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD), + (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD)); + } + mach_port_deallocate (mach_task_self (), task_port); + 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] = { 0x%llx, 0x%llx })", + __FUNCTION__, + exc_port, + thread_port, + task_port, + exc_type, MachException::Name(exc_type), + exc_data_count, + (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD), + (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD)); + } + + if (task_port == g_message->task_port) + { + g_message->task_port = task_port; + g_message->thread_port = thread_port; + g_message->exc_type = exc_type; + g_message->exc_data.resize(exc_data_count); + ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t)); + return KERN_SUCCESS; + } + return KERN_FAILURE; +} + + +void +MachException::Message::Dump() const +{ + DNBLogThreadedIf(LOG_EXCEPTIONS, + " exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = 0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = 0x%8.8x } ", + 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.8x size = 0x%8.8x remote-port = 0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = 0x%8.8x }", + reply_msg.hdr.msgh_bits, + reply_msg.hdr.msgh_size, + reply_msg.hdr.msgh_remote_port, + reply_msg.hdr.msgh_local_port, + reply_msg.hdr.msgh_reserved, + reply_msg.hdr.msgh_id); + + state.Dump(); +} + +bool +MachException::Data::GetStopInfo(struct DNBThreadStopInfo *stop_info) const +{ + // Zero out the structure. + memset(stop_info, 0, sizeof(struct DNBThreadStopInfo)); + + if (exc_type == 0) + { + stop_info->reason = eStopTypeInvalid; + return true; + } + + // 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); + 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[%llu] = {", (uint64_t)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, "0x%llx%c", (uint64_t)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 = 0, rcv_size = %llu, rcv_name = %#x, timeout = %u, notify = %#x)", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id, + options, + (uint64_t)sizeof (exc_msg.data), + port, + mach_msg_timeout, + notify_port); + } + + 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(task_t task) +{ + 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"); + state.task_port = task; + g_message = &state; + // The exc_server function is the MIG generated server handling function + // to handle messages from the kernel relating to the occurrence of an + // exception in a thread. Such messages are delivered to the exception port + // set via thread_set_exception_ports or task_set_exception_ports. When an + // exception occurs in a thread, the thread sends an exception message to + // its exception port, blocking in the kernel waiting for the receipt of a + // reply. The exc_server function performs all necessary argument handling + // for this kernel message and calls catch_exception_raise, + // catch_exception_raise_state or catch_exception_raise_state_identity, + // which should handle the exception. If the called routine returns + // KERN_SUCCESS, a reply message will be sent, allowing the thread to + // continue from the point of the exception; otherwise, no reply message + // is sent and the called routine must have dealt with the exception + // thread directly. + if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr)) + { + success = true; + } + else 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)((uintptr_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()) + { + DNBLogThreaded("error: mach_msg() returned an error when replying to a mach exception: error = %u", err.Error()); + 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[%llu]: 0x%llx", (uint64_t)idx, (uint64_t)exc_data[idx]); + } + } +} + +#define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS | \ + EXC_MASK_BAD_INSTRUCTION | \ + EXC_MASK_ARITHMETIC | \ + EXC_MASK_EMULATION | \ + EXC_MASK_SOFTWARE | \ + EXC_MASK_BREAKPOINT | \ + EXC_MASK_SYSCALL | \ + EXC_MASK_MACH_SYSCALL | \ + EXC_MASK_RPC_ALERT | \ + EXC_MASK_MACHINE) + +// Don't listen for EXC_RESOURCE, it should really get handled by the system handler. + +#ifndef EXC_RESOURCE +#define EXC_RESOURCE 11 +#endif + +#ifndef EXC_MASK_RESOURCE +#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE) +#endif + +#define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE) + +kern_return_t +MachException::PortInfo::Save (task_t task) +{ + DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Save ( task = 0x%4.4x )", task); + // Be careful to be able to have debugserver built on a newer OS than what + // it is currently running on by being able to start with all exceptions + // and back off to just what is supported on the current system + DNBError err; + + mask = LLDB_EXC_MASK; + + count = (sizeof (ports) / sizeof (ports[0])); + err = ::task_get_exception_ports (task, mask, 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, mask, count); + + if (err.Error() == KERN_INVALID_ARGUMENT && mask != PREV_EXC_MASK_ALL) + { + mask = PREV_EXC_MASK_ALL; + count = (sizeof (ports) / sizeof (ports[0])); + err = ::task_get_exception_ports (task, mask, 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, mask, count); + } + if (err.Fail()) + { + mask = 0; + 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/tools/debugserver/source/MacOSX/MachException.h b/tools/debugserver/source/MacOSX/MachException.h new file mode 100644 index 000000000000..c831479f2b6d --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachException.h @@ -0,0 +1,133 @@ +//===-- 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 + +class MachProcess; +class PThreadMutex; + +typedef union MachMessageTag +{ + mach_msg_header_t hdr; + char data[1024]; +} MachMessage; + + +class MachException +{ +public: + + struct PortInfo + { + exception_mask_t mask; // the exception mask for this device which may be a subset of EXC_MASK_ALL... + exception_mask_t masks[EXC_TYPES_COUNT]; + mach_port_t ports[EXC_TYPES_COUNT]; + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; + mach_msg_type_number_t count; + + 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 static_cast(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(task_t task); + 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/tools/debugserver/source/MacOSX/MachProcess.h b/tools/debugserver/source/MacOSX/MachProcess.h new file mode 100644 index 000000000000..3be0b2dbabb3 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachProcess.h @@ -0,0 +1,364 @@ +//===-- 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 +#include +#include +#include +#include + +#include "DNBDefs.h" +#include "DNBBreakpoint.h" +#include "DNBError.h" +#include "DNBThreadResumeActions.h" +#include "MachException.h" +#include "MachVMMemory.h" +#include "MachTask.h" +#include "MachThreadList.h" +#include "PThreadCondition.h" +#include "PThreadEvent.h" +#include "PThreadMutex.h" +#include "Genealogy.h" +#include "ThreadInfo.h" +#include "JSONGenerator.h" + +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 *working_directory, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + bool no_stdio, + nub_launch_flavor_t launch_flavor, + int disable_aslr, + const char *event_data, + DNBError &err); + + static uint32_t GetCPUTypeForLocalProcess (pid_t pid); + static pid_t ForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], MachProcess* process, DNBError &err); + static pid_t PosixSpawnChildForPTraceDebugging (const char *path, + cpu_type_t cpu_type, + char const *argv[], + char const *envp[], + const char *working_directory, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + bool no_stdio, + MachProcess* process, + int disable_aslr, + 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, nub_launch_flavor_t launch_flavor, bool success, DNBError &err_str); + static nub_process_t CheckForProcess (const void *attach_token, nub_launch_flavor_t launch_flavor); +#if defined(WITH_BKS) || defined(WITH_FBS) + pid_t BoardServiceLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err); + pid_t BoardServiceForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err); + bool BoardServiceSendEvent (const char *event, DNBError &error); +#endif + static bool GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch); +#ifdef WITH_BKS + static void BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str); +#endif // WITH_BKS +#ifdef WITH_FBS + static void FBSCleanupAfterAttach (const void *attach_token, DNBError &err_str); +#endif // WITH_FBS +#ifdef WITH_SPRINGBOARD + pid_t SBLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, DNBError &launch_err); + static pid_t SBForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err); +#endif // WITH_SPRINGBOARD + 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 Interrupt(); + bool SendEvent (const char *event, DNBError &send_err); + 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 + //---------------------------------------------------------------------- + DNBBreakpoint * CreateBreakpoint (nub_addr_t addr, nub_size_t length, bool hardware); + bool DisableBreakpoint (nub_addr_t addr, bool remove); + void DisableAllBreakpoints (bool remove); + bool EnableBreakpoint (nub_addr_t addr); + DNBBreakpointList& Breakpoints() { return m_breakpoints; } + const DNBBreakpointList& Breakpoints() const { return m_breakpoints; } + + //---------------------------------------------------------------------- + // Watchpoint functions + //---------------------------------------------------------------------- + DNBBreakpoint * CreateWatchpoint (nub_addr_t addr, nub_size_t length, uint32_t watch_type, bool hardware); + bool DisableWatchpoint (nub_addr_t addr, bool remove); + void DisableAllWatchpoints (bool remove); + bool EnableWatchpoint (nub_addr_t addr); + uint32_t GetNumSupportedHardwareWatchpoints () 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); + task_t ExceptionMessageBundleComplete (); + void SharedLibrariesUpdated (); + nub_size_t CopyImageInfos (struct DNBExecutableImageInfo **image_infos, bool only_changed); + + //---------------------------------------------------------------------- + // Profile functions + //---------------------------------------------------------------------- + void SetEnableAsyncProfiling (bool enable, uint64_t internal_usec, DNBProfileDataScanType scan_type); + bool IsProfilingEnabled () { return m_profile_enabled; } + useconds_t ProfileInterval () { return m_profile_interval_usec; } + bool StartProfileThread (); + static void * ProfileThread (void *arg); + void SignalAsyncProfileData (const char *info); + size_t GetAsyncProfileData (char *buf, size_t buf_size); + + //---------------------------------------------------------------------- + // 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; + nub_bool_t SyncThreadState (nub_thread_t tid); + const char * ThreadGetName (nub_thread_t tid); + nub_state_t ThreadGetState (nub_thread_t tid); + ThreadInfo::QoS GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index); + nub_addr_t GetPThreadT (nub_thread_t tid); + nub_addr_t GetDispatchQueueT (nub_thread_t tid); + nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); + JSONGenerator::ObjectSP GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count); + + nub_size_t GetNumThreads () const; + nub_thread_t GetThreadAtIndex (nub_size_t thread_idx) const; + nub_thread_t GetCurrentThread (); + nub_thread_t GetCurrentThreadMachPort (); + nub_thread_t SetCurrentThread (nub_thread_t tid); + MachThreadList & GetThreadList() { return m_thread_list; } + bool GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info); + void DumpThreadStoppedReason(nub_thread_t tid) const; + const char * GetThreadInfo (nub_thread_t tid) const; + + nub_thread_t GetThreadIDForMachPortNumber (thread_t mach_port_number) const; + + uint32_t GetCPUType (); + 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; + } + + 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); + } + const char * GetExitInfo () + { + return m_exit_info.c_str(); + } + + void SetExitInfo (const char *info); + + 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; } + bool ProcessUsingBackBoard() const { return (m_flags & eMachProcessFlagsUsingBKS) != 0; } + + Genealogy::ThreadActivitySP GetGenealogyInfoForThread (nub_thread_t tid, bool &timed_out); + + Genealogy::ProcessExecutableInfoSP GetGenealogyImageInfo (size_t idx); + + DNBProfileDataScanType GetProfileScanType () { return m_profile_scan_type; } + +private: + enum + { + eMachProcessFlagsNone = 0, + eMachProcessFlagsAttached = (1 << 0), + eMachProcessFlagsUsingSBS = (1 << 1), + eMachProcessFlagsUsingBKS = (1 << 2), + eMachProcessFlagsUsingFBS = (1 << 3) + }; + void Clear (bool detaching = false); + void ReplyToAllExceptions (); + void PrivateResume (); + + uint32_t Flags () const { return m_flags; } + nub_state_t DoSIGSTOP (bool clear_bps_and_wps, bool allow_running, uint32_t *thread_idx_ptr); + + pid_t m_pid; // Process ID of child process + cpu_type_t m_cpu_type; // The CPU type of this 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 + std::string m_exit_info; // Any extra info that we may have about the exit + 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; + + bool m_profile_enabled; // A flag to indicate if profiling is enabled + useconds_t m_profile_interval_usec; // If enable, the profiling interval in microseconds + DNBProfileDataScanType m_profile_scan_type; // Indicates what needs to be profiled + pthread_t m_profile_thread; // Thread ID for the thread that profiles the inferior + PThreadMutex m_profile_data_mutex; // Multithreaded protection for profile info data + std::vector m_profile_data; // Profile data, must be protected by m_profile_data_mutex + + DNBThreadResumeActions m_thread_actions; // The thread actions for the current MachProcess::Resume() call + 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_thread_list; // A list of threads that is maintained/updated after each stop + Genealogy m_activities; // A list of activities that is updated after every stop lazily + 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 + PThreadEvent m_private_events; // Used to coordinate running and stopping the process without affecting m_events + 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; + std::string m_bundle_id; // If we are a SB or BKS process, this will be our bundle ID. + int m_sent_interrupt_signo; // When we call MachProcess::Interrupt(), we want to send a single signal + // to the inferior and only send the signal if we aren't already stopped. + // If we end up sending a signal to stop the process we store it until we + // receive an exception with this signal. This helps us to verify we got + // the signal that interrupted the process. We might stop due to another + // reason after an interrupt signal is sent, so this helps us ensure that + // we don't report a spurious stop on the next resume. + int m_auto_resume_signo; // If we resume the process and still haven't received our interrupt signal + // acknownledgement, we will shortly after the next resume. We store the + // interrupt signal in this variable so when we get the interrupt signal + // as the sole reason for the process being stopped, we can auto resume + // the process. + bool m_did_exec; +}; + + +#endif // __MachProcess_h__ diff --git a/tools/debugserver/source/MacOSX/MachProcess.mm b/tools/debugserver/source/MacOSX/MachProcess.mm new file mode 100644 index 000000000000..b9e06307a4aa --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachProcess.mm @@ -0,0 +1,3685 @@ +//===-- 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 +#include +#include +#include +#include +#include +#include "MacOSX/CFUtils.h" +#include "SysSignal.h" + +#include +#include + +#import + +#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" + +#ifdef WITH_SPRINGBOARD + +#include +#include +#include + +static bool +IsSBProcess (nub_process_t pid) +{ + CFReleaser appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid)); + return appIdsForPID.get() != NULL; +} + +#endif // WITH_SPRINGBOARD + +#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS) +// 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); +#endif + +#if defined(WITH_BKS) || defined(WITH_FBS) +#import +static const int OPEN_APPLICATION_TIMEOUT_ERROR = 111; +typedef void (*SetErrorFunction) (NSInteger, DNBError &); +typedef bool (*CallOpenApplicationFunction) (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid); +// This function runs the BKSSystemService (or FBSSystemService) method openApplication:options:clientPort:withResult, +// messaging the app passed in bundleIDNSStr. +// The function should be run inside of an NSAutoReleasePool. +// +// It will use the "options" dictionary passed in, and fill the error passed in if there is an error. +// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID. +// If bundleIDNSStr is NULL, then the system application will be messaged. + +template +static bool +CallBoardSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid) +{ + // Now make our systemService: + OpenFlavor *system_service = [[OpenFlavor alloc] init]; + + if (bundleIDNSStr == nil) + { + bundleIDNSStr = [system_service systemApplicationBundleIdentifier]; + if (bundleIDNSStr == nil) + { + // Okay, no system app... + error.SetErrorString("No system application to message."); + return false; + } + } + + mach_port_t client_port = [system_service createClientPort]; + __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block ErrorFlavor open_app_error = no_error_enum_value; + bool wants_pid = (return_pid != NULL); + __block pid_t pid_in_block; + + const char *cstr = [bundleIDNSStr UTF8String]; + if (!cstr) + cstr = ""; + + DNBLog ("About to launch process for bundle ID: %s", cstr); + [system_service openApplication: bundleIDNSStr + options: options + clientPort: client_port + withResult: ^(NSError *bks_error) + { + // The system service will cleanup the client port we created for us. + if (bks_error) + open_app_error = (ErrorFlavor)[bks_error code]; + + if (open_app_error == no_error_enum_value) + { + if (wants_pid) + { + pid_in_block = [system_service pidForApplication: bundleIDNSStr]; + DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block); + DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block); + } + else + DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success."); + } + else + { + const char *error_str = [(NSString *)[bks_error localizedDescription] UTF8String]; + DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%ld).", + error_str ? error_str : "", + open_app_error); + // REMOVE ME + DNBLogError ("In completion handler for send event, got error \"%s\"(%ld).", + error_str ? error_str : "", + open_app_error); + } + + [system_service release]; + dispatch_semaphore_signal(semaphore); + } + + ]; + + const uint32_t timeout_secs = 9; + + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); + + long success = dispatch_semaphore_wait(semaphore, timeout) == 0; + + dispatch_release(semaphore); + + if (!success) +{ + DNBLogError("timed out trying to send openApplication to %s.", cstr); + error.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); + error.SetErrorString ("timed out trying to launch app"); + } + else if (open_app_error != no_error_enum_value) + { + error_function (open_app_error, error); + DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error); + success = false; + } + else if (wants_pid) + { + *return_pid = pid_in_block; + DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid); +} + + + return success; +} +#endif + +#ifdef WITH_BKS +#import +extern "C" +{ +#import +#import +#import +} + +static bool +IsBKSProcess (nub_process_t pid) +{ + BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init]; + BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid]; + return app_state != BKSApplicationStateUnknown; +} + +static void +SetBKSError (NSInteger error_code, DNBError &error) +{ + error.SetError (error_code, DNBError::BackBoard); + NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString((BKSOpenApplicationErrorCode) error_code); + const char *err_str = NULL; + if (err_nsstr == NULL) + err_str = "unknown BKS error"; + else + { + err_str = [err_nsstr UTF8String]; + if (err_str == NULL) + err_str = "unknown BKS error"; + } + error.SetErrorString(err_str); +} + +static bool +BKSAddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error) +{ + if (strcmp (event_data, "BackgroundContentFetching") == 0) + { + DNBLog("Setting ActivateForEvent key in options dictionary."); + NSDictionary *event_details = [NSDictionary dictionary]; + NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching]; + [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent]; + return true; + } + else + { + DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data); + option_error.SetErrorString("Unrecognized event data."); + return false; + } + +} + +static NSMutableDictionary * +BKSCreateOptionsDictionary(const char *app_bundle_path, NSMutableArray *launch_argv, NSMutableDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, const char *event_data) +{ + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + if (launch_argv != nil) + [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments]; + if (launch_envp != nil) + [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment]; + + [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath]; + [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger]; + if (disable_aslr) + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR]; + + // That will go in the overall dictionary: + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; + // And there are some other options at the top level in this dictionary: + [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice]; + + DNBError error; + BKSAddEventDataToOptions (options, event_data, error); + + return options; +} + +static CallOpenApplicationFunction BKSCallOpenApplicationFunction = CallBoardSystemServiceOpenApplication; +#endif // WITH_BKS + +#ifdef WITH_FBS +#import +extern "C" +{ +#import +#import +#import +#import +#import +} + +#ifdef WITH_BKS +static bool +IsFBSProcess (nub_process_t pid) +{ + BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init]; + BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid]; + return app_state != BKSApplicationStateUnknown; +} +#else +static bool +IsFBSProcess (nub_process_t pid) +{ + // FIXME: What is the FBS equivalent of BKSApplicationStateMonitor + return true; +} +#endif + +static void +SetFBSError (NSInteger error_code, DNBError &error) +{ + error.SetError ((DNBError::ValueType) error_code, DNBError::FrontBoard); + NSString *err_nsstr = ::FBSOpenApplicationErrorCodeToString((FBSOpenApplicationErrorCode) error_code); + const char *err_str = NULL; + if (err_nsstr == NULL) + err_str = "unknown FBS error"; + else + { + err_str = [err_nsstr UTF8String]; + if (err_str == NULL) + err_str = "unknown FBS error"; + } + error.SetErrorString(err_str); +} + +static bool +FBSAddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error) +{ + if (strcmp (event_data, "BackgroundContentFetching") == 0) + { + DNBLog("Setting ActivateForEvent key in options dictionary."); + NSDictionary *event_details = [NSDictionary dictionary]; + NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:FBSActivateForEventOptionTypeBackgroundContentFetching]; + [options setObject: event_dictionary forKey: FBSOpenApplicationOptionKeyActivateForEvent]; + return true; + } + else + { + DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data); + option_error.SetErrorString("Unrecognized event data."); + return false; + } + +} + +static NSMutableDictionary * +FBSCreateOptionsDictionary(const char *app_bundle_path, NSMutableArray *launch_argv, NSDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, const char *event_data) +{ + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + + if (launch_argv != nil) + [debug_options setObject: launch_argv forKey: FBSDebugOptionKeyArguments]; + if (launch_envp != nil) + [debug_options setObject: launch_envp forKey: FBSDebugOptionKeyEnvironment]; + + [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardOutPath]; + [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardErrorPath]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyWaitForDebugger]; + if (disable_aslr) + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyDisableASLR]; + + // That will go in the overall dictionary: + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions]; + // And there are some other options at the top level in this dictionary: + [options setObject: [NSNumber numberWithBool: YES] forKey: FBSOpenApplicationOptionKeyUnlockDevice]; + + // We have to get the "sequence ID & UUID" for this app bundle path and send them to FBS: + + NSURL *app_bundle_url = [NSURL fileURLWithPath: [NSString stringWithUTF8String: app_bundle_path] isDirectory: YES]; + LSApplicationProxy *app_proxy = [LSApplicationProxy applicationProxyForBundleURL: app_bundle_url]; + if (app_proxy) + { + DNBLog("Sending AppProxy info: sequence no: %lu, GUID: %s.", app_proxy.sequenceNumber, [app_proxy.cacheGUID.UUIDString UTF8String]); + [options setObject: [NSNumber numberWithUnsignedInteger: app_proxy.sequenceNumber] forKey: FBSOpenApplicationOptionKeyLSSequenceNumber]; + [options setObject: app_proxy.cacheGUID.UUIDString forKey: FBSOpenApplicationOptionKeyLSCacheGUID]; + } + + DNBError error; + FBSAddEventDataToOptions (options, event_data, error); + + return options; +} +static CallOpenApplicationFunction FBSCallOpenApplicationFunction = CallBoardSystemServiceOpenApplication; +#endif // WITH_FBS + +#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 + +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +#endif + +MachProcess::MachProcess() : + m_pid (0), + m_cpu_type (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_profile_enabled (false), + m_profile_interval_usec (0), + m_profile_thread (0), + m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), + m_profile_data (), + m_thread_actions (), + m_exception_messages (), + m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE), + m_thread_list (), + m_activities (), + m_state (eStateUnloaded), + m_state_mutex (PTHREAD_MUTEX_RECURSIVE), + m_events (0, kAllEventsMask), + m_private_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), + m_sent_interrupt_signo (0), + m_auto_resume_signo (0), + m_did_exec (false) +{ + 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 actually 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_thread_list.GetName(tid); +} + +nub_state_t +MachProcess::ThreadGetState(nub_thread_t tid) +{ + return m_thread_list.GetState(tid); +} + + +nub_size_t +MachProcess::GetNumThreads () const +{ + return m_thread_list.NumThreads(); +} + +nub_thread_t +MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const +{ + return m_thread_list.ThreadIDAtIndex(thread_idx); +} + +nub_thread_t +MachProcess::GetThreadIDForMachPortNumber (thread_t mach_port_number) const +{ + return m_thread_list.GetThreadIDByMachPortNumber (mach_port_number); +} + +nub_bool_t +MachProcess::SyncThreadState (nub_thread_t tid) +{ + MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid)); + if (!thread_sp) + return false; + kern_return_t kret = ::thread_abort_safely(thread_sp->MachPortNumber()); + DNBLogThreadedIf (LOG_THREAD, "thread = 0x%8.8" PRIx32 " calling thread_abort_safely (tid) => %u (GetGPRState() for stop_count = %u)", thread_sp->MachPortNumber(), kret, thread_sp->Process()->StopCount()); + + if (kret == KERN_SUCCESS) + return true; + else + return false; + +} + +ThreadInfo::QoS +MachProcess::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index) +{ + return m_thread_list.GetRequestedQoS (tid, tsd, dti_qos_class_index); +} + +nub_addr_t +MachProcess::GetPThreadT (nub_thread_t tid) +{ + return m_thread_list.GetPThreadT (tid); +} + +nub_addr_t +MachProcess::GetDispatchQueueT (nub_thread_t tid) +{ + return m_thread_list.GetDispatchQueueT (tid); +} + +nub_addr_t +MachProcess::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) +{ + return m_thread_list.GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); +} + + + +JSONGenerator::ObjectSP +MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) +{ + JSONGenerator::DictionarySP reply_sp; + + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + struct kinfo_proc processInfo; + size_t bufsize = sizeof(processInfo); + if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) + { + uint32_t pointer_size = 4; + if (processInfo.kp_proc.p_flag & P_LP64) + pointer_size = 8; + + struct segment + { + std::string name; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + uint64_t maxprot; + uint64_t initprot; + uint64_t nsects; + uint64_t flags; + }; + + struct image_info + { + uint64_t load_address; + std::string pathname; + uint64_t mod_date; + struct mach_header_64 mach_header; + std::vector segments; + uuid_t uuid; + }; + std::vector image_infos; + size_t image_infos_size = image_count * 3 * pointer_size; + + uint8_t *image_info_buf = (uint8_t *) malloc (image_infos_size); + if (image_info_buf == NULL) + { + return reply_sp; + } + if (ReadMemory (image_list_address, image_infos_size, image_info_buf) != image_infos_size) + { + return reply_sp; + } + + + //// First the image_infos array with (load addr, pathname, mod date) tuples + + + for (size_t i = 0; i < image_count; i++) + { + struct image_info info; + nub_addr_t pathname_address; + if (pointer_size == 4) + { + uint32_t load_address_32; + uint32_t pathname_address_32; + uint32_t mod_date_32; + ::memcpy (&load_address_32, image_info_buf + (i * 3 * pointer_size), 4); + ::memcpy (&pathname_address_32, image_info_buf + (i * 3 * pointer_size) + pointer_size, 4); + ::memcpy (&mod_date_32, image_info_buf + (i * 3 * pointer_size) + pointer_size + pointer_size, 4); + info.load_address = load_address_32; + info.mod_date = mod_date_32; + pathname_address = pathname_address_32; + } + else + { + uint64_t load_address_64; + uint64_t pathname_address_64; + uint64_t mod_date_64; + ::memcpy (&load_address_64, image_info_buf + (i * 3 * pointer_size), 8); + ::memcpy (&pathname_address_64, image_info_buf + (i * 3 * pointer_size) + pointer_size, 8); + ::memcpy (&mod_date_64, image_info_buf + (i * 3 * pointer_size) + pointer_size + pointer_size, 8); + info.load_address = load_address_64; + info.mod_date = mod_date_64; + pathname_address = pathname_address_64; + } + char strbuf[17]; + info.pathname = ""; + uint64_t pathname_ptr = pathname_address; + bool still_reading = true; + while (still_reading && ReadMemory (pathname_ptr, sizeof (strbuf) - 1, strbuf) == sizeof (strbuf) - 1) + { + strbuf[sizeof(strbuf) - 1] = '\0'; + info.pathname += strbuf; + pathname_ptr += sizeof (strbuf) - 1; + // Stop if we found nul byte indicating the end of the string + for (size_t i = 0; i < sizeof(strbuf) - 1; i++) + { + if (strbuf[i] == '\0') + { + still_reading = false; + break; + } + } + } + uuid_clear (info.uuid); + image_infos.push_back (info); + } + if (image_infos.size() == 0) + { + return reply_sp; + } + + + //// Second, read the mach header / load commands for all the dylibs + + + for (size_t i = 0; i < image_count; i++) + { + uint64_t load_cmds_p; + if (pointer_size == 4) + { + struct mach_header header; + if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header), &header) != sizeof (struct mach_header)) + { + return reply_sp; + } + load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header); + image_infos[i].mach_header.magic = header.magic; + image_infos[i].mach_header.cputype = header.cputype; + image_infos[i].mach_header.cpusubtype = header.cpusubtype; + image_infos[i].mach_header.filetype = header.filetype; + image_infos[i].mach_header.ncmds = header.ncmds; + image_infos[i].mach_header.sizeofcmds = header.sizeofcmds; + image_infos[i].mach_header.flags = header.flags; + } + else + { + struct mach_header_64 header; + if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header_64), &header) != sizeof (struct mach_header_64)) + { + return reply_sp; + } + load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header_64); + image_infos[i].mach_header.magic = header.magic; + image_infos[i].mach_header.cputype = header.cputype; + image_infos[i].mach_header.cpusubtype = header.cpusubtype; + image_infos[i].mach_header.filetype = header.filetype; + image_infos[i].mach_header.ncmds = header.ncmds; + image_infos[i].mach_header.sizeofcmds = header.sizeofcmds; + image_infos[i].mach_header.flags = header.flags; + } + for (uint32_t j = 0; j < image_infos[i].mach_header.ncmds; j++) + { + struct load_command lc; + if (ReadMemory (load_cmds_p, sizeof (struct load_command), &lc) != sizeof (struct load_command)) + { + return reply_sp; + } + if (lc.cmd == LC_SEGMENT) + { + struct segment_command seg; + if (ReadMemory (load_cmds_p, sizeof (struct segment_command), &seg) != sizeof (struct segment_command)) + { + return reply_sp; + } + struct segment this_seg; + char name[17]; + ::memset (name, 0, sizeof (name)); + memcpy (name, seg.segname, sizeof (seg.segname)); + this_seg.name = name; + this_seg.vmaddr = seg.vmaddr; + this_seg.vmsize = seg.vmsize; + this_seg.fileoff = seg.fileoff; + this_seg.filesize = seg.filesize; + this_seg.maxprot = seg.maxprot; + this_seg.initprot = seg.initprot; + this_seg.nsects = seg.nsects; + this_seg.flags = seg.flags; + image_infos[i].segments.push_back(this_seg); + } + if (lc.cmd == LC_SEGMENT_64) + { + struct segment_command_64 seg; + if (ReadMemory (load_cmds_p, sizeof (struct segment_command_64), &seg) != sizeof (struct segment_command_64)) + { + return reply_sp; + } + struct segment this_seg; + char name[17]; + ::memset (name, 0, sizeof (name)); + memcpy (name, seg.segname, sizeof (seg.segname)); + this_seg.name = name; + this_seg.vmaddr = seg.vmaddr; + this_seg.vmsize = seg.vmsize; + this_seg.fileoff = seg.fileoff; + this_seg.filesize = seg.filesize; + this_seg.maxprot = seg.maxprot; + this_seg.initprot = seg.initprot; + this_seg.nsects = seg.nsects; + this_seg.flags = seg.flags; + image_infos[i].segments.push_back(this_seg); + } + if (lc.cmd == LC_UUID) + { + struct uuid_command uuidcmd; + if (ReadMemory (load_cmds_p, sizeof (struct uuid_command), &uuidcmd) == sizeof (struct uuid_command)) + uuid_copy (image_infos[i].uuid, uuidcmd.uuid); + } + load_cmds_p += lc.cmdsize; + } + } + + + //// Thrid, format all of the above in the JSONGenerator object. + + + JSONGenerator::ArraySP image_infos_array_sp (new JSONGenerator::Array()); + for (size_t i = 0; i < image_count; i++) + { + JSONGenerator::DictionarySP image_info_dict_sp (new JSONGenerator::Dictionary()); + image_info_dict_sp->AddIntegerItem ("load_address", image_infos[i].load_address); + image_info_dict_sp->AddIntegerItem ("mod_date", image_infos[i].mod_date); + image_info_dict_sp->AddStringItem ("pathname", image_infos[i].pathname); + + uuid_string_t uuidstr; + uuid_unparse_upper (image_infos[i].uuid, uuidstr); + image_info_dict_sp->AddStringItem ("uuid", uuidstr); + + JSONGenerator::DictionarySP mach_header_dict_sp (new JSONGenerator::Dictionary()); + mach_header_dict_sp->AddIntegerItem ("magic", image_infos[i].mach_header.magic); + mach_header_dict_sp->AddIntegerItem ("cputype", image_infos[i].mach_header.cputype); + mach_header_dict_sp->AddIntegerItem ("cpusubtype", image_infos[i].mach_header.cpusubtype); + mach_header_dict_sp->AddIntegerItem ("filetype", image_infos[i].mach_header.filetype); + +// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them. +// mach_header_dict_sp->AddIntegerItem ("ncmds", image_infos[i].mach_header.ncmds); +// mach_header_dict_sp->AddIntegerItem ("sizeofcmds", image_infos[i].mach_header.sizeofcmds); +// mach_header_dict_sp->AddIntegerItem ("flags", image_infos[i].mach_header.flags); + image_info_dict_sp->AddItem ("mach_header", mach_header_dict_sp); + + JSONGenerator::ArraySP segments_sp (new JSONGenerator::Array()); + for (size_t j = 0; j < image_infos[i].segments.size(); j++) + { + JSONGenerator::DictionarySP segment_sp (new JSONGenerator::Dictionary()); + segment_sp->AddStringItem ("name", image_infos[i].segments[j].name); + segment_sp->AddIntegerItem ("vmaddr", image_infos[i].segments[j].vmaddr); + segment_sp->AddIntegerItem ("vmsize", image_infos[i].segments[j].vmsize); + segment_sp->AddIntegerItem ("fileoff", image_infos[i].segments[j].fileoff); + segment_sp->AddIntegerItem ("filesize", image_infos[i].segments[j].filesize); + segment_sp->AddIntegerItem ("maxprot", image_infos[i].segments[j].maxprot); + +// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them. +// segment_sp->AddIntegerItem ("initprot", image_infos[i].segments[j].initprot); +// segment_sp->AddIntegerItem ("nsects", image_infos[i].segments[j].nsects); +// segment_sp->AddIntegerItem ("flags", image_infos[i].segments[j].flags); + segments_sp->AddItem (segment_sp); + } + image_info_dict_sp->AddItem ("segments", segments_sp); + + image_infos_array_sp->AddItem (image_info_dict_sp); + } + reply_sp.reset (new JSONGenerator::Dictionary()); + reply_sp->AddItem ("images", image_infos_array_sp); + } + return reply_sp; +} + +nub_thread_t +MachProcess::GetCurrentThread () +{ + return m_thread_list.CurrentThreadID(); +} + +nub_thread_t +MachProcess::GetCurrentThreadMachPort () +{ + return m_thread_list.GetMachPortNumberByThreadID(m_thread_list.CurrentThreadID()); +} + +nub_thread_t +MachProcess::SetCurrentThread(nub_thread_t tid) +{ + return m_thread_list.SetCurrentThread(tid); +} + +bool +MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) +{ + if (m_thread_list.GetThreadStoppedReason(tid, stop_info)) + { + if (m_did_exec) + stop_info->reason = eStopTypeExec; + return true; + } + return false; +} + +void +MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const +{ + return m_thread_list.DumpThreadStoppedReason(tid); +} + +const char * +MachProcess::GetThreadInfo(nub_thread_t tid) const +{ + return m_thread_list.GetThreadInfo(tid); +} + +uint32_t +MachProcess::GetCPUType () +{ + if (m_cpu_type == 0 && m_pid != 0) + m_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid); + return m_cpu_type; +} + +const DNBRegisterSetInfo * +MachProcess::GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const +{ + MachThreadSP thread_sp (m_thread_list.GetThreadByID (tid)); + if (thread_sp) + { + DNBArchProtocol *arch = thread_sp->GetArchProtocol(); + if (arch) + return arch->GetRegisterSetInfo (num_reg_sets); + } + *num_reg_sets = 0; + return NULL; +} + +bool +MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const +{ + return m_thread_list.GetRegisterValue(tid, set, reg, value); +} + +bool +MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const +{ + return m_thread_list.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); + const nub_state_t old_state = m_state; + + if (old_state == eStateExited) + { + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring new state since current state is exited", DNBStateAsString(new_state)); + } + else if (old_state == new_state) + { + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring redundant state change...", DNBStateAsString(new_state)); + } + else + { + if (NUB_STATE_IS_STOPPED(new_state)) + event_mask = eEventProcessStoppedStateChanged; + else + event_mask = eEventProcessRunningStateChanged; + + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) upating state (previous state was %s), event_mask = 0x%8.8x", DNBStateAsString(new_state), DNBStateAsString(old_state), event_mask); + + m_state = new_state; + if (new_state == eStateStopped) + m_stop_count++; + } + } + + if (event_mask != 0) + { + m_events.SetEvents (event_mask); + m_private_events.SetEvents (event_mask); + if (event_mask == eEventProcessStoppedStateChanged) + m_private_events.ResetEvents (eEventProcessRunningStateChanged); + else + m_private_events.ResetEvents (eEventProcessStoppedStateChanged); + + // Wait for the event bit to reset if a reset ACK is requested + m_events.WaitForResetAck(event_mask); + } + +} + +void +MachProcess::Clear(bool detaching) +{ + // 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; + if (!detaching) + CloseChildFileDescriptors(); + + m_path.clear(); + m_args.clear(); + SetState(eStateUnloaded); + m_flags = eMachProcessFlagsNone; + m_stop_count = 0; + m_thread_list.Clear(); + { + PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); + m_exception_messages.clear(); + } + m_activities.Clear(); + if (m_profile_thread) + { + pthread_join(m_profile_thread, NULL); + m_profile_thread = NULL; + } +} + + +bool +MachProcess::StartSTDIOThread() +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); + // Create the thread that watches for the child STDIO + return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0; +} + +void +MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) +{ + m_profile_enabled = enable; + m_profile_interval_usec = static_cast(interval_usec); + m_profile_scan_type = scan_type; + + if (m_profile_enabled && (m_profile_thread == NULL)) + { + StartProfileThread(); + } + else if (!m_profile_enabled && m_profile_thread) + { + pthread_join(m_profile_thread, NULL); + m_profile_thread = NULL; + } +} + +bool +MachProcess::StartProfileThread() +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); + // Create the thread that profiles the inferior and reports back if enabled + return ::pthread_create (&m_profile_thread, NULL, MachProcess::ProfileThread, this) == 0; +} + + +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)) + { + m_thread_actions = thread_actions; + PrivateResume(); + return true; + } + else if (state == eStateRunning) + { + DNBLog("Resume() - task 0x%x is already running, ignoring...", m_task.TaskPort()); + return true; + } + DNBLog("Resume() - task 0x%x has state %s, can't continue...", m_task.TaskPort(), DNBStateAsString(state)); + return false; +} + +bool +MachProcess::Kill (const struct timespec *timeout_abstime) +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()"); + nub_state_t state = DoSIGSTOP(true, false, NULL); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state)); + errno = 0; + DNBLog ("Sending ptrace PT_KILL to terminate inferior process."); + ::ptrace (PT_KILL, m_pid, 0, 0); + DNBError err; + err.SetErrorToErrno(); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", m_pid, err.Error(), err.AsString()); + m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); + PrivateResume (); + + // Try and reap the process without touching our m_events since + // we want the code above this to still get the eStateExited event + const uint32_t reap_timeout_usec = 1000000; // Wait 1 second and try to reap the process + const uint32_t reap_interval_usec = 10000; // + uint32_t reap_time_elapsed; + for (reap_time_elapsed = 0; + reap_time_elapsed < reap_timeout_usec; + reap_time_elapsed += reap_interval_usec) + { + if (GetState() == eStateExited) + break; + usleep(reap_interval_usec); + } + DNBLog ("Waited %u ms for process to be reaped (state = %s)", reap_time_elapsed/1000, DNBStateAsString(GetState())); + return true; +} + +bool +MachProcess::Interrupt() +{ + nub_state_t state = GetState(); + if (IsRunning(state)) + { + if (m_sent_interrupt_signo == 0) + { + m_sent_interrupt_signo = SIGSTOP; + if (Signal (m_sent_interrupt_signo)) + { + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - sent %i signal to interrupt process", m_sent_interrupt_signo); + } + else + { + m_sent_interrupt_signo = 0; + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - failed to send %i signal to interrupt process", m_sent_interrupt_signo); + } + } + else + { + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - previously sent an interrupt signal %i that hasn't been received yet, interrupt aborted", m_sent_interrupt_signo); + } + } + else + { + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - process already stopped, no interrupt sent"); + } + return false; +} + +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) + { + // 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_private_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); + return true; + } + DNBError err(errno, DNBError::POSIX); + err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal); + return false; + +} + +bool +MachProcess::SendEvent (const char *event, DNBError &send_err) +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SendEvent (event = %s) to pid: %d", event, m_pid); + if (m_pid == INVALID_NUB_PROCESS) + return false; + // FIXME: Shouldn't we use the launch flavor we were started with? +#if defined(WITH_FBS) || defined(WITH_BKS) + return BoardServiceSendEvent (event, send_err); +#endif + return true; +} + +nub_state_t +MachProcess::DoSIGSTOP (bool clear_bps_and_wps, bool allow_running, uint32_t *thread_idx_ptr) +{ + 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; + } + + // If we already have a thread stopped due to a SIGSTOP, we don't have + // to do anything... + uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); + if (thread_idx_ptr) + *thread_idx_ptr = thread_idx; + if (thread_idx != UINT32_MAX) + return GetState(); + + // No threads were stopped with a SIGSTOP, we need to run and halt the + // process with a signal + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state)); + if (allow_running) + m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); + else + m_thread_actions = DNBThreadResumeActions (eStateSuspended, 0); + + PrivateResume (); + + // 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; + } + uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); + if (thread_idx_ptr) + *thread_idx_ptr = thread_idx; + return GetState(); +} + +bool +MachProcess::Detach() +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()"); + + uint32_t thread_idx = UINT32_MAX; + nub_state_t state = DoSIGSTOP(true, true, &thread_idx); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state)); + + { + m_thread_actions.Clear(); + m_activities.Clear(); + DNBThreadResumeAction thread_action; + thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx); + thread_action.state = eStateRunning; + thread_action.signal = -1; + thread_action.addr = INVALID_NUB_ADDRESS; + + m_thread_actions.Append (thread_action); + m_thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0); + + PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); + + ReplyToAllExceptions (); + + } + + m_task.ShutDownExcecptionThread(); + + // Detach from our process + errno = 0; + nub_process_t pid = m_pid; + int ret = ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); + DNBError err(errno, DNBError::POSIX); + if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0)) + err.LogThreaded("::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 + const bool detaching = true; + Clear(detaching); + + SetState(eStateDetached); + + return true; +} + +//---------------------------------------------------------------------- +// 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) + m_breakpoints.RemoveTrapsFromBuffer (addr, bytes_read, 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::vector bps; + + const size_t num_bps = m_breakpoints.FindBreakpointsThatOverlapRange(addr, size, bps); + if (num_bps == 0) + return m_task.WriteMemory(addr, size, buf); + + 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; + + for (size_t i=0; iIntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset); + UNUSED_IF_ASSERT_DISABLED(intersects); + assert(intersects); + assert(addr <= intersect_addr && intersect_addr < addr + size); + assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); + assert(opcode_offset + intersect_size <= bp->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 () +{ + 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 %u...", (uint32_t)std::distance(begin, pos)); + int thread_reply_signal = 0; + + nub_thread_t tid = m_thread_list.GetThreadIDByMachPortNumber (pos->state.thread_port); + const DNBThreadResumeAction *action = NULL; + if (tid != INVALID_NUB_THREAD) + { + action = m_thread_actions.GetActionForThread (tid, false); + } + + if (action) + { + thread_reply_signal = action->signal; + if (thread_reply_signal) + m_thread_actions.SetSignalHandledForThread (tid); + } + + DNBError err (pos->Reply(this, thread_reply_signal)); + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + 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 () +{ + PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); + + m_auto_resume_signo = m_sent_interrupt_signo; + if (m_auto_resume_signo) + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming (with unhandled interrupt signal %i)...", m_task.TaskPort(), m_auto_resume_signo); + else + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming...", m_task.TaskPort()); + + ReplyToAllExceptions (); +// bool stepOverBreakInstruction = step; + + // Let the thread prepare to resume and see if any threads want us to + // step over a breakpoint instruction (ProcessWillResume will modify + // the value of stepOverBreakInstruction). + m_thread_list.ProcessWillResume (this, m_thread_actions); + + // Set our state accordingly + if (m_thread_actions.NumActionsWithState(eStateStepping)) + SetState (eStateStepping); + else + SetState (eStateRunning); + + // Now resume our task. + m_task.Resume(); +} + +DNBBreakpoint * +MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware) +{ + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu, hardware = %i)", (uint64_t)addr, (uint64_t)length, hardware); + + DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); + if (bp) + bp->Retain(); + else + bp = m_breakpoints.Add(addr, length, hardware); + + if (EnableBreakpoint(addr)) + { + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, bp); + return bp; + } + else if (bp->Release() == 0) + { + m_breakpoints.Remove(addr); + } + // We failed to enable the breakpoint + return NULL; +} + +DNBBreakpoint * +MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu, flags = 0x%8.8x, hardware = %i)", (uint64_t)addr, (uint64_t)length, watch_flags, hardware); + + DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); + // since the Z packets only send an address, we can only have one watchpoint at + // an address. If there is already one, we must refuse to create another watchpoint + if (wp) + return NULL; + + wp = m_watchpoints.Add(addr, length, hardware); + wp->SetIsWatchpoint(watch_flags); + + if (EnableWatchpoint(addr)) + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, wp); + return wp; + } + else + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => FAILED", (uint64_t)addr, (uint64_t)length); + m_watchpoints.Remove(addr); + } + // We failed to enable the watchpoint + return NULL; +} + +void +MachProcess::DisableAllBreakpoints (bool remove) +{ + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); + + m_breakpoints.DisableAllBreakpoints (this); + + if (remove) + m_breakpoints.RemoveDisabled(); +} + +void +MachProcess::DisableAllWatchpoints(bool remove) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); + + m_watchpoints.DisableAllWatchpoints(this); + + if (remove) + m_watchpoints.RemoveDisabled(); +} + +bool +MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove) +{ + DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); + if (bp) + { + // After "exec" we might end up with a bunch of breakpoints that were disabled + // manually, just ignore them + if (!bp->IsEnabled()) + { + // Breakpoint might have been disabled by an exec + if (remove && bp->Release() == 0) + { + m_thread_list.NotifyBreakpointChanged(bp); + m_breakpoints.Remove(addr); + } + return true; + } + + // We have multiple references to this breakpoint, decrement the ref count + // and if it isn't zero, then return true; + if (remove && bp->Release() > 0) + return true; + + DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove); + + if (bp->IsHardware()) + { + bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint (bp); + + if (hw_disable_result == true) + { + bp->SetEnabled(false); + // Let the thread list know that a breakpoint has been modified + if (remove) + { + m_thread_list.NotifyBreakpointChanged(bp); + m_breakpoints.Remove(addr); + } + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove); + return true; + } + + return false; + } + + const nub_size_t break_op_size = bp->ByteSize(); + assert (break_op_size > 0); + const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (bp->ByteSize()); + if (break_op_size > 0) + { + // Clear a software breakpoint 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 ( addr = 0x%8.8llx, remove = %d ) memory write failed when restoring original opcode", (uint64_t)addr, remove); + } + } + else + { + DNBLogWarning("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) expected a breakpoint opcode but didn't find one.", (uint64_t)addr, remove); + // 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 ( addr = 0x%8.8llx, remove = %d ) is not enabled", (uint64_t)addr, remove); + // 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 && bp->Release() == 0) + { + m_thread_list.NotifyBreakpointChanged(bp); + m_breakpoints.Remove(addr); + } + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) => success", (uint64_t)addr, remove); + return true; + } + else + { + if (break_op_found) + DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : failed to restore original opcode", (uint64_t)addr, remove); + else + DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : opcode changed", (uint64_t)addr, remove); + } + } + 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 ( addr = 0x%8.8llx, remove = %d ) invalid breakpoint address", (uint64_t)addr, remove); + } + return false; +} + +bool +MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(addr = 0x%8.8llx, remove = %d)", __FUNCTION__, (uint64_t)addr, remove); + DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); + if (wp) + { + // If we have multiple references to a watchpoint, removing the watchpoint shouldn't clear it + if (remove && wp->Release() > 0) + return true; + + nub_addr_t addr = wp->Address(); + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove); + + if (wp->IsHardware()) + { + bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint (wp); + + if (hw_disable_result == true) + { + wp->SetEnabled(false); + if (remove) + m_watchpoints.Remove(addr); + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove); + return true; + } + } + + // TODO: clear software watchpoints if we implement them + } + else + { + DNBLogError("MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d ) invalid watchpoint ID", (uint64_t)addr, remove); + } + return false; +} + + +uint32_t +MachProcess::GetNumSupportedHardwareWatchpoints () const +{ + return m_thread_list.NumSupportedHardwareWatchpoints(); +} + +bool +MachProcess::EnableBreakpoint(nub_addr_t addr) +{ + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )", (uint64_t)addr); + DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); + if (bp) + { + if (bp->IsEnabled()) + { + DNBLogWarning("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint already enabled.", (uint64_t)addr); + return true; + } + else + { + if (bp->HardwarePreferred()) + { + bp->SetHardwareIndex(m_thread_list.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 = DNBArchProtocol::GetBreakpointOpcode (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_thread_list.NotifyBreakpointChanged(bp); + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) : SUCCESS.", (uint64_t)addr); + return true; + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint opcode verification failed.", (uint64_t)addr); + } + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory to verify breakpoint opcode.", (uint64_t)addr); + } + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to write breakpoint opcode to memory.", (uint64_t)addr); + } + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory at breakpoint address.", (uint64_t)addr); + } + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) no software breakpoint opcode for current architecture.", (uint64_t)addr); + } + } + } + return false; +} + +bool +MachProcess::EnableWatchpoint(nub_addr_t addr) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(addr = 0x%8.8llx)", (uint64_t)addr); + DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); + if (wp) + { + nub_addr_t addr = wp->Address(); + if (wp->IsEnabled()) + { + DNBLogWarning("MachProcess::EnableWatchpoint(addr = 0x%8.8llx): watchpoint already enabled.", (uint64_t)addr); + return true; + } + else + { + // Currently only try and set hardware watchpoints. + wp->SetHardwareIndex(m_thread_list.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); +} + +task_t +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: %llu exception messages.", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); + bool auto_resume = false; + if (!m_exception_messages.empty()) + { + m_did_exec = false; + // First check for any SIGTRAP and make sure we didn't exec + const task_t task = m_task.TaskPort(); + size_t i; + if (m_pid != 0) + { + bool received_interrupt = false; + uint32_t num_task_exceptions = 0; + for (i=0; i %s) ...", __FUNCTION__, (uint64_t)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[%llu]) ...", __FUNCTION__, buf, (uint64_t)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 () +{ + DNBError err; + return m_task.GetDYLDAllImageInfosAddress(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); + +#if defined (__APPLE__) + pthread_setname_np ("stdio monitoring thread"); +#endif + + // 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 available. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + 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 + ssize_t 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, ) => %zd errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %zd (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, ) => %zd errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %zd (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; +} + + +void +MachProcess::SignalAsyncProfileData (const char *info) +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%s) ...", __FUNCTION__, info); + PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex); + m_profile_data.push_back(info); + m_events.SetEvents(eEventProfileDataAvailable); + + // Wait for the event bit to reset if a reset ACK is requested + m_events.WaitForResetAck(eEventProfileDataAvailable); +} + + +size_t +MachProcess::GetAsyncProfileData (char *buf, size_t buf_size) +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size); + PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex); + if (m_profile_data.empty()) + return 0; + + size_t bytes_available = m_profile_data.front().size(); + if (bytes_available > 0) + { + if (bytes_available > buf_size) + { + memcpy(buf, m_profile_data.front().data(), buf_size); + m_profile_data.front().erase(0, buf_size); + bytes_available = buf_size; + } + else + { + memcpy(buf, m_profile_data.front().data(), bytes_available); + m_profile_data.erase(m_profile_data.begin()); + } + } + return bytes_available; +} + + +void * +MachProcess::ProfileThread(void *arg) +{ + MachProcess *proc = (MachProcess*) arg; + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg); + +#if defined (__APPLE__) + pthread_setname_np ("performance profiling thread"); +#endif + + while (proc->IsProfilingEnabled()) + { + nub_state_t state = proc->GetState(); + if (state == eStateRunning) + { + std::string data = proc->Task().GetProfileData(proc->GetProfileScanType()); + if (!data.empty()) + { + proc->SignalAsyncProfileData(data.c_str()); + } + } + else if ((state == eStateUnloaded) || (state == eStateDetached) || (state == eStateUnloaded)) + { + // Done. Get out of this thread. + break; + } + + // A simple way to set up the profile interval. We can also use select() or dispatch timer source if necessary. + usleep(proc->ProfileInterval()); + } + 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) + { + DNBError err; + // Make sure the process exists... + if (::getpgid (pid) < 0) + { + err.SetErrorToErrno(); + const char *err_cstr = 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 or BKS if the correct flag bit is set... +#if defined (WITH_FBS) || defined (WITH_BKS) + bool found_app_flavor = false; +#endif + +#if defined (WITH_FBS) + if (!found_app_flavor && IsFBSProcess (pid)) + { + found_app_flavor = true; + m_flags |= eMachProcessFlagsUsingFBS; + } +#elif defined (WITH_BKS) + if (!found_app_flavor && IsBKSProcess (pid)) + { + found_app_flavor = true; + m_flags |= eMachProcessFlagsUsingBKS; + } +#elif defined (WITH_SPRINGBOARD) + if (IsSBProcess(pid)) + m_flags |= eMachProcessFlagsUsingSBS; +#endif + if (!m_task.StartExceptionThread(err)) + { + const char *err_cstr = 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; + if (::ptrace (PT_ATTACHEXC, pid, 0, 0)) + err.SetError(errno); + else + err.Clear(); + + if (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", err.AsString()); + DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); + } + } + return INVALID_NUB_PROCESS; +} + +Genealogy::ThreadActivitySP +MachProcess::GetGenealogyInfoForThread (nub_thread_t tid, bool &timed_out) +{ + return m_activities.GetGenealogyInfoForThread (m_pid, tid, m_thread_list, m_task.TaskPort(), timed_out); +} + +Genealogy::ProcessExecutableInfoSP +MachProcess::GetGenealogyImageInfo (size_t idx) +{ + return m_activities.GetProcessExecutableInfosAtIndex (idx); +} + +bool +MachProcess::GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch) +{ + bool success = false; + +#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000) + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSOperatingSystemVersion vers = [[NSProcessInfo processInfo] operatingSystemVersion]; + if (major) + *major = vers.majorVersion; + if (minor) + *minor = vers.minorVersion; + if (patch) + *patch = vers.patchVersion; + + success = true; + + [pool drain]; +#endif + + return success; +} + +// 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 &attach_err) +{ +#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS) + // Tell SpringBoard to halt the next launch of this application on startup. + + if (!waitfor) + return NULL; + + const char *app_ext = strstr(path, ".app"); + const bool is_app = app_ext != NULL && (app_ext[4] == '\0' || app_ext[4] == '/'); + if (!is_app) + { + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, " + "we can't tell springboard to wait for launch...", + path); + return NULL; + } + +#if defined (WITH_FBS) + if (launch_flavor == eLaunchFlavorDefault) + launch_flavor = eLaunchFlavorFBS; + if (launch_flavor != eLaunchFlavorFBS) + return NULL; +#elif defined (WITH_BKS) + if (launch_flavor == eLaunchFlavorDefault) + launch_flavor = eLaunchFlavorBKS; + if (launch_flavor != eLaunchFlavorBKS) + return NULL; +#elif defined (WITH_SPRINGBOARD) + if (launch_flavor == eLaunchFlavorDefault) + launch_flavor = eLaunchFlavorSpringBoard; + if (launch_flavor != eLaunchFlavorSpringBoard) + return NULL; +#endif + + std::string app_bundle_path(path, app_ext + strlen(".app")); + + CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), attach_err); + 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; + } + +#if defined (WITH_FBS) + if (launch_flavor == eLaunchFlavorFBS) + { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *stdio_path = nil; + NSFileManager *file_manager = [NSFileManager defaultManager]; + const char *null_path = "/dev/null"; + stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)]; + + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + + DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", " + "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )", + bundleIDStr.c_str(), + null_path); + + [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardOutPath]; + [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardErrorPath]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyWaitForDebugger]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyDebugOnNextLaunch]; + + [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions]; + + FBSSystemService *system_service = [[FBSSystemService alloc] init]; + + mach_port_t client_port = [system_service createClientPort]; + __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block FBSOpenApplicationErrorCode attach_error_code = FBSOpenApplicationErrorCodeNone; + + NSString *bundleIDNSStr = (NSString *) bundleIDCFStr; + + [system_service openApplication: bundleIDNSStr + options: options + clientPort: client_port + withResult: ^(NSError *error) + { + // The system service will cleanup the client port we created for us. + if (error) + attach_error_code = (FBSOpenApplicationErrorCode)[error code]; + + [system_service release]; + dispatch_semaphore_signal(semaphore); + } + ]; + + const uint32_t timeout_secs = 9; + + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); + + long success = dispatch_semaphore_wait(semaphore, timeout) == 0; + + if (!success) + { + DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str()); + attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete."); + attach_err.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); + } + else if (attach_error_code != FBSOpenApplicationErrorCodeNone) + { + SetFBSError (attach_error_code, attach_err); + DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %ld", + bundleIDStr.c_str(), + (NSInteger) attach_error_code); + } + dispatch_release(semaphore); + [pool drain]; + } +#endif +#if defined (WITH_BKS) + if (launch_flavor == eLaunchFlavorBKS) + { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *stdio_path = nil; + NSFileManager *file_manager = [NSFileManager defaultManager]; + const char *null_path = "/dev/null"; + stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)]; + + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + + DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", " + "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )", + bundleIDStr.c_str(), + null_path); + + [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath]; + [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDebugOnNextLaunch]; + + [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; + + BKSSystemService *system_service = [[BKSSystemService alloc] init]; + + mach_port_t client_port = [system_service createClientPort]; + __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block BKSOpenApplicationErrorCode attach_error_code = BKSOpenApplicationErrorCodeNone; + + NSString *bundleIDNSStr = (NSString *) bundleIDCFStr; + + [system_service openApplication: bundleIDNSStr + options: options + clientPort: client_port + withResult: ^(NSError *error) + { + // The system service will cleanup the client port we created for us. + if (error) + attach_error_code = (BKSOpenApplicationErrorCode)[error code]; + + [system_service release]; + dispatch_semaphore_signal(semaphore); + } + ]; + + const uint32_t timeout_secs = 9; + + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); + + long success = dispatch_semaphore_wait(semaphore, timeout) == 0; + + if (!success) + { + DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str()); + attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete."); + attach_err.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); + } + else if (attach_error_code != BKSOpenApplicationErrorCodeNone) + { + SetBKSError (attach_error_code, attach_err); + DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %ld", + bundleIDStr.c_str(), + attach_error_code); + } + dispatch_release(semaphore); + [pool drain]; + } +#endif + +#if defined (WITH_SPRINGBOARD) + if (launch_flavor == eLaunchFlavorSpringBoard) + { + 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) + { + attach_err.SetError(sbs_error, DNBError::SpringBoard); + return NULL; + } + } +#endif // WITH_SPRINGBOARD + + DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch."); + return bundleIDCFStr; +# else // !(defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)) + 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, nub_launch_flavor_t launch_flavor) +{ + if (attach_token == NULL) + return INVALID_NUB_PROCESS; + +#if defined (WITH_FBS) + if (launch_flavor == eLaunchFlavorFBS) + { + NSString *bundleIDNSStr = (NSString *) attach_token; + FBSSystemService *systemService = [[FBSSystemService alloc] init]; + pid_t pid = [systemService pidForApplication: bundleIDNSStr]; + [systemService release]; + if (pid == 0) + return INVALID_NUB_PROCESS; + else + return pid; + } +#endif + +#if defined (WITH_BKS) + if (launch_flavor == eLaunchFlavorBKS) + { + NSString *bundleIDNSStr = (NSString *) attach_token; + BKSSystemService *systemService = [[BKSSystemService alloc] init]; + pid_t pid = [systemService pidForApplication: bundleIDNSStr]; + [systemService release]; + if (pid == 0) + return INVALID_NUB_PROCESS; + else + return pid; + } +#endif + +#if defined (WITH_SPRINGBOARD) + if (launch_flavor == eLaunchFlavorSpringBoard) + { + 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, nub_launch_flavor_t launch_flavor, bool success, DNBError &err_str) +{ + if (attach_token == NULL) + return; + +#if defined (WITH_FBS) + if (launch_flavor == eLaunchFlavorFBS) + { + if (!success) + { + FBSCleanupAfterAttach (attach_token, err_str); + } + CFRelease((CFStringRef) attach_token); + } +#endif + +#if defined (WITH_BKS) + + if (launch_flavor == eLaunchFlavorBKS) + { + if (!success) + { + BKSCleanupAfterAttach (attach_token, err_str); + } + CFRelease((CFStringRef) attach_token); + } +#endif + +#if defined (WITH_SPRINGBOARD) + // Tell SpringBoard to cancel the debug on next launch of this application + // if we failed to attach + if (launch_flavor == eMachProcessFlagsUsingSpringBoard) + { + 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 *working_directory, // NULL => don't change, non-NULL => set working directory for inferior to this + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + bool no_stdio, + nub_launch_flavor_t launch_flavor, + int disable_aslr, + const char *event_data, + 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, disable_aslr = %d )", __FUNCTION__, path, argv, envp, launch_flavor, disable_aslr); + + // Fork a child process for debugging + SetState(eStateLaunching); + + switch (launch_flavor) + { + case eLaunchFlavorForkExec: + m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err); + break; +#ifdef WITH_FBS + case eLaunchFlavorFBS: + { + const char *app_ext = strstr(path, ".app"); + if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + m_flags |= eMachProcessFlagsUsingFBS; + if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) + return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. + else + break; // We tried a FBS launch, but didn't succeed lets get out + } + } + break; +#endif +#ifdef WITH_BKS + case eLaunchFlavorBKS: + { + const char *app_ext = strstr(path, ".app"); + if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + m_flags |= eMachProcessFlagsUsingBKS; + if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) + return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. + else + break; // We tried a BKS launch, but didn't succeed lets get out + } + } + break; +#endif +#ifdef WITH_SPRINGBOARD + + case eLaunchFlavorSpringBoard: + { + // .../whatever.app/whatever ? + // Or .../com.apple.whatever.app/whatever -- be careful of ".app" in "com.apple.whatever" here + const char *app_ext = strstr (path, ".app/"); + if (app_ext == NULL) + { + // .../whatever.app ? + int len = strlen (path); + if (len > 5) + { + if (strcmp (path + len - 4, ".app") == 0) + { + app_ext = path + len - 4; + } + } + } + if (app_ext) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + if (SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, launch_err) != 0) + return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. + else + break; // We tried a springboard launch, but didn't succeed lets get out + } + } + break; + +#endif + + case eLaunchFlavorPosixSpawn: + m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, + DNBArchProtocol::GetArchitecture (), + argv, + envp, + working_directory, + stdin_path, + stdout_path, + stderr_path, + no_stdio, + this, + disable_aslr, + launch_err); + break; + + 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(launch_err); + if (launch_err.Fail()) + { + if (launch_err.AsString() == NULL) + launch_err.SetErrorString("unable to start the exception thread"); + DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); + ::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, + cpu_type_t cpu_type, + char const *argv[], + char const *envp[], + const char *working_directory, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + bool no_stdio, + MachProcess* process, + int disable_aslr, + DNBError& err +) +{ + posix_spawnattr_t attr; + short flags; + DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, working_dir=%s, stdin=%s, stdout=%s stderr=%s, no-stdio=%i)", + __FUNCTION__, + path, + argv, + envp, + working_directory, + stdin_path, + stdout_path, + stderr_path, + no_stdio); + + 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; + + flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + if (disable_aslr) + flags |= _POSIX_SPAWN_DISABLE_ASLR; + + sigset_t no_signals; + sigset_t all_signals; + sigemptyset (&no_signals); + sigfillset (&all_signals); + ::posix_spawnattr_setsigmask(&attr, &no_signals); + ::posix_spawnattr_setsigdefault(&attr, &all_signals); + + err.SetError( ::posix_spawnattr_setflags (&attr, flags), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" : ""); + 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.... + +#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... + if (cpu_type != 0) + { + 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 => %llu )", cpu_type, (uint64_t)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 (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL && !no_stdio) + { + pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); + if (pty_error == PseudoTerminal::success) + { + stdin_path = stdout_path = stderr_path = pty.SlaveName(); + } + } + + // if no_stdio or std paths not supplied, then route to "/dev/null". + if (no_stdio || stdin_path == NULL || stdin_path[0] == '\0') + stdin_path = "/dev/null"; + if (no_stdio || stdout_path == NULL || stdout_path[0] == '\0') + stdout_path = "/dev/null"; + if (no_stdio || stderr_path == NULL || stderr_path[0] == '\0') + stderr_path = "/dev/null"; + + err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, + STDIN_FILENO, + stdin_path, + O_RDONLY | O_NOCTTY, + 0), + DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) + err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDIN_FILENO, path='%s')", stdin_path); + + err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, + STDOUT_FILENO, + stdout_path, + O_WRONLY | O_NOCTTY | O_CREAT, + 0640), + DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) + err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDOUT_FILENO, path='%s')", stdout_path); + + err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, + STDERR_FILENO, + stderr_path, + O_WRONLY | O_NOCTTY | O_CREAT, + 0640), + DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) + err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDERR_FILENO, path='%s')", stderr_path); + + // TODO: Verify if we can set the working directory back immediately + // after the posix_spawnp call without creating a race condition??? + if (working_directory) + ::chdir (working_directory); + + 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 + { + // TODO: Verify if we can set the working directory back immediately + // after the posix_spawnp call without creating a race condition??? + if (working_directory) + ::chdir (working_directory); + + 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); + } + } + ::posix_spawnattr_destroy (&attr); + + if (pid != INVALID_NUB_PROCESS) + { + cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess (pid); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x", __FUNCTION__, pid, pid_cpu_type); + if (pid_cpu_type) + DNBArchProtocol::SetArchitecture (pid_cpu_type); + } + + 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; +} + +uint32_t +MachProcess::GetCPUTypeForLocalProcess (pid_t pid) +{ + int mib[CTL_MAXNAME]={0,}; + size_t len = CTL_MAXNAME; + if (::sysctlnametomib("sysctl.proc_cputype", mib, &len)) + return 0; + + mib[len] = pid; + len++; + + cpu_type_t cpu; + size_t cpu_len = sizeof(cpu); + if (::sysctl (mib, static_cast(len), &cpu, &cpu_len, 0, 0)) + cpu = 0; + return cpu; +} + +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. + if (::setgid (getgid ()) == 0) + { + + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::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 (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS) +// 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; +} +#endif // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS) +#ifdef WITH_SPRINGBOARD + +pid_t +MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, 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, no_stdio, 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(launch_err); + + if (launch_err.Fail()) + { + if (launch_err.AsString() == NULL) + launch_err.SetErrorString("unable to start the exception thread"); + DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); + ::ptrace (PT_KILL, m_pid, 0, 0); + m_pid = INVALID_NUB_PROCESS; + return INVALID_NUB_PROCESS; + } + + 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 + +pid_t +MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, 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; + if (!no_stdio) + { + 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; + + // This is just for logging: + std::string bundleID; + CFString::UTF8(bundleIDCFStr, bundleID); + + 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 // #ifdef WITH_SPRINGBOARD + + + +#if defined (WITH_BKS) || defined (WITH_FBS) +pid_t +MachProcess::BoardServiceLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err) +{ + DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); + + // Fork a child process for debugging + SetState(eStateLaunching); + m_pid = BoardServiceForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err); + if (m_pid != 0) + { + 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(launch_err); + + if (launch_err.Fail()) + { + if (launch_err.AsString() == NULL) + launch_err.SetErrorString("unable to start the exception thread"); + DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); + ::ptrace (PT_KILL, m_pid, 0, 0); + m_pid = INVALID_NUB_PROCESS; + return INVALID_NUB_PROCESS; + } + + 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; +} + +pid_t +MachProcess::BoardServiceForkChildForPTraceDebugging (const char *app_bundle_path, + char const *argv[], + char const *envp[], + bool no_stdio, + bool disable_aslr, + const char *event_data, + DNBError &launch_err) +{ + if (argv[0] == NULL) + return INVALID_NUB_PROCESS; + + DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, this); + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + size_t argc = 0; + // Count the number of arguments + while (argv[argc] != NULL) + argc++; + + // Enumerate the arguments + size_t first_launch_arg_idx = 1; + + NSMutableArray *launch_argv = nil; + + if (argv[first_launch_arg_idx]) + { + size_t launch_argc = argc > 0 ? argc - 1 : 0; + launch_argv = [NSMutableArray arrayWithCapacity: launch_argc]; + size_t i; + char const *arg; + NSString *launch_arg; + for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) + { + launch_arg = [NSString stringWithUTF8String: arg]; + // FIXME: Should we silently eat an argument that we can't convert into a UTF8 string? + if (launch_arg != nil) + [launch_argv addObject: launch_arg]; + else + break; + } + } + + NSMutableDictionary *launch_envp = nil; + if (envp[0]) + { + launch_envp = [[NSMutableDictionary alloc] init]; + const char *value; + int name_len; + NSString *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 = [[NSString alloc] initWithBytes: envp[i] length: name_len encoding: NSUTF8StringEncoding]; + value_string = [NSString stringWithUTF8String: value]; + [launch_envp setObject: value_string forKey: name_string]; + } + } + + NSString *stdio_path = nil; + NSFileManager *file_manager = [NSFileManager defaultManager]; + + PseudoTerminal pty; + if (!no_stdio) + { + 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 = [file_manager stringWithFileSystemRepresentation: slave_name length: strlen(slave_name)]; + } + } + } + + if (stdio_path == nil) + { + const char *null_path = "/dev/null"; + stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)]; + } + + CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err); + if (bundleIDCFStr == NULL) + { + [pool drain]; + return INVALID_NUB_PROCESS; + } + + // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: + NSString *bundleIDNSStr = (NSString *) bundleIDCFStr; + + // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: + + NSMutableDictionary *options = nullptr; + pid_t return_pid = INVALID_NUB_PROCESS; + bool success = false; + +#ifdef WITH_BKS + if (m_flags & eMachProcessFlagsUsingBKS) + { + options = BKSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, stdio_path, disable_aslr, event_data); + success = BKSCallOpenApplicationFunction (bundleIDNSStr, options, launch_err, &return_pid); + } +#endif +#ifdef WITH_FBS + if (m_flags & eMachProcessFlagsUsingFBS) + { + options = FBSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, stdio_path, disable_aslr, event_data); + success = FBSCallOpenApplicationFunction (bundleIDNSStr, options, launch_err, &return_pid); + } +#endif + + if (success) + { + int master_fd = pty.ReleaseMasterFD(); + SetChildFileDescriptors(master_fd, master_fd, master_fd); + CFString::UTF8(bundleIDCFStr, m_bundle_id); + } + + [pool drain]; + + return return_pid; +} + +bool +MachProcess::BoardServiceSendEvent (const char *event_data, DNBError &send_err) +{ + bool return_value = true; + + if (event_data == NULL || *event_data == '\0') + { + DNBLogError ("SendEvent called with NULL event data."); + send_err.SetErrorString("SendEvent called with empty event data"); + return false; + } + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (strcmp (event_data, "BackgroundApplication") == 0) + { + // This is an event I cooked up. What you actually do is foreground the system app, so: +#ifdef WITH_BKS + if (m_flags & eMachProcessFlagsUsingBKS) + { + return_value = BKSCallOpenApplicationFunction(nil, nil, send_err, NULL); + } +#endif +#ifdef WITH_FBS + if (m_flags & eMachProcessFlagsUsingFBS) + { + return_value = FBSCallOpenApplicationFunction(nil, nil, send_err, NULL); + } +#endif + if (!return_value) + { + DNBLogError ("Failed to background application, error: %s.", send_err.AsString()); + } + } + else + { + if (m_bundle_id.empty()) + { + // See if we can figure out the bundle ID for this PID: + + DNBLogError ("Tried to send event \"%s\" to a process that has no bundle ID.", event_data); + return false; + } + + NSString *bundleIDNSStr = [NSString stringWithUTF8String:m_bundle_id.c_str()]; + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + +#ifdef WITH_BKS + if (m_flags & eMachProcessFlagsUsingBKS) + { + if (!BKSAddEventDataToOptions(options, event_data, send_err)) + { + [pool drain]; + return false; + } + return_value = BKSCallOpenApplicationFunction (bundleIDNSStr, options, send_err, NULL); + DNBLogThreadedIf (LOG_PROCESS, "Called BKSCallOpenApplicationFunction to send event."); + + } +#endif +#ifdef WITH_FBS + if (m_flags & eMachProcessFlagsUsingFBS) + { + if (!FBSAddEventDataToOptions(options, event_data, send_err)) + { + [pool drain]; + return false; + } + return_value = FBSCallOpenApplicationFunction (bundleIDNSStr, options, send_err, NULL); + DNBLogThreadedIf (LOG_PROCESS, "Called FBSCallOpenApplicationFunction to send event."); + } +#endif + + if (!return_value) + { + DNBLogError ("Failed to send event: %s, error: %s.", event_data, send_err.AsString()); + } + } + + [pool drain]; + return return_value; +} +#endif // defined(WITH_BKS) || defined (WITH_FBS) + +#ifdef WITH_BKS +void +MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str) +{ + bool success; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: + NSString *bundleIDNSStr = (NSString *) attach_token; + + // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: + + // First we have the debug sub-dictionary: + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch]; + + // That will go in the overall dictionary: + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; + + success = BKSCallOpenApplicationFunction (bundleIDNSStr, options, err_str, NULL); + + if (!success) + { + DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString()); + } + + [pool drain]; +} +#endif // WITH_BKS + +#ifdef WITH_FBS +void +MachProcess::FBSCleanupAfterAttach (const void *attach_token, DNBError &err_str) +{ + bool success; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: + NSString *bundleIDNSStr = (NSString *) attach_token; + + // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: + + // First we have the debug sub-dictionary: + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyCancelDebugOnNextLaunch]; + + // That will go in the overall dictionary: + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions]; + + success = FBSCallOpenApplicationFunction (bundleIDNSStr, options, err_str, NULL); + + if (!success) + { + DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString()); + } + + [pool drain]; +} +#endif // WITH_FBS diff --git a/tools/debugserver/source/MacOSX/MachTask.h b/tools/debugserver/source/MacOSX/MachTask.h new file mode 100644 index 000000000000..96b991478c78 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachTask.h @@ -0,0 +1,134 @@ +//===-- 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 +#include +// Other libraries and framework includes +// Project includes +#include "DNBDefs.h" +#include "MachException.h" +#include "MachVMMemory.h" +#include "PThreadMutex.h" + +class MachProcess; + +typedef uint64_t MachMallocEventId; + +enum MachMallocEventType +{ + eMachMallocEventTypeAlloc = 2, + eMachMallocEventTypeDealloc = 4, + eMachMallocEventTypeOther = 1 +}; + +struct MachMallocEvent +{ + mach_vm_address_t m_base_address; + uint64_t m_size; + MachMallocEventType m_event_type; + MachMallocEventId m_event_id; +}; + +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); + int GetMemoryRegionInfo (nub_addr_t addr, DNBRegionInfo *region_info); + std::string GetProfileData (DNBProfileDataScanType scanType); + + 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, bool force = false); + static task_t TaskPortForProcessID (pid_t pid, DNBError &err, uint32_t num_retries = 10, uint32_t usec_interval = 10000); + + MachProcess * Process () { return m_process; } + const MachProcess * Process () const { return m_process; } + + nub_size_t PageSize (); + + bool HasMallocLoggingEnabled (); + + // enumerate the malloc records for a given address (starting with Mac OS X 10.6 Snow Leopard it should include + // all allocations that *include* address, rather than just those *starting* at address) + bool EnumerateMallocRecords (mach_vm_address_t address, + MachMallocEvent *event_buffer, + uint32_t buffer_size, + uint32_t *count); + + // enumerate every malloc record generated by this task, no matter what the address + bool EnumerateMallocRecords (MachMallocEvent *event_buffer, + uint32_t buffer_size, + uint32_t *count); + + // given a malloc event, report every stack frame that led to this event + bool EnumerateMallocFrames (MachMallocEventId event_id, + mach_vm_address_t *function_addresses_buffer, + uint32_t buffer_size, + uint32_t *count); + +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/tools/debugserver/source/MacOSX/MachTask.mm b/tools/debugserver/source/MacOSX/MachTask.mm new file mode 100644 index 000000000000..9c725663d559 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachTask.mm @@ -0,0 +1,1144 @@ +//===-- 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 +#import + +#if defined (__APPLE__) +#include +#include +#endif + +// C++ Includes +#include +#include + +// 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" +#include "stack_logging.h" + +#ifdef WITH_SPRINGBOARD + +#include +#include +#include + +#endif + +#ifdef WITH_BKS +extern "C" +{ + #import + #import + #import +} +#endif + +#include + +#ifdef LLDB_ENERGY +#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(); + if (task == TASK_NULL) + return KERN_INVALID_ARGUMENT; + + DNBError err; + err = BasicInfo(task, &task_info); + + if (err.Success()) + { + // task_resume isn't counted like task_suspend calls are, are, so if the + // task is not suspended, don't try and resume it since it is already + // running + if (task_info.suspend_count > 0) + { + err = ::task_resume (task); + if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) + err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task); + } + } + return err.Error(); +} + +//---------------------------------------------------------------------- +// 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(TaskPort()); +} + +//---------------------------------------------------------------------- +// MachTask::RestoreExceptionPortInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::RestoreExceptionPortInfo() +{ + return m_exc_port_info.Restore(TaskPort()); +} + + +//---------------------------------------------------------------------- +// MachTask::ReadMemory +//---------------------------------------------------------------------- +nub_size_t +MachTask::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf) +{ + nub_size_t n = 0; + task_t task = TaskPort(); + if (task != TASK_NULL) + { + n = m_vm_memory.Read(task, addr, buf, size); + + DNBLogThreadedIf(LOG_MEMORY, "MachTask::ReadMemory ( addr = 0x%8.8llx, size = %llu, buf = %p) => %llu bytes read", (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n); + if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) + { + DNBDataRef data((uint8_t*)buf, n, false); + data.Dump(0, static_cast(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 = %llu, buf = %p) => %llu bytes written", (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n); + if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) + { + DNBDataRef data((uint8_t*)buf, n, false); + data.Dump(0, static_cast(n), addr, DNBDataRef::TypeUInt8, 16); + } + } + return n; +} + +//---------------------------------------------------------------------- +// MachTask::MemoryRegionInfo +//---------------------------------------------------------------------- +int +MachTask::GetMemoryRegionInfo (nub_addr_t addr, DNBRegionInfo *region_info) +{ + task_t task = TaskPort(); + if (task == TASK_NULL) + return -1; + + int ret = m_vm_memory.GetMemoryRegionInfo(task, addr, region_info); + DNBLogThreadedIf(LOG_MEMORY, "MachTask::MemoryRegionInfo ( addr = 0x%8.8llx ) => %i (start = 0x%8.8llx, size = 0x%8.8llx, permissions = %u)", + (uint64_t)addr, + ret, + (uint64_t)region_info->addr, + (uint64_t)region_info->size, + region_info->permissions); + return ret; +} + +#define TIME_VALUE_TO_TIMEVAL(a, r) do { \ +(r)->tv_sec = (a)->seconds; \ +(r)->tv_usec = (a)->microseconds; \ +} while (0) + +// We should consider moving this into each MacThread. +static void get_threads_profile_data(DNBProfileDataScanType scanType, task_t task, nub_process_t pid, std::vector &threads_id, std::vector &threads_name, std::vector &threads_used_usec) +{ + kern_return_t kr; + thread_act_array_t threads; + mach_msg_type_number_t tcnt; + + kr = task_threads(task, &threads, &tcnt); + if (kr != KERN_SUCCESS) + return; + + for (mach_msg_type_number_t i = 0; i < tcnt; i++) + { + thread_identifier_info_data_t identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kr = ::thread_info(threads[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&identifier_info, &count); + if (kr != KERN_SUCCESS) continue; + + thread_basic_info_data_t basic_info; + count = THREAD_BASIC_INFO_COUNT; + kr = ::thread_info(threads[i], THREAD_BASIC_INFO, (thread_info_t)&basic_info, &count); + if (kr != KERN_SUCCESS) continue; + + if ((basic_info.flags & TH_FLAGS_IDLE) == 0) + { + nub_thread_t tid = MachThread::GetGloballyUniqueThreadIDForMachPortID (threads[i]); + threads_id.push_back(tid); + + if ((scanType & eProfileThreadName) && (identifier_info.thread_handle != 0)) + { + struct proc_threadinfo proc_threadinfo; + int len = ::proc_pidinfo(pid, PROC_PIDTHREADINFO, identifier_info.thread_handle, &proc_threadinfo, PROC_PIDTHREADINFO_SIZE); + if (len && proc_threadinfo.pth_name[0]) + { + threads_name.push_back(proc_threadinfo.pth_name); + } + else + { + threads_name.push_back(""); + } + } + else + { + threads_name.push_back(""); + } + struct timeval tv; + struct timeval thread_tv; + TIME_VALUE_TO_TIMEVAL(&basic_info.user_time, &thread_tv); + TIME_VALUE_TO_TIMEVAL(&basic_info.system_time, &tv); + timeradd(&thread_tv, &tv, &thread_tv); + uint64_t used_usec = thread_tv.tv_sec * 1000000ULL + thread_tv.tv_usec; + threads_used_usec.push_back(used_usec); + } + + mach_port_deallocate(mach_task_self(), threads[i]); + } + mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)threads, tcnt * sizeof(*threads)); +} + +#define RAW_HEXBASE std::setfill('0') << std::hex << std::right +#define DECIMAL std::dec << std::setfill(' ') +std::string +MachTask::GetProfileData (DNBProfileDataScanType scanType) +{ + std::string result; + + static int32_t numCPU = -1; + struct host_cpu_load_info host_info; + if (scanType & eProfileHostCPU) + { + int32_t mib[] = {CTL_HW, HW_AVAILCPU}; + size_t len = sizeof(numCPU); + if (numCPU == -1) + { + if (sysctl(mib, sizeof(mib) / sizeof(int32_t), &numCPU, &len, NULL, 0) != 0) + return result; + } + + mach_port_t localHost = mach_host_self(); + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; + kern_return_t kr = host_statistics(localHost, HOST_CPU_LOAD_INFO, (host_info_t)&host_info, &count); + if (kr != KERN_SUCCESS) + return result; + } + + task_t task = TaskPort(); + if (task == TASK_NULL) + return result; + + pid_t pid = m_process->ProcessID(); + + struct task_basic_info task_info; + DNBError err; + err = BasicInfo(task, &task_info); + + if (!err.Success()) + return result; + + uint64_t elapsed_usec = 0; + uint64_t task_used_usec = 0; + if (scanType & eProfileCPU) + { + // Get current used time. + struct timeval current_used_time; + struct timeval tv; + TIME_VALUE_TO_TIMEVAL(&task_info.user_time, ¤t_used_time); + TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv); + timeradd(¤t_used_time, &tv, ¤t_used_time); + task_used_usec = current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec; + + struct timeval current_elapsed_time; + int res = gettimeofday(¤t_elapsed_time, NULL); + if (res == 0) + { + elapsed_usec = current_elapsed_time.tv_sec * 1000000ULL + current_elapsed_time.tv_usec; + } + } + + std::vector threads_id; + std::vector threads_name; + std::vector threads_used_usec; + + if (scanType & eProfileThreadsCPU) + { + get_threads_profile_data(scanType, task, pid, threads_id, threads_name, threads_used_usec); + } + +#if defined (HOST_VM_INFO64_COUNT) + vm_statistics64_data_t vminfo; +#else + struct vm_statistics vminfo; +#endif + uint64_t physical_memory; + mach_vm_size_t rprvt = 0; + mach_vm_size_t rsize = 0; + mach_vm_size_t vprvt = 0; + mach_vm_size_t vsize = 0; + mach_vm_size_t dirty_size = 0; + mach_vm_size_t purgeable = 0; + mach_vm_size_t anonymous = 0; + if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, m_process->GetCPUType(), pid, vminfo, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size, purgeable, anonymous)) + { + std::ostringstream profile_data_stream; + + if (scanType & eProfileHostCPU) + { + profile_data_stream << "num_cpu:" << numCPU << ';'; + profile_data_stream << "host_user_ticks:" << host_info.cpu_ticks[CPU_STATE_USER] << ';'; + profile_data_stream << "host_sys_ticks:" << host_info.cpu_ticks[CPU_STATE_SYSTEM] << ';'; + profile_data_stream << "host_idle_ticks:" << host_info.cpu_ticks[CPU_STATE_IDLE] << ';'; + } + + if (scanType & eProfileCPU) + { + profile_data_stream << "elapsed_usec:" << elapsed_usec << ';'; + profile_data_stream << "task_used_usec:" << task_used_usec << ';'; + } + + if (scanType & eProfileThreadsCPU) + { + const size_t num_threads = threads_id.size(); + for (size_t i=0; iProcessID(), err); + return m_task; +} + +//---------------------------------------------------------------------- +// MachTask::TaskPortForProcessID +//---------------------------------------------------------------------- +task_t +MachTask::TaskPortForProcessID (pid_t pid, DNBError &err, uint32_t num_retries, uint32_t usec_interval) +{ + if (pid != INVALID_NUB_PROCESS) + { + DNBError err; + mach_port_t task_self = mach_task_self (); + task_t task = TASK_NULL; + for (uint32_t i=0; i err = 0x%8.8x (%s)", + task_self, + pid, + err.Error(), + err.AsString() ? err.AsString() : "success"); + if (err.Fail()) + err.SetErrorString(str); + err.LogThreaded(str); + } + + if (err.Success()) + return task; + + // Sleep a bit and try again + ::usleep (usec_interval); + } + } + return TASK_NULL; +} + + +//---------------------------------------------------------------------- +// 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.8llx, resident_size = 0x%8.8llx, user_time = %f, system_time = %f }", + info->suspend_count, + (uint64_t)info->virtual_size, + (uint64_t)info->resident_size, + user, + system); + } + return 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(); + + // We weren't able to save the info for our exception ports, we must stop... + if (m_exc_port_info.mask == 0) + { + err.SetErrorString("failed to get exception port info"); + return false; + } + + // Set the ability to get all exceptions on this port + err = ::task_set_exception_ports (task, m_exc_port_info.mask, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); + 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, + m_exc_port_info.mask, + 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 = 0; + + 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); + + 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); + +#if defined (__APPLE__) + pthread_setname_np ("exception monitoring thread"); +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + struct sched_param thread_param; + int thread_sched_policy; + if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0) + { + thread_param.sched_priority = 47; + pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); + } +#endif +#endif + + // 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 available. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + uint32_t num_exceptions_received = 0; + DNBError err; + task_t task = mach_task->TaskPort(); + mach_msg_timeout_t periodic_timeout = 0; + +#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS) + mach_msg_timeout_t watchdog_elapsed = 0; + mach_msg_timeout_t watchdog_timeout = 60 * 1000; + pid_t pid = 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 (WITH_SPRINGBOARD) && !defined (WITH_BKS) + +#ifdef WITH_BKS + CFReleaser watchdog; + if (mach_proc->ProcessUsingBackBoard()) + { + pid_t pid = mach_proc->ProcessID(); + CFAllocatorRef alloc = kCFAllocatorDefault; + watchdog.reset(::BKSWatchdogAssertionCreateForPID(alloc, pid)); + } +#endif // #ifdef WITH_BKS + + 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 and get the possibly updated task port back + // from the process in case we exec'ed and our task port changed + task = 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; + } + } + +#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS) + 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(task)) + { + ++num_exceptions_received; + mach_proc->ExceptionMessageReceived(exception_message); + } + } + } + +#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS) + if (watchdog.get()) + { + // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we + // all are up and running on systems that support it. The SBS framework has a #define + // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now + // so it should still build either way. + DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get()); + ::SBSWatchdogAssertionRelease (watchdog.get()); + } +#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS) + + 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 = VM_PROT_NONE; + 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); +#define ALWAYS_ZOMBIE_ALLOCATIONS 0 + if (ALWAYS_ZOMBIE_ALLOCATIONS || getenv ("DEBUGSERVER_ZOMBIE_ALLOCATIONS")) + { + ::mach_vm_protect (task, (*pos).first, (*pos).second, 0, VM_PROT_NONE); + return true; + } + else + return ::mach_vm_deallocate (task, (*pos).first, (*pos).second) == KERN_SUCCESS; + } + + } + return false; +} + +static void foundStackLog(mach_stack_logging_record_t record, void *context) { + *((bool*)context) = true; +} + +bool +MachTask::HasMallocLoggingEnabled () +{ + bool found = false; + + __mach_stack_logging_enumerate_records(m_task, 0x0, foundStackLog, &found); + return found; +} + +struct history_enumerator_impl_data +{ + MachMallocEvent *buffer; + uint32_t *position; + uint32_t count; +}; + +static void history_enumerator_impl(mach_stack_logging_record_t record, void* enum_obj) +{ + history_enumerator_impl_data *data = (history_enumerator_impl_data*)enum_obj; + + if (*data->position >= data->count) + return; + + data->buffer[*data->position].m_base_address = record.address; + data->buffer[*data->position].m_size = record.argument; + data->buffer[*data->position].m_event_id = record.stack_identifier; + data->buffer[*data->position].m_event_type = record.type_flags == stack_logging_type_alloc ? eMachMallocEventTypeAlloc : + record.type_flags == stack_logging_type_dealloc ? eMachMallocEventTypeDealloc : + eMachMallocEventTypeOther; + *data->position+=1; +} + +bool +MachTask::EnumerateMallocRecords (MachMallocEvent *event_buffer, + uint32_t buffer_size, + uint32_t *count) +{ + return EnumerateMallocRecords(0, + event_buffer, + buffer_size, + count); +} + +bool +MachTask::EnumerateMallocRecords (mach_vm_address_t address, + MachMallocEvent *event_buffer, + uint32_t buffer_size, + uint32_t *count) +{ + if (!event_buffer || !count) + return false; + + if (buffer_size == 0) + return false; + + *count = 0; + history_enumerator_impl_data data = { event_buffer, count, buffer_size }; + __mach_stack_logging_enumerate_records(m_task, address, history_enumerator_impl, &data); + return (*count > 0); +} + +bool +MachTask::EnumerateMallocFrames (MachMallocEventId event_id, + mach_vm_address_t *function_addresses_buffer, + uint32_t buffer_size, + uint32_t *count) +{ + if (!function_addresses_buffer || !count) + return false; + + if (buffer_size == 0) + return false; + + __mach_stack_logging_frames_for_uniqued_stack(m_task, event_id, &function_addresses_buffer[0], buffer_size, count); + *count -= 1; + if (function_addresses_buffer[*count-1] < PageSize()) + *count -= 1; + return (*count > 0); +} + +nub_size_t +MachTask::PageSize () +{ + return m_vm_memory.PageSize (m_task); +} diff --git a/tools/debugserver/source/MacOSX/MachThread.cpp b/tools/debugserver/source/MacOSX/MachThread.cpp new file mode 100644 index 000000000000..897484156080 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachThread.cpp @@ -0,0 +1,922 @@ +//===-- 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 +#include +#include +#include "MachThread.h" +#include "MachProcess.h" +#include "DNBLog.h" +#include "DNB.h" +#include "ThreadInfo.h" + +static uint32_t +GetSequenceID() +{ + static uint32_t g_nextID = 0; + return ++g_nextID; +} + +MachThread::MachThread (MachProcess *process, bool is_64_bit, uint64_t unique_thread_id, thread_t mach_port_num) : + m_process (process), + m_unique_id (unique_thread_id), + m_mach_port_number (mach_port_num), + m_seq_id (GetSequenceID()), + m_state (eStateUnloaded), + m_state_mutex (PTHREAD_MUTEX_RECURSIVE), + m_suspend_count (0), + m_stop_exception (), + m_arch_ap (DNBArchProtocol::Create (this)), + m_reg_sets (NULL), + m_num_reg_sets (0), + m_ident_info(), + m_proc_threadinfo(), + m_dispatch_queue_name(), + m_is_64_bit(is_64_bit), + m_pthread_qos_class_decode (nullptr) +{ + nub_size_t num_reg_sets = 0; + m_reg_sets = m_arch_ap->GetRegisterSetInfo (&num_reg_sets); + m_num_reg_sets = num_reg_sets; + + m_pthread_qos_class_decode = (unsigned int (*)(unsigned long, int*, unsigned long*)) dlsym (RTLD_DEFAULT, "_pthread_qos_class_decode"); + + // Get the thread state so we know if a thread is in a state where we can't + // muck with it and also so we get the suspend count correct in case it was + // already suspended + GetBasicInfo(); + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::MachThread ( process = %p, tid = 0x%8.8" PRIx64 ", seq_id = %u )", &m_process, m_unique_id, m_seq_id); +} + +MachThread::~MachThread() +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::~MachThread() for tid = 0x%8.8" PRIx64 " (%u)", m_unique_id, m_seq_id); +} + + + +void +MachThread::Suspend() +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); + if (MachPortNumberIsValid(m_mach_port_number)) + { + DNBError err(::thread_suspend (m_mach_port_number), DNBError::MachKernel); + if (err.Success()) + m_suspend_count++; + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number); + } +} + +void +MachThread::Resume(bool others_stopped) +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); + if (MachPortNumberIsValid(m_mach_port_number)) + { + SetSuspendCountBeforeResume(others_stopped); + } +} + +bool +MachThread::SetSuspendCountBeforeResume(bool others_stopped) +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); + DNBError err; + if (MachPortNumberIsValid(m_mach_port_number) == false) + return false; + + integer_t times_to_resume; + + if (others_stopped) + { + if (GetBasicInfo()) + { + times_to_resume = m_basic_info.suspend_count; + m_suspend_count = - (times_to_resume - m_suspend_count); + } + else + times_to_resume = 0; + } + else + { + times_to_resume = m_suspend_count; + m_suspend_count = 0; + } + + if (times_to_resume > 0) + { + while (times_to_resume > 0) + { + err = ::thread_resume (m_mach_port_number); + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number); + if (err.Success()) + --times_to_resume; + else + { + if (GetBasicInfo()) + times_to_resume = m_basic_info.suspend_count; + else + times_to_resume = 0; + } + } + } + return true; +} + +bool +MachThread::RestoreSuspendCountAfterStop () +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); + DNBError err; + if (MachPortNumberIsValid(m_mach_port_number) == false) + return false; + + if (m_suspend_count > 0) + { + while (m_suspend_count > 0) + { + err = ::thread_resume (m_mach_port_number); + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number); + if (err.Success()) + --m_suspend_count; + else + { + if (GetBasicInfo()) + m_suspend_count = m_basic_info.suspend_count; + else + m_suspend_count = 0; + return false; // ??? + } + } + } + else if (m_suspend_count < 0) + { + while (m_suspend_count < 0) + { + err = ::thread_suspend (m_mach_port_number); + if (err.Success()) + ++m_suspend_count; + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + { + err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number); + return false; + } + } + } + return true; +} + + +const char * +MachThread::GetBasicInfoAsString () const +{ + static char g_basic_info_string[1024]; + struct thread_basic_info basicInfo; + + if (GetBasicInfo(m_mach_port_number, &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%8.8" PRIx64 ": user=%f system=%f cpu=%d sleep_time=%d", + m_unique_id, + user, + system, + basicInfo.cpu_usage, + basicInfo.sleep_time); + + return g_basic_info_string; + } + return NULL; +} + +// Finds the Mach port number for a given thread in the inferior process' port namespace. +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_mach_port_number) + { + 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::IsUserReady() +{ + if (m_basic_info.run_state == 0) + GetBasicInfo (); + + switch (m_basic_info.run_state) + { + default: + case TH_STATE_UNINTERRUPTIBLE: + break; + + case TH_STATE_RUNNING: + case TH_STATE_STOPPED: + case TH_STATE_WAITING: + case TH_STATE_HALTED: + return true; + } + return false; +} + +struct thread_basic_info * +MachThread::GetBasicInfo () +{ + if (MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info)) + return &m_basic_info; + return NULL; +} + + +bool +MachThread::GetBasicInfo(thread_t thread, struct thread_basic_info *basicInfoPtr) +{ + if (MachPortNumberIsValid(thread)) + { + unsigned int info_count = THREAD_BASIC_INFO_COUNT; + kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count); + if (err == KERN_SUCCESS) + return true; + } + ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info)); + return false; +} + + +bool +MachThread::ThreadIDIsValid(uint64_t thread) +{ + return thread != 0; +} + +bool +MachThread::MachPortNumberIsValid(thread_t thread) +{ + return thread != THREAD_NULL; +} + +bool +MachThread::GetRegisterState(int flavor, bool force) +{ + return m_arch_ap->GetRegisterState(flavor, force) == KERN_SUCCESS; +} + +bool +MachThread::SetRegisterState(int flavor) +{ + return m_arch_ap->SetRegisterState(flavor) == KERN_SUCCESS; +} + +uint64_t +MachThread::GetPC(uint64_t failValue) +{ + // Get program counter + return m_arch_ap->GetPC(failValue); +} + +bool +MachThread::SetPC(uint64_t value) +{ + // Set program counter + return m_arch_ap->SetPC(value); +} + +uint64_t +MachThread::GetSP(uint64_t failValue) +{ + // Get stack pointer + return m_arch_ap->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_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; + } + + DNBLogThreaded("[%3u] #%3u tid: 0x%8.8" PRIx64 ", pc: 0x%16.16" PRIx64 ", sp: 0x%16.16" PRIx64 ", user: %d.%6.6d, system: %d.%6.6d, cpu: %2d, policy: %2d, run_state: %2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d", + index, + m_seq_id, + m_unique_id, + GetPC(INVALID_NUB_ADDRESS), + GetSP(INVALID_NUB_ADDRESS), + m_basic_info.user_time.seconds, m_basic_info.user_time.microseconds, + m_basic_info.system_time.seconds, m_basic_info.system_time.microseconds, + m_basic_info.cpu_usage, + m_basic_info.policy, + m_basic_info.run_state, + thread_run_state, + m_basic_info.flags, + m_basic_info.suspend_count, m_suspend_count, + m_basic_info.sleep_time); + //DumpRegisterState(0); +} + +void +MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action, bool others_stopped) +{ + if (thread_action->addr != INVALID_NUB_ADDRESS) + SetPC (thread_action->addr); + + SetState (thread_action->state); + switch (thread_action->state) + { + case eStateStopped: + case eStateSuspended: + assert (others_stopped == false); + Suspend(); + break; + + case eStateRunning: + case eStateStepping: + Resume(others_stopped); + break; + default: + break; + } + m_arch_ap->ThreadWillResume(); + m_stop_exception.Clear(); +} + +DNBBreakpoint * +MachThread::CurrentBreakpoint() +{ + return m_process->Breakpoints().FindByAddress(GetPC()); +} + +bool +MachThread::ShouldStop(bool &step_more) +{ + // See if this thread is at a breakpoint? + DNBBreakpoint *bp = CurrentBreakpoint(); + + if (bp) + { + // This thread is sitting at a breakpoint, ask the breakpoint + // if we should be stopping here. + return true; + } + else + { + if (m_arch_ap->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 GetState() == eStateStepping; +} + + +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_ap->ThreadDidStop(); + + + // We may have suspended this thread so the primary thread could step + // without worrying about race conditions, so lets restore our suspend + // count. + RestoreSuspendCountAfterStop(); + + // Update the basic information for a thread + MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info); + + if (m_basic_info.suspend_count > 0) + SetState(eStateSuspended); + else + SetState(eStateStopped); + return true; +} + +bool +MachThread::NotifyException(MachException::Data& exc) +{ + // Allow the arch specific protocol to process (MachException::Data &)exc + // first before possible reassignment of m_stop_exception with exc. + // See also MachThread::GetStopException(). + bool handled = m_arch_ap->NotifyException(exc); + + if (m_stop_exception.IsValid()) + { + // We may have more than one exception for a thread, but we need to + // only remember the one that we will say is the reason we stopped. + // We may have been single stepping and also gotten a signal exception, + // so just remember the most pertinent one. + if (m_stop_exception.IsBreakpoint()) + m_stop_exception = exc; + } + else + { + m_stop_exception = exc; + } + + return handled; +} + + +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%8.8" PRIx64 "", DNBStateAsString(state), m_unique_id); +} + +nub_size_t +MachThread::GetNumRegistersInSet(nub_size_t regSet) const +{ + if (regSet < m_num_reg_sets) + return m_reg_sets[regSet].num_registers; + return 0; +} + +const char * +MachThread::GetRegisterSetName(nub_size_t regSet) const +{ + if (regSet < m_num_reg_sets) + return m_reg_sets[regSet].name; + return NULL; +} + +const DNBRegisterInfo * +MachThread::GetRegisterInfo(nub_size_t regSet, nub_size_t regIndex) const +{ + if (regSet < m_num_reg_sets) + if (regIndex < m_reg_sets[regSet].num_registers) + return &m_reg_sets[regSet].registers[regIndex]; + return NULL; +} +void +MachThread::DumpRegisterState(nub_size_t regSet) +{ + if (regSet == REGISTER_SET_ALL) + { + for (regSet = 1; regSet < m_num_reg_sets; regSet++) + DumpRegisterState(regSet); + } + else + { + if (m_arch_ap->RegisterSetStateIsValid((int)regSet)) + { + const size_t numRegisters = GetNumRegistersInSet(regSet); + uint32_t regIndex = 0; + DNBRegisterValueClass reg; + for (regIndex = 0; regIndex < numRegisters; ++regIndex) + { + if (m_arch_ap->GetRegisterValue((uint32_t)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_num_reg_sets; + return &m_reg_sets[0]; +} + +bool +MachThread::GetRegisterValue ( uint32_t set, uint32_t reg, DNBRegisterValue *value ) +{ + return m_arch_ap->GetRegisterValue(set, reg, value); +} + +bool +MachThread::SetRegisterValue ( uint32_t set, uint32_t reg, const DNBRegisterValue *value ) +{ + return m_arch_ap->SetRegisterValue(set, reg, value); +} + +nub_size_t +MachThread::GetRegisterContext (void *buf, nub_size_t buf_len) +{ + return m_arch_ap->GetRegisterContext(buf, buf_len); +} + +nub_size_t +MachThread::SetRegisterContext (const void *buf, nub_size_t buf_len) +{ + return m_arch_ap->SetRegisterContext(buf, buf_len); +} + +uint32_t +MachThread::SaveRegisterState () +{ + return m_arch_ap->SaveRegisterState(); + +} +bool +MachThread::RestoreRegisterState (uint32_t save_id) +{ + return m_arch_ap->RestoreRegisterState(save_id); +} + +uint32_t +MachThread::EnableHardwareBreakpoint (const DNBBreakpoint *bp) +{ + if (bp != NULL && bp->IsBreakpoint()) + return m_arch_ap->EnableHardwareBreakpoint(bp->Address(), bp->ByteSize()); + return INVALID_NUB_HW_INDEX; +} + +uint32_t +MachThread::EnableHardwareWatchpoint (const DNBBreakpoint *wp, bool also_set_on_task) +{ + if (wp != NULL && wp->IsWatchpoint()) + return m_arch_ap->EnableHardwareWatchpoint(wp->Address(), wp->ByteSize(), wp->WatchpointRead(), wp->WatchpointWrite(), also_set_on_task); + return INVALID_NUB_HW_INDEX; +} + +bool +MachThread::RollbackTransForHWP() +{ + return m_arch_ap->RollbackTransForHWP(); +} + +bool +MachThread::FinishTransForHWP() +{ + return m_arch_ap->FinishTransForHWP(); +} + +bool +MachThread::DisableHardwareBreakpoint (const DNBBreakpoint *bp) +{ + if (bp != NULL && bp->IsHardware()) + return m_arch_ap->DisableHardwareBreakpoint(bp->GetHardwareIndex()); + return false; +} + +bool +MachThread::DisableHardwareWatchpoint (const DNBBreakpoint *wp, bool also_set_on_task) +{ + if (wp != NULL && wp->IsHardware()) + return m_arch_ap->DisableHardwareWatchpoint(wp->GetHardwareIndex(), also_set_on_task); + return false; +} + +uint32_t +MachThread::NumSupportedHardwareWatchpoints () const +{ + return m_arch_ap->NumSupportedHardwareWatchpoints(); +} + +bool +MachThread::GetIdentifierInfo () +{ + // Don't try to get the thread info once and cache it for the life of the thread. It changes over time, for instance + // if the thread name changes, then the thread_handle also changes... So you have to refetch it every time. + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kret = ::thread_info (m_mach_port_number, THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count); + return kret == KERN_SUCCESS; + + 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; +} + + +uint64_t +MachThread::GetGloballyUniqueThreadIDForMachPortID (thread_t mach_port_id) +{ + kern_return_t kr; + thread_identifier_info_data_t tident; + mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info (mach_port_id, THREAD_IDENTIFIER_INFO, + (thread_info_t) &tident, &tident_count); + if (kr != KERN_SUCCESS) + { + return mach_port_id; + } + return tident.thread_id; +} + +nub_addr_t +MachThread::GetPThreadT () +{ + nub_addr_t pthread_t_value = INVALID_NUB_ADDRESS; + if (MachPortNumberIsValid (m_mach_port_number)) + { + kern_return_t kr; + thread_identifier_info_data_t tident; + mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info (m_mach_port_number, THREAD_IDENTIFIER_INFO, + (thread_info_t) &tident, &tident_count); + if (kr == KERN_SUCCESS) + { + // Dereference thread_handle to get the pthread_t value for this thread. + if (m_is_64_bit) + { + uint64_t addr; + if (m_process->ReadMemory (tident.thread_handle, 8, &addr) == 8) + { + if (addr != 0) + { + pthread_t_value = addr; + } + } + } + else + { + uint32_t addr; + if (m_process->ReadMemory (tident.thread_handle, 4, &addr) == 4) + { + if (addr != 0) + { + pthread_t_value = addr; + } + } + } + } + } + return pthread_t_value; +} + +// Return this thread's TSD (Thread Specific Data) address. +// This is computed based on this thread's pthread_t value. +// +// We compute the TSD from the pthread_t by one of two methods. +// +// If plo_pthread_tsd_base_offset is non-zero, this is a simple offset that we add to +// the pthread_t to get the TSD base address. +// +// Else we read a pointer from memory at pthread_t + plo_pthread_tsd_base_address_offset and +// that gives us the TSD address. +// +// These plo_pthread_tsd_base values must be read out of libpthread by lldb & provided to debugserver. + +nub_addr_t +MachThread::GetTSDAddressForThread (uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) +{ + nub_addr_t tsd_addr = INVALID_NUB_ADDRESS; + nub_addr_t pthread_t_value = GetPThreadT(); + if (plo_pthread_tsd_base_offset != 0 && plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS) + { + tsd_addr = pthread_t_value + plo_pthread_tsd_base_offset; + } + else + { + if (plo_pthread_tsd_entry_size == 4) + { + uint32_t addr = 0; + if (m_process->ReadMemory (pthread_t_value + plo_pthread_tsd_base_address_offset, 4, &addr) == 4) + { + if (addr != 0) + { + tsd_addr = addr; + } + } + } + if (plo_pthread_tsd_entry_size == 4) + { + uint64_t addr = 0; + if (m_process->ReadMemory (pthread_t_value + plo_pthread_tsd_base_address_offset, 8, &addr) == 8) + { + if (addr != 0) + { + tsd_addr = addr; + } + } + } + } + return tsd_addr; +} + + +nub_addr_t +MachThread::GetDispatchQueueT () +{ + nub_addr_t dispatch_queue_t_value = INVALID_NUB_ADDRESS; + if (MachPortNumberIsValid (m_mach_port_number)) + { + kern_return_t kr; + thread_identifier_info_data_t tident; + mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info (m_mach_port_number, THREAD_IDENTIFIER_INFO, + (thread_info_t) &tident, &tident_count); + if (kr == KERN_SUCCESS && tident.dispatch_qaddr != 0 && tident.dispatch_qaddr != INVALID_NUB_ADDRESS) + { + // Dereference dispatch_qaddr to get the dispatch_queue_t value for this thread's queue, if any. + if (m_is_64_bit) + { + uint64_t addr; + if (m_process->ReadMemory (tident.dispatch_qaddr, 8, &addr) == 8) + { + if (addr != 0) + dispatch_queue_t_value = addr; + } + } + else + { + uint32_t addr; + if (m_process->ReadMemory (tident.dispatch_qaddr, 4, &addr) == 4) + { + if (addr != 0) + dispatch_queue_t_value = addr; + } + } + } + } + return dispatch_queue_t_value; +} + + +ThreadInfo::QoS +MachThread::GetRequestedQoS (nub_addr_t tsd, uint64_t dti_qos_class_index) +{ + ThreadInfo::QoS qos_value; + if (MachPortNumberIsValid (m_mach_port_number) && m_pthread_qos_class_decode != nullptr) + { + uint64_t pthread_priority_value = 0; + if (m_is_64_bit) + { + uint64_t pri; + if (m_process->ReadMemory (tsd + (dti_qos_class_index * 8), 8, &pri) == 8) + { + pthread_priority_value = pri; + } + } + else + { + uint32_t pri; + if (m_process->ReadMemory (tsd + (dti_qos_class_index * 4), 4, &pri) == 4) + { + pthread_priority_value = pri; + } + } + + uint32_t requested_qos = m_pthread_qos_class_decode (pthread_priority_value, NULL, NULL); + + switch (requested_qos) + { + // These constants from + case 0x21: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_USER_INTERACTIVE"; + qos_value.printable_name = "User Interactive"; + break; + case 0x19: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_USER_INITIATED"; + qos_value.printable_name = "User Initiated"; + break; + case 0x15: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_DEFAULT"; + qos_value.printable_name = "Default"; + break; + case 0x11: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_UTILITY"; + qos_value.printable_name = "Utility"; + break; + case 0x09: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_BACKGROUND"; + qos_value.printable_name = "Background"; + break; + case 0x00: + qos_value.enum_value = requested_qos; + qos_value.constant_name = "QOS_CLASS_UNSPECIFIED"; + qos_value.printable_name = "Unspecified"; + break; + } + } + return qos_value; +} diff --git a/tools/debugserver/source/MacOSX/MachThread.h b/tools/debugserver/source/MacOSX/MachThread.h new file mode 100644 index 000000000000..a2a318172588 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachThread.h @@ -0,0 +1,159 @@ +//===-- 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 +#include +#include +#include + +#include "PThreadCondition.h" +#include "PThreadMutex.h" +#include "MachException.h" +#include "DNBArch.h" +#include "DNBRegisterInfo.h" + +#include "ThreadInfo.h" + +class DNBBreakpoint; +class MachProcess; +class MachThreadList; + +class MachThread +{ +public: + + MachThread (MachProcess *process, bool is_64_bit, uint64_t unique_thread_id = 0, thread_t mach_port_number = 0); + ~MachThread (); + + MachProcess * Process() { return m_process; } + const MachProcess * + Process() const { return m_process; } + nub_process_t ProcessID() const; + void Dump(uint32_t index); + uint64_t ThreadID() const { return m_unique_id; } + thread_t MachPortNumber() const { return m_mach_port_number; } + thread_t InferiorThreadID() const; + + uint32_t SequenceID() const { return m_seq_id; } + static bool ThreadIDIsValid(uint64_t thread); // The 64-bit system-wide unique thread identifier + static bool MachPortNumberIsValid(thread_t thread); // The mach port # for this thread in debugserver namespace + void Resume(bool others_stopped); + void Suspend(); + bool SetSuspendCountBeforeResume(bool others_stopped); + bool RestoreSuspendCountAfterStop(); + + 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 + + DNBBreakpoint * CurrentBreakpoint(); + uint32_t EnableHardwareBreakpoint (const DNBBreakpoint *breakpoint); + uint32_t EnableHardwareWatchpoint (const DNBBreakpoint *watchpoint, bool also_set_on_task); + bool DisableHardwareBreakpoint (const DNBBreakpoint *breakpoint); + bool DisableHardwareWatchpoint (const DNBBreakpoint *watchpoint, bool also_set_on_task); + uint32_t NumSupportedHardwareWatchpoints () const; + bool RollbackTransForHWP(); + bool FinishTransForHWP(); + + nub_state_t GetState(); + void SetState(nub_state_t state); + + void ThreadWillResume (const DNBThreadResumeAction *thread_action, bool others_stopped = false); + bool ShouldStop(bool &step_more); + bool IsStepping(); + bool ThreadDidStop(); + bool NotifyException(MachException::Data& exc); + const MachException::Data& GetStopException() { return m_stop_exception; } + + nub_size_t GetNumRegistersInSet(nub_size_t regSet) const; + const char * GetRegisterSetName(nub_size_t regSet) const; + const DNBRegisterInfo * + GetRegisterInfo(nub_size_t regSet, nub_size_t regIndex) const; + void DumpRegisterState(nub_size_t 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); + uint32_t SaveRegisterState (); + bool RestoreRegisterState (uint32_t save_id); + + void NotifyBreakpointChanged (const DNBBreakpoint *bp) + { + } + + bool IsUserReady(); + struct thread_basic_info * + GetBasicInfo (); + const char * GetBasicInfoAsString () const; + const char * GetName (); + + DNBArchProtocol* + GetArchProtocol() + { + return m_arch_ap.get(); + } + + ThreadInfo::QoS GetRequestedQoS (nub_addr_t tsd, uint64_t dti_qos_class_index); + nub_addr_t GetPThreadT(); + nub_addr_t GetDispatchQueueT(); + nub_addr_t GetTSDAddressForThread (uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); + + static uint64_t GetGloballyUniqueThreadIDForMachPortID (thread_t mach_port_id); + +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 + uint64_t m_unique_id; // The globally unique ID for this thread (nub_thread_t) + thread_t m_mach_port_number; // The mach port # for this thread in debugserver namesp. + 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 + struct thread_basic_info m_basic_info; // Basic information for a thread used to see if a thread is valid + int32_t m_suspend_count; // The current suspend count > 0 means we have suspended m_suspendCount times, + // < 0 means we have resumed it m_suspendCount times. + MachException::Data m_stop_exception; // The best exception that describes why this thread is stopped + std::unique_ptr m_arch_ap; // Arch specific information for register state and more + const DNBRegisterSetInfo * m_reg_sets; // Register set information for this thread + nub_size_t m_num_reg_sets; + thread_identifier_info_data_t m_ident_info; + struct proc_threadinfo m_proc_threadinfo; + std::string m_dispatch_queue_name; + bool m_is_64_bit; + + // qos_class_t _pthread_qos_class_decode(pthread_priority_t priority, int *, unsigned long *); + unsigned int (*m_pthread_qos_class_decode) (unsigned long priority, int*, unsigned long *); + +private: + friend class MachThreadList; +}; + +typedef std::shared_ptr MachThreadSP; + +#endif diff --git a/tools/debugserver/source/MacOSX/MachThreadList.cpp b/tools/debugserver/source/MacOSX/MachThreadList.cpp new file mode 100644 index 000000000000..8a7da6f4531e --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachThreadList.cpp @@ -0,0 +1,668 @@ +//===-- 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 +#include + +#include "DNBLog.h" +#include "DNBThreadResumeActions.h" +#include "MachProcess.h" + +MachThreadList::MachThreadList() : + m_threads(), + m_threads_mutex(PTHREAD_MUTEX_RECURSIVE), + m_is_64_bit(false) +{ +} + +MachThreadList::~MachThreadList() +{ +} + +nub_state_t +MachThreadList::GetState(nub_thread_t tid) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetState(); + return eStateInvalid; +} + +const char * +MachThreadList::GetName (nub_thread_t tid) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetName(); + return NULL; +} + +ThreadInfo::QoS +MachThreadList::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetRequestedQoS(tsd, dti_qos_class_index); + return ThreadInfo::QoS(); +} + +nub_addr_t +MachThreadList::GetPThreadT (nub_thread_t tid) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetPThreadT(); + return INVALID_NUB_ADDRESS; +} + +nub_addr_t +MachThreadList::GetDispatchQueueT (nub_thread_t tid) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetDispatchQueueT(); + return INVALID_NUB_ADDRESS; +} + +nub_addr_t +MachThreadList::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetTSDAddressForThread(plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); + return INVALID_NUB_ADDRESS; +} + +nub_thread_t +MachThreadList::SetCurrentThread(nub_thread_t tid) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + { + m_current_thread = thread_sp; + return tid; + } + return INVALID_NUB_THREAD; +} + + +bool +MachThreadList::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetStopException().GetStopInfo(stop_info); + return false; +} + +bool +MachThreadList::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info) +{ + thread_t mach_port_number = GetMachPortNumberByThreadID (tid); + + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + return ::thread_info (mach_port_number, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS; +} + +void +MachThreadList::DumpThreadStoppedReason (nub_thread_t tid) const +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + thread_sp->GetStopException().DumpStopReason(); +} + +const char * +MachThreadList::GetThreadInfo (nub_thread_t tid) const +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetBasicInfoAsString(); + return NULL; +} + +MachThreadSP +MachThreadList::GetThreadByID (nub_thread_t tid) const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + MachThreadSP thread_sp; + const size_t num_threads = m_threads.size(); + for (size_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->ThreadID() == tid) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +MachThreadSP +MachThreadList::GetThreadByMachPortNumber (thread_t mach_port_number) const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + MachThreadSP thread_sp; + const size_t num_threads = m_threads.size(); + for (size_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->MachPortNumber() == mach_port_number) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +nub_thread_t +MachThreadList::GetThreadIDByMachPortNumber (thread_t mach_port_number) const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + MachThreadSP thread_sp; + const size_t num_threads = m_threads.size(); + for (size_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->MachPortNumber() == mach_port_number) + { + return m_threads[idx]->ThreadID(); + } + } + return INVALID_NUB_THREAD; +} + +thread_t +MachThreadList::GetMachPortNumberByThreadID (nub_thread_t globally_unique_id) const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + MachThreadSP thread_sp; + const size_t num_threads = m_threads.size(); + for (size_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->ThreadID() == globally_unique_id) + { + return m_threads[idx]->MachPortNumber(); + } + } + return 0; +} + +bool +MachThreadList::GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value ) const +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetRegisterValue(set, reg, reg_value); + + return false; +} + +bool +MachThreadList::SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *reg_value ) const +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->SetRegisterValue(set, reg, reg_value); + + return false; +} + +nub_size_t +MachThreadList::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->GetRegisterContext (buf, buf_len); + return 0; +} + +nub_size_t +MachThreadList::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->SetRegisterContext (buf, buf_len); + return 0; +} + +uint32_t +MachThreadList::SaveRegisterState (nub_thread_t tid) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->SaveRegisterState (); + return 0; +} + +bool +MachThreadList::RestoreRegisterState (nub_thread_t tid, uint32_t save_id) +{ + MachThreadSP thread_sp (GetThreadByID (tid)); + if (thread_sp) + return thread_sp->RestoreRegisterState (save_id); + return 0; +} + + +nub_size_t +MachThreadList::NumThreads () const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + return m_threads.size(); +} + +nub_thread_t +MachThreadList::ThreadIDAtIndex (nub_size_t idx) const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + if (idx < m_threads.size()) + return m_threads[idx]->ThreadID(); + return INVALID_NUB_THREAD; +} + +nub_thread_t +MachThreadList::CurrentThreadID ( ) +{ + MachThreadSP thread_sp; + CurrentThread(thread_sp); + if (thread_sp.get()) + return thread_sp->ThreadID(); + return INVALID_NUB_THREAD; +} + +bool +MachThreadList::NotifyException(MachException::Data& exc) +{ + MachThreadSP thread_sp (GetThreadByMachPortNumber (exc.thread_port)); + if (thread_sp) + { + thread_sp->NotifyException(exc); + return true; + } + return false; +} + +void +MachThreadList::Clear() +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + m_threads.clear(); +} + +uint32_t +MachThreadList::UpdateThreadList(MachProcess *process, bool update, MachThreadList::collection *new_threads) +{ + // locker will keep a mutex locked until it goes out of scope + DNBLogThreadedIf (LOG_THREAD, "MachThreadList::UpdateThreadList (pid = %4.4x, update = %u) process stop count = %u", process->ProcessID(), update, process->StopCount()); + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + + if (process->StopCount() == 0) + { + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process->ProcessID() }; + struct kinfo_proc processInfo; + size_t bufsize = sizeof(processInfo); + if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) + { + if (processInfo.kp_proc.p_flag & P_LP64) + m_is_64_bit = true; + } +#if defined (__i386__) || defined (__x86_64__) + if (m_is_64_bit) + DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64); + else + DNBArchProtocol::SetArchitecture(CPU_TYPE_I386); +#elif defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + if (m_is_64_bit) + DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64); + else + DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM); +#endif + } + + 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; + 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 thread_t mach_port_num = thread_list[idx]; + + uint64_t unique_thread_id = MachThread::GetGloballyUniqueThreadIDForMachPortID (mach_port_num); + MachThreadSP thread_sp (GetThreadByID (unique_thread_id)); + if (thread_sp) + { + // Keep the existing thread class + currThreads.push_back(thread_sp); + } + else + { + // We don't have this thread, lets add it. + thread_sp.reset(new MachThread(process, m_is_64_bit, unique_thread_id, mach_port_num)); + + // Add the new thread regardless of its is user ready state... + // Make sure the thread is ready to be displayed and shown to users + // before we add this thread to our list... + if (thread_sp->IsUserReady()) + { + if (new_threads) + new_threads->push_back(thread_sp); + + currThreads.push_back(thread_sp); + } + } + } + + m_threads.swap(currThreads); + m_current_thread.reset(); + + // Free the vm memory given to us by ::task_threads() + vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t)); + ::vm_deallocate (::mach_task_self(), + (vm_address_t)thread_list, + thread_list_size); + } + } + return static_cast(m_threads.size()); +} + + +void +MachThreadList::CurrentThread (MachThreadSP& thread_sp) +{ + // locker will keep a mutex locked until it goes out of scope + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + if (m_current_thread.get() == NULL) + { + // Figure out which thread is going to be our current thread. + // This is currently done by finding the first thread in the list + // that has a valid exception. + const size_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetStopException().IsValid()) + { + m_current_thread = m_threads[idx]; + break; + } + } + } + thread_sp = m_current_thread; +} + +void +MachThreadList::Dump() const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + const size_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + m_threads[idx]->Dump(idx); + } +} + + +void +MachThreadList::ProcessWillResume(MachProcess *process, const DNBThreadResumeActions &thread_actions) +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + + // Update our thread list, because sometimes libdispatch or the kernel + // will spawn threads while a task is suspended. + MachThreadList::collection new_threads; + + // First figure out if we were planning on running only one thread, and if so force that thread to resume. + bool run_one_thread; + nub_thread_t solo_thread = INVALID_NUB_THREAD; + if (thread_actions.GetSize() > 0 + && thread_actions.NumActionsWithState(eStateStepping) + thread_actions.NumActionsWithState (eStateRunning) == 1) + { + run_one_thread = true; + const DNBThreadResumeAction *action_ptr = thread_actions.GetFirst(); + size_t num_actions = thread_actions.GetSize(); + for (size_t i = 0; i < num_actions; i++, action_ptr++) + { + if (action_ptr->state == eStateStepping || action_ptr->state == eStateRunning) + { + solo_thread = action_ptr->tid; + break; + } + } + } + else + run_one_thread = false; + + UpdateThreadList(process, true, &new_threads); + + DNBThreadResumeAction resume_new_threads = { -1U, eStateRunning, 0, INVALID_NUB_ADDRESS }; + // If we are planning to run only one thread, any new threads should be suspended. + if (run_one_thread) + resume_new_threads.state = eStateSuspended; + + const size_t num_new_threads = new_threads.size(); + const size_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + MachThread *thread = m_threads[idx].get(); + bool handled = false; + for (uint32_t new_idx = 0; new_idx < num_new_threads; ++new_idx) + { + if (thread == new_threads[new_idx].get()) + { + thread->ThreadWillResume(&resume_new_threads); + handled = true; + break; + } + } + + if (!handled) + { + const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true); + // There must always be a thread action for every thread. + assert (thread_action); + bool others_stopped = false; + if (solo_thread == thread->ThreadID()) + others_stopped = true; + thread->ThreadWillResume (thread_action, others_stopped); + } + } + + if (new_threads.size()) + { + for (uint32_t idx = 0; idx < num_new_threads; ++idx) + { + DNBLogThreadedIf (LOG_THREAD, "MachThreadList::ProcessWillResume (pid = %4.4x) stop-id=%u, resuming newly discovered thread: 0x%8.8" PRIx64 ", thread-is-user-ready=%i)", + process->ProcessID(), + process->StopCount(), + new_threads[idx]->ThreadID(), + new_threads[idx]->IsUserReady()); + } + } +} + +uint32_t +MachThreadList::ProcessDidStop(MachProcess *process) +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + // Update our thread list + const uint32_t num_threads = UpdateThreadList(process, true); + for (uint32_t 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) +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + uint32_t should_stop = false; + const size_t num_threads = m_threads.size(); + for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) + { + should_stop = m_threads[idx]->ShouldStop(step_more); + } + return should_stop; +} + + +void +MachThreadList::NotifyBreakpointChanged (const DNBBreakpoint *bp) +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + const size_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + m_threads[idx]->NotifyBreakpointChanged(bp); + } +} + + +uint32_t +MachThreadList::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const +{ + if (bp != NULL) + { + const size_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + m_threads[idx]->EnableHardwareBreakpoint(bp); + } + return INVALID_NUB_HW_INDEX; +} + +bool +MachThreadList::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const +{ + if (bp != NULL) + { + const size_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + m_threads[idx]->DisableHardwareBreakpoint(bp); + } + return false; +} + +// DNBWatchpointSet() -> MachProcess::CreateWatchpoint() -> MachProcess::EnableWatchpoint() +// -> MachThreadList::EnableHardwareWatchpoint(). +uint32_t +MachThreadList::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const +{ + uint32_t hw_index = INVALID_NUB_HW_INDEX; + if (wp != NULL) + { + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + const size_t num_threads = m_threads.size(); + // On Mac OS X we have to prime the control registers for new threads. We do this + // using the control register data for the first thread, for lack of a better way of choosing. + bool also_set_on_task = true; + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + if ((hw_index = m_threads[idx]->EnableHardwareWatchpoint(wp, also_set_on_task)) == INVALID_NUB_HW_INDEX) + { + // We know that idx failed for some reason. Let's rollback the transaction for [0, idx). + for (uint32_t i = 0; i < idx; ++i) + m_threads[i]->RollbackTransForHWP(); + return INVALID_NUB_HW_INDEX; + } + also_set_on_task = false; + } + // Notify each thread to commit the pending transaction. + for (uint32_t idx = 0; idx < num_threads; ++idx) + m_threads[idx]->FinishTransForHWP(); + + } + return hw_index; +} + +bool +MachThreadList::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const +{ + if (wp != NULL) + { + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + const size_t num_threads = m_threads.size(); + + // On Mac OS X we have to prime the control registers for new threads. We do this + // using the control register data for the first thread, for lack of a better way of choosing. + bool also_set_on_task = true; + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + if (!m_threads[idx]->DisableHardwareWatchpoint(wp, also_set_on_task)) + { + // We know that idx failed for some reason. Let's rollback the transaction for [0, idx). + for (uint32_t i = 0; i < idx; ++i) + m_threads[i]->RollbackTransForHWP(); + return false; + } + also_set_on_task = false; + } + // Notify each thread to commit the pending transaction. + for (uint32_t idx = 0; idx < num_threads; ++idx) + m_threads[idx]->FinishTransForHWP(); + + return true; + } + return false; +} + +uint32_t +MachThreadList::NumSupportedHardwareWatchpoints () const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + const size_t num_threads = m_threads.size(); + // Use an arbitrary thread to retrieve the number of supported hardware watchpoints. + if (num_threads) + return m_threads[0]->NumSupportedHardwareWatchpoints(); + return 0; +} + +uint32_t +MachThreadList::GetThreadIndexForThreadStoppedWithSignal (const int signo) const +{ + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + uint32_t should_stop = false; + const size_t num_threads = m_threads.size(); + for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) + { + if (m_threads[idx]->GetStopException().SoftSignal () == signo) + return idx; + } + return UINT32_MAX; +} + diff --git a/tools/debugserver/source/MacOSX/MachThreadList.h b/tools/debugserver/source/MacOSX/MachThreadList.h new file mode 100644 index 000000000000..0ab550e83fc6 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachThreadList.h @@ -0,0 +1,87 @@ +//===-- 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" +#include "ThreadInfo.h" + +class DNBThreadResumeActions; + +class MachThreadList +{ +public: + MachThreadList (); + ~MachThreadList (); + + void Clear (); + void Dump () 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 *reg_value) const; + nub_size_t GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len); + nub_size_t SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len); + uint32_t SaveRegisterState (nub_thread_t tid); + bool RestoreRegisterState (nub_thread_t tid, uint32_t save_id); + 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 (nub_thread_t tid); + nub_state_t GetState (nub_thread_t tid); + nub_thread_t SetCurrentThread (nub_thread_t tid); + + ThreadInfo::QoS GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index); + nub_addr_t GetPThreadT (nub_thread_t tid); + nub_addr_t GetDispatchQueueT (nub_thread_t tid); + nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); + + 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 (); + 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; + uint32_t NumSupportedHardwareWatchpoints () const; + + uint32_t GetThreadIndexForThreadStoppedWithSignal (const int signo) const; + + MachThreadSP GetThreadByID (nub_thread_t tid) const; + + MachThreadSP GetThreadByMachPortNumber (thread_t mach_port_number) const; + nub_thread_t GetThreadIDByMachPortNumber (thread_t mach_port_number) const; + thread_t GetMachPortNumberByThreadID (nub_thread_t globally_unique_id) const; + +protected: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + uint32_t UpdateThreadList (MachProcess *process, bool update, collection *num_threads = NULL); +// const_iterator FindThreadByID (thread_t tid) const; + + collection m_threads; + mutable PThreadMutex m_threads_mutex; + MachThreadSP m_current_thread; + bool m_is_64_bit; +}; + +#endif // #ifndef __MachThreadList_h__ + diff --git a/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/tools/debugserver/source/MacOSX/MachVMMemory.cpp new file mode 100644 index 000000000000..3b86a83024d9 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachVMMemory.cpp @@ -0,0 +1,598 @@ +//===-- 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 +#include +#include +#include + +static const vm_size_t kInvalidPageSize = ~0; + +MachVMMemory::MachVMMemory() : + m_page_size (kInvalidPageSize), + m_err (0) +{ +} + +MachVMMemory::~MachVMMemory() +{ +} + +nub_size_t +MachVMMemory::PageSize(task_t task) +{ + if (m_page_size == kInvalidPageSize) + { +#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22 + if (task != TASK_NULL) + { + kern_return_t kr; + mach_msg_type_number_t info_count = TASK_VM_INFO_COUNT; + task_vm_info_data_t vm_info; + kr = task_info (task, TASK_VM_INFO, (task_info_t) &vm_info, &info_count); + if (kr == KERN_SUCCESS) + { + DNBLogThreadedIf(LOG_TASK, "MachVMMemory::PageSize task_info returned page size of 0x%x", (int) vm_info.page_size); + m_page_size = vm_info.page_size; + return m_page_size; + } + else + { + DNBLogThreadedIf(LOG_TASK, "MachVMMemory::PageSize task_info call failed to get page size, TASK_VM_INFO %d, TASK_VM_INFO_COUNT %d, kern return %d", TASK_VM_INFO, TASK_VM_INFO_COUNT, kr); + } + } +#endif + 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(task_t task, nub_addr_t addr, nub_size_t count) +{ + const nub_size_t page_size = PageSize(task); + 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_bool_t +MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address, DNBRegionInfo *region_info) +{ + MachVMRegion vmRegion(task); + + if (vmRegion.GetRegionForAddress(address)) + { + region_info->addr = vmRegion.StartAddress(); + region_info->size = vmRegion.GetByteSize(); + region_info->permissions = vmRegion.GetDNBPermissions(); + } + else + { + region_info->addr = address; + region_info->size = 0; + if (vmRegion.GetError().Success()) + { + // vmRegion.GetRegionForAddress() return false, indicating that "address" + // wasn't in a valid region, but the "vmRegion" info was successfully + // read from the task which means the info describes the next valid + // region from which we can infer the size of this invalid region + mach_vm_address_t start_addr = vmRegion.StartAddress(); + if (address < start_addr) + region_info->size = start_addr - address; + } + // If we can't get any info about the size from the next region it means + // we asked about an address that was past all mappings, so the size + // of this region will take up all remaining address space. + if (region_info->size == 0) + region_info->size = INVALID_NUB_ADDRESS - region_info->addr; + + // Not readable, writeable or executable + region_info->permissions = 0; + } + return true; +} + +// For integrated graphics chip, this makes the accounting info for 'wired' memory more like top. +uint64_t +MachVMMemory::GetStolenPages(task_t task) +{ + static uint64_t stolenPages = 0; + static bool calculated = false; + if (calculated) return stolenPages; + + static int mib_reserved[CTL_MAXNAME]; + static int mib_unusable[CTL_MAXNAME]; + static int mib_other[CTL_MAXNAME]; + static size_t mib_reserved_len = 0; + static size_t mib_unusable_len = 0; + static size_t mib_other_len = 0; + int r; + + /* This can be used for testing: */ + //tsamp->pages_stolen = (256 * 1024 * 1024ULL) / tsamp->pagesize; + + if(0 == mib_reserved_len) + { + mib_reserved_len = CTL_MAXNAME; + + r = sysctlnametomib("machdep.memmap.Reserved", mib_reserved, + &mib_reserved_len); + + if(-1 == r) + { + mib_reserved_len = 0; + return 0; + } + + mib_unusable_len = CTL_MAXNAME; + + r = sysctlnametomib("machdep.memmap.Unusable", mib_unusable, + &mib_unusable_len); + + if(-1 == r) + { + mib_reserved_len = 0; + return 0; + } + + + mib_other_len = CTL_MAXNAME; + + r = sysctlnametomib("machdep.memmap.Other", mib_other, + &mib_other_len); + + if(-1 == r) + { + mib_reserved_len = 0; + return 0; + } + } + + if(mib_reserved_len > 0 && mib_unusable_len > 0 && mib_other_len > 0) + { + uint64_t reserved = 0, unusable = 0, other = 0; + size_t reserved_len; + size_t unusable_len; + size_t other_len; + + reserved_len = sizeof(reserved); + unusable_len = sizeof(unusable); + other_len = sizeof(other); + + /* These are all declared as QUAD/uint64_t sysctls in the kernel. */ + + if (sysctl (mib_reserved, + static_cast(mib_reserved_len), + &reserved, + &reserved_len, + NULL, + 0)) + { + return 0; + } + + if (sysctl (mib_unusable, + static_cast(mib_unusable_len), + &unusable, + &unusable_len, + NULL, + 0)) + { + return 0; + } + + if (sysctl (mib_other, + static_cast(mib_other_len), + &other, + &other_len, + NULL, + 0)) + { + return 0; + } + + if (reserved_len == sizeof(reserved) && + unusable_len == sizeof(unusable) && + other_len == sizeof(other)) + { + uint64_t stolen = reserved + unusable + other; + uint64_t mb128 = 128 * 1024 * 1024ULL; + + if(stolen >= mb128) + { + stolen = (stolen & ~((128 * 1024 * 1024ULL) - 1)); // rounding down + stolenPages = stolen / PageSize (task); + } + } + } + + calculated = true; + return stolenPages; +} + +static uint64_t GetPhysicalMemory() +{ + // This doesn't change often at all. No need to poll each time. + static uint64_t physical_memory = 0; + static bool calculated = false; + if (calculated) return physical_memory; + + size_t len = sizeof(physical_memory); + sysctlbyname("hw.memsize", &physical_memory, &len, NULL, 0); + + calculated = true; + return physical_memory; +} + +// rsize and dirty_size is not adjusted for dyld shared cache and multiple __LINKEDIT segment, as in vmmap. In practice, dirty_size doesn't differ much but rsize may. There is performance penalty for the adjustment. Right now, only use the dirty_size. +void +MachVMMemory::GetRegionSizes(task_t task, mach_vm_size_t &rsize, mach_vm_size_t &dirty_size) +{ +#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22 + + task_vm_info_data_t vm_info; + mach_msg_type_number_t info_count; + kern_return_t kr; + + info_count = TASK_VM_INFO_COUNT; + kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count); + if (kr == KERN_SUCCESS) + dirty_size = vm_info.internal; +#endif +} + +// Test whether the virtual address is within the architecture's shared region. +static bool InSharedRegion(mach_vm_address_t addr, cpu_type_t type) +{ + mach_vm_address_t base = 0, size = 0; + + switch(type) { +#if defined (CPU_TYPE_ARM64) && defined (SHARED_REGION_BASE_ARM64) + case CPU_TYPE_ARM64: + base = SHARED_REGION_BASE_ARM64; + size = SHARED_REGION_SIZE_ARM64; + break; +#endif + + case CPU_TYPE_ARM: + base = SHARED_REGION_BASE_ARM; + size = SHARED_REGION_SIZE_ARM; + break; + + case CPU_TYPE_X86_64: + base = SHARED_REGION_BASE_X86_64; + size = SHARED_REGION_SIZE_X86_64; + break; + + case CPU_TYPE_I386: + base = SHARED_REGION_BASE_I386; + size = SHARED_REGION_SIZE_I386; + break; + + default: { + // Log error abut unknown CPU type + break; + } + } + + + return(addr >= base && addr < (base + size)); +} + +void +MachVMMemory::GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, mach_vm_size_t &rprvt, mach_vm_size_t &vprvt) +{ + // Collecting some other info cheaply but not reporting for now. + mach_vm_size_t empty = 0; + mach_vm_size_t fw_private = 0; + + mach_vm_size_t aliased = 0; + bool global_shared_text_data_mapped = false; + vm_size_t pagesize = PageSize (task); + + for (mach_vm_address_t addr=0, size=0; ; addr += size) + { + vm_region_top_info_data_t info; + mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT; + mach_port_t object_name; + + kern_return_t kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name); + if (kr != KERN_SUCCESS) break; + + if (InSharedRegion(addr, cputype)) + { + // Private Shared + fw_private += info.private_pages_resident * pagesize; + + // Check if this process has the globally shared text and data regions mapped in. If so, set global_shared_text_data_mapped to TRUE and avoid checking again. + if (global_shared_text_data_mapped == FALSE && info.share_mode == SM_EMPTY) { + vm_region_basic_info_data_64_t b_info; + mach_vm_address_t b_addr = addr; + mach_vm_size_t b_size = size; + count = VM_REGION_BASIC_INFO_COUNT_64; + + kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &count, &object_name); + if (kr != KERN_SUCCESS) break; + + if (b_info.reserved) { + global_shared_text_data_mapped = TRUE; + } + } + + // Short circuit the loop if this isn't a shared private region, since that's the only region type we care about within the current address range. + if (info.share_mode != SM_PRIVATE) + { + continue; + } + } + + // Update counters according to the region type. + if (info.share_mode == SM_COW && info.ref_count == 1) + { + // Treat single reference SM_COW as SM_PRIVATE + info.share_mode = SM_PRIVATE; + } + + switch (info.share_mode) + { + case SM_LARGE_PAGE: + // Treat SM_LARGE_PAGE the same as SM_PRIVATE + // since they are not shareable and are wired. + case SM_PRIVATE: + rprvt += info.private_pages_resident * pagesize; + rprvt += info.shared_pages_resident * pagesize; + vprvt += size; + break; + + case SM_EMPTY: + empty += size; + break; + + case SM_COW: + case SM_SHARED: + { + if (pid == 0) + { + // Treat kernel_task specially + if (info.share_mode == SM_COW) + { + rprvt += info.private_pages_resident * pagesize; + vprvt += size; + } + break; + } + + if (info.share_mode == SM_COW) + { + rprvt += info.private_pages_resident * pagesize; + vprvt += info.private_pages_resident * pagesize; + } + break; + } + default: + // log that something is really bad. + break; + } + } + + rprvt += aliased; +} + +static void +GetPurgeableAndAnonymous(task_t task, uint64_t &purgeable, uint64_t &anonymous) +{ +#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22 + + kern_return_t kr; + mach_msg_type_number_t info_count; + task_vm_info_data_t vm_info; + + info_count = TASK_VM_INFO_COUNT; + kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count); + if (kr == KERN_SUCCESS) + { + purgeable = vm_info.purgeable_volatile_resident; + anonymous = vm_info.internal + vm_info.compressed - vm_info.purgeable_volatile_pmap; + } + +#endif +} + +#if defined (HOST_VM_INFO64_COUNT) +nub_bool_t +MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics64_data_t &vminfo, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous) +#else +nub_bool_t +MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vminfo, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous) +#endif +{ + if (scanType & eProfileHostMemory) + physical_memory = GetPhysicalMemory(); + + if (scanType & eProfileMemory) + { + static mach_port_t localHost = mach_host_self(); +#if defined (HOST_VM_INFO64_COUNT) + mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; + host_statistics64(localHost, HOST_VM_INFO64, (host_info64_t)&vminfo, &count); +#else + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + host_statistics(localHost, HOST_VM_INFO, (host_info_t)&vminfo, &count); + vminfo.wire_count += GetStolenPages(task); +#endif + + /* We are no longer reporting these. Let's not waste time. + GetMemorySizes(task, cputype, pid, rprvt, vprvt); + rsize = ti.resident_size; + vsize = ti.virtual_size; + + if (scanType & eProfileMemoryDirtyPage) + { + // This uses vmmap strategy. We don't use the returned rsize for now. We prefer to match top's version since that's what we do for the rest of the metrics. + GetRegionSizes(task, rsize, dirty_size); + } + */ + + if (scanType & eProfileMemoryAnonymous) + { + GetPurgeableAndAnonymous(task, purgeable, anonymous); + } + } + + return true; +} + +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(task, curr_addr, data_count - total_bytes_read); + mach_msg_type_number_t curr_bytes_read = 0; + vm_offset_t vm_memory = 0; + m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read); + + 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 )", 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 = static_cast(MaxBytesLeftInPage(task, 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/tools/debugserver/source/MacOSX/MachVMMemory.h b/tools/debugserver/source/MacOSX/MachVMMemory.h new file mode 100644 index 000000000000..abaa20368a26 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachVMMemory.h @@ -0,0 +1,51 @@ +//===-- 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: + 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(task_t task); + nub_bool_t GetMemoryRegionInfo(task_t task, nub_addr_t address, DNBRegionInfo *region_info); +#if defined (HOST_VM_INFO64_COUNT) + nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics64_data_t &vminfo, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous); +#else + nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vminfo, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous); +#endif + +protected: + nub_size_t MaxBytesLeftInPage(task_t task, nub_addr_t addr, nub_size_t count); + + uint64_t GetStolenPages(task_t task); + void GetRegionSizes(task_t task, mach_vm_size_t &rsize, mach_vm_size_t &dirty_size); + void GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, mach_vm_size_t &rprvt, mach_vm_size_t &vprvt); + + + 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/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/tools/debugserver/source/MacOSX/MachVMRegion.cpp new file mode 100644 index 000000000000..38757595cfed --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachVMRegion.cpp @@ -0,0 +1,202 @@ +//===-- 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_err.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); + + const bool failed = m_err.Fail(); + const bool log_protections = DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS); + + if (log_protections || failed) + 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 (failed) + return false; + if (log_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; + + // We make a request for an address and got no error back, but this + // doesn't mean that "addr" is in the range. The data in this object will + // be valid though, so you could see where the next region begins. So we + // return false, yet leave "m_err" with a successfull return code. + if ((addr < m_start) || (addr >= (m_start + m_size))) + return false; + + return true; +} + +uint32_t +MachVMRegion::GetDNBPermissions () const +{ + if (m_addr == INVALID_NUB_ADDRESS || m_start == INVALID_NUB_ADDRESS || m_size == 0) + return 0; + uint32_t dnb_permissions = 0; + + if ((m_data.protection & VM_PROT_READ) == VM_PROT_READ) + dnb_permissions |= eMemoryPermissionsReadable; + if ((m_data.protection & VM_PROT_WRITE) == VM_PROT_WRITE) + dnb_permissions |= eMemoryPermissionsWritable; + if ((m_data.protection & VM_PROT_EXECUTE) == VM_PROT_EXECUTE) + dnb_permissions |= eMemoryPermissionsExecutable; + return dnb_permissions; +} diff --git a/tools/debugserver/source/MacOSX/MachVMRegion.h b/tools/debugserver/source/MacOSX/MachVMRegion.h new file mode 100644 index 000000000000..bcac60b83182 --- /dev/null +++ b/tools/debugserver/source/MacOSX/MachVMRegion.h @@ -0,0 +1,77 @@ +//===-- 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_size_t GetByteSize () const { return 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); + + uint32_t + GetDNBPermissions () const; + + const DNBError & + GetError () + { + return m_err; + } +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/tools/debugserver/source/MacOSX/Makefile b/tools/debugserver/source/MacOSX/Makefile new file mode 100644 index 000000000000..d047444a9c81 --- /dev/null +++ b/tools/debugserver/source/MacOSX/Makefile @@ -0,0 +1,54 @@ +##===- tools/debugserver/source/MacOSX/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LLDB_LEVEL := ../../../.. + +DIRS := i386 x86_64 + +TOOLNAME = debugserver + +CODESIGN_TOOLS := 1 + +TOOL_CODESIGN_IDENTITY := lldb_codesign + +LLVMLibsOptions += -llldbDebugserverCommon -llldbUtility -llldbDebugserverMacOSX_I386 -llldbDebugserverMacOSX_X86_64 \ + -framework Foundation -framework CoreFoundation + +GENERATED_MACH_SOURCES = $(PROJ_OBJ_DIR)/mach_excServer.c $(PROJ_OBJ_DIR)/mach_excUser.c + +SOURCES := CFBundle.cpp \ + CFData.cpp \ + CFString.cpp \ + MachException.cpp \ + MachProcess.cpp \ + MachTask.cpp \ + MachThread.cpp \ + MachThreadList.cpp \ + MachVMMemory.cpp \ + MachVMRegion.cpp + +BUILT_SOURCES = $(GENERATED_MACH_SOURCES) $(PROJ_OBJ_DIR)/HasAVX.o + +CPP.Flags += -I$(PROJ_OBJ_DIR)/../.. -I$(PROJ_SRC_DIR)/.. + +LD.Flags += -Wl,-sectcreate,__TEXT,__info_plist,$(PROJ_SRC_DIR)/../../resources/lldb-debugserver-Info.plist + +include $(LLDB_LEVEL)/Makefile + +ObjectsO += $(PROJ_OBJ_DIR)/HasAVX.o + +$(PROJ_OBJ_DIR)/HasAVX.o: $(PROJ_SRC_DIR)/HasAVX.s + $(Echo) "Compiling HasAVX.s for $(BuildMode) build" $(PIC_FLAG) + $(CC) $(TargetCommonOpts) $(CompileCommonOpts) -c $< -o $@ + +ifeq ($(HOST_OS),Darwin) +LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/ +endif + +$(GENERATED_MACH_SOURCES): + mig -I$(PROJ_OBJ_DIR)/../.. $(PROJ_SRC_DIR)/dbgnub-mig.defs \ No newline at end of file diff --git a/tools/debugserver/source/MacOSX/ThreadInfo.h b/tools/debugserver/source/MacOSX/ThreadInfo.h new file mode 100644 index 000000000000..1fd9d5790cf0 --- /dev/null +++ b/tools/debugserver/source/MacOSX/ThreadInfo.h @@ -0,0 +1,26 @@ +//===-- ThreadInfo.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __ThreadInfo_h__ +#define __ThreadInfo_h__ + +namespace ThreadInfo { + +class QoS { +public: + QoS () : constant_name(), printable_name(), enum_value(UINT32_MAX) { } + bool IsValid () { return enum_value != UINT32_MAX; } + std::string constant_name; + std::string printable_name; + uint32_t enum_value; +}; + +}; + +#endif // __ThreadInfo_h__ diff --git a/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp b/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp new file mode 100644 index 000000000000..2eac47b045c3 --- /dev/null +++ b/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp @@ -0,0 +1,2162 @@ +//===-- 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__) || defined (__arm64__) || defined (__aarch64__) + +#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 "ARM_ehframe_Registers.h" +#include "ARM_DWARF_Registers.h" + +#include +#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 privileged or user mode +#define S_RSVD ((uint32_t)(0u << 1)) +#define S_PRIV ((uint32_t)(1u << 1)) +#define S_USER ((uint32_t)(2u << 1)) +#define S_PRIV_USER ((S_PRIV) | (S_USER)) + +#define BCR_ENABLE ((uint32_t)(1u)) +#define WCR_ENABLE ((uint32_t)(1u)) + +// Watchpoint load/store +#define WCR_LOAD ((uint32_t)(1u << 3)) +#define WCR_STORE ((uint32_t)(1u << 4)) + +// Definitions for the Debug Status and Control Register fields: +// [5:2] => Method of debug entry +//#define WATCHPOINT_OCCURRED ((uint32_t)(2u)) +// I'm seeing this, instead. +#define WATCHPOINT_OCCURRED ((uint32_t)(10u)) + +// 0xE120BE70 +static const uint8_t g_arm_breakpoint_opcode[] = { 0x70, 0xBE, 0x20, 0xE1 }; +static const uint8_t g_thumb_breakpoint_opcode[] = { 0x70, 0xBE }; + +// A watchpoint may need to be implemented using two watchpoint registers. +// e.g. watching an 8-byte region when the device can only watch 4-bytes. +// +// This stores the lo->hi mappings. It's safe to initialize to all 0's +// since hi > lo and therefore LoHi[i] cannot be 0. +static uint32_t LoHi[16] = { 0 }; + +// 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 + +// Returns true if the first 16 bit opcode of a thumb instruction indicates +// the instruction will be a 32 bit thumb opcode +static bool +IsThumb32Opcode (uint16_t opcode) +{ + if (((opcode & 0xE000) == 0xE000) && (opcode & 0x1800)) + return true; + return false; +} + +void +DNBArchMachARM::Initialize() +{ + DNBArchPluginInfo arch_plugin_info = + { + CPU_TYPE_ARM, + DNBArchMachARM::Create, + DNBArchMachARM::GetRegisterSetInfo, + DNBArchMachARM::SoftwareBreakpointOpcode + }; + + // Register this arch plug-in with the main protocol class + DNBArchProtocol::RegisterArchPlugin (arch_plugin_info); +} + + +DNBArchProtocol * +DNBArchMachARM::Create (MachThread *thread) +{ + DNBArchMachARM *obj = new DNBArchMachARM (thread); + return obj; +} + +const uint8_t * +DNBArchMachARM::SoftwareBreakpointOpcode (nub_size_t byte_size) +{ + switch (byte_size) + { + case 2: return g_thumb_breakpoint_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.context.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.context.gpr.__pc = (uint32_t) 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.context.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->MachPortNumber(), ARM_THREAD_STATE, (thread_state_t)&m_state.context.gpr, &count); + uint32_t *r = &m_state.context.gpr.__r[0]; + DNBLogThreadedIf(LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count = %u) 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->MachPortNumber(), + ARM_THREAD_STATE, + ARM_THREAD_STATE_COUNT, + kret, + count, + 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; + + kern_return_t kret; + +#if defined (__arm64__) || defined (__aarch64__) + // Read the registers from our thread + mach_msg_type_number_t count = ARM_NEON_STATE_COUNT; + kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_NEON_STATE, (thread_state_t)&m_state.context.vfp, &count); + if (DNBLogEnabledForAny (LOG_THREAD)) + { + DNBLogThreaded("thread_get_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs" + "\n q0 = 0x%16.16llx%16.16llx" + "\n q1 = 0x%16.16llx%16.16llx" + "\n q2 = 0x%16.16llx%16.16llx" + "\n q3 = 0x%16.16llx%16.16llx" + "\n q4 = 0x%16.16llx%16.16llx" + "\n q5 = 0x%16.16llx%16.16llx" + "\n q6 = 0x%16.16llx%16.16llx" + "\n q7 = 0x%16.16llx%16.16llx" + "\n q8 = 0x%16.16llx%16.16llx" + "\n q9 = 0x%16.16llx%16.16llx" + "\n q10 = 0x%16.16llx%16.16llx" + "\n q11 = 0x%16.16llx%16.16llx" + "\n q12 = 0x%16.16llx%16.16llx" + "\n q13 = 0x%16.16llx%16.16llx" + "\n q14 = 0x%16.16llx%16.16llx" + "\n q15 = 0x%16.16llx%16.16llx" + "\n fpsr = 0x%8.8x" + "\n fpcr = 0x%8.8x\n\n", + m_thread->MachPortNumber(), + ARM_NEON_STATE, + ARM_NEON_STATE_COUNT, + kret, + count, + ((uint64_t *)&m_state.context.vfp.__v[0])[0] , ((uint64_t *)&m_state.context.vfp.__v[0])[1], + ((uint64_t *)&m_state.context.vfp.__v[1])[0] , ((uint64_t *)&m_state.context.vfp.__v[1])[1], + ((uint64_t *)&m_state.context.vfp.__v[2])[0] , ((uint64_t *)&m_state.context.vfp.__v[2])[1], + ((uint64_t *)&m_state.context.vfp.__v[3])[0] , ((uint64_t *)&m_state.context.vfp.__v[3])[1], + ((uint64_t *)&m_state.context.vfp.__v[4])[0] , ((uint64_t *)&m_state.context.vfp.__v[4])[1], + ((uint64_t *)&m_state.context.vfp.__v[5])[0] , ((uint64_t *)&m_state.context.vfp.__v[5])[1], + ((uint64_t *)&m_state.context.vfp.__v[6])[0] , ((uint64_t *)&m_state.context.vfp.__v[6])[1], + ((uint64_t *)&m_state.context.vfp.__v[7])[0] , ((uint64_t *)&m_state.context.vfp.__v[7])[1], + ((uint64_t *)&m_state.context.vfp.__v[8])[0] , ((uint64_t *)&m_state.context.vfp.__v[8])[1], + ((uint64_t *)&m_state.context.vfp.__v[9])[0] , ((uint64_t *)&m_state.context.vfp.__v[9])[1], + ((uint64_t *)&m_state.context.vfp.__v[10])[0], ((uint64_t *)&m_state.context.vfp.__v[10])[1], + ((uint64_t *)&m_state.context.vfp.__v[11])[0], ((uint64_t *)&m_state.context.vfp.__v[11])[1], + ((uint64_t *)&m_state.context.vfp.__v[12])[0], ((uint64_t *)&m_state.context.vfp.__v[12])[1], + ((uint64_t *)&m_state.context.vfp.__v[13])[0], ((uint64_t *)&m_state.context.vfp.__v[13])[1], + ((uint64_t *)&m_state.context.vfp.__v[14])[0], ((uint64_t *)&m_state.context.vfp.__v[14])[1], + ((uint64_t *)&m_state.context.vfp.__v[15])[0], ((uint64_t *)&m_state.context.vfp.__v[15])[1], + m_state.context.vfp.__fpsr, + m_state.context.vfp.__fpcr); + + } +#else + // Read the registers from our thread + mach_msg_type_number_t count = ARM_VFP_STATE_COUNT; + kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_VFP_STATE, (thread_state_t)&m_state.context.vfp, &count); + + if (DNBLogEnabledForAny (LOG_THREAD)) + { + uint32_t *r = &m_state.context.vfp.__r[0]; + DNBLogThreaded ("thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count => %u)", + m_thread->MachPortNumber(), + ARM_THREAD_STATE, + ARM_THREAD_STATE_COUNT, + kret, + count); + DNBLogThreaded(" s0=%8.8x s1=%8.8x s2=%8.8x s3=%8.8x s4=%8.8x s5=%8.8x s6=%8.8x s7=%8.8x",r[ 0],r[ 1],r[ 2],r[ 3],r[ 4],r[ 5],r[ 6],r[ 7]); + DNBLogThreaded(" s8=%8.8x s9=%8.8x s10=%8.8x s11=%8.8x s12=%8.8x s13=%8.8x s14=%8.8x s15=%8.8x",r[ 8],r[ 9],r[10],r[11],r[12],r[13],r[14],r[15]); + DNBLogThreaded(" s16=%8.8x s17=%8.8x s18=%8.8x s19=%8.8x s20=%8.8x s21=%8.8x s22=%8.8x s23=%8.8x",r[16],r[17],r[18],r[19],r[20],r[21],r[22],r[23]); + DNBLogThreaded(" s24=%8.8x s25=%8.8x s26=%8.8x s27=%8.8x s28=%8.8x s29=%8.8x s30=%8.8x s31=%8.8x",r[24],r[25],r[26],r[27],r[28],r[29],r[30],r[31]); + DNBLogThreaded(" s32=%8.8x s33=%8.8x s34=%8.8x s35=%8.8x s36=%8.8x s37=%8.8x s38=%8.8x s39=%8.8x",r[32],r[33],r[34],r[35],r[36],r[37],r[38],r[39]); + DNBLogThreaded(" s40=%8.8x s41=%8.8x s42=%8.8x s43=%8.8x s44=%8.8x s45=%8.8x s46=%8.8x s47=%8.8x",r[40],r[41],r[42],r[43],r[44],r[45],r[46],r[47]); + DNBLogThreaded(" s48=%8.8x s49=%8.8x s50=%8.8x s51=%8.8x s52=%8.8x s53=%8.8x s54=%8.8x s55=%8.8x",r[48],r[49],r[50],r[51],r[52],r[53],r[54],r[55]); + DNBLogThreaded(" s56=%8.8x s57=%8.8x s58=%8.8x s59=%8.8x s60=%8.8x s61=%8.8x s62=%8.8x s63=%8.8x fpscr=%8.8x",r[56],r[57],r[58],r[59],r[60],r[61],r[62],r[63],r[64]); + } + +#endif + 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->MachPortNumber(), ARM_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, &count); + m_state.SetError(set, Read, kret); + return kret; +} + +static void +DumpDBGState(const DNBArchMachARM::DBG& 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 +#if defined (ARM_DEBUG_STATE32) && (defined (__arm64__) || defined (__aarch64__)) + mach_msg_type_number_t count = ARM_DEBUG_STATE32_COUNT; + kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE32, (thread_state_t)&m_state.dbg, &count); +#else + mach_msg_type_number_t count = ARM_DEBUG_STATE_COUNT; + kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, &count); +#endif + 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->MachPortNumber(), ARM_THREAD_STATE, (thread_state_t)&m_state.context.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; + mach_msg_type_number_t count; + +#if defined (__arm64__) || defined (__aarch64__) + count = ARM_NEON_STATE_COUNT; + kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_NEON_STATE, (thread_state_t)&m_state.context.vfp, count); +#else + count = ARM_VFP_STATE_COUNT; + kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_VFP_STATE, (thread_state_t)&m_state.context.vfp, count); +#endif + +#if defined (__arm64__) || defined (__aarch64__) + if (DNBLogEnabledForAny (LOG_THREAD)) + { + DNBLogThreaded("thread_set_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs" + "\n q0 = 0x%16.16llx%16.16llx" + "\n q1 = 0x%16.16llx%16.16llx" + "\n q2 = 0x%16.16llx%16.16llx" + "\n q3 = 0x%16.16llx%16.16llx" + "\n q4 = 0x%16.16llx%16.16llx" + "\n q5 = 0x%16.16llx%16.16llx" + "\n q6 = 0x%16.16llx%16.16llx" + "\n q7 = 0x%16.16llx%16.16llx" + "\n q8 = 0x%16.16llx%16.16llx" + "\n q9 = 0x%16.16llx%16.16llx" + "\n q10 = 0x%16.16llx%16.16llx" + "\n q11 = 0x%16.16llx%16.16llx" + "\n q12 = 0x%16.16llx%16.16llx" + "\n q13 = 0x%16.16llx%16.16llx" + "\n q14 = 0x%16.16llx%16.16llx" + "\n q15 = 0x%16.16llx%16.16llx" + "\n fpsr = 0x%8.8x" + "\n fpcr = 0x%8.8x\n\n", + m_thread->MachPortNumber(), + ARM_NEON_STATE, + ARM_NEON_STATE_COUNT, + kret, + count, + ((uint64_t *)&m_state.context.vfp.__v[0])[0] , ((uint64_t *)&m_state.context.vfp.__v[0])[1], + ((uint64_t *)&m_state.context.vfp.__v[1])[0] , ((uint64_t *)&m_state.context.vfp.__v[1])[1], + ((uint64_t *)&m_state.context.vfp.__v[2])[0] , ((uint64_t *)&m_state.context.vfp.__v[2])[1], + ((uint64_t *)&m_state.context.vfp.__v[3])[0] , ((uint64_t *)&m_state.context.vfp.__v[3])[1], + ((uint64_t *)&m_state.context.vfp.__v[4])[0] , ((uint64_t *)&m_state.context.vfp.__v[4])[1], + ((uint64_t *)&m_state.context.vfp.__v[5])[0] , ((uint64_t *)&m_state.context.vfp.__v[5])[1], + ((uint64_t *)&m_state.context.vfp.__v[6])[0] , ((uint64_t *)&m_state.context.vfp.__v[6])[1], + ((uint64_t *)&m_state.context.vfp.__v[7])[0] , ((uint64_t *)&m_state.context.vfp.__v[7])[1], + ((uint64_t *)&m_state.context.vfp.__v[8])[0] , ((uint64_t *)&m_state.context.vfp.__v[8])[1], + ((uint64_t *)&m_state.context.vfp.__v[9])[0] , ((uint64_t *)&m_state.context.vfp.__v[9])[1], + ((uint64_t *)&m_state.context.vfp.__v[10])[0], ((uint64_t *)&m_state.context.vfp.__v[10])[1], + ((uint64_t *)&m_state.context.vfp.__v[11])[0], ((uint64_t *)&m_state.context.vfp.__v[11])[1], + ((uint64_t *)&m_state.context.vfp.__v[12])[0], ((uint64_t *)&m_state.context.vfp.__v[12])[1], + ((uint64_t *)&m_state.context.vfp.__v[13])[0], ((uint64_t *)&m_state.context.vfp.__v[13])[1], + ((uint64_t *)&m_state.context.vfp.__v[14])[0], ((uint64_t *)&m_state.context.vfp.__v[14])[1], + ((uint64_t *)&m_state.context.vfp.__v[15])[0], ((uint64_t *)&m_state.context.vfp.__v[15])[1], + m_state.context.vfp.__fpsr, + m_state.context.vfp.__fpcr); + } +#else + if (DNBLogEnabledForAny (LOG_THREAD)) + { + uint32_t *r = &m_state.context.vfp.__r[0]; + DNBLogThreaded ("thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count => %u)", + m_thread->MachPortNumber(), + ARM_THREAD_STATE, + ARM_THREAD_STATE_COUNT, + kret, + count); + DNBLogThreaded(" s0=%8.8x s1=%8.8x s2=%8.8x s3=%8.8x s4=%8.8x s5=%8.8x s6=%8.8x s7=%8.8x",r[ 0],r[ 1],r[ 2],r[ 3],r[ 4],r[ 5],r[ 6],r[ 7]); + DNBLogThreaded(" s8=%8.8x s9=%8.8x s10=%8.8x s11=%8.8x s12=%8.8x s13=%8.8x s14=%8.8x s15=%8.8x",r[ 8],r[ 9],r[10],r[11],r[12],r[13],r[14],r[15]); + DNBLogThreaded(" s16=%8.8x s17=%8.8x s18=%8.8x s19=%8.8x s20=%8.8x s21=%8.8x s22=%8.8x s23=%8.8x",r[16],r[17],r[18],r[19],r[20],r[21],r[22],r[23]); + DNBLogThreaded(" s24=%8.8x s25=%8.8x s26=%8.8x s27=%8.8x s28=%8.8x s29=%8.8x s30=%8.8x s31=%8.8x",r[24],r[25],r[26],r[27],r[28],r[29],r[30],r[31]); + DNBLogThreaded(" s32=%8.8x s33=%8.8x s34=%8.8x s35=%8.8x s36=%8.8x s37=%8.8x s38=%8.8x s39=%8.8x",r[32],r[33],r[34],r[35],r[36],r[37],r[38],r[39]); + DNBLogThreaded(" s40=%8.8x s41=%8.8x s42=%8.8x s43=%8.8x s44=%8.8x s45=%8.8x s46=%8.8x s47=%8.8x",r[40],r[41],r[42],r[43],r[44],r[45],r[46],r[47]); + DNBLogThreaded(" s48=%8.8x s49=%8.8x s50=%8.8x s51=%8.8x s52=%8.8x s53=%8.8x s54=%8.8x s55=%8.8x",r[48],r[49],r[50],r[51],r[52],r[53],r[54],r[55]); + DNBLogThreaded(" s56=%8.8x s57=%8.8x s58=%8.8x s59=%8.8x s60=%8.8x s61=%8.8x s62=%8.8x s63=%8.8x fpscr=%8.8x",r[56],r[57],r[58],r[59],r[60],r[61],r[62],r[63],r[64]); + } +#endif + + 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->MachPortNumber(), ARM_EXCEPTION_STATE, (thread_state_t)&m_state.context.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(bool also_set_on_task) +{ + int set = e_regSetDBG; +#if defined (ARM_DEBUG_STATE32) && (defined (__arm64__) || defined (__aarch64__)) + kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_DEBUG_STATE32, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE32_COUNT); + if (also_set_on_task) + { + kern_return_t task_kret = ::task_set_state (m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE32, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE32_COUNT); + if (task_kret != KERN_SUCCESS) + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::SetDBGState failed to set debug control register state: 0x%8.8x.", kret); + } +#else + kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE_COUNT); + if (also_set_on_task) + { + kern_return_t task_kret = ::task_set_state (m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE_COUNT); + if (task_kret != KERN_SUCCESS) + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::SetDBGState failed to set debug control register state: 0x%8.8x.", kret); + } +#endif + + 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()) + { + // This is the primary thread, let the arch do anything it needs + if (NumSupportedHardwareBreakpoints() > 0) + { + if (EnableHardwareSingleStep(true) != KERN_SUCCESS) + { + DNBLogThreaded("DNBArchMachARM::ThreadWillResume() failed to enable hardware single step"); + } + } + } + + // Disable the triggered watchpoint temporarily before we resume. + // Plus, we try to enable hardware single step to execute past the instruction which triggered our watchpoint. + if (m_watchpoint_did_occur) + { + if (m_watchpoint_hw_index >= 0) + { + kern_return_t kret = GetDBGState(false); + if (kret == KERN_SUCCESS && !IsWatchpointEnabled(m_state.dbg, m_watchpoint_hw_index)) { + // The watchpoint might have been disabled by the user. We don't need to do anything at all + // to enable hardware single stepping. + m_watchpoint_did_occur = false; + m_watchpoint_hw_index = -1; + return; + } + + DisableHardwareWatchpoint(m_watchpoint_hw_index, false); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() DisableHardwareWatchpoint(%d) called", + m_watchpoint_hw_index); + + // Enable hardware single step to move past the watchpoint-triggering instruction. + m_watchpoint_resume_single_step_enabled = (EnableHardwareSingleStep(true) == KERN_SUCCESS); + + // If we are not able to enable single step to move past the watchpoint-triggering instruction, + // at least we should reset the two watchpoint member variables so that the next time around + // this callback function is invoked, the enclosing logical branch is skipped. + if (!m_watchpoint_resume_single_step_enabled) { + // Reset the two watchpoint member variables. + m_watchpoint_did_occur = false; + m_watchpoint_hw_index = -1; + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() failed to enable single step"); + } + else + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() succeeded to enable single step"); + } + } +} + +bool +DNBArchMachARM::ThreadDidStop() +{ + bool success = true; + + m_state.InvalidateRegisterSetState (e_regSetALL); + + if (m_watchpoint_resume_single_step_enabled) + { + // Great! We now disable the hardware single step as well as re-enable the hardware watchpoint. + // See also ThreadWillResume(). + if (EnableHardwareSingleStep(false) == KERN_SUCCESS) + { + if (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0) + { + ReenableHardwareWatchpoint(m_watchpoint_hw_index); + m_watchpoint_resume_single_step_enabled = false; + m_watchpoint_did_occur = false; + m_watchpoint_hw_index = -1; + } + else + { + DNBLogError("internal error detected: m_watchpoint_resume_step_enabled is true but (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0) does not hold!"); + } + } + else + { + DNBLogError("internal error detected: m_watchpoint_resume_step_enabled is true but unable to disable single step!"); + } + } + + // Are we stepping a single instruction? + if (GetGPRState(true) == KERN_SUCCESS) + { + // We are single stepping, was this the primary thread? + if (m_thread->IsStepping()) + { + 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 +DNBArchMachARM::NotifyException(MachException::Data& exc) +{ + switch (exc.exc_type) + { + default: + break; + case EXC_BREAKPOINT: + if (exc.exc_data.size() == 2 && exc.exc_data[0] == EXC_ARM_DA_DEBUG) + { + // The data break address is passed as exc_data[1]. + nub_addr_t addr = exc.exc_data[1]; + // Find the hardware index with the side effect of possibly massaging the + // addr to return the starting address as seen from the debugger side. + uint32_t hw_index = GetHardwareWatchpointHit(addr); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException watchpoint %d was hit on address 0x%llx", hw_index, (uint64_t) addr); + const int num_watchpoints = NumSupportedHardwareWatchpoints (); + for (int i = 0; i < num_watchpoints; i++) + { + if (LoHi[i] != 0 + && LoHi[i] == hw_index + && LoHi[i] != i + && GetWatchpointAddressByIndex (i) != INVALID_NUB_ADDRESS) + { + addr = GetWatchpointAddressByIndex (i); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException It is a linked watchpoint; rewritten to index %d addr 0x%llx", LoHi[i], (uint64_t) addr); + } + } + if (hw_index != INVALID_NUB_HW_INDEX) + { + m_watchpoint_did_occur = true; + m_watchpoint_hw_index = hw_index; + exc.exc_data[1] = addr; + // Piggyback the hw_index in the exc.data. + exc.exc_data.push_back(hw_index); + } + + return true; + } + break; + } + return false; +} + +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.context.gpr.__pc == m_hw_single_chained_step_addr) + { + DNBLogThreadedIf(LOG_STEP, "Need to step some more at 0x%8.8llx", (uint64_t) m_hw_single_chained_step_addr); + return true; + } + } + } + + m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS; + return false; +} + +// 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(); + } + +// The use of __arm64__ here is not ideal. If debugserver is running on +// an armv8 device, regardless of whether it was built for arch arm or arch arm64, +// it needs to use the MDSCR_EL1 SS bit to single instruction step. + +#if defined (__arm64__) || defined (__aarch64__) + if (enable) + { + DNBLogThreadedIf(LOG_STEP, "%s: Setting MDSCR_EL1 Single Step bit at pc 0x%llx", __FUNCTION__, (uint64_t) m_state.context.gpr.__pc); + m_state.dbg.__mdscr_el1 |= 1; // Set bit 0 (single step, SS) in the MDSCR_EL1. + } + else + { + DNBLogThreadedIf(LOG_STEP, "%s: Clearing MDSCR_EL1 Single Step bit at pc 0x%llx", __FUNCTION__, (uint64_t) m_state.context.gpr.__pc); + m_state.dbg.__mdscr_el1 &= ~(1ULL); // Clear bit 0 (single step, SS) in the MDSCR_EL1. + } +#else + 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.context.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.context.gpr.__cpsr & 0x20) + { + // Thumb breakpoint + if (m_state.context.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.context.gpr.__pc, sizeof(opcode), &opcode)) + { + if (IsThumb32Opcode(opcode)) + { + // 32 bit thumb opcode... + if (m_state.context.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.context.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; + } +#endif + + return SetDBGState(false); +} + +// 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; +} + +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; + + size_t len; + uint32_t n = 0; + len = sizeof (n); + if (::sysctlbyname("hw.optional.breakpoint", &n, &len, NULL, 0) == 0) + { + g_num_supported_hw_breakpoints = n; + DNBLogThreadedIf(LOG_THREAD, "hw.optional.breakpoint=%u", n); + } + else + { +#if !defined (__arm64__) && !defined (__aarch64__) + // 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; + 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=%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; + } + } +#endif + } + } + 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; + + + size_t len; + uint32_t n = 0; + len = sizeof (n); + if (::sysctlbyname("hw.optional.watchpoint", &n, &len, NULL, 0) == 0) + { + g_num_supported_hw_watchpoints = n; + DNBLogThreadedIf(LOG_THREAD, "hw.optional.watchpoint=%u", n); + } + else + { +#if !defined (__arm64__) && !defined (__aarch64__) + // 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; + } + } +#endif + } + } + 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 = 0x%8.8llx, size = %llu) => all hardware breakpoint resources are being used.", (uint64_t)addr, (uint64_t)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(false); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + +// ARM v7 watchpoints may be either word-size or double-word-size. +// It's implementation defined which they can handle. It looks like on an +// armv8 device, armv7 processes can watch dwords. But on a genuine armv7 +// device I tried, only word watchpoints are supported. + +#if defined (__arm64__) || defined (__aarch64__) +#define WATCHPOINTS_ARE_DWORD 1 +#else +#undef WATCHPOINTS_ARE_DWORD +#endif + +uint32_t +DNBArchMachARM::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task) +{ + + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint(addr = 0x%8.8llx, size = %zu, read = %u, write = %u)", (uint64_t)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; + + // Otherwise, can't watch more than 8 bytes per WVR/WCR pair + if (size > 8) + return INVALID_NUB_HW_INDEX; + + // Treat arm watchpoints as having an 8-byte alignment requirement. You can put a watchpoint on a 4-byte + // offset address but you can only watch 4 bytes with that watchpoint. + + // arm watchpoints on an 8-byte (double word) aligned addr can watch any bytes in that + // 8-byte long region of memory. They can watch the 1st byte, the 2nd byte, 3rd byte, etc, or any + // combination therein by setting the bits in the BAS [12:5] (Byte Address Select) field of + // the DBGWCRn_EL1 reg for the watchpoint. + + // If the MASK [28:24] bits in the DBGWCRn_EL1 allow a single watchpoint to monitor a larger region + // of memory (16 bytes, 32 bytes, or 2GB) but the Byte Address Select bitfield then selects a larger + // range of bytes, instead of individual bytes. See the ARMv8 Debug Architecture manual for details. + // This implementation does not currently use the MASK bits; the largest single region watched by a single + // watchpoint right now is 8-bytes. + +#if defined (WATCHPOINTS_ARE_DWORD) + nub_addr_t aligned_wp_address = addr & ~0x7; + uint32_t addr_dword_offset = addr & 0x7; + const int max_watchpoint_size = 8; +#else + nub_addr_t aligned_wp_address = addr & ~0x3; + uint32_t addr_dword_offset = addr & 0x3; + const int max_watchpoint_size = 4; +#endif + + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint aligned_wp_address is 0x%llx and addr_dword_offset is 0x%x", (uint64_t)aligned_wp_address, addr_dword_offset); + + // Do we need to split up this logical watchpoint into two hardware watchpoint + // registers? + // e.g. a watchpoint of length 4 on address 6. We need do this with + // one watchpoint on address 0 with bytes 6 & 7 being monitored + // one watchpoint on address 8 with bytes 0, 1, 2, 3 being monitored + + if (addr_dword_offset + size > max_watchpoint_size) + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint(addr = 0x%8.8llx, size = %zu) needs two hardware watchpoints slots to monitor", (uint64_t)addr, size); + int low_watchpoint_size = max_watchpoint_size - addr_dword_offset; + int high_watchpoint_size = addr_dword_offset + size - max_watchpoint_size; + + uint32_t lo = EnableHardwareWatchpoint(addr, low_watchpoint_size, read, write, also_set_on_task); + if (lo == INVALID_NUB_HW_INDEX) + return INVALID_NUB_HW_INDEX; + uint32_t hi = EnableHardwareWatchpoint (aligned_wp_address + max_watchpoint_size, high_watchpoint_size, read, write, also_set_on_task); + if (hi == INVALID_NUB_HW_INDEX) + { + DisableHardwareWatchpoint (lo, also_set_on_task); + return INVALID_NUB_HW_INDEX; + } + // Tag this lo->hi mapping in our database. + LoHi[lo] = hi; + return lo; + } + + // At this point + // 1 aligned_wp_address is the requested address rounded down to 8-byte alignment + // 2 addr_dword_offset is the offset into that double word (8-byte) region that we are watching + // 3 size is the number of bytes within that 8-byte region that we are watching + + // Set the Byte Address Selects bits DBGWCRn_EL1 bits [12:5] based on the above. + // The bit shift and negation operation will give us 0b11 for 2, 0b1111 for 4, etc, up to 0b11111111 for 8. + // then we shift those bits left by the offset into this dword that we are interested in. + // e.g. if we are watching bytes 4,5,6,7 in a dword we want a BAS of 0b11110000. + uint32_t byte_address_select = ((1 << size) - 1) << addr_dword_offset; + + // Read the debug state + kern_return_t kret = GetDBGState(true); + + 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::ReenableHardwareWatchpoint (uint32_t hw_index) +{ + // If this logical watchpoint # is actually implemented using + // two hardware watchpoint registers, re-enable both of them. + + if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index]) + { + return ReenableHardwareWatchpoint_helper (hw_index) && ReenableHardwareWatchpoint_helper (LoHi[hw_index]); + } + else + { + return ReenableHardwareWatchpoint_helper (hw_index); + } +} + +bool +DNBArchMachARM::ReenableHardwareWatchpoint_helper (uint32_t hw_index) +{ + kern_return_t kret = GetDBGState(false); + if (kret != KERN_SUCCESS) + return false; + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (hw_index >= num_hw_points) + return false; + + m_state.dbg.__wvr[hw_index] = m_disabled_watchpoints[hw_index].addr; + m_state.dbg.__wcr[hw_index] = m_disabled_watchpoints[hw_index].control; + + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint( %u ) - WVR%u = 0x%8.8llx WCR%u = 0x%8.8llx", + hw_index, + hw_index, + (uint64_t) m_state.dbg.__wvr[hw_index], + hw_index, + (uint64_t) m_state.dbg.__wcr[hw_index]); + + // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us automatically, don't need to do it here. + + kret = SetDBGState(false); + + return (kret == KERN_SUCCESS); +} + +bool +DNBArchMachARM::DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task) +{ + if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index]) + { + return DisableHardwareWatchpoint_helper (hw_index, also_set_on_task) && DisableHardwareWatchpoint_helper (LoHi[hw_index], also_set_on_task); + } + else + { + return DisableHardwareWatchpoint_helper (hw_index, also_set_on_task); + } +} + +bool +DNBArchMachARM::DisableHardwareWatchpoint_helper (uint32_t hw_index, bool also_set_on_task) +{ + kern_return_t kret = GetDBGState(false); + if (kret != KERN_SUCCESS) + return false; + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (hw_index >= num_hw_points) + return false; + + m_disabled_watchpoints[hw_index].addr = m_state.dbg.__wvr[hw_index]; + m_disabled_watchpoints[hw_index].control = m_state.dbg.__wcr[hw_index]; + + m_state.dbg.__wvr[hw_index] = 0; + m_state.dbg.__wcr[hw_index] = 0; + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::DisableHardwareWatchpoint( %u ) - WVR%u = 0x%8.8llx WCR%u = 0x%8.8llx", + hw_index, + hw_index, + (uint64_t) m_state.dbg.__wvr[hw_index], + hw_index, + (uint64_t) m_state.dbg.__wcr[hw_index]); + + kret = SetDBGState(also_set_on_task); + + return (kret == KERN_SUCCESS); +} + +// Returns -1 if the trailing bit patterns are not one of: +// { 0b???1, 0b??10, 0b?100, 0b1000 }. +static inline +int32_t +LowestBitSet(uint32_t val) +{ + for (unsigned i = 0; i < 4; ++i) { + if (bit(val, i)) + return i; + } + return -1; +} + +// Iterate through the debug registers; return the index of the first watchpoint whose address matches. +// As a side effect, the starting address as understood by the debugger is returned which could be +// different from 'addr' passed as an in/out argument. +uint32_t +DNBArchMachARM::GetHardwareWatchpointHit(nub_addr_t &addr) +{ + // Read the debug state + kern_return_t kret = GetDBGState(true); + //DumpDBGState(m_state.dbg); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.", kret); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::GetHardwareWatchpointHit() addr = 0x%llx", (uint64_t)addr); + + // This is the watchpoint value to match against, i.e., word address. +#if defined (WATCHPOINTS_ARE_DWORD) + nub_addr_t wp_val = addr & ~((nub_addr_t)7); +#else + nub_addr_t wp_val = addr & ~((nub_addr_t)3); +#endif + if (kret == KERN_SUCCESS) + { + DBG &debug_state = m_state.dbg; + uint32_t i, num = NumSupportedHardwareWatchpoints(); + for (i = 0; i < num; ++i) + { + nub_addr_t wp_addr = GetWatchAddress(debug_state, i); + DNBLogThreadedIf(LOG_WATCHPOINTS, + "DNBArchMachARM::GetHardwareWatchpointHit() slot: %u (addr = 0x%llx).", + i, (uint64_t)wp_addr); + if (wp_val == wp_addr) { +#if defined (WATCHPOINTS_ARE_DWORD) + uint32_t byte_mask = bits(debug_state.__wcr[i], 12, 5); +#else + uint32_t byte_mask = bits(debug_state.__wcr[i], 8, 5); +#endif + + // Sanity check the byte_mask, first. + if (LowestBitSet(byte_mask) < 0) + continue; + + // Compute the starting address (from the point of view of the debugger). + addr = wp_addr + LowestBitSet(byte_mask); + return i; + } + } + } + return INVALID_NUB_HW_INDEX; +} + +nub_addr_t +DNBArchMachARM::GetWatchpointAddressByIndex (uint32_t hw_index) +{ + kern_return_t kret = GetDBGState(true); + if (kret != KERN_SUCCESS) + return INVALID_NUB_ADDRESS; + const uint32_t num = NumSupportedHardwareWatchpoints(); + if (hw_index >= num) + return INVALID_NUB_ADDRESS; + if (IsWatchpointEnabled (m_state.dbg, hw_index)) + return GetWatchAddress (m_state.dbg, hw_index); + return INVALID_NUB_ADDRESS; +} + +bool +DNBArchMachARM::IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index) +{ + // Watchpoint Control Registers, bitfield definitions + // ... + // Bits Value Description + // [0] 0 Watchpoint disabled + // 1 Watchpoint enabled. + return (debug_state.__wcr[hw_index] & 1u); +} + +nub_addr_t +DNBArchMachARM::GetWatchAddress(const DBG &debug_state, uint32_t hw_index) +{ + // Watchpoint Value Registers, bitfield definitions + // Bits Description + // [31:2] Watchpoint value (word address, i.e., 4-byte aligned) + // [1:0] RAZ/SBZP + return bits(debug_state.__wvr[hw_index], 31, 0); +} + +//---------------------------------------------------------------------- +// Register information definitions for 32 bit ARMV7. +//---------------------------------------------------------------------- +enum gpr_regnums +{ + 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_sp, + gpr_lr, + gpr_pc, + gpr_cpsr +}; + +enum +{ + vfp_s0 = 0, + vfp_s1, + vfp_s2, + vfp_s3, + vfp_s4, + vfp_s5, + vfp_s6, + vfp_s7, + vfp_s8, + vfp_s9, + vfp_s10, + vfp_s11, + vfp_s12, + vfp_s13, + vfp_s14, + vfp_s15, + vfp_s16, + vfp_s17, + vfp_s18, + vfp_s19, + vfp_s20, + vfp_s21, + vfp_s22, + vfp_s23, + vfp_s24, + vfp_s25, + vfp_s26, + vfp_s27, + vfp_s28, + vfp_s29, + vfp_s30, + vfp_s31, + vfp_d0, + vfp_d1, + vfp_d2, + vfp_d3, + vfp_d4, + vfp_d5, + vfp_d6, + vfp_d7, + vfp_d8, + vfp_d9, + vfp_d10, + vfp_d11, + vfp_d12, + vfp_d13, + vfp_d14, + vfp_d15, + vfp_d16, + vfp_d17, + vfp_d18, + vfp_d19, + vfp_d20, + vfp_d21, + vfp_d22, + vfp_d23, + vfp_d24, + vfp_d25, + vfp_d26, + vfp_d27, + vfp_d28, + vfp_d29, + vfp_d30, + vfp_d31, + vfp_q0, + vfp_q1, + vfp_q2, + vfp_q3, + vfp_q4, + vfp_q5, + vfp_q6, + vfp_q7, + vfp_q8, + vfp_q9, + vfp_q10, + vfp_q11, + vfp_q12, + vfp_q13, + vfp_q14, + vfp_q15, +#if defined (__arm64__) || defined (__aarch64__) + vfp_fpsr, + vfp_fpcr, +#else + vfp_fpscr +#endif +}; + +enum +{ + exc_exception, + exc_fsr, + exc_far, +}; + +#define GPR_OFFSET_IDX(idx) (offsetof (DNBArchMachARM::GPR, __r[idx])) +#define GPR_OFFSET_NAME(reg) (offsetof (DNBArchMachARM::GPR, __##reg)) + +#define EXC_OFFSET(reg) (offsetof (DNBArchMachARM::EXC, __##reg) + offsetof (DNBArchMachARM::Context, exc)) + +// 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_IDX(idx, reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 4, GPR_OFFSET_IDX(idx), ehframe_##reg, dwarf_##reg, gen, INVALID_NUB_REGNUM, NULL, NULL} +#define DEFINE_GPR_NAME(reg, alt, gen, inval) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 4, GPR_OFFSET_NAME(reg), ehframe_##reg, dwarf_##reg, gen, INVALID_NUB_REGNUM, NULL, inval} + +// In case we are debugging to a debug target that the ability to +// change into the protected modes with folded registers (ABT, IRQ, +// FIQ, SYS, USR, etc..), we should invalidate r8-r14 if the CPSR +// gets modified. + +const char * g_invalidate_cpsr[] = { "r8", "r9", "r10", "r11", "r12", "sp", "lr", NULL }; + +// General purpose registers +const DNBRegisterInfo +DNBArchMachARM::g_gpr_registers[] = +{ + DEFINE_GPR_IDX ( 0, r0,"arg1", GENERIC_REGNUM_ARG1 ), + DEFINE_GPR_IDX ( 1, r1,"arg2", GENERIC_REGNUM_ARG2 ), + DEFINE_GPR_IDX ( 2, r2,"arg3", GENERIC_REGNUM_ARG3 ), + DEFINE_GPR_IDX ( 3, r3,"arg4", GENERIC_REGNUM_ARG4 ), + DEFINE_GPR_IDX ( 4, r4, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX ( 5, r5, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX ( 6, r6, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX ( 7, r7, "fp", GENERIC_REGNUM_FP ), + DEFINE_GPR_IDX ( 8, r8, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX ( 9, r9, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (10, r10, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (11, r11, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (12, r12, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_NAME (sp, "r13", GENERIC_REGNUM_SP, NULL), + DEFINE_GPR_NAME (lr, "r14", GENERIC_REGNUM_RA, NULL), + DEFINE_GPR_NAME (pc, "r15", GENERIC_REGNUM_PC, NULL), + DEFINE_GPR_NAME (cpsr, "flags", GENERIC_REGNUM_FLAGS, g_invalidate_cpsr) +}; + +const char *g_contained_q0 [] { "q0", NULL }; +const char *g_contained_q1 [] { "q1", NULL }; +const char *g_contained_q2 [] { "q2", NULL }; +const char *g_contained_q3 [] { "q3", NULL }; +const char *g_contained_q4 [] { "q4", NULL }; +const char *g_contained_q5 [] { "q5", NULL }; +const char *g_contained_q6 [] { "q6", NULL }; +const char *g_contained_q7 [] { "q7", NULL }; +const char *g_contained_q8 [] { "q8", NULL }; +const char *g_contained_q9 [] { "q9", NULL }; +const char *g_contained_q10[] { "q10", NULL }; +const char *g_contained_q11[] { "q11", NULL }; +const char *g_contained_q12[] { "q12", NULL }; +const char *g_contained_q13[] { "q13", NULL }; +const char *g_contained_q14[] { "q14", NULL }; +const char *g_contained_q15[] { "q15", NULL }; + +const char *g_invalidate_q0[] { "q0", "d0" , "d1" , "s0" , "s1" , "s2" , "s3" , NULL }; +const char *g_invalidate_q1[] { "q1", "d2" , "d3" , "s4" , "s5" , "s6" , "s7" , NULL }; +const char *g_invalidate_q2[] { "q2", "d4" , "d5" , "s8" , "s9" , "s10", "s11", NULL }; +const char *g_invalidate_q3[] { "q3", "d6" , "d7" , "s12", "s13", "s14", "s15", NULL }; +const char *g_invalidate_q4[] { "q4", "d8" , "d9" , "s16", "s17", "s18", "s19", NULL }; +const char *g_invalidate_q5[] { "q5", "d10", "d11", "s20", "s21", "s22", "s23", NULL }; +const char *g_invalidate_q6[] { "q6", "d12", "d13", "s24", "s25", "s26", "s27", NULL }; +const char *g_invalidate_q7[] { "q7", "d14", "d15", "s28", "s29", "s30", "s31", NULL }; +const char *g_invalidate_q8[] { "q8", "d16", "d17", NULL }; +const char *g_invalidate_q9[] { "q9", "d18", "d19", NULL }; +const char *g_invalidate_q10[] { "q10", "d20", "d21", NULL }; +const char *g_invalidate_q11[] { "q11", "d22", "d23", NULL }; +const char *g_invalidate_q12[] { "q12", "d24", "d25", NULL }; +const char *g_invalidate_q13[] { "q13", "d26", "d27", NULL }; +const char *g_invalidate_q14[] { "q14", "d28", "d29", NULL }; +const char *g_invalidate_q15[] { "q15", "d30", "d31", NULL }; + +#define VFP_S_OFFSET_IDX(idx) (((idx) % 4) * 4) // offset into q reg: 0, 4, 8, 12 +#define VFP_D_OFFSET_IDX(idx) (((idx) % 2) * 8) // offset into q reg: 0, 8 +#define VFP_Q_OFFSET_IDX(idx) (VFP_S_OFFSET_IDX ((idx) * 4)) + +#define VFP_OFFSET_NAME(reg) (offsetof (DNBArchMachARM::FPU, __##reg) + offsetof (DNBArchMachARM::Context, vfp)) + +#define FLOAT_FORMAT Float + +#define DEFINE_VFP_S_IDX(idx) e_regSetVFP, vfp_s##idx, "s" #idx, NULL, IEEE754, FLOAT_FORMAT, 4, VFP_S_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_s##idx, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM +#define DEFINE_VFP_D_IDX(idx) e_regSetVFP, vfp_d##idx, "d" #idx, NULL, IEEE754, FLOAT_FORMAT, 8, VFP_D_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_d##idx, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM +#define DEFINE_VFP_Q_IDX(idx) e_regSetVFP, vfp_q##idx, "q" #idx, NULL, Vector, VectorOfUInt8, 16, VFP_Q_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_q##idx, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM + +// Floating point registers +const DNBRegisterInfo +DNBArchMachARM::g_vfp_registers[] = +{ + { DEFINE_VFP_S_IDX ( 0), g_contained_q0, g_invalidate_q0 }, + { DEFINE_VFP_S_IDX ( 1), g_contained_q0, g_invalidate_q0 }, + { DEFINE_VFP_S_IDX ( 2), g_contained_q0, g_invalidate_q0 }, + { DEFINE_VFP_S_IDX ( 3), g_contained_q0, g_invalidate_q0 }, + { DEFINE_VFP_S_IDX ( 4), g_contained_q1, g_invalidate_q1 }, + { DEFINE_VFP_S_IDX ( 5), g_contained_q1, g_invalidate_q1 }, + { DEFINE_VFP_S_IDX ( 6), g_contained_q1, g_invalidate_q1 }, + { DEFINE_VFP_S_IDX ( 7), g_contained_q1, g_invalidate_q1 }, + { DEFINE_VFP_S_IDX ( 8), g_contained_q2, g_invalidate_q2 }, + { DEFINE_VFP_S_IDX ( 9), g_contained_q2, g_invalidate_q2 }, + { DEFINE_VFP_S_IDX (10), g_contained_q2, g_invalidate_q2 }, + { DEFINE_VFP_S_IDX (11), g_contained_q2, g_invalidate_q2 }, + { DEFINE_VFP_S_IDX (12), g_contained_q3, g_invalidate_q3 }, + { DEFINE_VFP_S_IDX (13), g_contained_q3, g_invalidate_q3 }, + { DEFINE_VFP_S_IDX (14), g_contained_q3, g_invalidate_q3 }, + { DEFINE_VFP_S_IDX (15), g_contained_q3, g_invalidate_q3 }, + { DEFINE_VFP_S_IDX (16), g_contained_q4, g_invalidate_q4 }, + { DEFINE_VFP_S_IDX (17), g_contained_q4, g_invalidate_q4 }, + { DEFINE_VFP_S_IDX (18), g_contained_q4, g_invalidate_q4 }, + { DEFINE_VFP_S_IDX (19), g_contained_q4, g_invalidate_q4 }, + { DEFINE_VFP_S_IDX (20), g_contained_q5, g_invalidate_q5 }, + { DEFINE_VFP_S_IDX (21), g_contained_q5, g_invalidate_q5 }, + { DEFINE_VFP_S_IDX (22), g_contained_q5, g_invalidate_q5 }, + { DEFINE_VFP_S_IDX (23), g_contained_q5, g_invalidate_q5 }, + { DEFINE_VFP_S_IDX (24), g_contained_q6, g_invalidate_q6 }, + { DEFINE_VFP_S_IDX (25), g_contained_q6, g_invalidate_q6 }, + { DEFINE_VFP_S_IDX (26), g_contained_q6, g_invalidate_q6 }, + { DEFINE_VFP_S_IDX (27), g_contained_q6, g_invalidate_q6 }, + { DEFINE_VFP_S_IDX (28), g_contained_q7, g_invalidate_q7 }, + { DEFINE_VFP_S_IDX (29), g_contained_q7, g_invalidate_q7 }, + { DEFINE_VFP_S_IDX (30), g_contained_q7, g_invalidate_q7 }, + { DEFINE_VFP_S_IDX (31), g_contained_q7, g_invalidate_q7 }, + + { DEFINE_VFP_D_IDX (0), g_contained_q0, g_invalidate_q0 }, + { DEFINE_VFP_D_IDX (1), g_contained_q0, g_invalidate_q0 }, + { DEFINE_VFP_D_IDX (2), g_contained_q1, g_invalidate_q1 }, + { DEFINE_VFP_D_IDX (3), g_contained_q1, g_invalidate_q1 }, + { DEFINE_VFP_D_IDX (4), g_contained_q2, g_invalidate_q2 }, + { DEFINE_VFP_D_IDX (5), g_contained_q2, g_invalidate_q2 }, + { DEFINE_VFP_D_IDX (6), g_contained_q3, g_invalidate_q3 }, + { DEFINE_VFP_D_IDX (7), g_contained_q3, g_invalidate_q3 }, + { DEFINE_VFP_D_IDX (8), g_contained_q4, g_invalidate_q4 }, + { DEFINE_VFP_D_IDX (9), g_contained_q4, g_invalidate_q4 }, + { DEFINE_VFP_D_IDX (10), g_contained_q5, g_invalidate_q5 }, + { DEFINE_VFP_D_IDX (11), g_contained_q5, g_invalidate_q5 }, + { DEFINE_VFP_D_IDX (12), g_contained_q6, g_invalidate_q6 }, + { DEFINE_VFP_D_IDX (13), g_contained_q6, g_invalidate_q6 }, + { DEFINE_VFP_D_IDX (14), g_contained_q7, g_invalidate_q7 }, + { DEFINE_VFP_D_IDX (15), g_contained_q7, g_invalidate_q7 }, + { DEFINE_VFP_D_IDX (16), g_contained_q8, g_invalidate_q8 }, + { DEFINE_VFP_D_IDX (17), g_contained_q8, g_invalidate_q8 }, + { DEFINE_VFP_D_IDX (18), g_contained_q9, g_invalidate_q9 }, + { DEFINE_VFP_D_IDX (19), g_contained_q9, g_invalidate_q9 }, + { DEFINE_VFP_D_IDX (20), g_contained_q10, g_invalidate_q10 }, + { DEFINE_VFP_D_IDX (21), g_contained_q10, g_invalidate_q10 }, + { DEFINE_VFP_D_IDX (22), g_contained_q11, g_invalidate_q11 }, + { DEFINE_VFP_D_IDX (23), g_contained_q11, g_invalidate_q11 }, + { DEFINE_VFP_D_IDX (24), g_contained_q12, g_invalidate_q12 }, + { DEFINE_VFP_D_IDX (25), g_contained_q12, g_invalidate_q12 }, + { DEFINE_VFP_D_IDX (26), g_contained_q13, g_invalidate_q13 }, + { DEFINE_VFP_D_IDX (27), g_contained_q13, g_invalidate_q13 }, + { DEFINE_VFP_D_IDX (28), g_contained_q14, g_invalidate_q14 }, + { DEFINE_VFP_D_IDX (29), g_contained_q14, g_invalidate_q14 }, + { DEFINE_VFP_D_IDX (30), g_contained_q15, g_invalidate_q15 }, + { DEFINE_VFP_D_IDX (31), g_contained_q15, g_invalidate_q15 }, + + { DEFINE_VFP_Q_IDX (0), NULL, g_invalidate_q0 }, + { DEFINE_VFP_Q_IDX (1), NULL, g_invalidate_q1 }, + { DEFINE_VFP_Q_IDX (2), NULL, g_invalidate_q2 }, + { DEFINE_VFP_Q_IDX (3), NULL, g_invalidate_q3 }, + { DEFINE_VFP_Q_IDX (4), NULL, g_invalidate_q4 }, + { DEFINE_VFP_Q_IDX (5), NULL, g_invalidate_q5 }, + { DEFINE_VFP_Q_IDX (6), NULL, g_invalidate_q6 }, + { DEFINE_VFP_Q_IDX (7), NULL, g_invalidate_q7 }, + { DEFINE_VFP_Q_IDX (8), NULL, g_invalidate_q8 }, + { DEFINE_VFP_Q_IDX (9), NULL, g_invalidate_q9 }, + { DEFINE_VFP_Q_IDX (10), NULL, g_invalidate_q10 }, + { DEFINE_VFP_Q_IDX (11), NULL, g_invalidate_q11 }, + { DEFINE_VFP_Q_IDX (12), NULL, g_invalidate_q12 }, + { DEFINE_VFP_Q_IDX (13), NULL, g_invalidate_q13 }, + { DEFINE_VFP_Q_IDX (14), NULL, g_invalidate_q14 }, + { DEFINE_VFP_Q_IDX (15), NULL, g_invalidate_q15 }, + +#if defined (__arm64__) || defined (__aarch64__) + { e_regSetVFP, vfp_fpsr, "fpsr", NULL, Uint, Hex, 4, VFP_OFFSET_NAME(fpsr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, + { e_regSetVFP, vfp_fpcr, "fpcr", NULL, Uint, Hex, 4, VFP_OFFSET_NAME(fpcr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL } +#else + { e_regSetVFP, vfp_fpscr, "fpscr", NULL, Uint, Hex, 4, VFP_OFFSET_NAME(fpscr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL } +#endif +}; + +// Exception registers + +const DNBRegisterInfo +DNBArchMachARM::g_exc_registers[] = +{ + { e_regSetVFP, exc_exception , "exception" , NULL, Uint, Hex, 4, EXC_OFFSET(exception) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM }, + { e_regSetVFP, exc_fsr , "fsr" , NULL, Uint, Hex, 4, EXC_OFFSET(fsr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM }, + { e_regSetVFP, exc_far , "far" , NULL, Uint, Hex, 4, EXC_OFFSET(far) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM } +}; + +// Number of registers in each register set +const size_t DNBArchMachARM::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchMachARM::k_num_vfp_registers = sizeof(g_vfp_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchMachARM::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchMachARM::k_num_all_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. +//---------------------------------------------------------------------- +const DNBRegisterSetInfo +DNBArchMachARM::g_reg_sets[] = +{ + { "ARM Registers", NULL, k_num_all_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 DNBArchMachARM::k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo); + + +const DNBRegisterSetInfo * +DNBArchMachARM::GetRegisterSetInfo(nub_size_t *num_reg_sets) +{ + *num_reg_sets = k_num_register_sets; + return g_reg_sets; +} + +bool +DNBArchMachARM::GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = gpr_pc; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = gpr_sp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = gpr_r7; // is this the right reg? + break; + + case GENERIC_REGNUM_RA: // Return Address + set = e_regSetGPR; + reg = gpr_lr; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = gpr_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.context.gpr.__r[reg]; + return true; + } + break; + + case e_regSetVFP: + // "reg" is an index into the floating point register set at this point. + // We need to translate it up so entry 0 in the fp reg set is the same as vfp_s0 + // in the enumerated values for case statement below. + if (reg >= vfp_s0 && reg <= vfp_s31) + { +#if defined (__arm64__) || defined (__aarch64__) + uint32_t *s_reg = ((uint32_t *) &m_state.context.vfp.__v[0]) + (reg - vfp_s0); + memcpy (&value->value.v_uint8, s_reg, 4); +#else + value->value.uint32 = m_state.context.vfp.__r[reg]; +#endif + return true; + } + else if (reg >= vfp_d0 && reg <= vfp_d31) + { +#if defined (__arm64__) || defined (__aarch64__) + uint64_t *d_reg = ((uint64_t *) &m_state.context.vfp.__v[0]) + (reg - vfp_d0); + memcpy (&value->value.v_uint8, d_reg, 8); +#else + uint32_t d_reg_idx = reg - vfp_d0; + uint32_t s_reg_idx = d_reg_idx * 2; + value->value.v_sint32[0] = m_state.context.vfp.__r[s_reg_idx + 0]; + value->value.v_sint32[1] = m_state.context.vfp.__r[s_reg_idx + 1]; +#endif + return true; + } + else if (reg >= vfp_q0 && reg <= vfp_q15) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&value->value.v_uint8, (uint8_t *) &m_state.context.vfp.__v[reg - vfp_q0], 16); +#else + uint32_t s_reg_idx = (reg - vfp_q0) * 4; + memcpy (&value->value.v_uint8, (uint8_t *) &m_state.context.vfp.__r[s_reg_idx], 16); +#endif + return true; + } +#if defined (__arm64__) || defined (__aarch64__) + else if (reg == vfp_fpsr) + { + value->value.uint32 = m_state.context.vfp.__fpsr; + return true; + } + else if (reg == vfp_fpcr) + { + value->value.uint32 = m_state.context.vfp.__fpcr; + return true; + } +#else + else if (reg == vfp_fpscr) + { + value->value.uint32 = m_state.context.vfp.__fpscr; + return true; + } +#endif + break; + + case e_regSetEXC: + if (reg < k_num_exc_registers) + { + value->value.uint32 = (&m_state.context.exc.__exception)[reg]; + return true; + } + break; + } + } + return false; +} + +bool +DNBArchMachARM::SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = gpr_pc; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = gpr_sp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = gpr_r7; + break; + + case GENERIC_REGNUM_RA: // Return Address + set = e_regSetGPR; + reg = gpr_lr; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = gpr_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.context.gpr.__r[reg] = value->value.uint32; + success = true; + } + break; + + case e_regSetVFP: + // "reg" is an index into the floating point register set at this point. + // We need to translate it up so entry 0 in the fp reg set is the same as vfp_s0 + // in the enumerated values for case statement below. + if (reg >= vfp_s0 && reg <= vfp_s31) + { +#if defined (__arm64__) || defined (__aarch64__) + uint32_t *s_reg = ((uint32_t *) &m_state.context.vfp.__v[0]) + (reg - vfp_s0); + memcpy (s_reg, &value->value.v_uint8, 4); +#else + m_state.context.vfp.__r[reg] = value->value.uint32; +#endif + success = true; + } + else if (reg >= vfp_d0 && reg <= vfp_d31) + { +#if defined (__arm64__) || defined (__aarch64__) + uint64_t *d_reg = ((uint64_t *) &m_state.context.vfp.__v[0]) + (reg - vfp_d0); + memcpy (d_reg, &value->value.v_uint8, 8); +#else + uint32_t d_reg_idx = reg - vfp_d0; + uint32_t s_reg_idx = d_reg_idx * 2; + m_state.context.vfp.__r[s_reg_idx + 0] = value->value.v_sint32[0]; + m_state.context.vfp.__r[s_reg_idx + 1] = value->value.v_sint32[1]; +#endif + success = true; + } + else if (reg >= vfp_q0 && reg <= vfp_q15) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy ((uint8_t *) &m_state.context.vfp.__v[reg - vfp_q0], &value->value.v_uint8, 16); +#else + uint32_t s_reg_idx = (reg - vfp_q0) * 4; + memcpy ((uint8_t *) &m_state.context.vfp.__r[s_reg_idx], &value->value.v_uint8, 16); +#endif + success = true; + } +#if defined (__arm64__) || defined (__aarch64__) + else if (reg == vfp_fpsr) + { + m_state.context.vfp.__fpsr = value->value.uint32; + success = true; + } + else if (reg == vfp_fpcr) + { + m_state.context.vfp.__fpcr = value->value.uint32; + success = true; + } +#else + else if (reg == vfp_fpscr) + { + m_state.context.vfp.__fpscr = value->value.uint32; + success = true; + } +#endif + break; + + case e_regSetEXC: + if (reg < k_num_exc_registers) + { + (&m_state.context.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(false); + case e_regSetGPR: return SetGPRState(); + case e_regSetVFP: return SetVFPState(); + case e_regSetEXC: return SetEXCState(); + case e_regSetDBG: return SetDBGState(false); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +bool +DNBArchMachARM::RegisterSetStateIsValid (int set) const +{ + return m_state.RegsAreValid(set); +} + + +nub_size_t +DNBArchMachARM::GetRegisterContext (void *buf, nub_size_t buf_len) +{ + nub_size_t size = sizeof (m_state.context.gpr) + + sizeof (m_state.context.vfp) + + sizeof (m_state.context.exc); + + if (buf && buf_len) + { + if (size > buf_len) + size = buf_len; + + bool force = false; + if (GetGPRState(force) | GetVFPState(force) | GetEXCState(force)) + return 0; + + // Copy each struct individually to avoid any padding that might be between the structs in m_state.context + uint8_t *p = (uint8_t *)buf; + ::memcpy (p, &m_state.context.gpr, sizeof(m_state.context.gpr)); + p += sizeof(m_state.context.gpr); + ::memcpy (p, &m_state.context.vfp, sizeof(m_state.context.vfp)); + p += sizeof(m_state.context.vfp); + ::memcpy (p, &m_state.context.exc, sizeof(m_state.context.exc)); + p += sizeof(m_state.context.exc); + + size_t bytes_written = p - (uint8_t *)buf; + UNUSED_IF_ASSERT_DISABLED(bytes_written); + assert (bytes_written == size); + + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::GetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size); + // Return the size of the register context even if NULL was passed in + return size; +} + +nub_size_t +DNBArchMachARM::SetRegisterContext (const void *buf, nub_size_t buf_len) +{ + nub_size_t size = sizeof (m_state.context.gpr) + + sizeof (m_state.context.vfp) + + sizeof (m_state.context.exc); + + if (buf == NULL || buf_len == 0) + size = 0; + + if (size) + { + if (size > buf_len) + size = buf_len; + + // Copy each struct individually to avoid any padding that might be between the structs in m_state.context + uint8_t *p = (uint8_t *)buf; + ::memcpy (&m_state.context.gpr, p, sizeof(m_state.context.gpr)); + p += sizeof(m_state.context.gpr); + ::memcpy (&m_state.context.vfp, p, sizeof(m_state.context.vfp)); + p += sizeof(m_state.context.vfp); + ::memcpy (&m_state.context.exc, p, sizeof(m_state.context.exc)); + p += sizeof(m_state.context.exc); + + size_t bytes_written = p - (uint8_t *)buf; + UNUSED_IF_ASSERT_DISABLED(bytes_written); + assert (bytes_written == size); + + if (SetGPRState() | SetVFPState() | SetEXCState()) + return 0; + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::SetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size); + return size; +} + + +uint32_t +DNBArchMachARM::SaveRegisterState () +{ + kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber()); + DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount()); + + // Always re-read the registers because above we call thread_abort_safely(); + bool force = true; + + if ((kret = GetGPRState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::SaveRegisterState () error: GPR regs failed to read: %u ", kret); + } + else if ((kret = GetVFPState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::SaveRegisterState () error: %s regs failed to read: %u", "VFP", kret); + } + else + { + const uint32_t save_id = GetNextRegisterStateSaveID (); + m_saved_register_states[save_id] = m_state.context; + return save_id; + } + return UINT32_MAX; +} + +bool +DNBArchMachARM::RestoreRegisterState (uint32_t save_id) +{ + SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id); + if (pos != m_saved_register_states.end()) + { + m_state.context.gpr = pos->second.gpr; + m_state.context.vfp = pos->second.vfp; + kern_return_t kret; + bool success = true; + if ((kret = SetGPRState()) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::RestoreRegisterState (save_id = %u) error: GPR regs failed to write: %u", save_id, kret); + success = false; + } + else if ((kret = SetVFPState()) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM::RestoreRegisterState (save_id = %u) error: %s regs failed to write: %u", save_id, "VFP", kret); + success = false; + } + m_saved_register_states.erase(pos); + return success; + } + return false; +} + + +#endif // #if defined (__arm__) + diff --git a/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h b/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h new file mode 100644 index 000000000000..ae8974855238 --- /dev/null +++ b/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h @@ -0,0 +1,282 @@ +//===-- 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__) || defined (__arm64__) || defined (__aarch64__) + +#include "DNBArch.h" + +#include + +class MachThread; + +class DNBArchMachARM : public DNBArchProtocol +{ +public: + enum { kMaxNumThumbITBreakpoints = 4 }; + + DNBArchMachARM(MachThread *thread) : + m_thread(thread), + m_state(), + m_disabled_watchpoints(), + m_hw_single_chained_step_addr(INVALID_NUB_ADDRESS), + m_last_decode_pc(INVALID_NUB_ADDRESS), + m_watchpoint_hw_index(-1), + m_watchpoint_did_occur(false), + m_watchpoint_resume_single_step_enabled(false), + m_saved_register_states() + { + m_disabled_watchpoints.resize (16); + memset(&m_dbg_save, 0, sizeof(m_dbg_save)); +#if defined (USE_ARM_DISASSEMBLER_FRAMEWORK) + ThumbStaticsInit(&m_last_decode_thumb); +#endif + } + + virtual ~DNBArchMachARM() + { + } + + static void Initialize(); + static const DNBRegisterSetInfo * + GetRegisterSetInfo(nub_size_t *num_reg_sets); + + virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value); + virtual bool SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value); + 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 uint32_t SaveRegisterState (); + virtual bool RestoreRegisterState (uint32_t save_id); + + 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 DNBArchProtocol *Create (MachThread *thread); + static const uint8_t * 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 bool DisableHardwareBreakpoint (uint32_t hw_break_index); + + virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task); + virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index, bool also_set_on_task); + virtual bool DisableHardwareWatchpoint_helper (uint32_t hw_break_index, bool also_set_on_task); + virtual bool ReenableHardwareWatchpoint (uint32_t hw_break_index); + virtual bool ReenableHardwareWatchpoint_helper (uint32_t hw_break_index); + + virtual bool StepNotComplete (); + virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr); + +#if defined (ARM_DEBUG_STATE32) && (defined (__arm64__) || defined (__aarch64__)) + typedef arm_debug_state32_t DBG; +#else + typedef arm_debug_state_t DBG; +#endif + +protected: + + + kern_return_t EnableHardwareSingleStep (bool enable); + kern_return_t SetSingleStepSoftwareBreakpoints (); + + bool ConditionPassed(uint8_t condition, uint32_t cpsr); +#if defined (USE_ARM_DISASSEMBLER_FRAMEWORK) + bool ComputeNextPC(nub_addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, nub_addr_t *targetPC); + 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); + void DecodeITBlockInstructions(nub_addr_t curr_pc); +#endif + void EvaluateNextInstructionForSoftwareBreakpointSetup(nub_addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, nub_addr_t *nextPC, bool *nextPCIsThumb); + + + typedef enum RegisterSetTag + { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, // ARM_THREAD_STATE + e_regSetVFP, // ARM_VFP_STATE (ARM_NEON_STATE if defined __arm64__) + e_regSetEXC, // ARM_EXCEPTION_STATE + e_regSetDBG, // ARM_DEBUG_STATE (ARM_DEBUG_STATE32 if defined __arm64__) + kNumRegisterSets + } RegisterSet; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + typedef arm_thread_state_t GPR; +#if defined (__arm64__) || defined (__aarch64__) + typedef arm_neon_state_t FPU; +#else + typedef arm_vfp_state_t FPU; +#endif + typedef arm_exception_state_t EXC; + + static const DNBRegisterInfo g_gpr_registers[]; + static const DNBRegisterInfo g_vfp_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_vfp_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; + + struct Context + { + GPR gpr; + FPU vfp; + EXC exc; + }; + + struct State + { + Context context; + DBG 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 m_disabled_watchpoints; + + nub_addr_t m_hw_single_chained_step_addr; + nub_addr_t m_last_decode_pc; + + // The following member variables should be updated atomically. + int32_t m_watchpoint_hw_index; + bool m_watchpoint_did_occur; + bool m_watchpoint_resume_single_step_enabled; + + typedef std::map SaveRegisterStates; + SaveRegisterStates m_saved_register_states; +}; + +#endif // #if defined (__arm__) +#endif // #ifndef __DebugNubArchMachARM_h__ diff --git a/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp b/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp new file mode 100644 index 000000000000..e79d3d52e8f8 --- /dev/null +++ b/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp @@ -0,0 +1,2095 @@ +//===-- DNBArchMachARM64.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__) || defined (__arm64__) || defined (__aarch64__) + +#include "MacOSX/arm64/DNBArchImplARM64.h" + +#if defined (ARM_THREAD_STATE64_COUNT) + +#include "MacOSX/MachProcess.h" +#include "MacOSX/MachThread.h" +#include "DNBBreakpoint.h" +#include "DNBLog.h" +#include "DNBRegisterInfo.h" +#include "DNB.h" + +#include +#include + +// Break only in privileged or user mode +// (PAC bits in the DBGWVRn_EL1 watchpoint control register) +#define S_USER ((uint32_t)(2u << 1)) + +#define BCR_ENABLE ((uint32_t)(1u)) +#define WCR_ENABLE ((uint32_t)(1u)) + +// Watchpoint load/store +// (LSC bits in the DBGWVRn_EL1 watchpoint control register) +#define WCR_LOAD ((uint32_t)(1u << 3)) +#define WCR_STORE ((uint32_t)(1u << 4)) + +// Enable breakpoint, watchpoint, and vector catch debug exceptions. +// (MDE bit in the MDSCR_EL1 register. Equivalent to the MDBGen bit in DBGDSCRext in Aarch32) +#define MDE_ENABLE ((uint32_t)(1u << 15)) + +// Single instruction step +// (SS bit in the MDSCR_EL1 register) +#define SS_ENABLE ((uint32_t)(1u)) + +static const uint8_t g_arm64_breakpoint_opcode[] = { 0x00, 0x00, 0x20, 0xD4 }; // "brk #0", 0xd4200000 in BE byte order +static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; // this armv7 insn also works in arm64 + +// If we need to set one logical watchpoint by using +// two hardware watchpoint registers, the watchpoint +// will be split into a "high" and "low" watchpoint. +// Record both of them in the LoHi array. + +// It's safe to initialize to all 0's since +// hi > lo and therefore LoHi[i] cannot be 0. +static uint32_t LoHi[16] = { 0 }; + + +void +DNBArchMachARM64::Initialize() +{ + DNBArchPluginInfo arch_plugin_info = + { + CPU_TYPE_ARM64, + DNBArchMachARM64::Create, + DNBArchMachARM64::GetRegisterSetInfo, + DNBArchMachARM64::SoftwareBreakpointOpcode + }; + + // Register this arch plug-in with the main protocol class + DNBArchProtocol::RegisterArchPlugin (arch_plugin_info); +} + + +DNBArchProtocol * +DNBArchMachARM64::Create (MachThread *thread) +{ + DNBArchMachARM64 *obj = new DNBArchMachARM64 (thread); + + return obj; +} + +const uint8_t * +DNBArchMachARM64::SoftwareBreakpointOpcode (nub_size_t byte_size) +{ + return g_arm_breakpoint_opcode; +} + +uint32_t +DNBArchMachARM64::GetCPUType() +{ + return CPU_TYPE_ARM64; +} + +uint64_t +DNBArchMachARM64::GetPC(uint64_t failValue) +{ + // Get program counter + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.context.gpr.__pc; + return failValue; +} + +kern_return_t +DNBArchMachARM64::SetPC(uint64_t value) +{ + // Get program counter + kern_return_t err = GetGPRState(false); + if (err == KERN_SUCCESS) + { + m_state.context.gpr.__pc = value; + err = SetGPRState(); + } + return err == KERN_SUCCESS; +} + +uint64_t +DNBArchMachARM64::GetSP(uint64_t failValue) +{ + // Get stack pointer + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.context.gpr.__sp; + return failValue; +} + +kern_return_t +DNBArchMachARM64::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 = e_regSetGPRCount; + kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, &count); + if (DNBLogEnabledForAny (LOG_THREAD)) + { + uint64_t *x = &m_state.context.gpr.__x[0]; + DNBLogThreaded("thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count = %u) regs" + "\n x0=%16.16llx" + "\n x1=%16.16llx" + "\n x2=%16.16llx" + "\n x3=%16.16llx" + "\n x4=%16.16llx" + "\n x5=%16.16llx" + "\n x6=%16.16llx" + "\n x7=%16.16llx" + "\n x8=%16.16llx" + "\n x9=%16.16llx" + "\n x10=%16.16llx" + "\n x11=%16.16llx" + "\n x12=%16.16llx" + "\n x13=%16.16llx" + "\n x14=%16.16llx" + "\n x15=%16.16llx" + "\n x16=%16.16llx" + "\n x17=%16.16llx" + "\n x18=%16.16llx" + "\n x19=%16.16llx" + "\n x20=%16.16llx" + "\n x21=%16.16llx" + "\n x22=%16.16llx" + "\n x23=%16.16llx" + "\n x24=%16.16llx" + "\n x25=%16.16llx" + "\n x26=%16.16llx" + "\n x27=%16.16llx" + "\n x28=%16.16llx" + "\n fp=%16.16llx" + "\n lr=%16.16llx" + "\n sp=%16.16llx" + "\n pc=%16.16llx" + "\n cpsr=%8.8x", + m_thread->MachPortNumber(), + e_regSetGPR, + e_regSetGPRCount, + kret, + count, + x[0], + x[1], + x[2], + x[3], + x[4], + x[5], + x[6], + x[7], + x[8], + x[9], + x[0], + x[11], + x[12], + x[13], + x[14], + x[15], + x[16], + x[17], + x[18], + x[19], + x[20], + x[21], + x[22], + x[23], + x[24], + x[25], + x[26], + x[27], + x[28], + m_state.context.gpr.__fp, + m_state.context.gpr.__lr, + m_state.context.gpr.__sp, + m_state.context.gpr.__pc, + m_state.context.gpr.__cpsr); + } + m_state.SetError(set, Read, kret); + return kret; +} + +kern_return_t +DNBArchMachARM64::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 = e_regSetVFPCount; + kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_NEON_STATE64, (thread_state_t)&m_state.context.vfp, &count); + if (DNBLogEnabledForAny (LOG_THREAD)) + { +#if defined (__arm64__) || defined (__aarch64__) + DNBLogThreaded("thread_get_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs" + "\n q0 = 0x%16.16llx%16.16llx" + "\n q1 = 0x%16.16llx%16.16llx" + "\n q2 = 0x%16.16llx%16.16llx" + "\n q3 = 0x%16.16llx%16.16llx" + "\n q4 = 0x%16.16llx%16.16llx" + "\n q5 = 0x%16.16llx%16.16llx" + "\n q6 = 0x%16.16llx%16.16llx" + "\n q7 = 0x%16.16llx%16.16llx" + "\n q8 = 0x%16.16llx%16.16llx" + "\n q9 = 0x%16.16llx%16.16llx" + "\n q10 = 0x%16.16llx%16.16llx" + "\n q11 = 0x%16.16llx%16.16llx" + "\n q12 = 0x%16.16llx%16.16llx" + "\n q13 = 0x%16.16llx%16.16llx" + "\n q14 = 0x%16.16llx%16.16llx" + "\n q15 = 0x%16.16llx%16.16llx" + "\n q16 = 0x%16.16llx%16.16llx" + "\n q17 = 0x%16.16llx%16.16llx" + "\n q18 = 0x%16.16llx%16.16llx" + "\n q19 = 0x%16.16llx%16.16llx" + "\n q20 = 0x%16.16llx%16.16llx" + "\n q21 = 0x%16.16llx%16.16llx" + "\n q22 = 0x%16.16llx%16.16llx" + "\n q23 = 0x%16.16llx%16.16llx" + "\n q24 = 0x%16.16llx%16.16llx" + "\n q25 = 0x%16.16llx%16.16llx" + "\n q26 = 0x%16.16llx%16.16llx" + "\n q27 = 0x%16.16llx%16.16llx" + "\n q28 = 0x%16.16llx%16.16llx" + "\n q29 = 0x%16.16llx%16.16llx" + "\n q30 = 0x%16.16llx%16.16llx" + "\n q31 = 0x%16.16llx%16.16llx" + "\n fpsr = 0x%8.8x" + "\n fpcr = 0x%8.8x\n\n", + m_thread->MachPortNumber(), + e_regSetVFP, + e_regSetVFPCount, + kret, + count, + ((uint64_t *)&m_state.context.vfp.__v[0])[0] , ((uint64_t *)&m_state.context.vfp.__v[0])[1], + ((uint64_t *)&m_state.context.vfp.__v[1])[0] , ((uint64_t *)&m_state.context.vfp.__v[1])[1], + ((uint64_t *)&m_state.context.vfp.__v[2])[0] , ((uint64_t *)&m_state.context.vfp.__v[2])[1], + ((uint64_t *)&m_state.context.vfp.__v[3])[0] , ((uint64_t *)&m_state.context.vfp.__v[3])[1], + ((uint64_t *)&m_state.context.vfp.__v[4])[0] , ((uint64_t *)&m_state.context.vfp.__v[4])[1], + ((uint64_t *)&m_state.context.vfp.__v[5])[0] , ((uint64_t *)&m_state.context.vfp.__v[5])[1], + ((uint64_t *)&m_state.context.vfp.__v[6])[0] , ((uint64_t *)&m_state.context.vfp.__v[6])[1], + ((uint64_t *)&m_state.context.vfp.__v[7])[0] , ((uint64_t *)&m_state.context.vfp.__v[7])[1], + ((uint64_t *)&m_state.context.vfp.__v[8])[0] , ((uint64_t *)&m_state.context.vfp.__v[8])[1], + ((uint64_t *)&m_state.context.vfp.__v[9])[0] , ((uint64_t *)&m_state.context.vfp.__v[9])[1], + ((uint64_t *)&m_state.context.vfp.__v[10])[0], ((uint64_t *)&m_state.context.vfp.__v[10])[1], + ((uint64_t *)&m_state.context.vfp.__v[11])[0], ((uint64_t *)&m_state.context.vfp.__v[11])[1], + ((uint64_t *)&m_state.context.vfp.__v[12])[0], ((uint64_t *)&m_state.context.vfp.__v[12])[1], + ((uint64_t *)&m_state.context.vfp.__v[13])[0], ((uint64_t *)&m_state.context.vfp.__v[13])[1], + ((uint64_t *)&m_state.context.vfp.__v[14])[0], ((uint64_t *)&m_state.context.vfp.__v[14])[1], + ((uint64_t *)&m_state.context.vfp.__v[15])[0], ((uint64_t *)&m_state.context.vfp.__v[15])[1], + ((uint64_t *)&m_state.context.vfp.__v[16])[0], ((uint64_t *)&m_state.context.vfp.__v[16])[1], + ((uint64_t *)&m_state.context.vfp.__v[17])[0], ((uint64_t *)&m_state.context.vfp.__v[17])[1], + ((uint64_t *)&m_state.context.vfp.__v[18])[0], ((uint64_t *)&m_state.context.vfp.__v[18])[1], + ((uint64_t *)&m_state.context.vfp.__v[19])[0], ((uint64_t *)&m_state.context.vfp.__v[19])[1], + ((uint64_t *)&m_state.context.vfp.__v[20])[0], ((uint64_t *)&m_state.context.vfp.__v[20])[1], + ((uint64_t *)&m_state.context.vfp.__v[21])[0], ((uint64_t *)&m_state.context.vfp.__v[21])[1], + ((uint64_t *)&m_state.context.vfp.__v[22])[0], ((uint64_t *)&m_state.context.vfp.__v[22])[1], + ((uint64_t *)&m_state.context.vfp.__v[23])[0], ((uint64_t *)&m_state.context.vfp.__v[23])[1], + ((uint64_t *)&m_state.context.vfp.__v[24])[0], ((uint64_t *)&m_state.context.vfp.__v[24])[1], + ((uint64_t *)&m_state.context.vfp.__v[25])[0], ((uint64_t *)&m_state.context.vfp.__v[25])[1], + ((uint64_t *)&m_state.context.vfp.__v[26])[0], ((uint64_t *)&m_state.context.vfp.__v[26])[1], + ((uint64_t *)&m_state.context.vfp.__v[27])[0], ((uint64_t *)&m_state.context.vfp.__v[27])[1], + ((uint64_t *)&m_state.context.vfp.__v[28])[0], ((uint64_t *)&m_state.context.vfp.__v[28])[1], + ((uint64_t *)&m_state.context.vfp.__v[29])[0], ((uint64_t *)&m_state.context.vfp.__v[29])[1], + ((uint64_t *)&m_state.context.vfp.__v[30])[0], ((uint64_t *)&m_state.context.vfp.__v[30])[1], + ((uint64_t *)&m_state.context.vfp.__v[31])[0], ((uint64_t *)&m_state.context.vfp.__v[31])[1], + m_state.context.vfp.__fpsr, + m_state.context.vfp.__fpcr); +#endif + } + m_state.SetError(set, Read, kret); + return kret; +} + +kern_return_t +DNBArchMachARM64::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 = e_regSetEXCCount; + kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_EXCEPTION_STATE64, (thread_state_t)&m_state.context.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 +DNBArchMachARM64::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 = e_regSetDBGCount; + kern_return_t kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE64, (thread_state_t)&m_state.dbg, &count); + m_state.SetError(set, Read, kret); + + return kret; +} + +kern_return_t +DNBArchMachARM64::SetGPRState() +{ + int set = e_regSetGPR; + kern_return_t kret = ::thread_set_state(m_thread->MachPortNumber(), ARM_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, e_regSetGPRCount); + 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 +DNBArchMachARM64::SetVFPState() +{ + int set = e_regSetVFP; + kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_NEON_STATE64, (thread_state_t)&m_state.context.vfp, e_regSetVFPCount); + 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 +DNBArchMachARM64::SetEXCState() +{ + int set = e_regSetEXC; + kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_EXCEPTION_STATE64, (thread_state_t)&m_state.context.exc, e_regSetEXCCount); + 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 +DNBArchMachARM64::SetDBGState(bool also_set_on_task) +{ + int set = e_regSetDBG; + kern_return_t kret = ::thread_set_state (m_thread->MachPortNumber(), ARM_DEBUG_STATE64, (thread_state_t)&m_state.dbg, e_regSetDBGCount); + if (also_set_on_task) + { + kern_return_t task_kret = task_set_state (m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE64, (thread_state_t)&m_state.dbg, e_regSetDBGCount); + if (task_kret != KERN_SUCCESS) + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::SetDBGState failed to set debug control register state: 0x%8.8x.", task_kret); + } + 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 +DNBArchMachARM64::ThreadWillResume() +{ + // Do we need to step this thread? If so, let the mach thread tell us so. + if (m_thread->IsStepping()) + { + EnableHardwareSingleStep(true); + } + + // Disable the triggered watchpoint temporarily before we resume. + // Plus, we try to enable hardware single step to execute past the instruction which triggered our watchpoint. + if (m_watchpoint_did_occur) + { + if (m_watchpoint_hw_index >= 0) + { + kern_return_t kret = GetDBGState(false); + if (kret == KERN_SUCCESS && !IsWatchpointEnabled(m_state.dbg, m_watchpoint_hw_index)) { + // The watchpoint might have been disabled by the user. We don't need to do anything at all + // to enable hardware single stepping. + m_watchpoint_did_occur = false; + m_watchpoint_hw_index = -1; + return; + } + + DisableHardwareWatchpoint(m_watchpoint_hw_index, false); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() DisableHardwareWatchpoint(%d) called", + m_watchpoint_hw_index); + + // Enable hardware single step to move past the watchpoint-triggering instruction. + m_watchpoint_resume_single_step_enabled = (EnableHardwareSingleStep(true) == KERN_SUCCESS); + + // If we are not able to enable single step to move past the watchpoint-triggering instruction, + // at least we should reset the two watchpoint member variables so that the next time around + // this callback function is invoked, the enclosing logical branch is skipped. + if (!m_watchpoint_resume_single_step_enabled) { + // Reset the two watchpoint member variables. + m_watchpoint_did_occur = false; + m_watchpoint_hw_index = -1; + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() failed to enable single step"); + } + else + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() succeeded to enable single step"); + } + } +} + +bool +DNBArchMachARM64::NotifyException(MachException::Data& exc) +{ + + switch (exc.exc_type) + { + default: + break; + case EXC_BREAKPOINT: + if (exc.exc_data.size() == 2 && exc.exc_data[0] == EXC_ARM_DA_DEBUG) + { + // The data break address is passed as exc_data[1]. + nub_addr_t addr = exc.exc_data[1]; + // Find the hardware index with the side effect of possibly massaging the + // addr to return the starting address as seen from the debugger side. + uint32_t hw_index = GetHardwareWatchpointHit(addr); + + // One logical watchpoint was split into two watchpoint locations because + // it was too big. If the watchpoint exception is indicating the 2nd half + // of the two-parter, find the address of the 1st half and report that -- + // that's what lldb is going to expect to see. + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException watchpoint %d was hit on address 0x%llx", hw_index, (uint64_t) addr); + const int num_watchpoints = NumSupportedHardwareWatchpoints (); + for (int i = 0; i < num_watchpoints; i++) + { + if (LoHi[i] != 0 + && LoHi[i] == hw_index + && LoHi[i] != i + && GetWatchpointAddressByIndex (i) != INVALID_NUB_ADDRESS) + { + addr = GetWatchpointAddressByIndex (i); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException It is a linked watchpoint; rewritten to index %d addr 0x%llx", LoHi[i], (uint64_t) addr); + } + } + + if (hw_index != INVALID_NUB_HW_INDEX) + { + m_watchpoint_did_occur = true; + m_watchpoint_hw_index = hw_index; + exc.exc_data[1] = addr; + // Piggyback the hw_index in the exc.data. + exc.exc_data.push_back(hw_index); + } + + return true; + } + break; + } + return false; +} + +bool +DNBArchMachARM64::ThreadDidStop() +{ + bool success = true; + + m_state.InvalidateAllRegisterStates(); + + if (m_watchpoint_resume_single_step_enabled) + { + // Great! We now disable the hardware single step as well as re-enable the hardware watchpoint. + // See also ThreadWillResume(). + if (EnableHardwareSingleStep(false) == KERN_SUCCESS) + { + if (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0) + { + ReenableHardwareWatchpoint(m_watchpoint_hw_index); + m_watchpoint_resume_single_step_enabled = false; + m_watchpoint_did_occur = false; + m_watchpoint_hw_index = -1; + } + else + { + DNBLogError("internal error detected: m_watchpoint_resume_step_enabled is true but (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0) does not hold!"); + } + } + else + { + DNBLogError("internal error detected: m_watchpoint_resume_step_enabled is true but unable to disable single step!"); + } + } + + // 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 +DNBArchMachARM64::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(); + } + + if (enable) + { + DNBLogThreadedIf(LOG_STEP, "%s: Setting MDSCR_EL1 Single Step bit at pc 0x%llx", __FUNCTION__, (uint64_t) m_state.context.gpr.__pc); + m_state.dbg.__mdscr_el1 |= SS_ENABLE; + } + else + { + DNBLogThreadedIf(LOG_STEP, "%s: Clearing MDSCR_EL1 Single Step bit at pc 0x%llx", __FUNCTION__, (uint64_t) m_state.context.gpr.__pc); + m_state.dbg.__mdscr_el1 &= ~(SS_ENABLE); + } + + return SetDBGState(false); +} + +// 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 uint64_t bits(uint64_t value, uint32_t msbit, uint32_t lsbit) +{ + assert(msbit >= lsbit); + uint64_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 +} + +uint32_t +DNBArchMachARM64::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; + + + size_t len; + uint32_t n = 0; + len = sizeof (n); + if (::sysctlbyname("hw.optional.watchpoint", &n, &len, NULL, 0) == 0) + { + g_num_supported_hw_watchpoints = n; + DNBLogThreadedIf(LOG_THREAD, "hw.optional.watchpoint=%u", n); + } + else + { + // For AArch64 we would need to look at ID_AA64DFR0_EL1 but debugserver runs in EL0 so it can't + // access that reg. The kernel should have filled in the sysctls based on it though. +#if defined (__arm__) + uint32_t register_DBGDIDR; + + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + uint32_t numWRPs = bits(register_DBGDIDR, 31, 28); + // Zero is reserved for the WRP count, so don't increment it if it is zero + if (numWRPs > 0) + numWRPs++; + g_num_supported_hw_watchpoints = numWRPs; + DNBLogThreadedIf(LOG_THREAD, "Number of supported hw watchpoints via asm(): %d", g_num_supported_hw_watchpoints); +#endif + } + } + return g_num_supported_hw_watchpoints; +} + +uint32_t +DNBArchMachARM64::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint(addr = 0x%8.8llx, size = %zu, read = %u, write = %u)", (uint64_t)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; + + // Otherwise, can't watch more than 8 bytes per WVR/WCR pair + if (size > 8) + return INVALID_NUB_HW_INDEX; + + // arm64 watchpoints really have an 8-byte alignment requirement. You can put a watchpoint on a 4-byte + // offset address but you can only watch 4 bytes with that watchpoint. + + // arm64 watchpoints on an 8-byte (double word) aligned addr can watch any bytes in that + // 8-byte long region of memory. They can watch the 1st byte, the 2nd byte, 3rd byte, etc, or any + // combination therein by setting the bits in the BAS [12:5] (Byte Address Select) field of + // the DBGWCRn_EL1 reg for the watchpoint. + + // If the MASK [28:24] bits in the DBGWCRn_EL1 allow a single watchpoint to monitor a larger region + // of memory (16 bytes, 32 bytes, or 2GB) but the Byte Address Select bitfield then selects a larger + // range of bytes, instead of individual bytes. See the ARMv8 Debug Architecture manual for details. + // This implementation does not currently use the MASK bits; the largest single region watched by a single + // watchpoint right now is 8-bytes. + + nub_addr_t aligned_wp_address = addr & ~0x7; + uint32_t addr_dword_offset = addr & 0x7; + + // Do we need to split up this logical watchpoint into two hardware watchpoint + // registers? + // e.g. a watchpoint of length 4 on address 6. We need do this with + // one watchpoint on address 0 with bytes 6 & 7 being monitored + // one watchpoint on address 8 with bytes 0, 1, 2, 3 being monitored + + if (addr_dword_offset + size > 8) + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint(addr = 0x%8.8llx, size = %zu) needs two hardware watchpoints slots to monitor", (uint64_t)addr, size); + int low_watchpoint_size = 8 - addr_dword_offset; + int high_watchpoint_size = addr_dword_offset + size - 8; + + uint32_t lo = EnableHardwareWatchpoint(addr, low_watchpoint_size, read, write, also_set_on_task); + if (lo == INVALID_NUB_HW_INDEX) + return INVALID_NUB_HW_INDEX; + uint32_t hi = EnableHardwareWatchpoint (aligned_wp_address + 8, high_watchpoint_size, read, write, also_set_on_task); + if (hi == INVALID_NUB_HW_INDEX) + { + DisableHardwareWatchpoint (lo, also_set_on_task); + return INVALID_NUB_HW_INDEX; + } + // Tag this lo->hi mapping in our database. + LoHi[lo] = hi; + return lo; + } + + // At this point + // 1 aligned_wp_address is the requested address rounded down to 8-byte alignment + // 2 addr_dword_offset is the offset into that double word (8-byte) region that we are watching + // 3 size is the number of bytes within that 8-byte region that we are watching + + // Set the Byte Address Selects bits DBGWCRn_EL1 bits [12:5] based on the above. + // The bit shift and negation operation will give us 0b11 for 2, 0b1111 for 4, etc, up to 0b11111111 for 8. + // then we shift those bits left by the offset into this dword that we are interested in. + // e.g. if we are watching bytes 4,5,6,7 in a dword we want a BAS of 0b11110000. + uint32_t byte_address_select = ((1 << size) - 1) << addr_dword_offset; + + // 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, "DNBArchMachARM64::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints); + } + } + return INVALID_NUB_HW_INDEX; +} + +bool +DNBArchMachARM64::ReenableHardwareWatchpoint (uint32_t hw_index) +{ + // If this logical watchpoint # is actually implemented using + // two hardware watchpoint registers, re-enable both of them. + + if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index]) + { + return ReenableHardwareWatchpoint_helper (hw_index) && ReenableHardwareWatchpoint_helper (LoHi[hw_index]); + } + else + { + return ReenableHardwareWatchpoint_helper (hw_index); + } +} + +bool +DNBArchMachARM64::ReenableHardwareWatchpoint_helper (uint32_t hw_index) +{ + kern_return_t kret = GetDBGState(false); + if (kret != KERN_SUCCESS) + return false; + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (hw_index >= num_hw_points) + return false; + + m_state.dbg.__wvr[hw_index] = m_disabled_watchpoints[hw_index].addr; + m_state.dbg.__wcr[hw_index] = m_disabled_watchpoints[hw_index].control; + + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint( %u ) - WVR%u = 0x%8.8llx WCR%u = 0x%8.8llx", + hw_index, + hw_index, + (uint64_t) m_state.dbg.__wvr[hw_index], + hw_index, + (uint64_t) m_state.dbg.__wcr[hw_index]); + + // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us automatically, don't need to do it here. + + kret = SetDBGState(false); + + return (kret == KERN_SUCCESS); +} + +bool +DNBArchMachARM64::DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task) +{ + if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index]) + { + return DisableHardwareWatchpoint_helper (hw_index, also_set_on_task) && DisableHardwareWatchpoint_helper (LoHi[hw_index], also_set_on_task); + } + else + { + return DisableHardwareWatchpoint_helper (hw_index, also_set_on_task); + } +} + +bool +DNBArchMachARM64::DisableHardwareWatchpoint_helper (uint32_t hw_index, bool also_set_on_task) +{ + kern_return_t kret = GetDBGState(false); + if (kret != KERN_SUCCESS) + return false; + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (hw_index >= num_hw_points) + return false; + + m_disabled_watchpoints[hw_index].addr = m_state.dbg.__wvr[hw_index]; + m_disabled_watchpoints[hw_index].control = m_state.dbg.__wcr[hw_index]; + + m_state.dbg.__wcr[hw_index] &= ~((nub_addr_t)WCR_ENABLE); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::DisableHardwareWatchpoint( %u ) - WVR%u = 0x%8.8llx WCR%u = 0x%8.8llx", + hw_index, + hw_index, + (uint64_t) m_state.dbg.__wvr[hw_index], + hw_index, + (uint64_t) m_state.dbg.__wcr[hw_index]); + + kret = SetDBGState(also_set_on_task); + + return (kret == KERN_SUCCESS); +} + +// This is for checking the Byte Address Select bits in the DBRWCRn_EL1 control register. +// Returns -1 if the trailing bit patterns are not one of: +// { 0b???????1, 0b??????10, 0b?????100, 0b????1000, 0b???10000, 0b??100000, 0b?1000000, 0b10000000 }. +static inline +int32_t +LowestBitSet(uint32_t val) +{ + for (unsigned i = 0; i < 8; ++i) { + if (bit(val, i)) + return i; + } + return -1; +} + +// Iterate through the debug registers; return the index of the first watchpoint whose address matches. +// As a side effect, the starting address as understood by the debugger is returned which could be +// different from 'addr' passed as an in/out argument. +uint32_t +DNBArchMachARM64::GetHardwareWatchpointHit(nub_addr_t &addr) +{ + // Read the debug state + kern_return_t kret = GetDBGState(true); + //DumpDBGState(m_state.dbg); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.", kret); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::GetHardwareWatchpointHit() addr = 0x%llx", (uint64_t)addr); + + // This is the watchpoint value to match against, i.e., word address. + nub_addr_t wp_val = addr & ~((nub_addr_t)3); + if (kret == KERN_SUCCESS) + { + DBG &debug_state = m_state.dbg; + uint32_t i, num = NumSupportedHardwareWatchpoints(); + for (i = 0; i < num; ++i) + { + nub_addr_t wp_addr = GetWatchAddress(debug_state, i); + DNBLogThreadedIf(LOG_WATCHPOINTS, + "DNBArchMachARM64::GetHardwareWatchpointHit() slot: %u (addr = 0x%llx).", + i, (uint64_t)wp_addr); + if (wp_val == wp_addr) { + uint32_t byte_mask = bits(debug_state.__wcr[i], 12, 5); + + // Sanity check the byte_mask, first. + if (LowestBitSet(byte_mask) < 0) + continue; + + // Check that the watchpoint is enabled. + if (!IsWatchpointEnabled(debug_state, i)) + continue; + + // Compute the starting address (from the point of view of the debugger). + addr = wp_addr + LowestBitSet(byte_mask); + return i; + } + } + } + return INVALID_NUB_HW_INDEX; +} + +nub_addr_t +DNBArchMachARM64::GetWatchpointAddressByIndex (uint32_t hw_index) +{ + kern_return_t kret = GetDBGState(true); + if (kret != KERN_SUCCESS) + return INVALID_NUB_ADDRESS; + const uint32_t num = NumSupportedHardwareWatchpoints(); + if (hw_index >= num) + return INVALID_NUB_ADDRESS; + if (IsWatchpointEnabled (m_state.dbg, hw_index)) + return GetWatchAddress (m_state.dbg, hw_index); + return INVALID_NUB_ADDRESS; +} + +bool +DNBArchMachARM64::IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index) +{ + // Watchpoint Control Registers, bitfield definitions + // ... + // Bits Value Description + // [0] 0 Watchpoint disabled + // 1 Watchpoint enabled. + return (debug_state.__wcr[hw_index] & 1u); +} + +nub_addr_t +DNBArchMachARM64::GetWatchAddress(const DBG &debug_state, uint32_t hw_index) +{ + // Watchpoint Value Registers, bitfield definitions + // Bits Description + // [31:2] Watchpoint value (word address, i.e., 4-byte aligned) + // [1:0] RAZ/SBZP + return bits(debug_state.__wvr[hw_index], 63, 0); +} + +//---------------------------------------------------------------------- +// Register information definitions for 64 bit ARMv8. +//---------------------------------------------------------------------- +enum gpr_regnums +{ + gpr_x0 = 0, + gpr_x1, + gpr_x2, + gpr_x3, + gpr_x4, + gpr_x5, + gpr_x6, + gpr_x7, + gpr_x8, + gpr_x9, + gpr_x10, + gpr_x11, + gpr_x12, + gpr_x13, + gpr_x14, + gpr_x15, + gpr_x16, + gpr_x17, + gpr_x18, + gpr_x19, + gpr_x20, + gpr_x21, + gpr_x22, + gpr_x23, + gpr_x24, + gpr_x25, + gpr_x26, + gpr_x27, + gpr_x28, + gpr_fp, gpr_x29 = gpr_fp, + gpr_lr, gpr_x30 = gpr_lr, + gpr_sp, gpr_x31 = gpr_sp, + gpr_pc, + gpr_cpsr, + gpr_w0, + gpr_w1, + gpr_w2, + gpr_w3, + gpr_w4, + gpr_w5, + gpr_w6, + gpr_w7, + gpr_w8, + gpr_w9, + gpr_w10, + gpr_w11, + gpr_w12, + gpr_w13, + gpr_w14, + gpr_w15, + gpr_w16, + gpr_w17, + gpr_w18, + gpr_w19, + gpr_w20, + gpr_w21, + gpr_w22, + gpr_w23, + gpr_w24, + gpr_w25, + gpr_w26, + gpr_w27, + gpr_w28 + +}; + +enum +{ + vfp_v0 = 0, + vfp_v1, + vfp_v2, + vfp_v3, + vfp_v4, + vfp_v5, + vfp_v6, + vfp_v7, + vfp_v8, + vfp_v9, + vfp_v10, + vfp_v11, + vfp_v12, + vfp_v13, + vfp_v14, + vfp_v15, + vfp_v16, + vfp_v17, + vfp_v18, + vfp_v19, + vfp_v20, + vfp_v21, + vfp_v22, + vfp_v23, + vfp_v24, + vfp_v25, + vfp_v26, + vfp_v27, + vfp_v28, + vfp_v29, + vfp_v30, + vfp_v31, + vfp_fpsr, + vfp_fpcr, + + // lower 32 bits of the corresponding vfp_v reg. + vfp_s0, + vfp_s1, + vfp_s2, + vfp_s3, + vfp_s4, + vfp_s5, + vfp_s6, + vfp_s7, + vfp_s8, + vfp_s9, + vfp_s10, + vfp_s11, + vfp_s12, + vfp_s13, + vfp_s14, + vfp_s15, + vfp_s16, + vfp_s17, + vfp_s18, + vfp_s19, + vfp_s20, + vfp_s21, + vfp_s22, + vfp_s23, + vfp_s24, + vfp_s25, + vfp_s26, + vfp_s27, + vfp_s28, + vfp_s29, + vfp_s30, + vfp_s31, + + // lower 64 bits of the corresponding vfp_v reg. + vfp_d0, + vfp_d1, + vfp_d2, + vfp_d3, + vfp_d4, + vfp_d5, + vfp_d6, + vfp_d7, + vfp_d8, + vfp_d9, + vfp_d10, + vfp_d11, + vfp_d12, + vfp_d13, + vfp_d14, + vfp_d15, + vfp_d16, + vfp_d17, + vfp_d18, + vfp_d19, + vfp_d20, + vfp_d21, + vfp_d22, + vfp_d23, + vfp_d24, + vfp_d25, + vfp_d26, + vfp_d27, + vfp_d28, + vfp_d29, + vfp_d30, + vfp_d31 +}; + +enum +{ + exc_far = 0, + exc_esr, + exc_exception +}; + +// These numbers from the "DWARF for the ARM 64-bit Architecture (AArch64)" document. + +enum +{ + dwarf_x0 = 0, + dwarf_x1, + dwarf_x2, + dwarf_x3, + dwarf_x4, + dwarf_x5, + dwarf_x6, + dwarf_x7, + dwarf_x8, + dwarf_x9, + dwarf_x10, + dwarf_x11, + dwarf_x12, + dwarf_x13, + dwarf_x14, + dwarf_x15, + dwarf_x16, + dwarf_x17, + dwarf_x18, + dwarf_x19, + dwarf_x20, + dwarf_x21, + dwarf_x22, + dwarf_x23, + dwarf_x24, + dwarf_x25, + dwarf_x26, + dwarf_x27, + dwarf_x28, + dwarf_x29, + dwarf_x30, + dwarf_x31, + dwarf_pc = 32, + dwarf_elr_mode = 33, + dwarf_fp = dwarf_x29, + dwarf_lr = dwarf_x30, + dwarf_sp = dwarf_x31, + // 34-63 reserved + + // V0-V31 (128 bit vector registers) + dwarf_v0 = 64, + dwarf_v1, + dwarf_v2, + dwarf_v3, + dwarf_v4, + dwarf_v5, + dwarf_v6, + dwarf_v7, + dwarf_v8, + dwarf_v9, + dwarf_v10, + dwarf_v11, + dwarf_v12, + dwarf_v13, + dwarf_v14, + dwarf_v15, + dwarf_v16, + dwarf_v17, + dwarf_v18, + dwarf_v19, + dwarf_v20, + dwarf_v21, + dwarf_v22, + dwarf_v23, + dwarf_v24, + dwarf_v25, + dwarf_v26, + dwarf_v27, + dwarf_v28, + dwarf_v29, + dwarf_v30, + dwarf_v31 + + // 96-127 reserved +}; + +enum +{ + debugserver_gpr_x0 = 0, + debugserver_gpr_x1, + debugserver_gpr_x2, + debugserver_gpr_x3, + debugserver_gpr_x4, + debugserver_gpr_x5, + debugserver_gpr_x6, + debugserver_gpr_x7, + debugserver_gpr_x8, + debugserver_gpr_x9, + debugserver_gpr_x10, + debugserver_gpr_x11, + debugserver_gpr_x12, + debugserver_gpr_x13, + debugserver_gpr_x14, + debugserver_gpr_x15, + debugserver_gpr_x16, + debugserver_gpr_x17, + debugserver_gpr_x18, + debugserver_gpr_x19, + debugserver_gpr_x20, + debugserver_gpr_x21, + debugserver_gpr_x22, + debugserver_gpr_x23, + debugserver_gpr_x24, + debugserver_gpr_x25, + debugserver_gpr_x26, + debugserver_gpr_x27, + debugserver_gpr_x28, + debugserver_gpr_fp, // x29 + debugserver_gpr_lr, // x30 + debugserver_gpr_sp, // sp aka xsp + debugserver_gpr_pc, + debugserver_gpr_cpsr, + debugserver_vfp_v0, + debugserver_vfp_v1, + debugserver_vfp_v2, + debugserver_vfp_v3, + debugserver_vfp_v4, + debugserver_vfp_v5, + debugserver_vfp_v6, + debugserver_vfp_v7, + debugserver_vfp_v8, + debugserver_vfp_v9, + debugserver_vfp_v10, + debugserver_vfp_v11, + debugserver_vfp_v12, + debugserver_vfp_v13, + debugserver_vfp_v14, + debugserver_vfp_v15, + debugserver_vfp_v16, + debugserver_vfp_v17, + debugserver_vfp_v18, + debugserver_vfp_v19, + debugserver_vfp_v20, + debugserver_vfp_v21, + debugserver_vfp_v22, + debugserver_vfp_v23, + debugserver_vfp_v24, + debugserver_vfp_v25, + debugserver_vfp_v26, + debugserver_vfp_v27, + debugserver_vfp_v28, + debugserver_vfp_v29, + debugserver_vfp_v30, + debugserver_vfp_v31, + debugserver_vfp_fpsr, + debugserver_vfp_fpcr +}; + +const char *g_contained_x0[] {"x0", NULL }; +const char *g_contained_x1[] {"x1", NULL }; +const char *g_contained_x2[] {"x2", NULL }; +const char *g_contained_x3[] {"x3", NULL }; +const char *g_contained_x4[] {"x4", NULL }; +const char *g_contained_x5[] {"x5", NULL }; +const char *g_contained_x6[] {"x6", NULL }; +const char *g_contained_x7[] {"x7", NULL }; +const char *g_contained_x8[] {"x8", NULL }; +const char *g_contained_x9[] {"x9", NULL }; +const char *g_contained_x10[] {"x10", NULL }; +const char *g_contained_x11[] {"x11", NULL }; +const char *g_contained_x12[] {"x12", NULL }; +const char *g_contained_x13[] {"x13", NULL }; +const char *g_contained_x14[] {"x14", NULL }; +const char *g_contained_x15[] {"x15", NULL }; +const char *g_contained_x16[] {"x16", NULL }; +const char *g_contained_x17[] {"x17", NULL }; +const char *g_contained_x18[] {"x18", NULL }; +const char *g_contained_x19[] {"x19", NULL }; +const char *g_contained_x20[] {"x20", NULL }; +const char *g_contained_x21[] {"x21", NULL }; +const char *g_contained_x22[] {"x22", NULL }; +const char *g_contained_x23[] {"x23", NULL }; +const char *g_contained_x24[] {"x24", NULL }; +const char *g_contained_x25[] {"x25", NULL }; +const char *g_contained_x26[] {"x26", NULL }; +const char *g_contained_x27[] {"x27", NULL }; +const char *g_contained_x28[] {"x28", NULL }; + +const char *g_invalidate_x0[] {"x0", "w0", NULL }; +const char *g_invalidate_x1[] {"x1", "w1", NULL }; +const char *g_invalidate_x2[] {"x2", "w2", NULL }; +const char *g_invalidate_x3[] {"x3", "w3", NULL }; +const char *g_invalidate_x4[] {"x4", "w4", NULL }; +const char *g_invalidate_x5[] {"x5", "w5", NULL }; +const char *g_invalidate_x6[] {"x6", "w6", NULL }; +const char *g_invalidate_x7[] {"x7", "w7", NULL }; +const char *g_invalidate_x8[] {"x8", "w8", NULL }; +const char *g_invalidate_x9[] {"x9", "w9", NULL }; +const char *g_invalidate_x10[] {"x10", "w10", NULL }; +const char *g_invalidate_x11[] {"x11", "w11", NULL }; +const char *g_invalidate_x12[] {"x12", "w12", NULL }; +const char *g_invalidate_x13[] {"x13", "w13", NULL }; +const char *g_invalidate_x14[] {"x14", "w14", NULL }; +const char *g_invalidate_x15[] {"x15", "w15", NULL }; +const char *g_invalidate_x16[] {"x16", "w16", NULL }; +const char *g_invalidate_x17[] {"x17", "w17", NULL }; +const char *g_invalidate_x18[] {"x18", "w18", NULL }; +const char *g_invalidate_x19[] {"x19", "w19", NULL }; +const char *g_invalidate_x20[] {"x20", "w20", NULL }; +const char *g_invalidate_x21[] {"x21", "w21", NULL }; +const char *g_invalidate_x22[] {"x22", "w22", NULL }; +const char *g_invalidate_x23[] {"x23", "w23", NULL }; +const char *g_invalidate_x24[] {"x24", "w24", NULL }; +const char *g_invalidate_x25[] {"x25", "w25", NULL }; +const char *g_invalidate_x26[] {"x26", "w26", NULL }; +const char *g_invalidate_x27[] {"x27", "w27", NULL }; +const char *g_invalidate_x28[] {"x28", "w28", NULL }; + +#define GPR_OFFSET_IDX(idx) (offsetof (DNBArchMachARM64::GPR, __x[idx])) + +#define GPR_OFFSET_NAME(reg) (offsetof (DNBArchMachARM64::GPR , __##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_IDX(idx, reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 8, GPR_OFFSET_IDX(idx) , dwarf_##reg, dwarf_##reg, gen, debugserver_gpr_##reg, NULL, g_invalidate_x##idx } +#define DEFINE_GPR_NAME(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 8, GPR_OFFSET_NAME(reg), dwarf_##reg, dwarf_##reg, gen, debugserver_gpr_##reg, NULL, NULL } +#define DEFINE_PSEUDO_GPR_IDX(idx, reg) { e_regSetGPR, gpr_##reg, #reg, NULL, Uint, Hex, 4, 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_x##idx, g_invalidate_x##idx } + +//_STRUCT_ARM_THREAD_STATE64 +//{ +// uint64_t x[29]; /* General purpose registers x0-x28 */ +// uint64_t fp; /* Frame pointer x29 */ +// uint64_t lr; /* Link register x30 */ +// uint64_t sp; /* Stack pointer x31 */ +// uint64_t pc; /* Program counter */ +// uint32_t cpsr; /* Current program status register */ +//}; + + +// General purpose registers +const DNBRegisterInfo +DNBArchMachARM64::g_gpr_registers[] = +{ + DEFINE_GPR_IDX ( 0, x0, "arg1", GENERIC_REGNUM_ARG1 ), + DEFINE_GPR_IDX ( 1, x1, "arg2", GENERIC_REGNUM_ARG2 ), + DEFINE_GPR_IDX ( 2, x2, "arg3", GENERIC_REGNUM_ARG3 ), + DEFINE_GPR_IDX ( 3, x3, "arg4", GENERIC_REGNUM_ARG4 ), + DEFINE_GPR_IDX ( 4, x4, "arg5", GENERIC_REGNUM_ARG5 ), + DEFINE_GPR_IDX ( 5, x5, "arg6", GENERIC_REGNUM_ARG6 ), + DEFINE_GPR_IDX ( 6, x6, "arg7", GENERIC_REGNUM_ARG7 ), + DEFINE_GPR_IDX ( 7, x7, "arg8", GENERIC_REGNUM_ARG8 ), + DEFINE_GPR_IDX ( 8, x8, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX ( 9, x9, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (10, x10, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (11, x11, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (12, x12, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (13, x13, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (14, x14, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (15, x15, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (16, x16, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (17, x17, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (18, x18, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (19, x19, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (20, x20, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (21, x21, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (22, x22, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (23, x23, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (24, x24, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (25, x25, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (26, x26, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (27, x27, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_IDX (28, x28, NULL, INVALID_NUB_REGNUM ), + DEFINE_GPR_NAME (fp, "x29", GENERIC_REGNUM_FP), + DEFINE_GPR_NAME (lr, "x30", GENERIC_REGNUM_RA), + DEFINE_GPR_NAME (sp, "xsp", GENERIC_REGNUM_SP), + DEFINE_GPR_NAME (pc, NULL, GENERIC_REGNUM_PC), + + // in armv7 we specify that writing to the CPSR should invalidate r8-12, sp, lr. + // this should be specified for arm64 too even though debugserver is only used for + // userland debugging. + { e_regSetGPR, gpr_cpsr, "cpsr", "flags", Uint, Hex, 4, GPR_OFFSET_NAME(cpsr), dwarf_elr_mode, dwarf_elr_mode, INVALID_NUB_REGNUM, debugserver_gpr_cpsr, NULL, NULL }, + + DEFINE_PSEUDO_GPR_IDX ( 0, w0), + DEFINE_PSEUDO_GPR_IDX ( 1, w1), + DEFINE_PSEUDO_GPR_IDX ( 2, w2), + DEFINE_PSEUDO_GPR_IDX ( 3, w3), + DEFINE_PSEUDO_GPR_IDX ( 4, w4), + DEFINE_PSEUDO_GPR_IDX ( 5, w5), + DEFINE_PSEUDO_GPR_IDX ( 6, w6), + DEFINE_PSEUDO_GPR_IDX ( 7, w7), + DEFINE_PSEUDO_GPR_IDX ( 8, w8), + DEFINE_PSEUDO_GPR_IDX ( 9, w9), + DEFINE_PSEUDO_GPR_IDX (10, w10), + DEFINE_PSEUDO_GPR_IDX (11, w11), + DEFINE_PSEUDO_GPR_IDX (12, w12), + DEFINE_PSEUDO_GPR_IDX (13, w13), + DEFINE_PSEUDO_GPR_IDX (14, w14), + DEFINE_PSEUDO_GPR_IDX (15, w15), + DEFINE_PSEUDO_GPR_IDX (16, w16), + DEFINE_PSEUDO_GPR_IDX (17, w17), + DEFINE_PSEUDO_GPR_IDX (18, w18), + DEFINE_PSEUDO_GPR_IDX (19, w19), + DEFINE_PSEUDO_GPR_IDX (20, w20), + DEFINE_PSEUDO_GPR_IDX (21, w21), + DEFINE_PSEUDO_GPR_IDX (22, w22), + DEFINE_PSEUDO_GPR_IDX (23, w23), + DEFINE_PSEUDO_GPR_IDX (24, w24), + DEFINE_PSEUDO_GPR_IDX (25, w25), + DEFINE_PSEUDO_GPR_IDX (26, w26), + DEFINE_PSEUDO_GPR_IDX (27, w27), + DEFINE_PSEUDO_GPR_IDX (28, w28) +}; + +const char *g_contained_v0[] {"v0", NULL }; +const char *g_contained_v1[] {"v1", NULL }; +const char *g_contained_v2[] {"v2", NULL }; +const char *g_contained_v3[] {"v3", NULL }; +const char *g_contained_v4[] {"v4", NULL }; +const char *g_contained_v5[] {"v5", NULL }; +const char *g_contained_v6[] {"v6", NULL }; +const char *g_contained_v7[] {"v7", NULL }; +const char *g_contained_v8[] {"v8", NULL }; +const char *g_contained_v9[] {"v9", NULL }; +const char *g_contained_v10[] {"v10", NULL }; +const char *g_contained_v11[] {"v11", NULL }; +const char *g_contained_v12[] {"v12", NULL }; +const char *g_contained_v13[] {"v13", NULL }; +const char *g_contained_v14[] {"v14", NULL }; +const char *g_contained_v15[] {"v15", NULL }; +const char *g_contained_v16[] {"v16", NULL }; +const char *g_contained_v17[] {"v17", NULL }; +const char *g_contained_v18[] {"v18", NULL }; +const char *g_contained_v19[] {"v19", NULL }; +const char *g_contained_v20[] {"v20", NULL }; +const char *g_contained_v21[] {"v21", NULL }; +const char *g_contained_v22[] {"v22", NULL }; +const char *g_contained_v23[] {"v23", NULL }; +const char *g_contained_v24[] {"v24", NULL }; +const char *g_contained_v25[] {"v25", NULL }; +const char *g_contained_v26[] {"v26", NULL }; +const char *g_contained_v27[] {"v27", NULL }; +const char *g_contained_v28[] {"v28", NULL }; +const char *g_contained_v29[] {"v29", NULL }; +const char *g_contained_v30[] {"v30", NULL }; +const char *g_contained_v31[] {"v31", NULL }; + +const char *g_invalidate_v0[] {"v0", "d0", "s0", NULL }; +const char *g_invalidate_v1[] {"v1", "d1", "s1", NULL }; +const char *g_invalidate_v2[] {"v2", "d2", "s2", NULL }; +const char *g_invalidate_v3[] {"v3", "d3", "s3", NULL }; +const char *g_invalidate_v4[] {"v4", "d4", "s4", NULL }; +const char *g_invalidate_v5[] {"v5", "d5", "s5", NULL }; +const char *g_invalidate_v6[] {"v6", "d6", "s6", NULL }; +const char *g_invalidate_v7[] {"v7", "d7", "s7", NULL }; +const char *g_invalidate_v8[] {"v8", "d8", "s8", NULL }; +const char *g_invalidate_v9[] {"v9", "d9", "s9", NULL }; +const char *g_invalidate_v10[] {"v10", "d10", "s10", NULL }; +const char *g_invalidate_v11[] {"v11", "d11", "s11", NULL }; +const char *g_invalidate_v12[] {"v12", "d12", "s12", NULL }; +const char *g_invalidate_v13[] {"v13", "d13", "s13", NULL }; +const char *g_invalidate_v14[] {"v14", "d14", "s14", NULL }; +const char *g_invalidate_v15[] {"v15", "d15", "s15", NULL }; +const char *g_invalidate_v16[] {"v16", "d16", "s16", NULL }; +const char *g_invalidate_v17[] {"v17", "d17", "s17", NULL }; +const char *g_invalidate_v18[] {"v18", "d18", "s18", NULL }; +const char *g_invalidate_v19[] {"v19", "d19", "s19", NULL }; +const char *g_invalidate_v20[] {"v20", "d20", "s20", NULL }; +const char *g_invalidate_v21[] {"v21", "d21", "s21", NULL }; +const char *g_invalidate_v22[] {"v22", "d22", "s22", NULL }; +const char *g_invalidate_v23[] {"v23", "d23", "s23", NULL }; +const char *g_invalidate_v24[] {"v24", "d24", "s24", NULL }; +const char *g_invalidate_v25[] {"v25", "d25", "s25", NULL }; +const char *g_invalidate_v26[] {"v26", "d26", "s26", NULL }; +const char *g_invalidate_v27[] {"v27", "d27", "s27", NULL }; +const char *g_invalidate_v28[] {"v28", "d28", "s28", NULL }; +const char *g_invalidate_v29[] {"v29", "d29", "s29", NULL }; +const char *g_invalidate_v30[] {"v30", "d30", "s30", NULL }; +const char *g_invalidate_v31[] {"v31", "d31", "s31", NULL }; + +#if defined (__arm64__) || defined (__aarch64__) +#define VFP_V_OFFSET_IDX(idx) (offsetof (DNBArchMachARM64::FPU, __v) + (idx * 16) + offsetof (DNBArchMachARM64::Context, vfp)) +#else +#define VFP_V_OFFSET_IDX(idx) (offsetof (DNBArchMachARM64::FPU, opaque) + (idx * 16) + offsetof (DNBArchMachARM64::Context, vfp)) +#endif +#define VFP_OFFSET_NAME(reg) (offsetof (DNBArchMachARM64::FPU, reg) + offsetof (DNBArchMachARM64::Context, vfp)) +#define EXC_OFFSET(reg) (offsetof (DNBArchMachARM64::EXC, reg) + offsetof (DNBArchMachARM64::Context, exc)) + +//#define FLOAT_FORMAT Float +#define DEFINE_VFP_V_IDX(idx) { e_regSetVFP, vfp_v##idx, "v" #idx, "q" #idx, Vector, VectorOfUInt8, 16, VFP_V_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_v##idx, INVALID_NUB_REGNUM, debugserver_vfp_v##idx, NULL, g_invalidate_v##idx } +#define DEFINE_PSEUDO_VFP_S_IDX(idx) { e_regSetVFP, vfp_s##idx, "s" #idx, NULL, IEEE754, Float, 4, 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_v##idx, g_invalidate_v##idx } +#define DEFINE_PSEUDO_VFP_D_IDX(idx) { e_regSetVFP, vfp_d##idx, "d" #idx, NULL, IEEE754, Float, 8, 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_v##idx, g_invalidate_v##idx } + +// Floating point registers +const DNBRegisterInfo +DNBArchMachARM64::g_vfp_registers[] = +{ + DEFINE_VFP_V_IDX ( 0), + DEFINE_VFP_V_IDX ( 1), + DEFINE_VFP_V_IDX ( 2), + DEFINE_VFP_V_IDX ( 3), + DEFINE_VFP_V_IDX ( 4), + DEFINE_VFP_V_IDX ( 5), + DEFINE_VFP_V_IDX ( 6), + DEFINE_VFP_V_IDX ( 7), + DEFINE_VFP_V_IDX ( 8), + DEFINE_VFP_V_IDX ( 9), + DEFINE_VFP_V_IDX (10), + DEFINE_VFP_V_IDX (11), + DEFINE_VFP_V_IDX (12), + DEFINE_VFP_V_IDX (13), + DEFINE_VFP_V_IDX (14), + DEFINE_VFP_V_IDX (15), + DEFINE_VFP_V_IDX (16), + DEFINE_VFP_V_IDX (17), + DEFINE_VFP_V_IDX (18), + DEFINE_VFP_V_IDX (19), + DEFINE_VFP_V_IDX (20), + DEFINE_VFP_V_IDX (21), + DEFINE_VFP_V_IDX (22), + DEFINE_VFP_V_IDX (23), + DEFINE_VFP_V_IDX (24), + DEFINE_VFP_V_IDX (25), + DEFINE_VFP_V_IDX (26), + DEFINE_VFP_V_IDX (27), + DEFINE_VFP_V_IDX (28), + DEFINE_VFP_V_IDX (29), + DEFINE_VFP_V_IDX (30), + DEFINE_VFP_V_IDX (31), + { e_regSetVFP, vfp_fpsr, "fpsr", NULL, Uint, Hex, 4, VFP_V_OFFSET_IDX (32) + 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, + { e_regSetVFP, vfp_fpcr, "fpcr", NULL, Uint, Hex, 4, VFP_V_OFFSET_IDX (32) + 4, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, + + DEFINE_PSEUDO_VFP_S_IDX (0), + DEFINE_PSEUDO_VFP_S_IDX (1), + DEFINE_PSEUDO_VFP_S_IDX (2), + DEFINE_PSEUDO_VFP_S_IDX (3), + DEFINE_PSEUDO_VFP_S_IDX (4), + DEFINE_PSEUDO_VFP_S_IDX (5), + DEFINE_PSEUDO_VFP_S_IDX (6), + DEFINE_PSEUDO_VFP_S_IDX (7), + DEFINE_PSEUDO_VFP_S_IDX (8), + DEFINE_PSEUDO_VFP_S_IDX (9), + DEFINE_PSEUDO_VFP_S_IDX (10), + DEFINE_PSEUDO_VFP_S_IDX (11), + DEFINE_PSEUDO_VFP_S_IDX (12), + DEFINE_PSEUDO_VFP_S_IDX (13), + DEFINE_PSEUDO_VFP_S_IDX (14), + DEFINE_PSEUDO_VFP_S_IDX (15), + DEFINE_PSEUDO_VFP_S_IDX (16), + DEFINE_PSEUDO_VFP_S_IDX (17), + DEFINE_PSEUDO_VFP_S_IDX (18), + DEFINE_PSEUDO_VFP_S_IDX (19), + DEFINE_PSEUDO_VFP_S_IDX (20), + DEFINE_PSEUDO_VFP_S_IDX (21), + DEFINE_PSEUDO_VFP_S_IDX (22), + DEFINE_PSEUDO_VFP_S_IDX (23), + DEFINE_PSEUDO_VFP_S_IDX (24), + DEFINE_PSEUDO_VFP_S_IDX (25), + DEFINE_PSEUDO_VFP_S_IDX (26), + DEFINE_PSEUDO_VFP_S_IDX (27), + DEFINE_PSEUDO_VFP_S_IDX (28), + DEFINE_PSEUDO_VFP_S_IDX (29), + DEFINE_PSEUDO_VFP_S_IDX (30), + DEFINE_PSEUDO_VFP_S_IDX (31), + + DEFINE_PSEUDO_VFP_D_IDX (0), + DEFINE_PSEUDO_VFP_D_IDX (1), + DEFINE_PSEUDO_VFP_D_IDX (2), + DEFINE_PSEUDO_VFP_D_IDX (3), + DEFINE_PSEUDO_VFP_D_IDX (4), + DEFINE_PSEUDO_VFP_D_IDX (5), + DEFINE_PSEUDO_VFP_D_IDX (6), + DEFINE_PSEUDO_VFP_D_IDX (7), + DEFINE_PSEUDO_VFP_D_IDX (8), + DEFINE_PSEUDO_VFP_D_IDX (9), + DEFINE_PSEUDO_VFP_D_IDX (10), + DEFINE_PSEUDO_VFP_D_IDX (11), + DEFINE_PSEUDO_VFP_D_IDX (12), + DEFINE_PSEUDO_VFP_D_IDX (13), + DEFINE_PSEUDO_VFP_D_IDX (14), + DEFINE_PSEUDO_VFP_D_IDX (15), + DEFINE_PSEUDO_VFP_D_IDX (16), + DEFINE_PSEUDO_VFP_D_IDX (17), + DEFINE_PSEUDO_VFP_D_IDX (18), + DEFINE_PSEUDO_VFP_D_IDX (19), + DEFINE_PSEUDO_VFP_D_IDX (20), + DEFINE_PSEUDO_VFP_D_IDX (21), + DEFINE_PSEUDO_VFP_D_IDX (22), + DEFINE_PSEUDO_VFP_D_IDX (23), + DEFINE_PSEUDO_VFP_D_IDX (24), + DEFINE_PSEUDO_VFP_D_IDX (25), + DEFINE_PSEUDO_VFP_D_IDX (26), + DEFINE_PSEUDO_VFP_D_IDX (27), + DEFINE_PSEUDO_VFP_D_IDX (28), + DEFINE_PSEUDO_VFP_D_IDX (29), + DEFINE_PSEUDO_VFP_D_IDX (30), + DEFINE_PSEUDO_VFP_D_IDX (31) + +}; + + +//_STRUCT_ARM_EXCEPTION_STATE64 +//{ +// uint64_t far; /* Virtual Fault Address */ +// uint32_t esr; /* Exception syndrome */ +// uint32_t exception; /* number of arm exception taken */ +//}; + +// Exception registers +const DNBRegisterInfo +DNBArchMachARM64::g_exc_registers[] = +{ + { e_regSetEXC, exc_far , "far" , NULL, Uint, Hex, 8, EXC_OFFSET(__far) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, + { e_regSetEXC, exc_esr , "esr" , NULL, Uint, Hex, 4, EXC_OFFSET(__esr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, + { e_regSetEXC, exc_exception , "exception" , NULL, Uint, Hex, 4, EXC_OFFSET(__exception) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL } +}; + +// Number of registers in each register set +const size_t DNBArchMachARM64::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchMachARM64::k_num_vfp_registers = sizeof(g_vfp_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchMachARM64::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchMachARM64::k_num_all_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. +//---------------------------------------------------------------------- +const DNBRegisterSetInfo +DNBArchMachARM64::g_reg_sets[] = +{ + { "ARM64 Registers", NULL, k_num_all_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 DNBArchMachARM64::k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo); + + +const DNBRegisterSetInfo * +DNBArchMachARM64::GetRegisterSetInfo(nub_size_t *num_reg_sets) +{ + *num_reg_sets = k_num_register_sets; + return g_reg_sets; +} + +bool +DNBArchMachARM64::FixGenericRegisterNumber (uint32_t &set, uint32_t ®) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = gpr_pc; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = gpr_sp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = gpr_fp; + break; + + case GENERIC_REGNUM_RA: // Return Address + set = e_regSetGPR; + reg = gpr_lr; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = gpr_cpsr; + break; + + case GENERIC_REGNUM_ARG1: + case GENERIC_REGNUM_ARG2: + case GENERIC_REGNUM_ARG3: + case GENERIC_REGNUM_ARG4: + case GENERIC_REGNUM_ARG5: + case GENERIC_REGNUM_ARG6: + set = e_regSetGPR; + reg = gpr_x0 + reg - GENERIC_REGNUM_ARG1; + break; + + default: + return false; + } + } + return true; +} +bool +DNBArchMachARM64::GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value) +{ + if (!FixGenericRegisterNumber (set, reg)) + 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 <= gpr_pc) + { + value->value.uint64 = m_state.context.gpr.__x[reg]; + return true; + } + else if (reg == gpr_cpsr) + { + value->value.uint32 = m_state.context.gpr.__cpsr; + return true; + } + break; + + case e_regSetVFP: + + if (reg >= vfp_v0 && reg <= vfp_v31) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_v0], 16); +#else + memcpy (&value->value.v_uint8, ((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_v0) * 16), 16); +#endif + return true; + } + else if (reg == vfp_fpsr) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&value->value.uint32, &m_state.context.vfp.__fpsr, 4); +#else + memcpy (&value->value.uint32, ((uint8_t *) &m_state.context.vfp.opaque) + (32 * 16) + 0, 4); +#endif + return true; + } + else if (reg == vfp_fpcr) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&value->value.uint32, &m_state.context.vfp.__fpcr, 4); +#else + memcpy (&value->value.uint32, ((uint8_t *) &m_state.context.vfp.opaque) + (32 * 16) + 4, 4); +#endif + return true; + } + else if (reg >= vfp_s0 && reg <= vfp_s31) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_s0], 4); +#else + memcpy (&value->value.v_uint8, ((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_s0) * 16), 4); +#endif + return true; + } + else if (reg >= vfp_d0 && reg <= vfp_d31) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_d0], 8); +#else + memcpy (&value->value.v_uint8, ((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_d0) * 16), 8); +#endif + return true; + } + break; + + case e_regSetEXC: + if (reg == exc_far) + { + value->value.uint64 = m_state.context.exc.__far; + return true; + } + else if (reg == exc_esr) + { + value->value.uint32 = m_state.context.exc.__esr; + return true; + } + else if (reg == exc_exception) + { + value->value.uint32 = m_state.context.exc.__exception; + return true; + } + break; + } + } + return false; +} + +bool +DNBArchMachARM64::SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value) +{ + if (!FixGenericRegisterNumber (set, reg)) + 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 <= gpr_pc) + { + m_state.context.gpr.__x[reg] = value->value.uint64; + success = true; + } + else if (reg == gpr_cpsr) + { + m_state.context.gpr.__cpsr = value->value.uint32; + success = true; + } + break; + + case e_regSetVFP: + if (reg >= vfp_v0 && reg <= vfp_v31) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&m_state.context.vfp.__v[reg - vfp_v0], &value->value.v_uint8, 16); +#else + memcpy (((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_v0) * 16), &value->value.v_uint8, 16); +#endif + success = true; + } + else if (reg == vfp_fpsr) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&m_state.context.vfp.__fpsr, &value->value.uint32, 4); +#else + memcpy (((uint8_t *) &m_state.context.vfp.opaque) + (32 * 16) + 0, &value->value.uint32, 4); +#endif + success = true; + } + else if (reg == vfp_fpcr) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&m_state.context.vfp.__fpcr, &value->value.uint32, 4); +#else + memcpy (((uint8_t *) m_state.context.vfp.opaque) + (32 * 16) + 4, &value->value.uint32, 4); +#endif + success = true; + } + else if (reg >= vfp_s0 && reg <= vfp_s31) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&m_state.context.vfp.__v[reg - vfp_s0], &value->value.v_uint8, 4); +#else + memcpy (((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_s0) * 16), &value->value.v_uint8, 4); +#endif + success = true; + } + else if (reg >= vfp_d0 && reg <= vfp_d31) + { +#if defined (__arm64__) || defined (__aarch64__) + memcpy (&m_state.context.vfp.__v[reg - vfp_d0], &value->value.v_uint8, 8); +#else + memcpy (((uint8_t *) &m_state.context.vfp.opaque) + ((reg - vfp_d0) * 16), &value->value.v_uint8, 8); +#endif + success = true; + } + break; + + case e_regSetEXC: + if (reg == exc_far) + { + m_state.context.exc.__far = value->value.uint64; + success = true; + } + else if (reg == exc_esr) + { + m_state.context.exc.__esr = value->value.uint32; + success = true; + } + else if (reg == exc_exception) + { + m_state.context.exc.__exception = value->value.uint32; + success = true; + } + break; + } + + } + if (success) + return SetRegisterState(set) == KERN_SUCCESS; + return false; +} + +kern_return_t +DNBArchMachARM64::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 +DNBArchMachARM64::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(false); + case e_regSetGPR: return SetGPRState(); + case e_regSetVFP: return SetVFPState(); + case e_regSetEXC: return SetEXCState(); + case e_regSetDBG: return SetDBGState(false); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +bool +DNBArchMachARM64::RegisterSetStateIsValid (int set) const +{ + return m_state.RegsAreValid(set); +} + + +nub_size_t +DNBArchMachARM64::GetRegisterContext (void *buf, nub_size_t buf_len) +{ + nub_size_t size = sizeof (m_state.context.gpr) + + sizeof (m_state.context.vfp) + + sizeof (m_state.context.exc); + + if (buf && buf_len) + { + if (size > buf_len) + size = buf_len; + + bool force = false; + if (GetGPRState(force) | GetVFPState(force) | GetEXCState(force)) + return 0; + + // Copy each struct individually to avoid any padding that might be between the structs in m_state.context + uint8_t *p = (uint8_t *)buf; + ::memcpy (p, &m_state.context.gpr, sizeof(m_state.context.gpr)); + p += sizeof(m_state.context.gpr); + ::memcpy (p, &m_state.context.vfp, sizeof(m_state.context.vfp)); + p += sizeof(m_state.context.vfp); + ::memcpy (p, &m_state.context.exc, sizeof(m_state.context.exc)); + p += sizeof(m_state.context.exc); + + size_t bytes_written = p - (uint8_t *)buf; + UNUSED_IF_ASSERT_DISABLED(bytes_written); + assert (bytes_written == size); + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::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 +DNBArchMachARM64::SetRegisterContext (const void *buf, nub_size_t buf_len) +{ + nub_size_t size = sizeof (m_state.context.gpr) + + sizeof (m_state.context.vfp) + + sizeof (m_state.context.exc); + + if (buf == NULL || buf_len == 0) + size = 0; + + if (size) + { + if (size > buf_len) + size = buf_len; + + // Copy each struct individually to avoid any padding that might be between the structs in m_state.context + uint8_t *p = (uint8_t *)buf; + ::memcpy (&m_state.context.gpr, p, sizeof(m_state.context.gpr)); + p += sizeof(m_state.context.gpr); + ::memcpy (&m_state.context.vfp, p, sizeof(m_state.context.vfp)); + p += sizeof(m_state.context.vfp); + ::memcpy (&m_state.context.exc, p, sizeof(m_state.context.exc)); + p += sizeof(m_state.context.exc); + + size_t bytes_written = p - (uint8_t *)buf; + UNUSED_IF_ASSERT_DISABLED(bytes_written); + assert (bytes_written == size); + SetGPRState(); + SetVFPState(); + SetEXCState(); + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::SetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size); + return size; +} + +uint32_t +DNBArchMachARM64::SaveRegisterState () +{ + kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber()); + DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount()); + + // Always re-read the registers because above we call thread_abort_safely(); + bool force = true; + + if ((kret = GetGPRState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::SaveRegisterState () error: GPR regs failed to read: %u ", kret); + } + else if ((kret = GetVFPState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::SaveRegisterState () error: %s regs failed to read: %u", "VFP", kret); + } + else + { + const uint32_t save_id = GetNextRegisterStateSaveID (); + m_saved_register_states[save_id] = m_state.context; + return save_id; + } + return UINT32_MAX; +} + +bool +DNBArchMachARM64::RestoreRegisterState (uint32_t save_id) +{ + SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id); + if (pos != m_saved_register_states.end()) + { + m_state.context.gpr = pos->second.gpr; + m_state.context.vfp = pos->second.vfp; + kern_return_t kret; + bool success = true; + if ((kret = SetGPRState()) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::RestoreRegisterState (save_id = %u) error: GPR regs failed to write: %u", save_id, kret); + success = false; + } + else if ((kret = SetVFPState()) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchMachARM64::RestoreRegisterState (save_id = %u) error: %s regs failed to write: %u", save_id, "VFP", kret); + success = false; + } + m_saved_register_states.erase(pos); + return success; + } + return false; +} + + +#endif // #if defined (ARM_THREAD_STATE64_COUNT) +#endif // #if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) diff --git a/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h b/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h new file mode 100644 index 000000000000..7e68e411a765 --- /dev/null +++ b/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h @@ -0,0 +1,272 @@ +//===-- DNBArchMachARM64.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef __DNBArchImplARM64_h__ +#define __DNBArchImplARM64_h__ + +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + +#include +#include + +#if defined (ARM_THREAD_STATE64_COUNT) + +#include "DNBArch.h" + +class MachThread; + +class DNBArchMachARM64 : public DNBArchProtocol +{ +public: + enum { kMaxNumThumbITBreakpoints = 4 }; + + DNBArchMachARM64(MachThread *thread) : + m_thread(thread), + m_state(), + m_disabled_watchpoints(), + m_watchpoint_hw_index(-1), + m_watchpoint_did_occur(false), + m_watchpoint_resume_single_step_enabled(false), + m_saved_register_states() + { + m_disabled_watchpoints.resize (16); + memset(&m_dbg_save, 0, sizeof(m_dbg_save)); + } + + virtual ~DNBArchMachARM64() + { + } + + static void Initialize(); + static const DNBRegisterSetInfo * + GetRegisterSetInfo(nub_size_t *num_reg_sets); + + virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value); + virtual bool SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value); + 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 uint32_t SaveRegisterState (); + virtual bool RestoreRegisterState (uint32_t save_id); + + 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 DNBArchProtocol *Create (MachThread *thread); + static const uint8_t * SoftwareBreakpointOpcode (nub_size_t byte_size); + static uint32_t GetCPUType(); + + virtual uint32_t NumSupportedHardwareWatchpoints(); + virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task); + virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index, bool also_set_on_task); + virtual bool DisableHardwareWatchpoint_helper (uint32_t hw_break_index, bool also_set_on_task); + +protected: + + + kern_return_t EnableHardwareSingleStep (bool enable); + static bool FixGenericRegisterNumber (uint32_t &set, uint32_t ®); + + typedef enum RegisterSetTag + { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, // ARM_THREAD_STATE64, + e_regSetVFP, // ARM_NEON_STATE64, + e_regSetEXC, // ARM_EXCEPTION_STATE64, + e_regSetDBG, // ARM_DEBUG_STATE64, + kNumRegisterSets + } RegisterSet; + + enum + { + e_regSetGPRCount = ARM_THREAD_STATE64_COUNT, + e_regSetVFPCount = ARM_NEON_STATE64_COUNT, + e_regSetEXCCount = ARM_EXCEPTION_STATE64_COUNT, + e_regSetDBGCount = ARM_DEBUG_STATE64_COUNT, + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + typedef arm_thread_state64_t GPR; + typedef arm_neon_state64_t FPU; + typedef arm_exception_state64_t EXC; + + static const DNBRegisterInfo g_gpr_registers[]; + static const DNBRegisterInfo g_vfp_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_vfp_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; + + struct Context + { + GPR gpr; + FPU vfp; + EXC exc; + }; + + struct State + { + Context context; + arm_debug_state64_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 m_disabled_watchpoints; + + // The following member variables should be updated atomically. + int32_t m_watchpoint_hw_index; + bool m_watchpoint_did_occur; + bool m_watchpoint_resume_single_step_enabled; + + typedef std::map SaveRegisterStates; + SaveRegisterStates m_saved_register_states; +}; + +#endif // #if defined (ARM_THREAD_STATE64_COUNT) +#endif // #if defined (__arm__) +#endif // #ifndef __DNBArchImplARM64_h__ diff --git a/tools/debugserver/source/MacOSX/dbgnub-mig.defs b/tools/debugserver/source/MacOSX/dbgnub-mig.defs new file mode 100644 index 000000000000..cd5be1700704 --- /dev/null +++ b/tools/debugserver/source/MacOSX/dbgnub-mig.defs @@ -0,0 +1,5 @@ +/* + * nub.defs + */ + +#import diff --git a/tools/debugserver/source/MacOSX/i386/CMakeLists.txt b/tools/debugserver/source/MacOSX/i386/CMakeLists.txt new file mode 100644 index 000000000000..dee2c1ea96b0 --- /dev/null +++ b/tools/debugserver/source/MacOSX/i386/CMakeLists.txt @@ -0,0 +1,4 @@ +include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source) +add_library(lldbDebugserverMacOSX_I386 + DNBArchImplI386.cpp + ) diff --git a/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp new file mode 100644 index 000000000000..93d4d894300b --- /dev/null +++ b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp @@ -0,0 +1,1901 @@ +//===-- 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__) || defined (__x86_64__) + +#include + +#include "MacOSX/i386/DNBArchImplI386.h" +#include "DNBLog.h" +#include "MachThread.h" +#include "MachProcess.h" + +extern "C" bool CPUHasAVX(); // Defined over in DNBArchImplX86_64.cpp + +#if defined (LLDB_DEBUGSERVER_RELEASE) || defined (LLDB_DEBUGSERVER_DEBUG) +enum debugState { + debugStateUnknown, + debugStateOff, + debugStateOn +}; + +static debugState sFPUDebugState = debugStateUnknown; +static debugState sAVXForceState = debugStateUnknown; + +static bool DebugFPURegs () +{ + if (sFPUDebugState == debugStateUnknown) + { + if (getenv("DNB_DEBUG_FPU_REGS")) + sFPUDebugState = debugStateOn; + else + sFPUDebugState = debugStateOff; + } + + return (sFPUDebugState == debugStateOn); +} + +static bool ForceAVXRegs () +{ + if (sFPUDebugState == debugStateUnknown) + { + if (getenv("DNB_DEBUG_X86_FORCE_AVX_REGS")) + sAVXForceState = debugStateOn; + else + sAVXForceState = debugStateOff; + } + + return (sAVXForceState == debugStateOn); +} + +#define DEBUG_FPU_REGS (DebugFPURegs()) +#define FORCE_AVX_REGS (ForceAVXRegs()) +#else +#define DEBUG_FPU_REGS (0) +#define FORCE_AVX_REGS (0) +#endif + +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, + gpr_ax , + gpr_bx , + gpr_cx , + gpr_dx , + gpr_di , + gpr_si , + gpr_bp , + gpr_sp , + gpr_ah , + gpr_bh , + gpr_ch , + gpr_dh , + gpr_al , + gpr_bl , + gpr_cl , + gpr_dl , + gpr_dil, + gpr_sil, + gpr_bpl, + gpr_spl, + 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_ymm0, + fpu_ymm1, + fpu_ymm2, + fpu_ymm3, + fpu_ymm4, + fpu_ymm5, + fpu_ymm6, + fpu_ymm7, + 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 +{ + ehframe_eax = 0, + ehframe_ecx, + ehframe_edx, + ehframe_ebx, + + // On i386 Darwin the eh_frame register numbers for ebp and esp are reversed from DWARF. + // It's due to an ancient compiler bug in the output of the eh_frame. + // Specifically, on i386 darwin eh_frame, 4 is ebp, 5 is esp. + // On i386 darwin debug_frame (and debug_info), 4 is esp, 5 is ebp. + ehframe_ebp, + ehframe_esp, + ehframe_esi, + ehframe_edi, + ehframe_eip, + ehframe_eflags +}; + +enum +{ + dwarf_eax = 0, + dwarf_ecx, + dwarf_edx, + dwarf_ebx, + dwarf_esp, + dwarf_ebp, + dwarf_esi, + dwarf_edi, + dwarf_eip, + dwarf_eflags, + dwarf_stmm0 = 11, + dwarf_stmm1, + dwarf_stmm2, + dwarf_stmm3, + dwarf_stmm4, + dwarf_stmm5, + dwarf_stmm6, + dwarf_stmm7, + dwarf_xmm0 = 21, + dwarf_xmm1, + dwarf_xmm2, + dwarf_xmm3, + dwarf_xmm4, + dwarf_xmm5, + dwarf_xmm6, + dwarf_xmm7, + dwarf_ymm0 = dwarf_xmm0, + dwarf_ymm1 = dwarf_xmm1, + dwarf_ymm2 = dwarf_xmm2, + dwarf_ymm3 = dwarf_xmm3, + dwarf_ymm4 = dwarf_xmm4, + dwarf_ymm5 = dwarf_xmm5, + dwarf_ymm6 = dwarf_xmm6, + dwarf_ymm7 = dwarf_xmm7, +}; + +enum +{ + debugserver_eax = 0, + debugserver_ecx = 1, + debugserver_edx = 2, + debugserver_ebx = 3, + debugserver_esp = 4, + debugserver_ebp = 5, + debugserver_esi = 6, + debugserver_edi = 7, + debugserver_eip = 8, + debugserver_eflags = 9, + debugserver_cs = 10, + debugserver_ss = 11, + debugserver_ds = 12, + debugserver_es = 13, + debugserver_fs = 14, + debugserver_gs = 15, + debugserver_stmm0 = 16, + debugserver_stmm1 = 17, + debugserver_stmm2 = 18, + debugserver_stmm3 = 19, + debugserver_stmm4 = 20, + debugserver_stmm5 = 21, + debugserver_stmm6 = 22, + debugserver_stmm7 = 23, + debugserver_fctrl = 24, debugserver_fcw = debugserver_fctrl, + debugserver_fstat = 25, debugserver_fsw = debugserver_fstat, + debugserver_ftag = 26, debugserver_ftw = debugserver_ftag, + debugserver_fiseg = 27, debugserver_fpu_cs = debugserver_fiseg, + debugserver_fioff = 28, debugserver_ip = debugserver_fioff, + debugserver_foseg = 29, debugserver_fpu_ds = debugserver_foseg, + debugserver_fooff = 30, debugserver_dp = debugserver_fooff, + debugserver_fop = 31, + debugserver_xmm0 = 32, + debugserver_xmm1 = 33, + debugserver_xmm2 = 34, + debugserver_xmm3 = 35, + debugserver_xmm4 = 36, + debugserver_xmm5 = 37, + debugserver_xmm6 = 38, + debugserver_xmm7 = 39, + debugserver_mxcsr = 40, + debugserver_mm0 = 41, + debugserver_mm1 = 42, + debugserver_mm2 = 43, + debugserver_mm3 = 44, + debugserver_mm4 = 45, + debugserver_mm5 = 46, + debugserver_mm6 = 47, + debugserver_mm7 = 48, + debugserver_ymm0 = debugserver_xmm0, + debugserver_ymm1 = debugserver_xmm1, + debugserver_ymm2 = debugserver_xmm2, + debugserver_ymm3 = debugserver_xmm3, + debugserver_ymm4 = debugserver_xmm4, + debugserver_ymm5 = debugserver_xmm5, + debugserver_ymm6 = debugserver_xmm6, + debugserver_ymm7 = debugserver_xmm7 +}; + +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 = static_cast(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->MachPortNumber(), __i386_THREAD_STATE, (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_REGS) + { + if (CPUHasAVX() || FORCE_AVX_REGS) + { + m_state.context.fpu.avx.__fpu_reserved[0] = -1; + m_state.context.fpu.avx.__fpu_reserved[1] = -1; + *(uint16_t *)&(m_state.context.fpu.avx.__fpu_fcw) = 0x1234; + *(uint16_t *)&(m_state.context.fpu.avx.__fpu_fsw) = 0x5678; + m_state.context.fpu.avx.__fpu_ftw = 1; + m_state.context.fpu.avx.__fpu_rsrv1 = UINT8_MAX; + m_state.context.fpu.avx.__fpu_fop = 2; + m_state.context.fpu.avx.__fpu_ip = 3; + m_state.context.fpu.avx.__fpu_cs = 4; + m_state.context.fpu.avx.__fpu_rsrv2 = 5; + m_state.context.fpu.avx.__fpu_dp = 6; + m_state.context.fpu.avx.__fpu_ds = 7; + m_state.context.fpu.avx.__fpu_rsrv3 = UINT16_MAX; + m_state.context.fpu.avx.__fpu_mxcsr = 8; + m_state.context.fpu.avx.__fpu_mxcsrmask = 9; + int i; + for (i=0; i<16; ++i) + { + if (i<10) + { + m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg[i] = 'a'; + m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg[i] = 'b'; + m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg[i] = 'c'; + m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg[i] = 'd'; + m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg[i] = 'e'; + m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg[i] = 'f'; + m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg[i] = 'g'; + m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg[i] = 'h'; + } + else + { + m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg[i] = INT8_MIN; + } + + m_state.context.fpu.avx.__fpu_xmm0.__xmm_reg[i] = '0'; + m_state.context.fpu.avx.__fpu_xmm1.__xmm_reg[i] = '1'; + m_state.context.fpu.avx.__fpu_xmm2.__xmm_reg[i] = '2'; + m_state.context.fpu.avx.__fpu_xmm3.__xmm_reg[i] = '3'; + m_state.context.fpu.avx.__fpu_xmm4.__xmm_reg[i] = '4'; + m_state.context.fpu.avx.__fpu_xmm5.__xmm_reg[i] = '5'; + m_state.context.fpu.avx.__fpu_xmm6.__xmm_reg[i] = '6'; + m_state.context.fpu.avx.__fpu_xmm7.__xmm_reg[i] = '7'; + } + for (i=0; iMachPortNumber(), __i386_AVX_STATE, (thread_state_t)&m_state.context.fpu.avx, &count)); + DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &avx, %u (%u passed in)) => 0x%8.8x", + m_thread->MachPortNumber(), __i386_AVX_STATE, count, e_regSetWordSizeAVX, + m_state.GetError(e_regSetFPU, Read)); + } + else + { + mach_msg_type_number_t count = e_regSetWordSizeFPU; + m_state.SetError(e_regSetFPU, Read, ::thread_get_state(m_thread->MachPortNumber(), __i386_FLOAT_STATE, (thread_state_t)&m_state.context.fpu.no_avx, &count)); + DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &fpu, %u (%u passed in) => 0x%8.8x", + m_thread->MachPortNumber(), __i386_FLOAT_STATE, count, e_regSetWordSizeFPU, + m_state.GetError(e_regSetFPU, Read)); + } + } + } + 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->MachPortNumber(), __i386_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, &count)); + } + return m_state.GetError(e_regSetEXC, Read); +} + +kern_return_t +DNBArchImplI386::SetGPRState() +{ + kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber()); + DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount()); + + + m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_THREAD_STATE, (thread_state_t)&m_state.context.gpr, e_regSetWordSizeGPR)); + return m_state.GetError(e_regSetGPR, Write); +} + +kern_return_t +DNBArchImplI386::SetFPUState() +{ + if (DEBUG_FPU_REGS) + { + m_state.SetError(e_regSetFPU, Write, 0); + return m_state.GetError(e_regSetFPU, Write); + } + else + { + if (CPUHasAVX() || FORCE_AVX_REGS) + m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_AVX_STATE, (thread_state_t)&m_state.context.fpu.avx, e_regSetWordSizeAVX)); + else + m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_FLOAT_STATE, (thread_state_t)&m_state.context.fpu.no_avx, e_regSetWordSizeFPU)); + return m_state.GetError(e_regSetFPU, Write); + } +} + +kern_return_t +DNBArchImplI386::SetEXCState() +{ + m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, e_regSetWordSizeEXC)); + return m_state.GetError(e_regSetEXC, Write); +} + +kern_return_t +DNBArchImplI386::GetDBGState(bool force) +{ + if (force || m_state.GetError(e_regSetDBG, Read)) + { + mach_msg_type_number_t count = e_regSetWordSizeDBG; + m_state.SetError(e_regSetDBG, Read, ::thread_get_state(m_thread->MachPortNumber(), __i386_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, &count)); + } + return m_state.GetError(e_regSetDBG, Read); +} + +kern_return_t +DNBArchImplI386::SetDBGState(bool also_set_on_task) +{ + m_state.SetError(e_regSetDBG, Write, ::thread_set_state(m_thread->MachPortNumber(), __i386_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG)); + if (also_set_on_task) + { + kern_return_t kret = ::task_set_state(m_thread->Process()->Task().TaskPort(), __i386_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG); + if (kret != KERN_SUCCESS) + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::SetDBGState failed to set debug control register state: 0x%8.8x.", kret); + + } + return m_state.GetError(e_regSetDBG, 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); + } + + // Reset the debug status register, if necessary, before we resume. + kern_return_t kret = GetDBGState(false); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::ThreadWillResume() GetDBGState() => 0x%8.8x.", kret); + if (kret != KERN_SUCCESS) + return; + + DBG &debug_state = m_state.context.dbg; + bool need_reset = false; + uint32_t i, num = NumSupportedHardwareWatchpoints(); + for (i = 0; i < num; ++i) + if (IsWatchpointHit(debug_state, i)) + need_reset = true; + + if (need_reset) + { + ClearWatchpointHits(debug_state); + kret = SetDBGState(false); + DNBLogThreadedIf(LOG_WATCHPOINTS,"DNBArchImplI386::ThreadWillResume() SetDBGState() => 0x%8.8x.", kret); + } +} + +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) + { + // exc_code = EXC_I386_BPT + // + 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. + + DNBBreakpoint *bp = m_thread->Process()->Breakpoints().FindByAddress(pc); + if (bp) + { + // 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 = static_cast(pc); + // Write the new PC back out + SetGPRState (); + } + } + return true; + } + } + else if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 1) + { + // exc_code = EXC_I386_SGL + // + // Check whether this corresponds to a watchpoint hit event. + // If yes, set the exc_sub_code to the data break address. + nub_addr_t addr = 0; + uint32_t hw_index = GetHardwareWatchpointHit(addr); + if (hw_index != INVALID_NUB_HW_INDEX) + { + exc.exc_data[1] = addr; + // Piggyback the hw_index in the exc.data. + exc.exc_data.push_back(hw_index); + } + + return true; + } + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + return false; +} + +uint32_t +DNBArchImplI386::NumSupportedHardwareWatchpoints() +{ + // Available debug address registers: dr0, dr1, dr2, dr3. + return 4; +} + +static uint32_t +size_and_rw_bits(nub_size_t size, bool read, bool write) +{ + uint32_t rw; + if (read) { + rw = 0x3; // READ or READ/WRITE + } else if (write) { + rw = 0x1; // WRITE + } else { + assert(0 && "read and write cannot both be false"); + } + + switch (size) { + case 1: + return rw; + case 2: + return (0x1 << 2) | rw; + case 4: + return (0x3 << 2) | rw; + case 8: + return (0x2 << 2) | rw; + } + assert(0 && "invalid size, must be one of 1, 2, 4, or 8"); + return 0; +} + +void +DNBArchImplI386::SetWatchpoint(DBG &debug_state, uint32_t hw_index, nub_addr_t addr, nub_size_t size, bool read, bool write) +{ + // Set both dr7 (debug control register) and dri (debug address register). + + // dr7{7-0} encodes the local/gloabl enable bits: + // global enable --. .-- local enable + // | | + // v v + // dr0 -> bits{1-0} + // dr1 -> bits{3-2} + // dr2 -> bits{5-4} + // dr3 -> bits{7-6} + // + // dr7{31-16} encodes the rw/len bits: + // b_x+3, b_x+2, b_x+1, b_x + // where bits{x+1, x} => rw + // 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io read-or-write (unused) + // and bits{x+3, x+2} => len + // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte + // + // dr0 -> bits{19-16} + // dr1 -> bits{23-20} + // dr2 -> bits{27-24} + // dr3 -> bits{31-28} + debug_state.__dr7 |= (1 << (2*hw_index) | + size_and_rw_bits(size, read, write) << (16+4*hw_index)); + uint32_t addr_32 = addr & 0xffffffff; + switch (hw_index) { + case 0: + debug_state.__dr0 = addr_32; break; + case 1: + debug_state.__dr1 = addr_32; break; + case 2: + debug_state.__dr2 = addr_32; break; + case 3: + debug_state.__dr3 = addr_32; break; + default: + assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3"); + } + return; +} + +void +DNBArchImplI386::ClearWatchpoint(DBG &debug_state, uint32_t hw_index) +{ + debug_state.__dr7 &= ~(3 << (2*hw_index)); + switch (hw_index) { + case 0: + debug_state.__dr0 = 0; break; + case 1: + debug_state.__dr1 = 0; break; + case 2: + debug_state.__dr2 = 0; break; + case 3: + debug_state.__dr3 = 0; break; + default: + assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3"); + } + return; +} + +bool +DNBArchImplI386::IsWatchpointVacant(const DBG &debug_state, uint32_t hw_index) +{ + // Check dr7 (debug control register) for local/global enable bits: + // global enable --. .-- local enable + // | | + // v v + // dr0 -> bits{1-0} + // dr1 -> bits{3-2} + // dr2 -> bits{5-4} + // dr3 -> bits{7-6} + return (debug_state.__dr7 & (3 << (2*hw_index))) == 0; +} + +// Resets local copy of debug status register to wait for the next debug exception. +void +DNBArchImplI386::ClearWatchpointHits(DBG &debug_state) +{ + // See also IsWatchpointHit(). + debug_state.__dr6 = 0; + return; +} + +bool +DNBArchImplI386::IsWatchpointHit(const DBG &debug_state, uint32_t hw_index) +{ + // Check dr6 (debug status register) whether a watchpoint hits: + // is watchpoint hit? + // | + // v + // dr0 -> bits{0} + // dr1 -> bits{1} + // dr2 -> bits{2} + // dr3 -> bits{3} + return (debug_state.__dr6 & (1 << hw_index)); +} + +nub_addr_t +DNBArchImplI386::GetWatchAddress(const DBG &debug_state, uint32_t hw_index) +{ + switch (hw_index) { + case 0: + return debug_state.__dr0; + case 1: + return debug_state.__dr1; + case 2: + return debug_state.__dr2; + case 3: + return debug_state.__dr3; + } + assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3"); + return 0; +} + +bool +DNBArchImplI386::StartTransForHWP() +{ + if (m_2pc_trans_state != Trans_Done && m_2pc_trans_state != Trans_Rolled_Back) + DNBLogError ("%s inconsistent state detected, expected %d or %d, got: %d", __FUNCTION__, Trans_Done, Trans_Rolled_Back, m_2pc_trans_state); + m_2pc_dbg_checkpoint = m_state.context.dbg; + m_2pc_trans_state = Trans_Pending; + return true; +} +bool +DNBArchImplI386::RollbackTransForHWP() +{ + m_state.context.dbg = m_2pc_dbg_checkpoint; + if (m_2pc_trans_state != Trans_Pending) + DNBLogError ("%s inconsistent state detected, expected %d, got: %d", __FUNCTION__, Trans_Pending, m_2pc_trans_state); + m_2pc_trans_state = Trans_Rolled_Back; + kern_return_t kret = SetDBGState(false); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::RollbackTransForHWP() SetDBGState() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return true; + else + return false; +} +bool +DNBArchImplI386::FinishTransForHWP() +{ + m_2pc_trans_state = Trans_Done; + return true; +} +DNBArchImplI386::DBG +DNBArchImplI386::GetDBGCheckpoint() +{ + return m_2pc_dbg_checkpoint; +} + +uint32_t +DNBArchImplI386::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::EnableHardwareWatchpoint(addr = 0x%llx, size = %llu, read = %u, write = %u)", (uint64_t)addr, (uint64_t)size, read, write); + + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + // Can only watch 1, 2, 4, or 8 bytes. + if (!(size == 1 || size == 2 || size == 4 || size == 8)) + return INVALID_NUB_HW_INDEX; + + // We must watch for either read or write + if (read == false && write == false) + 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; + + DBG &debug_state = m_state.context.dbg; + for (i = 0; i < num_hw_watchpoints; ++i) + { + if (IsWatchpointVacant(debug_state, i)) + break; + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_watchpoints) + { + StartTransForHWP(); + + // Modify our local copy of the debug state, first. + SetWatchpoint(debug_state, i, addr, size, read, write); + // Now set the watch point in the inferior. + kret = SetDBGState(also_set_on_task); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::EnableHardwareWatchpoint() SetDBGState() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + else // Revert to the previous debug state voluntarily. The transaction coordinator knows that we have failed. + m_state.context.dbg = GetDBGCheckpoint(); + } + else + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints); + } + } + return INVALID_NUB_HW_INDEX; +} + +bool +DNBArchImplI386::DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task) +{ + kern_return_t kret = GetDBGState(false); + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (kret == KERN_SUCCESS) + { + DBG &debug_state = m_state.context.dbg; + if (hw_index < num_hw_points && !IsWatchpointVacant(debug_state, hw_index)) + { + StartTransForHWP(); + + // Modify our local copy of the debug state, first. + ClearWatchpoint(debug_state, hw_index); + // Now disable the watch point in the inferior. + kret = SetDBGState(also_set_on_task); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::DisableHardwareWatchpoint( %u )", + hw_index); + + if (kret == KERN_SUCCESS) + return true; + else // Revert to the previous debug state voluntarily. The transaction coordinator knows that we have failed. + m_state.context.dbg = GetDBGCheckpoint(); + } + } + return false; +} + +// Iterate through the debug status register; return the index of the first hit. +uint32_t +DNBArchImplI386::GetHardwareWatchpointHit(nub_addr_t &addr) +{ + // Read the debug state + kern_return_t kret = GetDBGState(true); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.", kret); + if (kret == KERN_SUCCESS) + { + DBG &debug_state = m_state.context.dbg; + uint32_t i, num = NumSupportedHardwareWatchpoints(); + for (i = 0; i < num; ++i) + { + if (IsWatchpointHit(debug_state, i)) + { + addr = GetWatchAddress(debug_state, i); + DNBLogThreadedIf(LOG_WATCHPOINTS, + "DNBArchImplI386::GetHardwareWatchpointHit() found => %u (addr = 0x%llx).", + i, (uint64_t)addr); + return i; + } + } + } + return INVALID_NUB_HW_INDEX; +} + +// 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 definitions +//---------------------------------------------------------------------- + +#define DEFINE_GPR_PSEUDO_16(reg16,reg32) { e_regSetGPR, gpr_##reg16, #reg16, NULL, Uint, Hex, 2, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg32, g_invalidate_##reg32 } +#define DEFINE_GPR_PSEUDO_8H(reg8,reg32) { e_regSetGPR, gpr_##reg8 , #reg8 , NULL, Uint, Hex, 1, 1,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg32, g_invalidate_##reg32 } +#define DEFINE_GPR_PSEUDO_8L(reg8,reg32) { e_regSetGPR, gpr_##reg8 , #reg8 , NULL, Uint, Hex, 1, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg32, g_invalidate_##reg32 } + + +#define GPR_OFFSET(reg) (offsetof (DNBArchImplI386::GPR, __##reg)) +#define FPU_OFFSET(reg) (offsetof (DNBArchImplI386::FPU, __fpu_##reg) + offsetof (DNBArchImplI386::Context, fpu.no_avx)) +#define AVX_OFFSET(reg) (offsetof (DNBArchImplI386::AVX, __fpu_##reg) + offsetof (DNBArchImplI386::Context, fpu.avx)) +#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 FPU_SIZE_YMM(reg) (32) +#define EXC_SIZE(reg) (sizeof(((DNBArchImplI386::EXC *)NULL)->__##reg)) + +// This does not accurately identify the location of ymm0...7 in +// Context.fpu.avx. That is because there is a bunch of padding +// in Context.fpu.avx that we don't need. Offset macros lay out +// the register state that Debugserver transmits to the debugger +// -- not to interpret the thread_get_state info. +#define AVX_OFFSET_YMM(n) (AVX_OFFSET(xmm7) + FPU_SIZE_XMM(xmm7) + (32 * n)) + +// 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. + +const char * g_contained_eax[] = { "eax", NULL }; +const char * g_contained_ebx[] = { "ebx", NULL }; +const char * g_contained_ecx[] = { "ecx", NULL }; +const char * g_contained_edx[] = { "edx", NULL }; +const char * g_contained_edi[] = { "edi", NULL }; +const char * g_contained_esi[] = { "esi", NULL }; +const char * g_contained_ebp[] = { "ebp", NULL }; +const char * g_contained_esp[] = { "esp", NULL }; + +const char * g_invalidate_eax[] = { "eax", "ax", "ah", "al", NULL }; +const char * g_invalidate_ebx[] = { "ebx", "bx", "bh", "bl", NULL }; +const char * g_invalidate_ecx[] = { "ecx", "cx", "ch", "cl", NULL }; +const char * g_invalidate_edx[] = { "edx", "dx", "dh", "dl", NULL }; +const char * g_invalidate_edi[] = { "edi", "di", "dil", NULL }; +const char * g_invalidate_esi[] = { "esi", "si", "sil", NULL }; +const char * g_invalidate_ebp[] = { "ebp", "bp", "bpl", NULL }; +const char * g_invalidate_esp[] = { "esp", "sp", "spl", NULL }; + +// 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) , ehframe_eax , dwarf_eax , INVALID_NUB_REGNUM , debugserver_eax , NULL, g_invalidate_eax }, +{ e_regSetGPR, gpr_ebx, "ebx" , NULL , Uint, Hex, GPR_SIZE(ebx), GPR_OFFSET(ebx) , ehframe_ebx , dwarf_ebx , INVALID_NUB_REGNUM , debugserver_ebx , NULL, g_invalidate_ebx }, +{ e_regSetGPR, gpr_ecx, "ecx" , NULL , Uint, Hex, GPR_SIZE(ecx), GPR_OFFSET(ecx) , ehframe_ecx , dwarf_ecx , INVALID_NUB_REGNUM , debugserver_ecx , NULL, g_invalidate_ecx }, +{ e_regSetGPR, gpr_edx, "edx" , NULL , Uint, Hex, GPR_SIZE(edx), GPR_OFFSET(edx) , ehframe_edx , dwarf_edx , INVALID_NUB_REGNUM , debugserver_edx , NULL, g_invalidate_edx }, +{ e_regSetGPR, gpr_edi, "edi" , NULL , Uint, Hex, GPR_SIZE(edi), GPR_OFFSET(edi) , ehframe_edi , dwarf_edi , INVALID_NUB_REGNUM , debugserver_edi , NULL, g_invalidate_edi }, +{ e_regSetGPR, gpr_esi, "esi" , NULL , Uint, Hex, GPR_SIZE(esi), GPR_OFFSET(esi) , ehframe_esi , dwarf_esi , INVALID_NUB_REGNUM , debugserver_esi , NULL, g_invalidate_esi }, +{ e_regSetGPR, gpr_ebp, "ebp" , "fp" , Uint, Hex, GPR_SIZE(ebp), GPR_OFFSET(ebp) , ehframe_ebp , dwarf_ebp , GENERIC_REGNUM_FP , debugserver_ebp , NULL, g_invalidate_ebp }, +{ e_regSetGPR, gpr_esp, "esp" , "sp" , Uint, Hex, GPR_SIZE(esp), GPR_OFFSET(esp) , ehframe_esp , dwarf_esp , GENERIC_REGNUM_SP , debugserver_esp , NULL, g_invalidate_esp }, +{ e_regSetGPR, gpr_ss, "ss" , NULL , Uint, Hex, GPR_SIZE(ss), GPR_OFFSET(ss) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_ss , NULL, NULL}, +{ e_regSetGPR, gpr_eflags, "eflags", "flags" , Uint, Hex, GPR_SIZE(eflags), GPR_OFFSET(eflags) , ehframe_eflags , dwarf_eflags , GENERIC_REGNUM_FLAGS , debugserver_eflags, NULL, NULL}, +{ e_regSetGPR, gpr_eip, "eip" , "pc" , Uint, Hex, GPR_SIZE(eip), GPR_OFFSET(eip) , ehframe_eip , dwarf_eip , GENERIC_REGNUM_PC , debugserver_eip , NULL, NULL}, +{ e_regSetGPR, gpr_cs, "cs" , NULL , Uint, Hex, GPR_SIZE(cs), GPR_OFFSET(cs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_cs , NULL, NULL}, +{ e_regSetGPR, gpr_ds, "ds" , NULL , Uint, Hex, GPR_SIZE(ds), GPR_OFFSET(ds) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_ds , NULL, NULL}, +{ e_regSetGPR, gpr_es, "es" , NULL , Uint, Hex, GPR_SIZE(es), GPR_OFFSET(es) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_es , NULL, NULL}, +{ e_regSetGPR, gpr_fs, "fs" , NULL , Uint, Hex, GPR_SIZE(fs), GPR_OFFSET(fs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_fs , NULL, NULL}, +{ e_regSetGPR, gpr_gs, "gs" , NULL , Uint, Hex, GPR_SIZE(gs), GPR_OFFSET(gs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM , debugserver_gs , NULL, NULL}, +DEFINE_GPR_PSEUDO_16 (ax , eax), +DEFINE_GPR_PSEUDO_16 (bx , ebx), +DEFINE_GPR_PSEUDO_16 (cx , ecx), +DEFINE_GPR_PSEUDO_16 (dx , edx), +DEFINE_GPR_PSEUDO_16 (di , edi), +DEFINE_GPR_PSEUDO_16 (si , esi), +DEFINE_GPR_PSEUDO_16 (bp , ebp), +DEFINE_GPR_PSEUDO_16 (sp , esp), +DEFINE_GPR_PSEUDO_8H (ah , eax), +DEFINE_GPR_PSEUDO_8H (bh , ebx), +DEFINE_GPR_PSEUDO_8H (ch , ecx), +DEFINE_GPR_PSEUDO_8H (dh , edx), +DEFINE_GPR_PSEUDO_8L (al , eax), +DEFINE_GPR_PSEUDO_8L (bl , ebx), +DEFINE_GPR_PSEUDO_8L (cl , ecx), +DEFINE_GPR_PSEUDO_8L (dl , edx), +DEFINE_GPR_PSEUDO_8L (dil, edi), +DEFINE_GPR_PSEUDO_8L (sil, esi), +DEFINE_GPR_PSEUDO_8L (bpl, ebp), +DEFINE_GPR_PSEUDO_8L (spl, esp) +}; + + +const DNBRegisterInfo +DNBArchImplI386::g_fpu_registers_no_avx[] = +{ +{ e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , FPU_OFFSET(fcw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , FPU_OFFSET(fsw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , FPU_OFFSET(ftw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , FPU_OFFSET(fop) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , FPU_OFFSET(ip) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , FPU_OFFSET(cs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , FPU_OFFSET(dp) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , FPU_OFFSET(ds) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , FPU_OFFSET(mxcsr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , FPU_OFFSET(mxcsrmask) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, + +{ e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), INVALID_NUB_REGNUM, dwarf_stmm0, INVALID_NUB_REGNUM, debugserver_stmm0, NULL, NULL }, +{ e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), INVALID_NUB_REGNUM, dwarf_stmm1, INVALID_NUB_REGNUM, debugserver_stmm1, NULL, NULL }, +{ e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), INVALID_NUB_REGNUM, dwarf_stmm2, INVALID_NUB_REGNUM, debugserver_stmm2, NULL, NULL }, +{ e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), INVALID_NUB_REGNUM, dwarf_stmm3, INVALID_NUB_REGNUM, debugserver_stmm3, NULL, NULL }, +{ e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), INVALID_NUB_REGNUM, dwarf_stmm4, INVALID_NUB_REGNUM, debugserver_stmm4, NULL, NULL }, +{ e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), INVALID_NUB_REGNUM, dwarf_stmm5, INVALID_NUB_REGNUM, debugserver_stmm5, NULL, NULL }, +{ e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), INVALID_NUB_REGNUM, dwarf_stmm6, INVALID_NUB_REGNUM, debugserver_stmm6, NULL, NULL }, +{ e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), INVALID_NUB_REGNUM, dwarf_stmm7, INVALID_NUB_REGNUM, debugserver_stmm7, NULL, NULL }, + +{ e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0), FPU_OFFSET(xmm0), INVALID_NUB_REGNUM, dwarf_xmm0, INVALID_NUB_REGNUM, debugserver_xmm0, NULL, NULL }, +{ e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1), FPU_OFFSET(xmm1), INVALID_NUB_REGNUM, dwarf_xmm1, INVALID_NUB_REGNUM, debugserver_xmm1, NULL, NULL }, +{ e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2), FPU_OFFSET(xmm2), INVALID_NUB_REGNUM, dwarf_xmm2, INVALID_NUB_REGNUM, debugserver_xmm2, NULL, NULL }, +{ e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3), FPU_OFFSET(xmm3), INVALID_NUB_REGNUM, dwarf_xmm3, INVALID_NUB_REGNUM, debugserver_xmm3, NULL, NULL }, +{ e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4), FPU_OFFSET(xmm4), INVALID_NUB_REGNUM, dwarf_xmm4, INVALID_NUB_REGNUM, debugserver_xmm4, NULL, NULL }, +{ e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5), FPU_OFFSET(xmm5), INVALID_NUB_REGNUM, dwarf_xmm5, INVALID_NUB_REGNUM, debugserver_xmm5, NULL, NULL }, +{ e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6), FPU_OFFSET(xmm6), INVALID_NUB_REGNUM, dwarf_xmm6, INVALID_NUB_REGNUM, debugserver_xmm6, NULL, NULL }, +{ e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7), FPU_OFFSET(xmm7), INVALID_NUB_REGNUM, dwarf_xmm7, INVALID_NUB_REGNUM, debugserver_xmm7, NULL, NULL } +}; + + +static const char *g_contained_ymm0 [] = { "ymm0", NULL }; +static const char *g_contained_ymm1 [] = { "ymm1", NULL }; +static const char *g_contained_ymm2 [] = { "ymm2", NULL }; +static const char *g_contained_ymm3 [] = { "ymm3", NULL }; +static const char *g_contained_ymm4 [] = { "ymm4", NULL }; +static const char *g_contained_ymm5 [] = { "ymm5", NULL }; +static const char *g_contained_ymm6 [] = { "ymm6", NULL }; +static const char *g_contained_ymm7 [] = { "ymm7", NULL }; + + +const DNBRegisterInfo +DNBArchImplI386::g_fpu_registers_avx[] = +{ +{ e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , AVX_OFFSET(fcw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , AVX_OFFSET(fsw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , AVX_OFFSET(ftw) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , AVX_OFFSET(fop) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , AVX_OFFSET(ip) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , AVX_OFFSET(cs) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , AVX_OFFSET(dp) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , AVX_OFFSET(ds) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , AVX_OFFSET(mxcsr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , AVX_OFFSET(mxcsrmask) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, + +{ e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), AVX_OFFSET(stmm0), INVALID_NUB_REGNUM, dwarf_stmm0, INVALID_NUB_REGNUM, debugserver_stmm0, NULL, NULL }, +{ e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), AVX_OFFSET(stmm1), INVALID_NUB_REGNUM, dwarf_stmm1, INVALID_NUB_REGNUM, debugserver_stmm1, NULL, NULL }, +{ e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), AVX_OFFSET(stmm2), INVALID_NUB_REGNUM, dwarf_stmm2, INVALID_NUB_REGNUM, debugserver_stmm2, NULL, NULL }, +{ e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), AVX_OFFSET(stmm3), INVALID_NUB_REGNUM, dwarf_stmm3, INVALID_NUB_REGNUM, debugserver_stmm3, NULL, NULL }, +{ e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), AVX_OFFSET(stmm4), INVALID_NUB_REGNUM, dwarf_stmm4, INVALID_NUB_REGNUM, debugserver_stmm4, NULL, NULL }, +{ e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), AVX_OFFSET(stmm5), INVALID_NUB_REGNUM, dwarf_stmm5, INVALID_NUB_REGNUM, debugserver_stmm5, NULL, NULL }, +{ e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), AVX_OFFSET(stmm6), INVALID_NUB_REGNUM, dwarf_stmm6, INVALID_NUB_REGNUM, debugserver_stmm6, NULL, NULL }, +{ e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), AVX_OFFSET(stmm7), INVALID_NUB_REGNUM, dwarf_stmm7, INVALID_NUB_REGNUM, debugserver_stmm7, NULL, NULL }, + +{ e_regSetFPU, fpu_ymm0, "ymm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm0), AVX_OFFSET_YMM(0), INVALID_NUB_REGNUM, dwarf_ymm0, INVALID_NUB_REGNUM, debugserver_ymm0, NULL, NULL }, +{ e_regSetFPU, fpu_ymm1, "ymm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm1), AVX_OFFSET_YMM(1), INVALID_NUB_REGNUM, dwarf_ymm1, INVALID_NUB_REGNUM, debugserver_ymm1, NULL, NULL }, +{ e_regSetFPU, fpu_ymm2, "ymm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm2), AVX_OFFSET_YMM(2), INVALID_NUB_REGNUM, dwarf_ymm2, INVALID_NUB_REGNUM, debugserver_ymm2, NULL, NULL }, +{ e_regSetFPU, fpu_ymm3, "ymm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm3), AVX_OFFSET_YMM(3), INVALID_NUB_REGNUM, dwarf_ymm3, INVALID_NUB_REGNUM, debugserver_ymm3, NULL, NULL }, +{ e_regSetFPU, fpu_ymm4, "ymm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm4), AVX_OFFSET_YMM(4), INVALID_NUB_REGNUM, dwarf_ymm4, INVALID_NUB_REGNUM, debugserver_ymm4, NULL, NULL }, +{ e_regSetFPU, fpu_ymm5, "ymm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm5), AVX_OFFSET_YMM(5), INVALID_NUB_REGNUM, dwarf_ymm5, INVALID_NUB_REGNUM, debugserver_ymm5, NULL, NULL }, +{ e_regSetFPU, fpu_ymm6, "ymm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm6), AVX_OFFSET_YMM(6), INVALID_NUB_REGNUM, dwarf_ymm6, INVALID_NUB_REGNUM, debugserver_ymm6, NULL, NULL }, +{ e_regSetFPU, fpu_ymm7, "ymm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm7), AVX_OFFSET_YMM(7), INVALID_NUB_REGNUM, dwarf_ymm7, INVALID_NUB_REGNUM, debugserver_ymm7, NULL, NULL }, + +{ e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0), 0, INVALID_NUB_REGNUM, dwarf_xmm0, INVALID_NUB_REGNUM, debugserver_xmm0, g_contained_ymm0, NULL }, +{ e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1), 0, INVALID_NUB_REGNUM, dwarf_xmm1, INVALID_NUB_REGNUM, debugserver_xmm1, g_contained_ymm1, NULL }, +{ e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2), 0, INVALID_NUB_REGNUM, dwarf_xmm2, INVALID_NUB_REGNUM, debugserver_xmm2, g_contained_ymm2, NULL }, +{ e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3), 0, INVALID_NUB_REGNUM, dwarf_xmm3, INVALID_NUB_REGNUM, debugserver_xmm3, g_contained_ymm3, NULL }, +{ e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4), 0, INVALID_NUB_REGNUM, dwarf_xmm4, INVALID_NUB_REGNUM, debugserver_xmm4, g_contained_ymm4, NULL }, +{ e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5), 0, INVALID_NUB_REGNUM, dwarf_xmm5, INVALID_NUB_REGNUM, debugserver_xmm5, g_contained_ymm5, NULL }, +{ e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6), 0, INVALID_NUB_REGNUM, dwarf_xmm6, INVALID_NUB_REGNUM, debugserver_xmm6, g_contained_ymm6, NULL }, +{ e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7), 0, INVALID_NUB_REGNUM, dwarf_xmm7, INVALID_NUB_REGNUM, debugserver_xmm7, g_contained_ymm7, NULL }, + +}; + +const DNBRegisterInfo +DNBArchImplI386::g_exc_registers[] = +{ +{ e_regSetEXC, exc_trapno, "trapno" , NULL, Uint, Hex, EXC_SIZE (trapno) , EXC_OFFSET (trapno) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetEXC, exc_err, "err" , NULL, Uint, Hex, EXC_SIZE (err) , EXC_OFFSET (err) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL }, +{ e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex, EXC_SIZE (faultvaddr), EXC_OFFSET (faultvaddr) , INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL } +}; + +// 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_no_avx = sizeof(g_fpu_registers_no_avx)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplI386::k_num_fpu_registers_avx = sizeof(g_fpu_registers_avx)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplI386::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplI386::k_num_all_registers_no_avx = k_num_gpr_registers + k_num_fpu_registers_no_avx + k_num_exc_registers; +const size_t DNBArchImplI386::k_num_all_registers_avx = k_num_gpr_registers + k_num_fpu_registers_avx + 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_no_avx[] = +{ + { "i386 Registers", NULL, k_num_all_registers_no_avx }, + { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers }, + { "Floating Point Registers", g_fpu_registers_no_avx, k_num_fpu_registers_no_avx }, + { "Exception State Registers", g_exc_registers, k_num_exc_registers } +}; + +const DNBRegisterSetInfo +DNBArchImplI386::g_reg_sets_avx[] = +{ + { "i386 Registers", NULL, k_num_all_registers_avx }, + { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers }, + { "Floating Point Registers", g_fpu_registers_avx, k_num_fpu_registers_avx }, + { "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_no_avx)/sizeof(DNBRegisterSetInfo); + +DNBArchProtocol * +DNBArchImplI386::Create (MachThread *thread) +{ + DNBArchImplI386 *obj = new DNBArchImplI386 (thread); + return obj; +} + +const uint8_t * +DNBArchImplI386::SoftwareBreakpointOpcode (nub_size_t byte_size) +{ + static const uint8_t g_breakpoint_opcode[] = { 0xCC }; + if (byte_size == 1) + return g_breakpoint_opcode; + return NULL; +} + +const DNBRegisterSetInfo * +DNBArchImplI386::GetRegisterSetInfo(nub_size_t *num_reg_sets) +{ + *num_reg_sets = k_num_register_sets; + if (CPUHasAVX() || FORCE_AVX_REGS) + return g_reg_sets_avx; + else + return g_reg_sets_no_avx; +} + + +void +DNBArchImplI386::Initialize() +{ + DNBArchPluginInfo arch_plugin_info = + { + CPU_TYPE_I386, + DNBArchImplI386::Create, + DNBArchImplI386::GetRegisterSetInfo, + DNBArchImplI386::SoftwareBreakpointOpcode + }; + + // Register this arch plug-in with the main protocol class + DNBArchProtocol::RegisterArchPlugin (arch_plugin_info); +} + +bool +DNBArchImplI386::GetRegisterValue(uint32_t set, uint32_t 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: + if (CPUHasAVX() || FORCE_AVX_REGS) + { + switch (reg) + { + case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fcw)); return true; + case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fsw)); return true; + case fpu_ftw: value->value.uint8 = m_state.context.fpu.avx.__fpu_ftw; return true; + case fpu_fop: value->value.uint16 = m_state.context.fpu.avx.__fpu_fop; return true; + case fpu_ip: value->value.uint32 = m_state.context.fpu.avx.__fpu_ip; return true; + case fpu_cs: value->value.uint16 = m_state.context.fpu.avx.__fpu_cs; return true; + case fpu_dp: value->value.uint32 = m_state.context.fpu.avx.__fpu_dp; return true; + case fpu_ds: value->value.uint16 = m_state.context.fpu.avx.__fpu_ds; return true; + case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.avx.__fpu_mxcsr; return true; + case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.avx.__fpu_mxcsrmask; return true; + + case fpu_stmm0: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg, 10); return true; + case fpu_stmm1: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg, 10); return true; + case fpu_stmm2: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg, 10); return true; + case fpu_stmm3: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg, 10); return true; + case fpu_stmm4: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg, 10); return true; + case fpu_stmm5: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg, 10); return true; + case fpu_stmm6: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg, 10); return true; + case fpu_stmm7: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg, 10); return true; + + case fpu_xmm0: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm0.__xmm_reg, 16); return true; + case fpu_xmm1: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm1.__xmm_reg, 16); return true; + case fpu_xmm2: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm2.__xmm_reg, 16); return true; + case fpu_xmm3: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm3.__xmm_reg, 16); return true; + case fpu_xmm4: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm4.__xmm_reg, 16); return true; + case fpu_xmm5: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm5.__xmm_reg, 16); return true; + case fpu_xmm6: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm6.__xmm_reg, 16); return true; + case fpu_xmm7: memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm7.__xmm_reg, 16); return true; + +#define MEMCPY_YMM(n) \ + memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm##n.__xmm_reg, 16); \ + memcpy((&value->value.uint8) + 16, m_state.context.fpu.avx.__fpu_ymmh##n.__xmm_reg, 16); + case fpu_ymm0: MEMCPY_YMM(0); return true; + case fpu_ymm1: MEMCPY_YMM(1); return true; + case fpu_ymm2: MEMCPY_YMM(2); return true; + case fpu_ymm3: MEMCPY_YMM(3); return true; + case fpu_ymm4: MEMCPY_YMM(4); return true; + case fpu_ymm5: MEMCPY_YMM(5); return true; + case fpu_ymm6: MEMCPY_YMM(6); return true; + case fpu_ymm7: MEMCPY_YMM(7); return true; +#undef MEMCPY_YMM + } + } + else + { + switch (reg) + { + case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)); return true; + case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)); return true; + case fpu_ftw: value->value.uint8 = m_state.context.fpu.no_avx.__fpu_ftw; return true; + case fpu_fop: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_fop; return true; + case fpu_ip: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_ip; return true; + case fpu_cs: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_cs; return true; + case fpu_dp: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_dp; return true; + case fpu_ds: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_ds; return true; + case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsr; return true; + case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsrmask; return true; + + case fpu_stmm0: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg, 10); return true; + case fpu_stmm1: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg, 10); return true; + case fpu_stmm2: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg, 10); return true; + case fpu_stmm3: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg, 10); return true; + case fpu_stmm4: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg, 10); return true; + case fpu_stmm5: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg, 10); return true; + case fpu_stmm6: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg, 10); return true; + case fpu_stmm7: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg, 10); return true; + + case fpu_xmm0: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg, 16); return true; + case fpu_xmm1: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg, 16); return true; + case fpu_xmm2: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg, 16); return true; + case fpu_xmm3: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg, 16); return true; + case fpu_xmm4: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg, 16); return true; + case fpu_xmm5: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg, 16); return true; + case fpu_xmm6: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg, 16); return true; + case fpu_xmm7: memcpy(&value->value.uint8, m_state.context.fpu.no_avx.__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(uint32_t set, uint32_t 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: + if (CPUHasAVX() || FORCE_AVX_REGS) + { + switch (reg) + { + case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fcw)) = value->value.uint16; success = true; break; + case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fsw)) = value->value.uint16; success = true; break; + case fpu_ftw: m_state.context.fpu.avx.__fpu_ftw = value->value.uint8; success = true; break; + case fpu_fop: m_state.context.fpu.avx.__fpu_fop = value->value.uint16; success = true; break; + case fpu_ip: m_state.context.fpu.avx.__fpu_ip = value->value.uint32; success = true; break; + case fpu_cs: m_state.context.fpu.avx.__fpu_cs = value->value.uint16; success = true; break; + case fpu_dp: m_state.context.fpu.avx.__fpu_dp = value->value.uint32; success = true; break; + case fpu_ds: m_state.context.fpu.avx.__fpu_ds = value->value.uint16; success = true; break; + case fpu_mxcsr: m_state.context.fpu.avx.__fpu_mxcsr = value->value.uint32; success = true; break; + case fpu_mxcsrmask: m_state.context.fpu.avx.__fpu_mxcsrmask = value->value.uint32; success = true; break; + + case fpu_stmm0: memcpy (m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm1: memcpy (m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm2: memcpy (m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm3: memcpy (m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm4: memcpy (m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm5: memcpy (m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm6: memcpy (m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm7: memcpy (m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg, &value->value.uint8, 10); success = true; break; + + case fpu_xmm0: memcpy(m_state.context.fpu.avx.__fpu_xmm0.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm1: memcpy(m_state.context.fpu.avx.__fpu_xmm1.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm2: memcpy(m_state.context.fpu.avx.__fpu_xmm2.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm3: memcpy(m_state.context.fpu.avx.__fpu_xmm3.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm4: memcpy(m_state.context.fpu.avx.__fpu_xmm4.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm5: memcpy(m_state.context.fpu.avx.__fpu_xmm5.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm6: memcpy(m_state.context.fpu.avx.__fpu_xmm6.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm7: memcpy(m_state.context.fpu.avx.__fpu_xmm7.__xmm_reg, &value->value.uint8, 16); success = true; break; + +#define MEMCPY_YMM(n) \ + memcpy(m_state.context.fpu.avx.__fpu_xmm##n.__xmm_reg, &value->value.uint8, 16); \ + memcpy(m_state.context.fpu.avx.__fpu_ymmh##n.__xmm_reg, (&value->value.uint8) + 16, 16); + case fpu_ymm0: MEMCPY_YMM(0); return true; + case fpu_ymm1: MEMCPY_YMM(1); return true; + case fpu_ymm2: MEMCPY_YMM(2); return true; + case fpu_ymm3: MEMCPY_YMM(3); return true; + case fpu_ymm4: MEMCPY_YMM(4); return true; + case fpu_ymm5: MEMCPY_YMM(5); return true; + case fpu_ymm6: MEMCPY_YMM(6); return true; + case fpu_ymm7: MEMCPY_YMM(7); return true; +#undef MEMCPY_YMM + } + } + else + { + switch (reg) + { + case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)) = value->value.uint16; success = true; break; + case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)) = value->value.uint16; success = true; break; + case fpu_ftw: m_state.context.fpu.no_avx.__fpu_ftw = value->value.uint8; success = true; break; + case fpu_fop: m_state.context.fpu.no_avx.__fpu_fop = value->value.uint16; success = true; break; + case fpu_ip: m_state.context.fpu.no_avx.__fpu_ip = value->value.uint32; success = true; break; + case fpu_cs: m_state.context.fpu.no_avx.__fpu_cs = value->value.uint16; success = true; break; + case fpu_dp: m_state.context.fpu.no_avx.__fpu_dp = value->value.uint32; success = true; break; + case fpu_ds: m_state.context.fpu.no_avx.__fpu_ds = value->value.uint16; success = true; break; + case fpu_mxcsr: m_state.context.fpu.no_avx.__fpu_mxcsr = value->value.uint32; success = true; break; + case fpu_mxcsrmask: m_state.context.fpu.no_avx.__fpu_mxcsrmask = value->value.uint32; success = true; break; + + case fpu_stmm0: memcpy (m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm1: memcpy (m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm2: memcpy (m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm3: memcpy (m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm4: memcpy (m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm5: memcpy (m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm6: memcpy (m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm7: memcpy (m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg, &value->value.uint8, 10); success = true; break; + + case fpu_xmm0: memcpy(m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm1: memcpy(m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm2: memcpy(m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm3: memcpy(m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm4: memcpy(m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm5: memcpy(m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm6: memcpy(m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm7: memcpy(m_state.context.fpu.no_avx.__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; +} + + +uint32_t +DNBArchImplI386::GetRegisterContextSize() +{ + static uint32_t g_cached_size = 0; + if (g_cached_size == 0) + { + if (CPUHasAVX() || FORCE_AVX_REGS) + { + for (size_t i=0; i buf_len) + size = static_cast(buf_len); + + bool force = false; + kern_return_t kret; + if ((kret = GetGPRState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) error: GPR regs failed to read: %u ", buf, (uint64_t)buf_len, kret); + size = 0; + } + else if ((kret = GetFPUState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) error: %s regs failed to read: %u", buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret); + size = 0; + } + else if ((kret = GetEXCState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) error: EXC regs failed to read: %u", buf, (uint64_t)buf_len, kret); + size = 0; + } + else + { + uint8_t *p = (uint8_t *)buf; + // Copy the GPR registers + memcpy(p, &m_state.context.gpr, sizeof(GPR)); + p += sizeof(GPR); + + if (CPUHasAVX() || FORCE_AVX_REGS) + { + // Walk around the gaps in the FPU regs + memcpy(p, &m_state.context.fpu.avx.__fpu_fcw, 5); + p += 5; + memcpy(p, &m_state.context.fpu.avx.__fpu_fop, 8); + p += 8; + memcpy(p, &m_state.context.fpu.avx.__fpu_dp, 6); + p += 6; + memcpy(p, &m_state.context.fpu.avx.__fpu_mxcsr, 8); + p += 8; + + // Work around the padding between the stmm registers as they are 16 + // byte structs with 10 bytes of the value in each + for (size_t i=0; i<8; ++i) + { + memcpy(p, &m_state.context.fpu.avx.__fpu_stmm0 + i, 10); + p += 10; + } + + // Interleave the XMM and YMMH registers to make the YMM registers + for (size_t i=0; i<8; ++i) + { + memcpy(p, &m_state.context.fpu.avx.__fpu_xmm0 + i, 16); + p += 16; + memcpy(p, &m_state.context.fpu.avx.__fpu_ymmh0 + i, 16); + p += 16; + } + } + else + { + // Walk around the gaps in the FPU regs + memcpy(p, &m_state.context.fpu.no_avx.__fpu_fcw, 5); + p += 5; + memcpy(p, &m_state.context.fpu.no_avx.__fpu_fop, 8); + p += 8; + memcpy(p, &m_state.context.fpu.no_avx.__fpu_dp, 6); + p += 6; + memcpy(p, &m_state.context.fpu.no_avx.__fpu_mxcsr, 8); + p += 8; + + // Work around the padding between the stmm registers as they are 16 + // byte structs with 10 bytes of the value in each + for (size_t i=0; i<8; ++i) + { + memcpy(p, &m_state.context.fpu.no_avx.__fpu_stmm0 + i, 10); + p += 10; + } + + // Copy the XMM registers in a single block + memcpy(p, &m_state.context.fpu.no_avx.__fpu_xmm0, 8 * 16); + p += 8 * 16; + } + + // Copy the exception registers + memcpy(p, &m_state.context.exc, sizeof(EXC)); + p += sizeof(EXC); + + // make sure we end up with exactly what we think we should have + size_t bytes_written = p - (uint8_t *)buf; + UNUSED_IF_ASSERT_DISABLED(bytes_written); + assert (bytes_written == size); + } + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)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; + + uint8_t *p = (uint8_t *)buf; + // Copy the GPR registers + memcpy(&m_state.context.gpr, p, sizeof(GPR)); + p += sizeof(GPR); + + if (CPUHasAVX() || FORCE_AVX_REGS) + { + // Walk around the gaps in the FPU regs + memcpy(&m_state.context.fpu.avx.__fpu_fcw, p, 5); + p += 5; + memcpy(&m_state.context.fpu.avx.__fpu_fop, p, 8); + p += 8; + memcpy(&m_state.context.fpu.avx.__fpu_dp, p, 6); + p += 6; + memcpy(&m_state.context.fpu.avx.__fpu_mxcsr, p, 8); + p += 8; + + // Work around the padding between the stmm registers as they are 16 + // byte structs with 10 bytes of the value in each + for (size_t i=0; i<8; ++i) + { + memcpy(&m_state.context.fpu.avx.__fpu_stmm0 + i, p, 10); + p += 10; + } + + // Interleave the XMM and YMMH registers to make the YMM registers + for (size_t i=0; i<8; ++i) + { + memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + i, p, 16); + p += 16; + memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + i, p, 16); + p += 16; + } + } + else + { + // Copy fcw through mxcsrmask as there is no padding + memcpy(&m_state.context.fpu.no_avx.__fpu_fcw, p, 5); + p += 5; + memcpy(&m_state.context.fpu.no_avx.__fpu_fop, p, 8); + p += 8; + memcpy(&m_state.context.fpu.no_avx.__fpu_dp, p, 6); + p += 6; + memcpy(&m_state.context.fpu.no_avx.__fpu_mxcsr, p, 8); + p += 8; + + // Work around the padding between the stmm registers as they are 16 + // byte structs with 10 bytes of the value in each + for (size_t i=0; i<8; ++i) + { + memcpy(&m_state.context.fpu.no_avx.__fpu_stmm0 + i, p, 10); + p += 10; + } + + // Copy the XMM registers in a single block + memcpy(&m_state.context.fpu.no_avx.__fpu_xmm0, p, 8 * 16); + p += 8 * 16; + } + + // Copy the exception registers + memcpy(&m_state.context.exc, p, sizeof(EXC)); + p += sizeof(EXC); + + // make sure we end up with exactly what we think we should have + size_t bytes_written = p - (uint8_t *)buf; + UNUSED_IF_ASSERT_DISABLED(bytes_written); + assert (bytes_written == size); + kern_return_t kret; + if ((kret = SetGPRState()) != KERN_SUCCESS) + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) error: GPR regs failed to write: %u", buf, (uint64_t)buf_len, kret); + if ((kret = SetFPUState()) != KERN_SUCCESS) + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) error: %s regs failed to write: %u", buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret); + if ((kret = SetEXCState()) != KERN_SUCCESS) + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) error: EXP regs failed to write: %u", buf, (uint64_t)buf_len, kret); + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size); + return size; +} + + +uint32_t +DNBArchImplI386::SaveRegisterState () +{ + kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber()); + DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount()); + + bool force = true; + + if ((kret = GetGPRState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SaveRegisterState () error: GPR regs failed to read: %u ", kret); + } + else if ((kret = GetFPUState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SaveRegisterState () error: %s regs failed to read: %u", CPUHasAVX() ? "AVX" : "FPU", kret); + } + else + { + const uint32_t save_id = GetNextRegisterStateSaveID (); + m_saved_register_states[save_id] = m_state.context; + return save_id; + } + return 0; +} +bool +DNBArchImplI386::RestoreRegisterState (uint32_t save_id) +{ + SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id); + if (pos != m_saved_register_states.end()) + { + m_state.context.gpr = pos->second.gpr; + m_state.context.fpu = pos->second.fpu; + m_state.context.exc = pos->second.exc; + m_state.SetError(e_regSetGPR, Read, 0); + m_state.SetError(e_regSetFPU, Read, 0); + m_state.SetError(e_regSetEXC, Read, 0); + kern_return_t kret; + bool success = true; + if ((kret = SetGPRState()) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::RestoreRegisterState (save_id = %u) error: GPR regs failed to write: %u", save_id, kret); + success = false; + } + else if ((kret = SetFPUState()) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::RestoreRegisterState (save_id = %u) error: %s regs failed to write: %u", save_id, CPUHasAVX() ? "AVX" : "FPU", kret); + success = false; + } + m_saved_register_states.erase(pos); + return success; + } + return false; +} + + +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/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h new file mode 100644 index 000000000000..6b4252151feb --- /dev/null +++ b/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h @@ -0,0 +1,255 @@ +//===-- 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__) || defined (__x86_64__) + +#include "DNBArch.h" +#include "../HasAVX.h" +#include "MachRegisterStatesI386.h" + +#include + +class MachThread; + +class DNBArchImplI386 : public DNBArchProtocol +{ +public: + DNBArchImplI386(MachThread *thread) : + DNBArchProtocol(), + m_thread(thread), + m_state(), + m_2pc_dbg_checkpoint(), + m_2pc_trans_state(Trans_Done), + m_saved_register_states() + { + } + virtual ~DNBArchImplI386() + { + } + + static void Initialize(); + + virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value); + virtual bool SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value); + 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 uint32_t SaveRegisterState (); + virtual bool RestoreRegisterState (uint32_t save_id); + + 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); + + virtual uint32_t NumSupportedHardwareWatchpoints(); + virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task); + virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index, bool also_set_on_task); + virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr); + +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; + typedef __i386_avx_state_t AVX; + typedef __i386_debug_state_t DBG; + + static const DNBRegisterInfo g_gpr_registers[]; + static const DNBRegisterInfo g_fpu_registers_no_avx[]; + static const DNBRegisterInfo g_fpu_registers_avx[]; + static const DNBRegisterInfo g_exc_registers[]; + static const DNBRegisterSetInfo g_reg_sets_no_avx[]; + static const DNBRegisterSetInfo g_reg_sets_avx[]; + static const size_t k_num_gpr_registers; + static const size_t k_num_fpu_registers_no_avx; + static const size_t k_num_fpu_registers_avx; + static const size_t k_num_exc_registers; + static const size_t k_num_all_registers_no_avx; + static const size_t k_num_all_registers_avx; + static const size_t k_num_register_sets; + + typedef enum RegisterSetTag + { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, + e_regSetFPU, + e_regSetEXC, + e_regSetDBG, + kNumRegisterSets + } RegisterSet; + + typedef enum RegisterSetWordSizeTag + { + e_regSetWordSizeGPR = sizeof(GPR) / sizeof(int), + e_regSetWordSizeFPU = sizeof(FPU) / sizeof(int), + e_regSetWordSizeEXC = sizeof(EXC) / sizeof(int), + e_regSetWordSizeAVX = sizeof(AVX) / sizeof(int), + e_regSetWordSizeDBG = sizeof(DBG) / sizeof(int) + } RegisterSetWordSize; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + struct Context + { + GPR gpr; + union { + FPU no_avx; + AVX avx; + } fpu; + EXC exc; + DBG dbg; + }; + + 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 + kern_return_t dbg_errs[2]; // Read/Write errors + + State() + { + uint32_t i; + for (i=0; i SaveRegisterStates; + SaveRegisterStates m_saved_register_states; +}; + +#endif // #if defined (__i386__) || defined (__x86_64__) +#endif // #ifndef __DNBArchImplI386_h__ diff --git a/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h b/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h new file mode 100644 index 000000000000..59cfbe055a36 --- /dev/null +++ b/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h @@ -0,0 +1,180 @@ +//===-- MachRegisterStatesI386.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 Sean Callanan on 3/16/11. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachRegisterStatesI386_h__ +#define __MachRegisterStatesI386_h__ + +#include + +#define __i386_THREAD_STATE 1 +#define __i386_FLOAT_STATE 2 +#define __i386_EXCEPTION_STATE 3 +#define __i386_DEBUG_STATE 10 +#define __i386_AVX_STATE 16 + +typedef struct { + 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; +} __i386_thread_state_t; + +typedef struct { + uint16_t __invalid : 1; + uint16_t __denorm : 1; + uint16_t __zdiv : 1; + uint16_t __ovrfl : 1; + uint16_t __undfl : 1; + uint16_t __precis : 1; + uint16_t __PAD1 : 2; + uint16_t __pc : 2; + uint16_t __rc : 2; + uint16_t __PAD2 : 1; + uint16_t __PAD3 : 3; +} __i386_fp_control_t; + +typedef struct { + uint16_t __invalid : 1; + uint16_t __denorm : 1; + uint16_t __zdiv : 1; + uint16_t __ovrfl : 1; + uint16_t __undfl : 1; + uint16_t __precis : 1; + uint16_t __stkflt : 1; + uint16_t __errsumm : 1; + uint16_t __c0 : 1; + uint16_t __c1 : 1; + uint16_t __c2 : 1; + uint16_t __tos : 3; + uint16_t __c3 : 1; + uint16_t __busy : 1; +} __i386_fp_status_t; + +typedef struct { + uint8_t __mmst_reg[10]; + uint8_t __mmst_rsrv[6]; +} __i386_mmst_reg; + +typedef struct { + uint8_t __xmm_reg[16]; +} __i386_xmm_reg; + +typedef struct { + uint32_t __fpu_reserved[2]; + __i386_fp_control_t __fpu_fcw; + __i386_fp_status_t __fpu_fsw; + uint8_t __fpu_ftw; + uint8_t __fpu_rsrv1; + uint16_t __fpu_fop; + uint32_t __fpu_ip; + uint16_t __fpu_cs; + uint16_t __fpu_rsrv2; + uint32_t __fpu_dp; + uint16_t __fpu_ds; + uint16_t __fpu_rsrv3; + uint32_t __fpu_mxcsr; + uint32_t __fpu_mxcsrmask; + __i386_mmst_reg __fpu_stmm0; + __i386_mmst_reg __fpu_stmm1; + __i386_mmst_reg __fpu_stmm2; + __i386_mmst_reg __fpu_stmm3; + __i386_mmst_reg __fpu_stmm4; + __i386_mmst_reg __fpu_stmm5; + __i386_mmst_reg __fpu_stmm6; + __i386_mmst_reg __fpu_stmm7; + __i386_xmm_reg __fpu_xmm0; + __i386_xmm_reg __fpu_xmm1; + __i386_xmm_reg __fpu_xmm2; + __i386_xmm_reg __fpu_xmm3; + __i386_xmm_reg __fpu_xmm4; + __i386_xmm_reg __fpu_xmm5; + __i386_xmm_reg __fpu_xmm6; + __i386_xmm_reg __fpu_xmm7; + uint8_t __fpu_rsrv4[14*16]; + uint32_t __fpu_reserved1; +} __i386_float_state_t; + +typedef struct { + uint32_t __fpu_reserved[2]; + __i386_fp_control_t __fpu_fcw; + __i386_fp_status_t __fpu_fsw; + uint8_t __fpu_ftw; + uint8_t __fpu_rsrv1; + uint16_t __fpu_fop; + uint32_t __fpu_ip; + uint16_t __fpu_cs; + uint16_t __fpu_rsrv2; + uint32_t __fpu_dp; + uint16_t __fpu_ds; + uint16_t __fpu_rsrv3; + uint32_t __fpu_mxcsr; + uint32_t __fpu_mxcsrmask; + __i386_mmst_reg __fpu_stmm0; + __i386_mmst_reg __fpu_stmm1; + __i386_mmst_reg __fpu_stmm2; + __i386_mmst_reg __fpu_stmm3; + __i386_mmst_reg __fpu_stmm4; + __i386_mmst_reg __fpu_stmm5; + __i386_mmst_reg __fpu_stmm6; + __i386_mmst_reg __fpu_stmm7; + __i386_xmm_reg __fpu_xmm0; + __i386_xmm_reg __fpu_xmm1; + __i386_xmm_reg __fpu_xmm2; + __i386_xmm_reg __fpu_xmm3; + __i386_xmm_reg __fpu_xmm4; + __i386_xmm_reg __fpu_xmm5; + __i386_xmm_reg __fpu_xmm6; + __i386_xmm_reg __fpu_xmm7; + uint8_t __fpu_rsrv4[14*16]; + uint32_t __fpu_reserved1; + uint8_t __avx_reserved1[64]; + __i386_xmm_reg __fpu_ymmh0; + __i386_xmm_reg __fpu_ymmh1; + __i386_xmm_reg __fpu_ymmh2; + __i386_xmm_reg __fpu_ymmh3; + __i386_xmm_reg __fpu_ymmh4; + __i386_xmm_reg __fpu_ymmh5; + __i386_xmm_reg __fpu_ymmh6; + __i386_xmm_reg __fpu_ymmh7; +} __i386_avx_state_t; + +typedef struct { + uint32_t __trapno; + uint32_t __err; + uint32_t __faultvaddr; +} __i386_exception_state_t; + +typedef struct { + uint32_t __dr0; + uint32_t __dr1; + uint32_t __dr2; + uint32_t __dr3; + uint32_t __dr4; + uint32_t __dr5; + uint32_t __dr6; + uint32_t __dr7; +} __i386_debug_state_t; + +#endif diff --git a/tools/debugserver/source/MacOSX/i386/Makefile b/tools/debugserver/source/MacOSX/i386/Makefile new file mode 100644 index 000000000000..f770b19834bb --- /dev/null +++ b/tools/debugserver/source/MacOSX/i386/Makefile @@ -0,0 +1,19 @@ +##===- tools/debugserver/source/MacOSX/i386/Makefile -------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../../.. + +LIBRARYNAME := lldbDebugserverMacOSX_I386 +BUILD_ARCHIVE = 1 + +SOURCES := DNBArchImplI386.cpp + +include $(LLDB_LEVEL)/Makefile + +CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../.. -I$(PROJ_OBJ_DIR)/../../.. \ No newline at end of file diff --git a/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp b/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp new file mode 100644 index 000000000000..c6f1a718ac92 --- /dev/null +++ b/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp @@ -0,0 +1,569 @@ +//===-- 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 (__powerpc__) || defined (__ppc__) || defined (__ppc64__) + +#if __DARWIN_UNIX03 +#define PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(reg) __##reg +#else +#define PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(reg) reg +#endif + +#include "MacOSX/ppc/DNBArchImpl.h" +#include "MacOSX/MachThread.h" +#include "DNBBreakpoint.h" +#include "DNBLog.h" +#include "DNBRegisterInfo.h" + +static const uint8_t g_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + +const uint8_t * +DNBArchMachPPC::SoftwareBreakpointOpcode (nub_size_t size) +{ + if (size == 4) + return g_breakpoint_opcode; + return NULL; +} + +uint32_t +DNBArchMachPPC::GetCPUType() +{ + return CPU_TYPE_POWERPC; +} + +uint64_t +DNBArchMachPPC::GetPC(uint64_t failValue) +{ + // Get program counter + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0); + return failValue; +} + +kern_return_t +DNBArchMachPPC::SetPC(uint64_t value) +{ + // Get program counter + kern_return_t err = GetGPRState(false); + if (err == KERN_SUCCESS) + { + m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0) = value; + err = SetGPRState(); + } + return err == KERN_SUCCESS; +} + +uint64_t +DNBArchMachPPC::GetSP(uint64_t failValue) +{ + // Get stack pointer + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(r1); + return failValue; +} + +kern_return_t +DNBArchMachPPC::GetGPRState(bool force) +{ + if (force || m_state.GetError(e_regSetGPR, Read)) + { + mach_msg_type_number_t count = e_regSetWordSizeGPR; + m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->MachPortNumber(), 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->MachPortNumber(), 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->MachPortNumber(), 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->MachPortNumber(), 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->MachPortNumber(), 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->MachPortNumber(), 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->MachPortNumber(), 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->MachPortNumber(), 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 definitions 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(uint32_t set, uint32_t 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/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h b/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h new file mode 100644 index 000000000000..8ea81538dc48 --- /dev/null +++ b/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h @@ -0,0 +1,179 @@ +//===-- 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(uint32_t set, uint32_t 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 * 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 + +#define stack_logging_type_free 0 +#define stack_logging_type_generic 1 /* anything that is not allocation/deallocation */ +#define stack_logging_type_alloc 2 /* malloc, realloc, etc... */ +#define stack_logging_type_dealloc 4 /* free, realloc, etc... */ + +// Following flags are absorbed by stack_logging_log_stack() +#define stack_logging_flag_zone 8 /* NSZoneMalloc, etc... */ +#define stack_logging_flag_calloc 16 /* multiply arguments to get the size */ +#define stack_logging_flag_object 32 /* NSAllocateObject(Class, extraBytes, zone) */ +#define stack_logging_flag_cleared 64 /* for NewEmptyHandle */ +#define stack_logging_flag_handle 128 /* for Handle (de-)allocation routines */ +#define stack_logging_flag_set_handle_size 256 /* (Handle, newSize) treated specially */ + +/* Macro used to disguise addresses so that leak finding can work */ +#define STACK_LOGGING_DISGUISE(address) ((address) ^ 0x00005555) /* nicely idempotent */ + +extern "C" int stack_logging_enable_logging; /* when clear, no logging takes place */ +extern "C" int stack_logging_dontcompact; /* default is to compact; when set does not compact alloc/free logs; useful for tracing history */ + + +extern "C" void stack_logging_log_stack(unsigned type, unsigned arg1, unsigned arg2, unsigned arg3, unsigned result, unsigned num_hot_to_skip); +/* This is the old log-to-memory logger, which is now deprecated. It remains for compatibility with performance tools that haven't been updated to disk_stack_logging_log_stack() yet. */ + +extern "C" void __disk_stack_logging_log_stack(uint32_t type_flags, uintptr_t zone_ptr, uintptr_t size, uintptr_t ptr_arg, uintptr_t return_val, uint32_t num_hot_to_skip); +/* Fits as the malloc_logger; logs malloc/free/realloc events and can log custom events if called directly */ + + +/* 64-bit-aware stack log access. */ +typedef struct { + uint32_t type_flags; + uint64_t stack_identifier; + uint64_t argument; + mach_vm_address_t address; +} mach_stack_logging_record_t; + +extern "C" kern_return_t __mach_stack_logging_get_frames(task_t task, mach_vm_address_t address, mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames, uint32_t *count); +/* Gets the last allocation record (malloc, realloc, or free) about address */ + +extern "C" kern_return_t __mach_stack_logging_enumerate_records(task_t task, mach_vm_address_t address, void enumerator(mach_stack_logging_record_t, void *), void *context); +/* Applies enumerator to all records involving address sending context as enumerator's second parameter; if !address, applies enumerator to all records */ + +extern "C" kern_return_t __mach_stack_logging_frames_for_uniqued_stack(task_t task, uint64_t stack_identifier, mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames, uint32_t *count); +/* Given a uniqued_stack fills stack_frames_buffer */ + + +#pragma mark - +#pragma mark Legacy + +/* The following is the old 32-bit-only, in-process-memory stack logging. This is deprecated and clients should move to the above 64-bit-aware disk stack logging SPI. */ + +typedef struct { + unsigned type; + unsigned uniqued_stack; + unsigned argument; + unsigned address; /* disguised, to avoid confusing leaks */ +} stack_logging_record_t; + +typedef struct { + unsigned overall_num_bytes; + unsigned num_records; + unsigned lock; /* 0 means OK to lock; used for inter-process locking */ + unsigned *uniquing_table; /* allocated using vm_allocate() */ + /* hashtable organized as (PC, uniqued parent) + Only the second half of the table is active + To enable us to grow dynamically */ + unsigned uniquing_table_num_pages; /* number of pages of the table */ + unsigned extra_retain_count; /* not used by stack_logging_log_stack */ + unsigned filler[2]; /* align to cache lines for better performance */ + stack_logging_record_t records[0]; /* records follow here */ +} stack_logging_record_list_t; + +extern "C" stack_logging_record_list_t *stack_logging_the_record_list; +/* This is the global variable containing all logs */ + +extern "C" kern_return_t stack_logging_get_frames(task_t task, memory_reader_t reader, vm_address_t address, vm_address_t *stack_frames_buffer, unsigned max_stack_frames, unsigned *num_frames); +/* Gets the last record in stack_logging_the_record_list about address */ + +#define STACK_LOGGING_ENUMERATION_PROVIDED 1 // temporary to avoid dependencies between projects + +extern "C" kern_return_t stack_logging_enumerate_records(task_t task, memory_reader_t reader, vm_address_t address, void enumerator(stack_logging_record_t, void *), void *context); +/* Gets all the records about address; + If !address, gets all records */ + +extern "C" kern_return_t stack_logging_frames_for_uniqued_stack(task_t task, memory_reader_t reader, unsigned uniqued_stack, vm_address_t *stack_frames_buffer, unsigned max_stack_frames, unsigned *num_frames); +/* Given a uniqued_stack fills stack_frames_buffer */ + + + +extern "C" void thread_stack_pcs(vm_address_t *buffer, unsigned max, unsigned *num); +/* Convenience to fill buffer with the PCs of the frames, starting with the hot frames; + num: returned number of frames + */ + +#endif diff --git a/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt b/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt new file mode 100644 index 000000000000..bb41b04d9d9e --- /dev/null +++ b/tools/debugserver/source/MacOSX/x86_64/CMakeLists.txt @@ -0,0 +1,8 @@ +# Due to sources including headers like: +# #include "MacOSX/i386/DNBArchImplI386.h" +# we must include the grandparent directory... +include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source) + +add_library(lldbDebugserverMacOSX_X86_64 + DNBArchImplX86_64.cpp + ) diff --git a/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp new file mode 100644 index 000000000000..3d2805cddb91 --- /dev/null +++ b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp @@ -0,0 +1,2289 @@ +//===-- DNBArchImplX86_64.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__) || defined (__x86_64__) + +#include +#include +#include + +#include "MacOSX/x86_64/DNBArchImplX86_64.h" +#include "../HasAVX.h" +#include "DNBLog.h" +#include "MachThread.h" +#include "MachProcess.h" +#include +#include + +#if defined (LLDB_DEBUGSERVER_RELEASE) || defined (LLDB_DEBUGSERVER_DEBUG) +enum debugState { + debugStateUnknown, + debugStateOff, + debugStateOn +}; + +static debugState sFPUDebugState = debugStateUnknown; +static debugState sAVXForceState = debugStateUnknown; + +static bool DebugFPURegs () +{ + if (sFPUDebugState == debugStateUnknown) + { + if (getenv("DNB_DEBUG_FPU_REGS")) + sFPUDebugState = debugStateOn; + else + sFPUDebugState = debugStateOff; + } + + return (sFPUDebugState == debugStateOn); +} + +static bool ForceAVXRegs () +{ + if (sFPUDebugState == debugStateUnknown) + { + if (getenv("DNB_DEBUG_X86_FORCE_AVX_REGS")) + sAVXForceState = debugStateOn; + else + sAVXForceState = debugStateOff; + } + + return (sAVXForceState == debugStateOn); +} + +#define DEBUG_FPU_REGS (DebugFPURegs()) +#define FORCE_AVX_REGS (ForceAVXRegs()) +#else +#define DEBUG_FPU_REGS (0) +#define FORCE_AVX_REGS (0) +#endif + + +extern "C" bool +CPUHasAVX() +{ + enum AVXPresence + { + eAVXUnknown = -1, + eAVXNotPresent = 0, + eAVXPresent = 1 + }; + + static AVXPresence g_has_avx = eAVXUnknown; + if (g_has_avx == eAVXUnknown) + { + g_has_avx = eAVXNotPresent; + + // Only xnu-2020 or later has AVX support, any versions before + // this have a busted thread_get_state RPC where it would truncate + // the thread state buffer (). So we need to + // verify the kernel version number manually or disable AVX support. + int mib[2]; + char buffer[1024]; + size_t length = sizeof(buffer); + uint64_t xnu_version = 0; + mib[0] = CTL_KERN; + mib[1] = KERN_VERSION; + int err = ::sysctl(mib, 2, &buffer, &length, NULL, 0); + if (err == 0) + { + const char *xnu = strstr (buffer, "xnu-"); + if (xnu) + { + const char *xnu_version_cstr = xnu + 4; + xnu_version = strtoull (xnu_version_cstr, NULL, 0); + if (xnu_version >= 2020 && xnu_version != ULLONG_MAX) + { + if (::HasAVX()) + { + g_has_avx = eAVXPresent; + } + } + } + } + DNBLogThreadedIf (LOG_THREAD, "CPUHasAVX(): g_has_avx = %i (err = %i, errno = %i, xnu_version = %llu)", g_has_avx, err, errno, xnu_version); + } + + return (g_has_avx == eAVXPresent); +} + +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 = e_regSetWordSizeGPR; + m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_THREAD_STATE, (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->MachPortNumber(), 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->MachPortNumber(), + // 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_REGS 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_REGS) { + if (CPUHasAVX() || FORCE_AVX_REGS) + { + m_state.context.fpu.avx.__fpu_reserved[0] = -1; + m_state.context.fpu.avx.__fpu_reserved[1] = -1; + *(uint16_t *)&(m_state.context.fpu.avx.__fpu_fcw) = 0x1234; + *(uint16_t *)&(m_state.context.fpu.avx.__fpu_fsw) = 0x5678; + m_state.context.fpu.avx.__fpu_ftw = 1; + m_state.context.fpu.avx.__fpu_rsrv1 = UINT8_MAX; + m_state.context.fpu.avx.__fpu_fop = 2; + m_state.context.fpu.avx.__fpu_ip = 3; + m_state.context.fpu.avx.__fpu_cs = 4; + m_state.context.fpu.avx.__fpu_rsrv2 = UINT8_MAX; + m_state.context.fpu.avx.__fpu_dp = 5; + m_state.context.fpu.avx.__fpu_ds = 6; + m_state.context.fpu.avx.__fpu_rsrv3 = UINT16_MAX; + m_state.context.fpu.avx.__fpu_mxcsr = 8; + m_state.context.fpu.avx.__fpu_mxcsrmask = 9; + int i; + for (i=0; i<16; ++i) + { + if (i<10) + { + m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg[i] = 'a'; + m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg[i] = 'b'; + m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg[i] = 'c'; + m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg[i] = 'd'; + m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg[i] = 'e'; + m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg[i] = 'f'; + m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg[i] = 'g'; + m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg[i] = 'h'; + } + else + { + m_state.context.fpu.avx.__fpu_stmm0.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm1.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm2.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm3.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm4.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm5.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm6.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.avx.__fpu_stmm7.__mmst_reg[i] = INT8_MIN; + } + + m_state.context.fpu.avx.__fpu_xmm0.__xmm_reg[i] = '0' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm1.__xmm_reg[i] = '1' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm2.__xmm_reg[i] = '2' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm3.__xmm_reg[i] = '3' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm4.__xmm_reg[i] = '4' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm5.__xmm_reg[i] = '5' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm6.__xmm_reg[i] = '6' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm7.__xmm_reg[i] = '7' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm8.__xmm_reg[i] = '8' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm9.__xmm_reg[i] = '9' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm10.__xmm_reg[i] = 'A' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm11.__xmm_reg[i] = 'B' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm12.__xmm_reg[i] = 'C' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm13.__xmm_reg[i] = 'D' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm14.__xmm_reg[i] = 'E' + 2 * i; + m_state.context.fpu.avx.__fpu_xmm15.__xmm_reg[i] = 'F' + 2 * i; + + m_state.context.fpu.avx.__fpu_ymmh0.__xmm_reg[i] = '0' + i; + m_state.context.fpu.avx.__fpu_ymmh1.__xmm_reg[i] = '1' + i; + m_state.context.fpu.avx.__fpu_ymmh2.__xmm_reg[i] = '2' + i; + m_state.context.fpu.avx.__fpu_ymmh3.__xmm_reg[i] = '3' + i; + m_state.context.fpu.avx.__fpu_ymmh4.__xmm_reg[i] = '4' + i; + m_state.context.fpu.avx.__fpu_ymmh5.__xmm_reg[i] = '5' + i; + m_state.context.fpu.avx.__fpu_ymmh6.__xmm_reg[i] = '6' + i; + m_state.context.fpu.avx.__fpu_ymmh7.__xmm_reg[i] = '7' + i; + m_state.context.fpu.avx.__fpu_ymmh8.__xmm_reg[i] = '8' + i; + m_state.context.fpu.avx.__fpu_ymmh9.__xmm_reg[i] = '9' + i; + m_state.context.fpu.avx.__fpu_ymmh10.__xmm_reg[i] = 'A' + i; + m_state.context.fpu.avx.__fpu_ymmh11.__xmm_reg[i] = 'B' + i; + m_state.context.fpu.avx.__fpu_ymmh12.__xmm_reg[i] = 'C' + i; + m_state.context.fpu.avx.__fpu_ymmh13.__xmm_reg[i] = 'D' + i; + m_state.context.fpu.avx.__fpu_ymmh14.__xmm_reg[i] = 'E' + i; + m_state.context.fpu.avx.__fpu_ymmh15.__xmm_reg[i] = 'F' + i; + } + for (i=0; iMachPortNumber(), __x86_64_AVX_STATE, (thread_state_t)&m_state.context.fpu.avx, &count)); + DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &avx, %u (%u passed in) carp) => 0x%8.8x", + m_thread->MachPortNumber(), __x86_64_AVX_STATE, (uint32_t)count, + e_regSetWordSizeAVX, m_state.GetError(e_regSetFPU, Read)); + } + else + { + mach_msg_type_number_t count = e_regSetWordSizeFPU; + m_state.SetError(e_regSetFPU, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_FLOAT_STATE, (thread_state_t)&m_state.context.fpu.no_avx, &count)); + DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &fpu, %u (%u passed in) => 0x%8.8x", + m_thread->MachPortNumber(), __x86_64_FLOAT_STATE, (uint32_t)count, + e_regSetWordSizeFPU, m_state.GetError(e_regSetFPU, Read)); + } + } + } + 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 = e_regSetWordSizeEXC; + m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, &count)); + } + return m_state.GetError(e_regSetEXC, Read); +} + +kern_return_t +DNBArchImplX86_64::SetGPRState() +{ + kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber()); + DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount()); + + m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_THREAD_STATE, (thread_state_t)&m_state.context.gpr, e_regSetWordSizeGPR)); + 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->MachPortNumber(), __x86_64_THREAD_STATE, e_regSetWordSizeGPR, + 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() +{ + if (DEBUG_FPU_REGS) + { + m_state.SetError(e_regSetFPU, Write, 0); + return m_state.GetError(e_regSetFPU, Write); + } + else + { + if (CPUHasAVX() || FORCE_AVX_REGS) + { + m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_AVX_STATE, (thread_state_t)&m_state.context.fpu.avx, e_regSetWordSizeAVX)); + return m_state.GetError(e_regSetFPU, Write); + } + else + { + m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_FLOAT_STATE, (thread_state_t)&m_state.context.fpu.no_avx, e_regSetWordSizeFPU)); + return m_state.GetError(e_regSetFPU, Write); + } + } +} + +kern_return_t +DNBArchImplX86_64::SetEXCState() +{ + m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_EXCEPTION_STATE, (thread_state_t)&m_state.context.exc, e_regSetWordSizeEXC)); + return m_state.GetError(e_regSetEXC, Write); +} + +kern_return_t +DNBArchImplX86_64::GetDBGState(bool force) +{ + if (force || m_state.GetError(e_regSetDBG, Read)) + { + mach_msg_type_number_t count = e_regSetWordSizeDBG; + m_state.SetError(e_regSetDBG, Read, ::thread_get_state(m_thread->MachPortNumber(), __x86_64_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, &count)); + } + return m_state.GetError(e_regSetDBG, Read); +} + +kern_return_t +DNBArchImplX86_64::SetDBGState(bool also_set_on_task) +{ + m_state.SetError(e_regSetDBG, Write, ::thread_set_state(m_thread->MachPortNumber(), __x86_64_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG)); + if (also_set_on_task) + { + kern_return_t kret = ::task_set_state(m_thread->Process()->Task().TaskPort(), __x86_64_DEBUG_STATE, (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG); + if (kret != KERN_SUCCESS) + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::SetDBGState failed to set debug control register state: 0x%8.8x.", kret); + } + return m_state.GetError(e_regSetDBG, 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); + } + + // Reset the debug status register, if necessary, before we resume. + kern_return_t kret = GetDBGState(false); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::ThreadWillResume() GetDBGState() => 0x%8.8x.", kret); + if (kret != KERN_SUCCESS) + return; + + DBG &debug_state = m_state.context.dbg; + bool need_reset = false; + uint32_t i, num = NumSupportedHardwareWatchpoints(); + for (i = 0; i < num; ++i) + if (IsWatchpointHit(debug_state, i)) + need_reset = true; + + if (need_reset) + { + ClearWatchpointHits(debug_state); + kret = SetDBGState(false); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::ThreadWillResume() SetDBGState() => 0x%8.8x.", kret); + } +} + +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) + { + // exc_code = EXC_I386_BPT + // + 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. + + DNBBreakpoint *bp = m_thread->Process()->Breakpoints().FindByAddress(pc); + if (bp) + { + // 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 (); + } + } + return true; + } + } + else if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 1) + { + // exc_code = EXC_I386_SGL + // + // Check whether this corresponds to a watchpoint hit event. + // If yes, set the exc_sub_code to the data break address. + nub_addr_t addr = 0; + uint32_t hw_index = GetHardwareWatchpointHit(addr); + if (hw_index != INVALID_NUB_HW_INDEX) + { + exc.exc_data[1] = addr; + // Piggyback the hw_index in the exc.data. + exc.exc_data.push_back(hw_index); + } + + return true; + } + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + return false; +} + +uint32_t +DNBArchImplX86_64::NumSupportedHardwareWatchpoints() +{ + // Available debug address registers: dr0, dr1, dr2, dr3. + return 4; +} + +static uint32_t +size_and_rw_bits(nub_size_t size, bool read, bool write) +{ + uint32_t rw; + if (read) { + rw = 0x3; // READ or READ/WRITE + } else if (write) { + rw = 0x1; // WRITE + } else { + assert(0 && "read and write cannot both be false"); + } + + switch (size) { + case 1: + return rw; + case 2: + return (0x1 << 2) | rw; + case 4: + return (0x3 << 2) | rw; + case 8: + return (0x2 << 2) | rw; + } + assert(0 && "invalid size, must be one of 1, 2, 4, or 8"); + return 0; +} +void +DNBArchImplX86_64::SetWatchpoint(DBG &debug_state, uint32_t hw_index, nub_addr_t addr, nub_size_t size, bool read, bool write) +{ + // Set both dr7 (debug control register) and dri (debug address register). + + // dr7{7-0} encodes the local/gloabl enable bits: + // global enable --. .-- local enable + // | | + // v v + // dr0 -> bits{1-0} + // dr1 -> bits{3-2} + // dr2 -> bits{5-4} + // dr3 -> bits{7-6} + // + // dr7{31-16} encodes the rw/len bits: + // b_x+3, b_x+2, b_x+1, b_x + // where bits{x+1, x} => rw + // 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io read-or-write (unused) + // and bits{x+3, x+2} => len + // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte + // + // dr0 -> bits{19-16} + // dr1 -> bits{23-20} + // dr2 -> bits{27-24} + // dr3 -> bits{31-28} + debug_state.__dr7 |= (1 << (2*hw_index) | + size_and_rw_bits(size, read, write) << (16+4*hw_index)); + switch (hw_index) { + case 0: + debug_state.__dr0 = addr; break; + case 1: + debug_state.__dr1 = addr; break; + case 2: + debug_state.__dr2 = addr; break; + case 3: + debug_state.__dr3 = addr; break; + default: + assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3"); + } + return; +} + +void +DNBArchImplX86_64::ClearWatchpoint(DBG &debug_state, uint32_t hw_index) +{ + debug_state.__dr7 &= ~(3 << (2*hw_index)); + switch (hw_index) { + case 0: + debug_state.__dr0 = 0; break; + case 1: + debug_state.__dr1 = 0; break; + case 2: + debug_state.__dr2 = 0; break; + case 3: + debug_state.__dr3 = 0; break; + default: + assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3"); + } + return; +} + +bool +DNBArchImplX86_64::IsWatchpointVacant(const DBG &debug_state, uint32_t hw_index) +{ + // Check dr7 (debug control register) for local/global enable bits: + // global enable --. .-- local enable + // | | + // v v + // dr0 -> bits{1-0} + // dr1 -> bits{3-2} + // dr2 -> bits{5-4} + // dr3 -> bits{7-6} + return (debug_state.__dr7 & (3 << (2*hw_index))) == 0; +} + +// Resets local copy of debug status register to wait for the next debug exception. +void +DNBArchImplX86_64::ClearWatchpointHits(DBG &debug_state) +{ + // See also IsWatchpointHit(). + debug_state.__dr6 = 0; + return; +} + +bool +DNBArchImplX86_64::IsWatchpointHit(const DBG &debug_state, uint32_t hw_index) +{ + // Check dr6 (debug status register) whether a watchpoint hits: + // is watchpoint hit? + // | + // v + // dr0 -> bits{0} + // dr1 -> bits{1} + // dr2 -> bits{2} + // dr3 -> bits{3} + return (debug_state.__dr6 & (1 << hw_index)); +} + +nub_addr_t +DNBArchImplX86_64::GetWatchAddress(const DBG &debug_state, uint32_t hw_index) +{ + switch (hw_index) { + case 0: + return debug_state.__dr0; + case 1: + return debug_state.__dr1; + case 2: + return debug_state.__dr2; + case 3: + return debug_state.__dr3; + } + assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3"); + return 0; +} + +bool +DNBArchImplX86_64::StartTransForHWP() +{ + if (m_2pc_trans_state != Trans_Done && m_2pc_trans_state != Trans_Rolled_Back) + DNBLogError ("%s inconsistent state detected, expected %d or %d, got: %d", __FUNCTION__, Trans_Done, Trans_Rolled_Back, m_2pc_trans_state); + m_2pc_dbg_checkpoint = m_state.context.dbg; + m_2pc_trans_state = Trans_Pending; + return true; +} +bool +DNBArchImplX86_64::RollbackTransForHWP() +{ + m_state.context.dbg = m_2pc_dbg_checkpoint; + if (m_2pc_trans_state != Trans_Pending) + DNBLogError ("%s inconsistent state detected, expected %d, got: %d", __FUNCTION__, Trans_Pending, m_2pc_trans_state); + m_2pc_trans_state = Trans_Rolled_Back; + kern_return_t kret = SetDBGState(false); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::RollbackTransForHWP() SetDBGState() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return true; + else + return false; +} +bool +DNBArchImplX86_64::FinishTransForHWP() +{ + m_2pc_trans_state = Trans_Done; + return true; +} +DNBArchImplX86_64::DBG +DNBArchImplX86_64::GetDBGCheckpoint() +{ + return m_2pc_dbg_checkpoint; +} + +uint32_t +DNBArchImplX86_64::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::EnableHardwareWatchpoint(addr = 0x%llx, size = %llu, read = %u, write = %u)", (uint64_t)addr, (uint64_t)size, read, write); + + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + // Can only watch 1, 2, 4, or 8 bytes. + if (!(size == 1 || size == 2 || size == 4 || size == 8)) + return INVALID_NUB_HW_INDEX; + + // We must watch for either read or write + if (read == false && write == false) + 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; + + DBG &debug_state = m_state.context.dbg; + for (i = 0; i < num_hw_watchpoints; ++i) + { + if (IsWatchpointVacant(debug_state, i)) + break; + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_watchpoints) + { + StartTransForHWP(); + + // Modify our local copy of the debug state, first. + SetWatchpoint(debug_state, i, addr, size, read, write); + // Now set the watch point in the inferior. + kret = SetDBGState(also_set_on_task); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::EnableHardwareWatchpoint() SetDBGState() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + else // Revert to the previous debug state voluntarily. The transaction coordinator knows that we have failed. + m_state.context.dbg = GetDBGCheckpoint(); + } + else + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints); + } + } + return INVALID_NUB_HW_INDEX; +} + +bool +DNBArchImplX86_64::DisableHardwareWatchpoint (uint32_t hw_index, bool also_set_on_task) +{ + kern_return_t kret = GetDBGState(false); + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (kret == KERN_SUCCESS) + { + DBG &debug_state = m_state.context.dbg; + if (hw_index < num_hw_points && !IsWatchpointVacant(debug_state, hw_index)) + { + StartTransForHWP(); + + // Modify our local copy of the debug state, first. + ClearWatchpoint(debug_state, hw_index); + // Now disable the watch point in the inferior. + kret = SetDBGState(also_set_on_task); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::DisableHardwareWatchpoint( %u )", + hw_index); + + if (kret == KERN_SUCCESS) + return true; + else // Revert to the previous debug state voluntarily. The transaction coordinator knows that we have failed. + m_state.context.dbg = GetDBGCheckpoint(); + } + } + return false; +} + +// Iterate through the debug status register; return the index of the first hit. +uint32_t +DNBArchImplX86_64::GetHardwareWatchpointHit(nub_addr_t &addr) +{ + // Read the debug state + kern_return_t kret = GetDBGState(true); + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.", kret); + if (kret == KERN_SUCCESS) + { + DBG &debug_state = m_state.context.dbg; + uint32_t i, num = NumSupportedHardwareWatchpoints(); + for (i = 0; i < num; ++i) + { + if (IsWatchpointHit(debug_state, i)) + { + addr = GetWatchAddress(debug_state, i); + DNBLogThreadedIf(LOG_WATCHPOINTS, + "DNBArchImplX86_64::GetHardwareWatchpointHit() found => %u (addr = 0x%llx).", + i, + (uint64_t)addr); + return i; + } + } + } + return INVALID_NUB_HW_INDEX; +} + +// 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 definitions +//---------------------------------------------------------------------- + +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, + gpr_eax, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_r8d, // Low 32 bits or r8 + gpr_r9d, // Low 32 bits or r9 + gpr_r10d, // Low 32 bits or r10 + gpr_r11d, // Low 32 bits or r11 + gpr_r12d, // Low 32 bits or r12 + gpr_r13d, // Low 32 bits or r13 + gpr_r14d, // Low 32 bits or r14 + gpr_r15d, // Low 32 bits or r15 + gpr_ax , + gpr_bx , + gpr_cx , + gpr_dx , + gpr_di , + gpr_si , + gpr_bp , + gpr_sp , + gpr_r8w, // Low 16 bits or r8 + gpr_r9w, // Low 16 bits or r9 + gpr_r10w, // Low 16 bits or r10 + gpr_r11w, // Low 16 bits or r11 + gpr_r12w, // Low 16 bits or r12 + gpr_r13w, // Low 16 bits or r13 + gpr_r14w, // Low 16 bits or r14 + gpr_r15w, // Low 16 bits or r15 + gpr_ah , + gpr_bh , + gpr_ch , + gpr_dh , + gpr_al , + gpr_bl , + gpr_cl , + gpr_dl , + gpr_dil, + gpr_sil, + gpr_bpl, + gpr_spl, + gpr_r8l, // Low 8 bits or r8 + gpr_r9l, // Low 8 bits or r9 + gpr_r10l, // Low 8 bits or r10 + gpr_r11l, // Low 8 bits or r11 + gpr_r12l, // Low 8 bits or r12 + gpr_r13l, // Low 8 bits or r13 + gpr_r14l, // Low 8 bits or r14 + gpr_r15l, // Low 8 bits or r15 + 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, + fpu_ymm0, + fpu_ymm1, + fpu_ymm2, + fpu_ymm3, + fpu_ymm4, + fpu_ymm5, + fpu_ymm6, + fpu_ymm7, + fpu_ymm8, + fpu_ymm9, + fpu_ymm10, + fpu_ymm11, + fpu_ymm12, + fpu_ymm13, + fpu_ymm14, + fpu_ymm15, + 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 ehframe_dwarf_regnums +{ + ehframe_dwarf_rax = 0, + ehframe_dwarf_rdx = 1, + ehframe_dwarf_rcx = 2, + ehframe_dwarf_rbx = 3, + ehframe_dwarf_rsi = 4, + ehframe_dwarf_rdi = 5, + ehframe_dwarf_rbp = 6, + ehframe_dwarf_rsp = 7, + ehframe_dwarf_r8, + ehframe_dwarf_r9, + ehframe_dwarf_r10, + ehframe_dwarf_r11, + ehframe_dwarf_r12, + ehframe_dwarf_r13, + ehframe_dwarf_r14, + ehframe_dwarf_r15, + ehframe_dwarf_rip, + ehframe_dwarf_xmm0, + ehframe_dwarf_xmm1, + ehframe_dwarf_xmm2, + ehframe_dwarf_xmm3, + ehframe_dwarf_xmm4, + ehframe_dwarf_xmm5, + ehframe_dwarf_xmm6, + ehframe_dwarf_xmm7, + ehframe_dwarf_xmm8, + ehframe_dwarf_xmm9, + ehframe_dwarf_xmm10, + ehframe_dwarf_xmm11, + ehframe_dwarf_xmm12, + ehframe_dwarf_xmm13, + ehframe_dwarf_xmm14, + ehframe_dwarf_xmm15, + ehframe_dwarf_stmm0, + ehframe_dwarf_stmm1, + ehframe_dwarf_stmm2, + ehframe_dwarf_stmm3, + ehframe_dwarf_stmm4, + ehframe_dwarf_stmm5, + ehframe_dwarf_stmm6, + ehframe_dwarf_stmm7, + ehframe_dwarf_ymm0 = ehframe_dwarf_xmm0, + ehframe_dwarf_ymm1 = ehframe_dwarf_xmm1, + ehframe_dwarf_ymm2 = ehframe_dwarf_xmm2, + ehframe_dwarf_ymm3 = ehframe_dwarf_xmm3, + ehframe_dwarf_ymm4 = ehframe_dwarf_xmm4, + ehframe_dwarf_ymm5 = ehframe_dwarf_xmm5, + ehframe_dwarf_ymm6 = ehframe_dwarf_xmm6, + ehframe_dwarf_ymm7 = ehframe_dwarf_xmm7, + ehframe_dwarf_ymm8 = ehframe_dwarf_xmm8, + ehframe_dwarf_ymm9 = ehframe_dwarf_xmm9, + ehframe_dwarf_ymm10 = ehframe_dwarf_xmm10, + ehframe_dwarf_ymm11 = ehframe_dwarf_xmm11, + ehframe_dwarf_ymm12 = ehframe_dwarf_xmm12, + ehframe_dwarf_ymm13 = ehframe_dwarf_xmm13, + ehframe_dwarf_ymm14 = ehframe_dwarf_xmm14, + ehframe_dwarf_ymm15 = ehframe_dwarf_xmm15 +}; + +enum debugserver_regnums +{ + debugserver_rax = 0, + debugserver_rbx = 1, + debugserver_rcx = 2, + debugserver_rdx = 3, + debugserver_rsi = 4, + debugserver_rdi = 5, + debugserver_rbp = 6, + debugserver_rsp = 7, + debugserver_r8 = 8, + debugserver_r9 = 9, + debugserver_r10 = 10, + debugserver_r11 = 11, + debugserver_r12 = 12, + debugserver_r13 = 13, + debugserver_r14 = 14, + debugserver_r15 = 15, + debugserver_rip = 16, + debugserver_rflags = 17, + debugserver_cs = 18, + debugserver_ss = 19, + debugserver_ds = 20, + debugserver_es = 21, + debugserver_fs = 22, + debugserver_gs = 23, + debugserver_stmm0 = 24, + debugserver_stmm1 = 25, + debugserver_stmm2 = 26, + debugserver_stmm3 = 27, + debugserver_stmm4 = 28, + debugserver_stmm5 = 29, + debugserver_stmm6 = 30, + debugserver_stmm7 = 31, + debugserver_fctrl = 32, debugserver_fcw = debugserver_fctrl, + debugserver_fstat = 33, debugserver_fsw = debugserver_fstat, + debugserver_ftag = 34, debugserver_ftw = debugserver_ftag, + debugserver_fiseg = 35, debugserver_fpu_cs = debugserver_fiseg, + debugserver_fioff = 36, debugserver_ip = debugserver_fioff, + debugserver_foseg = 37, debugserver_fpu_ds = debugserver_foseg, + debugserver_fooff = 38, debugserver_dp = debugserver_fooff, + debugserver_fop = 39, + debugserver_xmm0 = 40, + debugserver_xmm1 = 41, + debugserver_xmm2 = 42, + debugserver_xmm3 = 43, + debugserver_xmm4 = 44, + debugserver_xmm5 = 45, + debugserver_xmm6 = 46, + debugserver_xmm7 = 47, + debugserver_xmm8 = 48, + debugserver_xmm9 = 49, + debugserver_xmm10 = 50, + debugserver_xmm11 = 51, + debugserver_xmm12 = 52, + debugserver_xmm13 = 53, + debugserver_xmm14 = 54, + debugserver_xmm15 = 55, + debugserver_mxcsr = 56, + debugserver_ymm0 = debugserver_xmm0, + debugserver_ymm1 = debugserver_xmm1, + debugserver_ymm2 = debugserver_xmm2, + debugserver_ymm3 = debugserver_xmm3, + debugserver_ymm4 = debugserver_xmm4, + debugserver_ymm5 = debugserver_xmm5, + debugserver_ymm6 = debugserver_xmm6, + debugserver_ymm7 = debugserver_xmm7, + debugserver_ymm8 = debugserver_xmm8, + debugserver_ymm9 = debugserver_xmm9, + debugserver_ymm10 = debugserver_xmm10, + debugserver_ymm11 = debugserver_xmm11, + debugserver_ymm12 = debugserver_xmm12, + debugserver_ymm13 = debugserver_xmm13, + debugserver_ymm14 = debugserver_xmm14, + debugserver_ymm15 = debugserver_xmm15 +}; + +#define GPR_OFFSET(reg) (offsetof (DNBArchImplX86_64::GPR, __##reg)) +#define FPU_OFFSET(reg) (offsetof (DNBArchImplX86_64::FPU, __fpu_##reg) + offsetof (DNBArchImplX86_64::Context, fpu.no_avx)) +#define AVX_OFFSET(reg) (offsetof (DNBArchImplX86_64::AVX, __fpu_##reg) + offsetof (DNBArchImplX86_64::Context, fpu.avx)) +#define EXC_OFFSET(reg) (offsetof (DNBArchImplX86_64::EXC, __##reg) + offsetof (DNBArchImplX86_64::Context, exc)) +#define AVX_OFFSET_YMM(n) (AVX_OFFSET(ymmh0) + (32 * n)) + +#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 FPU_SIZE_YMM(reg) (32) +#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), ehframe_dwarf_##reg, ehframe_dwarf_##reg, INVALID_NUB_REGNUM, debugserver_##reg, NULL, g_invalidate_##reg } +#define DEFINE_GPR_ALT(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), ehframe_dwarf_##reg, ehframe_dwarf_##reg, gen, debugserver_##reg, NULL, g_invalidate_##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, debugserver_##reg, NULL, NULL } +#define DEFINE_GPR_ALT3(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, gen, debugserver_##reg, NULL, NULL } +#define DEFINE_GPR_ALT4(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), ehframe_dwarf_##reg, ehframe_dwarf_##reg, gen, debugserver_##reg, NULL, NULL } + +#define DEFINE_GPR_PSEUDO_32(reg32,reg64) { e_regSetGPR, gpr_##reg32, #reg32, NULL, Uint, Hex, 4, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 } +#define DEFINE_GPR_PSEUDO_16(reg16,reg64) { e_regSetGPR, gpr_##reg16, #reg16, NULL, Uint, Hex, 2, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 } +#define DEFINE_GPR_PSEUDO_8H(reg8,reg64) { e_regSetGPR, gpr_##reg8 , #reg8 , NULL, Uint, Hex, 1, 1,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 } +#define DEFINE_GPR_PSEUDO_8L(reg8,reg64) { e_regSetGPR, gpr_##reg8 , #reg8 , NULL, Uint, Hex, 1, 0,INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 } + +// General purpose registers for 64 bit + +const char *g_contained_rax[] = { "rax", NULL }; +const char *g_contained_rbx[] = { "rbx", NULL }; +const char *g_contained_rcx[] = { "rcx", NULL }; +const char *g_contained_rdx[] = { "rdx", NULL }; +const char *g_contained_rdi[] = { "rdi", NULL }; +const char *g_contained_rsi[] = { "rsi", NULL }; +const char *g_contained_rbp[] = { "rbp", NULL }; +const char *g_contained_rsp[] = { "rsp", NULL }; +const char *g_contained_r8[] = { "r8", NULL }; +const char *g_contained_r9[] = { "r9", NULL }; +const char *g_contained_r10[] = { "r10", NULL }; +const char *g_contained_r11[] = { "r11", NULL }; +const char *g_contained_r12[] = { "r12", NULL }; +const char *g_contained_r13[] = { "r13", NULL }; +const char *g_contained_r14[] = { "r14", NULL }; +const char *g_contained_r15[] = { "r15", NULL }; + +const char *g_invalidate_rax[] = { "rax", "eax", "ax", "ah", "al", NULL }; +const char *g_invalidate_rbx[] = { "rbx", "ebx", "bx", "bh", "bl", NULL }; +const char *g_invalidate_rcx[] = { "rcx", "ecx", "cx", "ch", "cl", NULL }; +const char *g_invalidate_rdx[] = { "rdx", "edx", "dx", "dh", "dl", NULL }; +const char *g_invalidate_rdi[] = { "rdi", "edi", "di", "dil", NULL }; +const char *g_invalidate_rsi[] = { "rsi", "esi", "si", "sil", NULL }; +const char *g_invalidate_rbp[] = { "rbp", "ebp", "bp", "bpl", NULL }; +const char *g_invalidate_rsp[] = { "rsp", "esp", "sp", "spl", NULL }; +const char *g_invalidate_r8 [] = { "r8", "r8d", "r8w", "r8l", NULL }; +const char *g_invalidate_r9 [] = { "r9", "r9d", "r9w", "r9l", NULL }; +const char *g_invalidate_r10[] = { "r10", "r10d", "r10w", "r10l", NULL }; +const char *g_invalidate_r11[] = { "r11", "r11d", "r11w", "r11l", NULL }; +const char *g_invalidate_r12[] = { "r12", "r12d", "r12w", "r12l", NULL }; +const char *g_invalidate_r13[] = { "r13", "r13d", "r13w", "r13l", NULL }; +const char *g_invalidate_r14[] = { "r14", "r14d", "r14w", "r14l", NULL }; +const char *g_invalidate_r15[] = { "r15", "r15d", "r15w", "r15l", NULL }; + +const DNBRegisterInfo +DNBArchImplX86_64::g_gpr_registers[] = +{ + DEFINE_GPR (rax), + DEFINE_GPR (rbx), + DEFINE_GPR_ALT (rcx , "arg4", GENERIC_REGNUM_ARG4), + DEFINE_GPR_ALT (rdx , "arg3", GENERIC_REGNUM_ARG3), + DEFINE_GPR_ALT (rdi , "arg1", GENERIC_REGNUM_ARG1), + DEFINE_GPR_ALT (rsi , "arg2", GENERIC_REGNUM_ARG2), + DEFINE_GPR_ALT (rbp , "fp" , GENERIC_REGNUM_FP), + DEFINE_GPR_ALT (rsp , "sp" , GENERIC_REGNUM_SP), + DEFINE_GPR_ALT (r8 , "arg5", GENERIC_REGNUM_ARG5), + DEFINE_GPR_ALT (r9 , "arg6", GENERIC_REGNUM_ARG6), + DEFINE_GPR (r10), + DEFINE_GPR (r11), + DEFINE_GPR (r12), + DEFINE_GPR (r13), + DEFINE_GPR (r14), + DEFINE_GPR (r15), + DEFINE_GPR_ALT4 (rip , "pc", GENERIC_REGNUM_PC), + DEFINE_GPR_ALT3 (rflags, "flags", GENERIC_REGNUM_FLAGS), + DEFINE_GPR_ALT2 (cs, NULL), + DEFINE_GPR_ALT2 (fs, NULL), + DEFINE_GPR_ALT2 (gs, NULL), + DEFINE_GPR_PSEUDO_32 (eax, rax), + DEFINE_GPR_PSEUDO_32 (ebx, rbx), + DEFINE_GPR_PSEUDO_32 (ecx, rcx), + DEFINE_GPR_PSEUDO_32 (edx, rdx), + DEFINE_GPR_PSEUDO_32 (edi, rdi), + DEFINE_GPR_PSEUDO_32 (esi, rsi), + DEFINE_GPR_PSEUDO_32 (ebp, rbp), + DEFINE_GPR_PSEUDO_32 (esp, rsp), + DEFINE_GPR_PSEUDO_32 (r8d, r8), + DEFINE_GPR_PSEUDO_32 (r9d, r9), + DEFINE_GPR_PSEUDO_32 (r10d, r10), + DEFINE_GPR_PSEUDO_32 (r11d, r11), + DEFINE_GPR_PSEUDO_32 (r12d, r12), + DEFINE_GPR_PSEUDO_32 (r13d, r13), + DEFINE_GPR_PSEUDO_32 (r14d, r14), + DEFINE_GPR_PSEUDO_32 (r15d, r15), + DEFINE_GPR_PSEUDO_16 (ax , rax), + DEFINE_GPR_PSEUDO_16 (bx , rbx), + DEFINE_GPR_PSEUDO_16 (cx , rcx), + DEFINE_GPR_PSEUDO_16 (dx , rdx), + DEFINE_GPR_PSEUDO_16 (di , rdi), + DEFINE_GPR_PSEUDO_16 (si , rsi), + DEFINE_GPR_PSEUDO_16 (bp , rbp), + DEFINE_GPR_PSEUDO_16 (sp , rsp), + DEFINE_GPR_PSEUDO_16 (r8w, r8), + DEFINE_GPR_PSEUDO_16 (r9w, r9), + DEFINE_GPR_PSEUDO_16 (r10w, r10), + DEFINE_GPR_PSEUDO_16 (r11w, r11), + DEFINE_GPR_PSEUDO_16 (r12w, r12), + DEFINE_GPR_PSEUDO_16 (r13w, r13), + DEFINE_GPR_PSEUDO_16 (r14w, r14), + DEFINE_GPR_PSEUDO_16 (r15w, r15), + DEFINE_GPR_PSEUDO_8H (ah , rax), + DEFINE_GPR_PSEUDO_8H (bh , rbx), + DEFINE_GPR_PSEUDO_8H (ch , rcx), + DEFINE_GPR_PSEUDO_8H (dh , rdx), + DEFINE_GPR_PSEUDO_8L (al , rax), + DEFINE_GPR_PSEUDO_8L (bl , rbx), + DEFINE_GPR_PSEUDO_8L (cl , rcx), + DEFINE_GPR_PSEUDO_8L (dl , rdx), + DEFINE_GPR_PSEUDO_8L (dil, rdi), + DEFINE_GPR_PSEUDO_8L (sil, rsi), + DEFINE_GPR_PSEUDO_8L (bpl, rbp), + DEFINE_GPR_PSEUDO_8L (spl, rsp), + DEFINE_GPR_PSEUDO_8L (r8l, r8), + DEFINE_GPR_PSEUDO_8L (r9l, r9), + DEFINE_GPR_PSEUDO_8L (r10l, r10), + DEFINE_GPR_PSEUDO_8L (r11l, r11), + DEFINE_GPR_PSEUDO_8L (r12l, r12), + DEFINE_GPR_PSEUDO_8L (r13l, r13), + DEFINE_GPR_PSEUDO_8L (r14l, r14), + DEFINE_GPR_PSEUDO_8L (r15l, r15) +}; + +// Floating point registers 64 bit +const DNBRegisterInfo +DNBArchImplX86_64::g_fpu_registers_no_avx[] = +{ + { e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , FPU_OFFSET(fcw) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , FPU_OFFSET(fsw) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , FPU_OFFSET(ftw) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , FPU_OFFSET(fop) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , FPU_OFFSET(ip) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , FPU_OFFSET(cs) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , FPU_OFFSET(dp) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , FPU_OFFSET(ds) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , FPU_OFFSET(mxcsr) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , FPU_OFFSET(mxcsrmask) , -1U, -1U, -1U, -1U, NULL, NULL }, + + { e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), ehframe_dwarf_stmm0, ehframe_dwarf_stmm0, -1U, debugserver_stmm0, NULL, NULL }, + { e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), ehframe_dwarf_stmm1, ehframe_dwarf_stmm1, -1U, debugserver_stmm1, NULL, NULL }, + { e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), ehframe_dwarf_stmm2, ehframe_dwarf_stmm2, -1U, debugserver_stmm2, NULL, NULL }, + { e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), ehframe_dwarf_stmm3, ehframe_dwarf_stmm3, -1U, debugserver_stmm3, NULL, NULL }, + { e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), ehframe_dwarf_stmm4, ehframe_dwarf_stmm4, -1U, debugserver_stmm4, NULL, NULL }, + { e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), ehframe_dwarf_stmm5, ehframe_dwarf_stmm5, -1U, debugserver_stmm5, NULL, NULL }, + { e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), ehframe_dwarf_stmm6, ehframe_dwarf_stmm6, -1U, debugserver_stmm6, NULL, NULL }, + { e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), ehframe_dwarf_stmm7, ehframe_dwarf_stmm7, -1U, debugserver_stmm7, NULL, NULL }, + + { e_regSetFPU, fpu_xmm0 , "xmm0" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0) , FPU_OFFSET(xmm0) , ehframe_dwarf_xmm0 , ehframe_dwarf_xmm0 , -1U, debugserver_xmm0 , NULL, NULL }, + { e_regSetFPU, fpu_xmm1 , "xmm1" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1) , FPU_OFFSET(xmm1) , ehframe_dwarf_xmm1 , ehframe_dwarf_xmm1 , -1U, debugserver_xmm1 , NULL, NULL }, + { e_regSetFPU, fpu_xmm2 , "xmm2" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2) , FPU_OFFSET(xmm2) , ehframe_dwarf_xmm2 , ehframe_dwarf_xmm2 , -1U, debugserver_xmm2 , NULL, NULL }, + { e_regSetFPU, fpu_xmm3 , "xmm3" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3) , FPU_OFFSET(xmm3) , ehframe_dwarf_xmm3 , ehframe_dwarf_xmm3 , -1U, debugserver_xmm3 , NULL, NULL }, + { e_regSetFPU, fpu_xmm4 , "xmm4" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4) , FPU_OFFSET(xmm4) , ehframe_dwarf_xmm4 , ehframe_dwarf_xmm4 , -1U, debugserver_xmm4 , NULL, NULL }, + { e_regSetFPU, fpu_xmm5 , "xmm5" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5) , FPU_OFFSET(xmm5) , ehframe_dwarf_xmm5 , ehframe_dwarf_xmm5 , -1U, debugserver_xmm5 , NULL, NULL }, + { e_regSetFPU, fpu_xmm6 , "xmm6" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6) , FPU_OFFSET(xmm6) , ehframe_dwarf_xmm6 , ehframe_dwarf_xmm6 , -1U, debugserver_xmm6 , NULL, NULL }, + { e_regSetFPU, fpu_xmm7 , "xmm7" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7) , FPU_OFFSET(xmm7) , ehframe_dwarf_xmm7 , ehframe_dwarf_xmm7 , -1U, debugserver_xmm7 , NULL, NULL }, + { e_regSetFPU, fpu_xmm8 , "xmm8" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm8) , FPU_OFFSET(xmm8) , ehframe_dwarf_xmm8 , ehframe_dwarf_xmm8 , -1U, debugserver_xmm8 , NULL, NULL }, + { e_regSetFPU, fpu_xmm9 , "xmm9" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm9) , FPU_OFFSET(xmm9) , ehframe_dwarf_xmm9 , ehframe_dwarf_xmm9 , -1U, debugserver_xmm9 , NULL, NULL }, + { e_regSetFPU, fpu_xmm10, "xmm10" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm10) , FPU_OFFSET(xmm10), ehframe_dwarf_xmm10, ehframe_dwarf_xmm10, -1U, debugserver_xmm10, NULL, NULL }, + { e_regSetFPU, fpu_xmm11, "xmm11" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm11) , FPU_OFFSET(xmm11), ehframe_dwarf_xmm11, ehframe_dwarf_xmm11, -1U, debugserver_xmm11, NULL, NULL }, + { e_regSetFPU, fpu_xmm12, "xmm12" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm12) , FPU_OFFSET(xmm12), ehframe_dwarf_xmm12, ehframe_dwarf_xmm12, -1U, debugserver_xmm12, NULL, NULL }, + { e_regSetFPU, fpu_xmm13, "xmm13" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm13) , FPU_OFFSET(xmm13), ehframe_dwarf_xmm13, ehframe_dwarf_xmm13, -1U, debugserver_xmm13, NULL, NULL }, + { e_regSetFPU, fpu_xmm14, "xmm14" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm14) , FPU_OFFSET(xmm14), ehframe_dwarf_xmm14, ehframe_dwarf_xmm14, -1U, debugserver_xmm14, NULL, NULL }, + { e_regSetFPU, fpu_xmm15, "xmm15" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm15) , FPU_OFFSET(xmm15), ehframe_dwarf_xmm15, ehframe_dwarf_xmm15, -1U, debugserver_xmm15, NULL, NULL }, +}; + +static const char *g_contained_ymm0 [] = { "ymm0", NULL }; +static const char *g_contained_ymm1 [] = { "ymm1", NULL }; +static const char *g_contained_ymm2 [] = { "ymm2", NULL }; +static const char *g_contained_ymm3 [] = { "ymm3", NULL }; +static const char *g_contained_ymm4 [] = { "ymm4", NULL }; +static const char *g_contained_ymm5 [] = { "ymm5", NULL }; +static const char *g_contained_ymm6 [] = { "ymm6", NULL }; +static const char *g_contained_ymm7 [] = { "ymm7", NULL }; +static const char *g_contained_ymm8 [] = { "ymm8", NULL }; +static const char *g_contained_ymm9 [] = { "ymm9", NULL }; +static const char *g_contained_ymm10[] = { "ymm10", NULL }; +static const char *g_contained_ymm11[] = { "ymm11", NULL }; +static const char *g_contained_ymm12[] = { "ymm12", NULL }; +static const char *g_contained_ymm13[] = { "ymm13", NULL }; +static const char *g_contained_ymm14[] = { "ymm14", NULL }; +static const char *g_contained_ymm15[] = { "ymm15", NULL }; + +const DNBRegisterInfo +DNBArchImplX86_64::g_fpu_registers_avx[] = +{ + { e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , AVX_OFFSET(fcw) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , AVX_OFFSET(fsw) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , AVX_OFFSET(ftw) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , AVX_OFFSET(fop) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , AVX_OFFSET(ip) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , AVX_OFFSET(cs) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , AVX_OFFSET(dp) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , AVX_OFFSET(ds) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , AVX_OFFSET(mxcsr) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , AVX_OFFSET(mxcsrmask) , -1U, -1U, -1U, -1U, NULL, NULL }, + + { e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), AVX_OFFSET(stmm0), ehframe_dwarf_stmm0, ehframe_dwarf_stmm0, -1U, debugserver_stmm0, NULL, NULL }, + { e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), AVX_OFFSET(stmm1), ehframe_dwarf_stmm1, ehframe_dwarf_stmm1, -1U, debugserver_stmm1, NULL, NULL }, + { e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), AVX_OFFSET(stmm2), ehframe_dwarf_stmm2, ehframe_dwarf_stmm2, -1U, debugserver_stmm2, NULL, NULL }, + { e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), AVX_OFFSET(stmm3), ehframe_dwarf_stmm3, ehframe_dwarf_stmm3, -1U, debugserver_stmm3, NULL, NULL }, + { e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), AVX_OFFSET(stmm4), ehframe_dwarf_stmm4, ehframe_dwarf_stmm4, -1U, debugserver_stmm4, NULL, NULL }, + { e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), AVX_OFFSET(stmm5), ehframe_dwarf_stmm5, ehframe_dwarf_stmm5, -1U, debugserver_stmm5, NULL, NULL }, + { e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), AVX_OFFSET(stmm6), ehframe_dwarf_stmm6, ehframe_dwarf_stmm6, -1U, debugserver_stmm6, NULL, NULL }, + { e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), AVX_OFFSET(stmm7), ehframe_dwarf_stmm7, ehframe_dwarf_stmm7, -1U, debugserver_stmm7, NULL, NULL }, + + { e_regSetFPU, fpu_ymm0 , "ymm0" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm0) , AVX_OFFSET_YMM(0) , ehframe_dwarf_ymm0 , ehframe_dwarf_ymm0 , -1U, debugserver_ymm0, NULL, NULL }, + { e_regSetFPU, fpu_ymm1 , "ymm1" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm1) , AVX_OFFSET_YMM(1) , ehframe_dwarf_ymm1 , ehframe_dwarf_ymm1 , -1U, debugserver_ymm1, NULL, NULL }, + { e_regSetFPU, fpu_ymm2 , "ymm2" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm2) , AVX_OFFSET_YMM(2) , ehframe_dwarf_ymm2 , ehframe_dwarf_ymm2 , -1U, debugserver_ymm2, NULL, NULL }, + { e_regSetFPU, fpu_ymm3 , "ymm3" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm3) , AVX_OFFSET_YMM(3) , ehframe_dwarf_ymm3 , ehframe_dwarf_ymm3 , -1U, debugserver_ymm3, NULL, NULL }, + { e_regSetFPU, fpu_ymm4 , "ymm4" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm4) , AVX_OFFSET_YMM(4) , ehframe_dwarf_ymm4 , ehframe_dwarf_ymm4 , -1U, debugserver_ymm4, NULL, NULL }, + { e_regSetFPU, fpu_ymm5 , "ymm5" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm5) , AVX_OFFSET_YMM(5) , ehframe_dwarf_ymm5 , ehframe_dwarf_ymm5 , -1U, debugserver_ymm5, NULL, NULL }, + { e_regSetFPU, fpu_ymm6 , "ymm6" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm6) , AVX_OFFSET_YMM(6) , ehframe_dwarf_ymm6 , ehframe_dwarf_ymm6 , -1U, debugserver_ymm6, NULL, NULL }, + { e_regSetFPU, fpu_ymm7 , "ymm7" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm7) , AVX_OFFSET_YMM(7) , ehframe_dwarf_ymm7 , ehframe_dwarf_ymm7 , -1U, debugserver_ymm7, NULL, NULL }, + { e_regSetFPU, fpu_ymm8 , "ymm8" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm8) , AVX_OFFSET_YMM(8) , ehframe_dwarf_ymm8 , ehframe_dwarf_ymm8 , -1U, debugserver_ymm8 , NULL, NULL }, + { e_regSetFPU, fpu_ymm9 , "ymm9" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm9) , AVX_OFFSET_YMM(9) , ehframe_dwarf_ymm9 , ehframe_dwarf_ymm9 , -1U, debugserver_ymm9 , NULL, NULL }, + { e_regSetFPU, fpu_ymm10, "ymm10" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm10) , AVX_OFFSET_YMM(10), ehframe_dwarf_ymm10, ehframe_dwarf_ymm10, -1U, debugserver_ymm10, NULL, NULL }, + { e_regSetFPU, fpu_ymm11, "ymm11" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm11) , AVX_OFFSET_YMM(11), ehframe_dwarf_ymm11, ehframe_dwarf_ymm11, -1U, debugserver_ymm11, NULL, NULL }, + { e_regSetFPU, fpu_ymm12, "ymm12" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm12) , AVX_OFFSET_YMM(12), ehframe_dwarf_ymm12, ehframe_dwarf_ymm12, -1U, debugserver_ymm12, NULL, NULL }, + { e_regSetFPU, fpu_ymm13, "ymm13" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm13) , AVX_OFFSET_YMM(13), ehframe_dwarf_ymm13, ehframe_dwarf_ymm13, -1U, debugserver_ymm13, NULL, NULL }, + { e_regSetFPU, fpu_ymm14, "ymm14" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm14) , AVX_OFFSET_YMM(14), ehframe_dwarf_ymm14, ehframe_dwarf_ymm14, -1U, debugserver_ymm14, NULL, NULL }, + { e_regSetFPU, fpu_ymm15, "ymm15" , NULL, Vector, VectorOfUInt8, FPU_SIZE_YMM(ymm15) , AVX_OFFSET_YMM(15), ehframe_dwarf_ymm15, ehframe_dwarf_ymm15, -1U, debugserver_ymm15, NULL, NULL }, + + { e_regSetFPU, fpu_xmm0 , "xmm0" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0) , 0, ehframe_dwarf_xmm0 , ehframe_dwarf_xmm0 , -1U, debugserver_xmm0 , g_contained_ymm0 , NULL }, + { e_regSetFPU, fpu_xmm1 , "xmm1" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1) , 0, ehframe_dwarf_xmm1 , ehframe_dwarf_xmm1 , -1U, debugserver_xmm1 , g_contained_ymm1 , NULL }, + { e_regSetFPU, fpu_xmm2 , "xmm2" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2) , 0, ehframe_dwarf_xmm2 , ehframe_dwarf_xmm2 , -1U, debugserver_xmm2 , g_contained_ymm2 , NULL }, + { e_regSetFPU, fpu_xmm3 , "xmm3" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3) , 0, ehframe_dwarf_xmm3 , ehframe_dwarf_xmm3 , -1U, debugserver_xmm3 , g_contained_ymm3 , NULL }, + { e_regSetFPU, fpu_xmm4 , "xmm4" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4) , 0, ehframe_dwarf_xmm4 , ehframe_dwarf_xmm4 , -1U, debugserver_xmm4 , g_contained_ymm4 , NULL }, + { e_regSetFPU, fpu_xmm5 , "xmm5" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5) , 0, ehframe_dwarf_xmm5 , ehframe_dwarf_xmm5 , -1U, debugserver_xmm5 , g_contained_ymm5 , NULL }, + { e_regSetFPU, fpu_xmm6 , "xmm6" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6) , 0, ehframe_dwarf_xmm6 , ehframe_dwarf_xmm6 , -1U, debugserver_xmm6 , g_contained_ymm6 , NULL }, + { e_regSetFPU, fpu_xmm7 , "xmm7" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7) , 0, ehframe_dwarf_xmm7 , ehframe_dwarf_xmm7 , -1U, debugserver_xmm7 , g_contained_ymm7 , NULL }, + { e_regSetFPU, fpu_xmm8 , "xmm8" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm8) , 0, ehframe_dwarf_xmm8 , ehframe_dwarf_xmm8 , -1U, debugserver_xmm8 , g_contained_ymm8 , NULL }, + { e_regSetFPU, fpu_xmm9 , "xmm9" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm9) , 0, ehframe_dwarf_xmm9 , ehframe_dwarf_xmm9 , -1U, debugserver_xmm9 , g_contained_ymm9 , NULL }, + { e_regSetFPU, fpu_xmm10, "xmm10" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm10) , 0, ehframe_dwarf_xmm10, ehframe_dwarf_xmm10, -1U, debugserver_xmm10, g_contained_ymm10, NULL }, + { e_regSetFPU, fpu_xmm11, "xmm11" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm11) , 0, ehframe_dwarf_xmm11, ehframe_dwarf_xmm11, -1U, debugserver_xmm11, g_contained_ymm11, NULL }, + { e_regSetFPU, fpu_xmm12, "xmm12" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm12) , 0, ehframe_dwarf_xmm12, ehframe_dwarf_xmm12, -1U, debugserver_xmm12, g_contained_ymm12, NULL }, + { e_regSetFPU, fpu_xmm13, "xmm13" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm13) , 0, ehframe_dwarf_xmm13, ehframe_dwarf_xmm13, -1U, debugserver_xmm13, g_contained_ymm13, NULL }, + { e_regSetFPU, fpu_xmm14, "xmm14" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm14) , 0, ehframe_dwarf_xmm14, ehframe_dwarf_xmm14, -1U, debugserver_xmm14, g_contained_ymm14, NULL }, + { e_regSetFPU, fpu_xmm15, "xmm15" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm15) , 0, ehframe_dwarf_xmm15, ehframe_dwarf_xmm15, -1U, debugserver_xmm15, g_contained_ymm15, NULL } + + +}; + +// Exception registers + +const DNBRegisterInfo +DNBArchImplX86_64::g_exc_registers[] = +{ + { e_regSetEXC, exc_trapno, "trapno" , NULL, Uint, Hex, EXC_SIZE (trapno) , EXC_OFFSET (trapno) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetEXC, exc_err, "err" , NULL, Uint, Hex, EXC_SIZE (err) , EXC_OFFSET (err) , -1U, -1U, -1U, -1U, NULL, NULL }, + { e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex, EXC_SIZE (faultvaddr), EXC_OFFSET (faultvaddr) , -1U, -1U, -1U, -1U, NULL, NULL } +}; + +// 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_no_avx = sizeof(g_fpu_registers_no_avx)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplX86_64::k_num_fpu_registers_avx = sizeof(g_fpu_registers_avx)/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_no_avx = k_num_gpr_registers + k_num_fpu_registers_no_avx + k_num_exc_registers; +const size_t DNBArchImplX86_64::k_num_all_registers_avx = k_num_gpr_registers + k_num_fpu_registers_avx + 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_no_avx[] = +{ + { "x86_64 Registers", NULL, k_num_all_registers_no_avx }, + { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers }, + { "Floating Point Registers", g_fpu_registers_no_avx, k_num_fpu_registers_no_avx }, + { "Exception State Registers", g_exc_registers, k_num_exc_registers } +}; + +const DNBRegisterSetInfo +DNBArchImplX86_64::g_reg_sets_avx[] = +{ + { "x86_64 Registers", NULL, k_num_all_registers_avx }, + { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers }, + { "Floating Point Registers", g_fpu_registers_avx, k_num_fpu_registers_avx }, + { "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_avx)/sizeof(DNBRegisterSetInfo); + + +DNBArchProtocol * +DNBArchImplX86_64::Create (MachThread *thread) +{ + DNBArchImplX86_64 *obj = new DNBArchImplX86_64 (thread); + return obj; +} + +const uint8_t * +DNBArchImplX86_64::SoftwareBreakpointOpcode (nub_size_t byte_size) +{ + static const uint8_t g_breakpoint_opcode[] = { 0xCC }; + if (byte_size == 1) + return g_breakpoint_opcode; + return NULL; +} + +const DNBRegisterSetInfo * +DNBArchImplX86_64::GetRegisterSetInfo(nub_size_t *num_reg_sets) +{ + *num_reg_sets = k_num_register_sets; + + if (CPUHasAVX() || FORCE_AVX_REGS) + return g_reg_sets_avx; + else + return g_reg_sets_no_avx; +} + +void +DNBArchImplX86_64::Initialize() +{ + DNBArchPluginInfo arch_plugin_info = + { + CPU_TYPE_X86_64, + DNBArchImplX86_64::Create, + DNBArchImplX86_64::GetRegisterSetInfo, + DNBArchImplX86_64::SoftwareBreakpointOpcode + }; + + // Register this arch plug-in with the main protocol class + DNBArchProtocol::RegisterArchPlugin (arch_plugin_info); +} + +bool +DNBArchImplX86_64::GetRegisterValue(uint32_t set, uint32_t 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: + if (CPUHasAVX() || FORCE_AVX_REGS) + { + switch (reg) + { + case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fcw)); return true; + case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fsw)); return true; + case fpu_ftw: value->value.uint8 = m_state.context.fpu.avx.__fpu_ftw; return true; + case fpu_fop: value->value.uint16 = m_state.context.fpu.avx.__fpu_fop; return true; + case fpu_ip: value->value.uint32 = m_state.context.fpu.avx.__fpu_ip; return true; + case fpu_cs: value->value.uint16 = m_state.context.fpu.avx.__fpu_cs; return true; + case fpu_dp: value->value.uint32 = m_state.context.fpu.avx.__fpu_dp; return true; + case fpu_ds: value->value.uint16 = m_state.context.fpu.avx.__fpu_ds; return true; + case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.avx.__fpu_mxcsr; return true; + case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.avx.__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.avx.__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.avx.__fpu_xmm0 + (reg - fpu_xmm0), 16); + return true; + + case fpu_ymm0: + case fpu_ymm1: + case fpu_ymm2: + case fpu_ymm3: + case fpu_ymm4: + case fpu_ymm5: + case fpu_ymm6: + case fpu_ymm7: + case fpu_ymm8: + case fpu_ymm9: + case fpu_ymm10: + case fpu_ymm11: + case fpu_ymm12: + case fpu_ymm13: + case fpu_ymm14: + case fpu_ymm15: + memcpy(&value->value.uint8, &m_state.context.fpu.avx.__fpu_xmm0 + (reg - fpu_ymm0), 16); + memcpy((&value->value.uint8) + 16, &m_state.context.fpu.avx.__fpu_ymmh0 + (reg - fpu_ymm0), 16); + return true; + } + } + else + { + switch (reg) + { + case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)); return true; + case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)); return true; + case fpu_ftw: value->value.uint8 = m_state.context.fpu.no_avx.__fpu_ftw; return true; + case fpu_fop: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_fop; return true; + case fpu_ip: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_ip; return true; + case fpu_cs: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_cs; return true; + case fpu_dp: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_dp; return true; + case fpu_ds: value->value.uint16 = m_state.context.fpu.no_avx.__fpu_ds; return true; + case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsr; return true; + case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.no_avx.__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.no_avx.__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.no_avx.__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(uint32_t set, uint32_t 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: + if (CPUHasAVX() || FORCE_AVX_REGS) + { + switch (reg) + { + case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fcw)) = value->value.uint16; success = true; break; + case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.avx.__fpu_fsw)) = value->value.uint16; success = true; break; + case fpu_ftw: m_state.context.fpu.avx.__fpu_ftw = value->value.uint8; success = true; break; + case fpu_fop: m_state.context.fpu.avx.__fpu_fop = value->value.uint16; success = true; break; + case fpu_ip: m_state.context.fpu.avx.__fpu_ip = value->value.uint32; success = true; break; + case fpu_cs: m_state.context.fpu.avx.__fpu_cs = value->value.uint16; success = true; break; + case fpu_dp: m_state.context.fpu.avx.__fpu_dp = value->value.uint32; success = true; break; + case fpu_ds: m_state.context.fpu.avx.__fpu_ds = value->value.uint16; success = true; break; + case fpu_mxcsr: m_state.context.fpu.avx.__fpu_mxcsr = value->value.uint32; success = true; break; + case fpu_mxcsrmask: m_state.context.fpu.avx.__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.avx.__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.avx.__fpu_xmm0 + (reg - fpu_xmm0), &value->value.uint8, 16); + success = true; + break; + + case fpu_ymm0: + case fpu_ymm1: + case fpu_ymm2: + case fpu_ymm3: + case fpu_ymm4: + case fpu_ymm5: + case fpu_ymm6: + case fpu_ymm7: + case fpu_ymm8: + case fpu_ymm9: + case fpu_ymm10: + case fpu_ymm11: + case fpu_ymm12: + case fpu_ymm13: + case fpu_ymm14: + case fpu_ymm15: + memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + (reg - fpu_ymm0), &value->value.uint8, 16); + memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + (reg - fpu_ymm0), (&value->value.uint8) + 16, 16); + return true; + } + } + else + { + switch (reg) + { + case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)) = value->value.uint16; success = true; break; + case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)) = value->value.uint16; success = true; break; + case fpu_ftw: m_state.context.fpu.no_avx.__fpu_ftw = value->value.uint8; success = true; break; + case fpu_fop: m_state.context.fpu.no_avx.__fpu_fop = value->value.uint16; success = true; break; + case fpu_ip: m_state.context.fpu.no_avx.__fpu_ip = value->value.uint32; success = true; break; + case fpu_cs: m_state.context.fpu.no_avx.__fpu_cs = value->value.uint16; success = true; break; + case fpu_dp: m_state.context.fpu.no_avx.__fpu_dp = value->value.uint32; success = true; break; + case fpu_ds: m_state.context.fpu.no_avx.__fpu_ds = value->value.uint16; success = true; break; + case fpu_mxcsr: m_state.context.fpu.no_avx.__fpu_mxcsr = value->value.uint32; success = true; break; + case fpu_mxcsrmask: m_state.context.fpu.no_avx.__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.no_avx.__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.no_avx.__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; +} + +uint32_t +DNBArchImplX86_64::GetRegisterContextSize() +{ + static uint32_t g_cached_size = 0; + if (g_cached_size == 0) + { + if (CPUHasAVX() || FORCE_AVX_REGS) + { + for (size_t i=0; i %u", buf, (uint64_t)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) +{ + uint32_t size = GetRegisterContextSize(); + if (buf == NULL || buf_len == 0) + size = 0; + + if (size) + { + if (size > buf_len) + size = static_cast(buf_len); + + uint8_t *p = (uint8_t *)buf; + // Copy the GPR registers + memcpy(&m_state.context.gpr, p, sizeof(GPR)); + p += sizeof(GPR); + + if (CPUHasAVX() || FORCE_AVX_REGS) + { + // Walk around the gaps in the FPU regs + memcpy(&m_state.context.fpu.avx.__fpu_fcw, p, 5); + p += 5; + memcpy(&m_state.context.fpu.avx.__fpu_fop, p, 8); + p += 8; + memcpy(&m_state.context.fpu.avx.__fpu_dp, p, 6); + p += 6; + memcpy(&m_state.context.fpu.avx.__fpu_mxcsr, p, 8); + p += 8; + + // Work around the padding between the stmm registers as they are 16 + // byte structs with 10 bytes of the value in each + for (size_t i=0; i<8; ++i) + { + memcpy(&m_state.context.fpu.avx.__fpu_stmm0 + i, p, 10); + p += 10; + } + + // Interleave the XMM and YMMH registers to make the YMM registers + for (size_t i=0; i<16; ++i) + { + memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + i, p, 16); + p += 16; + memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + i, p, 16); + p += 16; + } + } + else + { + // Copy fcw through mxcsrmask as there is no padding + memcpy(&m_state.context.fpu.no_avx.__fpu_fcw, p, 5); + p += 5; + memcpy(&m_state.context.fpu.no_avx.__fpu_fop, p, 8); + p += 8; + memcpy(&m_state.context.fpu.no_avx.__fpu_dp, p, 6); + p += 6; + memcpy(&m_state.context.fpu.no_avx.__fpu_mxcsr, p, 8); + p += 8; + + // Work around the padding between the stmm registers as they are 16 + // byte structs with 10 bytes of the value in each + for (size_t i=0; i<8; ++i) + { + memcpy(&m_state.context.fpu.no_avx.__fpu_stmm0 + i, p, 10); + p += 10; + } + + // Copy the XMM registers in a single block + memcpy(&m_state.context.fpu.no_avx.__fpu_xmm0, p, 16 * 16); + p += 16 * 16; + } + + // Copy the exception registers + memcpy(&m_state.context.exc, p, sizeof(EXC)); + p += sizeof(EXC); + + // make sure we end up with exactly what we think we should have + size_t bytes_written = p - (uint8_t *)buf; + UNUSED_IF_ASSERT_DISABLED(bytes_written); + assert (bytes_written == size); + + kern_return_t kret; + if ((kret = SetGPRState()) != KERN_SUCCESS) + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) error: GPR regs failed to write: %u", buf, (uint64_t)buf_len, kret); + if ((kret = SetFPUState()) != KERN_SUCCESS) + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) error: %s regs failed to write: %u", buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret); + if ((kret = SetEXCState()) != KERN_SUCCESS) + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) error: EXP regs failed to write: %u", buf, (uint64_t)buf_len, kret); + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) => %llu", buf, (uint64_t)buf_len, (uint64_t)size); + return size; +} + +uint32_t +DNBArchImplX86_64::SaveRegisterState () +{ + kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber()); + DNBLogThreadedIf (LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u (SetGPRState() for stop_count = %u)", m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount()); + + // Always re-read the registers because above we call thread_abort_safely(); + bool force = true; + + if ((kret = GetGPRState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SaveRegisterState () error: GPR regs failed to read: %u ", kret); + } + else if ((kret = GetFPUState(force)) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SaveRegisterState () error: %s regs failed to read: %u", CPUHasAVX() ? "AVX" : "FPU", kret); + } + else + { + const uint32_t save_id = GetNextRegisterStateSaveID (); + m_saved_register_states[save_id] = m_state.context; + return save_id; + } + return 0; +} +bool +DNBArchImplX86_64::RestoreRegisterState (uint32_t save_id) +{ + SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id); + if (pos != m_saved_register_states.end()) + { + m_state.context.gpr = pos->second.gpr; + m_state.context.fpu = pos->second.fpu; + m_state.SetError(e_regSetGPR, Read, 0); + m_state.SetError(e_regSetFPU, Read, 0); + kern_return_t kret; + bool success = true; + if ((kret = SetGPRState()) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::RestoreRegisterState (save_id = %u) error: GPR regs failed to write: %u", save_id, kret); + success = false; + } + else if ((kret = SetFPUState()) != KERN_SUCCESS) + { + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::RestoreRegisterState (save_id = %u) error: %s regs failed to write: %u", save_id, CPUHasAVX() ? "AVX" : "FPU", kret); + success = false; + } + m_saved_register_states.erase(pos); + return success; + } + return false; +} + + +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__) || defined (__x86_64__) diff --git a/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h new file mode 100644 index 000000000000..20844951261b --- /dev/null +++ b/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h @@ -0,0 +1,260 @@ +//===-- 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__) || defined (__x86_64__) +#include "DNBArch.h" +#include "MachRegisterStatesX86_64.h" + +#include + +class MachThread; + +class DNBArchImplX86_64 : public DNBArchProtocol +{ +public: + DNBArchImplX86_64(MachThread *thread) : + DNBArchProtocol(), + m_thread(thread), + m_state(), + m_2pc_dbg_checkpoint(), + m_2pc_trans_state(Trans_Done), + m_saved_register_states() + { + } + virtual ~DNBArchImplX86_64() + { + } + + static void Initialize(); + + virtual bool GetRegisterValue(uint32_t set, uint32_t reg, DNBRegisterValue *value); + virtual bool SetRegisterValue(uint32_t set, uint32_t reg, const DNBRegisterValue *value); + 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 uint32_t SaveRegisterState (); + virtual bool RestoreRegisterState (uint32_t save_id); + + 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); + + virtual uint32_t NumSupportedHardwareWatchpoints(); + virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task); + virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index, bool also_set_on_task); + virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr); + +protected: + kern_return_t EnableHardwareSingleStep (bool enable); + + typedef __x86_64_thread_state_t GPR; + typedef __x86_64_float_state_t FPU; + typedef __x86_64_exception_state_t EXC; + typedef __x86_64_avx_state_t AVX; + typedef __x86_64_debug_state_t DBG; + + static const DNBRegisterInfo g_gpr_registers[]; + static const DNBRegisterInfo g_fpu_registers_no_avx[]; + static const DNBRegisterInfo g_fpu_registers_avx[]; + static const DNBRegisterInfo g_exc_registers[]; + static const DNBRegisterSetInfo g_reg_sets_no_avx[]; + static const DNBRegisterSetInfo g_reg_sets_avx[]; + static const size_t k_num_gpr_registers; + static const size_t k_num_fpu_registers_no_avx; + static const size_t k_num_fpu_registers_avx; + static const size_t k_num_exc_registers; + static const size_t k_num_all_registers_no_avx; + static const size_t k_num_all_registers_avx; + static const size_t k_num_register_sets; + + typedef enum RegisterSetTag + { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, + e_regSetFPU, + e_regSetEXC, + e_regSetDBG, + kNumRegisterSets + } RegisterSet; + + typedef enum RegisterSetWordSizeTag + { + e_regSetWordSizeGPR = sizeof(GPR) / sizeof(int), + e_regSetWordSizeFPU = sizeof(FPU) / sizeof(int), + e_regSetWordSizeEXC = sizeof(EXC) / sizeof(int), + e_regSetWordSizeAVX = sizeof(AVX) / sizeof(int), + e_regSetWordSizeDBG = sizeof(DBG) / sizeof(int) + } RegisterSetWordSize; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + struct Context + { + GPR gpr; + union { + FPU no_avx; + AVX avx; + } fpu; + EXC exc; + DBG dbg; + }; + + 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 + kern_return_t dbg_errs[2]; // Read/Write errors + + State() + { + uint32_t i; + for (i=0; i SaveRegisterStates; + SaveRegisterStates m_saved_register_states; +}; + +#endif // #if defined (__i386__) || defined (__x86_64__) +#endif // #ifndef __DNBArchImplX86_64_h__ diff --git a/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h b/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h new file mode 100644 index 000000000000..4e48e9645dd5 --- /dev/null +++ b/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h @@ -0,0 +1,210 @@ +//===-- MachRegisterStatesX86_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 Sean Callanan on 3/16/11. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachRegisterStatesX86_64_h__ +#define __MachRegisterStatesX86_64_h__ + +#include + +#define __x86_64_THREAD_STATE 4 +#define __x86_64_FLOAT_STATE 5 +#define __x86_64_EXCEPTION_STATE 6 +#define __x86_64_DEBUG_STATE 11 +#define __x86_64_AVX_STATE 17 + +typedef struct { + 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; +} __x86_64_thread_state_t; + +typedef struct { + uint16_t __invalid : 1; + uint16_t __denorm : 1; + uint16_t __zdiv : 1; + uint16_t __ovrfl : 1; + uint16_t __undfl : 1; + uint16_t __precis : 1; + uint16_t __PAD1 : 2; + uint16_t __pc : 2; + uint16_t __rc : 2; + uint16_t __PAD2 : 1; + uint16_t __PAD3 : 3; +} __x86_64_fp_control_t; + +typedef struct { + uint16_t __invalid : 1; + uint16_t __denorm : 1; + uint16_t __zdiv : 1; + uint16_t __ovrfl : 1; + uint16_t __undfl : 1; + uint16_t __precis : 1; + uint16_t __stkflt : 1; + uint16_t __errsumm : 1; + uint16_t __c0 : 1; + uint16_t __c1 : 1; + uint16_t __c2 : 1; + uint16_t __tos : 3; + uint16_t __c3 : 1; + uint16_t __busy : 1; +} __x86_64_fp_status_t; + +typedef struct { + uint8_t __mmst_reg[10]; + uint8_t __mmst_rsrv[6]; +} __x86_64_mmst_reg; + +typedef struct { + uint8_t __xmm_reg[16]; +} __x86_64_xmm_reg; + +typedef struct { + int32_t __fpu_reserved[2]; + __x86_64_fp_control_t __fpu_fcw; + __x86_64_fp_status_t __fpu_fsw; + uint8_t __fpu_ftw; + uint8_t __fpu_rsrv1; + uint16_t __fpu_fop; + uint32_t __fpu_ip; + uint16_t __fpu_cs; + uint16_t __fpu_rsrv2; + uint32_t __fpu_dp; + uint16_t __fpu_ds; + uint16_t __fpu_rsrv3; + uint32_t __fpu_mxcsr; + uint32_t __fpu_mxcsrmask; + __x86_64_mmst_reg __fpu_stmm0; + __x86_64_mmst_reg __fpu_stmm1; + __x86_64_mmst_reg __fpu_stmm2; + __x86_64_mmst_reg __fpu_stmm3; + __x86_64_mmst_reg __fpu_stmm4; + __x86_64_mmst_reg __fpu_stmm5; + __x86_64_mmst_reg __fpu_stmm6; + __x86_64_mmst_reg __fpu_stmm7; + __x86_64_xmm_reg __fpu_xmm0; + __x86_64_xmm_reg __fpu_xmm1; + __x86_64_xmm_reg __fpu_xmm2; + __x86_64_xmm_reg __fpu_xmm3; + __x86_64_xmm_reg __fpu_xmm4; + __x86_64_xmm_reg __fpu_xmm5; + __x86_64_xmm_reg __fpu_xmm6; + __x86_64_xmm_reg __fpu_xmm7; + __x86_64_xmm_reg __fpu_xmm8; + __x86_64_xmm_reg __fpu_xmm9; + __x86_64_xmm_reg __fpu_xmm10; + __x86_64_xmm_reg __fpu_xmm11; + __x86_64_xmm_reg __fpu_xmm12; + __x86_64_xmm_reg __fpu_xmm13; + __x86_64_xmm_reg __fpu_xmm14; + __x86_64_xmm_reg __fpu_xmm15; + uint8_t __fpu_rsrv4[6*16]; + int32_t __fpu_reserved1; +} __x86_64_float_state_t; + +typedef struct { + uint32_t __fpu_reserved[2]; + __x86_64_fp_control_t __fpu_fcw; + __x86_64_fp_status_t __fpu_fsw; + uint8_t __fpu_ftw; + uint8_t __fpu_rsrv1; + uint16_t __fpu_fop; + uint32_t __fpu_ip; + uint16_t __fpu_cs; + uint16_t __fpu_rsrv2; + uint32_t __fpu_dp; + uint16_t __fpu_ds; + uint16_t __fpu_rsrv3; + uint32_t __fpu_mxcsr; + uint32_t __fpu_mxcsrmask; + __x86_64_mmst_reg __fpu_stmm0; + __x86_64_mmst_reg __fpu_stmm1; + __x86_64_mmst_reg __fpu_stmm2; + __x86_64_mmst_reg __fpu_stmm3; + __x86_64_mmst_reg __fpu_stmm4; + __x86_64_mmst_reg __fpu_stmm5; + __x86_64_mmst_reg __fpu_stmm6; + __x86_64_mmst_reg __fpu_stmm7; + __x86_64_xmm_reg __fpu_xmm0; + __x86_64_xmm_reg __fpu_xmm1; + __x86_64_xmm_reg __fpu_xmm2; + __x86_64_xmm_reg __fpu_xmm3; + __x86_64_xmm_reg __fpu_xmm4; + __x86_64_xmm_reg __fpu_xmm5; + __x86_64_xmm_reg __fpu_xmm6; + __x86_64_xmm_reg __fpu_xmm7; + __x86_64_xmm_reg __fpu_xmm8; + __x86_64_xmm_reg __fpu_xmm9; + __x86_64_xmm_reg __fpu_xmm10; + __x86_64_xmm_reg __fpu_xmm11; + __x86_64_xmm_reg __fpu_xmm12; + __x86_64_xmm_reg __fpu_xmm13; + __x86_64_xmm_reg __fpu_xmm14; + __x86_64_xmm_reg __fpu_xmm15; + uint8_t __fpu_rsrv4[6*16]; + uint32_t __fpu_reserved1; + uint8_t __avx_reserved1[64]; + __x86_64_xmm_reg __fpu_ymmh0; + __x86_64_xmm_reg __fpu_ymmh1; + __x86_64_xmm_reg __fpu_ymmh2; + __x86_64_xmm_reg __fpu_ymmh3; + __x86_64_xmm_reg __fpu_ymmh4; + __x86_64_xmm_reg __fpu_ymmh5; + __x86_64_xmm_reg __fpu_ymmh6; + __x86_64_xmm_reg __fpu_ymmh7; + __x86_64_xmm_reg __fpu_ymmh8; + __x86_64_xmm_reg __fpu_ymmh9; + __x86_64_xmm_reg __fpu_ymmh10; + __x86_64_xmm_reg __fpu_ymmh11; + __x86_64_xmm_reg __fpu_ymmh12; + __x86_64_xmm_reg __fpu_ymmh13; + __x86_64_xmm_reg __fpu_ymmh14; + __x86_64_xmm_reg __fpu_ymmh15; +} __x86_64_avx_state_t; + +typedef struct { + uint32_t __trapno; + uint32_t __err; + uint64_t __faultvaddr; +} __x86_64_exception_state_t; + + +typedef struct { + uint64_t __dr0; + uint64_t __dr1; + uint64_t __dr2; + uint64_t __dr3; + uint64_t __dr4; + uint64_t __dr5; + uint64_t __dr6; + uint64_t __dr7; +} __x86_64_debug_state_t; + +#endif diff --git a/tools/debugserver/source/MacOSX/x86_64/Makefile b/tools/debugserver/source/MacOSX/x86_64/Makefile new file mode 100644 index 000000000000..bf488c859242 --- /dev/null +++ b/tools/debugserver/source/MacOSX/x86_64/Makefile @@ -0,0 +1,19 @@ +##===- tools/debugserver/source/MacOSX/i386/Makefile -------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../../.. + +LIBRARYNAME := lldbDebugserverMacOSX_X86_64 +BUILD_ARCHIVE = 1 + +SOURCES := DNBArchImplX86_64.cpp + +include $(LLDB_LEVEL)/Makefile + +CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../.. -I$(PROJ_OBJ_DIR)/../../.. \ No newline at end of file diff --git a/tools/debugserver/source/Makefile b/tools/debugserver/source/Makefile new file mode 100644 index 000000000000..9eaeab4d3827 --- /dev/null +++ b/tools/debugserver/source/Makefile @@ -0,0 +1,46 @@ +##===- tools/debugserver/source/Makefile -------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LLDB_LEVEL := ../../.. + +LIBRARYNAME := lldbDebugserverCommon +BUILD_ARCHIVE = 1 + +SOURCES := debugserver.cpp \ + DNBArch.cpp \ + DNBBreakpoint.cpp \ + DNB.cpp \ + DNBDataRef.cpp \ + DNBError.cpp \ + DNBLog.cpp \ + DNBRegisterInfo.cpp \ + DNBThreadResumeActions.cpp \ + libdebugserver.cpp \ + PseudoTerminal.cpp \ + PThreadEvent.cpp \ + PThreadMutex.cpp \ + RNBContext.cpp \ + RNBRemote.cpp \ + RNBServices.cpp \ + RNBSocket.cpp \ + SysSignal.cpp \ + TTYState.cpp + +include $(LLDB_LEVEL)/Makefile + +ifeq ($(HOST_OS),Darwin) +DIRS := MacOSX/i386 MacOSX/x86_64 MacOSX +CPP.Flags += -I$(PROJ_SRC_DIR)/MacOSX +CPP.Flags += -I$(PROJ_OBJ_DIR)/.. +BUILT_SOURCES = debugserver_vers.c +endif + +ifeq ($(HOST_OS),Darwin) +debugserver_vers.c: $(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/generate-vers.pl $(PROJ_SRC_DIR)/../debugserver.xcodeproj/project.pbxproj + "$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/scripts/generate-vers.pl" "$(PROJ_SRC_DIR)/../debugserver.xcodeproj/project.pbxproj" debugserver > debugserver_vers.c +endif diff --git a/tools/debugserver/source/PThreadCondition.h b/tools/debugserver/source/PThreadCondition.h new file mode 100644 index 000000000000..787cc7941d52 --- /dev/null +++ b/tools/debugserver/source/PThreadCondition.h @@ -0,0 +1,53 @@ +//===-- PThreadCondition.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 __PThreadCondition_h__ +#define __PThreadCondition_h__ + +#include + +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/tools/debugserver/source/PThreadEvent.cpp b/tools/debugserver/source/PThreadEvent.cpp new file mode 100644 index 000000000000..b087bfc7d481 --- /dev/null +++ b/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/tools/debugserver/source/PThreadEvent.h b/tools/debugserver/source/PThreadEvent.h new file mode 100644 index 000000000000..46c7cc09b121 --- /dev/null +++ b/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 control 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 constructor + PThreadEvent& operator=(const PThreadEvent& rhs); + +}; + +#endif // #ifndef __PThreadEvent_h__ diff --git a/tools/debugserver/source/PThreadMutex.cpp b/tools/debugserver/source/PThreadMutex.cpp new file mode 100644 index 000000000000..bd91ed0154b1 --- /dev/null +++ b/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/tools/debugserver/source/PThreadMutex.h b/tools/debugserver/source/PThreadMutex.h new file mode 100644 index 000000000000..9a12f6e8e034 --- /dev/null +++ b/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/tools/debugserver/source/PseudoTerminal.cpp b/tools/debugserver/source/PseudoTerminal.cpp new file mode 100644 index 000000000000..f1b505cabd4a --- /dev/null +++ b/tools/debugserver/source/PseudoTerminal.cpp @@ -0,0 +1,227 @@ +//===-- 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 +#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/tools/debugserver/source/PseudoTerminal.h b/tools/debugserver/source/PseudoTerminal.h new file mode 100644 index 000000000000..da6b79307b9c --- /dev/null +++ b/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 + }; + + 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/tools/debugserver/source/RNBContext.cpp b/tools/debugserver/source/RNBContext.cpp new file mode 100644 index 000000000000..74ba5c45cb4b --- /dev/null +++ b/tools/debugserver/source/RNBContext.cpp @@ -0,0 +1,279 @@ +//===-- 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 +#include + +#if defined (__APPLE__) +#include +#include +#endif + +#include "RNBRemote.h" +#include "DNB.h" +#include "DNBLog.h" +#include "CFString.h" + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RNBContext::~RNBContext() +{ + SetProcessID (INVALID_NUB_PROCESS); +} + +//---------------------------------------------------------------------- +// RNBContext constructor +//---------------------------------------------------------------------- + +const char * +RNBContext::EnvironmentAtIndex (size_t index) +{ + if (index < m_env_vec.size()) + return m_env_vec[index].c_str(); + else + return NULL; +} + + +const char * +RNBContext::ArgumentAtIndex (size_t index) +{ + if (index < m_arg_vec.size()) + return m_arg_vec[index].c_str(); + else + return NULL; +} + +bool +RNBContext::SetWorkingDirectory (const char *path) +{ + struct stat working_directory_stat; + if (::stat (path, &working_directory_stat) != 0) + { + m_working_directory.clear(); + return false; + } + m_working_directory.assign(path); + return true; +} + + +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); + +#if defined (__APPLE__) + pthread_setname_np ("child process status watcher thread"); +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + struct sched_param thread_param; + int thread_sched_policy; + if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0) + { + thread_param.sched_priority = 47; + pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); + } +#endif +#endif + + bool done = false; + while (!done) + { + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, true)...", __FUNCTION__); + nub_event_t pid_status_event = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, true, NULL); + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, 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 & eEventProfileDataAvailable) + { + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got profile data event....", __FUNCTION__, pid); + ctx.Events().SetEvents (RNBContext::event_proc_profile_data); + // Wait for the main thread to consume this notification if it requested we wait for it + ctx.Events().WaitForResetAck(RNBContext::event_proc_profile_data); + } + + 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: + case eStateDetached: + done = true; + break; + default: + 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_proc_profile_data) + s += "proc_profile_data "; + 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/tools/debugserver/source/RNBContext.h b/tools/debugserver/source/RNBContext.h new file mode 100644 index 000000000000..34fb9796ebeb --- /dev/null +++ b/tools/debugserver/source/RNBContext.h @@ -0,0 +1,159 @@ +//===-- 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_proc_profile_data = 0x10, + event_read_packet_available = 0x20, + event_read_thread_running = 0x40, // Sticky + event_read_thread_exiting = 0x80, + + normal_event_bits = event_proc_state_changed | + event_proc_thread_exiting | + event_proc_stdio_available | + event_proc_profile_data | + 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 (), + m_detach_on_error(false) + { + } + + 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); + + size_t ArgumentCount () const { return m_arg_vec.size(); } + const char * ArgumentAtIndex (size_t 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()); } + + size_t EnvironmentCount () const { return m_env_vec.size(); } + const char * EnvironmentAtIndex (size_t 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; } + + const char * GetWorkingDirectory () const + { + if (!m_working_directory.empty()) + return m_working_directory.c_str(); + return NULL; + } + + bool SetWorkingDirectory (const char *path); + + std::string& GetSTDIN () { return m_stdin; } + std::string& GetSTDOUT () { return m_stdout; } + std::string& GetSTDERR () { return m_stderr; } + std::string& GetWorkingDir () { return m_working_dir; } + + const char * GetSTDINPath() { return m_stdin.empty() ? NULL : m_stdin.c_str(); } + const char * GetSTDOUTPath() { return m_stdout.empty() ? NULL : m_stdout.c_str(); } + const char * GetSTDERRPath() { return m_stderr.empty() ? NULL : m_stderr.c_str(); } + const char * GetWorkingDirPath() { return m_working_dir.empty() ? NULL : m_working_dir.c_str(); } + + void PushProcessEvent (const char *p) { m_process_event.assign(p); } + const char * GetProcessEvent () { return m_process_event.c_str(); } + + void SetDetachOnError(bool detach) { m_detach_on_error = detach; } + bool GetDetachOnError () { return m_detach_on_error; } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from RNBContext can see and modify these + //------------------------------------------------------------------ + nub_process_t m_pid; + std::string m_stdin; + std::string m_stdout; + std::string m_stderr; + std::string m_working_dir; + 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 + std::string m_working_directory; + std::string m_process_event; + bool m_detach_on_error; + + 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/tools/debugserver/source/RNBDefs.h b/tools/debugserver/source/RNBDefs.h new file mode 100644 index 000000000000..984b91152415 --- /dev/null +++ b/tools/debugserver/source/RNBDefs.h @@ -0,0 +1,87 @@ +//===-- 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 + +#define DEBUGSERVER_PROGRAM_NAME "debugserver" + +#ifndef DEBUGSERVER_VERSION_NUM +extern "C" const unsigned char debugserverVersionString[]; +#define DEBUGSERVER_VERSION_NUM debugserverVersionNumber +#endif + +#ifndef DEBUGSERVER_VERSION_STR +extern "C" const double debugserverVersionNumber; +#define DEBUGSERVER_VERSION_STR debugserverVersionString +#endif + +#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 (__arm64__) || defined (__aarch64__) + +#define RNB_ARCH "arm64" + +#elif defined (__arm__) + +#define RNB_ARCH "armv7" + +#else + +#error undefined architecture + +#endif + +class RNBRemote; +typedef std::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/tools/debugserver/source/RNBRemote.cpp b/tools/debugserver/source/RNBRemote.cpp new file mode 100644 index 000000000000..dadf479ce81b --- /dev/null +++ b/tools/debugserver/source/RNBRemote.cpp @@ -0,0 +1,6083 @@ +//===-- 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 +#include + +#if defined (__APPLE__) +#include +#include +#endif + +#include "DNB.h" +#include "DNBDataRef.h" +#include "DNBLog.h" +#include "DNBThreadResumeActions.h" +#include "JSONGenerator.h" +#include "RNBContext.h" +#include "RNBServices.h" +#include "RNBSocket.h" +#include "lldb/Utility/StringExtractor.h" +#include "MacOSX/Genealogy.h" +#include "JSONGenerator.h" + +#if defined (HAVE_LIBCOMPRESSION) +#include +#endif + +#if defined (HAVE_LIBZ) +#include +#endif + +#include +#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. + + +//---------------------------------------------------------------------- +// Decode a single hex character and return the hex value as a number or +// -1 if "ch" is not a hex character. +//---------------------------------------------------------------------- +static inline int +xdigit_to_sint (char ch) +{ + if (ch >= 'a' && ch <= 'f') + return 10 + ch - 'a'; + if (ch >= 'A' && ch <= 'F') + return 10 + ch - 'A'; + if (ch >= '0' && ch <= '9') + return ch - '0'; + return -1; +} + +//---------------------------------------------------------------------- +// Decode a single hex ASCII byte. Return -1 on failure, a value 0-255 +// on success. +//---------------------------------------------------------------------- +static inline int +decoded_hex_ascii_char(const char *p) +{ + const int hi_nibble = xdigit_to_sint(p[0]); + if (hi_nibble == -1) + return -1; + const int lo_nibble = xdigit_to_sint(p[1]); + if (lo_nibble == -1) + return -1; + return (uint8_t)((hi_nibble << 4) + lo_nibble); +} + +//---------------------------------------------------------------------- +// Decode a hex ASCII string back into a string +//---------------------------------------------------------------------- +static std::string +decode_hex_ascii_string(const char *p, uint32_t max_length = UINT32_MAX) +{ + std::string arg; + if (p) + { + for (const char *c = p; ((c - p)/2) < max_length; c += 2) + { + int ch = decoded_hex_ascii_char(c); + if (ch == -1) + break; + else + arg.push_back(ch); + } + } + return arg; +} + +uint64_t +decode_uint64 (const char *p, int base, char **end = nullptr, uint64_t fail_value = 0) +{ + nub_addr_t addr = strtoull (p, end, 16); + if (addr == 0 && errno != 0) + return fail_value; + return addr; +} + +extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args); + +#if defined (__APPLE__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000) +// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h +extern "C" { +#define CS_OPS_STATUS 0 /* return status */ +#define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */ +int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); + +// from rootless.h +bool rootless_allows_task_for_pid (pid_t pid); + +// from sys/csr.h +typedef uint32_t csr_config_t; +#define CSR_ALLOW_TASK_FOR_PID (1 << 2) +int csr_check(csr_config_t mask); +} +#endif + +RNBRemote::RNBRemote () : + m_ctx (), + m_comm (), + m_arch (), + m_continue_thread(-1), + m_thread(-1), + m_mutex(), + m_dispatch_queue_offsets (), + m_dispatch_queue_offsets_addr (INVALID_NUB_ADDRESS), + m_qSymbol_index (UINT32_MAX), + m_packets_recvd(0), + m_packets(), + m_rx_packets(), + m_rx_partial_data(), + m_rx_pthread(0), + m_max_payload_size(DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE - 4), + m_extended_mode(false), + m_noack_mode(false), + m_thread_suffix_supported (false), + m_list_threads_in_stop_reply (false), + m_compression_minsize (384), + m_enable_compression_next_send_packet (false), + m_compression_mode (compression_types::none) +{ + 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 the RNBRemote::HandlePacket_ function if a new function is needed + // 3 - Register the Packet definition with any needed callbacks in this function + // - 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 cycle")); + 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 (query_supported_features, &RNBRemote::HandlePacket_qSupported, NULL, "qSupported", "Query about supported features")); + 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 (vattachorwait, &RNBRemote::HandlePacket_v, NULL, "vAttachOrWait", "Attach to the process or if it doesn't exist, wait for the process to start up then attach to it")); + t.push_back (Packet (vattachname, &RNBRemote::HandlePacket_v, NULL, "vAttachName", "Attach to an existing process by name")); + 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")); + t.push_back (Packet (read_data_from_memory, &RNBRemote::HandlePacket_x, NULL, "x", "Read data from memory")); + 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_z, NULL, "Z2", "Insert write watchpoint")); + t.push_back (Packet (remove_write_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z2", "Remove write watchpoint")); + t.push_back (Packet (insert_read_watch_bp, &RNBRemote::HandlePacket_z, NULL, "Z3", "Insert read watchpoint")); + t.push_back (Packet (remove_read_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z3", "Remove read watchpoint")); + t.push_back (Packet (insert_access_watch_bp, &RNBRemote::HandlePacket_z, NULL, "Z4", "Insert access watchpoint")); + t.push_back (Packet (remove_access_watch_bp, &RNBRemote::HandlePacket_z, NULL, "z4", "Remove access watchpoint")); + t.push_back (Packet (query_monitor, &RNBRemote::HandlePacket_qRcmd, NULL, "qRcmd", "Monitor command")); + t.push_back (Packet (query_current_thread_id, &RNBRemote::HandlePacket_qC, NULL, "qC", "Query current thread ID")); + t.push_back (Packet (query_echo, &RNBRemote::HandlePacket_qEcho, NULL, "qEcho:", "Echo the packet back to allow the debugger to sync up with this server")); + t.push_back (Packet (query_get_pid, &RNBRemote::HandlePacket_qGetPid, NULL, "qGetPid", "Query process id")); + 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_vattachorwait_supported, &RNBRemote::HandlePacket_qVAttachOrWaitSupported,NULL, "qVAttachOrWaitSupported", "Replys with OK if the 'vAttachOrWait' packet is supported.")); + t.push_back (Packet (query_sync_thread_state_supported, &RNBRemote::HandlePacket_qSyncThreadStateSupported,NULL, "qSyncThreadStateSupported", "Replys with OK if the 'QSyncThreadState:' 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_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion , NULL, "qGDBServerVersion", "Replies with multiple 'key:value;' tuples appended to each other.")); + t.push_back (Packet (query_process_info, &RNBRemote::HandlePacket_qProcessInfo , NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); + t.push_back (Packet (query_symbol_lookup, &RNBRemote::HandlePacket_qSymbol , NULL, "qSymbol:", "Notify that host debugger is ready to do symbol lookups")); + t.push_back (Packet (json_query_thread_extended_info,&RNBRemote::HandlePacket_jThreadExtendedInfo , NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information.")); + t.push_back (Packet (json_query_get_loaded_dynamic_libraries_infos, &RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos, NULL, "jGetLoadedDynamicLibrariesInfos", "Replies with JSON data of all the shared libraries loaded in this process.")); + t.push_back (Packet (json_query_threads_info, &RNBRemote::HandlePacket_jThreadsInfo , NULL, "jThreadsInfo", "Replies with JSON data with information about all threads.")); + t.push_back (Packet (start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets")); + t.push_back (Packet (prefix_reg_packets_with_tid, &RNBRemote::HandlePacket_QThreadSuffixSupported , NULL, "QThreadSuffixSupported", "Check if thread specific packets (register packets 'g', 'G', 'p', and 'P') support having the thread ID appended to the end of the command")); + t.push_back (Packet (set_logging_mode, &RNBRemote::HandlePacket_QSetLogging , NULL, "QSetLogging:", "Check if register packets ('g', 'G', 'p', and 'P' support having the thread ID prefix")); + t.push_back (Packet (set_max_packet_size, &RNBRemote::HandlePacket_QSetMaxPacketSize , NULL, "QSetMaxPacketSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized packet gdb can handle")); + t.push_back (Packet (set_max_payload_size, &RNBRemote::HandlePacket_QSetMaxPayloadSize , NULL, "QSetMaxPayloadSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized payload gdb can handle")); + t.push_back (Packet (set_environment_variable, &RNBRemote::HandlePacket_QEnvironment , NULL, "QEnvironment:", "Add an environment variable to the inferior's environment")); + t.push_back (Packet (set_environment_variable_hex, &RNBRemote::HandlePacket_QEnvironmentHexEncoded , NULL, "QEnvironmentHexEncoded:", "Add an environment variable to the inferior's environment")); + t.push_back (Packet (set_launch_arch, &RNBRemote::HandlePacket_QLaunchArch , NULL, "QLaunchArch:", "Set the architecture to use when launching a process for hosts that can run multiple architecture slices from universal files.")); + t.push_back (Packet (set_disable_aslr, &RNBRemote::HandlePacket_QSetDisableASLR , NULL, "QSetDisableASLR:", "Set whether to disable ASLR when launching the process with the set argv ('A') packet")); + t.push_back (Packet (set_stdin, &RNBRemote::HandlePacket_QSetSTDIO , NULL, "QSetSTDIN:", "Set the standard input for a process to be launched with the 'A' packet")); + t.push_back (Packet (set_stdout, &RNBRemote::HandlePacket_QSetSTDIO , NULL, "QSetSTDOUT:", "Set the standard output for a process to be launched with the 'A' packet")); + t.push_back (Packet (set_stderr, &RNBRemote::HandlePacket_QSetSTDIO , NULL, "QSetSTDERR:", "Set the standard error for a process to be launched with the 'A' packet")); + t.push_back (Packet (set_working_dir, &RNBRemote::HandlePacket_QSetWorkingDir , NULL, "QSetWorkingDir:", "Set the working directory for a process to be launched with the 'A' packet")); + t.push_back (Packet (set_list_threads_in_stop_reply,&RNBRemote::HandlePacket_QListThreadsInStopReply , NULL, "QListThreadsInStopReply", "Set if the 'threads' key should be added to the stop reply packets with a list of all thread IDs.")); + t.push_back (Packet (sync_thread_state, &RNBRemote::HandlePacket_QSyncThreadState , NULL, "QSyncThreadState:", "Do whatever is necessary to make sure 'thread' is in a safe state to call functions on.")); +// 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.")); + t.push_back (Packet (save_register_state, &RNBRemote::HandlePacket_SaveRegisterState, NULL, "QSaveRegisterState", "Save the register state for the current thread and return a decimal save ID.")); + t.push_back (Packet (restore_register_state, &RNBRemote::HandlePacket_RestoreRegisterState, NULL, "QRestoreRegisterState:", "Restore the register state given a save ID previously returned from a call to QSaveRegisterState.")); + t.push_back (Packet (memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL, "qMemoryRegionInfo", "Return size and attributes of a memory region that contains the given address")); + t.push_back (Packet (get_profile_data, &RNBRemote::HandlePacket_GetProfileData, NULL, "qGetProfileData", "Return profiling data of the current target.")); + t.push_back (Packet (set_enable_profiling, &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL, "QSetEnableAsyncProfiling", "Enable or disable the profiling of current target.")); + t.push_back (Packet (enable_compression, &RNBRemote::HandlePacket_QEnableCompression, NULL, "QEnableCompression:", "Enable compression for the remainder of the connection")); + t.push_back (Packet (watchpoint_support_info, &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL, "qWatchpointSupportInfo", "Return the number of supported hardware watchpoints")); + t.push_back (Packet (set_process_event, &RNBRemote::HandlePacket_QSetProcessEvent, NULL, "QSetProcessEvent:", "Set a process event, to be passed to the process, can be set before the process is started, or after.")); + t.push_back (Packet (set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError, NULL, "QSetDetachOnError:", "Set whether debugserver will detach (1) or kill (0) from the process it is controlling if it loses connection to lldb.")); + t.push_back (Packet (speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:", "Test the maximum speed at which packet can be sent/received.")); + t.push_back (Packet (query_transfer, &RNBRemote::HandlePacket_qXfer, NULL, "qXfer:", "Support the qXfer packet.")); +} + + +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); + } +} + +void +RNBRemote::SendAsyncProfileData () +{ + if (m_ctx.HasValidProcessID()) + { + nub_process_t pid = m_ctx.ProcessID(); + char buf[1024]; + nub_size_t count; + do + { + count = DNBProcessGetAvailableProfileData(pid, buf, sizeof(buf)); + if (count > 0) + { + SendAsyncProfileDataPacket (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; i +// C: +// +// If compression is not requested, the original packet contents are returned + +std::string +RNBRemote::CompressString (const std::string &orig) +{ + std::string compressed; + compression_types compression_type = GetCompressionType(); + if (compression_type != compression_types::none) + { + bool compress_this_packet = false; + + if (orig.size() > m_compression_minsize) + { + compress_this_packet = true; + } + + if (compress_this_packet) + { + const size_t encoded_data_buf_size = orig.size() + 128; + std::vector encoded_data (encoded_data_buf_size); + size_t compressed_size = 0; + +#if defined (HAVE_LIBCOMPRESSION) + if (compression_decode_buffer && compression_type == compression_types::lz4) + { + compressed_size = compression_encode_buffer (encoded_data.data(), + encoded_data_buf_size, + (uint8_t*) orig.c_str(), + orig.size(), + nullptr, + COMPRESSION_LZ4_RAW); + } + if (compression_decode_buffer && compression_type == compression_types::zlib_deflate) + { + compressed_size = compression_encode_buffer (encoded_data.data(), + encoded_data_buf_size, + (uint8_t*) orig.c_str(), + orig.size(), + nullptr, + COMPRESSION_ZLIB); + } + if (compression_decode_buffer && compression_type == compression_types::lzma) + { + compressed_size = compression_encode_buffer (encoded_data.data(), + encoded_data_buf_size, + (uint8_t*) orig.c_str(), + orig.size(), + nullptr, + COMPRESSION_LZMA); + } + if (compression_decode_buffer && compression_type == compression_types::lzfse) + { + compressed_size = compression_encode_buffer (encoded_data.data(), + encoded_data_buf_size, + (uint8_t*) orig.c_str(), + orig.size(), + nullptr, + COMPRESSION_LZFSE); + } +#endif + +#if defined (HAVE_LIBZ) + if (compressed_size == 0 && compression_type == compression_types::zlib_deflate) + { + z_stream stream; + memset (&stream, 0, sizeof (z_stream)); + stream.next_in = (Bytef *) orig.c_str(); + stream.avail_in = (uInt) orig.size(); + stream.next_out = (Bytef *) encoded_data.data(); + stream.avail_out = (uInt) encoded_data_buf_size; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + deflateInit2 (&stream, 5, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); + int compress_status = deflate (&stream, Z_FINISH); + deflateEnd (&stream); + if (compress_status == Z_STREAM_END && stream.total_out > 0) + { + compressed_size = stream.total_out; + } + } +#endif + + if (compressed_size > 0) + { + compressed.clear (); + compressed.reserve (compressed_size); + compressed = "C"; + char numbuf[16]; + snprintf (numbuf, sizeof (numbuf), "%zu:", orig.size()); + numbuf[sizeof (numbuf) - 1] = '\0'; + compressed.append (numbuf); + + for (size_t i = 0; i < compressed_size; i++) + { + uint8_t byte = encoded_data[i]; + if (byte == '#' || byte == '$' || byte == '}' || byte == '*' || byte == '\0') + { + compressed.push_back (0x7d); + compressed.push_back (byte ^ 0x20); + } + else + { + compressed.push_back (byte); + } + } + } + else + { + compressed = "N" + orig; + } + } + else + { + compressed = "N" + orig; + } + } + else + { + compressed = orig; + } + + return compressed; +} + +rnb_err_t +RNBRemote::SendPacket (const std::string &s) +{ + DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s (%s) called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, s.c_str()); + + std::string s_compressed = CompressString (s); + + std::string sendpacket = "$" + s_compressed + "#"; + int cksum = 0; + char hexbuf[5]; + + if (m_noack_mode) + { + sendpacket += "00"; + } + else + { + for (size_t i = 0; i != s_compressed.size(); ++i) + cksum += s_compressed[i]; + snprintf (hexbuf, sizeof hexbuf, "%02x", cksum & 0xff); + sendpacket += hexbuf; + } + + rnb_err_t err = m_comm.Write (sendpacket.c_str(), sendpacket.size()); + if (err != rnb_success) + return err; + + if (m_noack_mode) + return rnb_success; + + std::string reply; + RNBRemote::Packet packet; + err = GetPacket (reply, packet, true); + + if (err != rnb_success) + { + DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s (%s) got error trying to get reply...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, sendpacket.c_str()); + return err; + } + + DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s (%s) got reply: '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, sendpacket.c_str(), reply.c_str()); + + if (packet.type == ack) + return rnb_success; + + // Should we try to resend the packet at this layer? + // if (packet.command == nack) + return rnb_err; +} + +/* Get a packet via gdb remote protocol. + Strip off the prefix/suffix, verify the checksum to make sure + a valid packet was received, send an ACK if they match. */ + +rnb_err_t +RNBRemote::GetPacketPayload (std::string &return_packet) +{ + //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + + PThreadMutex::Locker locker(m_mutex); + if (m_rx_packets.empty()) + { + // Only reset the remote command available event if we have no more packets + m_ctx.Events().ResetEvents ( RNBContext::event_read_packet_available ); + //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s error: no packets available...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + return rnb_err; + } + + //DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s has %u queued packets", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, m_rx_packets.size()); + return_packet.swap(m_rx_packets.front()); + m_rx_packets.pop_front(); + locker.Reset(); // Release our lock on the mutex + + if (m_rx_packets.empty()) + { + // Reset the remote command available event if we have no more packets + m_ctx.Events().ResetEvents ( RNBContext::event_read_packet_available ); + } + + //DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s: '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str()); + + switch (return_packet[0]) + { + case '+': + case '-': + case '\x03': + break; + + case '$': + { + long packet_checksum = 0; + if (!m_noack_mode) + { + for (size_t i = return_packet.size() - 2; i < return_packet.size(); ++i) + { + char checksum_char = tolower (return_packet[i]); + if (!isxdigit (checksum_char)) + { + m_comm.Write ("-", 1); + DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s error: packet with invalid checksum characters: %s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str()); + return rnb_err; + } + } + packet_checksum = strtol (&return_packet[return_packet.size() - 2], NULL, 16); + } + + return_packet.erase(0,1); // Strip the leading '$' + return_packet.erase(return_packet.size() - 3);// Strip the #XX checksum + + if (!m_noack_mode) + { + // Compute the checksum + int computed_checksum = 0; + for (std::string::iterator it = return_packet.begin (); + it != return_packet.end (); + ++it) + { + computed_checksum += *it; + } + + if (packet_checksum == (computed_checksum & 0xff)) + { + //DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str()); + m_comm.Write ("+", 1); + } + else + { + DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s' (error: packet checksum mismatch (0x%2.2lx != 0x%2.2x))", + (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), + __FUNCTION__, + return_packet.c_str(), + packet_checksum, + computed_checksum); + m_comm.Write ("-", 1); + return rnb_err; + } + } + } + break; + + default: + DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s tossing unexpected packet???? %s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, return_packet.c_str()); + if (!m_noack_mode) + m_comm.Write ("-", 1); + return rnb_err; + } + + return rnb_success; +} + +rnb_err_t +RNBRemote::HandlePacket_UNIMPLEMENTED (const char* p) +{ + DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s(\"%s\")", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p ? p : "NULL"); + return SendPacket (""); +} + +rnb_err_t +RNBRemote::HandlePacket_ILLFORMED (const char *file, int line, const char *p, const char *description) +{ + DNBLogThreadedIf (LOG_RNB_PACKETS, "%8u %s:%i ILLFORMED: '%s' (%s)", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), file, line, __FUNCTION__, p); + return SendPacket ("E03"); +} + +rnb_err_t +RNBRemote::GetPacket (std::string &packet_payload, RNBRemote::Packet& packet_info, bool wait) +{ + std::string payload; + rnb_err_t err = GetPacketPayload (payload); + if (err != rnb_success) + { + PThreadEvent& events = m_ctx.Events(); + nub_event_t set_events = events.GetEventBits(); + // TODO: add timeout version of GetPacket?? We would then need to pass + // that timeout value along to DNBProcessTimedWaitForEvent. + if (!wait || ((set_events & RNBContext::event_read_thread_running) == 0)) + return err; + + const nub_event_t events_to_wait_for = RNBContext::event_read_packet_available | RNBContext::event_read_thread_exiting; + + while ((set_events = events.WaitForSetEvents(events_to_wait_for)) != 0) + { + if (set_events & RNBContext::event_read_packet_available) + { + // Try the queue again now that we got an event + err = GetPacketPayload (payload); + if (err == rnb_success) + break; + } + + if (set_events & RNBContext::event_read_thread_exiting) + err = rnb_not_connected; + + if (err == rnb_not_connected) + return err; + + } while (err == rnb_err); + + if (set_events == 0) + err = rnb_not_connected; + } + + if (err == rnb_success) + { + Packet::iterator it; + for (it = m_packets.begin (); it != m_packets.end (); ++it) + { + if (payload.compare (0, it->abbrev.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 + size_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 + 3 > data_size) + { + end_idx = std::string::npos; + } + else + { + // Add two for the checksum bytes and 1 to point to the + // byte just past the end of this packet + end_idx += 3; + } + 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) + { + +#if defined (__APPLE__) + pthread_setname_np ("read gdb-remote packets thread"); +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + struct sched_param thread_param; + int thread_sched_policy; + if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0) + { + thread_param.sched_priority = 47; + pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); + } +#endif +#endif + + 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; +} + + +// If we fail to get back a valid CPU type for the remote process, +// make a best guess for the CPU type based on the currently running +// debugserver binary -- the debugger may not handle the case of an +// un-specified process CPU type correctly. + +static cpu_type_t +best_guess_cpu_type () +{ +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + if (sizeof (char *) == 8) + { + return CPU_TYPE_ARM64; + } + else + { + return CPU_TYPE_ARM; + } +#elif defined (__i386__) || defined (__x86_64__) + if (sizeof (char*) == 8) + { + return CPU_TYPE_X86_64; + } + else + { + return CPU_TYPE_I386; + } +#endif + return 0; +} + + +/* 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 ('#'), 0x24 ('$'), 0x2a ('*'). + LEN is the number of bytes to be processed. If a character is escaped, + it is 2 characters for LEN. A LEN of -1 means decode-until-nul-byte + (end of string). */ + +std::vector +decode_binary_data (const char *str, size_t len) +{ + std::vector bytes; + if (len == 0) + { + return bytes; + } + if (len == (size_t)-1) + len = strlen (str); + + while (len--) + { + unsigned char c = *str; + if (c == 0x7d && len > 0) + { + len--; + str++; + c = *str ^ 0x20; + } + bytes.push_back (c); + } + return bytes; +} + +// Quote any meta characters in a std::string as per the binary +// packet convention in the gdb-remote protocol. + +std::string +binary_encode_string (const std::string &s) +{ + std::string output; + const size_t s_size = s.size(); + const char *s_chars = s.c_str(); + + for (size_t i = 0; i < s_size; i++) + { + unsigned char ch = *(s_chars + i); + if (ch == '#' || ch == '$' || ch == '}' || ch == '*') + { + output.push_back ('}'); // 0x7d + output.push_back (ch ^ 0x20); + } + else + { + output.push_back (ch); + } + } + return output; +} + +// If the value side of a key-value pair in JSON is a string, +// and that string has a " character in it, the " character must +// be escaped. + +std::string +json_string_quote_metachars (const std::string &s) +{ + if (s.find('"') == std::string::npos) + return s; + + std::string output; + const size_t s_size = s.size(); + const char *s_chars = s.c_str(); + for (size_t i = 0; i < s_size; i++) + { + unsigned char ch = *(s_chars + i); + if (ch == '"') + { + output.push_back ('\\'); + } + output.push_back (ch); + } + return output; +} + +typedef struct register_map_entry +{ + uint32_t debugserver_regnum; // debugserver register number + uint32_t offset; // Offset in bytes into the register context data with no padding between register values + DNBRegisterInfo nub_info; // debugnub register info + std::vector value_regnums; + std::vector invalidate_regnums; +} 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 std::vector g_dynamic_register_map; +static register_map_entry_t *g_reg_entries = NULL; +static size_t g_num_reg_entries = 0; + +void +RNBRemote::Initialize() +{ + DNBInitialize(); +} + + +bool +RNBRemote::InitializeRegisters (bool force) +{ + pid_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return false; + + DNBLogThreadedIf (LOG_RNB_PROC, "RNBRemote::%s() getting native registers from DNB interface", __FUNCTION__); + // Discover the registers by querying the DNB interface and letting it + // state the registers that it would like to export. This allows the + // registers to be discovered using multiple qRegisterInfo calls to get + // all register information after the architecture for the process is + // determined. + if (force) + { + g_dynamic_register_map.clear(); + g_reg_entries = NULL; + g_num_reg_entries = 0; + } + + if (g_dynamic_register_map.empty()) + { + nub_size_t num_reg_sets = 0; + const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo (&num_reg_sets); + + assert (num_reg_sets > 0 && reg_sets != NULL); + + uint32_t regnum = 0; + uint32_t reg_data_offset = 0; + typedef std::map NameToRegNum; + NameToRegNum name_to_regnum; + 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_data_offset, // Offset into register context data, no gaps between registers + reg_sets[set].registers[reg] // DNBRegisterInfo + }; + + name_to_regnum[reg_entry.nub_info.name] = reg_entry.debugserver_regnum; + + if (reg_entry.nub_info.value_regs == NULL) + { + reg_data_offset += reg_entry.nub_info.size; + } + + g_dynamic_register_map.push_back (reg_entry); + } + } + + // Now we must find any registers whose values are in other registers and fix up + // the offsets since we removed all gaps... + for (auto ®_entry: g_dynamic_register_map) + { + if (reg_entry.nub_info.value_regs) + { + uint32_t new_offset = UINT32_MAX; + for (size_t i=0; reg_entry.nub_info.value_regs[i] != NULL; ++i) + { + const char *name = reg_entry.nub_info.value_regs[i]; + auto pos = name_to_regnum.find(name); + if (pos != name_to_regnum.end()) + { + regnum = pos->second; + reg_entry.value_regnums.push_back(regnum); + if (regnum < g_dynamic_register_map.size()) + { + // The offset for value_regs registers is the offset within the register with the lowest offset + const uint32_t reg_offset = g_dynamic_register_map[regnum].offset + reg_entry.nub_info.offset; + if (new_offset > reg_offset) + new_offset = reg_offset; + } + } + } + + if (new_offset != UINT32_MAX) + { + reg_entry.offset = new_offset; + } + else + { + DNBLogThreaded("no offset was calculated entry for register %s", reg_entry.nub_info.name); + reg_entry.offset = UINT32_MAX; + } + } + + if (reg_entry.nub_info.update_regs) + { + for (size_t i=0; reg_entry.nub_info.update_regs[i] != NULL; ++i) + { + const char *name = reg_entry.nub_info.update_regs[i]; + auto pos = name_to_regnum.find(name); + if (pos != name_to_regnum.end()) + { + regnum = pos->second; + reg_entry.invalidate_regnums.push_back(regnum); + } + } + } + } + + +// for (auto ®_entry: g_dynamic_register_map) +// { +// DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s", +// reg_entry.offset, +// reg_entry.nub_info.size, +// reg_entry.nub_info.value_regs != NULL, +// reg_entry.nub_info.name); +// } + + g_reg_entries = g_dynamic_register_map.data(); + g_num_reg_entries = g_dynamic_register_map.size(); + } + return true; +} + +/* The inferior has stopped executing; send a packet + to gdb to let it know. */ + +void +RNBRemote::NotifyThatProcessStopped (void) +{ + RNBRemote::HandlePacket_last_signal (NULL); + 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 (__FILE__, __LINE__, p, "Null packet for 'A' pkt"); + } + p++; + if (*p == '\0' || !isdigit (*p)) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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') + { + unsigned long arglen, argnum; + std::string arg; + char *c; + + errno = 0; + arglen = strtoul (buf, &c, 10); + if (errno != 0 && arglen == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "arglen not a number on 'A' pkt"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "argnum not a number on 'A' pkt"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 = static_cast(strtoul (smallbuf, NULL, 16)); + if (errno != 0 && ch == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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_qSyncThreadStateSupported (const char *p) +{ + // We support attachOrWait meaning attach if the process exists, otherwise wait to attach. + return SendPacket("OK"); +} + +rnb_err_t +RNBRemote::HandlePacket_qVAttachOrWaitSupported (const char *p) +{ + // We support attachOrWait meaning attach if the process exists, otherwise wait to attach. + 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 (__FILE__, __LINE__, p, "Illformed qThreadExtraInfo packet"); + errno = 0; + nub_thread_t tid = strtoul (p, NULL, 16); + if (errno != 0 && tid == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 (""); +} + + +const char *k_space_delimiters = " \t"; +static void +skip_spaces (std::string &line) +{ + if (!line.empty()) + { + size_t space_pos = line.find_first_not_of (k_space_delimiters); + if (space_pos > 0) + line.erase(0, space_pos); + } +} + +static std::string +get_identifier (std::string &line) +{ + std::string word; + skip_spaces (line); + const size_t line_size = line.size(); + size_t end_pos; + for (end_pos = 0; end_pos < line_size; ++end_pos) + { + if (end_pos == 0) + { + if (isalpha(line[end_pos]) || line[end_pos] == '_') + continue; + } + else if (isalnum(line[end_pos]) || line[end_pos] == '_') + continue; + break; + } + word.assign (line, 0, end_pos); + line.erase(0, end_pos); + return word; +} + +static std::string +get_operator (std::string &line) +{ + std::string op; + skip_spaces (line); + if (!line.empty()) + { + if (line[0] == '=') + { + op = '='; + line.erase(0,1); + } + } + return op; +} + +static std::string +get_value (std::string &line) +{ + std::string value; + skip_spaces (line); + if (!line.empty()) + { + value.swap(line); + } + return value; +} + +extern void FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args); +extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args); + +rnb_err_t +RNBRemote::HandlePacket_qRcmd (const char *p) +{ + const char *c = p + strlen("qRcmd,"); + std::string line; + while (c[0] && c[1]) + { + char smallbuf[3] = { c[0], c[1], '\0' }; + errno = 0; + int ch = static_cast(strtoul (smallbuf, NULL, 16)); + if (errno != 0 && ch == 0) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in payload of qRcmd packet"); + line.push_back(ch); + c += 2; + } + if (*c == '\0') + { + std::string command = get_identifier(line); + if (command.compare("set") == 0) + { + std::string variable = get_identifier (line); + std::string op = get_operator (line); + std::string value = get_value (line); + if (variable.compare("logfile") == 0) + { + FILE *log_file = fopen(value.c_str(), "w"); + if (log_file) + { + DNBLogSetLogCallback(FileLogCallback, log_file); + return SendPacket ("OK"); + } + return SendPacket ("E71"); + } + else if (variable.compare("logmask") == 0) + { + char *end; + errno = 0; + uint32_t logmask = static_cast(strtoul (value.c_str(), &end, 0)); + if (errno == 0 && end && *end == '\0') + { + DNBLogSetLogMask (logmask); + if (!DNBLogGetLogCallback()) + DNBLogSetLogCallback(ASLLogCallback, NULL); + return SendPacket ("OK"); + } + errno = 0; + logmask = static_cast(strtoul (value.c_str(), &end, 16)); + if (errno == 0 && end && *end == '\0') + { + DNBLogSetLogMask (logmask); + return SendPacket ("OK"); + } + return SendPacket ("E72"); + } + return SendPacket ("E70"); + } + return SendPacket ("E69"); + } + return SendPacket ("E73"); +} + +rnb_err_t +RNBRemote::HandlePacket_qC (const char *p) +{ + nub_thread_t tid; + 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()) + tid = 0; + else + { + // Grab the current thread. + tid = DNBProcessGetCurrentThread (m_ctx.ProcessID()); + // Make sure we set the current thread so g and p packets return + // the data the gdb will expect. + SetCurrentThread (tid); + } + rep << "QC" << std::hex << tid; + return SendPacket (rep.str()); +} + +rnb_err_t +RNBRemote::HandlePacket_qEcho (const char *p) +{ + // Just send the exact same packet back that we received to + // synchronize the response packets after a previous packet + // timed out. This allows the debugger to get back on track + // with responses after a packet timeout. + return SendPacket (p); +} + +rnb_err_t +RNBRemote::HandlePacket_qGetPid (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 << std::hex << pid; + return SendPacket (rep.str()); +} + +rnb_err_t +RNBRemote::HandlePacket_qRegisterInfo (const char *p) +{ + if (g_num_reg_entries == 0) + InitializeRegisters (); + + p += strlen ("qRegisterInfo"); + + nub_size_t num_reg_sets = 0; + const DNBRegisterSetInfo *reg_set_info = DNBGetRegisterSetInfo (&num_reg_sets); + uint32_t reg_num = static_cast(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; + if (reg_entry->nub_info.name) + ostrm << "name:" << reg_entry->nub_info.name << ';'; + if (reg_entry->nub_info.alt) + ostrm << "alt-name:" << reg_entry->nub_info.alt << ';'; + + ostrm << "bitsize:" << std::dec << reg_entry->nub_info.size * 8 << ';'; + ostrm << "offset:" << std::dec << reg_entry->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 (reg_entry->nub_info.reg_ehframe != INVALID_NUB_REGNUM) + ostrm << "ehframe:" << std::dec << reg_entry->nub_info.reg_ehframe << ';'; + + 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; + case GENERIC_REGNUM_ARG1: ostrm << "generic:arg1;"; break; + case GENERIC_REGNUM_ARG2: ostrm << "generic:arg2;"; break; + case GENERIC_REGNUM_ARG3: ostrm << "generic:arg3;"; break; + case GENERIC_REGNUM_ARG4: ostrm << "generic:arg4;"; break; + case GENERIC_REGNUM_ARG5: ostrm << "generic:arg5;"; break; + case GENERIC_REGNUM_ARG6: ostrm << "generic:arg6;"; break; + case GENERIC_REGNUM_ARG7: ostrm << "generic:arg7;"; break; + case GENERIC_REGNUM_ARG8: ostrm << "generic:arg8;"; break; + default: break; + } + + if (!reg_entry->value_regnums.empty()) + { + ostrm << "container-regs:"; + for (size_t i=0, n=reg_entry->value_regnums.size(); i < n; ++i) + { + if (i > 0) + ostrm << ','; + ostrm << RAW_HEXBASE << reg_entry->value_regnums[i]; + } + ostrm << ';'; + } + + if (!reg_entry->invalidate_regnums.empty()) + { + ostrm << "invalidate-regs:"; + for (size_t i=0, n=reg_entry->invalidate_regnums.size(); i < n; ++i) + { + if (i > 0) + ostrm << ','; + ostrm << RAW_HEXBASE << reg_entry->invalidate_regnums[i]; + } + ostrm << ';'; + } + + 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++; + +// to regenerate the LOG_ entries (not including the LOG_RNB entries) +// $ for logname in `grep '^#define LOG_' DNBDefs.h | egrep -v 'LOG_HI|LOG_LO' | awk '{print $2}'` +// do +// echo " else if (strncmp (p, \"$logname\", sizeof (\"$logname\") - 1) == 0)" +// echo " {" +// echo " p += sizeof (\"$logname\") - 1;" +// echo " bitmask |= $logname;" +// echo " }" +// done + 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_MEMORY_PROTECTIONS", sizeof ("LOG_MEMORY_PROTECTIONS") - 1) == 0) + { + p += sizeof ("LOG_MEMORY_PROTECTIONS") - 1; + bitmask |= LOG_MEMORY_PROTECTIONS; + } + else if (strncmp (p, "LOG_BREAKPOINTS", sizeof ("LOG_BREAKPOINTS") - 1) == 0) + { + p += sizeof ("LOG_BREAKPOINTS") - 1; + bitmask |= LOG_BREAKPOINTS; + } + else if (strncmp (p, "LOG_EVENTS", sizeof ("LOG_EVENTS") - 1) == 0) + { + p += sizeof ("LOG_EVENTS") - 1; + bitmask |= LOG_EVENTS; + } + else if (strncmp (p, "LOG_WATCHPOINTS", sizeof ("LOG_WATCHPOINTS") - 1) == 0) + { + p += sizeof ("LOG_WATCHPOINTS") - 1; + bitmask |= LOG_WATCHPOINTS; + } + else if (strncmp (p, "LOG_STEP", sizeof ("LOG_STEP") - 1) == 0) + { + p += sizeof ("LOG_STEP") - 1; + bitmask |= LOG_STEP; + } + else if (strncmp (p, "LOG_TASK", sizeof ("LOG_TASK") - 1) == 0) + { + p += sizeof ("LOG_TASK") - 1; + bitmask |= LOG_TASK; + } + else if (strncmp (p, "LOG_ALL", sizeof ("LOG_ALL") - 1) == 0) + { + p += sizeof ("LOG_ALL") - 1; + bitmask |= LOG_ALL; + } + else if (strncmp (p, "LOG_DEFAULT", sizeof ("LOG_DEFAULT") - 1) == 0) + { + p += sizeof ("LOG_DEFAULT") - 1; + bitmask |= LOG_DEFAULT; + } +// end of auto-generated entries + + 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 && *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_QThreadSuffixSupported (const char *p) +{ + m_thread_suffix_supported = true; + return SendPacket ("OK"); +} + +rnb_err_t +RNBRemote::HandlePacket_QStartNoAckMode (const char *p) +{ + // Send the OK packet first so the correct checksum is appended... + rnb_err_t result = SendPacket ("OK"); + m_noack_mode = true; + return result; +} + + +rnb_err_t +RNBRemote::HandlePacket_QSetLogging (const char *p) +{ + p += sizeof ("QSetLogging:") - 1; + rnb_err_t result = set_logging (p); + if (result == rnb_success) + return SendPacket ("OK"); + else + return SendPacket ("E35"); +} + +rnb_err_t +RNBRemote::HandlePacket_QSetDisableASLR (const char *p) +{ + extern int g_disable_aslr; + p += sizeof ("QSetDisableASLR:") - 1; + switch (*p) + { + case '0': g_disable_aslr = 0; break; + case '1': g_disable_aslr = 1; break; + default: + return SendPacket ("E56"); + } + return SendPacket ("OK"); +} + +rnb_err_t +RNBRemote::HandlePacket_QSetSTDIO (const char *p) +{ + // Only set stdin/out/err if we don't already have a process + if (!m_ctx.HasValidProcessID()) + { + bool success = false; + // Check the seventh character since the packet will be one of: + // QSetSTDIN + // QSetSTDOUT + // QSetSTDERR + StringExtractor packet(p); + packet.SetFilePos (7); + char ch = packet.GetChar(); + while (packet.GetChar() != ':') + /* Do nothing. */; + + switch (ch) + { + case 'I': // STDIN + packet.GetHexByteString (m_ctx.GetSTDIN()); + success = !m_ctx.GetSTDIN().empty(); + break; + + case 'O': // STDOUT + packet.GetHexByteString (m_ctx.GetSTDOUT()); + success = !m_ctx.GetSTDOUT().empty(); + break; + + case 'E': // STDERR + packet.GetHexByteString (m_ctx.GetSTDERR()); + success = !m_ctx.GetSTDERR().empty(); + break; + + default: + break; + } + if (success) + return SendPacket ("OK"); + return SendPacket ("E57"); + } + return SendPacket ("E58"); +} + +rnb_err_t +RNBRemote::HandlePacket_QSetWorkingDir (const char *p) +{ + // Only set the working directory if we don't already have a process + if (!m_ctx.HasValidProcessID()) + { + StringExtractor packet(p += sizeof ("QSetWorkingDir:") - 1); + if (packet.GetHexByteString (m_ctx.GetWorkingDir())) + { + struct stat working_dir_stat; + if (::stat(m_ctx.GetWorkingDirPath(), &working_dir_stat) == -1) + { + m_ctx.GetWorkingDir().clear(); + return SendPacket ("E61"); // Working directory doesn't exist... + } + else if ((working_dir_stat.st_mode & S_IFMT) == S_IFDIR) + { + return SendPacket ("OK"); + } + else + { + m_ctx.GetWorkingDir().clear(); + return SendPacket ("E62"); // Working directory isn't a directory... + } + } + return SendPacket ("E59"); // Invalid path + } + return SendPacket ("E60"); // Already had a process, too late to set working dir +} + +rnb_err_t +RNBRemote::HandlePacket_QSyncThreadState (const char *p) +{ + 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. + return SendPacket ("OK"); + } + + errno = 0; + p += strlen("QSyncThreadState:"); + nub_thread_t tid = strtoul (p, NULL, 16); + if (errno != 0 && tid == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid thread number in QSyncThreadState packet"); + } + if (DNBProcessSyncThreadState(m_ctx.ProcessID(), tid)) + return SendPacket("OK"); + else + return SendPacket ("E61"); +} + +rnb_err_t +RNBRemote::HandlePacket_QSetDetachOnError (const char *p) +{ + p += sizeof ("QSetDetachOnError:") - 1; + bool should_detach = true; + switch (*p) + { + case '0': should_detach = false; break; + case '1': should_detach = true; break; + default: + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid value for QSetDetachOnError - should be 0 or 1"); + break; + } + + m_ctx.SetDetachOnError(should_detach); + return SendPacket ("OK"); +} + +rnb_err_t +RNBRemote::HandlePacket_QListThreadsInStopReply (const char *p) +{ + // If this packet is received, it allows us to send an extra key/value + // pair in the stop reply packets where we will list all of the thread IDs + // separated by commas: + // + // "threads:10a,10b,10c;" + // + // This will get included in the stop reply packet as something like: + // + // "T11thread:10a;00:00000000;01:00010203:threads:10a,10b,10c;" + // + // This can save two packets on each stop: qfThreadInfo/qsThreadInfo and + // speed things up a bit. + // + // Send the OK packet first so the correct checksum is appended... + rnb_err_t result = SendPacket ("OK"); + m_list_threads_in_stop_reply = true; + + return result; +} + + +rnb_err_t +RNBRemote::HandlePacket_QSetMaxPayloadSize (const char *p) +{ + /* 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. */ + p += sizeof ("QSetMaxPayloadSize:") - 1; + errno = 0; + uint32_t size = static_cast(strtoul (p, NULL, 16)); + if (errno != 0 && size == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in QSetMaxPayloadSize packet"); + } + m_max_payload_size = size; + return SendPacket ("OK"); +} + +rnb_err_t +RNBRemote::HandlePacket_QSetMaxPacketSize (const char *p) +{ + /* 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. */ + p += sizeof ("QSetMaxPacketSize:") - 1; + errno = 0; + uint32_t size = static_cast(strtoul (p, NULL, 16)); + if (errno != 0 && size == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in QSetMaxPacketSize packet"); + } + m_max_payload_size = size - 5; + return SendPacket ("OK"); +} + + + + +rnb_err_t +RNBRemote::HandlePacket_QEnvironment (const char *p) +{ + /* This sets the environment for the target program. The packet is of the form: + + QEnvironment:VARIABLE=VALUE + + */ + + 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"); +} + +rnb_err_t +RNBRemote::HandlePacket_QEnvironmentHexEncoded (const char *p) +{ + /* This sets the environment for the target program. The packet is of the form: + + QEnvironmentHexEncoded:VARIABLE=VALUE + + The VARIABLE=VALUE part is sent hex-encoded so characters like '#' with special + meaning in the remote protocol won't break it. + */ + + DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironmentHexEncoded: \"%s\"", + (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p); + + p += sizeof ("QEnvironmentHexEncoded:") - 1; + + std::string arg; + const char *c; + c = p; + while (*c != '\0') + { + if (*(c + 1) == '\0') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'QEnvironmentHexEncoded' pkt"); + } + char smallbuf[3]; + smallbuf[0] = *c; + smallbuf[1] = *(c + 1); + smallbuf[2] = '\0'; + errno = 0; + int ch = static_cast(strtoul (smallbuf, NULL, 16)); + if (errno != 0 && ch == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'QEnvironmentHexEncoded' pkt"); + } + arg.push_back(ch); + c += 2; + } + + RNBContext& ctx = Context(); + if (arg.length() > 0) + ctx.PushEnvironment (arg.c_str()); + + return SendPacket ("OK"); +} + + +rnb_err_t +RNBRemote::HandlePacket_QLaunchArch (const char *p) +{ + p += sizeof ("QLaunchArch:") - 1; + if (DNBSetArchitecture(p)) + return SendPacket ("OK"); + return SendPacket ("E63"); +} + +rnb_err_t +RNBRemote::HandlePacket_QSetProcessEvent (const char *p) +{ + p += sizeof ("QSetProcessEvent:") - 1; + // If the process is running, then send the event to the process, otherwise + // store it in the context. + if (Context().HasValidProcessID()) + { + if (DNBProcessSendEvent (Context().ProcessID(), p)) + return SendPacket("OK"); + else + return SendPacket ("E80"); + } + else + { + Context().PushProcessEvent(p); + } + return SendPacket ("OK"); +} + +void +append_hex_value (std::ostream& ostrm, const void *buf, size_t buf_size, bool swap) +{ + int i; + const uint8_t *p = (const uint8_t *)buf; + if (swap) + { + for (i = static_cast(buf_size)-1; i >= 0; i--) + ostrm << RAWHEX8(p[i]); + } + else + { + for (size_t i = 0; i < buf_size; i++) + ostrm << RAWHEX8(p[i]); + } +} + +void +append_hexified_string (std::ostream& ostrm, const std::string &string) +{ + size_t string_size = string.size(); + const char *string_buf = string.c_str(); + for (size_t i = 0; i < string_size; i++) + { + ostrm << RAWHEX8(*(string_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, + const DNBRegisterValue *reg_value_ptr) +{ + if (reg != NULL) + { + DNBRegisterValue reg_value; + if (reg_value_ptr == NULL) + { + if (DNBThreadGetRegisterValueByID (pid, tid, reg->nub_info.set, reg->nub_info.reg, ®_value)) + reg_value_ptr = ®_value; + } + + if (reg_value_ptr) + { + append_hex_value (ostrm, reg_value_ptr->value.v_uint8, reg->nub_info.size, false); + } + else + { + // If we fail to read a register 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->nub_info.size > 0) + { + std::basic_string zeros(reg->nub_info.size, '\0'); + append_hex_value (ostrm, zeros.data(), zeros.size(), false); + } + } + } +} + + +void +debugserver_regnum_with_fixed_width_hex_register_value (std::ostream& ostrm, + nub_process_t pid, + nub_thread_t tid, + const register_map_entry_t* reg, + const DNBRegisterValue *reg_value_ptr) +{ + // 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->debugserver_regnum) << ':'; + register_value_in_hex_fixed_width (ostrm, pid, tid, reg, reg_value_ptr); + ostrm << ';'; + } +} + + +void +RNBRemote::DispatchQueueOffsets::GetThreadQueueInfo (nub_process_t pid, + nub_addr_t dispatch_qaddr, + std::string &queue_name, + uint64_t &queue_width, + uint64_t &queue_serialnum) const +{ + queue_name.clear(); + queue_width = 0; + queue_serialnum = 0; + + if (IsValid() && dispatch_qaddr != INVALID_NUB_ADDRESS && dispatch_qaddr != 0) + { + nub_addr_t dispatch_queue_addr = DNBProcessMemoryReadPointer (pid, dispatch_qaddr); + if (dispatch_queue_addr) + { + queue_width = DNBProcessMemoryReadInteger (pid, dispatch_queue_addr + dqo_width, dqo_width_size, 0); + queue_serialnum = DNBProcessMemoryReadInteger (pid, dispatch_queue_addr + dqo_serialnum, dqo_serialnum_size, 0); + + if (dqo_version >= 4) + { + // libdispatch versions 4+, pointer to dispatch name is in the + // queue structure. + nub_addr_t pointer_to_label_address = dispatch_queue_addr + dqo_label; + nub_addr_t label_addr = DNBProcessMemoryReadPointer (pid, pointer_to_label_address); + if (label_addr) + queue_name = std::move(DNBProcessMemoryReadCString (pid, label_addr)); + } + else + { + // libdispatch versions 1-3, dispatch name is a fixed width char array + // in the queue structure. + queue_name = std::move(DNBProcessMemoryReadCStringFixed(pid, dispatch_queue_addr + dqo_label, dqo_label_size)); + } + } + } +} + +struct StackMemory +{ + uint8_t bytes[2*sizeof(nub_addr_t)]; + nub_size_t length; +}; +typedef std::map StackMemoryMap; + + +static void +ReadStackMemory (nub_process_t pid, nub_thread_t tid, StackMemoryMap &stack_mmap, uint32_t backtrace_limit = 256) +{ + DNBRegisterValue reg_value; + if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_FP, ®_value)) + { + uint32_t frame_count = 0; + uint64_t fp = 0; + if (reg_value.info.size == 4) + fp = reg_value.value.uint32; + else + fp = reg_value.value.uint64; + while (fp != 0) + { + // Make sure we never recurse more than 256 times so we don't recurse too far or + // store up too much memory in the expedited cache + if (++frame_count > backtrace_limit) + break; + + const nub_size_t read_size = reg_value.info.size*2; + StackMemory stack_memory; + stack_memory.length = read_size; + if (DNBProcessMemoryRead(pid, fp, read_size, stack_memory.bytes) != read_size) + break; + // Make sure we don't try to put the same stack memory in more than once + if (stack_mmap.find(fp) != stack_mmap.end()) + break; + // Put the entry into the cache + stack_mmap[fp] = stack_memory; + // Dereference the frame pointer to get to the previous frame pointer + if (reg_value.info.size == 4) + fp = ((uint32_t *)stack_memory.bytes)[0]; + else + fp = ((uint64_t *)stack_memory.bytes)[0]; + } + } +} + +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)) + { + const bool did_exec = tid_stop_info.reason == eStopTypeExec; + if (did_exec) + { + RNBRemote::InitializeRegisters(true); + + // Reset any symbols that need resetting when we exec + m_dispatch_queue_offsets_addr = INVALID_NUB_ADDRESS; + m_dispatch_queue_offsets.Clear(); + } + + 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__, signum, 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 = static_cast(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]) + { + size_t thread_name_len = strlen(thread_name); + + + if (::strcspn (thread_name, "$#+-;:") == thread_name_len) + ostrm << std::hex << "name:" << thread_name << ';'; + else + { + // the thread name contains special chars, send as hex bytes + ostrm << std::hex << "hexname:"; + uint8_t *u_thread_name = (uint8_t *)thread_name; + for (size_t i = 0; i < thread_name_len; i++) + ostrm << RAWHEX8(u_thread_name[i]); + ostrm << ';'; + } + } + + thread_identifier_info_data_t thread_ident_info; + if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info)) + { + if (thread_ident_info.dispatch_qaddr != 0) + { + ostrm << "qaddr:" << std::hex << thread_ident_info.dispatch_qaddr << ';'; + const DispatchQueueOffsets *dispatch_queue_offsets = GetDispatchQueueOffsets(); + if (dispatch_queue_offsets) + { + std::string queue_name; + uint64_t queue_width = 0; + uint64_t queue_serialnum = 0; + dispatch_queue_offsets->GetThreadQueueInfo(pid, thread_ident_info.dispatch_qaddr, queue_name, queue_width, queue_serialnum); + if (!queue_name.empty()) + { + ostrm << "qname:"; + append_hex_value(ostrm, queue_name.data(), queue_name.size(), false); + ostrm << ';'; + } + if (queue_width == 1) + ostrm << "qkind:serial;"; + else if (queue_width > 1) + ostrm << "qkind:concurrent;"; + + if (queue_serialnum > 0) + ostrm << "qserial:" << DECIMAL << queue_serialnum << ';'; + } + } + } + + // If a 'QListThreadsInStopReply' was sent to enable this feature, we + // will send all thread IDs back in the "threads" key whose value is + // a list of hex thread IDs separated by commas: + // "threads:10a,10b,10c;" + // This will save the debugger from having to send a pair of qfThreadInfo + // and qsThreadInfo packets, but it also might take a lot of room in the + // stop reply packet, so it must be enabled only on systems where there + // are no limits on packet lengths. + if (m_list_threads_in_stop_reply) + { + const nub_size_t numthreads = DNBProcessGetNumThreads (pid); + if (numthreads > 0) + { + std::vector pc_values; + ostrm << std::hex << "threads:"; + for (nub_size_t i = 0; i < numthreads; ++i) + { + nub_thread_t th = DNBProcessGetThreadAtIndex (pid, i); + if (i > 0) + ostrm << ','; + ostrm << std::hex << th; + DNBRegisterValue pc_regval; + if (DNBThreadGetRegisterValueByID (pid, th, REGISTER_SET_GENERIC, GENERIC_REGNUM_PC, &pc_regval)) + { + uint64_t pc = INVALID_NUB_ADDRESS; + if (pc_regval.value.uint64 != INVALID_NUB_ADDRESS) + { + if (pc_regval.info.size == 4) + { + pc = pc_regval.value.uint32; + } + else if (pc_regval.info.size == 8) + { + pc = pc_regval.value.uint64; + } + if (pc != INVALID_NUB_ADDRESS) + { + pc_values.push_back (pc); + } + } + } + } + ostrm << ';'; + + // If we failed to get any of the thread pc values, the size of our vector will not + // be the same as the # of threads. Don't provide any expedited thread pc values in + // that case. This should not happen. + if (pc_values.size() == numthreads) + { + ostrm << std::hex << "thread-pcs:"; + for (nub_size_t i = 0; i < numthreads; ++i) + { + if (i > 0) + ostrm << ','; + ostrm << std::hex << pc_values[i]; + } + ostrm << ';'; + } + } + + // Include JSON info that describes the stop reason for any threads + // that actually have stop reasons. We use the new "jstopinfo" key + // whose values is hex ascii JSON that contains the thread IDs + // thread stop info only for threads that have stop reasons. Only send + // this if we have more than one thread otherwise this packet has all + // the info it needs. + if (numthreads > 1) + { + const bool threads_with_valid_stop_info_only = true; + JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(threads_with_valid_stop_info_only); + if (threads_info_sp) + { + ostrm << std::hex << "jstopinfo:"; + std::ostringstream json_strm; + threads_info_sp->Dump (json_strm); + append_hexified_string (ostrm, json_strm.str()); + ostrm << ';'; + } + } + } + + + if (g_num_reg_entries == 0) + InitializeRegisters (); + + if (g_reg_entries != NULL) + { + DNBRegisterValue reg_value; + for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) + { + // Expedite all registers in the first register set that aren't + // contained in other registers + if (g_reg_entries[reg].nub_info.set == 1 && + g_reg_entries[reg].nub_info.value_regs == NULL) + { + if (!DNBThreadGetRegisterValueByID (pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, ®_value)) + continue; + + debugserver_regnum_with_fixed_width_hex_register_value (ostrm, pid, tid, &g_reg_entries[reg], ®_value); + } + } + } + + if (did_exec) + { + ostrm << "reason:exec;"; + } + else 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 (nub_size_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) + ostrm << "medata:" << std::hex << tid_stop_info.details.exception.data[i] << ';'; + } + + // Add expedited stack memory so stack backtracing doesn't need to read anything from the + // frame pointer chain. + StackMemoryMap stack_mmap; + ReadStackMemory (pid, tid, stack_mmap, 2); + if (!stack_mmap.empty()) + { + for (const auto &stack_memory : stack_mmap) + { + ostrm << "memory:" << HEXBASE << stack_memory.first << '='; + append_hex_value (ostrm, stack_memory.second.bytes, stack_memory.second.length, false); + ostrm << ';'; + } + } + + 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: + case eStateDetached: + 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'; + } + + const char *exit_info = DNBProcessGetExitInfo (pid); + if (exit_info != NULL && *exit_info != '\0') + { + std::ostringstream exit_packet; + exit_packet << pid_exited_packet; + exit_packet << ';'; + exit_packet << RAW_HEXBASE << "description"; + exit_packet << ':'; + for (size_t i = 0; exit_info[i] != '\0'; i++) + exit_packet << RAWHEX8(exit_info[i]); + exit_packet << ';'; + return SendPacket (exit_packet.str()); + } + else + 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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "Invalid address in M packet"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma sep missing in M packet"); + } + + /* Advance 'p' to the length part of the packet. */ + p += (c - p) + 1; + + errno = 0; + unsigned long length = strtoul (p, &c, 16); + if (errno != 0 && length == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in M packet"); + } + if (length == 0) + { + return SendPacket ("OK"); + } + + if (*c != ':') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Missing colon in M packet"); + } + /* Advance 'p' to the data part of the packet. */ + p += (c - p) + 1; + + size_t datalen = strlen (p); + if (datalen & 0x1) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "Invalid address in m packet"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma sep missing in m packet"); + } + + /* Advance 'p' to the length part of the packet. */ + p += (c - p) + 1; + + errno = 0; + auto length = strtoul (p, NULL, 16); + if (errno != 0 && length == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in m packet"); + } + if (length == 0) + { + return SendPacket (""); + } + + std::string buf(length, '\0'); + if (buf.empty()) + { + return SendPacket ("E78"); + } + nub_size_t bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID(), addr, buf.size(), &buf[0]); + 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 (unsigned long i = 0; i < length; i++) + ostrm << RAWHEX8(buf[i]); + return SendPacket (ostrm.str ()); +} + +// Read memory, sent it up as binary data. +// Usage: xADDR,LEN +// ADDR and LEN are both base 16. + +// Responds with 'OK' for zero-length request +// or +// +// DATA +// +// where DATA is the binary data payload. + +rnb_err_t +RNBRemote::HandlePacket_x (const char *p) +{ + if (p == NULL || p[0] == '\0' || strlen (p) < 3) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Too short X packet"); + } + + char *c; + p++; + errno = 0; + nub_addr_t addr = strtoull (p, &c, 16); + if (errno != 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid address in X packet"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma sep missing in X packet"); + } + + /* Advance 'p' to the number of bytes to be read. */ + p += (c - p) + 1; + + errno = 0; + auto length = strtoul (p, NULL, 16); + if (errno != 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in x packet"); + } + + // zero length read means this is a test of whether that packet is implemented or not. + if (length == 0) + { + return SendPacket ("OK"); + } + + std::vector buf (length); + + if (buf.capacity() != length) + { + return SendPacket ("E79"); + } + nub_size_t bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID(), addr, buf.size(), &buf[0]); + if (bytes_read == 0) + { + return SendPacket ("E80"); + } + + std::vector buf_quoted; + buf_quoted.reserve (bytes_read + 30); + for (nub_size_t i = 0; i < bytes_read; i++) + { + if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*') + { + buf_quoted.push_back(0x7d); + buf_quoted.push_back(buf[i] ^ 0x20); + } + else + { + buf_quoted.push_back(buf[i]); + } + } + length = buf_quoted.size(); + + std::ostringstream ostrm; + for (unsigned long i = 0; i < length; i++) + ostrm << buf_quoted[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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "Invalid address in X packet"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma sep missing in X packet"); + } + + /* Advance 'p' to the length part of the packet. NB this is the length of the packet + including any escaped chars. The data payload may be a little bit smaller after + decoding. */ + p += (c - p) + 1; + + errno = 0; + auto length = strtoul (p, NULL, 16); + if (errno != 0 && length == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid length in X 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"); + } + + if (g_num_reg_entries == 0) + InitializeRegisters (); + + nub_process_t pid = m_ctx.ProcessID (); + nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p + 1); + if (tid == INVALID_NUB_THREAD) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet"); + + // 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 ()); + } + } + return SendPacket ("E74"); +} + +/* '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"); + } + + if (g_num_reg_entries == 0) + InitializeRegisters (); + + StringExtractor packet(p); + packet.SetFilePos(1); // Skip the 'G' + + nub_process_t pid = m_ctx.ProcessID(); + nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p); + if (tid == INVALID_NUB_THREAD) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet"); + + // 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); + + const nub_size_t bytes_extracted = packet.GetHexBytes (®_ctx[0], reg_ctx.size(), 0xcc); + if (bytes_extracted == 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"); + } + else + { + DNBLogError("RNBRemote::HandlePacket_G(%s): extracted %llu of %llu bytes, size mismatch\n", p, (uint64_t)bytes_extracted, (uint64_t)reg_ctx_size); + return SendPacket ("E64"); + } + } + return SendPacket ("E65"); +} + +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 previously 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"); +} + + +// FORMAT: QSaveRegisterState;thread:TTTT; (when thread suffix is supported) +// FORMAT: QSaveRegisterState (when thread suffix is NOT supported) +// TTTT: thread ID in hex +// +// RESPONSE: +// SAVEID: Where SAVEID is a decimal number that represents the save ID +// that can be passed back into a "QRestoreRegisterState" packet +// EXX: error code +// +// EXAMPLES: +// QSaveRegisterState;thread:1E34; (when thread suffix is supported) +// QSaveRegisterState (when thread suffix is NOT supported) + +rnb_err_t +RNBRemote::HandlePacket_SaveRegisterState (const char *p) +{ + nub_process_t pid = m_ctx.ProcessID (); + nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p); + if (tid == INVALID_NUB_THREAD) + { + if (m_thread_suffix_supported) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in QSaveRegisterState packet"); + else + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread was is set with the Hg packet"); + } + + // Get the register context size first by calling with NULL buffer + const uint32_t save_id = DNBThreadSaveRegisterState(pid, tid); + if (save_id != 0) + { + char response[64]; + snprintf (response, sizeof(response), "%u", save_id); + return SendPacket (response); + } + else + { + return SendPacket ("E75"); + } +} +// FORMAT: QRestoreRegisterState:SAVEID;thread:TTTT; (when thread suffix is supported) +// FORMAT: QRestoreRegisterState:SAVEID (when thread suffix is NOT supported) +// TTTT: thread ID in hex +// SAVEID: a decimal number that represents the save ID that was +// returned from a call to "QSaveRegisterState" +// +// RESPONSE: +// OK: successfully restored registers for the specified thread +// EXX: error code +// +// EXAMPLES: +// QRestoreRegisterState:1;thread:1E34; (when thread suffix is supported) +// QRestoreRegisterState:1 (when thread suffix is NOT supported) + +rnb_err_t +RNBRemote::HandlePacket_RestoreRegisterState (const char *p) +{ + nub_process_t pid = m_ctx.ProcessID (); + nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p); + if (tid == INVALID_NUB_THREAD) + { + if (m_thread_suffix_supported) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in QSaveRegisterState packet"); + else + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread was is set with the Hg packet"); + } + + StringExtractor packet (p); + packet.SetFilePos(strlen("QRestoreRegisterState:")); // Skip the "QRestoreRegisterState:" + const uint32_t save_id = packet.GetU32(0); + + if (save_id != 0) + { + // Get the register context size first by calling with NULL buffer + if (DNBThreadRestoreRegisterState(pid, tid, save_id)) + return SendPacket ("OK"); + else + return SendPacket ("E77"); + } + return SendPacket ("E76"); +} + +static bool +GetProcessNameFrom_vAttach (const char *&p, std::string &attach_name) +{ + bool return_val = true; + while (*p != '\0') + { + char smallbuf[3]; + smallbuf[0] = *p; + smallbuf[1] = *(p + 1); + smallbuf[2] = '\0'; + + errno = 0; + int ch = static_cast(strtoul (smallbuf, NULL, 16)); + if (errno != 0 && ch == 0) + { + return_val = false; + break; + } + + attach_name.push_back(ch); + p += 2; + } + return return_val; +} + +rnb_err_t +RNBRemote::HandlePacket_qSupported (const char *p) +{ + uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet size--debugger can always use less + char buf[256]; + snprintf (buf, sizeof(buf), "qXfer:features:read+;PacketSize=%x;qEcho+", max_packet_size); + + // By default, don't enable compression. It's only worth doing when we are working + // with a low speed communication channel. + bool enable_compression = false; + (void)enable_compression; + + // Enable compression when debugserver is running on a watchOS device where communication may be over Bluetooth. +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + enable_compression = true; +#endif + +#if defined (HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is available + if (enable_compression && compression_decode_buffer != NULL) + { + strcat (buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;DefaultCompressionMinSize="); + char numbuf[16]; + snprintf (numbuf, sizeof (numbuf), "%zu", m_compression_minsize); + numbuf[sizeof (numbuf) - 1] = '\0'; + strcat (buf, numbuf); + } +#elif defined (HAVE_LIBZ) + if (enable_compression) + { + strcat (buf, ";SupportedCompressions=zlib-deflate;DefaultCompressionMinSize="); + char numbuf[16]; + snprintf (numbuf, sizeof (numbuf), "%zu", m_compression_minsize); + numbuf[sizeof (numbuf) - 1] = '\0'; + strcat (buf, numbuf); + } +#endif + + return SendPacket (buf); +} + +/* + 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) + { + 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 = static_cast(strtoul (c, &c, 16)); + if (errno != 0) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 = static_cast(strtoul (c, &c, 16)); + if (errno != 0) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse signal in vCont packet"); + // Fall through to next case... + + case 's': + // Step + thread_action.state = eStateStepping; + break; + + default: + HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Unsupported action in vCont packet"); + break; + } + if (*c == ':') + { + errno = 0; + thread_action.tid = strtoul (++c, &c, 16); + if (errno != 0) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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; // attach_pid will be set to 0 if the attach fails + nub_process_t pid_attaching_to = INVALID_NUB_PROCESS; // pid_attaching_to is the original pid specified + char err_str[1024]={'\0'}; + std::string attach_name; + + if (strstr (p, "vAttachWait;") == p) + { + p += strlen("vAttachWait;"); + if (!GetProcessNameFrom_vAttach(p, attach_name)) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'vAttachWait' pkt"); + } + const bool ignore_existing = true; + attach_pid = DNBProcessAttachWait(attach_name.c_str (), m_ctx.LaunchFlavor(), ignore_existing, NULL, 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback); + + } + else if (strstr (p, "vAttachOrWait;") == p) + { + p += strlen("vAttachOrWait;"); + if (!GetProcessNameFrom_vAttach(p, attach_name)) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'vAttachOrWait' pkt"); + } + const bool ignore_existing = false; + attach_pid = DNBProcessAttachWait(attach_name.c_str (), m_ctx.LaunchFlavor(), ignore_existing, NULL, 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback); + } + else if (strstr (p, "vAttachName;") == p) + { + p += strlen("vAttachName;"); + if (!GetProcessNameFrom_vAttach(p, attach_name)) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "non-hex char in arg on 'vAttachName' pkt"); + } + + attach_pid = DNBProcessAttachByName (attach_name.c_str(), NULL, err_str, sizeof(err_str)); + + } + else if (strstr (p, "vAttach;") == p) + { + p += strlen("vAttach;"); + char *end = NULL; + pid_attaching_to = static_cast(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(pid_attaching_to, &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"); + +#if defined (__APPLE__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000) + if (pid_attaching_to == INVALID_NUB_PROCESS && !attach_name.empty()) + { + pid_attaching_to = DNBProcessGetPIDByName (attach_name.c_str()); + } + if (pid_attaching_to != INVALID_NUB_PROCESS && strcmp (err_str, "No such process") != 0) + { + // csr_check(CSR_ALLOW_TASK_FOR_PID) will be nonzero if System Integrity Protection is in effect. + if (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) + { + bool attach_failed_due_to_sip = false; + + if (rootless_allows_task_for_pid (pid_attaching_to) == 0) + { + attach_failed_due_to_sip = true; + } + + if (attach_failed_due_to_sip == false) + { + int csops_flags = 0; + int retval = ::csops (pid_attaching_to, CS_OPS_STATUS, &csops_flags, sizeof (csops_flags)); + if (retval != -1 && (csops_flags & CS_RESTRICT)) + { + attach_failed_due_to_sip = true; + } + } + if (attach_failed_due_to_sip) + { + SendPacket ("E87"); // E87 is the magic value which says that we are not allowed to attach + DNBLogError ("Attach failed because process does not allow attaching: \"%s\".", err_str); + return rnb_err; + } + } + } + +#endif + + SendPacket ("E01"); // E01 is our magic error value for attach failed. + DNBLogError ("Attach failed: \"%s\".", err_str); + return rnb_err; + } + } + + // 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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "Invalid address in z packet"); + p = c; + if (*p++ != ',') + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Comma separator missing in z packet"); + + errno = 0; + auto byte_size = strtoul (p, &c, 16); + if (errno != 0 && byte_size == 0) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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'); + + if (DNBBreakpointSet (pid, addr, byte_size, hardware)) + { + // We successfully created a breakpoint, now lets full out + // a ref count structure with the breakID and add it to our + // map. + 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; + + if (DNBWatchpointSet (pid, addr, byte_size, watch_flags, hardware)) + { + 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 + if (DNBBreakpointClear (pid, addr)) + { + return SendPacket ("OK"); + } + else + { + return SendPacket ("E08"); + } + break; + + case '2': // remove write watchpoint + case '3': // remove read watchpoint + case '4': // remove access watchpoint + if (DNBWatchpointClear (pid, addr)) + { + return SendPacket ("OK"); + } + else + { + return SendPacket ("E08"); + } + break; + + default: + break; + } + } + return HandlePacket_UNIMPLEMENTED(p); +} + +// Extract the thread number from the thread suffix that might be appended to +// thread specific packets. This will only be enabled if m_thread_suffix_supported +// is true. +nub_thread_t +RNBRemote::ExtractThreadIDFromThreadSuffix (const char *p) +{ + if (m_thread_suffix_supported) + { + nub_thread_t tid = INVALID_NUB_THREAD; + if (p) + { + const char *tid_cstr = strstr (p, "thread:"); + if (tid_cstr) + { + tid_cstr += strlen ("thread:"); + tid = strtoul(tid_cstr, NULL, 16); + } + } + return tid; + } + return GetCurrentThread(); + +} + +/* 'p XX' + print the contents of register X */ + +rnb_err_t +RNBRemote::HandlePacket_p (const char *p) +{ + if (g_num_reg_entries == 0) + InitializeRegisters (); + + if (p == NULL || *p == '\0') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet"); + } + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E15"); + } + nub_process_t pid = m_ctx.ProcessID(); + errno = 0; + char *tid_cstr = NULL; + uint32_t reg = static_cast(strtoul (p + 1, &tid_cstr, 16)); + if (errno != 0 && reg == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Could not parse register number in p packet"); + } + + nub_thread_t tid = ExtractThreadIDFromThreadSuffix (tid_cstr); + if (tid == INVALID_NUB_THREAD) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified 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 == (uint32_t)-1) + { + if (reg_entry->nub_info.size > 0) + { + std::basic_string zeros(reg_entry->nub_info.size, '\0'); + append_hex_value(ostrm, zeros.data(), zeros.size(), false); + } + } + else + { + register_value_in_hex_fixed_width (ostrm, pid, tid, reg_entry, NULL); + } + 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 (g_num_reg_entries == 0) + InitializeRegisters (); + + if (p == NULL || *p == '\0') + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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 == (uint32_t)-1 && reg_entry->nub_info.reg == (uint32_t)-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->nub_info.size, 0xcc); + + nub_thread_t tid = ExtractThreadIDFromThreadSuffix (p); + if (tid == INVALID_NUB_THREAD) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "No thread specified in p packet"); + + 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 (__FILE__, __LINE__, p, "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; +} + +rnb_err_t +RNBRemote::HandlePacket_MemoryRegionInfo (const char *p) +{ + /* This packet will find memory attributes (e.g. readable, writable, executable, stack, jitted code) + for the memory region containing a given address and return that information. + + Users of this packet must be prepared for three results: + + Region information is returned + Region information is unavailable for this address because the address is in unmapped memory + Region lookup cannot be performed on this platform or process is not yet launched + This packet isn't implemented + + Examples of use: + qMemoryRegionInfo:3a55140 + start:3a50000,size:100000,permissions:rwx + + qMemoryRegionInfo:0 + error:address in unmapped region + + qMemoryRegionInfo:3a551140 (on a different platform) + error:region lookup cannot be performed + + qMemoryRegionInfo + OK // this packet is implemented by the remote nub + */ + + p += sizeof ("qMemoryRegionInfo") - 1; + if (*p == '\0') + return SendPacket ("OK"); + if (*p++ != ':') + return SendPacket ("E67"); + if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) + p += 2; + + errno = 0; + uint64_t address = strtoul (p, NULL, 16); + if (errno != 0 && address == 0) + { + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid address in qMemoryRegionInfo packet"); + } + + DNBRegionInfo region_info = { 0, 0, 0 }; + DNBProcessMemoryRegionInfo (m_ctx.ProcessID(), address, ®ion_info); + std::ostringstream ostrm; + + // start:3a50000,size:100000,permissions:rwx + ostrm << "start:" << std::hex << region_info.addr << ';'; + + if (region_info.size > 0) + ostrm << "size:" << std::hex << region_info.size << ';'; + + if (region_info.permissions) + { + ostrm << "permissions:"; + + if (region_info.permissions & eMemoryPermissionsReadable) + ostrm << 'r'; + if (region_info.permissions & eMemoryPermissionsWritable) + ostrm << 'w'; + if (region_info.permissions & eMemoryPermissionsExecutable) + ostrm << 'x'; + ostrm << ';'; + } + return SendPacket (ostrm.str()); +} + +// qGetProfileData;scan_type:0xYYYYYYY +rnb_err_t +RNBRemote::HandlePacket_GetProfileData (const char *p) +{ + nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket ("OK"); + + StringExtractor packet(p += sizeof ("qGetProfileData")); + DNBProfileDataScanType scan_type = eProfileAll; + std::string name; + std::string value; + while (packet.GetNameColonValue(name, value)) + { + if (name.compare ("scan_type") == 0) + { + std::istringstream iss(value); + uint32_t int_value = 0; + if (iss >> std::hex >> int_value) + { + scan_type = (DNBProfileDataScanType)int_value; + } + } + } + + std::string data = DNBProcessGetProfileData(pid, scan_type); + if (!data.empty()) + { + return SendPacket (data.c_str()); + } + else + { + return SendPacket ("OK"); + } +} + +// QSetEnableAsyncProfiling;enable:[0|1]:interval_usec:XXXXXX;scan_type:0xYYYYYYY +rnb_err_t +RNBRemote::HandlePacket_SetEnableAsyncProfiling (const char *p) +{ + nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket ("OK"); + + StringExtractor packet(p += sizeof ("QSetEnableAsyncProfiling")); + bool enable = false; + uint64_t interval_usec = 0; + DNBProfileDataScanType scan_type = eProfileAll; + std::string name; + std::string value; + while (packet.GetNameColonValue(name, value)) + { + if (name.compare ("enable") == 0) + { + enable = strtoul(value.c_str(), NULL, 10) > 0; + } + else if (name.compare ("interval_usec") == 0) + { + interval_usec = strtoul(value.c_str(), NULL, 10); + } + else if (name.compare ("scan_type") == 0) + { + std::istringstream iss(value); + uint32_t int_value = 0; + if (iss >> std::hex >> int_value) + { + scan_type = (DNBProfileDataScanType)int_value; + } + } + } + + if (interval_usec == 0) + { + enable = 0; + } + + DNBProcessSetEnableAsyncProfiling(pid, enable, interval_usec, scan_type); + return SendPacket ("OK"); +} + +// QEnableCompression:type:;minsize:; +// +// type: must be a type previously reported by the qXfer:features: SupportedCompressions list +// +// minsize: is optional; by default the qXfer:features: DefaultCompressionMinSize value is used +// debugserver may have a better idea of what a good minimum packet size to compress is than lldb. + +rnb_err_t +RNBRemote::HandlePacket_QEnableCompression (const char *p) +{ + p += sizeof ("QEnableCompression:") - 1; + + size_t new_compression_minsize = m_compression_minsize; + const char *new_compression_minsize_str = strstr (p, "minsize:"); + if (new_compression_minsize_str) + { + new_compression_minsize_str += strlen ("minsize:"); + errno = 0; + new_compression_minsize = strtoul (new_compression_minsize_str, NULL, 10); + if (errno != 0 || new_compression_minsize == ULONG_MAX) + { + new_compression_minsize = m_compression_minsize; + } + } + +#if defined (HAVE_LIBCOMPRESSION) + if (compression_decode_buffer != NULL) + { + if (strstr (p, "type:zlib-deflate;") != nullptr) + { + EnableCompressionNextSendPacket (compression_types::zlib_deflate); + m_compression_minsize = new_compression_minsize; + return SendPacket ("OK"); + } + else if (strstr (p, "type:lz4;") != nullptr) + { + EnableCompressionNextSendPacket (compression_types::lz4); + m_compression_minsize = new_compression_minsize; + return SendPacket ("OK"); + } + else if (strstr (p, "type:lzma;") != nullptr) + { + EnableCompressionNextSendPacket (compression_types::lzma); + m_compression_minsize = new_compression_minsize; + return SendPacket ("OK"); + } + else if (strstr (p, "type:lzfse;") != nullptr) + { + EnableCompressionNextSendPacket (compression_types::lzfse); + m_compression_minsize = new_compression_minsize; + return SendPacket ("OK"); + } + } +#endif + +#if defined (HAVE_LIBZ) + if (strstr (p, "type:zlib-deflate;") != nullptr) + { + EnableCompressionNextSendPacket (compression_types::zlib_deflate); + m_compression_minsize = new_compression_minsize; + return SendPacket ("OK"); + } +#endif + + return SendPacket ("E88"); +} + +rnb_err_t +RNBRemote::HandlePacket_qSpeedTest (const char *p) +{ + p += strlen ("qSpeedTest:response_size:"); + char *end = NULL; + errno = 0; + uint64_t response_size = ::strtoul (p, &end, 16); + if (errno != 0) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Didn't find response_size value at right offset"); + else if (*end == ';') + { + static char g_data[4*1024*1024+16] = "data:"; + memset(g_data + 5, 'a', response_size); + g_data[response_size + 5] = '\0'; + return SendPacket (g_data); + } + else + { + return SendPacket ("E79"); + } +} + +rnb_err_t +RNBRemote::HandlePacket_WatchpointSupportInfo (const char *p) +{ + /* This packet simply returns the number of supported hardware watchpoints. + + Examples of use: + qWatchpointSupportInfo: + num:4 + + qWatchpointSupportInfo + OK // this packet is implemented by the remote nub + */ + + p += sizeof ("qWatchpointSupportInfo") - 1; + if (*p == '\0') + return SendPacket ("OK"); + if (*p++ != ':') + return SendPacket ("E67"); + + errno = 0; + uint32_t num = DNBWatchpointGetNumSupportedHWP (m_ctx.ProcessID()); + std::ostringstream ostrm; + + // size:4 + ostrm << "num:" << std::dec << num << ';'; + return SendPacket (ostrm.str()); +} + +/* '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 = static_cast(strtoul (p + 1, &end, 16)); + if (errno != 0) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "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) +{ + if (m_ctx.HasValidProcessID()) + { + if (DNBProcessDetach(m_ctx.ProcessID())) + SendPacket ("OK"); + else + SendPacket ("E"); + } + else + { + SendPacket ("E"); + } + return rnb_success; +} + +/* 'k' + Kill the inferior process. */ + +rnb_err_t +RNBRemote::HandlePacket_k (const char *p) +{ + DNBLog ("Got a 'k' packet, killing the inferior process."); + // No response to should be sent to the kill packet + if (m_ctx.HasValidProcessID()) + DNBProcessKill (m_ctx.ProcessID()); + SendPacket ("X09"); + return rnb_success; +} + +rnb_err_t +RNBRemote::HandlePacket_stop_process (const char *p) +{ +//#define TEST_EXIT_ON_INTERRUPT // This should only be uncommented to test exiting on interrupt +#if defined(TEST_EXIT_ON_INTERRUPT) + rnb_err_t err = HandlePacket_k (p); + m_comm.Disconnect(true); + return err; +#else + if (!DNBProcessInterrupt(m_ctx.ProcessID())) + { + // If we failed to interrupt the process, then send a stop + // reply packet as the process was probably already stopped + HandlePacket_last_signal (NULL); + } + return rnb_success; +#endif +} + +/* '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 == (nub_thread_t)-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 = static_cast(strtoul (p + 1, &end, 16)); + if (errno != 0) + return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "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 (__FILE__, __LINE__, p, "Could not parse address in S packet"); + } + } + } + + action.tid = GetContinueThread (); + if (action.tid == 0 || action.tid == (nub_thread_t)-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; +} + +static const char * +GetArchName (const uint32_t cputype, const uint32_t cpusubtype) +{ + switch (cputype) + { + case CPU_TYPE_ARM: + switch (cpusubtype) + { + case 5: return "armv4"; + case 6: return "armv6"; + case 7: return "armv5t"; + case 8: return "xscale"; + case 9: return "armv7"; + case 10: return "armv7f"; + case 11: return "armv7s"; + case 12: return "armv7k"; + case 14: return "armv6m"; + case 15: return "armv7m"; + case 16: return "armv7em"; + default: return "arm"; + } + break; + case CPU_TYPE_ARM64: return "arm64"; + case CPU_TYPE_I386: return "i386"; + case CPU_TYPE_X86_64: + switch (cpusubtype) + { + default: return "x86_64"; + case 8: return "x86_64h"; + } + break; + } + return NULL; +} + +static bool +GetHostCPUType (uint32_t &cputype, uint32_t &cpusubtype, uint32_t &is_64_bit_capable, bool &promoted_to_64) +{ + static uint32_t g_host_cputype = 0; + static uint32_t g_host_cpusubtype = 0; + static uint32_t g_is_64_bit_capable = 0; + static bool g_promoted_to_64 = false; + + if (g_host_cputype == 0) + { + g_promoted_to_64 = false; + size_t len = sizeof(uint32_t); + if (::sysctlbyname("hw.cputype", &g_host_cputype, &len, NULL, 0) == 0) + { + len = sizeof (uint32_t); + if (::sysctlbyname("hw.cpu64bit_capable", &g_is_64_bit_capable, &len, NULL, 0) == 0) + { + if (g_is_64_bit_capable && ((g_host_cputype & CPU_ARCH_ABI64) == 0)) + { + g_promoted_to_64 = true; + g_host_cputype |= CPU_ARCH_ABI64; + } + } + } + + len = sizeof(uint32_t); + if (::sysctlbyname("hw.cpusubtype", &g_host_cpusubtype, &len, NULL, 0) == 0) + { + if (g_promoted_to_64 && + g_host_cputype == CPU_TYPE_X86_64 && g_host_cpusubtype == CPU_SUBTYPE_486) + g_host_cpusubtype = CPU_SUBTYPE_X86_64_ALL; + } + } + + cputype = g_host_cputype; + cpusubtype = g_host_cpusubtype; + is_64_bit_capable = g_is_64_bit_capable; + promoted_to_64 = g_promoted_to_64; + return g_host_cputype != 0; +} + +rnb_err_t +RNBRemote::HandlePacket_qHostInfo (const char *p) +{ + std::ostringstream strm; + + uint32_t cputype = 0; + uint32_t cpusubtype = 0; + uint32_t is_64_bit_capable = 0; + bool promoted_to_64 = false; + if (GetHostCPUType (cputype, cpusubtype, is_64_bit_capable, promoted_to_64)) + { + strm << "cputype:" << std::dec << cputype << ';'; + strm << "cpusubtype:" << std::dec << cpusubtype << ';'; + } + + // The OS in the triple should be "ios" or "macosx" which doesn't match our + // "Darwin" which gets returned from "kern.ostype", so we need to hardcode + // this for now. + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + { +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 + strm << "ostype:tvos;"; +#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + strm << "ostype:watchos;"; +#else + strm << "ostype:ios;"; +#endif + + // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes. + strm << "watchpoint_exceptions_received:before;"; + } + else + { + strm << "ostype:macosx;"; + strm << "watchpoint_exceptions_received:after;"; + } +// char ostype[64]; +// len = sizeof(ostype); +// if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0) +// { +// len = strlen(ostype); +// std::transform (ostype, ostype + len, ostype, tolower); +// strm << "ostype:" << std::dec << ostype << ';'; +// } + + strm << "vendor:apple;"; + + uint64_t major, minor, patch; + if (DNBGetOSVersionNumbers (&major, &minor, &patch)) + { + strm << "osmajor:" << major << ";"; + strm << "osminor:" << minor << ";"; + strm << "ospatch:" << patch << ";"; + + strm << "version:" << major << "." << minor; + if (patch != 0) + { + strm << "." << patch; + } + strm << ";"; + } + +#if defined (__LITTLE_ENDIAN__) + strm << "endian:little;"; +#elif defined (__BIG_ENDIAN__) + strm << "endian:big;"; +#elif defined (__PDP_ENDIAN__) + strm << "endian:pdp;"; +#endif + + if (promoted_to_64) + strm << "ptrsize:8;"; + else + strm << "ptrsize:" << std::dec << sizeof(void *) << ';'; + +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + strm << "default_packet_timeout:10;"; +#endif + + return SendPacket (strm.str()); +} + +void +XMLElementStart (std::ostringstream &s, uint32_t indent, const char *name, bool has_attributes) +{ + if (indent) + s << INDENT_WITH_SPACES(indent); + s << '<' << name; + if (!has_attributes) + s << '>' << std::endl; +} + +void +XMLElementStartEndAttributes (std::ostringstream &s, bool empty) +{ + if (empty) + s << '/'; + s << '>' << std::endl; +} + +void +XMLElementEnd (std::ostringstream &s, uint32_t indent, const char *name) +{ + if (indent) + s << INDENT_WITH_SPACES(indent); + s << '<' << '/' << name << '>' << std::endl; +} + +void +XMLElementWithStringValue (std::ostringstream &s, uint32_t indent, const char *name, const char *value, bool close = true) +{ + if (value) + { + if (indent) + s << INDENT_WITH_SPACES(indent); + s << '<' << name << '>' << value; + if (close) + XMLElementEnd(s, 0, name); + } +} + +void +XMLElementWithUnsignedValue (std::ostringstream &s, uint32_t indent, const char *name, uint64_t value, bool close = true) +{ + if (indent) + s << INDENT_WITH_SPACES(indent); + + s << '<' << name << '>' << DECIMAL << value; + if (close) + XMLElementEnd(s, 0, name); +} + +void +XMLAttributeString (std::ostringstream &s, const char *name, const char *value, const char *default_value = NULL) +{ + if (value) + { + if (default_value && strcmp(value, default_value) == 0) + return; // No need to emit the attribute because it matches the default value + s <<' ' << name << "=\"" << value << "\""; + } +} + +void +XMLAttributeUnsignedDecimal (std::ostringstream &s, const char *name, uint64_t value) +{ + s <<' ' << name << "=\"" << DECIMAL << value << "\""; +} + +void +GenerateTargetXMLRegister (std::ostringstream &s, + const uint32_t reg_num, + nub_size_t num_reg_sets, + const DNBRegisterSetInfo *reg_set_info, + const register_map_entry_t ®) +{ + const char *default_lldb_encoding = "uint"; + const char *lldb_encoding = default_lldb_encoding; + const char *gdb_group = "general"; + const char *default_gdb_type = "int"; + const char *gdb_type = default_gdb_type; + const char *default_lldb_format = "hex"; + const char *lldb_format = default_lldb_format; + const char *lldb_set = NULL; + + switch (reg.nub_info.type) + { + case Uint: lldb_encoding = "uint"; break; + case Sint: lldb_encoding = "sint"; break; + case IEEE754: lldb_encoding = "ieee754"; if (reg.nub_info.set > 0) gdb_group = "float"; break; + case Vector: lldb_encoding = "vector"; if (reg.nub_info.set > 0) gdb_group = "vector"; break; + } + + switch (reg.nub_info.format) + { + case Binary: lldb_format = "binary"; break; + case Decimal: lldb_format = "decimal"; break; + case Hex: lldb_format = "hex"; break; + case Float: gdb_type = "float"; lldb_format = "float"; break; + case VectorOfSInt8: gdb_type = "float"; lldb_format = "vector-sint8"; break; + case VectorOfUInt8: gdb_type = "float"; lldb_format = "vector-uint8"; break; + case VectorOfSInt16: gdb_type = "float"; lldb_format = "vector-sint16"; break; + case VectorOfUInt16: gdb_type = "float"; lldb_format = "vector-uint16"; break; + case VectorOfSInt32: gdb_type = "float"; lldb_format = "vector-sint32"; break; + case VectorOfUInt32: gdb_type = "float"; lldb_format = "vector-uint32"; break; + case VectorOfFloat32: gdb_type = "float"; lldb_format = "vector-float32"; break; + case VectorOfUInt128: gdb_type = "float"; lldb_format = "vector-uint128"; break; + }; + if (reg_set_info && reg.nub_info.set < num_reg_sets) + lldb_set = reg_set_info[reg.nub_info.set].name; + + uint32_t indent = 2; + + XMLElementStart(s, indent, "reg", true); + XMLAttributeString(s, "name", reg.nub_info.name); + XMLAttributeUnsignedDecimal(s, "regnum", reg_num); + XMLAttributeUnsignedDecimal(s, "offset", reg.offset); + XMLAttributeUnsignedDecimal(s, "bitsize", reg.nub_info.size * 8); + XMLAttributeString(s, "group", gdb_group); + XMLAttributeString(s, "type", gdb_type, default_gdb_type); + XMLAttributeString (s, "altname", reg.nub_info.alt); + XMLAttributeString(s, "encoding", lldb_encoding, default_lldb_encoding); + XMLAttributeString(s, "format", lldb_format, default_lldb_format); + XMLAttributeUnsignedDecimal(s, "group_id", reg.nub_info.set); + if (reg.nub_info.reg_ehframe != INVALID_NUB_REGNUM) + XMLAttributeUnsignedDecimal(s, "ehframe_regnum", reg.nub_info.reg_ehframe); + if (reg.nub_info.reg_dwarf != INVALID_NUB_REGNUM) + XMLAttributeUnsignedDecimal(s, "dwarf_regnum", reg.nub_info.reg_dwarf); + + const char *lldb_generic = NULL; + switch (reg.nub_info.reg_generic) + { + case GENERIC_REGNUM_FP: lldb_generic = "fp"; break; + case GENERIC_REGNUM_PC: lldb_generic = "pc"; break; + case GENERIC_REGNUM_SP: lldb_generic = "sp"; break; + case GENERIC_REGNUM_RA: lldb_generic = "ra"; break; + case GENERIC_REGNUM_FLAGS: lldb_generic = "flags"; break; + case GENERIC_REGNUM_ARG1: lldb_generic = "arg1"; break; + case GENERIC_REGNUM_ARG2: lldb_generic = "arg2"; break; + case GENERIC_REGNUM_ARG3: lldb_generic = "arg3"; break; + case GENERIC_REGNUM_ARG4: lldb_generic = "arg4"; break; + case GENERIC_REGNUM_ARG5: lldb_generic = "arg5"; break; + case GENERIC_REGNUM_ARG6: lldb_generic = "arg6"; break; + case GENERIC_REGNUM_ARG7: lldb_generic = "arg7"; break; + case GENERIC_REGNUM_ARG8: lldb_generic = "arg8"; break; + default: break; + } + XMLAttributeString(s, "generic", lldb_generic); + + + bool empty = reg.value_regnums.empty() && reg.invalidate_regnums.empty(); + if (!empty) + { + if (!reg.value_regnums.empty()) + { + std::ostringstream regnums; + bool first = true; + regnums << DECIMAL; + for (auto regnum : reg.value_regnums) + { + if (!first) + regnums << ','; + regnums << regnum; + first = false; + } + XMLAttributeString(s, "value_regnums", regnums.str().c_str()); + } + + if (!reg.invalidate_regnums.empty()) + { + std::ostringstream regnums; + bool first = true; + regnums << DECIMAL; + for (auto regnum : reg.invalidate_regnums) + { + if (!first) + regnums << ','; + regnums << regnum; + first = false; + } + XMLAttributeString(s, "invalidate_regnums", regnums.str().c_str()); + } + } + XMLElementStartEndAttributes(s, true); +} + +void +GenerateTargetXMLRegisters (std::ostringstream &s) +{ + nub_size_t num_reg_sets = 0; + const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo (&num_reg_sets); + + + uint32_t cputype = DNBGetRegisterCPUType(); + if (cputype) + { + XMLElementStart(s, 0, "feature", true); + std::ostringstream name_strm; + name_strm << "com.apple.debugserver." << GetArchName (cputype, 0); + XMLAttributeString(s, "name", name_strm.str().c_str()); + XMLElementStartEndAttributes(s, false); + for (uint32_t reg_num = 0; reg_num < g_num_reg_entries; ++reg_num) +// for (const auto ®: g_dynamic_register_map) + { + GenerateTargetXMLRegister(s, reg_num, num_reg_sets, reg_sets, g_reg_entries[reg_num]); + } + XMLElementEnd(s, 0, "feature"); + + if (num_reg_sets > 0) + { + XMLElementStart(s, 0, "groups", false); + for (uint32_t set=1; set +)"; + +static const char *g_target_xml_footer = ""; + +static std::string g_target_xml; + +void +UpdateTargetXML () +{ + std::ostringstream s; + s << g_target_xml_header << std::endl; + + // Set the architecture + //s << "" << arch "" << std::endl; + + // Set the OSABI + //s << "abi-name" + + GenerateTargetXMLRegisters(s); + + s << g_target_xml_footer << std::endl; + + // Save the XML output in case it gets retrieved in chunks + g_target_xml = s.str(); +} + +rnb_err_t +RNBRemote::HandlePacket_qXfer (const char *command) +{ + const char *p = command; + p += strlen ("qXfer:"); + const char *sep = strchr(p, ':'); + if (sep) + { + std::string object(p, sep - p); // "auxv", "backtrace", "features", etc + p = sep + 1; + sep = strchr(p, ':'); + if (sep) + { + std::string rw(p, sep - p); // "read" or "write" + p = sep + 1; + sep = strchr(p, ':'); + if (sep) + { + std::string annex(p, sep - p); // "read" or "write" + + p = sep + 1; + sep = strchr(p, ','); + if (sep) + { + std::string offset_str(p, sep - p); // read the length as a string + p = sep + 1; + std::string length_str(p); // read the offset as a string + char *end = nullptr; + const uint64_t offset = strtoul(offset_str.c_str(), &end, 16); // convert offset_str to a offset + if (*end == '\0') + { + const uint64_t length = strtoul(length_str.c_str(), &end, 16); // convert length_str to a length + if (*end == '\0') + { + if (object == "features" && + rw == "read" && + annex == "target.xml") + { + std::ostringstream xml_out; + + if (offset == 0) + { + InitializeRegisters (true); + + UpdateTargetXML(); + if (g_target_xml.empty()) + return SendPacket("E83"); + + if (length > g_target_xml.size()) + { + xml_out << 'l'; // No more data + xml_out << binary_encode_string(g_target_xml); + } + else + { + xml_out << 'm'; // More data needs to be read with a subsequent call + xml_out << binary_encode_string(std::string(g_target_xml, offset, length)); + } + } + else + { + // Retrieving target XML in chunks + if (offset < g_target_xml.size()) + { + std::string chunk(g_target_xml, offset, length); + if (chunk.size() < length) + xml_out << 'l'; // No more data + else + xml_out << 'm'; // More data needs to be read with a subsequent call + xml_out << binary_encode_string(chunk.data()); + } + } + return SendPacket(xml_out.str()); + } + // Well formed, put not supported + return HandlePacket_UNIMPLEMENTED (command); + } + } + } + } + else + { + SendPacket ("E85"); + } + } + else + { + SendPacket ("E86"); + } + } + return SendPacket ("E82"); +} + + +rnb_err_t +RNBRemote::HandlePacket_qGDBServerVersion (const char *p) +{ + std::ostringstream strm; + +#if defined(DEBUGSERVER_PROGRAM_NAME) + strm << "name:" DEBUGSERVER_PROGRAM_NAME ";"; +#else + strm << "name:debugserver;"; +#endif + strm << "version:" << DEBUGSERVER_VERSION_NUM << ";"; + + return SendPacket (strm.str()); +} + +// A helper function that retrieves a single integer value from +// a one-level-deep JSON dictionary of key-value pairs. e.g. +// jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}] +// +uint64_t +get_integer_value_for_key_name_from_json (const char *key, const char *json_string) +{ + uint64_t retval = INVALID_NUB_ADDRESS; + std::string key_with_quotes = "\""; + key_with_quotes += key; + key_with_quotes += "\""; + const char *c = strstr (json_string, key_with_quotes.c_str()); + if (c) + { + c += key_with_quotes.size(); + + while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) + c++; + + if (*c == ':') + { + c++; + + while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) + c++; + + errno = 0; + retval = strtoul (c, NULL, 10); + if (errno != 0) + { + retval = INVALID_NUB_ADDRESS; + } + } + } + return retval; + +} + +JSONGenerator::ObjectSP +RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only) +{ + JSONGenerator::ArraySP threads_array_sp; + if (m_ctx.HasValidProcessID()) + { + threads_array_sp.reset(new JSONGenerator::Array()); + + nub_process_t pid = m_ctx.ProcessID(); + + nub_size_t numthreads = DNBProcessGetNumThreads (pid); + for (nub_size_t i = 0; i < numthreads; ++i) + { + nub_thread_t tid = DNBProcessGetThreadAtIndex (pid, i); + + struct DNBThreadStopInfo tid_stop_info; + + const bool stop_info_valid = DNBThreadGetStopReason (pid, tid, &tid_stop_info); + + // If we are doing stop info only, then we only show threads that have a + // valid stop reason + if (threads_with_valid_stop_info_only) + { + if (!stop_info_valid || tid_stop_info.reason == eStopTypeInvalid) + continue; + } + + JSONGenerator::DictionarySP thread_dict_sp(new JSONGenerator::Dictionary()); + thread_dict_sp->AddIntegerItem("tid", tid); + + std::string reason_value("none"); + + if (stop_info_valid) + { + switch (tid_stop_info.reason) + { + case eStopTypeInvalid: + break; + + case eStopTypeSignal: + if (tid_stop_info.details.signal.signo != 0) + { + thread_dict_sp->AddIntegerItem("signal", tid_stop_info.details.signal.signo); + reason_value = "signal"; + } + break; + + case eStopTypeException: + if (tid_stop_info.details.exception.type != 0) + { + reason_value = "exception"; + thread_dict_sp->AddIntegerItem("metype", tid_stop_info.details.exception.type); + JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array()); + for (nub_size_t i=0; iAddItem(JSONGenerator::IntegerSP(new JSONGenerator::Integer(tid_stop_info.details.exception.data[i]))); + } + thread_dict_sp->AddItem("medata", medata_array_sp); + } + break; + + case eStopTypeExec: + reason_value = "exec"; + break; + } + } + + thread_dict_sp->AddStringItem("reason", reason_value); + + if (threads_with_valid_stop_info_only == false) + { + const char *thread_name = DNBThreadGetName (pid, tid); + if (thread_name && thread_name[0]) + thread_dict_sp->AddStringItem("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) + { + thread_dict_sp->AddIntegerItem("qaddr", thread_ident_info.dispatch_qaddr); + + const DispatchQueueOffsets *dispatch_queue_offsets = GetDispatchQueueOffsets(); + if (dispatch_queue_offsets) + { + std::string queue_name; + uint64_t queue_width = 0; + uint64_t queue_serialnum = 0; + dispatch_queue_offsets->GetThreadQueueInfo(pid, thread_ident_info.dispatch_qaddr, queue_name, queue_width, queue_serialnum); + if (!queue_name.empty()) + thread_dict_sp->AddStringItem("qname", queue_name); + if (queue_width == 1) + thread_dict_sp->AddStringItem("qkind", "serial"); + else if (queue_width > 1) + thread_dict_sp->AddStringItem("qkind", "concurrent"); + if (queue_serialnum > 0) + thread_dict_sp->AddIntegerItem("qserial", queue_serialnum); + } + } + } + + DNBRegisterValue reg_value; + + if (g_reg_entries != NULL) + { + JSONGenerator::DictionarySP registers_dict_sp(new JSONGenerator::Dictionary()); + + for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) + { + // Expedite all registers in the first register set that aren't + // contained in other registers + if (g_reg_entries[reg].nub_info.set == 1 && + g_reg_entries[reg].nub_info.value_regs == NULL) + { + if (!DNBThreadGetRegisterValueByID (pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, ®_value)) + continue; + + std::ostringstream reg_num; + reg_num << std::dec << g_reg_entries[reg].debugserver_regnum; + // Encode native byte ordered bytes as hex ascii + registers_dict_sp->AddBytesAsHexASCIIString(reg_num.str(), reg_value.value.v_uint8, g_reg_entries[reg].nub_info.size); + } + } + thread_dict_sp->AddItem("registers", registers_dict_sp); + } + + // Add expedited stack memory so stack backtracing doesn't need to read anything from the + // frame pointer chain. + StackMemoryMap stack_mmap; + ReadStackMemory (pid, tid, stack_mmap); + if (!stack_mmap.empty()) + { + JSONGenerator::ArraySP memory_array_sp(new JSONGenerator::Array()); + + for (const auto &stack_memory : stack_mmap) + { + JSONGenerator::DictionarySP stack_memory_sp(new JSONGenerator::Dictionary()); + stack_memory_sp->AddIntegerItem("address", stack_memory.first); + stack_memory_sp->AddBytesAsHexASCIIString("bytes", stack_memory.second.bytes, stack_memory.second.length); + memory_array_sp->AddItem(stack_memory_sp); + } + thread_dict_sp->AddItem("memory", memory_array_sp); + } + } + + threads_array_sp->AddItem(thread_dict_sp); + } + } + return threads_array_sp; +} + +rnb_err_t +RNBRemote::HandlePacket_jThreadsInfo (const char *p) +{ + JSONGenerator::ObjectSP threads_info_sp; + std::ostringstream json; + std::ostringstream reply_strm; + // If we haven't run the process yet, return an error. + if (m_ctx.HasValidProcessID()) + { + const bool threads_with_valid_stop_info_only = false; + JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(threads_with_valid_stop_info_only); + + if (threads_info_sp) + { + std::ostringstream strm; + threads_info_sp->Dump (strm); + std::string binary_packet = binary_encode_string (strm.str()); + if (!binary_packet.empty()) + return SendPacket (binary_packet.c_str()); + } + } + return SendPacket ("E85"); + +} + +rnb_err_t +RNBRemote::HandlePacket_jThreadExtendedInfo (const char *p) +{ + nub_process_t pid; + std::ostringstream json; + // If we haven't run the process yet, return an error. + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E81"); + } + + pid = m_ctx.ProcessID(); + + const char thread_extended_info_str[] = { "jThreadExtendedInfo:{" }; + if (strncmp (p, thread_extended_info_str, sizeof (thread_extended_info_str) - 1) == 0) + { + p += strlen (thread_extended_info_str); + + uint64_t tid = get_integer_value_for_key_name_from_json ("thread", p); + uint64_t plo_pthread_tsd_base_address_offset = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_base_address_offset", p); + uint64_t plo_pthread_tsd_base_offset = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_base_offset", p); + uint64_t plo_pthread_tsd_entry_size = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_entry_size", p); + uint64_t dti_qos_class_index = get_integer_value_for_key_name_from_json ("dti_qos_class_index", p); + // Commented out the two variables below as they are not being used +// uint64_t dti_queue_index = get_integer_value_for_key_name_from_json ("dti_queue_index", p); +// uint64_t dti_voucher_index = get_integer_value_for_key_name_from_json ("dti_voucher_index", p); + + if (tid != INVALID_NUB_ADDRESS) + { + nub_addr_t pthread_t_value = DNBGetPThreadT (pid, tid); + + uint64_t tsd_address = INVALID_NUB_ADDRESS; + if (plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS + && plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS + && plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS) + { + tsd_address = DNBGetTSDAddressForThread (pid, tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); + } + + bool timed_out = false; + Genealogy::ThreadActivitySP thread_activity_sp; + + // If the pthread_t value is invalid, or if we were able to fetch the thread's TSD base + // and got an invalid value back, then we have a thread in early startup or shutdown and + // it's possible that gathering the genealogy information for this thread go badly. + // Ideally fetching this info for a thread in these odd states shouldn't matter - but + // we've seen some problems with these new SPI and threads in edge-casey states. + + double genealogy_fetch_time = 0; + if (pthread_t_value != INVALID_NUB_ADDRESS && tsd_address != INVALID_NUB_ADDRESS) + { + DNBTimer timer(false); + thread_activity_sp = DNBGetGenealogyInfoForThread (pid, tid, timed_out); + genealogy_fetch_time = timer.ElapsedMicroSeconds(false) / 1000000.0; + } + + std::unordered_set process_info_indexes; // an array of the process info #'s seen + + json << "{"; + + bool need_to_print_comma = false; + + if (thread_activity_sp && timed_out == false) + { + const Genealogy::Activity *activity = &thread_activity_sp->current_activity; + bool need_vouchers_comma_sep = false; + json << "\"activity_query_timed_out\":false,"; + if (genealogy_fetch_time != 0) + { + // If we append the floating point value with << we'll get it in scientific + // notation. + char floating_point_ascii_buffer[64]; + floating_point_ascii_buffer[0] = '\0'; + snprintf (floating_point_ascii_buffer, sizeof (floating_point_ascii_buffer), "%f", genealogy_fetch_time); + if (strlen (floating_point_ascii_buffer) > 0) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"activity_query_duration\":" << floating_point_ascii_buffer; + } + } + if (activity->activity_id != 0) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + need_vouchers_comma_sep = true; + json << "\"activity\":{"; + json << "\"start\":" << activity->activity_start << ","; + json << "\"id\":" << activity->activity_id << ","; + json << "\"parent_id\":" << activity->parent_id << ","; + json << "\"name\":\"" << json_string_quote_metachars (activity->activity_name) << "\","; + json << "\"reason\":\"" << json_string_quote_metachars (activity->reason) << "\""; + json << "}"; + } + if (thread_activity_sp->messages.size() > 0) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"trace_messages\":["; + bool printed_one_message = false; + for (auto iter = thread_activity_sp->messages.begin() ; iter != thread_activity_sp->messages.end(); ++iter) + { + if (printed_one_message) + json << ","; + else + printed_one_message = true; + json << "{"; + json << "\"timestamp\":" << iter->timestamp << ","; + json << "\"activity_id\":" << iter->activity_id << ","; + json << "\"trace_id\":" << iter->trace_id << ","; + json << "\"thread\":" << iter->thread << ","; + json << "\"type\":" << (int) iter->type << ","; + json << "\"process_info_index\":" << iter->process_info_index << ","; + process_info_indexes.insert (iter->process_info_index); + json << "\"message\":\"" << json_string_quote_metachars (iter->message) << "\""; + json << "}"; + } + json << "]"; + } + if (thread_activity_sp->breadcrumbs.size() == 1) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"breadcrumb\":{"; + for (auto iter = thread_activity_sp->breadcrumbs.begin() ; iter != thread_activity_sp->breadcrumbs.end(); ++iter) + { + json << "\"breadcrumb_id\":" << iter->breadcrumb_id << ","; + json << "\"activity_id\":" << iter->activity_id << ","; + json << "\"timestamp\":" << iter->timestamp << ","; + json << "\"name\":\"" << json_string_quote_metachars (iter->name) << "\""; + } + json << "}"; + } + if (process_info_indexes.size() > 0) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"process_infos\":["; + bool printed_one_process_info = false; + for (auto iter = process_info_indexes.begin(); iter != process_info_indexes.end(); ++iter) + { + if (printed_one_process_info) + json << ","; + else + printed_one_process_info = true; + Genealogy::ProcessExecutableInfoSP image_info_sp; + uint32_t idx = *iter; + image_info_sp = DNBGetGenealogyImageInfo (pid, idx); + json << "{"; + char uuid_buf[37]; + uuid_unparse_upper (image_info_sp->image_uuid, uuid_buf); + json << "\"process_info_index\":" << idx << ","; + json << "\"image_path\":\"" << json_string_quote_metachars (image_info_sp->image_path) << "\","; + json << "\"image_uuid\":\"" << uuid_buf <<"\""; + json << "}"; + } + json << "]"; + } + } + else + { + if (timed_out) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"activity_query_timed_out\":true"; + if (genealogy_fetch_time != 0) + { + // If we append the floating point value with << we'll get it in scientific + // notation. + char floating_point_ascii_buffer[64]; + floating_point_ascii_buffer[0] = '\0'; + snprintf (floating_point_ascii_buffer, sizeof (floating_point_ascii_buffer), "%f", genealogy_fetch_time); + if (strlen (floating_point_ascii_buffer) > 0) + { + json << ","; + json << "\"activity_query_duration\":" << floating_point_ascii_buffer; + } + } + } + } + + if (tsd_address != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"tsd_address\":" << tsd_address; + + if (dti_qos_class_index != 0 && dti_qos_class_index != UINT64_MAX) + { + ThreadInfo::QoS requested_qos = DNBGetRequestedQoSForThread (pid, tid, tsd_address, dti_qos_class_index); + if (requested_qos.IsValid()) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"requested_qos\":{"; + json << "\"enum_value\":" << requested_qos.enum_value << ","; + json << "\"constant_name\":\"" << json_string_quote_metachars (requested_qos.constant_name) << "\","; + json << "\"printable_name\":\"" << json_string_quote_metachars (requested_qos.printable_name) << "\""; + json << "}"; + } + } + } + + if (pthread_t_value != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"pthread_t\":" << pthread_t_value; + } + + nub_addr_t dispatch_queue_t_value = DNBGetDispatchQueueT (pid, tid); + if (dispatch_queue_t_value != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"dispatch_queue_t\":" << dispatch_queue_t_value; + } + + json << "}"; + std::string json_quoted = binary_encode_string (json.str()); + return SendPacket (json_quoted); + } + } + return SendPacket ("OK"); +} + +rnb_err_t +RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos (const char *p) +{ + nub_process_t pid; + // If we haven't run the process yet, return an error. + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E83"); + } + + pid = m_ctx.ProcessID(); + + const char get_loaded_dynamic_libraries_infos_str[] = { "jGetLoadedDynamicLibrariesInfos:{" }; + if (strncmp (p, get_loaded_dynamic_libraries_infos_str, sizeof (get_loaded_dynamic_libraries_infos_str) - 1) == 0) + { + p += strlen (get_loaded_dynamic_libraries_infos_str); + + nub_addr_t image_list_address = get_integer_value_for_key_name_from_json ("image_list_address", p); + nub_addr_t image_count = get_integer_value_for_key_name_from_json ("image_count", p); + + if (image_list_address != INVALID_NUB_ADDRESS && image_count != INVALID_NUB_ADDRESS) + { + JSONGenerator::ObjectSP json_sp; + + json_sp = DNBGetLoadedDynamicLibrariesInfos (pid, image_list_address, image_count); + + if (json_sp.get()) + { + std::ostringstream json_str; + json_sp->Dump (json_str); + if (json_str.str().size() > 0) + { + std::string json_str_quoted = binary_encode_string (json_str.str()); + return SendPacket (json_str_quoted.c_str()); + } + else + { + SendPacket ("E84"); + } + } + } + } + return SendPacket ("OK"); +} + +static bool +MachHeaderIsMainExecutable (nub_process_t pid, uint32_t addr_size, nub_addr_t mach_header_addr, mach_header &mh) +{ + DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx)", pid, addr_size, mach_header_addr); + const nub_size_t bytes_read = DNBProcessMemoryRead(pid, mach_header_addr, sizeof(mh), &mh); + if (bytes_read == sizeof(mh)) + { + DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx): mh = {\n magic = 0x%8.8x\n cpu = 0x%8.8x\n sub = 0x%8.8x\n filetype = %u\n ncmds = %u\n sizeofcmds = 0x%8.8x\n flags = 0x%8.8x }", pid, addr_size, mach_header_addr, mh.magic, mh.cputype, mh.cpusubtype, mh.filetype, mh.ncmds, mh.sizeofcmds, mh.flags); + if ((addr_size == 4 && mh.magic == MH_MAGIC) || + (addr_size == 8 && mh.magic == MH_MAGIC_64)) + { + if (mh.filetype == MH_EXECUTE) + { + DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx) -> this is the executable!!!", pid, addr_size, mach_header_addr); + return true; + } + } + } + return false; +} + +static nub_addr_t +GetMachHeaderForMainExecutable (const nub_process_t pid, const uint32_t addr_size, mach_header &mh) +{ + struct AllImageInfos + { + uint32_t version; + uint32_t dylib_info_count; + uint64_t dylib_info_addr; + }; + + uint64_t mach_header_addr = 0; + + const nub_addr_t shlib_addr = DNBProcessGetSharedLibraryInfoAddress (pid); + uint8_t bytes[256]; + nub_size_t bytes_read = 0; + DNBDataRef data (bytes, sizeof(bytes), false); + DNBDataRef::offset_t offset = 0; + data.SetPointerSize(addr_size); + + //---------------------------------------------------------------------- + // When we are sitting at __dyld_start, the kernel has placed the + // address of the mach header of the main executable on the stack. If we + // read the SP and dereference a pointer, we might find the mach header + // for the executable. We also just make sure there is only 1 thread + // since if we are at __dyld_start we shouldn't have multiple threads. + //---------------------------------------------------------------------- + if (DNBProcessGetNumThreads(pid) == 1) + { + nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, 0); + if (tid != INVALID_NUB_THREAD) + { + DNBRegisterValue sp_value; + if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_SP, &sp_value)) + { + uint64_t sp = addr_size == 8 ? sp_value.value.uint64 : sp_value.value.uint32; + bytes_read = DNBProcessMemoryRead(pid, sp, addr_size, bytes); + if (bytes_read == addr_size) + { + offset = 0; + mach_header_addr = data.GetPointer(&offset); + if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh)) + return mach_header_addr; + } + } + } + } + + //---------------------------------------------------------------------- + // Check the dyld_all_image_info structure for a list of mach header + // since it is a very easy thing to check + //---------------------------------------------------------------------- + if (shlib_addr != INVALID_NUB_ADDRESS) + { + bytes_read = DNBProcessMemoryRead(pid, shlib_addr, sizeof(AllImageInfos), bytes); + if (bytes_read > 0) + { + AllImageInfos aii; + offset = 0; + aii.version = data.Get32(&offset); + aii.dylib_info_count = data.Get32(&offset); + if (aii.dylib_info_count > 0) + { + aii.dylib_info_addr = data.GetPointer(&offset); + if (aii.dylib_info_addr != 0) + { + const size_t image_info_byte_size = 3 * addr_size; + for (uint32_t i=0; i p) + symbol_value_str.assign(p, sep - p); + p = sep + 1; + + if (*p) + { + // We have a symbol name + symbol_name = std::move(decode_hex_ascii_string(p)); + if (!symbol_value_str.empty()) + { + nub_addr_t symbol_value = decode_uint64(symbol_value_str.c_str(), 16); + if (symbol_name == "dispatch_queue_offsets") + m_dispatch_queue_offsets_addr = symbol_value; + } + ++m_qSymbol_index; + } + else + { + // No symbol name, set our symbol index to zero so we can + // read any symbols that we need + m_qSymbol_index = 0; + } + + symbol_name.clear(); + + if (m_qSymbol_index == 0) + { + if (m_dispatch_queue_offsets_addr == INVALID_NUB_ADDRESS) + symbol_name = "dispatch_queue_offsets"; + else + ++m_qSymbol_index; + } + +// // Lookup next symbol when we have one... +// if (m_qSymbol_index == 1) +// { +// } + + + if (symbol_name.empty()) + { + // Done with symbol lookups + return SendPacket ("OK"); + } + else + { + std::ostringstream reply; + reply << "qSymbol:"; + for (size_t i = 0; i < symbol_name.size(); ++i) + reply << RAWHEX8(symbol_name[i]); + return SendPacket (reply.str().c_str()); + } +} + +// Note that all numeric values returned by qProcessInfo are hex encoded, +// including the pid and the cpu type. + +rnb_err_t +RNBRemote::HandlePacket_qProcessInfo (const char *p) +{ + nub_process_t pid; + std::ostringstream rep; + + // If we haven't run the process yet, return an error. + if (!m_ctx.HasValidProcessID()) + return SendPacket ("E68"); + + pid = m_ctx.ProcessID(); + + rep << "pid:" << std::hex << pid << ';'; + + int procpid_mib[4]; + procpid_mib[0] = CTL_KERN; + procpid_mib[1] = KERN_PROC; + procpid_mib[2] = KERN_PROC_PID; + procpid_mib[3] = pid; + struct kinfo_proc proc_kinfo; + size_t proc_kinfo_size = sizeof(struct kinfo_proc); + + if (::sysctl (procpid_mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) + { + if (proc_kinfo_size > 0) + { + rep << "parent-pid:" << std::hex << proc_kinfo.kp_eproc.e_ppid << ';'; + rep << "real-uid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_ruid << ';'; + rep << "real-gid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_rgid << ';'; + rep << "effective-uid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_uid << ';'; + if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + rep << "effective-gid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_groups[0] << ';'; + } + } + + cpu_type_t cputype = DNBProcessGetCPUType (pid); + if (cputype == 0) + { + DNBLog ("Unable to get the process cpu_type, making a best guess."); + cputype = best_guess_cpu_type(); + } + + uint32_t addr_size = 0; + if (cputype != 0) + { + rep << "cputype:" << std::hex << cputype << ";"; + if (cputype & CPU_ARCH_ABI64) + addr_size = 8; + else + addr_size = 4; + } + + bool host_cpu_is_64bit = false; + uint32_t is64bit_capable; + size_t is64bit_capable_len = sizeof (is64bit_capable); + if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, &is64bit_capable_len, NULL, 0) == 0) + host_cpu_is_64bit = is64bit_capable != 0; + + uint32_t cpusubtype; + size_t cpusubtype_len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &cpusubtype_len, NULL, 0) == 0) + { + // If a process is CPU_TYPE_X86, then ignore the cpusubtype that we detected + // from the host and use CPU_SUBTYPE_I386_ALL because we don't want the + // CPU_SUBTYPE_X86_ARCH1 or CPU_SUBTYPE_X86_64_H to be used as the cpu subtype + // for i386... + if (host_cpu_is_64bit) + { + if (cputype == CPU_TYPE_X86) + { + cpusubtype = 3; // CPU_SUBTYPE_I386_ALL + } + else if (cputype == CPU_TYPE_ARM) + { + // We can query a process' cputype but we cannot query a process' cpusubtype. + // If the process has cputype CPU_TYPE_ARM, then it is an armv7 (32-bit process) and we + // need to override the host cpusubtype (which is in the CPU_SUBTYPE_ARM64 subtype namespace) + // with a reasonable CPU_SUBTYPE_ARMV7 subtype. + cpusubtype = 11; // CPU_SUBTYPE_ARM_V7S + } + } + rep << "cpusubtype:" << std::hex << cpusubtype << ';'; + } + + bool os_handled = false; + if (addr_size > 0) + { + rep << "ptrsize:" << std::dec << addr_size << ';'; + +#if (defined (__x86_64__) || defined (__i386__)) + // Try and get the OS type by looking at the load commands in the main + // executable and looking for a LC_VERSION_MIN load command. This is the + // most reliable way to determine the "ostype" value when on desktop. + + mach_header mh; + nub_addr_t exe_mach_header_addr = GetMachHeaderForMainExecutable (pid, addr_size, mh); + if (exe_mach_header_addr != INVALID_NUB_ADDRESS) + { + uint64_t load_command_addr = exe_mach_header_addr + ((addr_size == 8) ? sizeof(mach_header_64) : sizeof(mach_header)); + load_command lc; + for (uint32_t i=0; i 'ostype:ios;'"); + break; + + case LC_VERSION_MIN_MACOSX: + os_handled = true; + rep << "ostype:macosx;"; + DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_MACOSX -> 'ostype:macosx;'"); + break; + +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 + case LC_VERSION_MIN_TVOS: + os_handled = true; + rep << "ostype:tvos;"; + DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_TVOS -> 'ostype:tvos;'"); + break; +#endif + +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + case LC_VERSION_MIN_WATCHOS: + os_handled = true; + rep << "ostype:watchos;"; + DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_WATCHOS -> 'ostype:watchos;'"); + break; +#endif + + default: + break; + } + load_command_addr = load_command_addr + lc.cmdsize; + } + } +#endif + } + + // If we weren't able to find the OS in a LC_VERSION_MIN load command, try + // to set it correctly by using the cpu type and other tricks + if (!os_handled) + { + // The OS in the triple should be "ios" or "macosx" which doesn't match our + // "Darwin" which gets returned from "kern.ostype", so we need to hardcode + // this for now. + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + { +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 + rep << "ostype:tvos;"; +#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + rep << "ostype:watchos;"; +#else + rep << "ostype:ios;"; +#endif + } + else + { + bool is_ios_simulator = false; + if (cputype == CPU_TYPE_X86 || cputype == CPU_TYPE_X86_64) + { + // Check for iOS simulator binaries by getting the process argument + // and environment and checking for SIMULATOR_UDID in the environment + int proc_args_mib[3] = { CTL_KERN, KERN_PROCARGS2, (int)pid }; + + uint8_t arg_data[8192]; + size_t arg_data_size = sizeof(arg_data); + if (::sysctl (proc_args_mib, 3, arg_data, &arg_data_size , NULL, 0) == 0) + { + DNBDataRef data (arg_data, arg_data_size, false); + DNBDataRef::offset_t offset = 0; + uint32_t argc = data.Get32 (&offset); + const char *cstr; + + cstr = data.GetCStr (&offset); + if (cstr) + { + // Skip NULLs + while (1) + { + const char *p = data.PeekCStr(offset); + if ((p == NULL) || (*p != '\0')) + break; + ++offset; + } + // Now skip all arguments + for (uint32_t i = 0; i < argc; ++i) + { + data.GetCStr(&offset); + } + + // Now iterate across all environment variables + while ((cstr = data.GetCStr(&offset))) + { + if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0) + { + is_ios_simulator = true; + break; + } + if (cstr[0] == '\0') + break; + + } + } + } + } + if (is_ios_simulator) + { +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 + rep << "ostype:tvos;"; +#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + rep << "ostype:watchos;"; +#else + rep << "ostype:ios;"; +#endif + } + else + { + rep << "ostype:macosx;"; + } + } + } + + rep << "vendor:apple;"; + +#if defined (__LITTLE_ENDIAN__) + rep << "endian:little;"; +#elif defined (__BIG_ENDIAN__) + rep << "endian:big;"; +#elif defined (__PDP_ENDIAN__) + rep << "endian:pdp;"; +#endif + + if (addr_size == 0) + { +#if (defined (__x86_64__) || defined (__i386__)) && defined (x86_THREAD_STATE) + nub_thread_t thread = DNBProcessGetCurrentThreadMachPort (pid); + kern_return_t kr; + x86_thread_state_t gp_regs; + mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT; + kr = thread_get_state (static_cast(thread), + x86_THREAD_STATE, + (thread_state_t) &gp_regs, + &gp_count); + if (kr == KERN_SUCCESS) + { + if (gp_regs.tsh.flavor == x86_THREAD_STATE64) + rep << "ptrsize:8;"; + else + rep << "ptrsize:4;"; + } +#elif defined (__arm__) + rep << "ptrsize:4;"; +#elif (defined (__arm64__) || defined (__aarch64__)) && defined (ARM_UNIFIED_THREAD_STATE) + nub_thread_t thread = DNBProcessGetCurrentThreadMachPort (pid); + kern_return_t kr; + arm_unified_thread_state_t gp_regs; + mach_msg_type_number_t gp_count = ARM_UNIFIED_THREAD_STATE_COUNT; + kr = thread_get_state (thread, ARM_UNIFIED_THREAD_STATE, + (thread_state_t) &gp_regs, &gp_count); + if (kr == KERN_SUCCESS) + { + if (gp_regs.ash.flavor == ARM_THREAD_STATE64) + rep << "ptrsize:8;"; + else + rep << "ptrsize:4;"; + } +#endif + } + + return SendPacket (rep.str()); +} + +const RNBRemote::DispatchQueueOffsets * +RNBRemote::GetDispatchQueueOffsets() +{ + if (!m_dispatch_queue_offsets.IsValid() && m_dispatch_queue_offsets_addr != INVALID_NUB_ADDRESS && m_ctx.HasValidProcessID()) + { + nub_process_t pid = m_ctx.ProcessID(); + nub_size_t bytes_read = DNBProcessMemoryRead(pid, m_dispatch_queue_offsets_addr, sizeof(m_dispatch_queue_offsets), &m_dispatch_queue_offsets); + if (bytes_read != sizeof(m_dispatch_queue_offsets)) + m_dispatch_queue_offsets.Clear(); + } + + if (m_dispatch_queue_offsets.IsValid()) + return &m_dispatch_queue_offsets; + else + return nullptr; +} + +void +RNBRemote::EnableCompressionNextSendPacket (compression_types type) +{ + m_compression_mode = type; + m_enable_compression_next_send_packet = true; +} + +compression_types +RNBRemote::GetCompressionType () +{ + // The first packet we send back to the debugger after a QEnableCompression request + // should be uncompressed -- so we can indicate whether the compression was enabled + // or not via OK / Enn returns. After that, all packets sent will be using the + // compression protocol. + + if (m_enable_compression_next_send_packet) + { + // One time, we send back "None" as our compression type + m_enable_compression_next_send_packet = false; + return compression_types::none; + } + return m_compression_mode; +} diff --git a/tools/debugserver/source/RNBRemote.h b/tools/debugserver/source/RNBRemote.h new file mode 100644 index 000000000000..9d30106d5b82 --- /dev/null +++ b/tools/debugserver/source/RNBRemote.h @@ -0,0 +1,452 @@ + +// +// 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 }; + +enum class compression_types { zlib_deflate, lz4, lzma, lzfse, none }; + +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;pid' + vattachwait, // 'vAttachWait:XX...' where XX is one or more hex encoded process name ASCII bytes + vattachorwait, // 'vAttachOrWait:XX...' where XX is one or more hex encoded process name ASCII bytes + vattachname, // 'vAttachName:XX...' where XX is one or more hex encoded process name ASCII bytes + vcont, // 'vCont' + vcont_list_actions, // 'vCont?' + read_data_from_memory, // 'x' + 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_monitor, // 'qRcmd' + query_current_thread_id, // 'qC' + query_get_pid, // 'qGetPid' + query_echo, // 'qEcho' + 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, // 'qSymbol' + query_launch_success, // 'qLaunchSuccess' + query_register_info, // 'qRegisterInfo' + query_shlib_notify_info_addr, // 'qShlibInfoAddr' + query_step_packet_supported, // 'qStepPacketSupported' + query_supported_features, // 'qSupported' + query_vattachorwait_supported, // 'qVAttachOrWaitSupported' + query_sync_thread_state_supported,// 'QSyncThreadState' + query_host_info, // 'qHostInfo' + query_gdb_server_version, // 'qGDBServerVersion' + query_process_info, // 'qProcessInfo' + json_query_thread_extended_info,// 'jThreadExtendedInfo' + json_query_get_loaded_dynamic_libraries_infos, // 'jGetLoadedDynamicLibrariesInfos' + json_query_threads_info, // 'jThreadsInfo' + pass_signals_to_inferior, // 'QPassSignals' + start_noack_mode, // 'QStartNoAckMode' + prefix_reg_packets_with_tid, // 'QPrefixRegisterPacketsWithThreadID + set_logging_mode, // 'QSetLogging:' + set_max_packet_size, // 'QSetMaxPacketSize:' + set_max_payload_size, // 'QSetMaxPayloadSize:' + set_environment_variable, // 'QEnvironment:' + set_environment_variable_hex, // 'QEnvironmentHexEncoded:' + set_launch_arch, // 'QLaunchArch:' + set_disable_aslr, // 'QSetDisableASLR:' + set_stdin, // 'QSetSTDIN:' + set_stdout, // 'QSetSTDOUT:' + set_stderr, // 'QSetSTDERR:' + set_working_dir, // 'QSetWorkingDir:' + set_list_threads_in_stop_reply, // 'QListThreadsInStopReply:' + sync_thread_state, // 'QSyncThreadState:' + memory_region_info, // 'qMemoryRegionInfo:' + get_profile_data, // 'qGetProfileData' + set_enable_profiling, // 'QSetEnableAsyncProfiling' + enable_compression, // 'QEnableCompression:' + watchpoint_support_info, // 'qWatchpointSupportInfo:' + allocate_memory, // '_M' + deallocate_memory, // '_m' + set_process_event, // 'QSetProcessEvent:' + save_register_state, // '_g' + restore_register_state, // '_G' + speed_test, // 'qSpeedTest:' + set_detach_on_error, // 'QSetDetachOnError:' + query_transfer, // 'qXfer:' + unknown_type + } PacketEnum; + + typedef rnb_err_t (RNBRemote::*HandlePacketCallback)(const char *p); + + RNBRemote (); + ~RNBRemote (); + + void Initialize(); + + bool InitializeRegisters (bool force = false); + + 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 == (nub_thread_t)-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_qRcmd (const char *p); + rnb_err_t HandlePacket_qGetPid (const char *p); + rnb_err_t HandlePacket_qEcho (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_qVAttachOrWaitSupported (const char *p); + rnb_err_t HandlePacket_qSyncThreadStateSupported (const char *p); + rnb_err_t HandlePacket_qThreadInfo (const char *p); + rnb_err_t HandlePacket_jThreadExtendedInfo (const char *p); + rnb_err_t HandlePacket_jGetLoadedDynamicLibrariesInfos (const char *p); + rnb_err_t HandlePacket_jThreadsInfo (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_qGDBServerVersion (const char *p); + rnb_err_t HandlePacket_qProcessInfo (const char *p); + rnb_err_t HandlePacket_qSymbol (const char *p); + rnb_err_t HandlePacket_QStartNoAckMode (const char *p); + rnb_err_t HandlePacket_QThreadSuffixSupported (const char *p); + rnb_err_t HandlePacket_QSetLogging (const char *p); + rnb_err_t HandlePacket_QSetDisableASLR (const char *p); + rnb_err_t HandlePacket_QSetSTDIO (const char *p); + rnb_err_t HandlePacket_QSetWorkingDir (const char *p); + rnb_err_t HandlePacket_QSetMaxPayloadSize (const char *p); + rnb_err_t HandlePacket_QSetMaxPacketSize (const char *p); + rnb_err_t HandlePacket_QEnvironment (const char *p); + rnb_err_t HandlePacket_QEnvironmentHexEncoded (const char *p); + rnb_err_t HandlePacket_QLaunchArch (const char *p); + rnb_err_t HandlePacket_QListThreadsInStopReply (const char *p); + rnb_err_t HandlePacket_QSyncThreadState (const char *p); + rnb_err_t HandlePacket_QPrefixRegisterPacketsWithThreadID (const char *p); + rnb_err_t HandlePacket_QSetProcessEvent (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_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_qSupported (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 *file, int line, const char *p, const char *description); + rnb_err_t HandlePacket_AllocateMemory (const char *p); + rnb_err_t HandlePacket_DeallocateMemory (const char *p); + rnb_err_t HandlePacket_SaveRegisterState (const char *p); + rnb_err_t HandlePacket_RestoreRegisterState (const char *p); + rnb_err_t HandlePacket_MemoryRegionInfo (const char *p); + rnb_err_t HandlePacket_GetProfileData(const char *p); + rnb_err_t HandlePacket_SetEnableAsyncProfiling(const char *p); + rnb_err_t HandlePacket_QEnableCompression(const char *p); + rnb_err_t HandlePacket_WatchpointSupportInfo (const char *p); + rnb_err_t HandlePacket_qSpeedTest (const char *p); + rnb_err_t HandlePacket_qXfer (const char *p); + rnb_err_t HandlePacket_stop_process (const char *p); + rnb_err_t HandlePacket_QSetDetachOnError (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 (); + void SendAsyncProfileData (); + rnb_err_t SendAsyncProfileDataPacket (char *buf, nub_size_t buf_size); + + RNBContext& Context() { return m_ctx; } + RNBSocket& Comm() { return m_comm; } + +private: + // Outlaw some constructors + RNBRemote (const RNBRemote &); + +protected: + + 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; + + bool + IsPlatformPacket () const + { + switch (type) + { + case set_logging_mode: + case query_host_info: + return true; + default: + break; + } + return false; + } + 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) + { + } + }; + + + struct DispatchQueueOffsets + { + uint16_t dqo_version; + uint16_t dqo_label; + uint16_t dqo_label_size; + uint16_t dqo_flags; + uint16_t dqo_flags_size; + uint16_t dqo_serialnum; + uint16_t dqo_serialnum_size; + uint16_t dqo_width; + uint16_t dqo_width_size; + uint16_t dqo_running; + uint16_t dqo_running_size; + uint16_t dqo_suspend_cnt; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_suspend_cnt_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + + DispatchQueueOffsets () + { + Clear(); + } + + void + Clear() + { + dqo_version = UINT16_MAX; + dqo_label = UINT16_MAX; + dqo_label_size = UINT16_MAX; + dqo_flags = UINT16_MAX; + dqo_flags_size = UINT16_MAX; + dqo_serialnum = UINT16_MAX; + dqo_serialnum_size = UINT16_MAX; + dqo_width = UINT16_MAX; + dqo_width_size = UINT16_MAX; + dqo_running = UINT16_MAX; + dqo_running_size = UINT16_MAX; + dqo_suspend_cnt = UINT16_MAX; + dqo_suspend_cnt_size = UINT16_MAX; + dqo_target_queue = UINT16_MAX; + dqo_target_queue_size = UINT16_MAX; + dqo_priority = UINT16_MAX; + dqo_priority_size = UINT16_MAX; + } + + bool + IsValid () const + { + return dqo_version != UINT16_MAX; + } + + void + GetThreadQueueInfo (nub_process_t pid, + nub_addr_t dispatch_qaddr, + std::string &queue_name, + uint64_t &queue_width, + uint64_t &queue_serialnum) const; + }; + + rnb_err_t GetPacket (std::string &packet_data, RNBRemote::Packet& packet_info, bool wait); + rnb_err_t SendPacket (const std::string &); + std::string CompressString (const std::string &); + + void CreatePacketTable (); + rnb_err_t GetPacketPayload (std::string &); + + nub_thread_t + ExtractThreadIDFromThreadSuffix (const char *p); + + void + EnableCompressionNextSendPacket (compression_types); + + compression_types + GetCompressionType (); + + const DispatchQueueOffsets * + GetDispatchQueueOffsets(); + + JSONGenerator::ObjectSP + GetJSONThreadsInfo (bool threads_with_valid_stop_info_only); + + RNBContext m_ctx; // process context + RNBSocket m_comm; // communication port + std::string m_arch; + 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 + DispatchQueueOffsets m_dispatch_queue_offsets; + nub_addr_t m_dispatch_queue_offsets_addr; + uint32_t m_qSymbol_index; + 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; + uint32_t m_max_payload_size; // the maximum sized payload we should send to gdb + bool m_extended_mode; // are we in extended mode? + bool m_noack_mode; // are we in no-ack mode? + bool m_thread_suffix_supported; // Set to true if the 'p', 'P', 'g', and 'G' packets should be prefixed with the thread ID and colon: + // "$pRR;thread:TTTT;" instead of "$pRR" + // "$PRR=VVVVVVVV;thread:TTTT;" instead of "$PRR=VVVVVVVV" + // "$g;thread:TTTT" instead of "$g" + // "$GVVVVVVVVVVVVVV;thread:TTTT;#00 instead of "$GVVVVVVVVVVVVVV" + bool m_list_threads_in_stop_reply; + + size_t m_compression_minsize; // only packets larger than this size will be compressed + bool m_enable_compression_next_send_packet; + + compression_types m_compression_mode; +}; + +/* 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/tools/debugserver/source/RNBServices.cpp b/tools/debugserver/source/RNBServices.cpp new file mode 100644 index 000000000000..ebd390267f48 --- /dev/null +++ b/tools/debugserver/source/RNBServices.cpp @@ -0,0 +1,226 @@ +//===-- 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. +// +//===----------------------------------------------------------------------===// + +#include "RNBServices.h" + +#include +#include +#include +#include +#include "CFString.h" +#include +#include "DNBLog.h" +#include "MacOSX/CFUtils.h" + +// For now only SpringBoard has a notion of "Applications" that it can list for us. +// So we have to use the SpringBoard API's here. +#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) +#include +#endif + +// From DNB.cpp +size_t GetAllInfos (std::vector& proc_infos); + +int +GetProcesses (CFMutableArrayRef plistMutableArray, bool all_users) +{ + if (plistMutableArray == NULL) + return -1; + + // Running as root, get all processes + std::vector proc_infos; + const size_t num_proc_infos = GetAllInfos(proc_infos); + if (num_proc_infos > 0) + { + const pid_t our_pid = getpid(); + const uid_t our_uid = getuid(); + uint32_t i; + CFAllocatorRef alloc = kCFAllocatorDefault; + + for (i=0; i appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + + // Get the process id for the app (if there is one) + const int32_t pid_int32 = pid; + CFReleaser pidCFNumber (::CFNumberCreate (alloc, kCFNumberSInt32Type, &pid_int32)); + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get()); + + // Set the a boolean to indicate if this is the front most + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse); + + const char *pid_basename = proc_info.kp_proc.p_comm; + char proc_path_buf[PATH_MAX]; + + int return_val = proc_pidpath (pid, proc_path_buf, PATH_MAX); + if (return_val > 0) + { + // Okay, now search backwards from that to see if there is a + // slash in the name. Note, even though we got all the args we don't care + // because the list data is just a bunch of concatenated null terminated strings + // so strrchr will start from the end of argv0. + + pid_basename = strrchr(proc_path_buf, '/'); + if (pid_basename) + { + // Skip the '/' + ++pid_basename; + } + else + { + // We didn't find a directory delimiter in the process argv[0], just use what was in there + pid_basename = proc_path_buf; + } + CFString cf_pid_path (proc_path_buf); + if (cf_pid_path.get()) + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PATH_KEY, cf_pid_path.get()); + } + + if (pid_basename && pid_basename[0]) + { + CFString pid_name (pid_basename); + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get()); + } + + // Append the application info to the plist array + ::CFArrayAppendValue (plistMutableArray, appInfoDict.get()); + } + } + return 0; +} +int +ListApplications(std::string& plist, bool opt_runningApps, bool opt_debuggable) +{ + 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)); + + const uid_t our_uid = getuid(); + +#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) + + if (our_uid == 0) + { + bool all_users = true; + result = GetProcesses (plistMutableArray.get(), all_users); + } + else + { + CFReleaser sbsFrontAppID (::SBSCopyFrontmostApplicationDisplayIdentifier ()); + CFReleaser sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); + + // Need to check the return value from SBSCopyApplicationDisplayIdentifiers. + CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount (sbsAppIDs.get()) : 0; + 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()); + } + } +#else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) + // When root, show all processes + bool all_users = (our_uid == 0); + GetProcesses (plistMutableArray.get(), all_users); +#endif + + 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; + +} diff --git a/tools/debugserver/source/RNBServices.h b/tools/debugserver/source/RNBServices.h new file mode 100644 index 000000000000..b0b9c2193573 --- /dev/null +++ b/tools/debugserver/source/RNBServices.h @@ -0,0 +1,28 @@ +//===-- 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); + +#endif // __RNBServices_h__ diff --git a/tools/debugserver/source/RNBSocket.cpp b/tools/debugserver/source/RNBSocket.cpp new file mode 100644 index 000000000000..ce4886ab9df3 --- /dev/null +++ b/tools/debugserver/source/RNBSocket.cpp @@ -0,0 +1,421 @@ +//===-- 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 +#include +#include "DNBLog.h" +#include "DNBError.h" + +#ifdef WITH_LOCKDOWN +#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. */ + +bool +ResolveIPV4HostName (const char *hostname, in_addr_t &addr) +{ + if (hostname == NULL || + hostname[0] == '\0' || + strcmp(hostname, "localhost") == 0 || + strcmp(hostname, "127.0.0.1") == 0) + { + addr = htonl (INADDR_LOOPBACK); + return true; + } + else if (strcmp(hostname, "*") == 0) + { + addr = htonl (INADDR_ANY); + return true; + } + else + { + // See if an IP address was specified as numbers + int inet_pton_result = ::inet_pton (AF_INET, hostname, &addr); + + if (inet_pton_result == 1) + return true; + + struct hostent *host_entry = gethostbyname (hostname); + if (host_entry) + { + std::string ip_str (::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list)); + inet_pton_result = ::inet_pton (AF_INET, ip_str.c_str(), &addr); + if (inet_pton_result == 1) + return true; + } + } + return false; +} + +rnb_err_t +RNBSocket::Listen (const char *listen_host, uint16_t port, PortBoundCallback callback, const void *callback_baton) +{ + //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); + // Disconnect without saving errno + Disconnect (false); + + // Now figure out the hostname that will be attaching and palce it into + struct sockaddr_in listen_addr; + ::memset (&listen_addr, 0, sizeof listen_addr); + listen_addr.sin_len = sizeof listen_addr; + listen_addr.sin_family = AF_INET; + listen_addr.sin_port = htons (port); + listen_addr.sin_addr.s_addr = INADDR_ANY; + + if (!ResolveIPV4HostName(listen_host, listen_addr.sin_addr.s_addr)) + { + DNBLogThreaded("error: failed to resolve connecting host '%s'", listen_host); + return rnb_err; + } + + DNBError err; + int listen_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_fd == -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_fd); + + if (err.Fail()) + return rnb_err; + + // enable local address reuse + SetSocketOption (listen_fd, 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 (port); + sa.sin_addr.s_addr = INADDR_ANY; // Let incoming connections bind to any host network interface (this is NOT who can connect to us) + int error = ::bind (listen_fd, (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_fd); + + if (err.Fail()) + { + ClosePort (listen_fd, false); + return rnb_err; + } + + error = ::listen (listen_fd, 5); + if (error == -1) + err.SetError(errno, DNBError::POSIX); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd); + + if (err.Fail()) + { + ClosePort (listen_fd, false); + return rnb_err; + } + + if (callback) + { + // We were asked to listen on port zero which means we + // must now read the actual port that was given to us + // as port zero is a special code for "find an open port + // for me". + if (port == 0) + { + socklen_t sa_len = sizeof (sa); + if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0) + { + port = ntohs (sa.sin_port); + callback (callback_baton, port); + } + } + else + { + callback (callback_baton, port); + } + } + + struct sockaddr_in accept_addr; + ::memset (&accept_addr, 0, sizeof accept_addr); + accept_addr.sin_len = sizeof accept_addr; + + bool accept_connection = false; + + // Loop until we are happy with our connection + while (!accept_connection) + { + socklen_t accept_addr_len = sizeof accept_addr; + m_fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len); + + if (m_fd == -1) + err.SetError(errno, DNBError::POSIX); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::accept ( socket = %i, address = %p, address_len = %u )", listen_fd, &accept_addr, accept_addr_len); + + if (err.Fail()) + break; + + if (listen_addr.sin_addr.s_addr == INADDR_ANY) + accept_connection = true; + else + { + if (accept_addr_len == listen_addr.sin_len && + accept_addr.sin_addr.s_addr == listen_addr.sin_addr.s_addr) + { + accept_connection = true; + } + else + { + ::close (m_fd); + m_fd = -1; + const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr; + const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sin_addr.s_addr; + ::fprintf (stderr, + "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n", + accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3], + listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]); + DNBLogThreaded ("error: rejecting connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)", + accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3], + listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]); + } + } + } + + ClosePort (listen_fd, false); + + if (err.Fail()) + { + return rnb_err; + } + else + { + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); + } + + return rnb_success; +} + +rnb_err_t +RNBSocket::Connect (const char *host, uint16_t port) +{ + Disconnect (false); + + // Create the socket + m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_fd == -1) + return rnb_err; + + // Enable local address reuse + SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1); + + struct sockaddr_in sa; + ::memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (port); + + if (!ResolveIPV4HostName(host, sa.sin_addr.s_addr)) + { + DNBLogThreaded("error: failed to resolve host '%s'", host); + Disconnect (false); + return rnb_err; + } + + if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa))) + { + Disconnect (false); + return rnb_err; + } + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); + return rnb_success; +} + +rnb_err_t +RNBSocket::useFD(int fd) +{ + if (fd < 0) { + DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in."); + return rnb_err; + } + + m_fd = fd; + return rnb_success; +} + +#ifdef WITH_LOCKDOWN +rnb_err_t +RNBSocket::ConnectToService() +{ + DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME); + // Disconnect from any previous connections + Disconnect(false); + if (::secure_lockdown_checkin (&m_ld_conn, NULL, NULL) != kLDESuccess) + { + DNBLogThreadedIf(LOG_RNB_COMM, "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed"); + m_fd = -1; + return rnb_not_connected; + } + m_fd = ::lockdown_get_socket (m_ld_conn); + if (m_fd == -1) + { + DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed"); + return rnb_not_connected; + } + m_fd_from_lockdown = true; + return rnb_success; +} +#endif + +rnb_err_t +RNBSocket::OpenFile (const char *path) +{ + DNBError err; + m_fd = open (path, O_RDWR); + if (m_fd == -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_fd, &stdin_termios) == 0) + { + stdin_termios.c_lflag &= ~ECHO; // Turn off echoing + stdin_termios.c_lflag &= ~ICANON; // Get one char at a time + ::tcsetattr (m_fd, 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) +{ +#ifdef WITH_LOCKDOWN + if (m_fd_from_lockdown) + { + m_fd_from_lockdown = false; + m_fd = -1; + lockdown_disconnect (m_ld_conn); + return rnb_success; + } +#endif + return ClosePort (m_fd, 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_fd == -1) + return rnb_err; + + //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); + DNBError err; + ssize_t bytesread = read (m_fd, 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, %llu ) => %i", m_fd, buf, sizeof (buf), (uint64_t)bytesread); + + // Our port went away - we have to mark this so IsConnected will return the truth. + if (bytesread == 0) + { + m_fd = -1; + return rnb_not_connected; + } + else if (bytesread == -1) + { + m_fd = -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_fd == -1) + return rnb_err; + + DNBError err; + ssize_t bytessent = write (m_fd, buffer, length); + if (bytessent < 0) + err.SetError(errno, DNBError::POSIX); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i", m_fd, buffer, length, (uint64_t)bytessent); + + if (bytessent < 0) + return rnb_err; + + if ((size_t)bytessent != length) + return rnb_err; + + DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", (int)length, (char *)buffer); // All data is string based in debugserver, so this is safe + DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)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/tools/debugserver/source/RNBSocket.h b/tools/debugserver/source/RNBSocket.h new file mode 100644 index 000000000000..32f4ebeed2a4 --- /dev/null +++ b/tools/debugserver/source/RNBSocket.h @@ -0,0 +1,85 @@ +//===-- 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" + +#ifdef WITH_LOCKDOWN +#include "lockdown.h" +#endif + +class RNBSocket +{ +public: + typedef void (*PortBoundCallback) (const void *baton, uint16_t port); + + RNBSocket () : + m_fd (-1), +#ifdef WITH_LOCKDOWN + m_fd_from_lockdown (false), + m_ld_conn (), +#endif + m_timer (true) // Make a thread safe timer + { + } + ~RNBSocket (void) + { + Disconnect (false); + } + + rnb_err_t Listen (const char *listen_host, + uint16_t port, + PortBoundCallback callback, + const void *callback_baton); + rnb_err_t Connect (const char *host, uint16_t port); + + rnb_err_t useFD(int fd); + +#ifdef WITH_LOCKDOWN + 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_fd != -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_fd; // Socket we use to communicate once conn established + +#ifdef WITH_LOCKDOWN + bool m_fd_from_lockdown; + lockdown_connection m_ld_conn; +#endif + + DNBTimer m_timer; +}; + + +#endif // #ifndef __RNBSocket_h__ diff --git a/tools/debugserver/source/SysSignal.cpp b/tools/debugserver/source/SysSignal.cpp new file mode 100644 index 000000000000..69f34ed605c5 --- /dev/null +++ b/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/tools/debugserver/source/SysSignal.h b/tools/debugserver/source/SysSignal.h new file mode 100644 index 000000000000..438d137f3104 --- /dev/null +++ b/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/tools/debugserver/source/TTYState.cpp b/tools/debugserver/source/TTYState.cpp new file mode 100644 index 000000000000..28bc956dc28b --- /dev/null +++ b/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/tools/debugserver/source/TTYState.h b/tools/debugserver/source/TTYState.h new file mode 100644 index 000000000000..c01d51255439 --- /dev/null +++ b/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/tools/debugserver/source/com.apple.debugserver.applist.internal.plist b/tools/debugserver/source/com.apple.debugserver.applist.internal.plist new file mode 100644 index 000000000000..e9a74bd0bf79 --- /dev/null +++ b/tools/debugserver/source/com.apple.debugserver.applist.internal.plist @@ -0,0 +1,16 @@ + + + + + Label + com.apple.debugserver.applist.internal + ProgramArguments + + /Developer/usr/bin/debugserver + --lockdown + --applist + + AllowByProxy + + + diff --git a/tools/debugserver/source/com.apple.debugserver.applist.plist b/tools/debugserver/source/com.apple.debugserver.applist.plist new file mode 100644 index 000000000000..002e90d98d13 --- /dev/null +++ b/tools/debugserver/source/com.apple.debugserver.applist.plist @@ -0,0 +1,19 @@ + + + + + Label + com.apple.debugserver.applist + UserName + mobile + ProgramArguments + + /Developer/usr/bin/debugserver + --lockdown + --applist + --launch=frontboard + + AllowByProxy + + + diff --git a/tools/debugserver/source/com.apple.debugserver.internal.plist b/tools/debugserver/source/com.apple.debugserver.internal.plist new file mode 100644 index 000000000000..b9f57f731232 --- /dev/null +++ b/tools/debugserver/source/com.apple.debugserver.internal.plist @@ -0,0 +1,15 @@ + + + + + Label + com.apple.debugserver.internal + ProgramArguments + + /Developer/usr/bin/debugserver + --lockdown + + AllowByProxy + + + diff --git a/tools/debugserver/source/com.apple.debugserver.plist b/tools/debugserver/source/com.apple.debugserver.plist new file mode 100644 index 000000000000..c07466e27cd2 --- /dev/null +++ b/tools/debugserver/source/com.apple.debugserver.plist @@ -0,0 +1,18 @@ + + + + + Label + com.apple.debugserver + UserName + mobile + ProgramArguments + + /Developer/usr/bin/debugserver + --lockdown + --launch=frontboard + + AllowByProxy + + + diff --git a/tools/debugserver/source/com.apple.debugserver.posix.plist b/tools/debugserver/source/com.apple.debugserver.posix.plist new file mode 100644 index 000000000000..4083f8a75c67 --- /dev/null +++ b/tools/debugserver/source/com.apple.debugserver.posix.plist @@ -0,0 +1,18 @@ + + + + + Label + com.apple.debugserver.posix + UserName + mobile + ProgramArguments + + /Developer/usr/bin/debugserver + --lockdown + --launch=posix + + AllowByProxy + + + diff --git a/tools/debugserver/source/debugserver-entitlements.plist b/tools/debugserver/source/debugserver-entitlements.plist new file mode 100644 index 000000000000..4134ee958613 --- /dev/null +++ b/tools/debugserver/source/debugserver-entitlements.plist @@ -0,0 +1,28 @@ + + + + + com.apple.springboard.debugapplications + + com.apple.backboardd.launchapplications + + com.apple.backboardd.debugapplications + + com.apple.frontboard.launchapplications + + com.apple.frontboard.debugapplications + + run-unsigned-code + + seatbelt-profiles + + debugserver + + com.apple.diagnosticd.diagnostic + + com.apple.security.network.server + + com.apple.security.network.client + + + diff --git a/tools/debugserver/source/debugserver-macosx-entitlements.plist b/tools/debugserver/source/debugserver-macosx-entitlements.plist new file mode 100644 index 000000000000..eddbaa0063ef --- /dev/null +++ b/tools/debugserver/source/debugserver-macosx-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.diagnosticd.diagnostic + + + diff --git a/tools/debugserver/source/debugserver.cpp b/tools/debugserver/source/debugserver.cpp new file mode 100644 index 000000000000..78990a671d82 --- /dev/null +++ b/tools/debugserver/source/debugserver.cpp @@ -0,0 +1,1672 @@ +//===-- 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 +#include +#include +#include +#include +#include +#include // for _NSGetEnviron() + +#if defined (__APPLE__) +#include +extern "C" int proc_set_wakemon_params(pid_t, int, int); // SPI +#endif + +#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, + eRNBRunLoopModePlatformMode, + 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_disable_aslr = 0; + +int g_isatty = 0; +bool g_detach_on_error = true; + +#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) + +//---------------------------------------------------------------------- +// 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 (RNBRemote* remote) +{ + std::string packet; + + if (remote) + { + RNBContext& ctx = remote->Context(); + uint32_t event_mask = RNBContext::event_read_packet_available | + RNBContext::event_read_thread_exiting; + + // 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_thread_exiting) + { + RNBLogSTDERR ("error: packet read thread exited.\n"); + return eRNBRunLoopModeExit; + } + + 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 || type == RNBRemote::vattachorwait) + { + if (err == rnb_success) + { + RNBLogSTDOUT ("Attach succeeded, ready to debug.\n"); + return eRNBRunLoopModeInferiorExecuting; + } + else + { + RNBLogSTDERR ("error: attach failed.\n"); + 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.\n"); + 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 (RNBRemote *remote, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio) +{ + 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 WITH_FBS + // Check if we have an app bundle, if so launch using BackBoard Services. + if (strstr(inferior_argv[0], ".app")) + { + launch_flavor = eLaunchFlavorFBS; + } +#elif defined WITH_BKS + // Check if we have an app bundle, if so launch using BackBoard Services. + if (strstr(inferior_argv[0], ".app")) + { + launch_flavor = eLaunchFlavorBKS; + } +#elif defined WITH_SPRINGBOARD + // 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'; + const char * cwd = (ctx.GetWorkingDirPath() != NULL ? ctx.GetWorkingDirPath() + : ctx.GetWorkingDirectory()); + const char *process_event = ctx.GetProcessEvent(); + nub_process_t pid = DNBProcessLaunch (resolved_path, + &inferior_argv[0], + &inferior_envp[0], + cwd, + stdin_path, + stdout_path, + stderr_path, + no_stdio, + launch_flavor, + g_disable_aslr, + process_event, + 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__, launch_err_str); + ctx.LaunchStatus().SetError(-1, DNBError::Generic); + ctx.LaunchStatus().SetErrorString(launch_err_str); + } + else if (pid == INVALID_NUB_PROCESS) + { + DNBLogThreaded ("%s DNBProcessLaunch() failed to launch process, unknown failure", __FUNCTION__); + ctx.LaunchStatus().SetError(-1, DNBError::Generic); + ctx.LaunchStatus().SetErrorString(""); + } + 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) + { + 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 (RNBRemote *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; + default: + break; + } + } + } + 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 (RNBRemote *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 %llu (old %llu)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count); + } + else + { + + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %llu (old %llu)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count); + remote->NotifyThatProcessStopped (); + } + } + else + { + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %llu (old %llu)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count); + } + } + return eRNBRunLoopModeInferiorExecuting; + + case eStateStepping: + case eStateRunning: + return eRNBRunLoopModeInferiorExecuting; + + case eStateExited: + remote->HandlePacket_last_signal(NULL); + case eStateDetached: + 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 (RNBRemote *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 some bits if we are not running so we don't send any async packets + event_mask &= ~RNBContext::event_proc_stdio_available; + event_mask &= ~RNBContext::event_proc_profile_data; + } + + // 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_proc_profile_data) + { + remote->SendAsyncProfileData(); + } + + 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()) + { + if (ctx.GetDetachOnError()) + { + DNBLog ("debugserver's event read thread is exiting, detaching from the inferior process."); + DNBProcessDetach (ctx.ProcessID()); + } + else + { + DNBLog ("debugserver's event read thread is exiting, killing the inferior process."); + DNBProcessKill (ctx.ProcessID()); + } + } + else + { + if (ctx.GetDetachOnError()) + { + DNBLog ("debugserver's event read thread is exiting, detaching from the inferior process."); + DNBProcessDetach (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; +} + + +RNBRunLoopMode +RNBRunLoopPlatform (RNBRemote *remote) +{ + RNBRunLoopMode mode = eRNBRunLoopModePlatformMode; + RNBContext& ctx = remote->Context(); + + while (mode == eRNBRunLoopModePlatformMode) + { + std::string set_events_str; + const uint32_t event_mask = RNBContext::event_read_packet_available | + RNBContext::event_read_thread_exiting; + + 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_read_packet_available) + { + if (remote->HandleReceivedPacket() == rnb_not_connected) + mode = eRNBRunLoopModeExit; + } + + if (set_events & RNBContext::event_read_thread_exiting) + { + mode = eRNBRunLoopModeExit; + } + ctx.Events().ResetEvents(set_events); + } + } + return eRNBRunLoopModeExit; +} + +//---------------------------------------------------------------------- +// Convenience function to set up the remote listening port +// Returns 1 for success 0 for failure. +//---------------------------------------------------------------------- + +static void +PortWasBoundCallbackUnixSocket (const void *baton, in_port_t port) +{ + //::printf ("PortWasBoundCallbackUnixSocket (baton = %p, port = %u)\n", baton, port); + + const char *unix_socket_name = (const char *)baton; + + if (unix_socket_name && unix_socket_name[0]) + { + // We were given a unix socket name to use to communicate the port + // that we ended up binding to back to our parent process + struct sockaddr_un saddr_un; + int s = ::socket (AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + { + perror("error: socket (AF_UNIX, SOCK_STREAM, 0)"); + exit(1); + } + + saddr_un.sun_family = AF_UNIX; + ::strncpy(saddr_un.sun_path, unix_socket_name, sizeof(saddr_un.sun_path) - 1); + saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; + saddr_un.sun_len = SUN_LEN (&saddr_un); + + if (::connect (s, (struct sockaddr *)&saddr_un, static_cast(SUN_LEN (&saddr_un))) < 0) + { + perror("error: connect (socket, &saddr_un, saddr_un_len)"); + exit(1); + } + + //::printf ("connect () sucess!!\n"); + + + // We were able to connect to the socket, now write our PID so whomever + // launched us will know this process's ID + RNBLogSTDOUT ("Listening to port %i...\n", port); + + char pid_str[64]; + const int pid_str_len = ::snprintf (pid_str, sizeof(pid_str), "%u", port); + const ssize_t bytes_sent = ::send (s, pid_str, pid_str_len, 0); + + if (pid_str_len != bytes_sent) + { + perror("error: send (s, pid_str, pid_str_len, 0)"); + exit (1); + } + + //::printf ("send () sucess!!\n"); + + // We are done with the socket + close (s); + } +} + +static void +PortWasBoundCallbackNamedPipe (const void *baton, uint16_t port) +{ + const char *named_pipe = (const char *)baton; + if (named_pipe && named_pipe[0]) + { + int fd = ::open(named_pipe, O_WRONLY); + if (fd > -1) + { + char port_str[64]; + const ssize_t port_str_len = ::snprintf (port_str, sizeof(port_str), "%u", port); + // Write the port number as a C string with the NULL terminator + ::write (fd, port_str, port_str_len + 1); + close (fd); + } + } +} + +static int +ConnectRemote (RNBRemote *remote, + const char *host, + int port, + bool reverse_connect, + const char *named_pipe_path, + const char *unix_socket_name) +{ + if (!remote->Comm().IsConnected()) + { + if (reverse_connect) + { + if (port == 0) + { + DNBLogThreaded("error: invalid port supplied for reverse connection: %i.\n", port); + return 0; + } + if (remote->Comm().Connect(host, port) != rnb_success) + { + DNBLogThreaded("Failed to reverse connect to %s:%i.\n", host, port); + return 0; + } + } + else + { + if (port != 0) + RNBLogSTDOUT ("Listening to port %i for a connection from %s...\n", port, host ? host : "127.0.0.1"); + if (unix_socket_name && unix_socket_name[0]) + { + if (remote->Comm().Listen(host, port, PortWasBoundCallbackUnixSocket, unix_socket_name) != rnb_success) + { + RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); + return 0; + } + } + else + { + if (remote->Comm().Listen(host, port, PortWasBoundCallbackNamedPipe, named_pipe_path) != rnb_success) + { + RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); + return 0; + } + } + } + remote->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-%s", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_STR); + ::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_only() +//---------------------------------------------------------------------- +static struct option g_long_options[] = +{ + { "attach", required_argument, NULL, 'a' }, + { "arch", required_argument, NULL, 'A' }, + { "debug", no_argument, NULL, 'g' }, + { "kill-on-error", no_argument, NULL, 'K' }, + { "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 (STDIN, STDOUT and STDERR) (only if debugserver launches the process) + { "stdin-path", required_argument, NULL, 'I' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process) + { "stdout-path", required_argument, NULL, 'O' }, // Set the STDOUT path to be used when launching applications (only if debugserver launches the process) + { "stderr-path", required_argument, NULL, 'E' }, // Set the STDERR path to be used when launching applications (only if debugserver launches the process) + { "no-stdio", no_argument, NULL, 'n' }, // Do not set up any stdio (perhaps the program is a GUI program) (only if debugserver launches the process) + { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own session + { "disable-aslr", no_argument, NULL, 'D' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization + { "working-dir", required_argument, NULL, 'W' }, // The working directory that the inferior process should have (only if debugserver launches the process) + { "platform", required_argument, NULL, 'p' }, // Put this executable into a remote platform mode + { "unix-socket", required_argument, NULL, 'u' }, // If we need to handshake with our parent process, an option will be passed down that specifies a unix socket name to use + { "named-pipe", required_argument, NULL, 'P' }, + { "reverse-connect", no_argument, NULL, 'R' }, + { "env", required_argument, NULL, 'e' }, // When debugserver launches the process, set a single environment entry as specified by the option value ("./debugserver -e FOO=1 -e BAR=2 localhost:1234 -- /bin/ls") + { "forward-env", no_argument, NULL, 'F' }, // When debugserver launches the process, forward debugserver's current environment variables to the child process ("./debugserver -F localhost:1234 -- /bin/ls" + { NULL, 0, NULL, 0 } +}; + + +//---------------------------------------------------------------------- +// main +//---------------------------------------------------------------------- +int +main (int argc, char *argv[]) +{ + const char *argv_sub_zero = argv[0]; // save a copy of argv[0] for error reporting post-launch + +#if defined (__APPLE__) + pthread_setname_np ("main thread"); +#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) + struct sched_param thread_param; + int thread_sched_policy; + if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0) + { + thread_param.sched_priority = 47; + pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); + } + + ::proc_set_wakemon_params (getpid(), 500, 0); // Allow up to 500 wakeups/sec to avoid EXC_RESOURCE for normal use. +#endif +#endif + + 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); + signal (SIGHUP, signal_handler); + + // We're always sitting in waitpid or kevent waiting on our target process' death, + // we don't need no stinking SIGCHLD's... + + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + g_remoteSP.reset (new RNBRemote ()); + + + RNBRemote *remote = g_remoteSP.get(); + if (remote == NULL) + { + RNBLogSTDERR ("error: failed to create a remote connection class\n"); + return -1; + } + + RNBContext& ctx = remote->Context(); + + 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 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 arch_name; + std::string working_dir; // The new working directory to use for the inferior + std::string unix_socket_name; // If we need to handshake with our parent process, an option will be passed down that specifies a unix socket name to use + std::string named_pipe_path; // If we need to handshake with our parent process, an option will be passed down that specifies a named pipe to use + 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. + bool no_stdio = false; + bool reverse_connect = false; // Set to true by an option to indicate we should reverse connect to the host:port supplied as the first debugserver argument + +#if !defined (DNBLOG_ENABLED) + compile_options += "(no-logging) "; +#endif + + RNBRunLoopMode start_mode = eRNBRunLoopModeExit; + + char short_options[512]; + uint32_t short_options_idx = 0; + + // Handle the two case that don't have short options in g_long_options + short_options[short_options_idx++] = 'k'; + short_options[short_options_idx++] = 't'; + + for (i=0; g_long_options[i].name != NULL; ++i) + { + if (isalpha(g_long_options[i].val)) + { + short_options[short_options_idx++] = g_long_options[i].val; + switch (g_long_options[i].has_arg) + { + default: + case no_argument: + break; + + case optional_argument: + short_options[short_options_idx++] = ':'; + // Fall through to required_argument case below... + case required_argument: + short_options[short_options_idx++] = ':'; + break; + } + } + } + // NULL terminate the short option string. + short_options[short_options_idx++] = '\0'; + +#if __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + + while ((ch = getopt_long_only(argc, argv, short_options, 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]) + arch_name.assign(optarg); + break; + + case 'a': + if (optarg && optarg[0]) + { + if (isdigit(optarg[0])) + { + char *end = NULL; + attach_pid = static_cast(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 = static_cast(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 = static_cast(strtoul(optarg, &end, 0)); + if (end == NULL || *end != '\0') + { + RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg); + exit (7); + } + } + break; + + case 'K': + g_detach_on_error = false; + + case 'W': + if (optarg && optarg[0]) + working_dir.assign(optarg); + 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; +#ifdef WITH_SPRINGBOARD + else if (strcasestr(optarg, "spring") == optarg) + g_launch_flavor = eLaunchFlavorSpringBoard; +#endif +#ifdef WITH_BKS + else if (strcasestr(optarg, "backboard") == optarg) + g_launch_flavor = eLaunchFlavorBKS; +#endif +#ifdef WITH_FBS + else if (strcasestr(optarg, "frontboard") == optarg) + g_launch_flavor = eLaunchFlavorFBS; +#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"); +#ifdef WITH_SPRINGBOARD + RNBLogSTDERR (" spring Launch the executable through Springboard.\n"); +#endif +#ifdef WITH_BKS + RNBLogSTDERR (" backboard Launch the executable through BackBoard Services.\n"); +#endif +#ifdef WITH_FBS + RNBLogSTDERR (" frontboard Launch the executable through FrontBoard Services.\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) + setlinebuf(log_file); + } + + 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 = static_cast(strtoul(optarg, NULL, 0)); + break; + + case 'g': + debug = 1; + DNBLogSetDebug(debug); + break; + + case 't': + g_applist_opt = 1; + break; + + case 'k': + g_lockdown_opt = 1; + break; + + case 'r': + // Do nothing, native regs is the default these days + break; + + case 'R': + reverse_connect = true; + break; + case 'v': + DNBLogSetVerbose(1); + break; + + case 's': + ctx.GetSTDIN().assign(optarg); + ctx.GetSTDOUT().assign(optarg); + ctx.GetSTDERR().assign(optarg); + break; + + case 'I': + ctx.GetSTDIN().assign(optarg); + break; + + case 'O': + ctx.GetSTDOUT().assign(optarg); + break; + + case 'E': + ctx.GetSTDERR().assign(optarg); + break; + + case 'n': + no_stdio = true; + 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; + case 'D': + g_disable_aslr = 1; + break; + + case 'p': + start_mode = eRNBRunLoopModePlatformMode; + break; + + case 'u': + unix_socket_name.assign (optarg); + break; + + case 'P': + named_pipe_path.assign (optarg); + break; + + case 'e': + // Pass a single specified environment variable down to the process that gets launched + remote->Context().PushEnvironment(optarg); + break; + + case 'F': + // Pass the current environment down to the process that gets launched + { + char **host_env = *_NSGetEnviron(); + char *env_entry; + size_t i; + for (i=0; (env_entry = host_env[i]) != NULL; ++i) + remote->Context().PushEnvironment(env_entry); + } + break; + } + } + + if (arch_name.empty()) + { +#if defined (__arm__) + arch_name.assign ("arm"); +#endif + } + else + { + DNBSetArchitecture (arch_name.c_str()); + } + +// if (arch_name.empty()) +// { +// fprintf(stderr, "error: no architecture was specified\n"); +// exit (8); +// } + // Skip any options we consumed with getopt_long_only + argc -= optind; + argv += optind; + + + if (!working_dir.empty()) + { + if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false) + { + RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str()); + exit (8); + } + } + + remote->Context().SetDetachOnError(g_detach_on_error); + + remote->Initialize(); + + // 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 (remote->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()); + + remote->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; + remote->Comm().Read(buf); + } + remote->Comm().Disconnect(false); + mode = eRNBRunLoopModeExit; + break; + } + else + { + // Start watching for remote packets + remote->StartReadRemoteDataThread(); + } + } + } + else +#endif + if (port != INT32_MAX) + { + if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str())) + mode = eRNBRunLoopModeExit; + } + else if (str[0] == '/') + { + if (remote->Comm().OpenFile (str)) + mode = eRNBRunLoopModeExit; + } + + if (mode != eRNBRunLoopModeExit) + { + RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n"); + + mode = RNBRunLoopGetStartModeFromRemote (remote); + } + 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 WITH_FBS + // Check if we have an app bundle, if so launch using SpringBoard. + if (waitfor_pid_name.find (".app") != std::string::npos) + { + launch_flavor = eLaunchFlavorFBS; + } +#elif defined WITH_BKS + // Check if we have an app bundle, if so launch using SpringBoard. + if (waitfor_pid_name.find (".app") != std::string::npos) + { + launch_flavor = eLaunchFlavorBKS; + } +#elif defined WITH_SPRINGBOARD + // 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); + bool ignore_existing = false; + RNBLogSTDOUT ("Waiting to attach to process %s...\n", waitfor_pid_name.c_str()); + nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, ignore_existing, 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\n", 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 (remote, 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; + } + + RNBLogSTDOUT ("Attaching to process %s...\n", attach_pid_name.c_str()); + 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\n", 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.\n"); + mode = eRNBRunLoopModeExit; + } + + if (mode != eRNBRunLoopModeExit) + { + if (port != INT32_MAX) + { + if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str())) + mode = eRNBRunLoopModeExit; + } + else if (str[0] == '/') + { + if (remote->Comm().OpenFile (str)) + mode = eRNBRunLoopModeExit; + } + if (mode != eRNBRunLoopModeExit) + RNBLogSTDOUT ("Waiting for debugger instructions for process %d.\n", attach_pid); + } + break; + + case eRNBRunLoopModeInferiorLaunching: + { + mode = RNBRunLoopLaunchInferior (remote, + ctx.GetSTDINPath(), + ctx.GetSTDOUTPath(), + ctx.GetSTDERRPath(), + no_stdio); + + if (mode == eRNBRunLoopModeInferiorExecuting) + { + if (port != INT32_MAX) + { + if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str())) + mode = eRNBRunLoopModeExit; + } + else if (str[0] == '/') + { + if (remote->Comm().OpenFile (str)) + mode = eRNBRunLoopModeExit; + } + + if (mode != eRNBRunLoopModeExit) + { + const char *proc_name = ""; + if (ctx.ArgumentCount() > 0) + proc_name = ctx.ArgumentAtIndex(0); + RNBLogSTDOUT ("Got a connection, launched process %s (pid = %d).\n", proc_name, ctx.ProcessID()); + } + } + else + { + const char *error_str = remote->Context().LaunchStatus().AsString(); + RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv_sub_zero, error_str ? error_str : "unknown error."); + } + } + break; + + case eRNBRunLoopModeInferiorExecuting: + mode = RNBRunLoopInferiorExecuting(remote); + break; + + case eRNBRunLoopModePlatformMode: + if (port != INT32_MAX) + { + if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str())) + mode = eRNBRunLoopModeExit; + } + else if (str[0] == '/') + { + if (remote->Comm().OpenFile (str)) + mode = eRNBRunLoopModeExit; + } + + if (mode != eRNBRunLoopModeExit) + mode = RNBRunLoopPlatform (remote); + break; + + default: + mode = eRNBRunLoopModeExit; + case eRNBRunLoopModeExit: + break; + } + } + + remote->StopReadRemoteDataThread (); + remote->Context().SetProcessID(INVALID_NUB_PROCESS); + RNBLogSTDOUT ("Exiting.\n"); + + return 0; +} diff --git a/tools/debugserver/source/libdebugserver.cpp b/tools/debugserver/source/libdebugserver.cpp new file mode 100644 index 000000000000..63d76eb26aef --- /dev/null +++ b/tools/debugserver/source/libdebugserver.cpp @@ -0,0 +1,397 @@ +//===-- libdebugserver.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 "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" + +//---------------------------------------------------------------------- +// Run loop modes which determine which run loop function will be called +//---------------------------------------------------------------------- +typedef enum +{ + eRNBRunLoopModeInvalid = 0, + eRNBRunLoopModeGetStartModeFromRemoteProtocol, + eRNBRunLoopModeInferiorExecuting, + eRNBRunLoopModeExit +} RNBRunLoopMode; + + +//---------------------------------------------------------------------- +// Global Variables +//---------------------------------------------------------------------- +RNBRemoteSP g_remoteSP; +int g_disable_aslr = 0; +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) + + +//---------------------------------------------------------------------- +// 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) + { + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Got success...",__FUNCTION__); + continue; + } + 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; +} + + +//---------------------------------------------------------------------- +// Watch for signals: +// SIGINT: so we can halt our inferior. (disabled for now) +// SIGPIPE: in case our child process dies +//---------------------------------------------------------------------- +nub_process_t g_pid; +int g_sigpipe_received = 0; +void +signal_handler(int signo) +{ + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(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 +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 (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 %zu (old %zu)) 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 %zu (old %zu)) 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 %zu (old %zu)) 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; + case eStateDetached: + 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; +} + +void +ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args) +{ +#if 0 + vprintf(format, args); +#endif +} + +extern "C" int +debug_server_main(int fd) +{ +#if 1 + g_isatty = 0; +#else + g_isatty = ::isatty (STDIN_FILENO); + + DNBLogSetDebug(1); + DNBLogSetVerbose(1); + DNBLogSetLogMask(-1); + DNBLogSetLogCallback(ASLLogCallback, NULL); +#endif + + signal (SIGPIPE, signal_handler); + + g_remoteSP.reset (new RNBRemote); + + RNBRemote *remote = g_remoteSP.get(); + if (remote == NULL) + { + RNBLogSTDERR ("error: failed to create a remote connection class\n"); + return -1; + } + + + RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol; + + while (mode != eRNBRunLoopModeExit) + { + switch (mode) + { + case eRNBRunLoopModeGetStartModeFromRemoteProtocol: + if (g_remoteSP->Comm().useFD(fd) == rnb_success) { + RNBLogSTDOUT("Starting remote data thread.\n"); + g_remoteSP->StartReadRemoteDataThread(); + + RNBLogSTDOUT("Waiting for start mode from remote.\n"); + mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP); + } + else + { + mode = eRNBRunLoopModeExit; + } + break; + + case eRNBRunLoopModeInferiorExecuting: + mode = RNBRunLoopInferiorExecuting(g_remoteSP); + break; + + default: + mode = eRNBRunLoopModeExit; + break; + + case eRNBRunLoopModeExit: + break; + } + } + + g_remoteSP->StopReadRemoteDataThread (); + g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS); + + return 0; +} diff --git a/tools/debugserver/source/libdebugserver.h b/tools/debugserver/source/libdebugserver.h new file mode 100644 index 000000000000..2576e269ee60 --- /dev/null +++ b/tools/debugserver/source/libdebugserver.h @@ -0,0 +1,15 @@ +//===-- libdebugserver.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef debugserver_libdebugserver_h +#define debugserver_libdebugserver_h + +int debug_server_main(int fd); + +#endif diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt new file mode 100644 index 000000000000..2d4f905323e3 --- /dev/null +++ b/tools/driver/CMakeLists.txt @@ -0,0 +1,28 @@ +add_lldb_executable(lldb + Driver.cpp + Platform.cpp + ) + +if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) + add_definitions( -DIMPORT_LIBLLDB ) +endif() + +# Add lldb dependency on lldb-server if we can use it. +if ( LLDB_CAN_USE_LLDB_SERVER ) + add_dependencies(lldb lldb-server) +endif() + +# Add lldb dependency on debugserver if we can use it. +if ( LLDB_CAN_USE_DEBUGSERVER ) + add_dependencies(lldb debugserver) +endif() + +target_link_libraries(lldb liblldb) +# TODO: why isn't this done by add_lldb_executable? +#target_link_libraries(lldb ${LLDB_USED_LIBS}) +#llvm_config(lldb ${LLVM_LINK_COMPONENTS}) + +set_target_properties(lldb PROPERTIES VERSION ${LLDB_VERSION}) + +install(TARGETS lldb + RUNTIME DESTINATION bin) diff --git a/tools/driver/Makefile b/tools/driver/Makefile new file mode 100644 index 000000000000..05a245721bde --- /dev/null +++ b/tools/driver/Makefile @@ -0,0 +1,36 @@ +##===- tools/driver/Makefile -------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LLDB_LEVEL := ../.. + +TOOLNAME = lldb + +NO_PEDANTIC = 1 + +include $(LLDB_LEVEL)/Makefile + +ifneq ($(HOST_OS),MingW) +LLVMLibsOptions += -ledit -llldb -llldbUtility +else +LLVMLibsOptions += -llldb -llldbUtility +CPP.Flags += -DIMPORT_LIBLLDB +endif + +ifeq ($(HOST_OS),Darwin) + LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/ + LLVMLibsOptions += -Wl,-sectcreate -Wl,__TEXT -Wl,__info_plist -Wl,"$(PROJ_SRC_DIR)/lldb-Info.plist" +endif + +ifneq (,$(filter $(HOST_OS), Linux GNU/kFreeBSD NetBSD)) + LLVMLibsOptions += -Wl,-rpath,$(LibDir) +endif + +ifeq ($(HOST_OS),FreeBSD) + CPP.Flags += -I/usr/include/edit #-v + LLVMLibsOptions += -Wl,-rpath,$(LibDir) +endif diff --git a/tools/driver/lldb-Info.plist b/tools/driver/lldb-Info.plist new file mode 100644 index 000000000000..7c1bfc734a7f --- /dev/null +++ b/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/tools/install-headers/Makefile b/tools/install-headers/Makefile new file mode 100644 index 000000000000..384a54e6d6f6 --- /dev/null +++ b/tools/install-headers/Makefile @@ -0,0 +1,23 @@ +installsrc: + echo "installsrc (doing nothing)" + +install: + echo "install (doing nothing)" + +clean: + echo "clean (doing nothing)" + +LLDB_VERSION=`echo ${CURRENT_PROJECT_VERSION} | /usr/bin/sed -E 's/^([0-9]+).([0-9]+).([0-9]+)(.[0-9]+)?$$/\1/g'` +LLDB_REVISION=`echo ${CURRENT_PROJECT_VERSION} | /usr/bin/sed -E 's/^([0-9]+).([0-9]+).([0-9]+)(.[0-9]+)?$$/\3/g'` +LLDB_VERSION_STRING=`echo ${CURRENT_PROJECT_VERSION}` + +installhdrs: + cd "${TARGET_BUILD_DIR}/${LLDB_FRAMEWORK_INSTALL_DIR}/LLDB.framework/Headers" ;\ + for file in *.h ;\ + do \ + /usr/bin/sed -i '' 's/\(#include\)[ ]*"lldb\/\(API\/\)\{0,1\}\(.*\)"/\1 /1' "$$file" ;\ + /usr/bin/sed -i '' 's|GetValue()); - // FIXME: this won't work for header files! To try and use existing - // commands to get this to work for header files would be too slow. - // Instead, this code should be rewritten to use APIs and/or support - // should be added to lldb which would work for header files. - const CMIUtilString strCmd(CMIUtilString::Format("target modules dump line-table \"%s\"", strFilePath.AddSlashes().c_str())); + const CMIUtilString strCmd(CMIUtilString::Format("source info --file \"%s\"", strFilePath.AddSlashes().c_str())); CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); const lldb::ReturnStatus rtn = rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand(strCmd.c_str(), m_lldbResult); @@ -110,10 +106,10 @@ ParseLLDBLineAddressHeader(const char *input, CMIUtilString &file) { // Match LineEntry using regex. static MIUtilParse::CRegexParser g_lineentry_header_regex( - "^ *Line table for (.+) in `(.+)$"); - // ^1=file ^2=module + "^ *Lines found for file (.+) in compilation unit (.+) in `(.+)$"); + // ^1=file ^2=cu ^3=module - MIUtilParse::CRegexParser::Match match(3); + MIUtilParse::CRegexParser::Match match(4); const bool ok = g_lineentry_header_regex.Execute(input, match); if (ok) @@ -146,12 +142,12 @@ ParseLLDBLineAddressEntry(const char *input, CMIUtilString &addr, // Match LineEntry using regex. static MIUtilParse::CRegexParser g_lineentry_nocol_regex( - "^ *(0x[0-9a-fA-F]+): (.+):([0-9]+)$"); + "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+)$"); static MIUtilParse::CRegexParser g_lineentry_col_regex( - "^ *(0x[0-9a-fA-F]+): (.+):([0-9]+):[0-9]+$"); - // ^1=addr ^2=f ^3=line ^4=:col(opt) + "^ *\\[(0x[0-9a-fA-F]+)-(0x[0-9a-fA-F]+)\\): (.+):([0-9]+):[0-9]+$"); + // ^1=start ^2=end ^3=f ^4=line ^5=:col(opt) - MIUtilParse::CRegexParser::Match match(5); + MIUtilParse::CRegexParser::Match match(6); // First try matching the LineEntry with the column, // then try without the column. @@ -160,8 +156,8 @@ ParseLLDBLineAddressEntry(const char *input, CMIUtilString &addr, if (ok) { addr = match.GetMatchAtIndex(1); - file = match.GetMatchAtIndex(2); - line = match.GetMatchAtIndex(3); + file = match.GetMatchAtIndex(3); + line = match.GetMatchAtIndex(4); } return ok; } @@ -222,10 +218,6 @@ CMICmdCmdSymbolListLines::Acknowledge() if (!ParseLLDBLineAddressEntry(rLine.c_str(), strAddr, strFile, strLine)) continue; - // Skip entries which don't match the desired source. - if (strWantFile != strFile) - continue; - const CMICmnMIValueConst miValueConst(strAddr); const CMICmnMIValueResult miValueResult("pc", miValueConst); CMICmnMIValueTuple miValueTuple(miValueResult); diff --git a/tools/lldb-mi/Makefile b/tools/lldb-mi/Makefile new file mode 100644 index 000000000000..cdd766633b16 --- /dev/null +++ b/tools/lldb-mi/Makefile @@ -0,0 +1,32 @@ +##===- tools/lldb-mi/Makefile -------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LLDB_LEVEL := ../.. + +TOOLNAME = lldb-mi + +NO_PEDANTIC = 1 + +LLVMLibsOptions += -ledit -llldb -llldbUtility +LINK_COMPONENTS := support + +include $(LLDB_LEVEL)/Makefile + +ifeq ($(HOST_OS),Darwin) + LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/ + LLVMLibsOptions += -Wl,-sectcreate -Wl,__TEXT -Wl,__info_plist -Wl,"$(PROJ_SRC_DIR)/lldb-Info.plist" +endif + +ifneq (,$(filter $(HOST_OS), Linux GNU/kFreeBSD NetBSD)) + LLVMLibsOptions += -Wl,-rpath,$(LibDir) -lpthread +endif + +ifeq ($(HOST_OS),FreeBSD) + CPP.Flags += -I/usr/include/edit #-v + LLVMLibsOptions += -Wl,-rpath,$(LibDir) -lpthread +endif diff --git a/tools/lldb-mi/lldb-Info.plist b/tools/lldb-mi/lldb-Info.plist new file mode 100644 index 000000000000..795512691ef0 --- /dev/null +++ b/tools/lldb-mi/lldb-Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.lldb + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + lldb-mi + CFBundleVersion + 2 + SecTaskAccess + + allowed + debug + + + diff --git a/tools/lldb-perf/README b/tools/lldb-perf/README new file mode 100644 index 000000000000..7cec4faac2c8 --- /dev/null +++ b/tools/lldb-perf/README @@ -0,0 +1,295 @@ + The lldb-perf infrastructure for LLDB performance testing +=========================================================== + +lldb-perf is an infrastructure meant to simplify the creation of performance +tests for the LLDB debugger. It is contained in liblldbperf.a which is part of +the standard opensource checkout of LLDB + +Its main concepts are: +- Gauges: a gauge is a thing that takes a sample. Samples include elapsed time, + memory used, and energy consumed. +- Metrics: a metric is a collection of samples that knows how to do statistics + like sum() and average(). Metrics can be extended as needed. +- Measurements: a measurement is the thing that stores an action, a gauge and + a metric. You define measurements as in “take the time to run this function”, + “take the memory to run this block of code”, and then after you invoke it, + your stats will automagically be there. +- Tests: a test is a sequence of steps and measurements. + +Tests cases should be added as targets to the lldbperf.xcodeproj project. It +is probably easiest to duplicate one of the existing targets. In order to +write a test based on lldb-perf, you need to subclass lldb_perf::TestCase: + +using namespace lldb_perf; + +class FormattersTest : public TestCase +{ + +Usually, you will define measurements as variables of your test case class: + +private: + // C++ formatters + TimeMeasurement> m_dump_std_vector_measurement; + TimeMeasurement> m_dump_std_list_measurement; + TimeMeasurement> m_dump_std_map_measurement; + TimeMeasurement> m_dump_std_string_measurement; + + // Cocoa formatters + TimeMeasurement> m_dump_nsstring_measurement; + TimeMeasurement> m_dump_nsarray_measurement; + TimeMeasurement> m_dump_nsdictionary_measurement; + TimeMeasurement> m_dump_nsset_measurement; + TimeMeasurement> m_dump_nsbundle_measurement; + TimeMeasurement> m_dump_nsdate_measurement; + +A TimeMeasurement is, obviously, a class that measures “how much time to run +this block of code”. The block of code is passed as an std::function which you +can construct with a lambda! You need to give the prototype of your block of +code. In this example, we run blocks of code that take an SBValue and return +nothing. + +These blocks look like: + + m_dump_std_vector_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "std-vector", "time to dump an std::vector"); + +Here we are saying: make me a measurement named “std-vector”, whose +description is “time to dump an std::vector” and that takes the time required +to call lldb_perf::Xcode::FetchVariable(value,1,false). + +The Xcode class is a collection of utility functions that replicate common +Xcode patterns (FetchVariable unsurprisingly calls API functions that Xcode +could use when populating a variables view entry - the 1 means “expand 1 level +of depth” and the false means “do not dump the data to stdout”) + +A full constructor for a TestCase looks like: + +FormattersTest () : TestCase() +{ + m_dump_std_vector_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "std-vector", "time to dump an std::vector"); + m_dump_std_list_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "std-list", "time to dump an std::list"); + m_dump_std_map_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "std-map", "time to dump an std::map"); + m_dump_std_string_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "std-string", "time to dump an std::string"); + + m_dump_nsstring_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,0,false); + }, "ns-string", "time to dump an NSString"); + + m_dump_nsarray_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "ns-array", "time to dump an NSArray"); + + m_dump_nsdictionary_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "ns-dictionary", "time to dump an NSDictionary"); + + m_dump_nsset_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "ns-set", "time to dump an NSSet"); + + m_dump_nsbundle_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "ns-bundle", "time to dump an NSBundle"); + + m_dump_nsdate_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,0,false); + }, "ns-date", "time to dump an NSDate"); +} + +Once your test case is constructed, Setup() is called on it: + + virtual bool + Setup (int argc, const char** argv) + { + m_app_path.assign(argv[1]); + m_out_path.assign(argv[2]); + m_target = m_debugger.CreateTarget(m_app_path.c_str()); + m_target.BreakpointCreateByName("main"); + SBLaunchInfo launch_info (argv); + return Launch (launch_info); + } + +Setup() returns a boolean value that indicates if setup was successful. +In Setup() you fill out a SBLaunchInfo with any needed settings for launching +your process like arguments, environment variables, working directory, and +much more. + +The last thing you want to do in setup is call Launch(): + + bool + Launch (coSBLaunchInfo &launch_info); + +This ensures your target is now alive. Make sure to have a breakpoint created. + +Once you launched, the event loop is entered. The event loop waits for stops, +and when it gets one, it calls your test case’s TestStep() function: + + virtual void + TestStep (int counter, ActionWanted &next_action) + +the counter is the step id (a monotonically increasing counter). In TestStep() +you will essentially run your measurements and then return what you want the +driver to do by filling in the ActionWanted object named "next_action". + +Possible options are: +- continue process next_action.Continue(); +- kill process next_action.Kill(); +- Step-out on a thread next_action.StepOut(SBThread) +- step-over on a thread. next_action.StepOver(SBThread) + +If you use ActionWanted::Next() or ActionWanted::Finish() you need to specify +a thread to use. By default the TestCase class will select the first thread +that had a stop reason other than eStopReasonNone and place it into the +m_thread member variable of TestCase. This means if your test case hits a +breakpoint or steps, the thread that hit the breakpoint or finished the step +will automatically be selected in the process (m_process) and m_thread will +be set to this thread. If you have one or more threads that will stop with a +reason simultaneously, you will need to find those threads manually by +iterating through the process list and determine what to do next. + +For your convenience TestCase has m_debugger, m_target and m_process as member +variables. As state above m_thread will be filled in with the first thread +that has a stop reason. + +An example: + + virtual void + TestStep (int counter, ActionWanted &next_action) + { + case 0: + m_target.BreakpointCreateByLocation("fmts_tester.mm", 68); + next_action.Continue(); + break; + case 1: + DoTest (); + next_action.Continue(); + break; + case 2: + DoTest (); + next_action.StepOver(m_thread); + break; + +DoTest() is a function I define in my own class that calls the measurements: + void + DoTest () + { + SBThread thread_main(m_thread); + SBFrame frame_zero(thread_main.GetFrameAtIndex(0)); + + m_dump_nsarray_measurement(frame_zero.FindVariable("nsarray", lldb::eDynamicCanRunTarget)); + m_dump_nsarray_measurement(frame_zero.FindVariable("nsmutablearray", lldb::eDynamicCanRunTarget)); + + m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsdictionary", lldb::eDynamicCanRunTarget)); + m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsmutabledictionary", lldb::eDynamicCanRunTarget)); + + m_dump_nsstring_measurement(frame_zero.FindVariable("str0", lldb::eDynamicCanRunTarget)); + m_dump_nsstring_measurement(frame_zero.FindVariable("str1", lldb::eDynamicCanRunTarget)); + m_dump_nsstring_measurement(frame_zero.FindVariable("str2", lldb::eDynamicCanRunTarget)); + m_dump_nsstring_measurement(frame_zero.FindVariable("str3", lldb::eDynamicCanRunTarget)); + m_dump_nsstring_measurement(frame_zero.FindVariable("str4", lldb::eDynamicCanRunTarget)); + + m_dump_nsdate_measurement(frame_zero.FindVariable("me", lldb::eDynamicCanRunTarget)); + m_dump_nsdate_measurement(frame_zero.FindVariable("cutie", lldb::eDynamicCanRunTarget)); + m_dump_nsdate_measurement(frame_zero.FindVariable("mom", lldb::eDynamicCanRunTarget)); + m_dump_nsdate_measurement(frame_zero.FindVariable("dad", lldb::eDynamicCanRunTarget)); + m_dump_nsdate_measurement(frame_zero.FindVariable("today", lldb::eDynamicCanRunTarget)); + + m_dump_nsbundle_measurement(frame_zero.FindVariable("bundles", lldb::eDynamicCanRunTarget)); + m_dump_nsbundle_measurement(frame_zero.FindVariable("frameworks", lldb::eDynamicCanRunTarget)); + + m_dump_nsset_measurement(frame_zero.FindVariable("nsset", lldb::eDynamicCanRunTarget)); + m_dump_nsset_measurement(frame_zero.FindVariable("nsmutableset", lldb::eDynamicCanRunTarget)); + + m_dump_std_vector_measurement(frame_zero.FindVariable("vector", lldb::eDynamicCanRunTarget)); + m_dump_std_list_measurement(frame_zero.FindVariable("list", lldb::eDynamicCanRunTarget)); + m_dump_std_map_measurement(frame_zero.FindVariable("map", lldb::eDynamicCanRunTarget)); + + m_dump_std_string_measurement(frame_zero.FindVariable("sstr0", lldb::eDynamicCanRunTarget)); + m_dump_std_string_measurement(frame_zero.FindVariable("sstr1", lldb::eDynamicCanRunTarget)); + m_dump_std_string_measurement(frame_zero.FindVariable("sstr2", lldb::eDynamicCanRunTarget)); + m_dump_std_string_measurement(frame_zero.FindVariable("sstr3", lldb::eDynamicCanRunTarget)); + m_dump_std_string_measurement(frame_zero.FindVariable("sstr4", lldb::eDynamicCanRunTarget)); + } + +Essentially, you call your measurements as if they were functions, passing +them arguments and all, and they will do the right thing with gathering stats. + +The last step is usually to KILL the inferior and bail out: + + virtual ActionWanted + TestStep (int counter) + { +... + case 9: + DoTest (); + next_action.Continue(); + break; + case 10: + DoTest (); + next_action.Continue(); + break; + default: + next_action.Kill(); + break; + } + + +At the end, you define a Results() function: + + void + Results () + { + CFCMutableArray array; + m_dump_std_vector_measurement.Write(array); + m_dump_std_list_measurement.Write(array); + m_dump_std_map_measurement.Write(array); + m_dump_std_string_measurement.Write(array); + + m_dump_nsstring_measurement.Write(array); + m_dump_nsarray_measurement.Write(array); + m_dump_nsdictionary_measurement.Write(array); + m_dump_nsset_measurement.Write(array); + m_dump_nsbundle_measurement.Write(array); + m_dump_nsdate_measurement.Write(array); + + CFDataRef xmlData = CFPropertyListCreateData (kCFAllocatorDefault, + array.get(), + kCFPropertyListXMLFormat_v1_0, + 0, + NULL); + + CFURLRef file = CFURLCreateFromFileSystemRepresentation (NULL, + (const UInt8*)m_out_path.c_str(), + m_out_path.size(), + FALSE); + + CFURLWriteDataAndPropertiesToResource(file,xmlData,NULL,NULL); + } + +For now, pretty much copy this and just call Write() on all your measurements. +I plan to move this higher in the hierarchy (e.g. make a +TestCase::Write(filename) fairly soon). + +Your main() will look like: + +int main(int argc, const char * argv[]) +{ + MyTest test; + TestCase::Run (test, argc, argv); + return 0; +} + +If you are debugging your test, before Run() call + + test.SetVerbose(true); + +Feel free to send any questions and ideas for improvements. diff --git a/tools/lldb-perf/common/clang/build-clang.sh b/tools/lldb-perf/common/clang/build-clang.sh new file mode 100755 index 000000000000..3d9add79c4ab --- /dev/null +++ b/tools/lldb-perf/common/clang/build-clang.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +if [ -d "llvm-build" ]; then + echo "Using existing 'llvm-build' directory..." +else + mkdir llvm-build +fi + +cd llvm-build + +if [ -d "llvm" ]; then + echo "Using existing 'llvm' directory..." +else + svn co --revision 176809 http://llvm.org/svn/llvm-project/llvm/trunk llvm + ( cd llvm/tools ; svn co --revision 176809 http://llvm.org/svn/llvm-project/cfe/trunk clang ) +fi + +if [ ! -d "build" ]; then + mkdir build + cd build + ../llvm/configure --enable-targets=x86_64,arm --build=x86_64-apple-darwin10 --disable-optimized --disable-assertions --enable-libcpp + make -j8 clang-only DEBUG_SYMBOLS=1 + rm -rf lib projects runtime unittests utils config.* + ( cd ./Debug/bin ; rm -rf ll* clang-check clang-tblgen count diagtool fpcmp macho-dump not opt yaml2obj FileCheck FileUpdate arcmt-test c-arcmt-test c-index-test bugpoint ) + ( cd ./tools ; rm -rf ll* clang-check clang-tblgen count diagtool fpcmp lto macho-dump not opt yaml2obj FileCheck FileUpdate arcmt-test c-arcmt-test c-index-test bugpoint ) + ( cd ./tools/clang ; rm -rf lib unittests utils ) + ( cd ./tools/clang/tools ; rm -rf arcmt-test c-arcmt-test c-index-test clang-check diagtool libclang ) + ( cd ../llvm ; rm -rf cmake configure docs examples projects *.txt *.TXT autoconf bindings test unittests utils ; find . -type d -name .svn -print0 | xargs -0 rm -rf ) + ( cd ../llvm/tools ; rm -rf *.txt bugpoint bugpoint-passes ll* lto macho-dump opt gold ) +fi + + + diff --git a/tools/lldb-perf/common/clang/lldb_perf_clang.cpp b/tools/lldb-perf/common/clang/lldb_perf_clang.cpp new file mode 100644 index 000000000000..ac9481c366ec --- /dev/null +++ b/tools/lldb-perf/common/clang/lldb_perf_clang.cpp @@ -0,0 +1,484 @@ +//===-- lldb_perf_clang.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb-perf/lib/Timer.h" +#include "lldb-perf/lib/Metric.h" +#include "lldb-perf/lib/Measurement.h" +#include "lldb-perf/lib/Results.h" +#include "lldb-perf/lib/TestCase.h" +#include "lldb-perf/lib/Xcode.h" +#include "llvm/ADT/STLExtras.h" +#include +#include +#include +#include + +using namespace lldb_perf; + +#define NUM_EXPR_ITERATIONS 3 +class ClangTest : public TestCase +{ +public: + ClangTest () : + TestCase(), + m_time_create_target ([this] () -> void + { + m_memory_change_create_target.Start(); + m_target = m_debugger.CreateTarget(m_exe_path.c_str()); + m_memory_change_create_target.Stop(); + }, "time-create-target", "The time it takes to create a target."), + m_time_set_bp_main([this] () -> void + { + m_memory_change_break_main.Start(); + m_target.BreakpointCreateByName("main"); + m_memory_change_break_main.Stop(); + }, "time-set-break-main", "Elapsed time it takes to set a breakpoint at 'main' by name."), + m_memory_change_create_target (), + m_memory_change_break_main (), + m_memory_total (), + m_time_launch_stop_main(), + m_time_total (), + m_expr_first_evaluate([this] (SBFrame frame) -> void + { + frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError(); + }, "time-expr", "Elapsed time it takes to evaluate an expression for the first time."), + m_expr_frame_zero ([this] (SBFrame frame) -> void + { + frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError(); + }, "time-expr-frame-zero", "Elapsed time it takes to evaluate an expression 3 times at frame zero."), + m_expr_frame_non_zero ([this] (SBFrame frame) -> void + { + frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError(); + }, "time-expr-frame-non-zero", "Elapsed time it takes to evaluate an expression 3 times at a non-zero frame."), + m_exe_path(), + m_out_path(), + m_launch_info (NULL), + m_use_dsym (false) + { + } + + virtual + ~ClangTest () + { + } + + virtual bool + Setup (int& argc, const char**& argv) + { + if (m_exe_path.empty()) + return false; + m_launch_info.SetArguments(argv, false); + return true; + } + + void + DoTest () + { + } + + virtual void + TestStep (int counter, ActionWanted &next_action) + { + char temp_source_path[PATH_MAX] = "/tmp/main.XXXXXX.cpp"; + + switch (counter) + { + case 0: + { + //Xcode::RunCommand(m_debugger,"log enable -f /tmp/packets.txt gdb-remote packets",true); + + m_memory_total.Start(); + m_time_total.Start(); + + // Time creating the target + m_time_create_target(); + + m_time_set_bp_main(); + + int fd = mkstemps(temp_source_path, 4); + + if (fd >= 0) + { + const char *source_content = R"( +#include +#include +#include + +namespace { + struct Foo + { + int i; int j; + }; + void doit (const Foo &foo) + { + printf ("doit(%i)\n", foo.i); + } +} + +int main (int argc, char const *argv[], char const *envp[]) +{ + std::vector ints; + for (int i=0;i<10;++i) + ints.push_back(i); + printf ("hello world\n"); + Foo foo = { 12, 13 }; + doit (foo); + return 0; +} +)"; + write (fd, source_content, strlen(source_content)); + close(fd); + } + else + { + const char *error_cstr = strerror(errno); + fprintf (stderr, "error: failed to created temporary source file: '%s' (%s)", temp_source_path, error_cstr); + exit(2); + } + + m_time_launch_stop_main.Start(); + const char *clang_argv[] = { + "-cc1", + "-triple", "x86_64-apple-macosx10.8.0", + "-emit-obj", + "-mrelax-all", + "-disable-free", + "-disable-llvm-verifier", + "-main-file-name", "main.cpp", + "-mrelocation-model", "pic", + "-pic-level", "2", + "-mdisable-fp-elim", + "-masm-verbose", + "-munwind-tables", + "-target-cpu", "core2", + "-target-linker-version", "132.10.1", + "-v", + "-g", + "-O0", + "-fdeprecated-macro", + "-ferror-limit", "19", + "-fmessage-length", "298", + "-stack-protector", "1", + "-mstackrealign", + "-fblocks", + "-fobjc-runtime=macosx-10.8.0", + "-fobjc-dispatch-method=mixed", + "-fencode-extended-block-signature", + "-fcxx-exceptions", + "-fexceptions", + "-fdiagnostics-show-option", + "-fcolor-diagnostics", + "-backend-option", + "-vectorize-loops", + "-o", "/tmp/main.o", + "-x", "c++", + NULL, + NULL }; + clang_argv[llvm::array_lengthof(clang_argv)-2] = temp_source_path; + SBLaunchInfo launch_info(clang_argv); + Launch (launch_info); + next_action.None(); // Don't continue or do anything, just wait for next event... + } + break; + case 1: + { + m_time_launch_stop_main.Stop(); + m_time_total.Stop(); + SBFrame frame (m_thread.GetFrameAtIndex(0)); + + // Time the first expression evaluation + m_expr_first_evaluate(frame); + + SBValue result; + for (size_t i=0; i> m_time_create_target; + TimeMeasurement> m_time_set_bp_main; + MemoryGauge m_memory_change_create_target; + MemoryGauge m_memory_change_break_main; + MemoryGauge m_memory_total; + TimeGauge m_time_launch_stop_main; + TimeGauge m_time_total; + TimeMeasurement> m_expr_first_evaluate; + TimeMeasurement> m_expr_frame_zero; + TimeMeasurement> m_expr_frame_non_zero; + std::string m_exe_path; + std::string m_out_path; + SBLaunchInfo m_launch_info; + bool m_use_dsym; + +}; + + +struct Options +{ + std::string clang_path; + std::string out_file; + bool verbose; + bool use_dsym; + bool error; + bool print_help; + + Options() : + verbose (false), + error (false), + print_help (false) + { + } +}; + +static struct option g_long_options[] = { + { "verbose", no_argument, NULL, 'v' }, + { "clang", required_argument, NULL, 'c' }, + { "out-file", required_argument, NULL, 'o' }, + { "dsym", no_argument, NULL, 'd' }, + { NULL, 0, NULL, 0 } +}; + + +std::string +GetShortOptionString (struct option *long_options) +{ + std::string option_string; + 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 (2, ':'); + break; + } + } + } + return option_string; +} + +int main(int argc, const char * argv[]) +{ + + // Prepare for & make calls to getopt_long_only. + + std::string short_option_string (GetShortOptionString(g_long_options)); + + ClangTest test; + + Options option_data; + bool done = false; + +#if __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + while (!done) + { + int long_options_index = -1; + const int short_option = ::getopt_long_only (argc, + const_cast(argv), + short_option_string.c_str(), + g_long_options, + &long_options_index); + + switch (short_option) + { + case 0: + // Already handled + break; + + case -1: + done = true; + break; + + case '?': + option_data.print_help = true; + break; + + case 'h': + option_data.print_help = true; + break; + + case 'v': + option_data.verbose = true; + break; + + case 'c': + { + SBFileSpec file(optarg); + if (file.Exists()) + test.SetExecutablePath(optarg); + else + fprintf(stderr, "error: file specified in --clang (-c) option doesn't exist: '%s'\n", optarg); + } + break; + + case 'o': + test.SetResultFilePath(optarg); + break; + + case 'd': + test.SetUseDSYM(true); + break; + + default: + option_data.error = true; + option_data.print_help = true; + fprintf (stderr, "error: unrecognized option %c\n", short_option); + break; + } + } + + + if (test.GetExecutablePath() == NULL) + { + // --clang is mandatory + option_data.print_help = true; + option_data.error = true; + fprintf (stderr, "error: the '--clang=PATH' option is mandatory\n"); + } + + if (option_data.print_help) + { + puts(R"( +NAME + lldb_perf_clang -- a tool that measures LLDB peformance while debugging clang. + +SYNOPSIS + lldb_perf_clang --clang=PATH [--out-file=PATH --verbose --dsym] -- [clang options] + +DESCRIPTION + Runs a set of static timing and memory tasks against clang and outputs results + to a plist file. +)"); + } + if (option_data.error) + { + exit(1); + } + + // Update argc and argv after parsing options + argc -= optind; + argv += optind; + + test.SetVerbose(true); + TestCase::Run(test, argc, argv); + return 0; +} + diff --git a/tools/lldb-perf/common/clang/main.cpp b/tools/lldb-perf/common/clang/main.cpp new file mode 100644 index 000000000000..709c3946fb2f --- /dev/null +++ b/tools/lldb-perf/common/clang/main.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +namespace { + struct Foo + { + int i; int j; + }; + void doit (const Foo &foo) + { + printf ("doit(%i)\n", foo.i); + } +} +int main (int argc, char const *argv[], char const *envp[]) +{ + std::vector ints; + for (int i=0;i<10;++i) + ints.push_back(i); + printf ("hello world\n"); + Foo foo = { 12, 13 }; + doit (foo); + return 0; +} diff --git a/tools/lldb-perf/common/stepping/lldb-perf-stepping.cpp b/tools/lldb-perf/common/stepping/lldb-perf-stepping.cpp new file mode 100644 index 000000000000..bb607ef06fa7 --- /dev/null +++ b/tools/lldb-perf/common/stepping/lldb-perf-stepping.cpp @@ -0,0 +1,335 @@ +#include + +#include "lldb-perf/lib/Timer.h" +#include "lldb-perf/lib/Metric.h" +#include "lldb-perf/lib/Measurement.h" +#include "lldb-perf/lib/TestCase.h" +#include "lldb-perf/lib/Xcode.h" + +#include +#include +#include + +using namespace lldb_perf; + +class StepTest : public TestCase +{ + typedef void (*no_function) (void); + +public: + StepTest(bool use_single_stepping = false) : + m_main_source("stepping-testcase.cpp"), + m_use_single_stepping(use_single_stepping), + m_time_measurements(nullptr) + { + } + + virtual + ~StepTest() {} + + virtual bool + Setup (int& argc, const char**& argv) + { + TestCase::Setup (argc, argv); + + // Toggle the fast stepping command on or off as required. + const char *single_step_cmd = "settings set target.use-fast-stepping false"; + const char *fast_step_cmd = "settings set target.use-fast-stepping true"; + const char *cmd_to_use; + + if (m_use_single_stepping) + cmd_to_use = single_step_cmd; + else + cmd_to_use = fast_step_cmd; + + SBCommandReturnObject return_object; + m_debugger.GetCommandInterpreter().HandleCommand(cmd_to_use, + return_object); + if (!return_object.Succeeded()) + { + if (return_object.GetError() != NULL) + printf ("Got an error running settings set: %s.\n", return_object.GetError()); + else + printf ("Failed running settings set, no error.\n"); + } + + m_target = m_debugger.CreateTarget(m_app_path.c_str()); + m_first_bp = m_target.BreakpointCreateBySourceRegex("Here is some code to stop at originally.", m_main_source); + + const char* file_arg = m_app_path.c_str(); + const char* empty = nullptr; + const char* args[] = {file_arg, empty}; + SBLaunchInfo launch_info (args); + + return Launch (launch_info); + } + + void + WriteResults (Results &results) + { + // Gotta turn off the last timer now. + m_individual_step_times.push_back(m_time_measurements.Stop()); + + size_t num_time_measurements = m_individual_step_times.size(); + + Results::Dictionary& results_dict = results.GetDictionary(); + const char *short_format_string = "step-time-%0.2d"; + const size_t short_size = strlen(short_format_string) + 5; + char short_buffer[short_size]; + const char *long_format_string = "The time it takes for step %d in the step sequence."; + const size_t long_size = strlen(long_format_string) + 5; + char long_buffer[long_size]; + + for (size_t i = 0; i < num_time_measurements; i++) + { + snprintf (short_buffer, short_size, short_format_string, i); + snprintf (long_buffer, long_size, long_format_string, i); + + results_dict.AddDouble(short_buffer, + long_buffer, + m_individual_step_times[i]); + + } + results_dict.AddDouble ("total-time", "Total time spent stepping.", m_time_measurements.GetMetric().GetSum()); + results_dict.AddDouble ("stddev-time", "StdDev of time spent stepping.", m_time_measurements.GetMetric().GetStandardDeviation()); + + results.Write(m_out_path.c_str()); + } + + + const char * + GetExecutablePath () const + { + if (m_app_path.empty()) + return NULL; + return m_app_path.c_str(); + } + + const char * + GetResultFilePath () const + { + if (m_out_path.empty()) + return NULL; + return m_out_path.c_str(); + } + + void + SetExecutablePath (const char *path) + { + if (path && path[0]) + m_app_path = path; + else + m_app_path.clear(); + } + + void + SetResultFilePath (const char *path) + { + if (path && path[0]) + m_out_path = path; + else + m_out_path.clear(); + } + + void + SetUseSingleStep (bool use_it) + { + m_use_single_stepping = use_it; + } +private: + virtual void + TestStep (int counter, ActionWanted &next_action) + { + if (counter > 0) + { + m_individual_step_times.push_back(m_time_measurements.Stop()); + + } + + // Disable the breakpoint, just in case it gets multiple locations we don't want that confusing the stepping. + if (counter == 0) + m_first_bp.SetEnabled(false); + + next_action.StepOver(m_process.GetThreadAtIndex(0)); + m_time_measurements.Start(); + + + } + + SBBreakpoint m_first_bp; + SBFileSpec m_main_source; + TimeMeasurement m_time_measurements; + std::vector m_individual_step_times; + bool m_use_single_stepping; + std::string m_app_path; + std::string m_out_path; + + +}; + +struct Options +{ + std::string test_file_path; + std::string out_file; + bool verbose; + bool fast_step; + bool error; + bool print_help; + + Options() : + verbose (false), + fast_step (true), + error (false), + print_help (false) + { + } +}; + +static struct option g_long_options[] = { + { "verbose", no_argument, NULL, 'v' }, + { "single-step", no_argument, NULL, 's' }, + { "test-file", required_argument, NULL, 't' }, + { "out-file", required_argument, NULL, 'o' }, + { NULL, 0, NULL, 0 } +}; + + +std::string +GetShortOptionString (struct option *long_options) +{ + std::string option_string; + 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 (2, ':'); + break; + } + } + } + return option_string; +} + +int main(int argc, const char * argv[]) +{ + + // Prepare for & make calls to getopt_long_only. + + std::string short_option_string (GetShortOptionString(g_long_options)); + + StepTest test; + + Options option_data; + bool done = false; + +#if __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + while (!done) + { + int long_options_index = -1; + const int short_option = ::getopt_long_only (argc, + const_cast(argv), + short_option_string.c_str(), + g_long_options, + &long_options_index); + + switch (short_option) + { + case 0: + // Already handled + break; + + case -1: + done = true; + break; + + case '?': + option_data.print_help = true; + break; + + case 'h': + option_data.print_help = true; + break; + + case 'v': + option_data.verbose = true; + break; + + case 's': + option_data.fast_step = false; + test.SetUseSingleStep(true); + break; + + case 't': + { + SBFileSpec file(optarg); + if (file.Exists()) + test.SetExecutablePath(optarg); + else + fprintf(stderr, "error: file specified in --test-file (-t) option doesn't exist: '%s'\n", optarg); + } + break; + + case 'o': + test.SetResultFilePath(optarg); + break; + + default: + option_data.error = true; + option_data.print_help = true; + fprintf (stderr, "error: unrecognized option %c\n", short_option); + break; + } + } + + + if (option_data.print_help) + { + puts(R"( +NAME + lldb-perf-stepping -- a tool that measures LLDB peformance of simple stepping operations. + +SYNOPSIS + lldb-perf-stepping --test-file=FILE [--out-file=PATH --verbose --fast-step] + +DESCRIPTION + Runs a set of stepping operations, timing each step and outputs results + to a plist file. +)"); + exit(0); + } + if (option_data.error) + { + exit(1); + } + + if (test.GetExecutablePath() == NULL) + { + // --clang is mandatory + option_data.print_help = true; + option_data.error = true; + fprintf (stderr, "error: the '--test-file=PATH' option is mandatory\n"); + } + + // Update argc and argv after parsing options + argc -= optind; + argv += optind; + + test.SetVerbose(true); + TestCase::Run(test, argc, argv); + return 0; +} diff --git a/tools/lldb-perf/common/stepping/stepping-testcase.cpp b/tools/lldb-perf/common/stepping/stepping-testcase.cpp new file mode 100644 index 000000000000..f842c2379c1a --- /dev/null +++ b/tools/lldb-perf/common/stepping/stepping-testcase.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +struct struct_for_copying +{ + struct_for_copying (int in_int, double in_double, const char *in_string) : + int_value(in_int), + double_value(in_double), + string_value (in_string) + { + + } + struct_for_copying() + { + struct_for_copying (0, 0, ""); + } + + int int_value; + double double_value; + std::string string_value; +}; + +int main (int argc, char **argv) +{ + struct_for_copying input_struct (150 * argc, 10.0 * argc, argv[0]); + struct_for_copying output_struct; + int some_int = 44; + double some_double = 34.5; + double other_double; + size_t vector_size; + std::vector my_vector; + + printf ("Here is some code to stop at originally. Got: %d, %p.\n", argc, argv); + output_struct = input_struct; + other_double = (some_double * some_int)/((double) argc); + other_double = other_double > 0 ? some_double/other_double : some_double > 0 ? other_double/some_double : 10.0; + my_vector.push_back (input_struct); + vector_size = my_vector.size(); + + return vector_size == 0 ? 0 : 1; +} diff --git a/tools/lldb-perf/darwin/formatters/fmts_tester.mm b/tools/lldb-perf/darwin/formatters/fmts_tester.mm new file mode 100644 index 000000000000..57ce008297d9 --- /dev/null +++ b/tools/lldb-perf/darwin/formatters/fmts_tester.mm @@ -0,0 +1,79 @@ +//===-- fmts_tester.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#import +#include +#include +#include +#include + +int main() +{ + NSArray* nsarray = @[@1,@2,@"hello world",@3,@4,@"foobar"]; + NSMutableArray* nsmutablearray = [[NSMutableArray alloc] initWithCapacity:5]; + [nsmutablearray addObject:@1]; + [nsmutablearray addObject:@2]; + [nsmutablearray addObject:@"hello world"]; + [nsmutablearray addObject:@3]; + [nsmutablearray addObject:@4]; + [nsmutablearray addObject:@"foobar"]; + NSDictionary* nsdictionary = @{@1 : @1, @2 : @2, @"hello" : @"world", @3 : @3}; + NSMutableDictionary* nsmutabledictionary = [[NSMutableDictionary alloc] initWithCapacity:5]; + [nsmutabledictionary setObject:@1 forKey:@1]; + [nsmutabledictionary setObject:@2 forKey:@2]; + [nsmutabledictionary setObject:@"hello" forKey:@"world"]; + [nsmutabledictionary setObject:@3 forKey:@3]; + NSString* str0 = @"Hello world"; + NSString* str1 = @"Hello ℥"; + NSString* str2 = @"Hello world"; + NSString* str3 = @"Hello ℥"; + NSString* str4 = @"Hello world"; + NSDate* me = [NSDate dateWithNaturalLanguageString:@"April 10, 1985"]; + NSDate* cutie = [NSDate dateWithNaturalLanguageString:@"January 29, 1983"]; + NSDate* mom = [NSDate dateWithNaturalLanguageString:@"May 24, 1959"]; + NSDate* dad = [NSDate dateWithNaturalLanguageString:@"October 29, 1954"]; + NSDate* today = [NSDate dateWithNaturalLanguageString:@"March 14, 2013"]; + NSArray* bundles = [NSBundle allBundles]; + NSArray* frameworks = [NSBundle allFrameworks]; + NSSet* nsset = [NSSet setWithArray:nsarray]; + NSMutableSet* nsmutableset = [NSMutableSet setWithCapacity:5]; + [nsmutableset addObject:@1]; + [nsmutableset addObject:@2]; + [nsmutableset addObject:@"hello world"]; + [nsmutableset addObject:@3]; + [nsmutableset addObject:@4]; + [nsmutableset addObject:@"foobar"]; + std::vector vector; + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + vector.push_back(4); + vector.push_back(5); + std::list list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.push_back(4); + list.push_back(5); + std::map map; + map[1] = 1; + map[2] = 2; + map[3] = 3; + map[4] = 4; + map[5] = 5; + std::string sstr0("Hello world"); + std::string sstr1("Hello world"); + std::string sstr2("Hello world"); + std::string sstr3("Hello world"); + std::string sstr4("Hello world"); + int x = 0; + for (;;) + x++; +} \ No newline at end of file diff --git a/tools/lldb-perf/darwin/formatters/formatters.cpp b/tools/lldb-perf/darwin/formatters/formatters.cpp new file mode 100644 index 000000000000..ee3875618427 --- /dev/null +++ b/tools/lldb-perf/darwin/formatters/formatters.cpp @@ -0,0 +1,246 @@ +//===-- formatters.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-perf/lib/Timer.h" +#include "lldb-perf/lib/Metric.h" +#include "lldb-perf/lib/Measurement.h" +#include "lldb-perf/lib/TestCase.h" +#include "lldb-perf/lib/Xcode.h" + +#include +#include +#include + +using namespace lldb_perf; + +class FormattersTest : public TestCase +{ +public: + FormattersTest () : TestCase() + { + m_dump_std_vector_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "std-vector", "time to dump an std::vector"); + m_dump_std_list_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "std-list", "time to dump an std::list"); + m_dump_std_map_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "std-map", "time to dump an std::map"); + + // use this in manual mode + m_dump_std_string_measurement = CreateTimeMeasurement([] () -> void { + }, "std-string", "time to dump an std::string"); + + m_dump_nsstring_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,0,false); + }, "ns-string", "time to dump an NSString"); + + m_dump_nsarray_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "ns-array", "time to dump an NSArray"); + + m_dump_nsdictionary_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "ns-dictionary", "time to dump an NSDictionary"); + + m_dump_nsset_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "ns-set", "time to dump an NSSet"); + + m_dump_nsbundle_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,1,false); + }, "ns-bundle", "time to dump an NSBundle"); + + m_dump_nsdate_measurement = CreateTimeMeasurement([] (SBValue value) -> void { + lldb_perf::Xcode::FetchVariable (value,0,false); + }, "ns-date", "time to dump an NSDate"); + } + + virtual + ~FormattersTest () + { + } + + virtual bool + Setup (int& argc, const char**& argv) + { + m_app_path.assign(argv[1]); + m_out_path.assign(argv[2]); + m_target = m_debugger.CreateTarget(m_app_path.c_str()); + m_target.BreakpointCreateByName("main"); + SBLaunchInfo launch_info(argv); + return Launch (launch_info); + } + + void + DoTest () + { + SBFrame frame_zero(m_thread.GetFrameAtIndex(0)); + + m_dump_nsarray_measurement(frame_zero.FindVariable("nsarray", lldb::eDynamicCanRunTarget)); + m_dump_nsarray_measurement(frame_zero.FindVariable("nsmutablearray", lldb::eDynamicCanRunTarget)); + + m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsdictionary", lldb::eDynamicCanRunTarget)); + m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsmutabledictionary", lldb::eDynamicCanRunTarget)); + + m_dump_nsstring_measurement(frame_zero.FindVariable("str0", lldb::eDynamicCanRunTarget)); + m_dump_nsstring_measurement(frame_zero.FindVariable("str1", lldb::eDynamicCanRunTarget)); + m_dump_nsstring_measurement(frame_zero.FindVariable("str2", lldb::eDynamicCanRunTarget)); + m_dump_nsstring_measurement(frame_zero.FindVariable("str3", lldb::eDynamicCanRunTarget)); + m_dump_nsstring_measurement(frame_zero.FindVariable("str4", lldb::eDynamicCanRunTarget)); + + m_dump_nsdate_measurement(frame_zero.FindVariable("me", lldb::eDynamicCanRunTarget)); + m_dump_nsdate_measurement(frame_zero.FindVariable("cutie", lldb::eDynamicCanRunTarget)); + m_dump_nsdate_measurement(frame_zero.FindVariable("mom", lldb::eDynamicCanRunTarget)); + m_dump_nsdate_measurement(frame_zero.FindVariable("dad", lldb::eDynamicCanRunTarget)); + m_dump_nsdate_measurement(frame_zero.FindVariable("today", lldb::eDynamicCanRunTarget)); + + m_dump_nsbundle_measurement(frame_zero.FindVariable("bundles", lldb::eDynamicCanRunTarget)); + m_dump_nsbundle_measurement(frame_zero.FindVariable("frameworks", lldb::eDynamicCanRunTarget)); + + m_dump_nsset_measurement(frame_zero.FindVariable("nsset", lldb::eDynamicCanRunTarget)); + m_dump_nsset_measurement(frame_zero.FindVariable("nsmutableset", lldb::eDynamicCanRunTarget)); + + m_dump_std_vector_measurement(frame_zero.FindVariable("vector", lldb::eDynamicCanRunTarget)); + m_dump_std_list_measurement(frame_zero.FindVariable("list", lldb::eDynamicCanRunTarget)); + m_dump_std_map_measurement(frame_zero.FindVariable("map", lldb::eDynamicCanRunTarget)); + + auto sstr0 = frame_zero.FindVariable("sstr0", lldb::eDynamicCanRunTarget); + auto sstr1 = frame_zero.FindVariable("sstr1", lldb::eDynamicCanRunTarget); + auto sstr2 = frame_zero.FindVariable("sstr2", lldb::eDynamicCanRunTarget); + auto sstr3 = frame_zero.FindVariable("sstr3", lldb::eDynamicCanRunTarget); + auto sstr4 = frame_zero.FindVariable("sstr4", lldb::eDynamicCanRunTarget); + + m_dump_std_string_measurement.Start(); + Xcode::FetchVariable(sstr0,0,false); + m_dump_std_string_measurement.Stop(); + + m_dump_std_string_measurement.Start(); + Xcode::FetchVariable(sstr1,0,false); + m_dump_std_string_measurement.Stop(); + + m_dump_std_string_measurement.Start(); + Xcode::FetchVariable(sstr2,0,false); + m_dump_std_string_measurement.Stop(); + + m_dump_std_string_measurement.Start(); + Xcode::FetchVariable(sstr3,0,false); + m_dump_std_string_measurement.Stop(); + + m_dump_std_string_measurement.Start(); + Xcode::FetchVariable(sstr4,0,false); + m_dump_std_string_measurement.Stop(); + + } + + virtual void + TestStep (int counter, ActionWanted &next_action) + { + switch (counter) + { + case 0: + m_target.BreakpointCreateByLocation("fmts_tester.mm", 78); + next_action.Continue(); + break; + case 1: + DoTest (); + next_action.Continue(); + break; + case 2: + DoTest (); + next_action.Continue(); + break; + case 3: + DoTest (); + next_action.Continue(); + break; + case 4: + DoTest (); + next_action.Continue(); + break; + case 5: + DoTest (); + next_action.Continue(); + break; + case 6: + DoTest (); + next_action.Continue(); + break; + case 7: + DoTest (); + next_action.Continue(); + break; + case 8: + DoTest (); + next_action.Continue(); + break; + case 9: + DoTest (); + next_action.Continue(); + break; + case 10: + DoTest (); + next_action.Continue(); + break; + default: + next_action.Kill(); + break; + } + } + + virtual void + WriteResults (Results &results) + { + m_dump_std_vector_measurement.WriteAverageAndStandardDeviation(results); + m_dump_std_list_measurement.WriteAverageAndStandardDeviation(results); + m_dump_std_map_measurement.WriteAverageAndStandardDeviation(results); + m_dump_std_string_measurement.WriteAverageAndStandardDeviation(results); + + m_dump_nsstring_measurement.WriteAverageAndStandardDeviation(results); + m_dump_nsarray_measurement.WriteAverageAndStandardDeviation(results); + m_dump_nsdictionary_measurement.WriteAverageAndStandardDeviation(results); + m_dump_nsset_measurement.WriteAverageAndStandardDeviation(results); + m_dump_nsbundle_measurement.WriteAverageAndStandardDeviation(results); + m_dump_nsdate_measurement.WriteAverageAndStandardDeviation(results); + results.Write(m_out_path.c_str()); + } + +private: + // C++ formatters + TimeMeasurement> m_dump_std_vector_measurement; + TimeMeasurement> m_dump_std_list_measurement; + TimeMeasurement> m_dump_std_map_measurement; + TimeMeasurement> m_dump_std_string_measurement; + + // Cocoa formatters + TimeMeasurement> m_dump_nsstring_measurement; + TimeMeasurement> m_dump_nsarray_measurement; + TimeMeasurement> m_dump_nsdictionary_measurement; + TimeMeasurement> m_dump_nsset_measurement; + TimeMeasurement> m_dump_nsbundle_measurement; + TimeMeasurement> m_dump_nsdate_measurement; + + // useful files + std::string m_app_path; + std::string m_out_path; +}; + +// argv[1] == path to app +// argv[2] == path to result +int main(int argc, const char * argv[]) +{ + FormattersTest frmtest; + frmtest.SetVerbose(true); + TestCase::Run(frmtest,argc,argv); + return 0; +} + diff --git a/tools/lldb-perf/darwin/sketch/foobar.sketch2 b/tools/lldb-perf/darwin/sketch/foobar.sketch2 new file mode 100644 index 0000000000000000000000000000000000000000..553c698b180cd8a79b8192863113cb5bbb3b4434 GIT binary patch literal 10027 zcmd^EO>7&-6<*4+Ey+Kke`Ce8xLk^E<4DW>A-SYO%a$q2QY=%9N!gYaLs)W$)Y>a9 zvs_tGSQc=>=++*bz($H9aqJudBbWNzqKEX9w5Jwm4lRlTE!x~%pgr`G_Rao~D~eK7 zqi85au%+3Vd2inL-kbMk=4DHYT-F)pH?EEyojZ43yMwf{tQKcVnq1T;ih1>VK@&^! za<1&|m^yI$S?8Xzt|4(jUoIi(?%#IQT<2=F+MZ-OWfQn+wXS4(Dtim%bmHxMFnTDN zzPzZ5Sp|*CIUTr*+A>kA?M+ zgG)JPCRo0s>mnB?^`?xvvrjdL_`va8GyqHd-DA78Wx@ z`*Z^~ow_`(YVuoZQ5Tgfs2mUC`9Nlox2D(S+-;=OIk1+tw`SeOY2tqcoXJZLm9-wh=y>!Oav6tPg3 z2p!IZ**u-ack%0#x4!p#Qep2Uj7=$zXtO6^~kgsvDs#qmeCeljm&xh_nu{x;5Hl zOOzM088gq8M9BMdGMt&_>}=X9OT%#{HJZY66RL!g;sUb6mm_ojH+f{U34Oz$y*8tF z$L-vRRc>cY+{UOjK!K-N$B0R!>j+LBjZ=k9-+b1wa%8JC-pE2tdAz%xv|+`1CU^8U zB+Y1Iv8)(5J8hhP3M%QTUMaa0V1ccUXw5>UIRE|9=eJ zq?#ZnZF5IxM!41!4C4*4(N)H?Y`RXjAhh;?*y`>!^8iozM(8b@gB#>+4?2WQI0{dc2_J+7&&;n2LMk*smzHeVeayz95&jn-Hzt9@5* z7Ih;oPDyS0wSwA3n`TLELE}NACNkUuRh{?V!!;!QDw3ApLPnWqcQSodR2C5db)ZvF zW2z)B;;}Q5RQ=$t)N%LN4ukG!GTo?+D9Z_Wn@1Xw3>HvJN-i#Bkw$#*ey!%pedsbs z4g+WF@zP1~2#Wz#sY#UAFN<11F2d}`m@xln+#rO?(-pZe?;QQu8Upb%YRN(N@uM3y zjpK3M=J>{A!WcbLmtIuhA|@A&Hwtssg9jbP9EZ3~Q=UFBxj>cdysGQ!LW}i#p`KzCJ+m6a#EkNF3~!xyKn&3)0;-Lq%!zWu%K0|yTs zK62E3Q_{pGcz%Z$74fxdd zGW6BUmm@m7MP(P^4h|{=MZ)00WV%JKLu_0C1(R9@Rg=yvxmxY}P&G}CBSlfkB~??T z9{i)v%ViS&Eg<}*$azGT=ha0;BC|$Mez8dARgFYmxcZ!4^1pF32h709?|k>AQ*MCtVkOMdY$U{(qVgQg z@(}^Ua{xM6;ThVR!iIPn2HOP*9b{OJ$4CxUxG-p&Wmr1Ig@q{AH{Rf;O^#PsCPZ`5 zFl!oxi?DP!95O7|E?9(U?DQ*fjOA#BXIYxZp=*!~GvGf>7V*G@5Y01DJJ5wrdt_lhL6ypm=z#cmTx#?^>y$@eE$-Q1qFSxPFKhE+Ld%> z8smDUQsH2SFcCWzuwXGkFnkc>V!0?Ci`f?91dfhcVWV1|0bmJP%e4)vnH#gO-^8e5 zl}a>1+mN^zTMq#SCV&`Wc{mvue#wXFP>40+TpMB%vpET2OwmdO_D7WGjGO{lNPzTW zB^Yd|m_Tz@=-^;Jpg?LFoVd5hj9?@zr?y`Or2>HU-UOYc`? zlpG-|N&%9sxUC=3k;<{k8s_Jo6v#aZ2|9 literal 0 HcmV?d00001 diff --git a/tools/lldb-perf/darwin/sketch/sketch.cpp b/tools/lldb-perf/darwin/sketch/sketch.cpp new file mode 100644 index 000000000000..93e39165c133 --- /dev/null +++ b/tools/lldb-perf/darwin/sketch/sketch.cpp @@ -0,0 +1,380 @@ +//===-- sketch.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-perf/lib/Timer.h" +#include "lldb-perf/lib/Metric.h" +#include "lldb-perf/lib/Measurement.h" +#include "lldb-perf/lib/TestCase.h" +#include "lldb-perf/lib/Xcode.h" + +#include +#include +#include +#include + +using namespace lldb_perf; + +static struct option g_long_options[] = { + { "verbose", no_argument, NULL, 'v' }, + { "sketch", required_argument, NULL, 'c' }, + { "foobar", required_argument, NULL, 'f' }, + { "out-file", required_argument, NULL, 'o' }, + { NULL, 0, NULL, 0 } +}; + +class SketchTest : public TestCase +{ +public: + SketchTest () : + m_fetch_frames_measurement ([this] () -> void + { + Xcode::FetchFrames (GetProcess(),false,false); + }, "fetch-frames", "time to dump backtrace for every frame in every thread"), + m_file_line_bp_measurement([this] (const char* file, uint32_t line) -> void + { + Xcode::CreateFileLineBreakpoint(GetTarget(), file, line); + }, "file-line-bkpt", "time to set a breakpoint given a file and line"), + m_fetch_modules_measurement ([this] () -> void + { + Xcode::FetchModules(GetTarget()); + }, "fetch-modules", "time to get info for all modules in the process"), + m_fetch_vars_measurement([this] (int depth) -> void + { + SBProcess process (GetProcess()); + auto threads_count = process.GetNumThreads(); + for (size_t thread_num = 0; thread_num < threads_count; thread_num++) + { + SBThread thread(process.GetThreadAtIndex(thread_num)); + SBFrame frame(thread.GetFrameAtIndex(0)); + Xcode::FetchVariables(frame,depth,GetVerbose()); + } + }, "fetch-vars", "time to dump variables for the topmost frame in every thread"), + m_run_expr_measurement([this] (SBFrame frame, const char* expr) -> void + { + SBValue value(frame.EvaluateExpression(expr, lldb::eDynamicCanRunTarget)); + Xcode::FetchVariable (value, 0, GetVerbose()); + }, "run-expr", "time to evaluate an expression and display the result") + { + m_app_path.clear(); + m_out_path.clear(); + m_doc_path.clear(); + m_print_help = false; + } + + virtual + ~SketchTest () + { + } + + virtual bool + ParseOption (int short_option, const char* optarg) + { + switch (short_option) + { + case 0: + return false; + + case -1: + return false; + + case '?': + case 'h': + m_print_help = true; + break; + + case 'v': + SetVerbose(true); + break; + + case 'c': + { + SBFileSpec file(optarg); + if (file.Exists()) + SetExecutablePath(optarg); + else + fprintf(stderr, "error: file specified in --sketch (-c) option doesn't exist: '%s'\n", optarg); + } + break; + + case 'f': + { + SBFileSpec file(optarg); + if (file.Exists()) + SetDocumentPath(optarg); + else + fprintf(stderr, "error: file specified in --foobar (-f) option doesn't exist: '%s'\n", optarg); + } + break; + + case 'o': + SetResultFilePath(optarg); + break; + + default: + m_print_help = true; + fprintf (stderr, "error: unrecognized option %c\n", short_option); + break; + } + return true; + } + + virtual struct option* + GetLongOptions () + { + return g_long_options; + } + + virtual bool + Setup (int& argc, const char**& argv) + { + TestCase::Setup(argc,argv); + bool error = false; + + if (GetExecutablePath() == NULL) + { + // --sketch is mandatory + error = true; + fprintf (stderr, "error: the '--sketch=PATH' option is mandatory\n"); + } + + if (GetDocumentPath() == NULL) + { + // --foobar is mandatory + error = true; + fprintf (stderr, "error: the '--foobar=PATH' option is mandatory\n"); + } + + if (error || GetPrintHelp()) + { + puts(R"( + NAME + lldb_perf_sketch -- a tool that measures LLDB peformance while debugging sketch. + + SYNOPSIS + lldb_perf_sketch --sketch=PATH --foobar=PATH [--out-file=PATH --verbose] + + DESCRIPTION + Runs a set of static timing and memory tasks against sketch and outputs results + to a plist file. + )"); + } + + if (error) + { + exit(1); + } + lldb::SBLaunchInfo launch_info = GetLaunchInfo(); + m_target = m_debugger.CreateTarget(m_app_path.c_str()); + m_file_line_bp_measurement("SKTDocument.m",245); + m_file_line_bp_measurement("SKTDocument.m",283); + m_file_line_bp_measurement("SKTText.m",326); + return Launch (launch_info); + } + + lldb::SBLaunchInfo + GetLaunchInfo () + { + const char* file_arg = m_doc_path.c_str(); + const char* persist_arg = "-ApplePersistenceIgnoreState"; + const char* persist_skip = "YES"; + const char* empty = nullptr; + const char* args[] = {file_arg,persist_arg,persist_skip,empty}; + return SBLaunchInfo(args); + } + + void + DoTest () + { + m_fetch_frames_measurement(); + m_fetch_modules_measurement(); + m_fetch_vars_measurement(1); + } + + virtual void + TestStep (int counter, ActionWanted &next_action) + { + static int launch = 1; + switch (counter % 10) + { + case 0: + { + DoTest (); + if (counter == 0) + m_file_line_bp_measurement("SKTDocument.m",254); + next_action.Continue(); + } + break; + + case 1: + { + DoTest (); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"properties"); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[properties description]"); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"typeName"); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"data"); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[data description]"); + next_action.Continue(); + } + break; + + case 2: + { + DoTest (); + next_action.Continue(); + } + break; + + case 3: + { + DoTest (); + next_action.StepOver(m_thread); + } + break; + + case 4: + { + DoTest (); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"layoutManager"); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"contents"); + next_action.StepOver(m_thread); + } + break; + + case 5: + { + DoTest (); + next_action.StepOver(m_thread); + } + break; + + case 6: + { + DoTest (); + next_action.StepOver(m_thread); + } + break; + + case 7: + { + DoTest (); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"@\"an NSString\""); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[(id)@\"an NSString\" description]"); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"@[@1,@2,@3]"); + next_action.StepOut(m_thread); + } + break; + + case 8: + { + DoTest (); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[graphics description]"); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[selectionIndexes description]"); + m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"(BOOL)NSIntersectsRect(rect, graphicDrawingBounds)"); + } + next_action.CallNext(); + break; + case 9: + if (++launch < 10) + next_action.Relaunch(GetLaunchInfo()); + else + next_action.Kill(); + break; + + + default: + { + next_action.Kill(); + } + break; + } + } + + virtual void + WriteResults (Results &results) + { + m_fetch_frames_measurement.WriteAverageAndStandardDeviation(results); + m_file_line_bp_measurement.WriteAverageAndStandardDeviation(results); + m_fetch_modules_measurement.WriteAverageAndStandardDeviation(results); + m_fetch_vars_measurement.WriteAverageAndStandardDeviation(results); + m_run_expr_measurement.WriteAverageAndStandardDeviation(results); + results.Write(GetResultFilePath()); + } + + void + SetExecutablePath (const char* str) + { + if (str) + m_app_path.assign(str); + } + + const char* + GetExecutablePath () + { + if (m_app_path.empty()) + return NULL; + return m_app_path.c_str(); + } + + void + SetDocumentPath (const char* str) + { + if (str) + m_doc_path.assign(str); + } + + const char* + GetDocumentPath () + { + if (m_doc_path.empty()) + return NULL; + return m_doc_path.c_str(); + } + + + void + SetResultFilePath (const char* str) + { + if (str) + m_out_path.assign(str); + } + + const char* + GetResultFilePath () + { + if (m_out_path.empty()) + return "/dev/stdout"; + return m_out_path.c_str(); + } + + bool + GetPrintHelp () + { + return m_print_help; + } + +private: + Measurement> m_fetch_frames_measurement; + Measurement> m_file_line_bp_measurement; + Measurement> m_fetch_modules_measurement; + Measurement> m_fetch_vars_measurement; + Measurement> m_run_expr_measurement; + + std::string m_app_path; + std::string m_doc_path; + std::string m_out_path; + bool m_print_help; +}; + +int main(int argc, const char * argv[]) +{ + SketchTest test; + return TestCase::Run(test, argc, argv); +} diff --git a/tools/lldb-perf/lib/Gauge.cpp b/tools/lldb-perf/lib/Gauge.cpp new file mode 100644 index 000000000000..4c4593b3b292 --- /dev/null +++ b/tools/lldb-perf/lib/Gauge.cpp @@ -0,0 +1,53 @@ +//===-- Gauge.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Gauge.h" +#include "lldb/lldb-forward.h" + +template <> +lldb_perf::Results::ResultSP +lldb_perf::GetResult (const char *description, double value) +{ + if (description && description[0]) + { + std::unique_ptr value_dict_ap (new Results::Dictionary ()); + value_dict_ap->AddString("description", NULL, description); + value_dict_ap->AddDouble("value", NULL, value); + return Results::ResultSP (value_dict_ap.release()); + } + return Results::ResultSP (new Results::Double (NULL, NULL, value)); +} + +template <> +lldb_perf::Results::ResultSP +lldb_perf::GetResult (const char *description, uint64_t value) +{ + if (description && description[0]) + { + std::unique_ptr value_dict_ap (new Results::Dictionary ()); + value_dict_ap->AddString("description", NULL, description); + value_dict_ap->AddUnsigned("value", NULL, value); + return Results::ResultSP (value_dict_ap.release()); + } + return Results::ResultSP (new Results::Unsigned (NULL, NULL, value)); +} + +template <> +lldb_perf::Results::ResultSP +lldb_perf::GetResult (const char *description, std::string value) +{ + if (description && description[0]) + { + std::unique_ptr value_dict_ap (new Results::Dictionary ()); + value_dict_ap->AddString("description", NULL, description); + value_dict_ap->AddString("value", NULL, value.c_str()); + return Results::ResultSP (value_dict_ap.release()); + } + return Results::ResultSP (new Results::String (NULL, NULL, value.c_str())); +} diff --git a/tools/lldb-perf/lib/Gauge.h b/tools/lldb-perf/lib/Gauge.h new file mode 100644 index 000000000000..fc5c444a38b8 --- /dev/null +++ b/tools/lldb-perf/lib/Gauge.h @@ -0,0 +1,64 @@ +//===-- Gauge.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef PerfTestDriver_Gauge_h +#define PerfTestDriver_Gauge_h + +#include +#include + +#include "Results.h" + +namespace lldb_perf { + +template +class Gauge +{ +public: + typedef T ValueType; + + Gauge () + {} + + virtual + ~Gauge () + {} + + virtual void + Start () = 0; + + virtual ValueType + Stop () = 0; + + virtual ValueType + GetStartValue () const = 0; + + virtual ValueType + GetStopValue () const = 0; + + virtual ValueType + GetDeltaValue () const = 0; + +}; + +template +Results::ResultSP GetResult (const char *description, T value); + +template <> +Results::ResultSP GetResult (const char *description, double value); + +template <> +Results::ResultSP GetResult (const char *description, uint64_t value); + +template <> +Results::ResultSP GetResult (const char *description, std::string value); + +} + +#endif diff --git a/tools/lldb-perf/lib/Measurement.h b/tools/lldb-perf/lib/Measurement.h new file mode 100644 index 000000000000..877e618d5469 --- /dev/null +++ b/tools/lldb-perf/lib/Measurement.h @@ -0,0 +1,217 @@ +//===-- Measurement.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __PerfTestDriver__Measurement__ +#define __PerfTestDriver__Measurement__ + +#include "Gauge.h" +#include "Timer.h" +#include "Metric.h" +#include "MemoryGauge.h" + +namespace lldb_perf +{ +template +class Measurement +{ +public: + Measurement () : + m_gauge (), + m_callable (), + m_metric () + { + } + + Measurement (Callable callable, const char* name, const char* desc) : + m_gauge (), + m_callable (callable), + m_metric (Metric(name, desc)) + { + } + + Measurement (const char* name, const char* desc) : + m_gauge (), + m_callable (), + m_metric (Metric(name, desc)) + { + } + + template + Measurement (const Measurement& rhs) : + m_gauge(rhs.GetGauge()), + m_callable(rhs.GetCallable()), + m_metric(rhs.GetMetric()) + { + } + + template + void + operator () (Args... args) + { + m_gauge.Start(); + m_callable(args...); + m_metric.Append (m_gauge.Stop()); + } + + virtual const Callable& + GetCallable () const + { + return m_callable; + } + + virtual const GaugeType& + GetGauge () const + { + return m_gauge; + } + + virtual const Metric& + GetMetric () const + { + return m_metric; + } + + void + Start () + { + m_gauge.Start(); + } + + typename GaugeType::ValueType + Stop () + { + auto value = m_gauge.Stop(); + m_metric.Append(value); + return value; + } + + void + WriteStartValue (Results &results) + { + auto metric = GetMetric (); + results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult (NULL, metric.GetStartValue())); + } + + void + WriteStopValue (Results &results) + { + auto metric = GetMetric (); + results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult (NULL, metric.GetStopValue())); + } + + void + WriteAverageValue (Results &results) + { + auto metric = GetMetric (); + results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult (NULL, metric.GetAverage())); + } + + void + WriteAverageAndStandardDeviation (Results &results) + { + auto metric = GetMetric (); + auto dictionary = (Results::Dictionary*)results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult (NULL, metric.GetAverage())).get(); + if (dictionary) + { + dictionary->Add("stddev", NULL, lldb_perf::GetResult (NULL, metric.GetStandardDeviation())); + } + } + + void + WriteStandardDeviation (Results &results) + { + auto metric = GetMetric (); + results.GetDictionary().Add(metric.GetName(), metric.GetDescription(), lldb_perf::GetResult (NULL, metric.GetStandardDeviation())); + } + +protected: + GaugeType m_gauge; + Callable m_callable; + Metric m_metric; +}; + +template +class TimeMeasurement : public Measurement +{ +public: + TimeMeasurement () : + Measurement () + { + } + + TimeMeasurement (Callable callable, + const char* name = NULL, + const char* descr = NULL) : + Measurement (callable, name, descr) + { + } + + template + TimeMeasurement (const TimeMeasurement& rhs) : + Measurement(rhs) + { + } + + template + TimeMeasurement (const Measurement& rhs) : + Measurement(rhs) + { + } + + template + void + operator () (Args... args) + { + Measurement::operator()(args...); + } +}; + +template +class MemoryMeasurement : public Measurement +{ +public: + MemoryMeasurement () : Measurement () + { + } + + MemoryMeasurement (Callable callable, + const char* name, + const char* descr) : + Measurement (callable, name, descr) + { + } + + MemoryMeasurement (const char* name, const char* descr) : + Measurement (name, descr) + { + } + + template + MemoryMeasurement (const MemoryMeasurement& rhs) : + Measurement(rhs) + { + } + + template + MemoryMeasurement (const Measurement& rhs) : + Measurement(rhs) + { + } + + template + void + operator () (Args... args) + { + Measurement::operator()(args...); + } +}; + +} + +#endif /* defined(__PerfTestDriver__Measurement__) */ diff --git a/tools/lldb-perf/lib/MemoryGauge.cpp b/tools/lldb-perf/lib/MemoryGauge.cpp new file mode 100644 index 000000000000..2a46453f540b --- /dev/null +++ b/tools/lldb-perf/lib/MemoryGauge.cpp @@ -0,0 +1,165 @@ +//===-- MemoryGauge.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MemoryGauge.h" +#include "lldb/lldb-forward.h" +#include +#include +#include +#include +#include + +using namespace lldb_perf; + +MemoryStats::MemoryStats (mach_vm_size_t virtual_size, + mach_vm_size_t resident_size, + mach_vm_size_t max_resident_size) : + m_virtual_size (virtual_size), + m_resident_size (resident_size), + m_max_resident_size (max_resident_size) +{ +} + +MemoryStats::MemoryStats (const MemoryStats& rhs) : + m_virtual_size (rhs.m_virtual_size), + m_resident_size (rhs.m_resident_size), + m_max_resident_size (rhs.m_max_resident_size) +{ +} + + +MemoryStats& +MemoryStats::operator = (const MemoryStats& rhs) +{ + if (this != &rhs) + { + m_virtual_size = rhs.m_virtual_size; + m_resident_size = rhs.m_resident_size; + m_max_resident_size = rhs.m_max_resident_size; + } + return *this; +} + +MemoryStats& +MemoryStats::operator += (const MemoryStats& rhs) +{ + m_virtual_size += rhs.m_virtual_size; + m_resident_size += rhs.m_resident_size; + m_max_resident_size += rhs.m_max_resident_size; + return *this; +} + +MemoryStats +MemoryStats::operator - (const MemoryStats& rhs) +{ + return MemoryStats(m_virtual_size - rhs.m_virtual_size, + m_resident_size - rhs.m_resident_size, + m_max_resident_size - rhs.m_max_resident_size); +} + +MemoryStats +MemoryStats::operator + (const MemoryStats& rhs) +{ + return MemoryStats(m_virtual_size + rhs.m_virtual_size, + m_resident_size + rhs.m_resident_size, + m_max_resident_size + rhs.m_max_resident_size); +} + +MemoryStats +MemoryStats::operator / (size_t n) +{ + MemoryStats result(*this); + result.m_virtual_size /= n; + result.m_resident_size /= n; + result.m_max_resident_size /= n; + return result; +} + +MemoryStats +MemoryStats::operator * (const MemoryStats& rhs) +{ + return MemoryStats(m_virtual_size * rhs.m_virtual_size, + m_resident_size * rhs.m_resident_size, + m_max_resident_size * rhs.m_max_resident_size); +} + +Results::ResultSP +MemoryStats::GetResult (const char *name, const char *description) const +{ + std::unique_ptr dict_ap (new Results::Dictionary (name, NULL)); + dict_ap->AddUnsigned("resident", NULL, GetResidentSize()); + dict_ap->AddUnsigned("max_resident", NULL, GetMaxResidentSize()); + return Results::ResultSP(dict_ap.release()); +} + +MemoryGauge::ValueType +MemoryGauge::Now () +{ + task_t task = mach_task_self(); + mach_task_basic_info_data_t taskBasicInfo; + mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT; + auto task_info_ret = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t) & taskBasicInfo, &count); + if (task_info_ret == KERN_SUCCESS) { + return MemoryStats(taskBasicInfo.virtual_size, taskBasicInfo.resident_size, taskBasicInfo.resident_size_max); + } + return 0; +} + +MemoryGauge::MemoryGauge () : + m_state(MemoryGauge::State::eNeverUsed), + m_start(), + m_delta() +{ +} + +void +MemoryGauge::Start () +{ + m_state = MemoryGauge::State::eCounting; + m_start = Now(); +} + +MemoryGauge::ValueType +MemoryGauge::Stop () +{ + m_stop = Now(); + assert(m_state == MemoryGauge::State::eCounting && "cannot stop a non-started gauge"); + m_state = MemoryGauge::State::eStopped; + m_delta = m_stop - m_start; + return m_delta; +} + + +MemoryGauge::ValueType +MemoryGauge::GetDeltaValue () const +{ + assert(m_state == MemoryGauge::State::eStopped && "gauge must be used before you can evaluate it"); + return m_delta; +} + +template <> +Results::ResultSP +lldb_perf::GetResult (const char *description, MemoryStats value) +{ + return value.GetResult (NULL, description); +} + +MemoryStats +sqrt (const MemoryStats& arg) +{ + long double virt_size = arg.GetVirtualSize(); + long double resident_size = arg.GetResidentSize(); + long double max_resident_size = arg.GetMaxResidentSize(); + + virt_size = sqrtl(virt_size); + resident_size = sqrtl(resident_size); + max_resident_size = sqrtl(max_resident_size); + + return MemoryStats(virt_size,resident_size,max_resident_size); +} diff --git a/tools/lldb-perf/lib/MemoryGauge.h b/tools/lldb-perf/lib/MemoryGauge.h new file mode 100644 index 000000000000..a1221b6b66cc --- /dev/null +++ b/tools/lldb-perf/lib/MemoryGauge.h @@ -0,0 +1,147 @@ +//===-- MemoryGauge.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __PerfTestDriver__MemoryGauge__ +#define __PerfTestDriver__MemoryGauge__ + +#include "Gauge.h" +#include "Results.h" + +#include + +namespace lldb_perf { + +class MemoryStats +{ +public: + MemoryStats (mach_vm_size_t virtual_size = 0, + mach_vm_size_t resident_size = 0, + mach_vm_size_t max_resident_size = 0); + MemoryStats (const MemoryStats& rhs); + + MemoryStats& + operator = (const MemoryStats& rhs); + + MemoryStats& + operator += (const MemoryStats& rhs); + + MemoryStats + operator - (const MemoryStats& rhs); + + MemoryStats + operator + (const MemoryStats& rhs); + + MemoryStats + operator / (size_t rhs); + + MemoryStats + operator * (const MemoryStats& rhs); + + mach_vm_size_t + GetVirtualSize () const + { + return m_virtual_size; + } + + mach_vm_size_t + GetResidentSize () const + { + return m_resident_size; + } + + mach_vm_size_t + GetMaxResidentSize () const + { + return m_max_resident_size; + } + + void + SetVirtualSize (mach_vm_size_t vs) + { + m_virtual_size = vs; + } + + void + SetResidentSize (mach_vm_size_t rs) + { + m_resident_size = rs; + } + + void + SetMaxResidentSize (mach_vm_size_t mrs) + { + m_max_resident_size = mrs; + } + + Results::ResultSP + GetResult (const char *name, const char *description) const; +private: + mach_vm_size_t m_virtual_size; + mach_vm_size_t m_resident_size; + mach_vm_size_t m_max_resident_size; +}; + +class MemoryGauge : public Gauge +{ +public: + MemoryGauge (); + + virtual + ~MemoryGauge () + { + } + + void + Start (); + + ValueType + Stop (); + + virtual ValueType + GetStartValue() const + { + return m_start; + } + + virtual ValueType + GetStopValue() const + { + return m_stop; + } + + virtual ValueType + GetDeltaValue() const; + +private: + enum class State + { + eNeverUsed, + eCounting, + eStopped + }; + + ValueType + Now (); + + State m_state; + ValueType m_start; + ValueType m_stop; + ValueType m_delta; +}; + +template <> +Results::ResultSP +GetResult (const char *description, MemoryStats value); + +} // namespace lldb_perf + +lldb_perf::MemoryStats +sqrt (const lldb_perf::MemoryStats& arg); + +#endif // #ifndef __PerfTestDriver__MemoryGauge__ diff --git a/tools/lldb-perf/lib/Metric.cpp b/tools/lldb-perf/lib/Metric.cpp new file mode 100644 index 000000000000..1951cdb0250a --- /dev/null +++ b/tools/lldb-perf/lib/Metric.cpp @@ -0,0 +1,85 @@ +//===-- Metric.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Metric.h" +#include "MemoryGauge.h" +#include + +using namespace lldb_perf; + +template +Metric::Metric () : Metric ("") +{ +} + +template +Metric::Metric (const char* n, const char* d) : + m_name(n ? n : ""), + m_description(d ? d : ""), + m_dataset () +{ +} + +template +void +Metric::Append (T v) +{ + m_dataset.push_back(v); +} + +template +size_t +Metric::GetCount () const +{ + return m_dataset.size(); +} + +template +T +Metric::GetSum () const +{ + T sum = 0; + for (auto v : m_dataset) + sum += v; + return sum; +} + +template +T +Metric::GetAverage () const +{ + return GetSum()/GetCount(); +} + + +// Knuth's algorithm for stddev - massive cancellation resistant +template +T +Metric::GetStandardDeviation (StandardDeviationMode mode) const +{ + size_t n = 0; + T mean = 0; + T M2 = 0; + for (auto x : m_dataset) + { + n = n + 1; + T delta = x - mean; + mean = mean + delta/n; + M2 = M2+delta*(x-mean); + } + T variance; + if (mode == StandardDeviationMode::ePopulation || n == 1) + variance = M2 / n; + else + variance = M2 / (n - 1); + return sqrt(variance); +} + +template class lldb_perf::Metric; +template class lldb_perf::Metric; diff --git a/tools/lldb-perf/lib/Metric.h b/tools/lldb-perf/lib/Metric.h new file mode 100644 index 000000000000..45342d25b41a --- /dev/null +++ b/tools/lldb-perf/lib/Metric.h @@ -0,0 +1,72 @@ +//===-- Metric.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __PerfTestDriver__Metric__ +#define __PerfTestDriver__Metric__ + +#include +#include +#include + +namespace lldb_perf { + +class MemoryStats; + +template +class Metric +{ +public: + enum class StandardDeviationMode + { + eSample, + ePopulation + }; + + Metric (); + Metric (const char*, const char* = NULL); + + void + Append (ValueType v); + + ValueType + GetAverage () const; + + size_t + GetCount () const; + + ValueType + GetSum () const; + + ValueType + GetStandardDeviation (StandardDeviationMode mode = StandardDeviationMode::ePopulation) const; + + const char* + GetName () const + { + if (m_name.empty()) + return NULL; + return m_name.c_str(); + } + + const char* + GetDescription () const + { + if (m_description.empty()) + return NULL; + return m_description.c_str(); + } + +private: + std::string m_name; + std::string m_description; + std::vector m_dataset; +}; +} + +#endif /* defined(__PerfTestDriver__Metric__) */ diff --git a/tools/lldb-perf/lib/Results.cpp b/tools/lldb-perf/lib/Results.cpp new file mode 100644 index 000000000000..6abf67e53b67 --- /dev/null +++ b/tools/lldb-perf/lib/Results.cpp @@ -0,0 +1,275 @@ +//===-- Results.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Results.h" +#include + +#ifdef __APPLE__ +#include "CFCMutableArray.h" +#include "CFCMutableDictionary.h" +#include "CFCReleaser.h" +#include "CFCString.h" +#endif + +using namespace lldb_perf; + +static void +AddResultToArray (CFCMutableArray &array, Results::Result *result); + +static void +AddResultToDictionary (CFCMutableDictionary &parent_dict, const char *key, Results::Result *result); + +static void +AddResultToArray (CFCMutableArray &parent_array, Results::Result *result) +{ + switch (result->GetType()) + { + case Results::Result::Type::Invalid: + break; + + case Results::Result::Type::Array: + { + Results::Array *value = result->GetAsArray(); + CFCMutableArray array; + value->ForEach([&array](const Results::ResultSP &value_sp) -> bool + { + AddResultToArray (array, value_sp.get()); + return true; + }); + parent_array.AppendValue(array.get(), true); + } + break; + + case Results::Result::Type::Dictionary: + { + Results::Dictionary *value = result->GetAsDictionary(); + CFCMutableDictionary dict; + value->ForEach([&dict](const std::string &key, const Results::ResultSP &value_sp) -> bool + { + AddResultToDictionary (dict, key.c_str(), value_sp.get()); + return true; + }); + if (result->GetDescription()) + { + dict.AddValueCString(CFSTR("description"), result->GetDescription()); + } + parent_array.AppendValue(dict.get(), true); + } + break; + + case Results::Result::Type::Double: + { + double d = result->GetAsDouble()->GetValue(); + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &d)); + if (cf_number.get()) + parent_array.AppendValue(cf_number.get(), true); + } + break; + case Results::Result::Type::String: + { + CFCString cfstr (result->GetAsString()->GetValue()); + if (cfstr.get()) + parent_array.AppendValue(cfstr.get(), true); + } + break; + + case Results::Result::Type::Unsigned: + { + uint64_t uval64 = result->GetAsUnsigned()->GetValue(); + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &uval64)); + if (cf_number.get()) + parent_array.AppendValue(cf_number.get(), true); + } + break; + + default: + assert (!"unhandled result"); + break; + } +} + + +static void +AddResultToDictionary (CFCMutableDictionary &parent_dict, const char *key, Results::Result *result) +{ + assert (key && key[0]); + CFCString cf_key(key); + switch (result->GetType()) + { + case Results::Result::Type::Invalid: + break; + + case Results::Result::Type::Array: + { + Results::Array *value = result->GetAsArray(); + CFCMutableArray array; + value->ForEach([&array](const Results::ResultSP &value_sp) -> bool + { + AddResultToArray (array, value_sp.get()); + return true; + }); + parent_dict.AddValue(cf_key.get(), array.get(), true); + } + break; + case Results::Result::Type::Dictionary: + { + Results::Dictionary *value = result->GetAsDictionary(); + CFCMutableDictionary dict; + value->ForEach([&dict](const std::string &key, const Results::ResultSP &value_sp) -> bool + { + AddResultToDictionary (dict, key.c_str(), value_sp.get()); + return true; + }); + if (result->GetDescription()) + { + dict.AddValueCString(CFSTR("description"), result->GetDescription()); + } + parent_dict.AddValue(cf_key.get(), dict.get(), true); + } + break; + case Results::Result::Type::Double: + { + parent_dict.SetValueDouble(cf_key.get(), result->GetAsDouble()->GetValue(), true); + } + break; + case Results::Result::Type::String: + { + parent_dict.SetValueCString(cf_key.get(), result->GetAsString()->GetValue(), true); + } + break; + + case Results::Result::Type::Unsigned: + { + parent_dict.SetValueUInt64 (cf_key.get(), result->GetAsUnsigned()->GetValue(), true); + } + break; + default: + assert (!"unhandled result"); + break; + } +} +void +Results::Write (const char *out_path) +{ +#ifdef __APPLE__ + CFCMutableDictionary dict; + + m_results.ForEach([&dict](const std::string &key, const ResultSP &value_sp) -> bool + { + AddResultToDictionary (dict, key.c_str(), value_sp.get()); + return true; + }); + CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, dict.get(), kCFPropertyListXMLFormat_v1_0, 0, NULL); + + if (out_path == NULL) + out_path = "/dev/stdout"; + + CFURLRef file = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8*)out_path, strlen(out_path), FALSE); + + CFURLWriteDataAndPropertiesToResource(file, xmlData, NULL, NULL); +#endif +} + +Results::ResultSP +Results::Dictionary::AddUnsigned (const char *name, const char *description, uint64_t value) +{ + assert (name && name[0]); + if (description && description[0]) + { + std::unique_ptr value_dict_ap (new Results::Dictionary ()); + value_dict_ap->AddString("description", NULL, description); + value_dict_ap->AddUnsigned("value", NULL, value); + m_dictionary[std::string(name)] = ResultSP (value_dict_ap.release()); + } + else + m_dictionary[std::string(name)] = ResultSP (new Unsigned (name, description, value)); + return m_dictionary[std::string(name)]; +} + +Results::ResultSP +Results::Dictionary::AddDouble (const char *name, const char *description, double value) +{ + assert (name && name[0]); + + if (description && description[0]) + { + std::unique_ptr value_dict_ap (new Results::Dictionary ()); + value_dict_ap->AddString("description", NULL, description); + value_dict_ap->AddDouble("value", NULL, value); + m_dictionary[std::string(name)] = ResultSP (value_dict_ap.release()); + } + else + m_dictionary[std::string(name)] = ResultSP (new Double (name, description, value)); + return m_dictionary[std::string(name)]; +} +Results::ResultSP +Results::Dictionary::AddString (const char *name, const char *description, const char *value) +{ + assert (name && name[0]); + if (description && description[0]) + { + std::unique_ptr value_dict_ap (new Results::Dictionary ()); + value_dict_ap->AddString("description", NULL, description); + value_dict_ap->AddString("value", NULL, value); + m_dictionary[std::string(name)] = ResultSP (value_dict_ap.release()); + } + else + m_dictionary[std::string(name)] = ResultSP (new String (name, description, value)); + return m_dictionary[std::string(name)]; +} + +Results::ResultSP +Results::Dictionary::Add (const char *name, const char *description, const ResultSP &result_sp) +{ + assert (name && name[0]); + if (description && description[0]) + { + std::unique_ptr value_dict_ap (new Results::Dictionary ()); + value_dict_ap->AddString("description", NULL, description); + value_dict_ap->Add("value", NULL, result_sp); + m_dictionary[std::string(name)] = ResultSP (value_dict_ap.release()); + } + else + m_dictionary[std::string(name)] = result_sp; + return m_dictionary[std::string(name)]; +} + +void +Results::Dictionary::ForEach (const std::function &callback) +{ + collection::const_iterator pos, end = m_dictionary.end(); + for (pos = m_dictionary.begin(); pos != end; ++pos) + { + if (callback (pos->first.c_str(), pos->second) == false) + return; + } +} + + + +Results::ResultSP +Results::Array::Append (const ResultSP &result_sp) +{ + m_array.push_back (result_sp); + return result_sp; +} + +void +Results::Array::ForEach (const std::function &callback) +{ + collection::const_iterator pos, end = m_array.end(); + for (pos = m_array.begin(); pos != end; ++pos) + { + if (callback (*pos) == false) + return; + } +} + + + diff --git a/tools/lldb-perf/lib/Results.h b/tools/lldb-perf/lib/Results.h new file mode 100644 index 000000000000..388077e7f581 --- /dev/null +++ b/tools/lldb-perf/lib/Results.h @@ -0,0 +1,312 @@ +//===-- Results.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __PerfTestDriver_Results_h__ +#define __PerfTestDriver_Results_h__ + +#include "lldb/lldb-forward.h" +#include +#include +#include + +namespace lldb_perf { + +class Results +{ +public: + class Array; + class Dictionary; + class Double; + class String; + class Unsigned; + + class Result + { + public: + enum class Type + { + Invalid, + Array, + Dictionary, + Double, + String, + Unsigned + }; + + Result (Type type, const char *name, const char *description) : + m_name (), + m_description(), + m_type (type) + { + if (name && name[0]) + m_name = name; + if (description && description[0]) + m_description = description; + } + + virtual + ~Result() + { + } + + virtual void + Write (Results &results) = 0; + + Array * + GetAsArray () + { + if (m_type == Type::Array) + return (Array *)this; + return NULL; + } + Dictionary * + GetAsDictionary () + { + if (m_type == Type::Dictionary) + return (Dictionary *)this; + return NULL; + } + Double * + GetAsDouble () + { + if (m_type == Type::Double) + return (Double *)this; + return NULL; + } + + String * + GetAsString () + { + if (m_type == Type::String) + return (String *)this; + return NULL; + } + Unsigned * + GetAsUnsigned () + { + if (m_type == Type::Unsigned) + return (Unsigned *)this; + return NULL; + } + + const char * + GetName() const + { + if (m_name.empty()) + return NULL; + return m_name.c_str(); + } + + const char * + GetDescription() const + { + if (m_description.empty()) + return NULL; + return m_description.c_str(); + } + + Type + GetType() const + { + return m_type; + } + + protected: + std::string m_name; + std::string m_description; + Type m_type; + }; + + typedef std::shared_ptr ResultSP; + + class Array : public Result + { + public: + Array (const char *name, const char *description) : + Result (Type::Array, name, description) + { + } + + virtual + ~Array() + { + } + + ResultSP + Append (const ResultSP &result_sp); + + void + ForEach (const std::function &callback); + + virtual void + Write (Results &results) + { + } + protected: + typedef std::vector collection; + collection m_array; + }; + + class Dictionary : public Result + { + public: + Dictionary () : + Result (Type::Dictionary, NULL, NULL) + { + } + + Dictionary (const char *name, const char *description) : + Result (Type::Dictionary, name, description) + { + } + + virtual + ~Dictionary() + { + } + + virtual void + Write (Results &results) + { + } + + void + ForEach (const std::function &callback); + + ResultSP + Add (const char *name, const char *description, const ResultSP &result_sp); + + ResultSP + AddDouble (const char *name, const char *descriptiorn, double value); + + ResultSP + AddUnsigned (const char *name, const char *description, uint64_t value); + + ResultSP + AddString (const char *name, const char *description, const char *value); + + protected: + + typedef std::map collection; + collection m_dictionary; + }; + + class String : public Result + { + public: + String (const char *name, const char *description, const char *value) : + Result (Type::String, name, description), + m_string () + { + if (value && value[0]) + m_string = value; + } + + virtual + ~String() + { + } + + virtual void + Write (Results &results) + { + } + + const char * + GetValue () const + { + return m_string.empty() ? NULL : m_string.c_str(); + } + + protected: + std::string m_string; + }; + + class Double : public Result + { + public: + Double (const char *name, const char *description, double value) : + Result (Type::Double, name, description), + m_double (value) + { + } + + virtual + ~Double() + { + } + + virtual void + Write (Results &results) + { + } + + double + GetValue () const + { + return m_double; + } + + protected: + double m_double; + }; + + class Unsigned : public Result + { + public: + Unsigned (const char *name, const char *description, uint64_t value) : + Result (Type::Unsigned, name, description), + m_unsigned (value) + { + } + + virtual + ~Unsigned() + { + } + + virtual void + Write (Results &results) + { + } + + uint64_t + GetValue () const + { + return m_unsigned; + } + + protected: + uint64_t m_unsigned; + }; + + Results () : + m_results () + { + } + + ~Results() + { + } + + Dictionary & + GetDictionary () + { + return m_results; + } + + void + Write (const char *path); + +protected: + Dictionary m_results; +}; + +} // namespace lldb_perf +#endif // #ifndef __PerfTestDriver_Results_h__ diff --git a/tools/lldb-perf/lib/TestCase.cpp b/tools/lldb-perf/lib/TestCase.cpp new file mode 100644 index 000000000000..c23a5e519773 --- /dev/null +++ b/tools/lldb-perf/lib/TestCase.cpp @@ -0,0 +1,358 @@ +//===-- TestCase.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestCase.h" +#include "Results.h" +#include "Xcode.h" + +using namespace lldb_perf; + +TestCase::TestCase () : + m_debugger(), + m_target(), + m_process(), + m_thread(), + m_listener(), + m_verbose(false), + m_step(0) +{ + SBDebugger::Initialize(); + SBHostOS::ThreadCreated (""); + m_debugger = SBDebugger::Create(false); + m_listener = m_debugger.GetListener(); + m_listener.StartListeningForEventClass (m_debugger, SBProcess::GetBroadcasterClass(), SBProcess::eBroadcastBitStateChanged | SBProcess::eBroadcastBitInterrupt); +} + +static std::string +GetShortOptionString (struct option *long_options) +{ + std::string option_string; + 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 (2, ':'); + break; + } + } + } + return option_string; +} + +bool +TestCase::Setup (int& argc, const char**& argv) +{ + bool done = false; + + struct option* long_options = GetLongOptions(); + + if (long_options) + { + std::string short_option_string (GetShortOptionString(long_options)); + + #if __GLIBC__ + optind = 0; + #else + optreset = 1; + optind = 1; + #endif + while (!done) + { + int long_options_index = -1; + const int short_option = ::getopt_long_only (argc, + const_cast(argv), + short_option_string.c_str(), + long_options, + &long_options_index); + + switch (short_option) + { + case 0: + // Already handled + break; + + case -1: + done = true; + break; + + default: + done = !ParseOption(short_option, optarg); + break; + } + } + argc -= optind; + argv += optind; + } + + return false; +} + +bool +TestCase::Launch (lldb::SBLaunchInfo &launch_info) +{ + lldb::SBError error; + m_process = m_target.Launch (launch_info, error); + if (!error.Success()) + fprintf (stderr, "error: %s\n", error.GetCString()); + if (m_process.IsValid()) + return true; + return false; +} + +bool +TestCase::Launch (std::initializer_list args) +{ + std::vector args_vect(args); + args_vect.push_back(NULL); + lldb::SBLaunchInfo launch_info((const char**)&args_vect[0]); + return Launch(launch_info); +} + +void +TestCase::SetVerbose (bool b) +{ + m_verbose = b; +} + +bool +TestCase::GetVerbose () +{ + return m_verbose; +} + +void +TestCase::Loop () +{ + while (true) + { + bool call_test_step = false; + if (m_process.IsValid()) + { + SBEvent evt; + m_listener.WaitForEvent (UINT32_MAX, evt); + StateType state = SBProcess::GetStateFromEvent (evt); + if (m_verbose) + printf("event = %s\n",SBDebugger::StateAsCString(state)); + if (SBProcess::GetRestartedFromEvent(evt)) + { + if (m_verbose) + { + const uint32_t num_threads = m_process.GetNumThreads(); + for (auto thread_index = 0; thread_index < num_threads; thread_index++) + { + SBThread thread(m_process.GetThreadAtIndex(thread_index)); + SBFrame frame(thread.GetFrameAtIndex(0)); + SBStream strm; + strm.RedirectToFileHandle(stdout, false); + frame.GetDescription(strm); + } + puts("restarted"); + } + call_test_step = false; + } + else + { + switch (state) + { + case eStateInvalid: + case eStateDetached: + case eStateCrashed: + case eStateUnloaded: + break; + case eStateExited: + return; + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + call_test_step = false; + break; + + case eStateStopped: + case eStateSuspended: + { + call_test_step = true; + bool fatal = false; + bool selected_thread = false; + const uint32_t num_threads = m_process.GetNumThreads(); + for (auto thread_index = 0; thread_index < num_threads; thread_index++) + { + SBThread thread(m_process.GetThreadAtIndex(thread_index)); + SBFrame frame(thread.GetFrameAtIndex(0)); + SBStream strm; + strm.RedirectToFileHandle(stdout, false); + frame.GetDescription(strm); + bool select_thread = false; + StopReason stop_reason = thread.GetStopReason(); + if (m_verbose) printf("tid = 0x%llx pc = 0x%llx ",thread.GetThreadID(),frame.GetPC()); + switch (stop_reason) + { + case eStopReasonNone: + if (m_verbose) + printf("none\n"); + break; + + case eStopReasonTrace: + select_thread = true; + if (m_verbose) + printf("trace\n"); + break; + + case eStopReasonPlanComplete: + select_thread = true; + if (m_verbose) + printf("plan complete\n"); + break; + case eStopReasonThreadExiting: + if (m_verbose) + printf("thread exiting\n"); + break; + case eStopReasonExec: + if (m_verbose) + printf("exec\n"); + break; + case eStopReasonInvalid: + if (m_verbose) + printf("invalid\n"); + break; + case eStopReasonException: + select_thread = true; + if (m_verbose) + printf("exception\n"); + fatal = true; + break; + case eStopReasonBreakpoint: + select_thread = true; + if (m_verbose) + printf("breakpoint id = %lld.%lld\n",thread.GetStopReasonDataAtIndex(0),thread.GetStopReasonDataAtIndex(1)); + break; + case eStopReasonWatchpoint: + select_thread = true; + if (m_verbose) + printf("watchpoint id = %lld\n",thread.GetStopReasonDataAtIndex(0)); + break; + case eStopReasonSignal: + select_thread = true; + if (m_verbose) + printf("signal %d\n",(int)thread.GetStopReasonDataAtIndex(0)); + break; + } + if (select_thread && !selected_thread) + { + m_thread = thread; + selected_thread = m_process.SetSelectedThread(thread); + } + } + if (fatal) + { + if (m_verbose) Xcode::RunCommand(m_debugger,"bt all",true); + exit(1); + } + } + break; + } + } + } + else + { + call_test_step = true; + } + + if (call_test_step) + { + do_the_call: + if (m_verbose) + printf("RUNNING STEP %d\n",m_step); + ActionWanted action; + TestStep(m_step, action); + m_step++; + SBError err; + switch (action.type) + { + case ActionWanted::Type::eNone: + // Just exit and wait for the next event + break; + case ActionWanted::Type::eContinue: + err = m_process.Continue(); + break; + case ActionWanted::Type::eStepOut: + if (action.thread.IsValid() == false) + { + if (m_verbose) + { + Xcode::RunCommand(m_debugger,"bt all",true); + printf("error: invalid thread for step out on step %d\n", m_step); + } + exit(501); + } + m_process.SetSelectedThread(action.thread); + action.thread.StepOut(); + break; + case ActionWanted::Type::eStepOver: + if (action.thread.IsValid() == false) + { + if (m_verbose) + { + Xcode::RunCommand(m_debugger,"bt all",true); + printf("error: invalid thread for step over %d\n",m_step); + } + exit(500); + } + m_process.SetSelectedThread(action.thread); + action.thread.StepOver(); + break; + case ActionWanted::Type::eRelaunch: + if (m_process.IsValid()) + { + m_process.Kill(); + m_process.Clear(); + } + Launch(action.launch_info); + break; + case ActionWanted::Type::eKill: + if (m_verbose) + printf("kill\n"); + m_process.Kill(); + return; + case ActionWanted::Type::eCallNext: + goto do_the_call; + break; + } + } + + } + + if (GetVerbose()) printf("I am gonna die at step %d\n",m_step); +} + +int +TestCase::Run (TestCase& test, int argc, const char** argv) +{ + if (test.Setup(argc, argv)) + { + test.Loop(); + Results results; + test.WriteResults(results); + return RUN_SUCCESS; + } + else + return RUN_SETUP_ERROR; +} + diff --git a/tools/lldb-perf/lib/TestCase.h b/tools/lldb-perf/lib/TestCase.h new file mode 100644 index 000000000000..811d0432b58a --- /dev/null +++ b/tools/lldb-perf/lib/TestCase.h @@ -0,0 +1,205 @@ +//===-- TestCase.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __PerfTestDriver__TestCase__ +#define __PerfTestDriver__TestCase__ + +#include "lldb/API/LLDB.h" +#include "Measurement.h" +#include + +namespace lldb_perf { + +class Results; + +class TestCase +{ +public: + TestCase(); + + struct ActionWanted + { + enum class Type + { + eStepOver, + eContinue, + eStepOut, + eRelaunch, + eCallNext, + eNone, + eKill + } type; + lldb::SBThread thread; + lldb::SBLaunchInfo launch_info; + + ActionWanted () : + type (Type::eContinue), + thread (), + launch_info (NULL) + { + } + + void + None () + { + type = Type::eNone; + thread = lldb::SBThread(); + } + + void + Continue() + { + type = Type::eContinue; + thread = lldb::SBThread(); + } + + void + StepOver (lldb::SBThread t) + { + type = Type::eStepOver; + thread = t; + } + + void + StepOut (lldb::SBThread t) + { + type = Type::eStepOut; + thread = t; + } + + void + Relaunch (lldb::SBLaunchInfo l) + { + type = Type::eRelaunch; + thread = lldb::SBThread(); + launch_info = l; + } + + void + Kill () + { + type = Type::eKill; + thread = lldb::SBThread(); + } + + void + CallNext () + { + type = Type::eCallNext; + thread = lldb::SBThread(); + } + }; + + virtual + ~TestCase () + { + } + + virtual bool + Setup (int& argc, const char**& argv); + + virtual void + TestStep (int counter, ActionWanted &next_action) = 0; + + bool + Launch (lldb::SBLaunchInfo &launch_info); + + bool + Launch (std::initializer_list args = {}); + + void + Loop(); + + void + SetVerbose (bool); + + bool + GetVerbose (); + + virtual void + WriteResults (Results &results) = 0; + + template + Measurement CreateMeasurement (A a, const char* name = NULL, const char* description = NULL) + { + return Measurement (a,name, description); + } + + template + TimeMeasurement CreateTimeMeasurement (A a, const char* name = NULL, const char* description = NULL) + { + return TimeMeasurement (a,name, description); + } + + template + MemoryMeasurement CreateMemoryMeasurement (A a, const char* name = NULL, const char* description = NULL) + { + return MemoryMeasurement (a,name, description); + } + + static int + Run (TestCase& test, int argc, const char** argv); + + virtual bool + ParseOption (int short_option, const char* optarg) + { + return false; + } + + virtual struct option* + GetLongOptions () + { + return NULL; + } + + lldb::SBDebugger & + GetDebugger() + { + return m_debugger; + } + + lldb::SBTarget & + GetTarget() + { + return m_target; + } + + lldb::SBProcess & + GetProcess () + { + return m_process; + } + + lldb::SBThread & + GetThread () + { + return m_thread; + } + + int + GetStep () + { + return m_step; + } + + static const int RUN_SUCCESS = 0; + static const int RUN_SETUP_ERROR = 100; + +protected: + lldb::SBDebugger m_debugger; + lldb::SBTarget m_target; + lldb::SBProcess m_process; + lldb::SBThread m_thread; + lldb::SBListener m_listener; + bool m_verbose; + int m_step; +}; +} + +#endif /* defined(__PerfTestDriver__TestCase__) */ diff --git a/tools/lldb-perf/lib/Timer.cpp b/tools/lldb-perf/lib/Timer.cpp new file mode 100644 index 000000000000..4bbab904c63e --- /dev/null +++ b/tools/lldb-perf/lib/Timer.cpp @@ -0,0 +1,61 @@ +//===-- 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 "Timer.h" +#include + +using namespace lldb_perf; + +TimeGauge::TimeType +TimeGauge::Now () +{ + return high_resolution_clock::now(); +} + +TimeGauge::TimeGauge () : + m_start(), + m_state(TimeGauge::State::eNeverUsed) +{ +} + +void +TimeGauge::Start () +{ + m_state = TimeGauge::State::eCounting; + m_start = Now(); +} + +double +TimeGauge::Stop () +{ + m_stop = Now(); + assert(m_state == TimeGauge::State::eCounting && "cannot stop a non-started clock"); + m_state = TimeGauge::State::eStopped; + m_delta = duration_cast>(m_stop-m_start).count(); + return m_delta; +} + +double +TimeGauge::GetStartValue () const +{ + return (double)m_start.time_since_epoch().count() * (double)system_clock::period::num / (double)system_clock::period::den; +} + +double +TimeGauge::GetStopValue () const +{ + return (double)m_stop.time_since_epoch().count() * (double)system_clock::period::num / (double)system_clock::period::den; +} + +double +TimeGauge::GetDeltaValue () const +{ + assert(m_state == TimeGauge::State::eStopped && "clock must be used before you can evaluate it"); + return m_delta; +} diff --git a/tools/lldb-perf/lib/Timer.h b/tools/lldb-perf/lib/Timer.h new file mode 100644 index 000000000000..ff179355cd43 --- /dev/null +++ b/tools/lldb-perf/lib/Timer.h @@ -0,0 +1,66 @@ +//===-- 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 __PerfTestDriver__Timer__ +#define __PerfTestDriver__Timer__ + +#include "Gauge.h" + +#include + +using namespace std::chrono; + +namespace lldb_perf +{ +class TimeGauge : public Gauge +{ +public: + TimeGauge (); + + virtual + ~TimeGauge () + { + } + + void + Start (); + + double + Stop (); + + virtual double + GetStartValue () const; + + virtual double + GetStopValue () const; + + virtual double + GetDeltaValue () const; + +private: + enum class State + { + eNeverUsed, + eCounting, + eStopped + }; + + typedef high_resolution_clock::time_point TimeType; + TimeType m_start; + TimeType m_stop; + double m_delta; + State m_state; + + TimeType + Now (); + +}; +} + +#endif /* defined(__PerfTestDriver__Timer__) */ diff --git a/tools/lldb-perf/lib/Xcode.cpp b/tools/lldb-perf/lib/Xcode.cpp new file mode 100644 index 000000000000..7b35e1c8cb54 --- /dev/null +++ b/tools/lldb-perf/lib/Xcode.cpp @@ -0,0 +1,165 @@ +//===-- Xcode.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Xcode.h" +#include + +using namespace std; +using namespace lldb_perf; + +void +Xcode::FetchVariable (SBValue value, uint32_t expand, bool verbose) +{ + auto name = value.GetName(); + auto num_value = value.GetValueAsUnsigned(0); + auto summary = value.GetSummary(); + auto in_scope = value.IsInScope(); + auto has_children = value.MightHaveChildren(); + auto type_1 = value.GetType(); + auto type_2 = value.GetType(); + auto type_name_1 = value.GetTypeName(); + auto type_3 = value.GetType(); + auto type_name_2 = value.GetTypeName(); + if (verbose) + printf("%s %s = 0x%llx (%llu) %s\n",value.GetTypeName(),value.GetName(),num_value, num_value,summary); + if (expand > 0) + { + auto count = value.GetNumChildren(); + for (int i = 0; i < count; i++) + { + SBValue child(value.GetChildAtIndex(i, lldb::eDynamicCanRunTarget, true)); + FetchVariable (child,expand-1,verbose); + } + } +} + +void +Xcode::FetchModules (SBTarget target, bool verbose) +{ + auto count = target.GetNumModules(); + for (int i = 0; i < count; i++) + { + SBModule module(target.GetModuleAtIndex(i)); + auto fspec = module.GetFileSpec(); + std::string path(1024,0); + fspec.GetPath(&path[0],1024); + auto uuid = module.GetUUIDBytes(); + if (verbose) + { + printf("%s %s\n",path.c_str(),module.GetUUIDString()); + } + } +} + +void +Xcode::FetchVariables (SBFrame frame, uint32_t expand, bool verbose) +{ + auto values = frame.GetVariables (true,true,true,false, eDynamicCanRunTarget); + auto count = values.GetSize(); + for (int i = 0; i < count; i++) + { + SBValue value(values.GetValueAtIndex(i)); + FetchVariable (value,expand,verbose); + } +} + +void +Xcode::FetchFrames(SBProcess process, bool variables, bool verbose) +{ + auto pCount = process.GetNumThreads(); + for (int p = 0; p < pCount; p++) + { + SBThread thread(process.GetThreadAtIndex(p)); + auto tCount = thread.GetNumFrames (); + if (verbose) + printf("%s %d %d {%d}\n",thread.GetQueueName(),tCount,thread.GetStopReason(),eStopReasonBreakpoint); + for (int t = 0; t < tCount; t++) + { + SBFrame frame(thread.GetFrameAtIndex(t)); + auto fp = frame.GetFP(); + SBThread thread_dup = frame.GetThread(); + SBFileSpec filespec(process.GetTarget().GetExecutable()); + std::string path(1024,0); + filespec.GetPath(&path[0],1024); + auto state = process.GetState(); + auto pCount_dup = process.GetNumThreads(); + auto byte_size = process.GetAddressByteSize(); + auto pc = frame.GetPC(); + SBSymbolContext context(frame.GetSymbolContext(0x0000006e)); + SBModule module(context.GetModule()); + SBLineEntry entry(context.GetLineEntry()); + SBFileSpec entry_filespec(process.GetTarget().GetExecutable()); + std::string entry_path(1024,0); + entry_filespec.GetPath(&entry_path[0],1024); + auto line_1 = entry.GetLine(); + auto line_2 = entry.GetLine(); + auto fname = frame.GetFunctionName(); + if (verbose) + printf("%llu %s %d %d %llu %s %d %s\n",fp,path.c_str(),state,byte_size,pc,entry_path.c_str(),line_1,fname); + if (variables) + FetchVariables (frame, 0, verbose); + } + } +} + +void +Xcode::RunExpression (SBFrame frame, const char* expression, bool po, bool verbose) +{ + SBValue value (frame.EvaluateExpression (expression, eDynamicCanRunTarget)); + FetchVariable (value,0,verbose); + if (po) + { + auto descr = value.GetObjectDescription(); + if (descr) + printf("po = %s\n",descr); + } +} + +void +Xcode::Next (SBThread thread) +{ + thread.StepOver(); +} + +void +Xcode::Continue (SBProcess process) +{ + process.Continue(); +} + +void +Xcode::RunCommand (SBDebugger debugger, const char* cmd, bool verbose) +{ + SBCommandReturnObject sb_ret; + auto interpreter = debugger.GetCommandInterpreter(); + interpreter.HandleCommand(cmd,sb_ret); + if (verbose) + printf("%s\n%s\n",sb_ret.GetOutput(false),sb_ret.GetError(false)); +} + +SBThread +Xcode::GetThreadWithStopReason (SBProcess process, StopReason reason) +{ + auto threads_count = process.GetNumThreads(); + for (auto thread_num = 0; thread_num < threads_count; thread_num++) + { + SBThread thread(process.GetThreadAtIndex(thread_num)); + if (thread.GetStopReason() == reason) + { + return thread; + } + } + return SBThread(); +} + +SBBreakpoint +Xcode::CreateFileLineBreakpoint (SBTarget target, const char* file, uint32_t line) +{ + return target.BreakpointCreateByLocation(file, line); +} diff --git a/tools/lldb-perf/lib/Xcode.h b/tools/lldb-perf/lib/Xcode.h new file mode 100644 index 000000000000..77e025369372 --- /dev/null +++ b/tools/lldb-perf/lib/Xcode.h @@ -0,0 +1,64 @@ +//===-- Xcode.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __PerfTestDriver__Xcode__ +#define __PerfTestDriver__Xcode__ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBBreakpoint.h" + +using namespace lldb; + +namespace lldb_perf +{ +class Xcode +{ +public: + static void + FetchVariable (SBValue value, uint32_t expand = 0, bool verbose = false); + + static void + FetchModules (SBTarget target, bool verbose = false); + + static void + FetchVariables (SBFrame frame, uint32_t expand = 0, bool verbose = false); + + static void + FetchFrames (SBProcess process, bool variables = false, bool verbose = false); + + static void + RunExpression (SBFrame frame, const char* expression, bool po = false, bool verbose = false); + + static void + Next (SBThread thread); + + static void + Continue (SBProcess process); + + static void + RunCommand (SBDebugger debugger, const char* cmd, bool verbose = false); + + static SBThread + GetThreadWithStopReason (SBProcess process, StopReason reason); + + static SBBreakpoint + CreateFileLineBreakpoint (SBTarget target, const char* file, uint32_t line); +}; +} + +#endif /* defined(__PerfTestDriver__Xcode__) */ diff --git a/tools/lldb-perf/lldbperf.xcodeproj/project.pbxproj b/tools/lldb-perf/lldbperf.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..0750f700b11d --- /dev/null +++ b/tools/lldb-perf/lldbperf.xcodeproj/project.pbxproj @@ -0,0 +1,1224 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + 4C1E37E316F7A0A500FF10BB /* All Perf Tests */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 4C1E37E416F7A0A600FF10BB /* Build configuration list for PBXAggregateTarget "All Perf Tests" */; + buildPhases = ( + ); + dependencies = ( + 4CE3706916FB6FCA00BFD501 /* PBXTargetDependency */, + 4CE3706B16FB6FCC00BFD501 /* PBXTargetDependency */, + 4CE3706D16FB6FCF00BFD501 /* PBXTargetDependency */, + 4CE3706F16FB6FD200BFD501 /* PBXTargetDependency */, + ); + name = "All Perf Tests"; + productName = All; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 26B902E51700FA8D00EFDCE2 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C86C5C616F7A37800844407 /* LLDB.framework */; }; + 26B902E61700FAE800EFDCE2 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C86C5C616F7A37800844407 /* LLDB.framework */; }; + 26DBAD6216FA63F0008243D2 /* lldb_perf_clang.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DBAD4916FA637D008243D2 /* lldb_perf_clang.cpp */; }; + 26DBAD6316FA66DC008243D2 /* liblldbperf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; }; + 26DBAD6516FA66EA008243D2 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */; }; + 26DF762916FBCE7100B4CC2E /* Results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DF762716FBCE7100B4CC2E /* Results.cpp */; }; + 26DF762A16FBCE7100B4CC2E /* Results.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DF762816FBCE7100B4CC2E /* Results.h */; }; + 26DF764316FBF30E00B4CC2E /* Gauge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DF764216FBF30E00B4CC2E /* Gauge.cpp */; }; + 4C1E374E16F407C800FF10BB /* Gauge.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374216F407C800FF10BB /* Gauge.h */; }; + 4C1E374F16F407C800FF10BB /* Measurement.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374316F407C800FF10BB /* Measurement.h */; }; + 4C1E375016F407C800FF10BB /* MemoryGauge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374416F407C800FF10BB /* MemoryGauge.cpp */; }; + 4C1E375116F407C800FF10BB /* MemoryGauge.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374516F407C800FF10BB /* MemoryGauge.h */; }; + 4C1E375216F407C800FF10BB /* Metric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374616F407C800FF10BB /* Metric.cpp */; }; + 4C1E375316F407C800FF10BB /* Metric.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374716F407C800FF10BB /* Metric.h */; }; + 4C1E375416F407C800FF10BB /* TestCase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374816F407C800FF10BB /* TestCase.cpp */; }; + 4C1E375516F407C800FF10BB /* TestCase.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374916F407C800FF10BB /* TestCase.h */; }; + 4C1E375616F407C800FF10BB /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374A16F407C800FF10BB /* Timer.cpp */; }; + 4C1E375716F407C800FF10BB /* Timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374B16F407C800FF10BB /* Timer.h */; }; + 4C1E375816F407C800FF10BB /* Xcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E374C16F407C800FF10BB /* Xcode.cpp */; }; + 4C1E375916F407C800FF10BB /* Xcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E374D16F407C800FF10BB /* Xcode.h */; }; + 4C1E377916F4089E00FF10BB /* sketch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E377816F4089E00FF10BB /* sketch.cpp */; }; + 4C1E377A16F4091700FF10BB /* liblldbperf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; }; + 4C1E378216F40B8900FF10BB /* CFCBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E375B16F4081300FF10BB /* CFCBundle.cpp */; }; + 4C1E378316F40B8C00FF10BB /* CFCData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E375D16F4081300FF10BB /* CFCData.cpp */; }; + 4C1E378416F40B8F00FF10BB /* CFCMutableArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E375F16F4081300FF10BB /* CFCMutableArray.cpp */; }; + 4C1E378516F40B9200FF10BB /* CFCMutableDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E376116F4081300FF10BB /* CFCMutableDictionary.cpp */; }; + 4C1E378616F40B9600FF10BB /* CFCMutableSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E376316F4081300FF10BB /* CFCMutableSet.cpp */; }; + 4C1E378716F40B9C00FF10BB /* CFCReleaser.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376516F4081300FF10BB /* CFCReleaser.h */; }; + 4C1E378816F40B9F00FF10BB /* CFCString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E376616F4081300FF10BB /* CFCString.cpp */; }; + 4C1E378916F40BA200FF10BB /* CFCString.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376716F4081300FF10BB /* CFCString.h */; }; + 4C1E378A16F40BA600FF10BB /* CoreFoundationCPP.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376816F4081300FF10BB /* CoreFoundationCPP.h */; }; + 4C1E378B16F40BAB00FF10BB /* CFCBundle.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E375C16F4081300FF10BB /* CFCBundle.h */; }; + 4C1E378C16F40BAF00FF10BB /* CFCData.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E375E16F4081300FF10BB /* CFCData.h */; }; + 4C1E378D16F40BB300FF10BB /* CFCMutableArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376016F4081300FF10BB /* CFCMutableArray.h */; }; + 4C1E378E16F40BB700FF10BB /* CFCMutableDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376216F4081300FF10BB /* CFCMutableDictionary.h */; }; + 4C1E378F16F40BBB00FF10BB /* CFCMutableSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1E376416F4081300FF10BB /* CFCMutableSet.h */; }; + 4C1E37C716F79EC300FF10BB /* liblldbperf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; }; + 4C1E37CB16F79EFA00FF10BB /* formatters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E37B416F79E4600FF10BB /* formatters.cpp */; }; + 4C1E37E016F7A05D00FF10BB /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */; }; + 4C1E37E216F7A07300FF10BB /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */; }; + 4C86C5CB16F7C1D300844407 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C86C5C616F7A37800844407 /* LLDB.framework */; }; + 4C86C5CC16F7C1E000844407 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C86C5C616F7A37800844407 /* LLDB.framework */; }; + 4C86C5DA16F7CED300844407 /* fmts_tester.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E37B316F79E4600FF10BB /* fmts_tester.mm */; }; + 4CDDF51017011EBB00D95015 /* stepping-testcase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CE3708716FB70E100BFD501 /* stepping-testcase.cpp */; }; + 4CE3707316FB701000BFD501 /* lldb-perf-stepping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CE3707216FB701000BFD501 /* lldb-perf-stepping.cpp */; }; + 4CE3707516FB703B00BFD501 /* liblldbperf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; }; + 4CE3707616FB704300BFD501 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 264B3DE816F7E47600D1E7AB /* LLDB.framework */; }; + 4CE3707716FB704B00BFD501 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 264B3DE516F7E47600D1E7AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 26F5C26A10F3D9A4009D5894; + remoteInfo = "lldb-tool"; + }; + 264B3DE716F7E47600D1E7AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 26680207115FD0ED008E1FE4; + remoteInfo = LLDB; + }; + 264B3DE916F7E47600D1E7AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 26579F68126A25920007C5CB; + remoteInfo = "darwin-debug"; + }; + 264B3DEB16F7E47600D1E7AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2689FFCA13353D7A00698AC0; + remoteInfo = "lldb-core"; + }; + 264B3DED16F7E47600D1E7AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 26DC6A101337FE6900FF7998; + remoteInfo = "lldb-platform"; + }; + 264B3DEF16F7E47600D1E7AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EDC6D49914E5C19B001B75F8; + remoteInfo = launcherXPCService; + }; + 264B3DF116F7E47600D1E7AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EDE274EC14EDCE1F005B0F75; + remoteInfo = launcherRootXPCService; + }; + 26B902D61700F9BD00EFDCE2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 26680206115FD0ED008E1FE4; + remoteInfo = LLDB; + }; + 26B902E21700F9CC00EFDCE2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C1E373816F4035D00FF10BB; + remoteInfo = lldbperf; + }; + 4C1E379016F40BCD00FF10BB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C1E373816F4035D00FF10BB; + remoteInfo = lldbperf; + }; + 4C1E37C316F79EB000FF10BB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C1E373816F4035D00FF10BB; + remoteInfo = lldbperf; + }; + 4C86C5DB16F7CF1200844407 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C86C5D016F7CC8900844407; + remoteInfo = "format-tester"; + }; + 4CE3706816FB6FCA00BFD501 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C1E376C16F4087A00FF10BB; + remoteInfo = "lldb-perf-sketch"; + }; + 4CE3706A16FB6FCC00BFD501 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C1E37B916F79E9D00FF10BB; + remoteInfo = "lldb-perf-formatters"; + }; + 4CE3706C16FB6FCF00BFD501 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26DBAD5816FA63B1008243D2; + remoteInfo = "lldb-perf-clang"; + }; + 4CE3706E16FB6FD200BFD501 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CE3705316FB6FA100BFD501; + remoteInfo = "lldb-perf-step"; + }; + 4CE3708A16FB711200BFD501 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CE3707B16FB70AD00BFD501; + remoteInfo = "stepping-testcase"; + }; + 4CE3708C16FB712300BFD501 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4C1E373116F4035D00FF10BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C1E373816F4035D00FF10BB; + remoteInfo = lldbperf; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 26DBAD5716FA63B1008243D2 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 26DBAD4816FA637D008243D2 /* build-clang.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-clang.sh"; sourceTree = ""; }; + 26DBAD4916FA637D008243D2 /* lldb_perf_clang.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lldb_perf_clang.cpp; sourceTree = ""; }; + 26DBAD5916FA63B1008243D2 /* lldb-perf-clang */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-perf-clang"; sourceTree = BUILT_PRODUCTS_DIR; }; + 26DF762716FBCE7100B4CC2E /* Results.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Results.cpp; sourceTree = ""; }; + 26DF762816FBCE7100B4CC2E /* Results.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Results.h; sourceTree = ""; }; + 26DF764216FBF30E00B4CC2E /* Gauge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Gauge.cpp; sourceTree = ""; }; + 4C1E373916F4035D00FF10BB /* liblldbperf.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblldbperf.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C1E374216F407C800FF10BB /* Gauge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gauge.h; sourceTree = ""; }; + 4C1E374316F407C800FF10BB /* Measurement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Measurement.h; sourceTree = ""; }; + 4C1E374416F407C800FF10BB /* MemoryGauge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryGauge.cpp; sourceTree = ""; }; + 4C1E374516F407C800FF10BB /* MemoryGauge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryGauge.h; sourceTree = ""; }; + 4C1E374616F407C800FF10BB /* Metric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Metric.cpp; sourceTree = ""; }; + 4C1E374716F407C800FF10BB /* Metric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Metric.h; sourceTree = ""; }; + 4C1E374816F407C800FF10BB /* TestCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestCase.cpp; sourceTree = ""; }; + 4C1E374916F407C800FF10BB /* TestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestCase.h; sourceTree = ""; }; + 4C1E374A16F407C800FF10BB /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = ""; }; + 4C1E374B16F407C800FF10BB /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timer.h; sourceTree = ""; }; + 4C1E374C16F407C800FF10BB /* Xcode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Xcode.cpp; sourceTree = ""; }; + 4C1E374D16F407C800FF10BB /* Xcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Xcode.h; sourceTree = ""; }; + 4C1E375B16F4081300FF10BB /* CFCBundle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCBundle.cpp; sourceTree = ""; }; + 4C1E375C16F4081300FF10BB /* CFCBundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCBundle.h; sourceTree = ""; }; + 4C1E375D16F4081300FF10BB /* CFCData.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCData.cpp; sourceTree = ""; }; + 4C1E375E16F4081300FF10BB /* CFCData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCData.h; sourceTree = ""; }; + 4C1E375F16F4081300FF10BB /* CFCMutableArray.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCMutableArray.cpp; sourceTree = ""; }; + 4C1E376016F4081300FF10BB /* CFCMutableArray.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCMutableArray.h; sourceTree = ""; }; + 4C1E376116F4081300FF10BB /* CFCMutableDictionary.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCMutableDictionary.cpp; sourceTree = ""; }; + 4C1E376216F4081300FF10BB /* CFCMutableDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCMutableDictionary.h; sourceTree = ""; }; + 4C1E376316F4081300FF10BB /* CFCMutableSet.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCMutableSet.cpp; sourceTree = ""; }; + 4C1E376416F4081300FF10BB /* CFCMutableSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCMutableSet.h; sourceTree = ""; }; + 4C1E376516F4081300FF10BB /* CFCReleaser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCReleaser.h; sourceTree = ""; }; + 4C1E376616F4081300FF10BB /* CFCString.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CFCString.cpp; sourceTree = ""; }; + 4C1E376716F4081300FF10BB /* CFCString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFCString.h; sourceTree = ""; }; + 4C1E376816F4081300FF10BB /* CoreFoundationCPP.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreFoundationCPP.h; sourceTree = ""; }; + 4C1E376D16F4087A00FF10BB /* lldb-perf-sketch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-perf-sketch"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C1E377716F4089E00FF10BB /* foobar.sketch2 */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = foobar.sketch2; sourceTree = ""; }; + 4C1E377816F4089E00FF10BB /* sketch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sketch.cpp; sourceTree = ""; }; + 4C1E37B316F79E4600FF10BB /* fmts_tester.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = fmts_tester.mm; sourceTree = ""; }; + 4C1E37B416F79E4600FF10BB /* formatters.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = formatters.cpp; sourceTree = ""; }; + 4C1E37BA16F79E9D00FF10BB /* lldb-perf-formatters */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-perf-formatters"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; + 4C86C5C316F7A35000844407 /* lldb.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = lldb.xcodeproj; path = ../../lldb.xcodeproj; sourceTree = ""; }; + 4C86C5C616F7A37800844407 /* LLDB.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LLDB.framework; path = build/Debug/LLDB.framework; sourceTree = ""; }; + 4C86C5D116F7CC8900844407 /* format-tester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "format-tester"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CE3705416FB6FA100BFD501 /* lldb-perf-step */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "lldb-perf-step"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CE3707216FB701000BFD501 /* lldb-perf-stepping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lldb-perf-stepping.cpp"; path = "stepping/lldb-perf-stepping.cpp"; sourceTree = ""; }; + 4CE3707C16FB70AD00BFD501 /* stepping-testcase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "stepping-testcase"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CE3708716FB70E100BFD501 /* stepping-testcase.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "stepping-testcase.cpp"; path = "stepping/stepping-testcase.cpp"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 26DBAD5616FA63B1008243D2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 26B902E61700FAE800EFDCE2 /* LLDB.framework in Frameworks */, + 26DBAD6516FA66EA008243D2 /* CoreFoundation.framework in Frameworks */, + 26DBAD6316FA66DC008243D2 /* liblldbperf.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C1E373616F4035D00FF10BB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C1E376A16F4087A00FF10BB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C86C5CC16F7C1E000844407 /* LLDB.framework in Frameworks */, + 4C1E377A16F4091700FF10BB /* liblldbperf.a in Frameworks */, + 4C1E37E016F7A05D00FF10BB /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C1E37B716F79E9D00FF10BB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C86C5CB16F7C1D300844407 /* LLDB.framework in Frameworks */, + 4C1E37E216F7A07300FF10BB /* CoreFoundation.framework in Frameworks */, + 4C1E37C716F79EC300FF10BB /* liblldbperf.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C86C5CE16F7CC8900844407 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CE3705116FB6FA100BFD501 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 26B902E51700FA8D00EFDCE2 /* LLDB.framework in Frameworks */, + 4CE3707716FB704B00BFD501 /* CoreFoundation.framework in Frameworks */, + 4CE3707616FB704300BFD501 /* LLDB.framework in Frameworks */, + 4CE3707516FB703B00BFD501 /* liblldbperf.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CE3707916FB70AD00BFD501 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 26DBAD4616FA637D008243D2 /* common */ = { + isa = PBXGroup; + children = ( + 4CE3707416FB701E00BFD501 /* stepping */, + 26DBAD4716FA637D008243D2 /* clang */, + ); + path = common; + sourceTree = ""; + }; + 26DBAD4716FA637D008243D2 /* clang */ = { + isa = PBXGroup; + children = ( + 26DBAD4816FA637D008243D2 /* build-clang.sh */, + 26DBAD4916FA637D008243D2 /* lldb_perf_clang.cpp */, + ); + path = clang; + sourceTree = ""; + }; + 4C1E373016F4035D00FF10BB = { + isa = PBXGroup; + children = ( + 4C86C5C316F7A35000844407 /* lldb.xcodeproj */, + 4C1E37B516F79E6600FF10BB /* Darwin */, + 26DBAD4616FA637D008243D2 /* common */, + 4C1E375A16F4081300FF10BB /* cfcpp */, + 4C1E374116F407C800FF10BB /* lib */, + 4C1E373A16F4035D00FF10BB /* Products */, + 4C1E37DD16F7A03900FF10BB /* Frameworks */, + ); + sourceTree = ""; + }; + 4C1E373A16F4035D00FF10BB /* Products */ = { + isa = PBXGroup; + children = ( + 4C1E373916F4035D00FF10BB /* liblldbperf.a */, + 4C1E376D16F4087A00FF10BB /* lldb-perf-sketch */, + 4C1E37BA16F79E9D00FF10BB /* lldb-perf-formatters */, + 4C86C5D116F7CC8900844407 /* format-tester */, + 26DBAD5916FA63B1008243D2 /* lldb-perf-clang */, + 4CE3705416FB6FA100BFD501 /* lldb-perf-step */, + 4CE3707C16FB70AD00BFD501 /* stepping-testcase */, + ); + name = Products; + sourceTree = ""; + }; + 4C1E374116F407C800FF10BB /* lib */ = { + isa = PBXGroup; + children = ( + 4C1E374216F407C800FF10BB /* Gauge.h */, + 26DF764216FBF30E00B4CC2E /* Gauge.cpp */, + 4C1E374316F407C800FF10BB /* Measurement.h */, + 4C1E374416F407C800FF10BB /* MemoryGauge.cpp */, + 4C1E374516F407C800FF10BB /* MemoryGauge.h */, + 4C1E374616F407C800FF10BB /* Metric.cpp */, + 4C1E374716F407C800FF10BB /* Metric.h */, + 26DF762716FBCE7100B4CC2E /* Results.cpp */, + 26DF762816FBCE7100B4CC2E /* Results.h */, + 4C1E374816F407C800FF10BB /* TestCase.cpp */, + 4C1E374916F407C800FF10BB /* TestCase.h */, + 4C1E374A16F407C800FF10BB /* Timer.cpp */, + 4C1E374B16F407C800FF10BB /* Timer.h */, + 4C1E374C16F407C800FF10BB /* Xcode.cpp */, + 4C1E374D16F407C800FF10BB /* Xcode.h */, + ); + path = lib; + sourceTree = ""; + }; + 4C1E375A16F4081300FF10BB /* cfcpp */ = { + isa = PBXGroup; + children = ( + 4C1E375B16F4081300FF10BB /* CFCBundle.cpp */, + 4C1E375C16F4081300FF10BB /* CFCBundle.h */, + 4C1E375D16F4081300FF10BB /* CFCData.cpp */, + 4C1E375E16F4081300FF10BB /* CFCData.h */, + 4C1E375F16F4081300FF10BB /* CFCMutableArray.cpp */, + 4C1E376016F4081300FF10BB /* CFCMutableArray.h */, + 4C1E376116F4081300FF10BB /* CFCMutableDictionary.cpp */, + 4C1E376216F4081300FF10BB /* CFCMutableDictionary.h */, + 4C1E376316F4081300FF10BB /* CFCMutableSet.cpp */, + 4C1E376416F4081300FF10BB /* CFCMutableSet.h */, + 4C1E376516F4081300FF10BB /* CFCReleaser.h */, + 4C1E376616F4081300FF10BB /* CFCString.cpp */, + 4C1E376716F4081300FF10BB /* CFCString.h */, + 4C1E376816F4081300FF10BB /* CoreFoundationCPP.h */, + ); + name = cfcpp; + path = ../../source/Host/macosx/cfcpp; + sourceTree = ""; + }; + 4C1E377616F4089E00FF10BB /* sketch */ = { + isa = PBXGroup; + children = ( + 4C1E377716F4089E00FF10BB /* foobar.sketch2 */, + 4C1E377816F4089E00FF10BB /* sketch.cpp */, + ); + name = sketch; + path = darwin/sketch; + sourceTree = ""; + }; + 4C1E37B216F79E4600FF10BB /* formatters */ = { + isa = PBXGroup; + children = ( + 4C1E37B316F79E4600FF10BB /* fmts_tester.mm */, + 4C1E37B416F79E4600FF10BB /* formatters.cpp */, + ); + name = formatters; + path = darwin/formatters; + sourceTree = ""; + }; + 4C1E37B516F79E6600FF10BB /* Darwin */ = { + isa = PBXGroup; + children = ( + 4C1E377616F4089E00FF10BB /* sketch */, + 4C1E37B216F79E4600FF10BB /* formatters */, + ); + name = Darwin; + sourceTree = ""; + }; + 4C1E37DD16F7A03900FF10BB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4C86C5C616F7A37800844407 /* LLDB.framework */, + 4C1E37DB16F7A03900FF10BB /* CoreFoundation.framework */, + ); + name = Frameworks; + path = ../..; + sourceTree = ""; + }; + 4C86C5DE16F7D18F00844407 /* Products */ = { + isa = PBXGroup; + children = ( + 264B3DE616F7E47600D1E7AB /* lldb */, + 264B3DE816F7E47600D1E7AB /* LLDB.framework */, + 264B3DEA16F7E47600D1E7AB /* darwin-debug */, + 264B3DEC16F7E47600D1E7AB /* liblldb-core.a */, + 264B3DEE16F7E47600D1E7AB /* lldb-platform */, + 264B3DF016F7E47600D1E7AB /* com.apple.lldb.launcherXPCService.xpc */, + 264B3DF216F7E47600D1E7AB /* com.apple.lldb.launcherRootXPCService.xpc */, + ); + name = Products; + sourceTree = ""; + }; + 4CE3707416FB701E00BFD501 /* stepping */ = { + isa = PBXGroup; + children = ( + 4CE3708716FB70E100BFD501 /* stepping-testcase.cpp */, + 4CE3707216FB701000BFD501 /* lldb-perf-stepping.cpp */, + ); + name = stepping; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 4C1E373716F4035D00FF10BB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C1E375716F407C800FF10BB /* Timer.h in Headers */, + 26DF762A16FBCE7100B4CC2E /* Results.h in Headers */, + 4C1E378D16F40BB300FF10BB /* CFCMutableArray.h in Headers */, + 4C1E374E16F407C800FF10BB /* Gauge.h in Headers */, + 4C1E378716F40B9C00FF10BB /* CFCReleaser.h in Headers */, + 4C1E378916F40BA200FF10BB /* CFCString.h in Headers */, + 4C1E375116F407C800FF10BB /* MemoryGauge.h in Headers */, + 4C1E378C16F40BAF00FF10BB /* CFCData.h in Headers */, + 4C1E375516F407C800FF10BB /* TestCase.h in Headers */, + 4C1E375916F407C800FF10BB /* Xcode.h in Headers */, + 4C1E375316F407C800FF10BB /* Metric.h in Headers */, + 4C1E378F16F40BBB00FF10BB /* CFCMutableSet.h in Headers */, + 4C1E378E16F40BB700FF10BB /* CFCMutableDictionary.h in Headers */, + 4C1E378B16F40BAB00FF10BB /* CFCBundle.h in Headers */, + 4C1E378A16F40BA600FF10BB /* CoreFoundationCPP.h in Headers */, + 4C1E374F16F407C800FF10BB /* Measurement.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 26DBAD5816FA63B1008243D2 /* lldb-perf-clang */ = { + isa = PBXNativeTarget; + buildConfigurationList = 26DBAD5F16FA63B1008243D2 /* Build configuration list for PBXNativeTarget "lldb-perf-clang" */; + buildPhases = ( + 26DBAD5516FA63B1008243D2 /* Sources */, + 26DBAD5616FA63B1008243D2 /* Frameworks */, + 26DBAD5716FA63B1008243D2 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 26B902E31700F9CC00EFDCE2 /* PBXTargetDependency */, + ); + name = "lldb-perf-clang"; + productName = lldb_perf_clang; + productReference = 26DBAD5916FA63B1008243D2 /* lldb-perf-clang */; + productType = "com.apple.product-type.tool"; + }; + 4C1E373816F4035D00FF10BB /* lldbperf */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4C1E373D16F4035D00FF10BB /* Build configuration list for PBXNativeTarget "lldbperf" */; + buildPhases = ( + 4C1E373516F4035D00FF10BB /* Sources */, + 4C1E373616F4035D00FF10BB /* Frameworks */, + 4C1E373716F4035D00FF10BB /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 26B902D71700F9BD00EFDCE2 /* PBXTargetDependency */, + ); + name = lldbperf; + productName = lldbperf; + productReference = 4C1E373916F4035D00FF10BB /* liblldbperf.a */; + productType = "com.apple.product-type.library.static"; + }; + 4C1E376C16F4087A00FF10BB /* lldb-perf-sketch */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4C1E377316F4087A00FF10BB /* Build configuration list for PBXNativeTarget "lldb-perf-sketch" */; + buildPhases = ( + 4C1E376916F4087A00FF10BB /* Sources */, + 4C1E376A16F4087A00FF10BB /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 4C1E379116F40BCD00FF10BB /* PBXTargetDependency */, + ); + name = "lldb-perf-sketch"; + productName = "lldb-perf-sketch"; + productReference = 4C1E376D16F4087A00FF10BB /* lldb-perf-sketch */; + productType = "com.apple.product-type.tool"; + }; + 4C1E37B916F79E9D00FF10BB /* lldb-perf-formatters */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4C1E37C016F79E9D00FF10BB /* Build configuration list for PBXNativeTarget "lldb-perf-formatters" */; + buildPhases = ( + 4C1E37B616F79E9D00FF10BB /* Sources */, + 4C1E37B716F79E9D00FF10BB /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 4C1E37C416F79EB000FF10BB /* PBXTargetDependency */, + 4C86C5DC16F7CF1200844407 /* PBXTargetDependency */, + ); + name = "lldb-perf-formatters"; + productName = formatters; + productReference = 4C1E37BA16F79E9D00FF10BB /* lldb-perf-formatters */; + productType = "com.apple.product-type.tool"; + }; + 4C86C5D016F7CC8900844407 /* format-tester */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4C86C5D716F7CC8900844407 /* Build configuration list for PBXNativeTarget "format-tester" */; + buildPhases = ( + 4C86C5CD16F7CC8900844407 /* Sources */, + 4C86C5CE16F7CC8900844407 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "format-tester"; + productName = "format-tester"; + productReference = 4C86C5D116F7CC8900844407 /* format-tester */; + productType = "com.apple.product-type.tool"; + }; + 4CE3705316FB6FA100BFD501 /* lldb-perf-step */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4CE3706716FB6FA100BFD501 /* Build configuration list for PBXNativeTarget "lldb-perf-step" */; + buildPhases = ( + 4CE3705016FB6FA100BFD501 /* Sources */, + 4CE3705116FB6FA100BFD501 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 4CE3708D16FB712300BFD501 /* PBXTargetDependency */, + 4CE3708B16FB711200BFD501 /* PBXTargetDependency */, + ); + name = "lldb-perf-step"; + productName = "lldb-step-test"; + productReference = 4CE3705416FB6FA100BFD501 /* lldb-perf-step */; + productType = "com.apple.product-type.tool"; + }; + 4CE3707B16FB70AD00BFD501 /* stepping-testcase */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4CE3708216FB70AD00BFD501 /* Build configuration list for PBXNativeTarget "stepping-testcase" */; + buildPhases = ( + 4CE3707816FB70AD00BFD501 /* Sources */, + 4CE3707916FB70AD00BFD501 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "stepping-testcase"; + productName = "stepping-testcase"; + productReference = 4CE3707C16FB70AD00BFD501 /* stepping-testcase */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4C1E373116F4035D00FF10BB /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0500; + ORGANIZATIONNAME = lldb.llvm.org; + }; + buildConfigurationList = 4C1E373416F4035D00FF10BB /* Build configuration list for PBXProject "lldbperf" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 4C1E373016F4035D00FF10BB; + productRefGroup = 4C1E373A16F4035D00FF10BB /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 4C86C5DE16F7D18F00844407 /* Products */; + ProjectRef = 4C86C5C316F7A35000844407 /* lldb.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 4C1E373816F4035D00FF10BB /* lldbperf */, + 4C1E376C16F4087A00FF10BB /* lldb-perf-sketch */, + 4C1E37B916F79E9D00FF10BB /* lldb-perf-formatters */, + 26DBAD5816FA63B1008243D2 /* lldb-perf-clang */, + 4C86C5D016F7CC8900844407 /* format-tester */, + 4CE3705316FB6FA100BFD501 /* lldb-perf-step */, + 4CE3707B16FB70AD00BFD501 /* stepping-testcase */, + 4C1E37E316F7A0A500FF10BB /* All Perf Tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 264B3DE616F7E47600D1E7AB /* lldb */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = lldb; + remoteRef = 264B3DE516F7E47600D1E7AB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 264B3DE816F7E47600D1E7AB /* LLDB.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = LLDB.framework; + remoteRef = 264B3DE716F7E47600D1E7AB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 264B3DEA16F7E47600D1E7AB /* darwin-debug */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = "darwin-debug"; + remoteRef = 264B3DE916F7E47600D1E7AB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 264B3DEC16F7E47600D1E7AB /* liblldb-core.a */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.dylib"; + path = "liblldb-core.a"; + remoteRef = 264B3DEB16F7E47600D1E7AB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 264B3DEE16F7E47600D1E7AB /* lldb-platform */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = "lldb-platform"; + remoteRef = 264B3DED16F7E47600D1E7AB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 264B3DF016F7E47600D1E7AB /* com.apple.lldb.launcherXPCService.xpc */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = com.apple.lldb.launcherXPCService.xpc; + remoteRef = 264B3DEF16F7E47600D1E7AB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 264B3DF216F7E47600D1E7AB /* com.apple.lldb.launcherRootXPCService.xpc */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = com.apple.lldb.launcherRootXPCService.xpc; + remoteRef = 264B3DF116F7E47600D1E7AB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXSourcesBuildPhase section */ + 26DBAD5516FA63B1008243D2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 26DBAD6216FA63F0008243D2 /* lldb_perf_clang.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C1E373516F4035D00FF10BB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C1E378316F40B8C00FF10BB /* CFCData.cpp in Sources */, + 4C1E378416F40B8F00FF10BB /* CFCMutableArray.cpp in Sources */, + 4C1E378216F40B8900FF10BB /* CFCBundle.cpp in Sources */, + 4C1E375016F407C800FF10BB /* MemoryGauge.cpp in Sources */, + 4C1E375416F407C800FF10BB /* TestCase.cpp in Sources */, + 4C1E375816F407C800FF10BB /* Xcode.cpp in Sources */, + 26DF762916FBCE7100B4CC2E /* Results.cpp in Sources */, + 4C1E375216F407C800FF10BB /* Metric.cpp in Sources */, + 4C1E375616F407C800FF10BB /* Timer.cpp in Sources */, + 4C1E378616F40B9600FF10BB /* CFCMutableSet.cpp in Sources */, + 4C1E378516F40B9200FF10BB /* CFCMutableDictionary.cpp in Sources */, + 4C1E378816F40B9F00FF10BB /* CFCString.cpp in Sources */, + 26DF764316FBF30E00B4CC2E /* Gauge.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C1E376916F4087A00FF10BB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C1E377916F4089E00FF10BB /* sketch.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C1E37B616F79E9D00FF10BB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C1E37CB16F79EFA00FF10BB /* formatters.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C86C5CD16F7CC8900844407 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C86C5DA16F7CED300844407 /* fmts_tester.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CE3705016FB6FA100BFD501 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CE3707316FB701000BFD501 /* lldb-perf-stepping.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CE3707816FB70AD00BFD501 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CDDF51017011EBB00D95015 /* stepping-testcase.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 26B902D71700F9BD00EFDCE2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = LLDB; + targetProxy = 26B902D61700F9BD00EFDCE2 /* PBXContainerItemProxy */; + }; + 26B902E31700F9CC00EFDCE2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C1E373816F4035D00FF10BB /* lldbperf */; + targetProxy = 26B902E21700F9CC00EFDCE2 /* PBXContainerItemProxy */; + }; + 4C1E379116F40BCD00FF10BB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C1E373816F4035D00FF10BB /* lldbperf */; + targetProxy = 4C1E379016F40BCD00FF10BB /* PBXContainerItemProxy */; + }; + 4C1E37C416F79EB000FF10BB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C1E373816F4035D00FF10BB /* lldbperf */; + targetProxy = 4C1E37C316F79EB000FF10BB /* PBXContainerItemProxy */; + }; + 4C86C5DC16F7CF1200844407 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C86C5D016F7CC8900844407 /* format-tester */; + targetProxy = 4C86C5DB16F7CF1200844407 /* PBXContainerItemProxy */; + }; + 4CE3706916FB6FCA00BFD501 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C1E376C16F4087A00FF10BB /* lldb-perf-sketch */; + targetProxy = 4CE3706816FB6FCA00BFD501 /* PBXContainerItemProxy */; + }; + 4CE3706B16FB6FCC00BFD501 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C1E37B916F79E9D00FF10BB /* lldb-perf-formatters */; + targetProxy = 4CE3706A16FB6FCC00BFD501 /* PBXContainerItemProxy */; + }; + 4CE3706D16FB6FCF00BFD501 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 26DBAD5816FA63B1008243D2 /* lldb-perf-clang */; + targetProxy = 4CE3706C16FB6FCF00BFD501 /* PBXContainerItemProxy */; + }; + 4CE3706F16FB6FD200BFD501 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CE3705316FB6FA100BFD501 /* lldb-perf-step */; + targetProxy = 4CE3706E16FB6FD200BFD501 /* PBXContainerItemProxy */; + }; + 4CE3708B16FB711200BFD501 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CE3707B16FB70AD00BFD501 /* stepping-testcase */; + targetProxy = 4CE3708A16FB711200BFD501 /* PBXContainerItemProxy */; + }; + 4CE3708D16FB712300BFD501 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C1E373816F4035D00FF10BB /* lldbperf */; + targetProxy = 4CE3708C16FB712300BFD501 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 26DBAD6016FA63B1008243D2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../build/Debug", + ); + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/"; + }; + name = Debug; + }; + 26DBAD6116FA63B1008243D2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../build/Debug", + ); + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Release"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/"; + }; + name = Release; + }; + 4C1E373B16F4035D00FF10BB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + 4C1E373C16F4035D00FF10BB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + }; + name = Release; + }; + 4C1E373E16F4035D00FF10BB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include"; + }; + name = Debug; + }; + 4C1E373F16F4035D00FF10BB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include"; + }; + name = Release; + }; + 4C1E377416F4087A00FF10BB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../build/Debug", + ); + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include $(SRCROOT)/../../source/Host/macosx/cfcpp $(SRCROOT)/../"; + }; + name = Debug; + }; + 4C1E377516F4087A00FF10BB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../build/Debug", + ); + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include $(SRCROOT)/../../source/Host/macosx/cfcpp $(SRCROOT)/../"; + }; + name = Release; + }; + 4C1E37C116F79E9D00FF10BB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../build/Debug", + ); + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/"; + }; + name = Debug; + }; + 4C1E37C216F79E9D00FF10BB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../build/Debug", + ); + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug"; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/"; + }; + name = Release; + }; + 4C1E37E516F7A0A600FF10BB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 4C1E37E616F7A0A600FF10BB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 4C86C5D816F7CC8900844407 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 4C86C5D916F7CC8900844407 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 4CE3705A16FB6FA100BFD501 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../build/Debug", + ); + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/"; + }; + name = Debug; + }; + 4CE3705B16FB6FA100BFD501 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../../build/Debug", + ); + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../../../../build/Debug"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../ $(SRCROOT)/../../include/"; + }; + name = Release; + }; + 4CE3708316FB70AD00BFD501 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + 4CE3708416FB70AD00BFD501 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_OPTIMIZATION_LEVEL = 0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 26DBAD5F16FA63B1008243D2 /* Build configuration list for PBXNativeTarget "lldb-perf-clang" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 26DBAD6016FA63B1008243D2 /* Debug */, + 26DBAD6116FA63B1008243D2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4C1E373416F4035D00FF10BB /* Build configuration list for PBXProject "lldbperf" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4C1E373B16F4035D00FF10BB /* Debug */, + 4C1E373C16F4035D00FF10BB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4C1E373D16F4035D00FF10BB /* Build configuration list for PBXNativeTarget "lldbperf" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4C1E373E16F4035D00FF10BB /* Debug */, + 4C1E373F16F4035D00FF10BB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4C1E377316F4087A00FF10BB /* Build configuration list for PBXNativeTarget "lldb-perf-sketch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4C1E377416F4087A00FF10BB /* Debug */, + 4C1E377516F4087A00FF10BB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4C1E37C016F79E9D00FF10BB /* Build configuration list for PBXNativeTarget "lldb-perf-formatters" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4C1E37C116F79E9D00FF10BB /* Debug */, + 4C1E37C216F79E9D00FF10BB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4C1E37E416F7A0A600FF10BB /* Build configuration list for PBXAggregateTarget "All Perf Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4C1E37E516F7A0A600FF10BB /* Debug */, + 4C1E37E616F7A0A600FF10BB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4C86C5D716F7CC8900844407 /* Build configuration list for PBXNativeTarget "format-tester" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4C86C5D816F7CC8900844407 /* Debug */, + 4C86C5D916F7CC8900844407 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4CE3706716FB6FA100BFD501 /* Build configuration list for PBXNativeTarget "lldb-perf-step" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CE3705A16FB6FA100BFD501 /* Debug */, + 4CE3705B16FB6FA100BFD501 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4CE3708216FB70AD00BFD501 /* Build configuration list for PBXNativeTarget "stepping-testcase" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CE3708316FB70AD00BFD501 /* Debug */, + 4CE3708416FB70AD00BFD501 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4C1E373116F4035D00FF10BB /* Project object */; +} diff --git a/tools/lldb-server/CMakeLists.txt b/tools/lldb-server/CMakeLists.txt new file mode 100644 index 000000000000..c82be8a2beb3 --- /dev/null +++ b/tools/lldb-server/CMakeLists.txt @@ -0,0 +1,58 @@ +if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) +include_directories( + ../../../../llvm/include + ../../source/Plugins/Process/Linux + ../../source/Plugins/Process/POSIX + ) +endif () + +if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" ) +include_directories( + ../../../../llvm/include + ../../source/Plugins/Process/FreeBSD + ../../source/Plugins/Process/POSIX + ) +endif () + +if ( CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) +include_directories( + ../../../../llvm/include + ../../source/Plugins/Process/POSIX + ) +endif () + +include_directories(../../source) + +include(../../cmake/LLDBDependencies.cmake) + +add_lldb_executable(lldb-server + Acceptor.cpp + lldb-gdbserver.cpp + lldb-platform.cpp + lldb-server.cpp + LLDBServerUtilities.cpp +) + +if (BUILD_SHARED_LIBS ) + target_link_libraries(lldb-server liblldb) + target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS}) +else() + # The Darwin linker doesn't understand --start-group/--end-group. + if (LLDB_LINKER_SUPPORTS_GROUPS) + target_link_libraries(lldb-server + -Wl,--start-group ${LLDB_USED_LIBS} -Wl,--end-group) + target_link_libraries(lldb-server + -Wl,--start-group ${CLANG_USED_LIBS} -Wl,--end-group) + else() + target_link_libraries(lldb-server ${LLDB_USED_LIBS}) + target_link_libraries(lldb-server ${CLANG_USED_LIBS}) + endif() + llvm_config(lldb-server ${LLVM_LINK_COMPONENTS}) + + target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS}) +endif() + +set_target_properties(lldb-server PROPERTIES VERSION ${LLDB_VERSION}) + +install(TARGETS lldb-server + RUNTIME DESTINATION bin) diff --git a/tools/lldb-server/Makefile b/tools/lldb-server/Makefile new file mode 100644 index 000000000000..849b313dae8a --- /dev/null +++ b/tools/lldb-server/Makefile @@ -0,0 +1,25 @@ +##===- tools/lldb-server/Makefile ------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LLDB_LEVEL := ../.. + +TOOLNAME = lldb-server + +LLVMLibsOptions += -llldb -llldbUtility + +LINK_COMPONENTS := support + +include $(LLDB_LEVEL)/Makefile + +ifeq ($(HOST_OS),Darwin) + LLVMLibsOptions += -Wl,-rpath,@loader_path/../lib/ +endif + +ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux FreeBSD GNU/kFreeBSD NetBSD)) + LLVMLibsOptions += -Wl,-rpath,$(LibDir) -lpthread +endif diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt new file mode 100644 index 000000000000..94031accfec7 --- /dev/null +++ b/unittests/CMakeLists.txt @@ -0,0 +1,31 @@ +add_custom_target(LLDBUnitTests) +set_target_properties(LLDBUnitTests PROPERTIES FOLDER "LLDB tests") + +include_directories(${LLDB_SOURCE_ROOT}) + +set(LLDB_GTEST_COMMON_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/gtest_common.h) +if (MSVC) + list(APPEND LLVM_COMPILE_FLAGS /FI ${LLDB_GTEST_COMMON_INCLUDE}) +else () + list(APPEND LLVM_COMPILE_FLAGS -include ${LLDB_GTEST_COMMON_INCLUDE}) +endif () + +include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) + +function(add_lldb_unittest test_name) + add_unittest(LLDBUnitTests + ${test_name} + ${ARGN} + ) + + lldb_link_common_libs(${test_name} EXE) + target_link_libraries(${test_name} ${CLANG_USED_LIBS} ${LLDB_SYSTEM_LIBS}) + llvm_config(${test_name} ${LLVM_LINK_COMPONENTS}) +endfunction() + +add_subdirectory(Editline) +add_subdirectory(Expression) +add_subdirectory(Host) +add_subdirectory(Interpreter) +add_subdirectory(ScriptInterpreter) +add_subdirectory(Utility) diff --git a/unittests/Editline/CMakeLists.txt b/unittests/Editline/CMakeLists.txt new file mode 100644 index 000000000000..eaa21c490d98 --- /dev/null +++ b/unittests/Editline/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_unittest(EditlineTests + EditlineTest.cpp + ) diff --git a/unittests/Editline/EditlineTest.cpp b/unittests/Editline/EditlineTest.cpp new file mode 100644 index 000000000000..d82ef4c17c77 --- /dev/null +++ b/unittests/Editline/EditlineTest.cpp @@ -0,0 +1,368 @@ +//===-- EditlineTest.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_DISABLE_LIBEDIT + +#define EDITLINE_TEST_DUMP_OUTPUT 0 + +#include +#include + +#include +#include + +#include "gtest/gtest.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/StringList.h" +#include "lldb/Host/Editline.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Utility/PseudoTerminal.h" + +namespace +{ + const size_t TIMEOUT_MILLIS = 5000; +} + +class FilePointer +{ +public: + + FilePointer () = delete; + + FilePointer (const FilePointer&) = delete; + + FilePointer (FILE *file_p) + : _file_p (file_p) + { + } + + ~FilePointer () + { + if (_file_p != nullptr) + { + const int close_result = fclose (_file_p); + EXPECT_EQ(0, close_result); + } + } + + operator FILE* () + { + return _file_p; + } + +private: + + FILE *_file_p; + +}; + +/** + Wraps an Editline class, providing a simple way to feed + input (as if from the keyboard) and receive output from Editline. + */ +class EditlineAdapter +{ +public: + + EditlineAdapter (); + + void + CloseInput (); + + bool + IsValid () const + { + return _editline_sp.get () != nullptr; + } + + lldb_private::Editline& + GetEditline () + { + return *_editline_sp; + } + + bool + SendLine (const std::string &line); + + bool + SendLines (const std::vector &lines); + + bool + GetLine (std::string &line, bool &interrupted, size_t timeout_millis); + + bool + GetLines (lldb_private::StringList &lines, bool &interrupted, size_t timeout_millis); + + void + ConsumeAllOutput (); + +private: + + static bool + IsInputComplete ( + lldb_private::Editline * editline, + lldb_private::StringList & lines, + void * baton); + + std::unique_ptr _editline_sp; + + lldb_utility::PseudoTerminal _pty; + int _pty_master_fd; + int _pty_slave_fd; + + std::unique_ptr _el_slave_file; +}; + +EditlineAdapter::EditlineAdapter () : + _editline_sp (), + _pty (), + _pty_master_fd (-1), + _pty_slave_fd (-1), + _el_slave_file () +{ + lldb_private::Error error; + + // Open the first master pty available. + char error_string[256]; + error_string[0] = '\0'; + if (!_pty.OpenFirstAvailableMaster (O_RDWR, error_string, sizeof (error_string))) + { + fprintf(stderr, "failed to open first available master pty: '%s'\n", error_string); + return; + } + + // Grab the master fd. This is a file descriptor we will: + // (1) write to when we want to send input to editline. + // (2) read from when we want to see what editline sends back. + _pty_master_fd = _pty.GetMasterFileDescriptor(); + + // Open the corresponding slave pty. + if (!_pty.OpenSlave (O_RDWR, error_string, sizeof (error_string))) + { + fprintf(stderr, "failed to open slave pty: '%s'\n", error_string); + return; + } + _pty_slave_fd = _pty.GetSlaveFileDescriptor(); + + _el_slave_file.reset (new FilePointer (fdopen (_pty_slave_fd, "rw"))); + EXPECT_FALSE (nullptr == *_el_slave_file); + if (*_el_slave_file == nullptr) + return; + + // Create an Editline instance. + _editline_sp.reset (new lldb_private::Editline("gtest editor", *_el_slave_file, *_el_slave_file, *_el_slave_file, false)); + _editline_sp->SetPrompt ("> "); + + // Hookup our input complete callback. + _editline_sp->SetIsInputCompleteCallback(IsInputComplete, this); +} + +void +EditlineAdapter::CloseInput () +{ + if (_el_slave_file != nullptr) + _el_slave_file.reset (nullptr); +} + +bool +EditlineAdapter::SendLine (const std::string &line) +{ + // Ensure we're valid before proceeding. + if (!IsValid ()) + return false; + + // Write the line out to the pipe connected to editline's input. + ssize_t input_bytes_written = + ::write (_pty_master_fd, + line.c_str(), + line.length() * sizeof (std::string::value_type)); + + const char *eoln = "\n"; + const size_t eoln_length = strlen(eoln); + input_bytes_written = + ::write (_pty_master_fd, + eoln, + eoln_length * sizeof (char)); + + EXPECT_EQ (eoln_length * sizeof (char), input_bytes_written); + return eoln_length * sizeof (char) == input_bytes_written; +} + +bool +EditlineAdapter::SendLines (const std::vector &lines) +{ + for (auto &line : lines) + { +#if EDITLINE_TEST_DUMP_OUTPUT + printf (" sending line \"%s\"\n", line.c_str()); +#endif + if (!SendLine (line)) + return false; + } + return true; +} + +// We ignore the timeout for now. +bool +EditlineAdapter::GetLine (std::string &line, bool &interrupted, size_t /* timeout_millis */) +{ + // Ensure we're valid before proceeding. + if (!IsValid ()) + return false; + + _editline_sp->GetLine (line, interrupted); + return true; +} + +bool +EditlineAdapter::GetLines (lldb_private::StringList &lines, bool &interrupted, size_t /* timeout_millis */) +{ + // Ensure we're valid before proceeding. + if (!IsValid ()) + return false; + + _editline_sp->GetLines (1, lines, interrupted); + return true; +} + +bool +EditlineAdapter::IsInputComplete ( + lldb_private::Editline * editline, + lldb_private::StringList & lines, + void * baton) +{ + // We'll call ourselves complete if we've received a balanced set of braces. + int start_block_count = 0; + int brace_balance = 0; + + for (size_t i = 0; i < lines.GetSize (); ++i) + { + for (auto ch : lines[i]) + { + if (ch == '{') + { + ++start_block_count; + ++brace_balance; + } + else if (ch == '}') + --brace_balance; + } + } + + return (start_block_count > 0) && (brace_balance == 0); +} + +void +EditlineAdapter::ConsumeAllOutput () +{ + FilePointer output_file (fdopen (_pty_master_fd, "r")); + + int ch; + while ((ch = fgetc(output_file)) != EOF) + { +#if EDITLINE_TEST_DUMP_OUTPUT + char display_str[] = { 0, 0, 0 }; + switch (ch) + { + case '\t': + display_str[0] = '\\'; + display_str[1] = 't'; + break; + case '\n': + display_str[0] = '\\'; + display_str[1] = 'n'; + break; + case '\r': + display_str[0] = '\\'; + display_str[1] = 'r'; + break; + default: + display_str[0] = ch; + break; + } + printf (" 0x%02x (%03d) (%s)\n", ch, ch, display_str); + // putc(ch, stdout); +#endif + } +} + +TEST (EditlineTest, EditlineReceivesSingleLineText) +{ + setenv ("TERM", "vt100", 1); + + // Create an editline. + EditlineAdapter el_adapter; + EXPECT_TRUE (el_adapter.IsValid ()); + if (!el_adapter.IsValid ()) + return; + + // Dump output. + std::thread el_output_thread( [&] { el_adapter.ConsumeAllOutput (); }); + + // Send it some text via our virtual keyboard. + const std::string input_text ("Hello, world"); + EXPECT_TRUE (el_adapter.SendLine (input_text)); + + // Verify editline sees what we put in. + std::string el_reported_line; + bool input_interrupted = false; + const bool received_line = el_adapter.GetLine (el_reported_line, input_interrupted, TIMEOUT_MILLIS); + + EXPECT_TRUE (received_line); + EXPECT_FALSE (input_interrupted); + EXPECT_EQ (input_text, el_reported_line); + + el_adapter.CloseInput(); + el_output_thread.join(); +} + +TEST (EditlineTest, EditlineReceivesMultiLineText) +{ + setenv ("TERM", "vt100", 1); + + // Create an editline. + EditlineAdapter el_adapter; + EXPECT_TRUE (el_adapter.IsValid ()); + if (!el_adapter.IsValid ()) + return; + + // Stick editline output/error dumpers on separate threads. + std::thread el_output_thread( [&] { el_adapter.ConsumeAllOutput (); }); + + // Send it some text via our virtual keyboard. + std::vector input_lines; + input_lines.push_back ("int foo()"); + input_lines.push_back ("{"); + input_lines.push_back ("printf(\"Hello, world\");"); + input_lines.push_back ("}"); + input_lines.push_back (""); + + EXPECT_TRUE (el_adapter.SendLines (input_lines)); + + // Verify editline sees what we put in. + lldb_private::StringList el_reported_lines; + bool input_interrupted = false; + + EXPECT_TRUE (el_adapter.GetLines (el_reported_lines, input_interrupted, TIMEOUT_MILLIS)); + EXPECT_FALSE (input_interrupted); + + // Without any auto indentation support, our output should directly match our input. + EXPECT_EQ (input_lines.size (), el_reported_lines.GetSize ()); + if (input_lines.size () == el_reported_lines.GetSize ()) + { + for (auto i = 0; i < input_lines.size(); ++i) + EXPECT_EQ (input_lines[i], el_reported_lines[i]); + } + + el_adapter.CloseInput(); + el_output_thread.join(); +} + +#endif diff --git a/unittests/Expression/CMakeLists.txt b/unittests/Expression/CMakeLists.txt new file mode 100644 index 000000000000..04bad141170b --- /dev/null +++ b/unittests/Expression/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_unittest(ExpressionTests + GoParserTest.cpp + ) diff --git a/unittests/Expression/GoParserTest.cpp b/unittests/Expression/GoParserTest.cpp new file mode 100644 index 000000000000..76483979f353 --- /dev/null +++ b/unittests/Expression/GoParserTest.cpp @@ -0,0 +1,250 @@ +//===-- GoParserTest.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(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Workaround for MSVC standard library bug, which fails to include when +// exceptions are disabled. +#include +#endif + +#include + +#include "gtest/gtest.h" + +#include "lldb/Core/Error.h" +#include "Plugins/ExpressionParser/Go/GoParser.h" + +using namespace lldb_private; + +namespace +{ +struct ASTPrinter +{ + ASTPrinter(GoASTNode *n) { (*this)(n); } + + void + operator()(GoASTNode *n) + { + if (n == nullptr) + { + m_stream << "nil "; + return; + } + m_stream << "(" << n->GetKindName() << " "; + n->WalkChildren(*this); + if (auto *nn = llvm::dyn_cast(n)) + m_stream << nn->GetDefine() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << nn->GetValue().m_value.str() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << nn->GetName().m_value.str() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << (nn->GetEllipsis() ? "..." : "") << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << nn->GetDir() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << nn->GetDefine() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << nn->GetSlice3() << " "; + if (auto *nn = llvm::dyn_cast(n)) + m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " "; + m_stream << ") "; + } + + const std::string + str() const + { + return m_stream.str(); + } + std::stringstream m_stream; +}; + +testing::AssertionResult +CheckStatement(const char *_s, const char *c_expr, const char *sexpr, const char *code) +{ + GoParser parser(code); + std::unique_ptr stmt(parser.Statement()); + if (parser.Failed() || !stmt) + { + Error err; + parser.GetError(err); + return testing::AssertionFailure() << "Error parsing " << c_expr << "\n\t" << err.AsCString(); + } + std::string actual_sexpr = ASTPrinter(stmt.get()).str(); + if (actual_sexpr == sexpr) + return testing::AssertionSuccess(); + return testing::AssertionFailure() << "Parsing: " << c_expr << "\nExpected: " << sexpr + << "\nGot: " << actual_sexpr; +} +} // namespace + +#define EXPECT_PARSE(s, c) EXPECT_PRED_FORMAT2(CheckStatement, s, c) + +TEST(GoParserTest, ParseBasicLiterals) +{ + EXPECT_PARSE("(ExprStmt (BasicLit 0 ) ) ", "0"); + EXPECT_PARSE("(ExprStmt (BasicLit 42 ) ) ", "42"); + EXPECT_PARSE("(ExprStmt (BasicLit 0600 ) ) ", "0600"); + EXPECT_PARSE("(ExprStmt (BasicLit 0xBadFace ) ) ", "0xBadFace"); + EXPECT_PARSE("(ExprStmt (BasicLit 170141183460469231731687303715884105727 ) ) ", + "170141183460469231731687303715884105727"); + + EXPECT_PARSE("(ExprStmt (BasicLit 0. ) ) ", "0."); + EXPECT_PARSE("(ExprStmt (BasicLit 72.40 ) ) ", "72.40"); + EXPECT_PARSE("(ExprStmt (BasicLit 072.40 ) ) ", "072.40"); + EXPECT_PARSE("(ExprStmt (BasicLit 2.71828 ) ) ", "2.71828"); + EXPECT_PARSE("(ExprStmt (BasicLit 1.e+0 ) ) ", "1.e+0"); + EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11 ) ) ", "6.67428e-11"); + EXPECT_PARSE("(ExprStmt (BasicLit 1E6 ) ) ", "1E6"); + EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6 ) ) ", ".12345E+6"); + + EXPECT_PARSE("(ExprStmt (BasicLit 0i ) ) ", "0i"); + EXPECT_PARSE("(ExprStmt (BasicLit 011i ) ) ", "011i"); + EXPECT_PARSE("(ExprStmt (BasicLit 0.i ) ) ", "0.i"); + EXPECT_PARSE("(ExprStmt (BasicLit 2.71828i ) ) ", "2.71828i"); + EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11i ) ) ", "6.67428e-11i"); + EXPECT_PARSE("(ExprStmt (BasicLit 1E6i ) ) ", "1E6i"); + EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6i ) ) ", ".12345E+6i"); + + EXPECT_PARSE("(ExprStmt (BasicLit 'a' ) ) ", "'a'"); + EXPECT_PARSE("(ExprStmt (BasicLit '本' ) ) ", "'本'"); + EXPECT_PARSE("(ExprStmt (BasicLit \"abc\" ) ) ", "\"abc\""); + EXPECT_PARSE("(ExprStmt (BasicLit `abc` ) ) ", "`abc`"); + EXPECT_PARSE("(ExprStmt (BasicLit `ab\nc` ) ) ", "`ab\nc`"); +} + +TEST(GoParserTest, ParseOperand) +{ + EXPECT_PARSE("(ExprStmt (Ident a ) ) ", "a"); + EXPECT_PARSE("(ExprStmt (Ident _x9 ) ) ", "_x9"); + EXPECT_PARSE("(ExprStmt (Ident ThisVariableIsExported ) ) ", "ThisVariableIsExported"); + EXPECT_PARSE("(ExprStmt (Ident αβ ) ) ", "αβ"); + + EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident math ) (Ident Sin ) ) ) ", "math.Sin"); +} + +TEST(GoParserTest, ParseCompositeLiterals) +{ + EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Point3D ) ) ) ", "Point3D{}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Line ) (Ident origin ) (CompositeLit (Ident Point3D ) (KeyValueExpr " + "(Ident y ) (UnaryExpr (BasicLit 4 ) - ) ) (KeyValueExpr (Ident z ) (BasicLit 12.3 ) ) ) ) ) ", + "Line{origin, Point3D{y: -4, z: 12.3}}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident string ) ) ) ) ", "[10]string{}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 6 ) (Ident int ) ) (BasicLit 1 ) (BasicLit 2 ) " + "(BasicLit 3 ) (BasicLit 5 ) ) ) ", + "[6]int {1, 2, 3, 5}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType nil (Ident int ) ) (BasicLit 2 ) (BasicLit 3 ) (BasicLit 5 ) " + "(BasicLit 7 ) (BasicLit 9 ) (BasicLit 2147483647 ) ) ) ", + "[]int{2, 3, 5, 7, 9, 2147483647}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 128 ) (Ident bool ) ) (KeyValueExpr (BasicLit 'a' ) " + "(Ident true ) ) (KeyValueExpr (BasicLit 'e' ) (Ident true ) ) (KeyValueExpr (BasicLit 'i' ) (Ident " + "true ) ) (KeyValueExpr (BasicLit 'o' ) (Ident true ) ) (KeyValueExpr (BasicLit 'u' ) (Ident true ) ) " + "(KeyValueExpr (BasicLit 'y' ) (Ident true ) ) ) ) ", + "[128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident float32 ) ) (UnaryExpr (BasicLit 1 ) - ) " + "(KeyValueExpr (BasicLit 4 ) (UnaryExpr (BasicLit 0.1 ) - ) ) (UnaryExpr (BasicLit 0.1 ) - ) " + "(KeyValueExpr (BasicLit 9 ) (UnaryExpr (BasicLit 1 ) - ) ) ) ) ", + "[10]float32{-1, 4: -0.1, -0.1, 9: -1}"); +} + +TEST(GoParserTest, ParseEllipsisArray) +{ + EXPECT_PARSE( + "(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident string ) ) (BasicLit `Sat` ) (BasicLit `Sun` ) ) ) ", + "[...]string {`Sat`, `Sun`}"); + EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident Point ) ) (CompositeLit nil (BasicLit 1.5 " + ") (UnaryExpr (BasicLit 3.5 ) - ) ) (CompositeLit nil (BasicLit 0 ) (BasicLit 0 ) ) ) ) ", + "[...]Point{{1.5, -3.5}, {0, 0}}"); +} + +TEST(GoParserTest, ParseMap) +{ + EXPECT_PARSE("(ExprStmt (CompositeLit (MapType (Ident string ) (Ident float32 ) ) (KeyValueExpr (BasicLit `C0` ) " + "(BasicLit 16.35 ) ) (KeyValueExpr (BasicLit `D0` ) (BasicLit 18.35 ) ) ) ) ", + "map[string]float32{`C0`: 16.35, `D0`: 18.35, }"); +} + +TEST(GoParserTest, UnaryExpr) +{ + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) + ) ) ", "+x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) - ) ) ", "-x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ! ) ) ", "!x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ^ ) ) ", "^x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) & ) ) ", "&x"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) <- ) ) ", "<-x"); + EXPECT_PARSE("(ExprStmt (StarExpr (Ident x ) ) ) ", "*x"); +} + +TEST(GoParserTest, BinaryExpr) +{ + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) || ) ) ", "a || b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) && ) ) ", "a && b"); + + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) == ) ) ", "a == b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) != ) ) ", "a != b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) < ) ) ", "a < b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) <= ) ) ", "a <= b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) > ) ) ", "a > b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >= ) ) ", "a >= b"); + + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) + ) ) ", "a + b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) - ) ) ", "a - b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) | ) ) ", "a | b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) ^ ) ) ", "a ^ b"); + + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) * ) ) ", "a * b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) / ) ) ", "a / b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) % ) ) ", "a % b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) << ) ) ", "a << b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >> ) ) ", "a >> b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) & ) ) ", "a & b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) &^ ) ) ", "a &^ b"); + + EXPECT_PARSE( + "(ExprStmt (BinaryExpr (BasicLit 23 ) (BinaryExpr (BasicLit 3 ) (IndexExpr (Ident x ) (Ident i ) ) * ) + ) ) ", + "23 + 3*x[i]"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (UnaryExpr (UnaryExpr (Ident a ) + ) + ) + ) ) ", "a + + + a"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (UnaryExpr (Ident a ) ^ ) (Ident b ) >> ) ) ", "^a >> b"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (CallExpr (Ident f ) ) (CallExpr (Ident g ) ) || ) ) ", "f() || g()"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (BinaryExpr (Ident x ) (BinaryExpr (Ident y ) (BasicLit 1 ) + ) == ) " + "(BinaryExpr (UnaryExpr (Ident chanPtr ) <- ) (BasicLit 0 ) > ) && ) ) ", + "x == y+1 && <-chanPtr > 0"); +} + +TEST(GoParserTest, PrimaryExpr) +{ + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident x ) (CallExpr (Ident f ) ) <= ) ) ", "x <= f()"); + EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident s ) (BasicLit `.txt` ) + ) ) ", "(s + `.txt`)"); + EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident true ) ) ) ", "f(3.1415, true)"); + EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident a ) ... ) ) ", "f(3.1415, a...)"); + EXPECT_PARSE("(ExprStmt (IndexExpr (Ident m ) (BasicLit '1' ) ) ) ", "m['1']"); + EXPECT_PARSE("(ExprStmt (SliceExpr (Ident s ) (Ident i ) (BinaryExpr (Ident j ) (BasicLit 1 ) + ) nil 0 ) ) ", + "s[i : j + 1]"); + EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident obj ) (Ident color ) ) ) ", "obj.color"); + EXPECT_PARSE("(ExprStmt (CallExpr (SelectorExpr (IndexExpr (SelectorExpr (Ident f ) (Ident p ) ) (Ident i ) ) " + "(Ident x ) ) ) ) ", + "f.p[i].x()"); +} + +TEST(GoParserTest, Conversions) +{ + EXPECT_PARSE("(ExprStmt (StarExpr (CallExpr (Ident Point ) (Ident p ) ) ) ) ", "*Point(p)"); + EXPECT_PARSE("(ExprStmt (CallExpr (StarExpr (Ident Point ) ) (Ident p ) ) ) ", "(*Point)(p)"); + EXPECT_PARSE("(ExprStmt (UnaryExpr (CallExpr (ChanType (Ident int ) 0 ) (Ident c ) ) <- ) ) ", "<-chan int(c)"); + EXPECT_PARSE("(ExprStmt (TypeAssertExpr (Ident y ) (SelectorExpr (Ident io ) (Ident Reader ) ) ) ) ", + "y.(io.Reader)"); +} diff --git a/unittests/Host/CMakeLists.txt b/unittests/Host/CMakeLists.txt new file mode 100644 index 000000000000..b4739e113f48 --- /dev/null +++ b/unittests/Host/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_unittest(HostTests + SocketAddressTest.cpp + SocketTest.cpp + SymbolsTest.cpp + ) diff --git a/unittests/Host/SocketAddressTest.cpp b/unittests/Host/SocketAddressTest.cpp new file mode 100644 index 000000000000..bd6bda13f449 --- /dev/null +++ b/unittests/Host/SocketAddressTest.cpp @@ -0,0 +1,77 @@ +//===-- SocketAddressTest.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "gtest/gtest.h" + +#include "lldb/Host/SocketAddress.h" + +namespace +{ + class SocketAddressTest: public ::testing::Test + { + }; +} + +using namespace lldb_private; + +TEST_F (SocketAddressTest, Set) +{ + SocketAddress sa; + ASSERT_TRUE (sa.SetToLocalhost (AF_INET, 1138)); + ASSERT_STREQ ("127.0.0.1", sa.GetIPAddress ().c_str ()); + ASSERT_EQ (1138, sa.GetPort ()); + + ASSERT_TRUE (sa.SetToAnyAddress (AF_INET, 0)); + ASSERT_STREQ ("0.0.0.0", sa.GetIPAddress ().c_str ()); + ASSERT_EQ (0, sa.GetPort ()); + + ASSERT_TRUE (sa.SetToLocalhost (AF_INET6, 1139)); +#ifdef _WIN32 + ASSERT_STREQ ("0:0:0:0:0:0:0:1", sa.GetIPAddress ().c_str ()); +#else + ASSERT_STREQ ("::1", sa.GetIPAddress ().c_str ()); +#endif + ASSERT_EQ (1139, sa.GetPort ()); +} + +#ifdef _WIN32 + +// we need to test our inet_ntop implementation for Windows XP +const char* inet_ntop (int af, const void * src, + char * dst, socklen_t size); + +TEST_F (SocketAddressTest, inet_ntop) +{ + const uint8_t address4[4] = {255, 0, 1, 100}; + const uint8_t address6[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 255, 0}; + + char buffer[INET6_ADDRSTRLEN]; + memset (buffer, 'x', sizeof (buffer)); + EXPECT_STREQ ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop (AF_INET6, address6, buffer, sizeof (buffer))); + memset (buffer, 'x', sizeof (buffer)); + EXPECT_STREQ ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop (AF_INET6, address6, buffer, 31)); + memset (buffer, 'x', sizeof (buffer)); + EXPECT_STREQ (nullptr, inet_ntop (AF_INET6, address6, buffer, 0)); + memset (buffer, 'x', sizeof (buffer)); + EXPECT_STREQ (nullptr, inet_ntop (AF_INET6, address6, buffer, 30)); + + memset (buffer, 'x', sizeof (buffer)); + EXPECT_STREQ ("255.0.1.100", inet_ntop (AF_INET, address4, buffer, sizeof (buffer))); + memset (buffer, 'x', sizeof (buffer)); + EXPECT_STREQ ("255.0.1.100", inet_ntop (AF_INET, address4, buffer, 12)); + memset (buffer, 'x', sizeof (buffer)); + EXPECT_STREQ (nullptr, inet_ntop (AF_INET, address4, buffer, 0)); + memset (buffer, 'x', sizeof (buffer)); + EXPECT_STREQ (nullptr, inet_ntop (AF_INET, address4, buffer, 11)); +} + +#endif + + diff --git a/unittests/Host/SocketTest.cpp b/unittests/Host/SocketTest.cpp new file mode 100644 index 000000000000..ebb2f319a9c5 --- /dev/null +++ b/unittests/Host/SocketTest.cpp @@ -0,0 +1,201 @@ +//===-- SocketTest.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(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Workaround for MSVC standard library bug, which fails to include when +// exceptions are disabled. +#include +#endif + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "lldb/Host/Config.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/common/UDPSocket.h" + +#ifndef LLDB_DISABLE_POSIX +#include "lldb/Host/posix/DomainSocket.h" +#endif + +using namespace lldb_private; + +class SocketTest : public testing::Test +{ + public: + void + SetUp() override + { +#if defined(_MSC_VER) + WSADATA data; + ::WSAStartup(MAKEWORD(2, 2), &data); +#endif + } + + void + TearDown() override + { +#if defined(_MSC_VER) + ::WSACleanup(); +#endif + } + + protected: + static void + AcceptThread(Socket *listen_socket, const char *listen_remote_address, bool child_processes_inherit, + Socket **accept_socket, Error *error) + { + *error = listen_socket->Accept(listen_remote_address, child_processes_inherit, *accept_socket); + } + + template + void + CreateConnectedSockets(const char *listen_remote_address, const std::function &get_connect_addr, std::unique_ptr *a_up, std::unique_ptr *b_up) + { + bool child_processes_inherit = false; + Error error; + std::unique_ptr listen_socket_up(new SocketType(child_processes_inherit, error)); + EXPECT_FALSE(error.Fail()); + error = listen_socket_up->Listen(listen_remote_address, 5); + EXPECT_FALSE(error.Fail()); + EXPECT_TRUE(listen_socket_up->IsValid()); + + Error accept_error; + Socket *accept_socket; + std::thread accept_thread(AcceptThread, listen_socket_up.get(), listen_remote_address, child_processes_inherit, + &accept_socket, &accept_error); + + std::string connect_remote_address = get_connect_addr(*listen_socket_up); + std::unique_ptr connect_socket_up(new SocketType(child_processes_inherit, error)); + EXPECT_FALSE(error.Fail()); + error = connect_socket_up->Connect(connect_remote_address.c_str()); + EXPECT_FALSE(error.Fail()); + EXPECT_TRUE(connect_socket_up->IsValid()); + + a_up->swap(connect_socket_up); + EXPECT_TRUE(error.Success()); + EXPECT_NE(nullptr, a_up->get()); + EXPECT_TRUE((*a_up)->IsValid()); + + accept_thread.join(); + b_up->reset(static_cast(accept_socket)); + EXPECT_TRUE(accept_error.Success()); + EXPECT_NE(nullptr, b_up->get()); + EXPECT_TRUE((*b_up)->IsValid()); + + listen_socket_up.reset(); + } +}; + +TEST_F (SocketTest, DecodeHostAndPort) +{ + std::string host_str; + std::string port_str; + int32_t port; + Error error; + EXPECT_TRUE (Socket::DecodeHostAndPort ("localhost:1138", host_str, port_str, port, &error)); + EXPECT_STREQ ("localhost", host_str.c_str ()); + EXPECT_STREQ ("1138", port_str.c_str ()); + EXPECT_EQ (1138, port); + EXPECT_TRUE (error.Success ()); + + EXPECT_FALSE (Socket::DecodeHostAndPort ("google.com:65536", host_str, port_str, port, &error)); + EXPECT_TRUE (error.Fail ()); + EXPECT_STREQ ("invalid host:port specification: 'google.com:65536'", error.AsCString ()); + + EXPECT_FALSE (Socket::DecodeHostAndPort ("google.com:-1138", host_str, port_str, port, &error)); + EXPECT_TRUE (error.Fail ()); + EXPECT_STREQ ("invalid host:port specification: 'google.com:-1138'", error.AsCString ()); + + EXPECT_TRUE (Socket::DecodeHostAndPort ("12345", host_str, port_str, port, &error)); + EXPECT_STREQ ("", host_str.c_str ()); + EXPECT_STREQ ("12345", port_str.c_str ()); + EXPECT_EQ (12345, port); + EXPECT_TRUE (error.Success ()); + + EXPECT_TRUE (Socket::DecodeHostAndPort ("*:0", host_str, port_str, port, &error)); + EXPECT_STREQ ("*", host_str.c_str ()); + EXPECT_STREQ ("0", port_str.c_str ()); + EXPECT_EQ (0, port); + EXPECT_TRUE (error.Success ()); +} + +#ifndef LLDB_DISABLE_POSIX +TEST_F (SocketTest, DomainListenConnectAccept) +{ + char* file_name_str = tempnam(nullptr, nullptr); + EXPECT_NE (nullptr, file_name_str); + const std::string file_name(file_name_str); + free(file_name_str); + + std::unique_ptr socket_a_up; + std::unique_ptr socket_b_up; + CreateConnectedSockets(file_name.c_str(), + [=](const DomainSocket &) + { + return file_name; + }, + &socket_a_up, &socket_b_up); +} +#endif + +TEST_F (SocketTest, TCPListen0ConnectAccept) +{ + std::unique_ptr socket_a_up; + std::unique_ptr socket_b_up; + CreateConnectedSockets("127.0.0.1:0", + [=](const TCPSocket &s) + { + char connect_remote_address[64]; + snprintf(connect_remote_address, sizeof(connect_remote_address), "localhost:%u", s.GetLocalPortNumber()); + return std::string(connect_remote_address); + }, + &socket_a_up, &socket_b_up); +} + +TEST_F (SocketTest, TCPGetAddress) +{ + std::unique_ptr socket_a_up; + std::unique_ptr socket_b_up; + CreateConnectedSockets("127.0.0.1:0", + [=](const TCPSocket &s) + { + char connect_remote_address[64]; + snprintf(connect_remote_address, sizeof(connect_remote_address), "localhost:%u", s.GetLocalPortNumber()); + return std::string(connect_remote_address); + }, + &socket_a_up, + &socket_b_up); + + EXPECT_EQ (socket_a_up->GetLocalPortNumber (), socket_b_up->GetRemotePortNumber ()); + EXPECT_EQ (socket_b_up->GetLocalPortNumber (), socket_a_up->GetRemotePortNumber ()); + EXPECT_NE (socket_a_up->GetLocalPortNumber (), socket_b_up->GetLocalPortNumber ()); + EXPECT_STREQ ("127.0.0.1", socket_a_up->GetRemoteIPAddress ().c_str ()); + EXPECT_STREQ ("127.0.0.1", socket_b_up->GetRemoteIPAddress ().c_str ()); +} + +TEST_F (SocketTest, UDPConnect) +{ + Socket* socket_a; + Socket* socket_b; + + bool child_processes_inherit = false; + auto error = UDPSocket::Connect("127.0.0.1:0", child_processes_inherit, socket_a, socket_b); + + std::unique_ptr a_up(socket_a); + std::unique_ptr b_up(socket_b); + + EXPECT_TRUE(error.Success ()); + EXPECT_TRUE(a_up->IsValid()); + EXPECT_TRUE(b_up->IsValid()); +} diff --git a/unittests/Host/SymbolsTest.cpp b/unittests/Host/SymbolsTest.cpp new file mode 100644 index 000000000000..cb59b2c56539 --- /dev/null +++ b/unittests/Host/SymbolsTest.cpp @@ -0,0 +1,30 @@ +//===-- SymbolsTest.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Core/ModuleSpec.h" + +using namespace lldb_private; + +TEST(SymbolsTest, LocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile) +{ + ModuleSpec module_spec; + FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec); + EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); +} + +TEST(SymbolsTest, LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile) +{ + ModuleSpec module_spec; + // using a GUID here because the symbol file shouldn't actually exist on disk + module_spec.GetSymbolFileSpec().SetFile("4A524676-B24B-4F4E-968A-551D465EBAF1.so", false); + FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec); + EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); +} diff --git a/unittests/Interpreter/CMakeLists.txt b/unittests/Interpreter/CMakeLists.txt new file mode 100644 index 000000000000..4078476bc75a --- /dev/null +++ b/unittests/Interpreter/CMakeLists.txt @@ -0,0 +1,7 @@ +add_lldb_unittest(InterpreterTests + TestArgs.cpp + ) + +target_link_libraries(InterpreterTests + ${PYTHON_LIBRARY} + ) diff --git a/unittests/Interpreter/TestArgs.cpp b/unittests/Interpreter/TestArgs.cpp new file mode 100644 index 000000000000..e2cf1c4d4a0a --- /dev/null +++ b/unittests/Interpreter/TestArgs.cpp @@ -0,0 +1,70 @@ +//===-- ArgsTest.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Interpreter/Args.h" + +using namespace lldb_private; + +TEST(ArgsTest, TestSingleArg) +{ + Args args; + args.SetCommandString("arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); +} + +TEST(ArgsTest, TestSingleQuotedArgWithSpace) +{ + Args args; + args.SetCommandString("\"arg with space\""); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); +} + +TEST(ArgsTest, TestSingleArgWithQuotedSpace) +{ + Args args; + args.SetCommandString("arg\\ with\\ space"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space"); +} + +TEST(ArgsTest, TestMultipleArgs) +{ + Args args; + args.SetCommandString("this has multiple args"); + EXPECT_EQ(4u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "this"); + EXPECT_STREQ(args.GetArgumentAtIndex(1), "has"); + EXPECT_STREQ(args.GetArgumentAtIndex(2), "multiple"); + EXPECT_STREQ(args.GetArgumentAtIndex(3), "args"); +} + +TEST(ArgsTest, TestOverwriteArgs) +{ + Args args; + args.SetCommandString("this has multiple args"); + EXPECT_EQ(4u, args.GetArgumentCount()); + args.SetCommandString("arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg"); +} + +TEST(ArgsTest, TestAppendArg) +{ + Args args; + args.SetCommandString("first_arg"); + EXPECT_EQ(1u, args.GetArgumentCount()); + args.AppendArgument("second_arg"); + EXPECT_EQ(2u, args.GetArgumentCount()); + EXPECT_STREQ(args.GetArgumentAtIndex(0), "first_arg"); + EXPECT_STREQ(args.GetArgumentAtIndex(1), "second_arg"); +} diff --git a/unittests/ScriptInterpreter/CMakeLists.txt b/unittests/ScriptInterpreter/CMakeLists.txt new file mode 100644 index 000000000000..667e7a7da323 --- /dev/null +++ b/unittests/ScriptInterpreter/CMakeLists.txt @@ -0,0 +1,3 @@ +if (NOT LLDB_DISABLE_PYTHON) + add_subdirectory(Python) +endif() diff --git a/unittests/ScriptInterpreter/Python/CMakeLists.txt b/unittests/ScriptInterpreter/Python/CMakeLists.txt new file mode 100644 index 000000000000..f011200e6475 --- /dev/null +++ b/unittests/ScriptInterpreter/Python/CMakeLists.txt @@ -0,0 +1,8 @@ +add_lldb_unittest(ScriptInterpreterPythonTests + PythonDataObjectsTests.cpp + PythonExceptionStateTests.cpp + PythonTestSuite.cpp + ) + + target_link_libraries(ScriptInterpreterPythonTests lldbPluginScriptInterpreterPython ${PYTHON_LIBRARY}) + \ No newline at end of file diff --git a/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp b/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp new file mode 100644 index 000000000000..5c6925179c6a --- /dev/null +++ b/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp @@ -0,0 +1,564 @@ +//===-- PythonDataObjectsTests.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Host/File.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h" +#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" + +#include "PythonTestSuite.h" + +using namespace lldb_private; + +class PythonDataObjectsTest : public PythonTestSuite +{ + public: + void + SetUp() override + { + PythonTestSuite::SetUp(); + + PythonString sys_module("sys"); + m_sys_module.Reset(PyRefType::Owned, PyImport_Import(sys_module.get())); + m_main_module = PythonModule::MainModule(); + m_builtins_module = PythonModule::BuiltinsModule(); + } + + void + TearDown() override + { + m_sys_module.Reset(); + m_main_module.Reset(); + m_builtins_module.Reset(); + + PythonTestSuite::TearDown(); + } + + protected: + PythonModule m_sys_module; + PythonModule m_main_module; + PythonModule m_builtins_module; +}; + +TEST_F(PythonDataObjectsTest, TestOwnedReferences) +{ + // After creating a new object, the refcount should be >= 1 + PyObject *obj = PyLong_FromLong(3); + Py_ssize_t original_refcnt = obj->ob_refcnt; + EXPECT_LE(1, original_refcnt); + + // If we take an owned reference, the refcount should be the same + PythonObject owned_long(PyRefType::Owned, obj); + EXPECT_EQ(original_refcnt, owned_long.get()->ob_refcnt); + + // Take another reference and verify that the refcount increases by 1 + PythonObject strong_ref(owned_long); + EXPECT_EQ(original_refcnt + 1, strong_ref.get()->ob_refcnt); + + // If we reset the first one, the refcount should be the original value. + owned_long.Reset(); + EXPECT_EQ(original_refcnt, strong_ref.get()->ob_refcnt); +} + +TEST_F(PythonDataObjectsTest, TestResetting) +{ + PythonDictionary dict(PyInitialValue::Empty); + + PyObject *new_dict = PyDict_New(); + dict.Reset(PyRefType::Owned, new_dict); + EXPECT_EQ(new_dict, dict.get()); + + dict.Reset(PyRefType::Owned, nullptr); + EXPECT_EQ(nullptr, dict.get()); + + dict.Reset(PyRefType::Owned, PyDict_New()); + EXPECT_NE(nullptr, dict.get()); + dict.Reset(); + EXPECT_EQ(nullptr, dict.get()); +} + +TEST_F(PythonDataObjectsTest, TestBorrowedReferences) +{ + PythonInteger long_value(PyRefType::Owned, PyLong_FromLong(3)); + Py_ssize_t original_refcnt = long_value.get()->ob_refcnt; + EXPECT_LE(1, original_refcnt); + + PythonInteger borrowed_long(PyRefType::Borrowed, long_value.get()); + EXPECT_EQ(original_refcnt + 1, borrowed_long.get()->ob_refcnt); +} + +TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionNoDot) +{ + PythonObject sys_module = m_main_module.ResolveName("sys"); + EXPECT_EQ(m_sys_module.get(), sys_module.get()); + EXPECT_TRUE(sys_module.IsAllocated()); + EXPECT_TRUE(PythonModule::Check(sys_module.get())); +} + +TEST_F(PythonDataObjectsTest, TestModuleNameResolutionNoDot) +{ + PythonObject sys_path = m_sys_module.ResolveName("path"); + PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); + EXPECT_TRUE(sys_path.IsAllocated()); + EXPECT_TRUE(sys_version_info.IsAllocated()); + + EXPECT_TRUE(PythonList::Check(sys_path.get())); +} + +TEST_F(PythonDataObjectsTest, TestTypeNameResolutionNoDot) +{ + PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); + + PythonObject version_info_type(PyRefType::Owned, PyObject_Type(sys_version_info.get())); + EXPECT_TRUE(version_info_type.IsAllocated()); + PythonObject major_version_field = version_info_type.ResolveName("major"); + EXPECT_TRUE(major_version_field.IsAllocated()); +} + +TEST_F(PythonDataObjectsTest, TestInstanceNameResolutionNoDot) +{ + PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); + PythonObject major_version_field = sys_version_info.ResolveName("major"); + PythonObject minor_version_field = sys_version_info.ResolveName("minor"); + + EXPECT_TRUE(major_version_field.IsAllocated()); + EXPECT_TRUE(minor_version_field.IsAllocated()); + + PythonInteger major_version_value = major_version_field.AsType(); + PythonInteger minor_version_value = minor_version_field.AsType(); + + EXPECT_EQ(PY_MAJOR_VERSION, major_version_value.GetInteger()); + EXPECT_EQ(PY_MINOR_VERSION, minor_version_value.GetInteger()); +} + +TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionWithDot) +{ + PythonObject sys_path = m_main_module.ResolveName("sys.path"); + EXPECT_TRUE(sys_path.IsAllocated()); + EXPECT_TRUE(PythonList::Check(sys_path.get())); + + PythonInteger version_major = m_main_module.ResolveName( + "sys.version_info.major").AsType(); + PythonInteger version_minor = m_main_module.ResolveName( + "sys.version_info.minor").AsType(); + EXPECT_TRUE(version_major.IsAllocated()); + EXPECT_TRUE(version_minor.IsAllocated()); + EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger()); + EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger()); +} + +TEST_F(PythonDataObjectsTest, TestDictionaryResolutionWithDot) +{ + // Make up a custom dictionary with "sys" pointing to the `sys` module. + PythonDictionary dict(PyInitialValue::Empty); + dict.SetItemForKey(PythonString("sys"), m_sys_module); + + // Now use that dictionary to resolve `sys.version_info.major` + PythonInteger version_major = PythonObject::ResolveNameWithDictionary( + "sys.version_info.major", dict).AsType(); + PythonInteger version_minor = PythonObject::ResolveNameWithDictionary( + "sys.version_info.minor", dict).AsType(); + EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger()); + EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger()); +} + +TEST_F(PythonDataObjectsTest, TestPythonInteger) +{ +// Test that integers behave correctly when wrapped by a PythonInteger. + +#if PY_MAJOR_VERSION < 3 + // Verify that `PythonInt` works correctly when given a PyInt object. + // Note that PyInt doesn't exist in Python 3.x, so this is only for 2.x + PyObject *py_int = PyInt_FromLong(12); + EXPECT_TRUE(PythonInteger::Check(py_int)); + PythonInteger python_int(PyRefType::Owned, py_int); + + EXPECT_EQ(PyObjectType::Integer, python_int.GetObjectType()); + EXPECT_EQ(12, python_int.GetInteger()); +#endif + + // Verify that `PythonInteger` works correctly when given a PyLong object. + PyObject *py_long = PyLong_FromLong(12); + EXPECT_TRUE(PythonInteger::Check(py_long)); + PythonInteger python_long(PyRefType::Owned, py_long); + EXPECT_EQ(PyObjectType::Integer, python_long.GetObjectType()); + + // Verify that you can reset the value and that it is reflected properly. + python_long.SetInteger(40); + EXPECT_EQ(40, python_long.GetInteger()); + + // Test that creating a `PythonInteger` object works correctly with the + // int constructor. + PythonInteger constructed_int(7); + EXPECT_EQ(7, constructed_int.GetInteger()); +} + +TEST_F(PythonDataObjectsTest, TestPythonString) +{ + // Test that strings behave correctly when wrapped by a PythonString. + + static const char *test_string = "PythonDataObjectsTest::TestPythonString1"; + static const char *test_string2 = "PythonDataObjectsTest::TestPythonString2"; + static const char *test_string3 = "PythonDataObjectsTest::TestPythonString3"; + +#if PY_MAJOR_VERSION < 3 + // Verify that `PythonString` works correctly when given a PyString object. + // Note that PyString doesn't exist in Python 3.x, so this is only for 2.x + PyObject *py_string = PyString_FromString(test_string); + EXPECT_TRUE(PythonString::Check(py_string)); + PythonString python_string(PyRefType::Owned, py_string); + + EXPECT_EQ(PyObjectType::String, python_string.GetObjectType()); + EXPECT_STREQ(test_string, python_string.GetString().data()); +#else + // Verify that `PythonString` works correctly when given a PyUnicode object. + PyObject *py_unicode = PyUnicode_FromString(test_string); + EXPECT_TRUE(PythonString::Check(py_unicode)); + PythonString python_unicode(PyRefType::Owned, py_unicode); + EXPECT_EQ(PyObjectType::String, python_unicode.GetObjectType()); + EXPECT_STREQ(test_string, python_unicode.GetString().data()); +#endif + + // Test that creating a `PythonString` object works correctly with the + // string constructor + PythonString constructed_string(test_string3); + EXPECT_STREQ(test_string3, constructed_string.GetString().str().c_str()); +} + +TEST_F(PythonDataObjectsTest, TestPythonStringToStr) +{ + const char *c_str = "PythonDataObjectsTest::TestPythonStringToStr"; + + PythonString str(c_str); + EXPECT_STREQ(c_str, str.GetString().str().c_str()); + + PythonString str_str = str.Str(); + EXPECT_STREQ(c_str, str_str.GetString().str().c_str()); +} + +TEST_F(PythonDataObjectsTest, TestPythonIntegerToStr) +{ +} + +TEST_F(PythonDataObjectsTest, TestPythonIntegerToStructuredInteger) +{ + PythonInteger integer(7); + auto int_sp = integer.CreateStructuredInteger(); + EXPECT_EQ(7, int_sp->GetValue()); +} + +TEST_F(PythonDataObjectsTest, TestPythonStringToStructuredString) +{ + static const char *test_string = "PythonDataObjectsTest::TestPythonStringToStructuredString"; + PythonString constructed_string(test_string); + auto string_sp = constructed_string.CreateStructuredString(); + EXPECT_STREQ(test_string, string_sp->GetStringValue().c_str()); +} + +TEST_F(PythonDataObjectsTest, TestPythonListValueEquality) +{ + // Test that a list which is built through the native + // Python API behaves correctly when wrapped by a PythonList. + static const int list_size = 2; + static const long long_value0 = 5; + static const char *const string_value1 = "String Index 1"; + + PyObject *py_list = PyList_New(2); + EXPECT_TRUE(PythonList::Check(py_list)); + PythonList list(PyRefType::Owned, py_list); + + PythonObject list_items[list_size]; + list_items[0].Reset(PythonInteger(long_value0)); + list_items[1].Reset(PythonString(string_value1)); + + for (int i = 0; i < list_size; ++i) + list.SetItemAtIndex(i, list_items[i]); + + EXPECT_EQ(list_size, list.GetSize()); + EXPECT_EQ(PyObjectType::List, list.GetObjectType()); + + // Verify that the values match + PythonObject chk_value1 = list.GetItemAtIndex(0); + PythonObject chk_value2 = list.GetItemAtIndex(1); + EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); + EXPECT_TRUE(PythonString::Check(chk_value2.get())); + + PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); + PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); + + EXPECT_EQ(long_value0, chk_int.GetInteger()); + EXPECT_STREQ(string_value1, chk_str.GetString().str().c_str()); +} + +TEST_F(PythonDataObjectsTest, TestPythonListManipulation) +{ + // Test that manipulation of a PythonList behaves correctly when + // wrapped by a PythonDictionary. + + static const long long_value0 = 5; + static const char *const string_value1 = "String Index 1"; + + PythonList list(PyInitialValue::Empty); + PythonInteger integer(long_value0); + PythonString string(string_value1); + + list.AppendItem(integer); + list.AppendItem(string); + EXPECT_EQ(2, list.GetSize()); + + // Verify that the values match + PythonObject chk_value1 = list.GetItemAtIndex(0); + PythonObject chk_value2 = list.GetItemAtIndex(1); + EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); + EXPECT_TRUE(PythonString::Check(chk_value2.get())); + + PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); + PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); + + EXPECT_EQ(long_value0, chk_int.GetInteger()); + EXPECT_STREQ(string_value1, chk_str.GetString().str().c_str()); +} + +TEST_F(PythonDataObjectsTest, TestPythonListToStructuredList) +{ + static const long long_value0 = 5; + static const char *const string_value1 = "String Index 1"; + + PythonList list(PyInitialValue::Empty); + list.AppendItem(PythonInteger(long_value0)); + list.AppendItem(PythonString(string_value1)); + + auto array_sp = list.CreateStructuredArray(); + EXPECT_EQ(StructuredData::Type::eTypeInteger, array_sp->GetItemAtIndex(0)->GetType()); + EXPECT_EQ(StructuredData::Type::eTypeString, array_sp->GetItemAtIndex(1)->GetType()); + + auto int_sp = array_sp->GetItemAtIndex(0)->GetAsInteger(); + auto string_sp = array_sp->GetItemAtIndex(1)->GetAsString(); + + EXPECT_EQ(long_value0, int_sp->GetValue()); + EXPECT_STREQ(string_value1, string_sp->GetValue().c_str()); +} + +TEST_F(PythonDataObjectsTest, TestPythonTupleSize) +{ + PythonTuple tuple(PyInitialValue::Empty); + EXPECT_EQ(0, tuple.GetSize()); + + tuple = PythonTuple(3); + EXPECT_EQ(3, tuple.GetSize()); +} + +TEST_F(PythonDataObjectsTest, TestPythonTupleValues) +{ + PythonTuple tuple(3); + + PythonInteger int_value(1); + PythonString string_value("Test"); + PythonObject none_value(PyRefType::Borrowed, Py_None); + + tuple.SetItemAtIndex(0, int_value); + tuple.SetItemAtIndex(1, string_value); + tuple.SetItemAtIndex(2, none_value); + + EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); +} + +TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList) +{ + PythonInteger int_value(1); + PythonString string_value("Test"); + PythonObject none_value(PyRefType::Borrowed, Py_None); + PythonTuple tuple{ int_value, string_value, none_value }; + EXPECT_EQ(3, tuple.GetSize()); + + EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); +} + +TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList2) +{ + PythonInteger int_value(1); + PythonString string_value("Test"); + PythonObject none_value(PyRefType::Borrowed, Py_None); + + PythonTuple tuple{ int_value.get(), string_value.get(), none_value.get() }; + EXPECT_EQ(3, tuple.GetSize()); + + EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); + EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); +} + +TEST_F(PythonDataObjectsTest, TestPythonTupleToStructuredList) +{ + PythonInteger int_value(1); + PythonString string_value("Test"); + + PythonTuple tuple{ int_value.get(), string_value.get() }; + + auto array_sp = tuple.CreateStructuredArray(); + EXPECT_EQ(tuple.GetSize(), array_sp->GetSize()); + EXPECT_EQ(StructuredData::Type::eTypeInteger, array_sp->GetItemAtIndex(0)->GetType()); + EXPECT_EQ(StructuredData::Type::eTypeString, array_sp->GetItemAtIndex(1)->GetType()); +} + +TEST_F(PythonDataObjectsTest, TestPythonDictionaryValueEquality) +{ + // Test that a dictionary which is built through the native + // Python API behaves correctly when wrapped by a PythonDictionary. + static const int dict_entries = 2; + const char *key_0 = "Key 0"; + int key_1 = 1; + const int value_0 = 0; + const char *value_1 = "Value 1"; + + PythonObject py_keys[dict_entries]; + PythonObject py_values[dict_entries]; + + py_keys[0].Reset(PythonString(key_0)); + py_keys[1].Reset(PythonInteger(key_1)); + py_values[0].Reset(PythonInteger(value_0)); + py_values[1].Reset(PythonString(value_1)); + + PyObject *py_dict = PyDict_New(); + EXPECT_TRUE(PythonDictionary::Check(py_dict)); + PythonDictionary dict(PyRefType::Owned, py_dict); + + for (int i = 0; i < dict_entries; ++i) + PyDict_SetItem(py_dict, py_keys[i].get(), py_values[i].get()); + EXPECT_EQ(dict.GetSize(), dict_entries); + EXPECT_EQ(PyObjectType::Dictionary, dict.GetObjectType()); + + // Verify that the values match + PythonObject chk_value1 = dict.GetItemForKey(py_keys[0]); + PythonObject chk_value2 = dict.GetItemForKey(py_keys[1]); + EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); + EXPECT_TRUE(PythonString::Check(chk_value2.get())); + + PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); + PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); + + EXPECT_EQ(value_0, chk_int.GetInteger()); + EXPECT_STREQ(value_1, chk_str.GetString().str().c_str()); +} + +TEST_F(PythonDataObjectsTest, TestPythonDictionaryManipulation) +{ + // Test that manipulation of a dictionary behaves correctly when wrapped + // by a PythonDictionary. + static const int dict_entries = 2; + + const char *const key_0 = "Key 0"; + const char *const key_1 = "Key 1"; + const long value_0 = 1; + const char *const value_1 = "Value 1"; + + PythonString keys[dict_entries]; + PythonObject values[dict_entries]; + + keys[0].Reset(PythonString(key_0)); + keys[1].Reset(PythonString(key_1)); + values[0].Reset(PythonInteger(value_0)); + values[1].Reset(PythonString(value_1)); + + PythonDictionary dict(PyInitialValue::Empty); + for (int i = 0; i < 2; ++i) + dict.SetItemForKey(keys[i], values[i]); + + EXPECT_EQ(dict_entries, dict.GetSize()); + + // Verify that the keys and values match + PythonObject chk_value1 = dict.GetItemForKey(keys[0]); + PythonObject chk_value2 = dict.GetItemForKey(keys[1]); + EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); + EXPECT_TRUE(PythonString::Check(chk_value2.get())); + + PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); + PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); + + EXPECT_EQ(value_0, chk_int.GetInteger()); + EXPECT_STREQ(value_1, chk_str.GetString().str().c_str()); +} + +TEST_F(PythonDataObjectsTest, TestPythonDictionaryToStructuredDictionary) +{ + static const char *const string_key0 = "String Key 0"; + static const char *const string_key1 = "String Key 1"; + + static const char *const string_value0 = "String Value 0"; + static const long int_value1 = 7; + + PythonDictionary dict(PyInitialValue::Empty); + dict.SetItemForKey(PythonString(string_key0), PythonString(string_value0)); + dict.SetItemForKey(PythonString(string_key1), PythonInteger(int_value1)); + + auto dict_sp = dict.CreateStructuredDictionary(); + EXPECT_EQ(2, dict_sp->GetSize()); + + EXPECT_TRUE(dict_sp->HasKey(string_key0)); + EXPECT_TRUE(dict_sp->HasKey(string_key1)); + + auto string_sp = dict_sp->GetValueForKey(string_key0)->GetAsString(); + auto int_sp = dict_sp->GetValueForKey(string_key1)->GetAsInteger(); + + EXPECT_STREQ(string_value0, string_sp->GetValue().c_str()); + EXPECT_EQ(int_value1, int_sp->GetValue()); +} + +TEST_F(PythonDataObjectsTest, TestPythonCallableCheck) +{ + PythonObject sys_exc_info = m_sys_module.ResolveName("exc_info"); + PythonObject none(PyRefType::Borrowed, Py_None); + + EXPECT_TRUE(PythonCallable::Check(sys_exc_info.get())); + EXPECT_FALSE(PythonCallable::Check(none.get())); +} + +TEST_F(PythonDataObjectsTest, TestPythonCallableInvoke) +{ + auto list = m_builtins_module.ResolveName("list").AsType(); + PythonInteger one(1); + PythonString two("two"); + PythonTuple three = { one, two }; + + PythonTuple tuple_to_convert = { one, two, three }; + PythonObject result = list({ tuple_to_convert }); + + EXPECT_TRUE(PythonList::Check(result.get())); + auto list_result = result.AsType(); + EXPECT_EQ(3, list_result.GetSize()); + EXPECT_EQ(one.get(), list_result.GetItemAtIndex(0).get()); + EXPECT_EQ(two.get(), list_result.GetItemAtIndex(1).get()); + EXPECT_EQ(three.get(), list_result.GetItemAtIndex(2).get()); +} + +TEST_F(PythonDataObjectsTest, TestPythonFile) +{ + File file(FileSystem::DEV_NULL, File::eOpenOptionRead); + PythonFile py_file(file, "r"); + EXPECT_TRUE(PythonFile::Check(py_file.get())); +} + +TEST_F(PythonDataObjectsTest, TestObjectAttributes) +{ + PythonInteger py_int(42); + EXPECT_TRUE(py_int.HasAttribute("numerator")); + EXPECT_FALSE(py_int.HasAttribute("this_should_not_exist")); + + PythonInteger numerator_attr = py_int.GetAttributeValue("numerator").AsType(); + EXPECT_TRUE(numerator_attr.IsAllocated()); + EXPECT_EQ(42, numerator_attr.GetInteger()); +} \ No newline at end of file diff --git a/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp b/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp new file mode 100644 index 000000000000..a0a6f986cef6 --- /dev/null +++ b/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp @@ -0,0 +1,174 @@ +//===-- PythonExceptionStateTest.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h" +#include "Plugins/ScriptInterpreter/Python/PythonExceptionState.h" +#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" + +#include "PythonTestSuite.h" + +using namespace lldb_private; + +class PythonExceptionStateTest : public PythonTestSuite +{ + public: + protected: + void + RaiseException() + { + PyErr_SetString(PyExc_RuntimeError, "PythonExceptionStateTest test error"); + } +}; + +TEST_F(PythonExceptionStateTest, TestExceptionStateChecking) +{ + PyErr_Clear(); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + RaiseException(); + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); +} + +TEST_F(PythonExceptionStateTest, TestAcquisitionSemantics) +{ + PyErr_Clear(); + PythonExceptionState no_error(false); + EXPECT_FALSE(no_error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); + RaiseException(); + PythonExceptionState error(false); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error.Discard(); + + PyErr_Clear(); + RaiseException(); + error.Acquire(false); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); +} + +TEST_F(PythonExceptionStateTest, TestDiscardSemantics) +{ + PyErr_Clear(); + + // Test that discarding an exception does not restore the exception + // state even when auto-restore==true is set + RaiseException(); + PythonExceptionState error(true); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + error.Discard(); + EXPECT_FALSE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); +} + +TEST_F(PythonExceptionStateTest, TestResetSemantics) +{ + PyErr_Clear(); + + // Resetting when auto-restore is true should restore. + RaiseException(); + PythonExceptionState error(true); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error.Reset(); + EXPECT_FALSE(error.IsError()); + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); + + // Resetting when auto-restore is false should discard. + RaiseException(); + PythonExceptionState error2(false); + EXPECT_TRUE(error2.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + error2.Reset(); + EXPECT_FALSE(error2.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); +} + +TEST_F(PythonExceptionStateTest, TestManualRestoreSemantics) +{ + PyErr_Clear(); + RaiseException(); + PythonExceptionState error(false); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + error.Restore(); + EXPECT_FALSE(error.IsError()); + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); +} + +TEST_F(PythonExceptionStateTest, TestAutoRestoreSemantics) +{ + PyErr_Clear(); + // Test that using the auto-restore flag correctly restores the exception + // state on destruction, and not using the auto-restore flag correctly + // does NOT restore the state on destruction. + { + RaiseException(); + PythonExceptionState error(false); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + } + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); + { + RaiseException(); + PythonExceptionState error(true); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + } + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); +} + +TEST_F(PythonExceptionStateTest, TestAutoRestoreChanged) +{ + // Test that if we re-acquire with different auto-restore semantics, + // that the new semantics are respected. + PyErr_Clear(); + + RaiseException(); + PythonExceptionState error(false); + EXPECT_TRUE(error.IsError()); + + error.Reset(); + EXPECT_FALSE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + RaiseException(); + error.Acquire(true); + EXPECT_TRUE(error.IsError()); + EXPECT_FALSE(PythonExceptionState::HasErrorOccurred()); + + error.Reset(); + EXPECT_FALSE(error.IsError()); + EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); + + PyErr_Clear(); +} \ No newline at end of file diff --git a/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp new file mode 100644 index 000000000000..3d1727dc1c25 --- /dev/null +++ b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -0,0 +1,42 @@ +//===-- PythonTestSuite.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Host/HostInfo.h" +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" + +#include "PythonTestSuite.h" + +using namespace lldb_private; + +void +PythonTestSuite::SetUp() +{ + HostInfoBase::Initialize(); + // ScriptInterpreterPython::Initialize() depends on HostInfo being + // initializedso it can compute the python directory etc. + ScriptInterpreterPython::Initialize(); + + // Although we don't care about concurrency for the purposes of running + // this test suite, Python requires the GIL to be locked even for + // deallocating memory, which can happen when you call Py_DECREF or + // Py_INCREF. So acquire the GIL for the entire duration of this + // test suite. + m_gil_state = PyGILState_Ensure(); +} + +void +PythonTestSuite::TearDown() +{ + PyGILState_Release(m_gil_state); + + ScriptInterpreterPython::Terminate(); +} diff --git a/unittests/ScriptInterpreter/Python/PythonTestSuite.h b/unittests/ScriptInterpreter/Python/PythonTestSuite.h new file mode 100644 index 000000000000..461fc1d5676c --- /dev/null +++ b/unittests/ScriptInterpreter/Python/PythonTestSuite.h @@ -0,0 +1,26 @@ +//===-- PythonTestSuite.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +using namespace lldb_private; + +class PythonTestSuite : public testing::Test +{ +public: + void + SetUp() override; + + void + TearDown() override; + +private: + PyGILState_STATE m_gil_state; +}; + diff --git a/unittests/Utility/CMakeLists.txt b/unittests/Utility/CMakeLists.txt new file mode 100644 index 000000000000..30936acce9dc --- /dev/null +++ b/unittests/Utility/CMakeLists.txt @@ -0,0 +1,5 @@ +add_lldb_unittest(UtilityTests + StringExtractorTest.cpp + TaskPoolTest.cpp + UriParserTest.cpp + ) diff --git a/unittests/Utility/StringExtractorTest.cpp b/unittests/Utility/StringExtractorTest.cpp new file mode 100644 index 000000000000..0eb6d1bda8bb --- /dev/null +++ b/unittests/Utility/StringExtractorTest.cpp @@ -0,0 +1,406 @@ +#include +#include "gtest/gtest.h" + +#include "lldb/Utility/StringExtractor.h" + +namespace +{ + class StringExtractorTest: public ::testing::Test + { + }; +} + +TEST_F (StringExtractorTest, InitEmpty) +{ + const char kEmptyString[] = ""; + StringExtractor ex (kEmptyString); + + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (0u, ex.GetFilePos()); + ASSERT_STREQ (kEmptyString, ex.GetStringRef().c_str()); + ASSERT_EQ (true, ex.Empty()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, InitMisc) +{ + const char kInitMiscString[] = "Hello, StringExtractor!"; + StringExtractor ex (kInitMiscString); + + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (0u, ex.GetFilePos()); + ASSERT_STREQ (kInitMiscString, ex.GetStringRef().c_str()); + ASSERT_EQ (false, ex.Empty()); + ASSERT_EQ (sizeof(kInitMiscString)-1, ex.GetBytesLeft()); + ASSERT_EQ (kInitMiscString[0], *ex.Peek()); +} + +TEST_F (StringExtractorTest, DecodeHexU8_Underflow) +{ + const char kEmptyString[] = ""; + StringExtractor ex (kEmptyString); + + ASSERT_EQ (-1, ex.DecodeHexU8()); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (0u, ex.GetFilePos()); + ASSERT_EQ (true, ex.Empty()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, DecodeHexU8_Underflow2) +{ + const char kEmptyString[] = "1"; + StringExtractor ex (kEmptyString); + + ASSERT_EQ (-1, ex.DecodeHexU8()); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (0u, ex.GetFilePos()); + ASSERT_EQ (1u, ex.GetBytesLeft()); + ASSERT_EQ ('1', *ex.Peek()); +} + +TEST_F (StringExtractorTest, DecodeHexU8_InvalidHex) +{ + const char kInvalidHex[] = "xa"; + StringExtractor ex (kInvalidHex); + + ASSERT_EQ (-1, ex.DecodeHexU8()); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (0u, ex.GetFilePos()); + ASSERT_EQ (2u, ex.GetBytesLeft()); + ASSERT_EQ ('x', *ex.Peek()); +} + +TEST_F (StringExtractorTest, DecodeHexU8_InvalidHex2) +{ + const char kInvalidHex[] = "ax"; + StringExtractor ex (kInvalidHex); + + ASSERT_EQ (-1, ex.DecodeHexU8()); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (0u, ex.GetFilePos()); + ASSERT_EQ (2u, ex.GetBytesLeft()); + ASSERT_EQ ('a', *ex.Peek()); +} + +TEST_F (StringExtractorTest, DecodeHexU8_Exact) +{ + const char kValidHexPair[] = "12"; + StringExtractor ex (kValidHexPair); + + ASSERT_EQ (0x12, ex.DecodeHexU8()); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (2u, ex.GetFilePos()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, DecodeHexU8_Extra) +{ + const char kValidHexPair[] = "1234"; + StringExtractor ex (kValidHexPair); + + ASSERT_EQ (0x12, ex.DecodeHexU8()); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (2u, ex.GetFilePos()); + ASSERT_EQ (2u, ex.GetBytesLeft()); + ASSERT_EQ ('3', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_Underflow) +{ + const char kEmptyString[] = ""; + StringExtractor ex (kEmptyString); + + ASSERT_EQ (0xab, ex.GetHexU8(0xab)); + ASSERT_EQ (false, ex.IsGood()); + ASSERT_EQ (UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ (true, ex.Empty()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_Underflow2) +{ + const char kOneNibble[] = "1"; + StringExtractor ex (kOneNibble); + + ASSERT_EQ (0xbc, ex.GetHexU8(0xbc)); + ASSERT_EQ (false, ex.IsGood()); + ASSERT_EQ (UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_InvalidHex) +{ + const char kInvalidHex[] = "xx"; + StringExtractor ex (kInvalidHex); + + ASSERT_EQ (0xcd, ex.GetHexU8(0xcd)); + ASSERT_EQ (false, ex.IsGood()); + ASSERT_EQ (UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_Exact) +{ + const char kValidHexPair[] = "12"; + StringExtractor ex (kValidHexPair); + + ASSERT_EQ (0x12, ex.GetHexU8(0x12)); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (2u, ex.GetFilePos()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_Extra) +{ + const char kValidHexPair[] = "1234"; + StringExtractor ex (kValidHexPair); + + ASSERT_EQ (0x12, ex.GetHexU8(0x12)); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (2u, ex.GetFilePos()); + ASSERT_EQ (2u, ex.GetBytesLeft()); + ASSERT_EQ ('3', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_Underflow_NoEof) +{ + const char kEmptyString[] = ""; + StringExtractor ex (kEmptyString); + const bool kSetEofOnFail = false; + + ASSERT_EQ (0xab, ex.GetHexU8(0xab, kSetEofOnFail)); + ASSERT_EQ (false, ex.IsGood()); // this result seems inconsistent with kSetEofOnFail == false + ASSERT_EQ (UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ (true, ex.Empty()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_Underflow2_NoEof) +{ + const char kOneNibble[] = "1"; + StringExtractor ex (kOneNibble); + const bool kSetEofOnFail = false; + + ASSERT_EQ (0xbc, ex.GetHexU8(0xbc, kSetEofOnFail)); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (0u, ex.GetFilePos()); + ASSERT_EQ (1u, ex.GetBytesLeft()); + ASSERT_EQ ('1', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_InvalidHex_NoEof) +{ + const char kInvalidHex[] = "xx"; + StringExtractor ex (kInvalidHex); + const bool kSetEofOnFail = false; + + ASSERT_EQ (0xcd, ex.GetHexU8(0xcd, kSetEofOnFail)); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (0u, ex.GetFilePos()); + ASSERT_EQ (2u, ex.GetBytesLeft()); + ASSERT_EQ ('x', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_Exact_NoEof) +{ + const char kValidHexPair[] = "12"; + StringExtractor ex (kValidHexPair); + const bool kSetEofOnFail = false; + + ASSERT_EQ (0x12, ex.GetHexU8(0x12, kSetEofOnFail)); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (2u, ex.GetFilePos()); + ASSERT_EQ (0u, ex.GetBytesLeft()); + ASSERT_EQ (nullptr, ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexU8_Extra_NoEof) +{ + const char kValidHexPair[] = "1234"; + StringExtractor ex (kValidHexPair); + const bool kSetEofOnFail = false; + + ASSERT_EQ (0x12, ex.GetHexU8(0x12, kSetEofOnFail)); + ASSERT_EQ (true, ex.IsGood()); + ASSERT_EQ (2u, ex.GetFilePos()); + ASSERT_EQ (2u, ex.GetBytesLeft()); + ASSERT_EQ ('3', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexBytes) +{ + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[kValidHexPairs]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytes (dst, kValidHexPairs, 0xde)); + EXPECT_EQ(0xab,dst[0]); + EXPECT_EQ(0xcd,dst[1]); + EXPECT_EQ(0xef,dst[2]); + EXPECT_EQ(0x01,dst[3]); + EXPECT_EQ(0x23,dst[4]); + EXPECT_EQ(0x45,dst[5]); + EXPECT_EQ(0x67,dst[6]); + EXPECT_EQ(0x89,dst[7]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2*kValidHexPairs, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(4u, ex.GetBytesLeft()); + ASSERT_EQ('x', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexBytes_Underflow) +{ + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[12]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytes (dst, sizeof(dst), 0xde)); + EXPECT_EQ(0xab,dst[0]); + EXPECT_EQ(0xcd,dst[1]); + EXPECT_EQ(0xef,dst[2]); + EXPECT_EQ(0x01,dst[3]); + EXPECT_EQ(0x23,dst[4]); + EXPECT_EQ(0x45,dst[5]); + EXPECT_EQ(0x67,dst[6]); + EXPECT_EQ(0x89,dst[7]); + // these bytes should be filled with fail_fill_value 0xde + EXPECT_EQ(0xde,dst[8]); + EXPECT_EQ(0xde,dst[9]); + EXPECT_EQ(0xde,dst[10]); + EXPECT_EQ(0xde,dst[11]); + + ASSERT_EQ(false, ex.IsGood()); + ASSERT_EQ(UINT64_MAX, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(0u, ex.GetBytesLeft()); + ASSERT_EQ(0, ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexBytes_Partial) +{ + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kReadBytes = 4; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[12]; + memset(dst, 0xab, sizeof(dst)); + ASSERT_EQ(kReadBytes, ex.GetHexBytes (dst, kReadBytes, 0xde)); + EXPECT_EQ(0xab,dst[0]); + EXPECT_EQ(0xcd,dst[1]); + EXPECT_EQ(0xef,dst[2]); + EXPECT_EQ(0x01,dst[3]); + // these bytes should be unchanged + EXPECT_EQ(0xab,dst[4]); + EXPECT_EQ(0xab,dst[5]); + EXPECT_EQ(0xab,dst[6]); + EXPECT_EQ(0xab,dst[7]); + EXPECT_EQ(0xab,dst[8]); + EXPECT_EQ(0xab,dst[9]); + EXPECT_EQ(0xab,dst[10]); + EXPECT_EQ(0xab,dst[11]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(kReadBytes*2, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(12u, ex.GetBytesLeft()); + ASSERT_EQ('2', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexBytesAvail) +{ + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[kValidHexPairs]; + ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail (dst, kValidHexPairs)); + EXPECT_EQ(0xab,dst[0]); + EXPECT_EQ(0xcd,dst[1]); + EXPECT_EQ(0xef,dst[2]); + EXPECT_EQ(0x01,dst[3]); + EXPECT_EQ(0x23,dst[4]); + EXPECT_EQ(0x45,dst[5]); + EXPECT_EQ(0x67,dst[6]); + EXPECT_EQ(0x89,dst[7]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(2*kValidHexPairs, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(4u, ex.GetBytesLeft()); + ASSERT_EQ('x', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexBytesAvail_Underflow) +{ + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kValidHexPairs = 8; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[12]; + memset(dst, 0xef, sizeof(dst)); + ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail (dst, sizeof(dst))); + EXPECT_EQ(0xab,dst[0]); + EXPECT_EQ(0xcd,dst[1]); + EXPECT_EQ(0xef,dst[2]); + EXPECT_EQ(0x01,dst[3]); + EXPECT_EQ(0x23,dst[4]); + EXPECT_EQ(0x45,dst[5]); + EXPECT_EQ(0x67,dst[6]); + EXPECT_EQ(0x89,dst[7]); + // these bytes should be unchanged + EXPECT_EQ(0xef,dst[8]); + EXPECT_EQ(0xef,dst[9]); + EXPECT_EQ(0xef,dst[10]); + EXPECT_EQ(0xef,dst[11]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(kValidHexPairs*2, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(4u, ex.GetBytesLeft()); + ASSERT_EQ('x', *ex.Peek()); +} + +TEST_F (StringExtractorTest, GetHexBytesAvail_Partial) +{ + const char kHexEncodedBytes[] = "abcdef0123456789xyzw"; + const size_t kReadBytes = 4; + StringExtractor ex(kHexEncodedBytes); + + uint8_t dst[12]; + memset(dst, 0xab, sizeof(dst)); + ASSERT_EQ(kReadBytes, ex.GetHexBytesAvail (dst, kReadBytes)); + EXPECT_EQ(0xab,dst[0]); + EXPECT_EQ(0xcd,dst[1]); + EXPECT_EQ(0xef,dst[2]); + EXPECT_EQ(0x01,dst[3]); + // these bytes should be unchanged + EXPECT_EQ(0xab,dst[4]); + EXPECT_EQ(0xab,dst[5]); + EXPECT_EQ(0xab,dst[6]); + EXPECT_EQ(0xab,dst[7]); + EXPECT_EQ(0xab,dst[8]); + EXPECT_EQ(0xab,dst[9]); + EXPECT_EQ(0xab,dst[10]); + EXPECT_EQ(0xab,dst[11]); + + ASSERT_EQ(true, ex.IsGood()); + ASSERT_EQ(kReadBytes*2, ex.GetFilePos()); + ASSERT_EQ(false, ex.Empty()); + ASSERT_EQ(12u, ex.GetBytesLeft()); + ASSERT_EQ('2', *ex.Peek()); +} + + diff --git a/unittests/Utility/TaskPoolTest.cpp b/unittests/Utility/TaskPoolTest.cpp new file mode 100644 index 000000000000..24431e2c7896 --- /dev/null +++ b/unittests/Utility/TaskPoolTest.cpp @@ -0,0 +1,62 @@ +#include "gtest/gtest.h" + +#include "lldb/Utility/TaskPool.h" + +TEST (TaskPoolTest, AddTask) +{ + auto fn = [](int x) { return x * x + 1; }; + + auto f1 = TaskPool::AddTask(fn, 1); + auto f2 = TaskPool::AddTask(fn, 2); + auto f3 = TaskPool::AddTask(fn, 3); + auto f4 = TaskPool::AddTask(fn, 4); + + ASSERT_EQ (10, f3.get()); + ASSERT_EQ ( 2, f1.get()); + ASSERT_EQ (17, f4.get()); + ASSERT_EQ ( 5, f2.get()); +} + +TEST (TaskPoolTest, RunTasks) +{ + std::vector r(4); + + auto fn = [](int x, int& y) { y = x * x + 1; }; + + TaskPool::RunTasks( + [fn, &r]() { fn(1, r[0]); }, + [fn, &r]() { fn(2, r[1]); }, + [fn, &r]() { fn(3, r[2]); }, + [fn, &r]() { fn(4, r[3]); } + ); + + ASSERT_EQ ( 2, r[0]); + ASSERT_EQ ( 5, r[1]); + ASSERT_EQ (10, r[2]); + ASSERT_EQ (17, r[3]); +} + +TEST (TaskPoolTest, TaskRunner) +{ + auto fn = [](int x) { return std::make_pair(x, x * x); }; + + TaskRunner> tr; + tr.AddTask(fn, 1); + tr.AddTask(fn, 2); + tr.AddTask(fn, 3); + tr.AddTask(fn, 4); + + int count = 0; + while (true) + { + auto f = tr.WaitForNextCompletedTask(); + if (!f.valid()) + break; + + ++count; + std::pair v = f.get(); + ASSERT_EQ (v.first * v.first, v.second); + } + + ASSERT_EQ(4, count); +} diff --git a/unittests/Utility/UriParserTest.cpp b/unittests/Utility/UriParserTest.cpp new file mode 100644 index 000000000000..fe0a6a70f212 --- /dev/null +++ b/unittests/Utility/UriParserTest.cpp @@ -0,0 +1,159 @@ +#include "gtest/gtest.h" +#include "Utility/UriParser.h" + +namespace +{ + class UriParserTest: public ::testing::Test + { + }; +} + +// result strings (scheme/hostname/port/path) passed into UriParser::Parse +// are initialized to kAsdf so we can verify that they are unmodified if the +// URI is invalid +static const char* kAsdf = "asdf"; + +class UriTestCase +{ +public: + UriTestCase(const char* uri, const char* scheme, const char* hostname, int port, const char* path) : + m_uri(uri), + m_result(true), + m_scheme(scheme), + m_hostname(hostname), + m_port(port), + m_path(path) + { + } + + UriTestCase(const char* uri) : + m_uri(uri), + m_result(false), + m_scheme(kAsdf), + m_hostname(kAsdf), + m_port(1138), + m_path(kAsdf) + { + } + + const char* m_uri; + bool m_result; + const char* m_scheme; + const char* m_hostname; + int m_port; + const char* m_path; +}; + +#define VALIDATE \ + std::string scheme(kAsdf); \ + std::string hostname(kAsdf); \ + int port(1138); \ + std::string path(kAsdf); \ + EXPECT_EQ (testCase.m_result, UriParser::Parse(testCase.m_uri, scheme, hostname, port, path)); \ + EXPECT_STREQ (testCase.m_scheme, scheme.c_str()); \ + EXPECT_STREQ (testCase.m_hostname, hostname.c_str()); \ + EXPECT_EQ (testCase.m_port, port); \ + EXPECT_STREQ (testCase.m_path, path.c_str()); + +TEST_F (UriParserTest, Minimal) +{ + const UriTestCase testCase("x://y", "x", "y", -1, "/"); + VALIDATE +} + +TEST_F (UriParserTest, MinimalPort) +{ + const UriTestCase testCase("x://y:1", "x", "y", 1, "/"); + VALIDATE +} + +TEST_F (UriParserTest, MinimalPath) +{ + const UriTestCase testCase("x://y/", "x", "y", -1, "/"); + VALIDATE +} + +TEST_F (UriParserTest, MinimalPortPath) +{ + const UriTestCase testCase("x://y:1/", "x", "y", 1, "/"); + VALIDATE +} + +TEST_F (UriParserTest, LongPath) +{ + const UriTestCase testCase("x://y/abc/def/xyz", "x", "y", -1, "/abc/def/xyz"); + VALIDATE +} + +TEST_F (UriParserTest, TypicalPortPath) +{ + const UriTestCase testCase("connect://192.168.100.132:5432/", "connect", "192.168.100.132", 5432, "/"); + VALIDATE +} + +TEST_F (UriParserTest, BracketedHostnamePort) +{ + const UriTestCase testCase("connect://[192.168.100.132]:5432/", "connect", "192.168.100.132", 5432, "/"); + VALIDATE +} + +TEST_F (UriParserTest, BracketedHostname) +{ + const UriTestCase testCase("connect://[192.168.100.132]", "connect", "192.168.100.132", -1, "/"); + VALIDATE +} + +TEST_F (UriParserTest, BracketedHostnameWithColon) +{ + const UriTestCase testCase("connect://[192.168.100.132:5555]:1234", "connect", "192.168.100.132:5555", 1234, "/"); + VALIDATE +} + +TEST_F (UriParserTest, SchemeHostSeparator) +{ + const UriTestCase testCase("x:/y"); + VALIDATE +} + +TEST_F (UriParserTest, SchemeHostSeparator2) +{ + const UriTestCase testCase("x:y"); + VALIDATE +} + +TEST_F (UriParserTest, SchemeHostSeparator3) +{ + const UriTestCase testCase("x//y"); + VALIDATE +} + +TEST_F (UriParserTest, SchemeHostSeparator4) +{ + const UriTestCase testCase("x/y"); + VALIDATE +} + +TEST_F (UriParserTest, BadPort) +{ + const UriTestCase testCase("x://y:a/"); + VALIDATE +} + +TEST_F (UriParserTest, BadPort2) +{ + const UriTestCase testCase("x://y:5432a/"); + VALIDATE +} + +TEST_F (UriParserTest, Empty) +{ + const UriTestCase testCase(""); + VALIDATE +} + +TEST_F (UriParserTest, PortOverflow) +{ + const UriTestCase testCase("x://y:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/"); + VALIDATE +} + diff --git a/unittests/gtest_common.h b/unittests/gtest_common.h new file mode 100644 index 000000000000..006c9596ca4f --- /dev/null +++ b/unittests/gtest_common.h @@ -0,0 +1,24 @@ +//===-- gtest_common.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(LLDB_GTEST_COMMON_H) +#error "gtest_common.h should not be included manually." +#else +#define LLDB_GTEST_COMMON_H +#endif + +// This header file is force included by all of LLDB's unittest compilation +// units. Be very leary about putting anything in this file. + +#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Due to a bug in , when _HAS_EXCEPTIONS == 0 the header will try to call +// uncaught_exception() without having a declaration for it. The fix for this is +// to manually #include , which contains this declaration. +#include +#endif diff --git a/use_lldb_suite_root.py b/use_lldb_suite_root.py new file mode 100644 index 000000000000..5492d4081ac5 --- /dev/null +++ b/use_lldb_suite_root.py @@ -0,0 +1,22 @@ +import inspect +import os +import sys + +def add_third_party_module_dirs(lldb_root): + third_party_modules_dir = os.path.join(lldb_root, "third_party", "Python", "module") + if not os.path.isdir(third_party_modules_dir): + return + + module_dirs = os.listdir(third_party_modules_dir) + for module_dir in module_dirs: + module_dir = os.path.join(third_party_modules_dir, module_dir) + sys.path.insert(0, module_dir) + +def add_lldbsuite_packages_dir(lldb_root): + packages_dir = os.path.join(lldb_root, "packages", "Python") + sys.path.insert(0, packages_dir) + +lldb_root = os.path.dirname(inspect.getfile(inspect.currentframe())) + +add_third_party_module_dirs(lldb_root) +add_lldbsuite_packages_dir(lldb_root) diff --git a/utils/git-svn/convert.py b/utils/git-svn/convert.py new file mode 100755 index 000000000000..c230a016b7f0 --- /dev/null +++ b/utils/git-svn/convert.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +""" +Convert the raw message sources from git patch emails to git-am friendly files. + +Usage: + +1. Mail.app -> Save As -> api.eml (Raw Message Source) +2. .../convert.py api.eml +3. git am [--signoff] < api.eml +4. git svn dcommit [--commit-url https://id@llvm.org/svn/llvm-project/lldb/trunk] +""" + +import os, re, sys +import StringIO + +def usage(problem_file=None): + if problem_file: + print "%s is not a file" % problem_file + print "Usage: convert.py raw-message-source [raw-message-source2 ...]" + sys.exit(0) + +def do_convert(file): + """Skip all preceding mail message headers until 'From: ' is encountered. + Then for each line ('From: ' header included), replace the dos style CRLF + end-of-line with unix style LF end-of-line. + """ + print "converting %s ..." % file + + with open(file, 'r') as f_in: + content = f_in.read() + + # The new content to be written back to the same file. + new_content = StringIO.StringIO() + + # Boolean flag controls whether to start printing lines. + from_header_seen = False + + # By default, splitlines() don't include line breaks. CRLF should be gone. + for line in content.splitlines(): + # Wait till we scan the 'From: ' header before start printing the lines. + if not from_header_seen: + if not line.startswith('From: '): + continue + else: + from_header_seen = True + + print >> new_content, line + + with open(file, 'w') as f_out: + f_out.write(new_content.getvalue()) + + print "done" + +def main(): + if len(sys.argv) == 1: + usage() + # Convert the raw message source one by one. + for file in sys.argv[1:]: + if not os.path.isfile(file): + usage(file) + do_convert(file) + +if __name__ == '__main__': + main() diff --git a/utils/lui/Readme b/utils/lui/Readme new file mode 100644 index 000000000000..7ba51ce81105 --- /dev/null +++ b/utils/lui/Readme @@ -0,0 +1,36 @@ + +LLDB (Terminal) User Interface +------------------------------ + +This directory contains the curses user interface for LLDB. To use it, ensure Python can find your lldb module. You may have to modify PYTHONPATH for that purpose: + +$ export PYTHONPATH=/path/to/lldb/module + +Then, run the lui.py. To load a core file: +$ ./lui.py --core core + +To create a target from an executable: +$ ./lui.py /bin/echo "hello world" + +To attach to a running process: +$ ./lui.py --attach + + +Known Issues +------------ +1. Resizing the terminal will most likely cause lui to crash. +2. Missing paging in command-window +3. Only minimal testing (on Ubuntu Linux x86_64) + +Missing Features +---------------- +- stdin/stdout/stderr windows +- memory window +- backtrace window +- threads window +- tab-completion +- syntax-highlighting (via pygments library) +- (local) variables window +- registers window +- disassembly window +- custom layout diff --git a/utils/lui/breakwin.py b/utils/lui/breakwin.py new file mode 100644 index 000000000000..734f5eddba42 --- /dev/null +++ b/utils/lui/breakwin.py @@ -0,0 +1,90 @@ +##===-- breakwin.py ------------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + +import cui +import curses +import lldb, lldbutil +import re + +class BreakWin(cui.ListWin): + def __init__(self, driver, x, y, w, h): + super(BreakWin, self).__init__(x, y, w, h) + self.driver = driver + self.update() + self.showDetails = {} + + def handleEvent(self, event): + if isinstance(event, lldb.SBEvent): + if lldb.SBBreakpoint.EventIsBreakpointEvent(event): + self.update() + if isinstance(event, int): + if event == ord('d'): + self.deleteSelected() + if event == curses.ascii.NL or event == curses.ascii.SP: + self.toggleSelected() + elif event == curses.ascii.TAB: + if self.getSelected() != -1: + target = self.driver.getTarget() + if not target.IsValid(): + return + i = target.GetBreakpointAtIndex(self.getSelected()).id + self.showDetails[i] = not self.showDetails[i] + self.update() + super(BreakWin, self).handleEvent(event) + + def toggleSelected(self): + if self.getSelected() == -1: + return + target = self.driver.getTarget() + if not target.IsValid(): + return + bp = target.GetBreakpointAtIndex(self.getSelected()) + bp.SetEnabled(not bp.IsEnabled()) + + def deleteSelected(self): + if self.getSelected() == -1: + return + target = self.driver.getTarget() + if not target.IsValid(): + return + bp = target.GetBreakpointAtIndex(self.getSelected()) + target.BreakpointDelete(bp.id) + + def update(self): + target = self.driver.getTarget() + if not target.IsValid(): + self.win.erase() + self.win.noutrefresh() + return + selected = self.getSelected() + self.clearItems() + for i in range(0, target.GetNumBreakpoints()): + bp = target.GetBreakpointAtIndex(i) + if bp.IsInternal(): + continue + text = lldbutil.get_description(bp) + # FIXME: Use an API for this, not parsing the description. + match = re.search('SBBreakpoint: id = ([^,]+), (.*)', text) + try: + id = match.group(1) + desc = match.group(2).strip() + if bp.IsEnabled(): + text = '%s: %s' % (id, desc) + else: + text = '%s: (disabled) %s' % (id, desc) + except ValueError as e: + # bp unparsable + pass + + if self.showDetails.setdefault(bp.id, False): + for location in bp: + desc = lldbutil.get_description(location, lldb.eDescriptionLevelFull) + text += '\n ' + desc + self.addItem(text) + self.setSelected(selected) diff --git a/utils/lui/commandwin.py b/utils/lui/commandwin.py new file mode 100644 index 000000000000..2eb2082c6c80 --- /dev/null +++ b/utils/lui/commandwin.py @@ -0,0 +1,121 @@ +##===-- commandwin.py ----------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + +import cui +import curses +import lldb +from itertools import islice + +class History(object): + def __init__(self): + self.data = {} + self.pos = 0 + self.tempEntry = '' + + def previous(self, curr): + if self.pos == len(self.data): + self.tempEntry = curr + + if self.pos < 0: + return '' + if self.pos == 0: + self.pos -= 1 + return '' + if self.pos > 0: + self.pos -= 1 + return self.data[self.pos] + + def next(self): + if self.pos < len(self.data): + self.pos += 1 + + if self.pos < len(self.data): + return self.data[self.pos] + elif self.tempEntry != '': + return self.tempEntry + else: + return '' + + def add(self, c): + self.tempEntry = '' + self.pos = len(self.data) + if self.pos == 0 or self.data[self.pos-1] != c: + self.data[self.pos] = c + self.pos += 1 + +class CommandWin(cui.TitledWin): + def __init__(self, driver, x, y, w, h): + super(CommandWin, self).__init__(x, y, w, h, "Commands") + self.command = "" + self.data = "" + driver.setSize(w, h) + + self.win.scrollok(1) + + self.driver = driver + self.history = History() + + def enterCallback(content): + self.handleCommand(content) + def tabCompleteCallback(content): + self.data = content + matches = lldb.SBStringList() + commandinterpreter = self.getCommandInterpreter() + commandinterpreter.HandleCompletion(self.data, self.el.index, 0, -1, matches) + if matches.GetSize() == 2: + self.el.content += matches.GetStringAtIndex(0) + self.el.index = len(self.el.content) + self.el.draw() + else: + self.win.move(self.el.starty, self.el.startx) + self.win.scroll(1) + self.win.addstr("Available Completions:") + self.win.scroll(1) + for m in islice(matches, 1, None): + self.win.addstr(self.win.getyx()[0], 0, m) + self.win.scroll(1) + self.el.draw() + + self.startline = self.win.getmaxyx()[0]-2 + + self.el = cui.CursesEditLine(self.win, self.history, enterCallback, tabCompleteCallback) + self.el.prompt = self.driver.getPrompt() + self.el.showPrompt(self.startline, 0) + + def handleCommand(self, cmd): + # enter! + self.win.scroll(1) # TODO: scroll more for longer commands + if cmd == '': + cmd = self.history.previous('') + elif cmd in ('q', 'quit'): + self.driver.terminate() + return + + self.history.add(cmd) + ret = self.driver.handleCommand(cmd) + if ret.Succeeded(): + out = ret.GetOutput() + attr = curses.A_NORMAL + else: + out = ret.GetError() + attr = curses.color_pair(3) # red on black + self.win.addstr(self.startline, 0, out + '\n', attr) + self.win.scroll(1) + self.el.showPrompt(self.startline, 0) + + def handleEvent(self, event): + if isinstance(event, int): + if event == curses.ascii.EOT and self.el.content == '': + # When the command is empty, treat CTRL-D as EOF. + self.driver.terminate() + return + self.el.handleEvent(event) + + def getCommandInterpreter(self): + return self.driver.getCommandInterpreter() diff --git a/utils/lui/cui.py b/utils/lui/cui.py new file mode 100755 index 000000000000..e82f0abac2cc --- /dev/null +++ b/utils/lui/cui.py @@ -0,0 +1,320 @@ +##===-- cui.py -----------------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + +import curses +import curses.ascii +import threading + +class CursesWin(object): + def __init__(self, x, y, w, h): + self.win = curses.newwin(h, w, y, x) + self.focus = False + + def setFocus(self, focus): + self.focus = focus + def getFocus(self): + return self.focus + def canFocus(self): + return True + + def handleEvent(self, event): + return + def draw(self): + return + +class TextWin(CursesWin): + def __init__(self, x, y, w): + super(TextWin, self).__init__(x, y, w, 1) + self.win.bkgd(curses.color_pair(1)) + self.text = '' + self.reverse = False + + def canFocus(self): + return False + + def draw(self): + w = self.win.getmaxyx()[1] + text = self.text + if len(text) > w: + #trunc_length = len(text) - w + text = text[-w+1:] + if self.reverse: + self.win.addstr(0, 0, text, curses.A_REVERSE) + else: + self.win.addstr(0, 0, text) + self.win.noutrefresh() + + def setReverse(self, reverse): + self.reverse = reverse + + def setText(self, text): + self.text = text + +class TitledWin(CursesWin): + def __init__(self, x, y, w, h, title): + super(TitledWin, self).__init__(x, y+1, w, h-1) + self.title = title + self.title_win = TextWin(x, y, w) + self.title_win.setText(title) + self.draw() + + def setTitle(self, title): + self.title_win.setText(title) + + def draw(self): + self.title_win.setReverse(self.getFocus()) + self.title_win.draw() + self.win.noutrefresh() + +class ListWin(CursesWin): + def __init__(self, x, y, w, h): + super(ListWin, self).__init__(x, y, w, h) + self.items = [] + self.selected = 0 + self.first_drawn = 0 + self.win.leaveok(True) + + def draw(self): + if len(self.items) == 0: + self.win.erase() + return + + h, w = self.win.getmaxyx() + + allLines = [] + firstSelected = -1 + lastSelected = -1 + for i, item in enumerate(self.items): + lines = self.items[i].split('\n') + lines = lines if lines[len(lines)-1] != '' else lines[:-1] + if len(lines) == 0: + lines = [''] + + if i == self.getSelected(): + firstSelected = len(allLines) + allLines.extend(lines) + if i == self.selected: + lastSelected = len(allLines) - 1 + + if firstSelected < self.first_drawn: + self.first_drawn = firstSelected + elif lastSelected >= self.first_drawn + h: + self.first_drawn = lastSelected - h + 1 + + self.win.erase() + + begin = self.first_drawn + end = begin + h + + y = 0 + for i, line in list(enumerate(allLines))[begin:end]: + attr = curses.A_NORMAL + if i >= firstSelected and i <= lastSelected: + attr = curses.A_REVERSE + line = '{0:{width}}'.format(line, width=w-1) + + # Ignore the error we get from drawing over the bottom-right char. + try: + self.win.addstr(y, 0, line[:w], attr) + except curses.error: + pass + y += 1 + self.win.noutrefresh() + + def getSelected(self): + if self.items: + return self.selected + return -1 + + def setSelected(self, selected): + self.selected = selected + if self.selected < 0: + self.selected = 0 + elif self.selected >= len(self.items): + self.selected = len(self.items) - 1 + + def handleEvent(self, event): + if isinstance(event, int): + if len(self.items) > 0: + if event == curses.KEY_UP: + self.setSelected(self.selected - 1) + if event == curses.KEY_DOWN: + self.setSelected(self.selected + 1) + if event == curses.ascii.NL: + self.handleSelect(self.selected) + + def addItem(self, item): + self.items.append(item) + + def clearItems(self): + self.items = [] + + def handleSelect(self, index): + return + +class InputHandler(threading.Thread): + def __init__(self, screen, queue): + super(InputHandler, self).__init__() + self.screen = screen + self.queue = queue + + def run(self): + while True: + c = self.screen.getch() + self.queue.put(c) + + +class CursesUI(object): + """ Responsible for updating the console UI with curses. """ + def __init__(self, screen, event_queue): + self.screen = screen + self.event_queue = event_queue + + curses.start_color() + curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) + curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLACK) + curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) + self.screen.bkgd(curses.color_pair(1)) + self.screen.clear() + + self.input_handler = InputHandler(self.screen, self.event_queue) + self.input_handler.daemon = True + + self.focus = 0 + + self.screen.refresh() + + def focusNext(self): + self.wins[self.focus].setFocus(False) + old = self.focus + while True: + self.focus += 1 + if self.focus >= len(self.wins): + self.focus = 0 + if self.wins[self.focus].canFocus(): + break + self.wins[self.focus].setFocus(True) + + def handleEvent(self, event): + if isinstance(event, int): + if event == curses.KEY_F3: + self.focusNext() + + def eventLoop(self): + + self.input_handler.start() + self.wins[self.focus].setFocus(True) + + while True: + self.screen.noutrefresh() + + for i, win in enumerate(self.wins): + if i != self.focus: + win.draw() + # Draw the focused window last so that the cursor shows up. + if self.wins: + self.wins[self.focus].draw() + curses.doupdate() # redraw the physical screen + + event = self.event_queue.get() + + for win in self.wins: + if isinstance(event, int): + if win.getFocus() or not win.canFocus(): + win.handleEvent(event) + else: + win.handleEvent(event) + self.handleEvent(event) + +class CursesEditLine(object): + """ Embed an 'editline'-compatible prompt inside a CursesWin. """ + def __init__(self, win, history, enterCallback, tabCompleteCallback): + self.win = win + self.history = history + self.enterCallback = enterCallback + self.tabCompleteCallback = tabCompleteCallback + + self.prompt = '' + self.content = '' + self.index = 0 + self.startx = -1 + self.starty = -1 + + def draw(self, prompt=None): + if not prompt: + prompt = self.prompt + (h, w) = self.win.getmaxyx() + if (len(prompt) + len(self.content)) / w + self.starty >= h-1: + self.win.scroll(1) + self.starty -= 1 + if self.starty < 0: + raise RuntimeError('Input too long; aborting') + (y, x) = (self.starty, self.startx) + + self.win.move(y, x) + self.win.clrtobot() + self.win.addstr(y, x, prompt) + remain = self.content + self.win.addstr(remain[:w-len(prompt)]) + remain = remain[w-len(prompt):] + while remain != '': + y += 1 + self.win.addstr(y, 0, remain[:w]) + remain = remain[w:] + + length = self.index + len(prompt) + self.win.move(self.starty + length / w, length % w) + + def showPrompt(self, y, x, prompt=None): + self.content = '' + self.index = 0 + self.startx = x + self.starty = y + self.draw(prompt) + + def handleEvent(self, event): + if not isinstance(event, int): + return # not handled + key = event + + if self.startx == -1: + raise RuntimeError('Trying to handle input without prompt') + + if key == curses.ascii.NL: + self.enterCallback(self.content) + elif key == curses.ascii.TAB: + self.tabCompleteCallback(self.content) + elif curses.ascii.isprint(key): + self.content = self.content[:self.index] + chr(key) + self.content[self.index:] + self.index += 1 + elif key == curses.KEY_BACKSPACE or key == curses.ascii.BS: + if self.index > 0: + self.index -= 1 + self.content = self.content[:self.index] + self.content[self.index+1:] + elif key == curses.KEY_DC or key == curses.ascii.DEL or key == curses.ascii.EOT: + self.content = self.content[:self.index] + self.content[self.index+1:] + elif key == curses.ascii.VT: # CTRL-K + self.content = self.content[:self.index] + elif key == curses.KEY_LEFT or key == curses.ascii.STX: # left or CTRL-B + if self.index > 0: + self.index -= 1 + elif key == curses.KEY_RIGHT or key == curses.ascii.ACK: # right or CTRL-F + if self.index < len(self.content): + self.index += 1 + elif key == curses.ascii.SOH: # CTRL-A + self.index = 0 + elif key == curses.ascii.ENQ: # CTRL-E + self.index = len(self.content) + elif key == curses.KEY_UP or key == curses.ascii.DLE: # up or CTRL-P + self.content = self.history.previous(self.content) + self.index = len(self.content) + elif key == curses.KEY_DOWN or key == curses.ascii.SO: # down or CTRL-N + self.content = self.history.next() + self.index = len(self.content) + self.draw() diff --git a/utils/lui/debuggerdriver.py b/utils/lui/debuggerdriver.py new file mode 100644 index 000000000000..f7b885107cc6 --- /dev/null +++ b/utils/lui/debuggerdriver.py @@ -0,0 +1,138 @@ +##===-- debuggerdriver.py ------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + + +import lldb +import lldbutil +import sys +from threading import Thread + +class DebuggerDriver(Thread): + """ Drives the debugger and responds to events. """ + def __init__(self, debugger, event_queue): + Thread.__init__(self) + self.event_queue = event_queue + # This is probably not great because it does not give liblldb a chance to clean up + self.daemon = True + self.initialize(debugger) + + def initialize(self, debugger): + self.done = False + self.debugger = debugger + self.listener = debugger.GetListener() + if not self.listener.IsValid(): + raise "Invalid listener" + + self.listener.StartListeningForEventClass(self.debugger, + lldb.SBTarget.GetBroadcasterClassName(), + lldb.SBTarget.eBroadcastBitBreakpointChanged + #| lldb.SBTarget.eBroadcastBitModuleLoaded + #| lldb.SBTarget.eBroadcastBitModuleUnloaded + | lldb.SBTarget.eBroadcastBitWatchpointChanged + #| lldb.SBTarget.eBroadcastBitSymbolLoaded + ) + + self.listener.StartListeningForEventClass(self.debugger, + lldb.SBThread.GetBroadcasterClassName(), + lldb.SBThread.eBroadcastBitStackChanged + # lldb.SBThread.eBroadcastBitBreakpointChanged + | lldb.SBThread.eBroadcastBitThreadSuspended + | lldb.SBThread.eBroadcastBitThreadResumed + | lldb.SBThread.eBroadcastBitSelectedFrameChanged + | lldb.SBThread.eBroadcastBitThreadSelected + ) + + self.listener.StartListeningForEventClass(self.debugger, + lldb.SBProcess.GetBroadcasterClassName(), + lldb.SBProcess.eBroadcastBitStateChanged + | lldb.SBProcess.eBroadcastBitInterrupt + | lldb.SBProcess.eBroadcastBitSTDOUT + | lldb.SBProcess.eBroadcastBitSTDERR + | lldb.SBProcess.eBroadcastBitProfileData + ) + self.listener.StartListeningForEventClass(self.debugger, + lldb.SBCommandInterpreter.GetBroadcasterClass(), + lldb.SBCommandInterpreter.eBroadcastBitThreadShouldExit + | lldb.SBCommandInterpreter.eBroadcastBitResetPrompt + | lldb.SBCommandInterpreter.eBroadcastBitQuitCommandReceived + | lldb.SBCommandInterpreter.eBroadcastBitAsynchronousOutputData + | lldb.SBCommandInterpreter.eBroadcastBitAsynchronousErrorData + ) + def createTarget(self, target_image, args=None): + self.handleCommand("target create %s" % target_image) + if args is not None: + self.handleCommand("settings set target.run-args %s" % args) + + def attachProcess(self, pid): + self.handleCommand("process attach -p %d" % pid) + pass + + def loadCore(self, corefile): + self.handleCommand("target create -c %s" % corefile) + pass + + def setDone(self): + self.done = True + + def isDone(self): + return self.done + + def getPrompt(self): + return self.debugger.GetPrompt() + + def getCommandInterpreter(self): + return self.debugger.GetCommandInterpreter() + + def getSourceManager(self): + return self.debugger.GetSourceManager() + + def setSize(self, width, height): + # FIXME: respect height + self.debugger.SetTerminalWidth(width) + + def getTarget(self): + return self.debugger.GetTargetAtIndex(0) + + def handleCommand(self, cmd): + ret = lldb.SBCommandReturnObject() + self.getCommandInterpreter().HandleCommand(cmd, ret) + return ret + + def eventLoop(self): + while not self.isDone(): + event = lldb.SBEvent() + got_event = self.listener.WaitForEvent(lldb.UINT32_MAX, event) + if got_event and not event.IsValid(): + self.winAddStr("Warning: Invalid or no event...") + continue + elif not event.GetBroadcaster().IsValid(): + continue + + self.event_queue.put(event) + + def run(self): + self.eventLoop() + + def terminate(self): + lldb.SBDebugger.Terminate() + sys.exit(0) + +def createDriver(debugger, event_queue): + driver = DebuggerDriver(debugger, event_queue) + #driver.start() + # if pid specified: + # - attach to pid + # else if core file specified + # - create target from corefile + # else + # - create target from file + # - settings append target.run-args + # source .lldbinit file + + return driver diff --git a/utils/lui/eventwin.py b/utils/lui/eventwin.py new file mode 100644 index 000000000000..327978a3b456 --- /dev/null +++ b/utils/lui/eventwin.py @@ -0,0 +1,25 @@ +##===-- eventwin.py ------------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + +import cui +import lldb, lldbutil + +class EventWin(cui.TitledWin): + def __init__(self, x, y, w, h): + super(EventWin, self).__init__(x, y, w, h, 'LLDB Event Log') + self.win.scrollok(1) + super(EventWin, self).draw() + + def handleEvent(self, event): + if isinstance(event, lldb.SBEvent): + self.win.scroll() + h = self.win.getmaxyx()[0] + self.win.addstr(h-1, 0, lldbutil.get_description(event)) + return + diff --git a/utils/lui/lldbutil.py b/utils/lui/lldbutil.py new file mode 100644 index 000000000000..8bdb074728f3 --- /dev/null +++ b/utils/lui/lldbutil.py @@ -0,0 +1,899 @@ +##===-- lldbutil.py ------------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + +""" +This LLDB module contains miscellaneous utilities. +Some of the test suite takes advantage of the utility functions defined here. +They can also be useful for general purpose lldb scripting. +""" + +import lldb +import os, sys +import StringIO + +# =================================================== +# Utilities for locating/checking executable programs +# =================================================== + +def is_exe(fpath): + """Returns True if fpath is an executable.""" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + """Returns the full path to a program; None otherwise.""" + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +# =================================================== +# Disassembly for an SBFunction or an SBSymbol object +# =================================================== + +def disassemble(target, function_or_symbol): + """Disassemble the function or symbol given a target. + + It returns the disassembly content in a string object. + """ + buf = StringIO.StringIO() + insts = function_or_symbol.GetInstructions(target) + for i in insts: + print >> buf, i + return buf.getvalue() + +# ========================================================== +# Integer (byte size 1, 2, 4, and 8) to bytearray conversion +# ========================================================== + +def int_to_bytearray(val, bytesize): + """Utility function to convert an integer into a bytearray. + + It returns the bytearray in the little endian format. It is easy to get the + big endian format, just do ba.reverse() on the returned object. + """ + import struct + + if bytesize == 1: + return bytearray([val]) + + # Little endian followed by a format character. + template = "<%c" + if bytesize == 2: + fmt = template % 'h' + elif bytesize == 4: + fmt = template % 'i' + elif bytesize == 4: + fmt = template % 'q' + else: + return None + + packed = struct.pack(fmt, val) + return bytearray(map(ord, packed)) + +def bytearray_to_int(bytes, bytesize): + """Utility function to convert a bytearray into an integer. + + It interprets the bytearray in the little endian format. For a big endian + bytearray, just do ba.reverse() on the object before passing it in. + """ + import struct + + if bytesize == 1: + return bytes[0] + + # Little endian followed by a format character. + template = "<%c" + if bytesize == 2: + fmt = template % 'h' + elif bytesize == 4: + fmt = template % 'i' + elif bytesize == 4: + fmt = template % 'q' + else: + return None + + unpacked = struct.unpack(fmt, str(bytes)) + return unpacked[0] + + +# ============================================================== +# Get the description of an lldb object or None if not available +# ============================================================== +def get_description(obj, option=None): + """Calls lldb_obj.GetDescription() and returns a string, or None. + + For SBTarget, SBBreakpointLocation, and SBWatchpoint lldb objects, an extra + option can be passed in to describe the detailed level of description + desired: + o lldb.eDescriptionLevelBrief + o lldb.eDescriptionLevelFull + o lldb.eDescriptionLevelVerbose + """ + method = getattr(obj, 'GetDescription') + if not method: + return None + tuple = (lldb.SBTarget, lldb.SBBreakpointLocation, lldb.SBWatchpoint) + if isinstance(obj, tuple): + if option is None: + option = lldb.eDescriptionLevelBrief + + stream = lldb.SBStream() + if option is None: + success = method(stream) + else: + success = method(stream, option) + if not success: + return None + return stream.GetData() + + +# ================================================= +# Convert some enum value to its string counterpart +# ================================================= + +def state_type_to_str(enum): + """Returns the stateType string given an enum.""" + if enum == lldb.eStateInvalid: + return "invalid" + elif enum == lldb.eStateUnloaded: + return "unloaded" + elif enum == lldb.eStateConnected: + return "connected" + elif enum == lldb.eStateAttaching: + return "attaching" + elif enum == lldb.eStateLaunching: + return "launching" + elif enum == lldb.eStateStopped: + return "stopped" + elif enum == lldb.eStateRunning: + return "running" + elif enum == lldb.eStateStepping: + return "stepping" + elif enum == lldb.eStateCrashed: + return "crashed" + elif enum == lldb.eStateDetached: + return "detached" + elif enum == lldb.eStateExited: + return "exited" + elif enum == lldb.eStateSuspended: + return "suspended" + else: + raise Exception("Unknown StateType enum") + +def stop_reason_to_str(enum): + """Returns the stopReason string given an enum.""" + if enum == lldb.eStopReasonInvalid: + return "invalid" + elif enum == lldb.eStopReasonNone: + return "none" + elif enum == lldb.eStopReasonTrace: + return "trace" + elif enum == lldb.eStopReasonBreakpoint: + return "breakpoint" + elif enum == lldb.eStopReasonWatchpoint: + return "watchpoint" + elif enum == lldb.eStopReasonSignal: + return "signal" + elif enum == lldb.eStopReasonException: + return "exception" + elif enum == lldb.eStopReasonPlanComplete: + return "plancomplete" + elif enum == lldb.eStopReasonThreadExiting: + return "threadexiting" + else: + raise Exception("Unknown StopReason enum") + +def symbol_type_to_str(enum): + """Returns the symbolType string given an enum.""" + if enum == lldb.eSymbolTypeInvalid: + return "invalid" + elif enum == lldb.eSymbolTypeAbsolute: + return "absolute" + elif enum == lldb.eSymbolTypeCode: + return "code" + elif enum == lldb.eSymbolTypeData: + return "data" + elif enum == lldb.eSymbolTypeTrampoline: + return "trampoline" + elif enum == lldb.eSymbolTypeRuntime: + return "runtime" + elif enum == lldb.eSymbolTypeException: + return "exception" + elif enum == lldb.eSymbolTypeSourceFile: + return "sourcefile" + elif enum == lldb.eSymbolTypeHeaderFile: + return "headerfile" + elif enum == lldb.eSymbolTypeObjectFile: + return "objectfile" + elif enum == lldb.eSymbolTypeCommonBlock: + return "commonblock" + elif enum == lldb.eSymbolTypeBlock: + return "block" + elif enum == lldb.eSymbolTypeLocal: + return "local" + elif enum == lldb.eSymbolTypeParam: + return "param" + elif enum == lldb.eSymbolTypeVariable: + return "variable" + elif enum == lldb.eSymbolTypeVariableType: + return "variabletype" + elif enum == lldb.eSymbolTypeLineEntry: + return "lineentry" + elif enum == lldb.eSymbolTypeLineHeader: + return "lineheader" + elif enum == lldb.eSymbolTypeScopeBegin: + return "scopebegin" + elif enum == lldb.eSymbolTypeScopeEnd: + return "scopeend" + elif enum == lldb.eSymbolTypeAdditional: + return "additional" + elif enum == lldb.eSymbolTypeCompiler: + return "compiler" + elif enum == lldb.eSymbolTypeInstrumentation: + return "instrumentation" + elif enum == lldb.eSymbolTypeUndefined: + return "undefined" + +def value_type_to_str(enum): + """Returns the valueType string given an enum.""" + if enum == lldb.eValueTypeInvalid: + return "invalid" + elif enum == lldb.eValueTypeVariableGlobal: + return "global_variable" + elif enum == lldb.eValueTypeVariableStatic: + return "static_variable" + elif enum == lldb.eValueTypeVariableArgument: + return "argument_variable" + elif enum == lldb.eValueTypeVariableLocal: + return "local_variable" + elif enum == lldb.eValueTypeRegister: + return "register" + elif enum == lldb.eValueTypeRegisterSet: + return "register_set" + elif enum == lldb.eValueTypeConstResult: + return "constant_result" + else: + raise Exception("Unknown ValueType enum") + + +# ================================================== +# Get stopped threads due to each stop reason. +# ================================================== + +def sort_stopped_threads(process, + breakpoint_threads = None, + crashed_threads = None, + watchpoint_threads = None, + signal_threads = None, + exiting_threads = None, + other_threads = None): + """ Fills array *_threads with threads stopped for the corresponding stop + reason. + """ + for lst in [breakpoint_threads, + watchpoint_threads, + signal_threads, + exiting_threads, + other_threads]: + if lst is not None: + lst[:] = [] + + for thread in process: + dispatched = False + for (reason, list) in [(lldb.eStopReasonBreakpoint, breakpoint_threads), + (lldb.eStopReasonException, crashed_threads), + (lldb.eStopReasonWatchpoint, watchpoint_threads), + (lldb.eStopReasonSignal, signal_threads), + (lldb.eStopReasonThreadExiting, exiting_threads), + (None, other_threads)]: + if not dispatched and list is not None: + if thread.GetStopReason() == reason or reason is None: + list.append(thread) + dispatched = True + +# ================================================== +# Utility functions for setting breakpoints +# ================================================== + +def run_break_set_by_file_and_line (test, file_name, line_number, extra_options = None, num_expected_locations = 1, loc_exact=False, module_name=None): + """Set a breakpoint by file and line, returning the breakpoint number. + + If extra_options is not None, then we append it to the breakpoint set command. + + If num_expected_locations is -1 we check that we got AT LEAST one location, otherwise we check that num_expected_locations equals the number of locations. + + If loc_exact is true, we check that there is one location, and that location must be at the input file and line number.""" + + if file_name == None: + command = 'breakpoint set -l %d'%(line_number) + else: + command = 'breakpoint set -f "%s" -l %d'%(file_name, line_number) + + if module_name: + command += " --shlib '%s'" % (module_name) + + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + if num_expected_locations == 1 and loc_exact: + check_breakpoint_result (test, break_results, num_locations=num_expected_locations, file_name = file_name, line_number = line_number, module_name=module_name) + else: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_by_symbol (test, symbol, extra_options = None, num_expected_locations = -1, sym_exact = False, module_name=None): + """Set a breakpoint by symbol name. Common options are the same as run_break_set_by_file_and_line. + + If sym_exact is true, then the output symbol must match the input exactly, otherwise we do a substring match.""" + command = 'breakpoint set -n "%s"'%(symbol) + + if module_name: + command += " --shlib '%s'" % (module_name) + + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + if num_expected_locations == 1 and sym_exact: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations, symbol_name = symbol, module_name=module_name) + else: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_by_selector (test, selector, extra_options = None, num_expected_locations = -1, module_name=None): + """Set a breakpoint by selector. Common options are the same as run_break_set_by_file_and_line.""" + + command = 'breakpoint set -S "%s"' % (selector) + + if module_name: + command += ' --shlib "%s"' % (module_name) + + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + if num_expected_locations == 1: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations, symbol_name = selector, symbol_match_exact=False, module_name=module_name) + else: + check_breakpoint_result (test, break_results, num_locations = num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_by_regexp (test, regexp, extra_options=None, num_expected_locations=-1): + """Set a breakpoint by regular expression match on symbol name. Common options are the same as run_break_set_by_file_and_line.""" + + command = 'breakpoint set -r "%s"'%(regexp) + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + check_breakpoint_result (test, break_results, num_locations=num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_by_source_regexp (test, regexp, extra_options=None, num_expected_locations=-1): + """Set a breakpoint by source regular expression. Common options are the same as run_break_set_by_file_and_line.""" + command = 'breakpoint set -p "%s"'%(regexp) + if extra_options: + command += " " + extra_options + + break_results = run_break_set_command (test, command) + + check_breakpoint_result (test, break_results, num_locations=num_expected_locations) + + return get_bpno_from_match (break_results) + +def run_break_set_command (test, command): + """Run the command passed in - it must be some break set variant - and analyze the result. + Returns a dictionary of information gleaned from the command-line results. + Will assert if the breakpoint setting fails altogether. + + Dictionary will contain: + bpno - breakpoint of the newly created breakpoint, -1 on error. + num_locations - number of locations set for the breakpoint. + + If there is only one location, the dictionary MAY contain: + file - source file name + line_no - source line number + symbol - symbol name + inline_symbol - inlined symbol name + offset - offset from the original symbol + module - module + address - address at which the breakpoint was set.""" + + patterns = [r"^Breakpoint (?P[0-9]+): (?P[0-9]+) locations\.$", + r"^Breakpoint (?P[0-9]+): (?Pno) locations \(pending\)\.", + r"^Breakpoint (?P[0-9]+): where = (?P.*)`(?P[+\-]{0,1}[^+]+)( \+ (?P[0-9]+)){0,1}( \[inlined\] (?P.*)){0,1} at (?P[^:]+):(?P[0-9]+), address = (?P
0x[0-9a-fA-F]+)$", + r"^Breakpoint (?P[0-9]+): where = (?P.*)`(?P.*)( \+ (?P[0-9]+)){0,1}, address = (?P
0x[0-9a-fA-F]+)$"] + match_object = test.match (command, patterns) + break_results = match_object.groupdict() + + # We always insert the breakpoint number, setting it to -1 if we couldn't find it + # Also, make sure it gets stored as an integer. + if not 'bpno' in break_results: + break_results['bpno'] = -1 + else: + break_results['bpno'] = int(break_results['bpno']) + + # We always insert the number of locations + # If ONE location is set for the breakpoint, then the output doesn't mention locations, but it has to be 1... + # We also make sure it is an integer. + + if not 'num_locations' in break_results: + num_locations = 1 + else: + num_locations = break_results['num_locations'] + if num_locations == 'no': + num_locations = 0 + else: + num_locations = int(break_results['num_locations']) + + break_results['num_locations'] = num_locations + + if 'line_no' in break_results: + break_results['line_no'] = int(break_results['line_no']) + + return break_results + +def get_bpno_from_match (break_results): + return int (break_results['bpno']) + +def check_breakpoint_result (test, break_results, file_name=None, line_number=-1, symbol_name=None, symbol_match_exact=True, module_name=None, offset=-1, num_locations=-1): + + out_num_locations = break_results['num_locations'] + + if num_locations == -1: + test.assertTrue (out_num_locations > 0, "Expecting one or more locations, got none.") + else: + test.assertTrue (num_locations == out_num_locations, "Expecting %d locations, got %d."%(num_locations, out_num_locations)) + + if file_name: + out_file_name = "" + if 'file' in break_results: + out_file_name = break_results['file'] + test.assertTrue (file_name == out_file_name, "Breakpoint file name '%s' doesn't match resultant name '%s'."%(file_name, out_file_name)) + + if line_number != -1: + out_file_line = -1 + if 'line_no' in break_results: + out_line_number = break_results['line_no'] + + test.assertTrue (line_number == out_line_number, "Breakpoint line number %s doesn't match resultant line %s."%(line_number, out_line_number)) + + if symbol_name: + out_symbol_name = "" + # Look first for the inlined symbol name, otherwise use the symbol name: + if 'inline_symbol' in break_results and break_results['inline_symbol']: + out_symbol_name = break_results['inline_symbol'] + elif 'symbol' in break_results: + out_symbol_name = break_results['symbol'] + + if symbol_match_exact: + test.assertTrue(symbol_name == out_symbol_name, "Symbol name '%s' doesn't match resultant symbol '%s'."%(symbol_name, out_symbol_name)) + else: + test.assertTrue(out_symbol_name.find(symbol_name) != -1, "Symbol name '%s' isn't in resultant symbol '%s'."%(symbol_name, out_symbol_name)) + + if module_name: + out_nodule_name = None + if 'module' in break_results: + out_module_name = break_results['module'] + + test.assertTrue (module_name.find(out_module_name) != -1, "Symbol module name '%s' isn't in expected module name '%s'."%(out_module_name, module_name)) + +# ================================================== +# Utility functions related to Threads and Processes +# ================================================== + +def get_stopped_threads(process, reason): + """Returns the thread(s) with the specified stop reason in a list. + + The list can be empty if no such thread exists. + """ + threads = [] + for t in process: + if t.GetStopReason() == reason: + threads.append(t) + return threads + +def get_stopped_thread(process, reason): + """A convenience function which returns the first thread with the given stop + reason or None. + + Example usages: + + 1. Get the stopped thread due to a breakpoint condition + + ... + from lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonPlanComplete) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") + ... + + 2. Get the thread stopped due to a breakpoint + + ... + from lldbutil import get_stopped_thread + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + ... + + """ + threads = get_stopped_threads(process, reason) + if len(threads) == 0: + return None + return threads[0] + +def get_threads_stopped_at_breakpoint (process, bkpt): + """ For a stopped process returns the thread stopped at the breakpoint passed in bkpt""" + stopped_threads = [] + threads = [] + + stopped_threads = get_stopped_threads (process, lldb.eStopReasonBreakpoint) + + if len(stopped_threads) == 0: + return threads + + for thread in stopped_threads: + # Make sure we've hit our breakpoint... + break_id = thread.GetStopReasonDataAtIndex (0) + if break_id == bkpt.GetID(): + threads.append(thread) + + return threads + +def continue_to_breakpoint (process, bkpt): + """ Continues the process, if it stops, returns the threads stopped at bkpt; otherwise, returns None""" + process.Continue() + if process.GetState() != lldb.eStateStopped: + return None + else: + return get_threads_stopped_at_breakpoint (process, bkpt) + +def get_caller_symbol(thread): + """ + Returns the symbol name for the call site of the leaf function. + """ + depth = thread.GetNumFrames() + if depth <= 1: + return None + caller = thread.GetFrameAtIndex(1).GetSymbol() + if caller: + return caller.GetName() + else: + return None + + +def get_function_names(thread): + """ + Returns a sequence of function names from the stack frames of this thread. + """ + def GetFuncName(i): + return thread.GetFrameAtIndex(i).GetFunctionName() + + return map(GetFuncName, range(thread.GetNumFrames())) + + +def get_symbol_names(thread): + """ + Returns a sequence of symbols for this thread. + """ + def GetSymbol(i): + return thread.GetFrameAtIndex(i).GetSymbol().GetName() + + return map(GetSymbol, range(thread.GetNumFrames())) + + +def get_pc_addresses(thread): + """ + Returns a sequence of pc addresses for this thread. + """ + def GetPCAddress(i): + return thread.GetFrameAtIndex(i).GetPCAddress() + + return map(GetPCAddress, range(thread.GetNumFrames())) + + +def get_filenames(thread): + """ + Returns a sequence of file names from the stack frames of this thread. + """ + def GetFilename(i): + return thread.GetFrameAtIndex(i).GetLineEntry().GetFileSpec().GetFilename() + + return map(GetFilename, range(thread.GetNumFrames())) + + +def get_line_numbers(thread): + """ + Returns a sequence of line numbers from the stack frames of this thread. + """ + def GetLineNumber(i): + return thread.GetFrameAtIndex(i).GetLineEntry().GetLine() + + return map(GetLineNumber, range(thread.GetNumFrames())) + + +def get_module_names(thread): + """ + Returns a sequence of module names from the stack frames of this thread. + """ + def GetModuleName(i): + return thread.GetFrameAtIndex(i).GetModule().GetFileSpec().GetFilename() + + return map(GetModuleName, range(thread.GetNumFrames())) + + +def get_stack_frames(thread): + """ + Returns a sequence of stack frames for this thread. + """ + def GetStackFrame(i): + return thread.GetFrameAtIndex(i) + + return map(GetStackFrame, range(thread.GetNumFrames())) + + +def print_stacktrace(thread, string_buffer = False): + """Prints a simple stack trace of this thread.""" + + output = StringIO.StringIO() if string_buffer else sys.stdout + target = thread.GetProcess().GetTarget() + + depth = thread.GetNumFrames() + + mods = get_module_names(thread) + funcs = get_function_names(thread) + symbols = get_symbol_names(thread) + files = get_filenames(thread) + lines = get_line_numbers(thread) + addrs = get_pc_addresses(thread) + + if thread.GetStopReason() != lldb.eStopReasonInvalid: + desc = "stop reason=" + stop_reason_to_str(thread.GetStopReason()) + else: + desc = "" + print >> output, "Stack trace for thread id={0:#x} name={1} queue={2} ".format( + thread.GetThreadID(), thread.GetName(), thread.GetQueueName()) + desc + + for i in range(depth): + frame = thread.GetFrameAtIndex(i) + function = frame.GetFunction() + + load_addr = addrs[i].GetLoadAddress(target) + if not function: + file_addr = addrs[i].GetFileAddress() + start_addr = frame.GetSymbol().GetStartAddress().GetFileAddress() + symbol_offset = file_addr - start_addr + print >> output, " frame #{num}: {addr:#016x} {mod}`{symbol} + {offset}".format( + num=i, addr=load_addr, mod=mods[i], symbol=symbols[i], offset=symbol_offset) + else: + print >> output, " frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line} {args}".format( + num=i, addr=load_addr, mod=mods[i], + func='%s [inlined]' % funcs[i] if frame.IsInlined() else funcs[i], + file=files[i], line=lines[i], + args=get_args_as_string(frame, showFuncName=False) if not frame.IsInlined() else '()') + + if string_buffer: + return output.getvalue() + + +def print_stacktraces(process, string_buffer = False): + """Prints the stack traces of all the threads.""" + + output = StringIO.StringIO() if string_buffer else sys.stdout + + print >> output, "Stack traces for " + str(process) + + for thread in process: + print >> output, print_stacktrace(thread, string_buffer=True) + + if string_buffer: + return output.getvalue() + +# =================================== +# Utility functions related to Frames +# =================================== + +def get_parent_frame(frame): + """ + Returns the parent frame of the input frame object; None if not available. + """ + thread = frame.GetThread() + parent_found = False + for f in thread: + if parent_found: + return f + if f.GetFrameID() == frame.GetFrameID(): + parent_found = True + + # If we reach here, no parent has been found, return None. + return None + +def get_args_as_string(frame, showFuncName=True): + """ + Returns the args of the input frame object as a string. + """ + # arguments => True + # locals => False + # statics => False + # in_scope_only => True + vars = frame.GetVariables(True, False, False, True) # type of SBValueList + args = [] # list of strings + for var in vars: + args.append("(%s)%s=%s" % (var.GetTypeName(), + var.GetName(), + var.GetValue())) + if frame.GetFunction(): + name = frame.GetFunction().GetName() + elif frame.GetSymbol(): + name = frame.GetSymbol().GetName() + else: + name = "" + if showFuncName: + return "%s(%s)" % (name, ", ".join(args)) + else: + return "(%s)" % (", ".join(args)) + +def print_registers(frame, string_buffer = False): + """Prints all the register sets of the frame.""" + + output = StringIO.StringIO() if string_buffer else sys.stdout + + print >> output, "Register sets for " + str(frame) + + registerSet = frame.GetRegisters() # Return type of SBValueList. + print >> output, "Frame registers (size of register set = %d):" % registerSet.GetSize() + for value in registerSet: + #print >> output, value + print >> output, "%s (number of children = %d):" % (value.GetName(), value.GetNumChildren()) + for child in value: + print >> output, "Name: %s, Value: %s" % (child.GetName(), child.GetValue()) + + if string_buffer: + return output.getvalue() + +def get_registers(frame, kind): + """Returns the registers given the frame and the kind of registers desired. + + Returns None if there's no such kind. + """ + registerSet = frame.GetRegisters() # Return type of SBValueList. + for value in registerSet: + if kind.lower() in value.GetName().lower(): + return value + + return None + +def get_GPRs(frame): + """Returns the general purpose registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_GPRs + regs = get_GPRs(frame) + for reg in regs: + print "%s => %s" % (reg.GetName(), reg.GetValue()) + ... + """ + return get_registers(frame, "general purpose") + +def get_FPRs(frame): + """Returns the floating point registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_FPRs + regs = get_FPRs(frame) + for reg in regs: + print "%s => %s" % (reg.GetName(), reg.GetValue()) + ... + """ + return get_registers(frame, "floating point") + +def get_ESRs(frame): + """Returns the exception state registers of the frame as an SBValue. + + The returned SBValue object is iterable. An example: + ... + from lldbutil import get_ESRs + regs = get_ESRs(frame) + for reg in regs: + print "%s => %s" % (reg.GetName(), reg.GetValue()) + ... + """ + return get_registers(frame, "exception state") + +# ====================================== +# Utility classes/functions for SBValues +# ====================================== + +class BasicFormatter(object): + """The basic formatter inspects the value object and prints the value.""" + def format(self, value, buffer=None, indent=0): + if not buffer: + output = StringIO.StringIO() + else: + output = buffer + # If there is a summary, it suffices. + val = value.GetSummary() + # Otherwise, get the value. + if val == None: + val = value.GetValue() + if val == None and value.GetNumChildren() > 0: + val = "%s (location)" % value.GetLocation() + print >> output, "{indentation}({type}) {name} = {value}".format( + indentation = ' ' * indent, + type = value.GetTypeName(), + name = value.GetName(), + value = val) + return output.getvalue() + +class ChildVisitingFormatter(BasicFormatter): + """The child visiting formatter prints the value and its immediate children. + + The constructor takes a keyword arg: indent_child, which defaults to 2. + """ + def __init__(self, indent_child=2): + """Default indentation of 2 SPC's for the children.""" + self.cindent = indent_child + def format(self, value, buffer=None): + if not buffer: + output = StringIO.StringIO() + else: + output = buffer + + BasicFormatter.format(self, value, buffer=output) + for child in value: + BasicFormatter.format(self, child, buffer=output, indent=self.cindent) + + return output.getvalue() + +class RecursiveDecentFormatter(BasicFormatter): + """The recursive decent formatter prints the value and the decendents. + + The constructor takes two keyword args: indent_level, which defaults to 0, + and indent_child, which defaults to 2. The current indentation level is + determined by indent_level, while the immediate children has an additional + indentation by inden_child. + """ + def __init__(self, indent_level=0, indent_child=2): + self.lindent = indent_level + self.cindent = indent_child + def format(self, value, buffer=None): + if not buffer: + output = StringIO.StringIO() + else: + output = buffer + + BasicFormatter.format(self, value, buffer=output, indent=self.lindent) + new_indent = self.lindent + self.cindent + for child in value: + if child.GetSummary() != None: + BasicFormatter.format(self, child, buffer=output, indent=new_indent) + else: + if child.GetNumChildren() > 0: + rdf = RecursiveDecentFormatter(indent_level=new_indent) + rdf.format(child, buffer=output) + else: + BasicFormatter.format(self, child, buffer=output, indent=new_indent) + + return output.getvalue() diff --git a/utils/lui/lui.py b/utils/lui/lui.py new file mode 100755 index 000000000000..31b152c77f0d --- /dev/null +++ b/utils/lui/lui.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +##===-- lui.py -----------------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + + + +import curses + +import lldb +import lldbutil + +from optparse import OptionParser +import os +import signal +import sys + +import Queue + +import debuggerdriver +import cui + +import breakwin +import commandwin +import eventwin +import sourcewin +import statuswin + +event_queue = None + +def handle_args(driver, argv): + parser = OptionParser() + parser.add_option("-p", "--attach", dest="pid", help="Attach to specified Process ID", type="int") + parser.add_option("-c", "--core", dest="core", help="Load specified core file", type="string") + + (options, args) = parser.parse_args(argv) + + if options.pid is not None: + try: + pid = int(options.pid) + driver.attachProcess(ui, pid) + except ValueError: + print "Error: expecting integer PID, got '%s'" % options.pid + elif options.core is not None: + if not os.path.exists(options.core): + raise Exception("Specified core file '%s' does not exist." % options.core) + driver.loadCore(options.core) + elif len(args) == 2: + if not os.path.isfile(args[1]): + raise Exception("Specified target '%s' does not exist" % args[1]) + driver.createTarget(args[1]) + elif len(args) > 2: + if not os.path.isfile(args[1]): + raise Exception("Specified target '%s' does not exist" % args[1]) + driver.createTarget(args[1], args[2:]) + +def sigint_handler(signal, frame): + global debugger + debugger.terminate() + +class LLDBUI(cui.CursesUI): + def __init__(self, screen, event_queue, driver): + super(LLDBUI, self).__init__(screen, event_queue) + + self.driver = driver + + h, w = self.screen.getmaxyx() + + command_win_height = 20 + break_win_width = 60 + + self.status_win = statuswin.StatusWin(0, h-1, w, 1) + h -= 1 + self.command_win = commandwin.CommandWin(driver, 0, h - command_win_height, + w, command_win_height) + h -= command_win_height + self.source_win = sourcewin.SourceWin(driver, 0, 0, + w - break_win_width - 1, h) + self.break_win = breakwin.BreakWin(driver, w - break_win_width, 0, + break_win_width, h) + + + self.wins = [self.status_win, + #self.event_win, + self.source_win, + self.break_win, + self.command_win, + ] + + self.focus = len(self.wins) - 1 # index of command window; + + def handleEvent(self, event): + # hack + if isinstance(event, int): + if event == curses.KEY_F10: + self.driver.terminate() + if event == 20: # ctrl-T + def foo(cmd): + ret = lldb.SBCommandReturnObject() + self.driver.getCommandInterpreter().HandleCommand(cmd, ret) + foo('target create a.out') + foo('b main') + foo('run') + super(LLDBUI, self).handleEvent(event) + +def main(screen): + signal.signal(signal.SIGINT, sigint_handler) + + global event_queue + event_queue = Queue.Queue() + + global debugger + debugger = lldb.SBDebugger.Create() + + driver = debuggerdriver.createDriver(debugger, event_queue) + view = LLDBUI(screen, event_queue, driver) + + driver.start() + + # hack to avoid hanging waiting for prompts! + driver.handleCommand("settings set auto-confirm true") + + handle_args(driver, sys.argv) + view.eventLoop() + +if __name__ == "__main__": + try: + curses.wrapper(main) + except KeyboardInterrupt: + exit() diff --git a/utils/lui/sandbox.py b/utils/lui/sandbox.py new file mode 100755 index 000000000000..5a3a64cba692 --- /dev/null +++ b/utils/lui/sandbox.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +##===-- sandbox.py -------------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + + + +import curses + +import os +import signal +import sys + +import Queue + +import cui + +event_queue = None + +class SandboxUI(cui.CursesUI): + def __init__(self, screen, event_queue): + super(SandboxUI, self).__init__(screen, event_queue) + + height, width = self.screen.getmaxyx() + w2 = width / 2 + h2 = height / 2 + + self.wins = [] + #self.wins.append(cui.TitledWin(w2, h2, w2, h2, "Test Window 4")) + list_win = cui.ListWin(w2, h2, w2, h2) + for i in range(0, 40): + list_win.addItem('Item %s' % i) + self.wins.append(list_win) + self.wins.append(cui.TitledWin( 0, 0, w2, h2, "Test Window 1")) + self.wins.append(cui.TitledWin(w2, 0, w2, h2, "Test Window 2")) + self.wins.append(cui.TitledWin( 0, h2, w2, h2, "Test Window 3")) + + #def callback(s, content): + # self.wins[0].win.scroll(1) + # self.wins[0].win.addstr(10, 0, '%s: %s' % (s, content)) + # self.wins[0].win.scroll(1) + # self.el.showPrompt(10, 0) + + #self.wins[0].win.scrollok(1) + #self.el = cui.CursesEditLine(self.wins[0].win, None, + # lambda c: callback('got', c), lambda c: callback('tab', c)) + #self.el.prompt = '>>> ' + #self.el.showPrompt(10, 0) + + def handleEvent(self, event): + if isinstance(event, int): + if event == ord('q'): + sys.exit(0) + #self.el.handleEvent(event) + super(SandboxUI, self).handleEvent(event) + +def main(screen): + global event_queue + event_queue = Queue.Queue() + + sandbox = SandboxUI(screen, event_queue) + sandbox.eventLoop() + +if __name__ == "__main__": + try: + curses.wrapper(main) + except KeyboardInterrupt: + exit() diff --git a/utils/lui/sourcewin.py b/utils/lui/sourcewin.py new file mode 100644 index 000000000000..5e7067fdebe2 --- /dev/null +++ b/utils/lui/sourcewin.py @@ -0,0 +1,232 @@ +##===-- sourcewin.py -----------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + +import cui +import curses +import lldb, lldbutil +import re +import os + +class SourceWin(cui.TitledWin): + def __init__(self, driver, x, y, w, h): + super(SourceWin, self).__init__(x, y, w, h, "Source") + self.sourceman = driver.getSourceManager() + self.sources = {} + + self.filename= None + self.pc_line = None + self.viewline = 0 + + self.breakpoints = { } + + self.win.scrollok(1) + + self.markerPC = ":) " + self.markerBP = "B> " + self.markerNone = " " + + try: + from pygments.formatters import TerminalFormatter + self.formatter = TerminalFormatter() + except ImportError: + #self.win.addstr("\nWarning: no 'pygments' library found. Syntax highlighting is disabled.") + self.lexer = None + self.formatter = None + pass + + # FIXME: syntax highlight broken + self.formatter = None + self.lexer = None + + def handleEvent(self, event): + if isinstance(event, int): + self.handleKey(event) + return + + if isinstance(event, lldb.SBEvent): + if lldb.SBBreakpoint.EventIsBreakpointEvent(event): + self.handleBPEvent(event) + + if lldb.SBProcess.EventIsProcessEvent(event) and \ + not lldb.SBProcess.GetRestartedFromEvent(event): + process = lldb.SBProcess.GetProcessFromEvent(event) + if not process.IsValid(): + return + if process.GetState() == lldb.eStateStopped: + self.refreshSource(process) + elif process.GetState() == lldb.eStateExited: + self.notifyExited(process) + + def notifyExited(self, process): + self.win.erase() + target = lldbutil.get_description(process.GetTarget()) + pid = process.GetProcessID() + ec = process.GetExitStatus() + self.win.addstr("\nProcess %s [%d] has exited with exit-code %d" % (target, pid, ec)) + + def pageUp(self): + if self.viewline > 0: + self.viewline = self.viewline - 1 + self.refreshSource() + + def pageDown(self): + if self.viewline < len(self.content) - self.height + 1: + self.viewline = self.viewline + 1 + self.refreshSource() + pass + + def handleKey(self, key): + if key == curses.KEY_DOWN: + self.pageDown() + elif key == curses.KEY_UP: + self.pageUp() + + def updateViewline(self): + half = self.height / 2 + if self.pc_line < half: + self.viewline = 0 + else: + self.viewline = self.pc_line - half + 1 + + if self.viewline < 0: + raise Exception("negative viewline: pc=%d viewline=%d" % (self.pc_line, self.viewline)) + + def refreshSource(self, process = None): + (self.height, self.width) = self.win.getmaxyx() + + if process is not None: + loc = process.GetSelectedThread().GetSelectedFrame().GetLineEntry() + f = loc.GetFileSpec() + self.pc_line = loc.GetLine() + + if not f.IsValid(): + self.win.addstr(0, 0, "Invalid source file") + return + + self.filename = f.GetFilename() + path = os.path.join(f.GetDirectory(), self.filename) + self.setTitle(path) + self.content = self.getContent(path) + self.updateViewline() + + if self.filename is None: + return + + if self.formatter is not None: + from pygments.lexers import get_lexer_for_filename + self.lexer = get_lexer_for_filename(self.filename) + + bps = [] if not self.filename in self.breakpoints else self.breakpoints[self.filename] + self.win.erase() + if self.content: + self.formatContent(self.content, self.pc_line, bps) + + def getContent(self, path): + content = [] + if path in self.sources: + content = self.sources[path] + else: + if os.path.exists(path): + with open(path) as x: + content = x.readlines() + self.sources[path] = content + return content + + def formatContent(self, content, pc_line, breakpoints): + source = "" + count = 1 + self.win.erase() + end = min(len(content), self.viewline + self.height) + for i in range(self.viewline, end): + line_num = i + 1 + marker = self.markerNone + attr = curses.A_NORMAL + if line_num == pc_line: + attr = curses.A_REVERSE + if line_num in breakpoints: + marker = self.markerBP + line = "%s%3d %s" % (marker, line_num, self.highlight(content[i])) + if len(line) >= self.width: + line = line[0:self.width-1] + "\n" + self.win.addstr(line, attr) + source += line + count = count + 1 + return source + + def highlight(self, source): + if self.lexer and self.formatter: + from pygments import highlight + return highlight(source, self.lexer, self.formatter) + else: + return source + + def addBPLocations(self, locations): + for path in locations: + lines = locations[path] + if path in self.breakpoints: + self.breakpoints[path].update(lines) + else: + self.breakpoints[path] = lines + + def removeBPLocations(self, locations): + for path in locations: + lines = locations[path] + if path in self.breakpoints: + self.breakpoints[path].difference_update(lines) + else: + raise "Removing locations that were never added...no good" + + def handleBPEvent(self, event): + def getLocations(event): + locs = {} + + bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event) + + if bp.IsInternal(): + # don't show anything for internal breakpoints + return + + for location in bp: + # hack! getting the LineEntry via SBBreakpointLocation.GetAddress.GetLineEntry does not work good for + # inlined frames, so we get the description (which does take into account inlined functions) and parse it. + desc = lldbutil.get_description(location, lldb.eDescriptionLevelFull) + match = re.search('at\ ([^:]+):([\d]+)', desc) + try: + path = match.group(1) + line = int(match.group(2).strip()) + except ValueError as e: + # bp loc unparsable + continue + + if path in locs: + locs[path].add(line) + else: + locs[path] = set([line]) + return locs + + event_type = lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event) + if event_type == lldb.eBreakpointEventTypeEnabled \ + or event_type == lldb.eBreakpointEventTypeAdded \ + or event_type == lldb.eBreakpointEventTypeLocationsResolved \ + or event_type == lldb.eBreakpointEventTypeLocationsAdded: + self.addBPLocations(getLocations(event)) + elif event_type == lldb.eBreakpointEventTypeRemoved \ + or event_type == lldb.eBreakpointEventTypeLocationsRemoved \ + or event_type == lldb.eBreakpointEventTypeDisabled: + self.removeBPLocations(getLocations(event)) + elif event_type == lldb.eBreakpointEventTypeCommandChanged \ + or event_type == lldb.eBreakpointEventTypeConditionChanged \ + or event_type == lldb.eBreakpointEventTypeIgnoreChanged \ + or event_type == lldb.eBreakpointEventTypeThreadChanged \ + or event_type == lldb.eBreakpointEventTypeInvalidType: + # no-op + pass + self.refreshSource() + + diff --git a/utils/lui/statuswin.py b/utils/lui/statuswin.py new file mode 100644 index 000000000000..c6f6c990b34d --- /dev/null +++ b/utils/lui/statuswin.py @@ -0,0 +1,40 @@ +##===-- statuswin.py -----------------------------------------*- Python -*-===## +## +## The LLVM Compiler Infrastructure +## +## This file is distributed under the University of Illinois Open Source +## License. See LICENSE.TXT for details. +## +##===----------------------------------------------------------------------===## + +import lldb, lldbutil +import cui +import curses + +class StatusWin(cui.TextWin): + def __init__(self, x, y, w, h): + super(StatusWin, self).__init__(x, y, w) + + self.keys = [#('F1', 'Help', curses.KEY_F1), + ('F3', 'Cycle-focus', curses.KEY_F3), + ('F10', 'Quit', curses.KEY_F10)] + + def draw(self): + self.win.addstr(0, 0, '') + for key in self.keys: + self.win.addstr('{0}'.format(key[0]), curses.A_REVERSE) + self.win.addstr(' {0} '.format(key[1]), curses.A_NORMAL) + super(StatusWin, self).draw() + + def handleEvent(self, event): + if isinstance(event, int): + pass + elif isinstance(event, lldb.SBEvent): + if lldb.SBProcess.EventIsProcessEvent(event): + state = lldb.SBProcess.GetStateFromEvent(event) + status = lldbutil.state_type_to_str(state) + self.win.erase() + x = self.win.getmaxyx()[1] - len(status) - 1 + self.win.addstr(0, x, status) + return + diff --git a/utils/misc/grep-svn-log.py b/utils/misc/grep-svn-log.py new file mode 100755 index 000000000000..ebbfe33f0520 --- /dev/null +++ b/utils/misc/grep-svn-log.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +""" +Greps and returns the first svn log entry containing a line matching the regular +expression pattern passed as the only arg. + +Example: + +svn log -v | grep-svn-log.py '^ D.+why_are_you_missing.h$' +""" + +import fileinput, re, sys, StringIO + +# Separator string for "svn log -v" output. +separator = '-' * 72 + +usage = """Usage: grep-svn-log.py line-pattern +Example: + svn log -v | grep-svn-log.py '^ D.+why_are_you_missing.h'""" + +class Log(StringIO.StringIO): + """Simple facade to keep track of the log content.""" + def __init__(self): + self.reset() + def add_line(self, a_line): + """Add a line to the content, if there is a previous line, commit it.""" + global separator + if self.prev_line != None: + print >> self, self.prev_line + self.prev_line = a_line + self.separator_added = (a_line == separator) + def del_line(self): + """Forget about the previous line, do not commit it.""" + self.prev_line = None + def reset(self): + """Forget about the previous lines entered.""" + StringIO.StringIO.__init__(self) + self.prev_line = None + def finish(self): + """Call this when you're finished with populating content.""" + if self.prev_line != None: + print >> self, self.prev_line + self.prev_line = None + +def grep(regexp): + # The log content to be written out once a match is found. + log = Log() + + LOOKING_FOR_MATCH = 0 + FOUND_LINE_MATCH = 1 + state = LOOKING_FOR_MATCH + + while 1: + line = sys.stdin.readline() + if not line: + return + line = line.splitlines()[0] + if state == FOUND_LINE_MATCH: + # At this state, we keep on accumulating lines until the separator + # is encountered. At which point, we can return the log content. + if line == separator: + log.finish() + print log.getvalue() + return + log.add_line(line) + + elif state == LOOKING_FOR_MATCH: + if line == separator: + log.reset() + log.add_line(line) + # Update next state if necessary. + if regexp.search(line): + state = FOUND_LINE_MATCH + +def main(): + if len(sys.argv) != 2: + print usage + sys.exit(0) + + regexp = re.compile(sys.argv[1]) + grep(regexp) + sys.stdin.close() + +if __name__ == '__main__': + main() diff --git a/utils/sync-source/README.txt b/utils/sync-source/README.txt new file mode 100644 index 000000000000..9e019b64d9ad --- /dev/null +++ b/utils/sync-source/README.txt @@ -0,0 +1,293 @@ +syncsource.py + +OVERVIEW + +The syncsource.py utility transfers groups of files between +computers. The primary use case is to enable developing LLVM project +software on one machine, transfer it efficiently to other machines --- +possibly of other architectures --- and test it there. syncsource.py +supports configurable, named source-to-destination mappings and has a +transfer agent plug-in architecture. The current distribution provides +an rsync-over-ssh transfer agent. + +The primary benefits of using syncsource.py are: + +* Provides a simple, reliable way to get a mirror copy of primary- + machine files onto several different destinations without concern + of compromising the patch during testing on different machines. + +* Handles directory-mapping differences between two machines. For + LLDB, this is helpful when going between OS X and any other non-OS X + target system. + +EXAMPLE WORKFLOW + +This utility was developed in the context of working on the LLDB +project. Below we show the transfers we'd like to have happen, +and the configuration that supports it. + +Workflow Example: + +* Develop on OS X (primary machine) +* Test candidate changes on OS X. +* Test candidate changes on a Linux machine (machine-name: lldb-linux). +* Test candidate changes on a FreeBSD machine (machine-name: lldb-freebsd). +* Do check-ins from OS X machine. + +Requirements: + +* OS X machine requires the lldb source layout: lldb, lldb/llvm, + lldb/llvm/tools/clang. Note this is different than the canonical + llvm, llvm/tools/clang, llvm/tools/lldb layout that we'll want on + the Linux and FreeBSD test machines. + +* Linux machine requires the llvm, llvm/tools/clang and + llvm/tools/lldb layout. + +* FreeBSD machine requires the same layout as the llvm machine. + +syncsource.py configuration in ~/.syncsourcerc: + +# This is my configuration with a comment. Configuration +# files are JSON-based. +{ "configurations": [ + # Here we have a collection of named configuration blocks. + # Configuration blocks can chain back to a parent by name. + { + # Every block has a name so it can be referenced from + # the command line or chained back to by a child block + # for sharing. + "name": "base_tot_settings", + + # This directive lists the "directory ids" that we'll care + # about. If your local repository has additional directories + # for other projects that need to ride along, add them here. + # For defaulting purposes, it makes sense to name the + # directory IDs as the most likely name for the directory + # itself. For stock LLDB from top of tree, we generally only + # care about lldb, llvm and clang. + "dir_names": [ "llvm", "clang", "lldb" ], + + # This section describes where the source files live on + # the primary machine. There should always be a base_dir + # entry, which indicates where in the local filesystem the + # projects are rooted. For each dir in dir_names, there + # should be either: + # 1. an entry named {dir-id}_dir (e.g. llvm_dir), which + # specifies the source directory for the given dir id + # relative to the base_dir entry, OR + # 2. no entry, in which case the directory is assumed to + # be the same as {dir-id}. In the example below, the + # base_dir-relative directory for the "lldb" dir-id is + # defaulted to being "lldb". That's exactly what + # we need in an OS X-style lldb dir layout. + "source": { + "base_dir": "~/work/lldb-tot", + "llvm_dir": "lldb/llvm", + "clang_dir": "lldb/llvm/tools/clang" + }, + + # source_excludes covers any exclusions that: + # * should be applied when copying files from the source + # * should be excluded from deletion on the destination + # + # By default, ".git", ".svn" and ".pyc" are added to + # all dir-id exclusions. The default excludes can be + # controlled by the syncsource.py --default-excludes + # option. + # + # Below, I have transfer of the lldb dir skip everything + # rooted at "/llvm" below the the lldb dir. This is + # because we want the source OS X lldb to move to + # a destination of {some-dest-root}/llvm/tools/lldb, and + # not have the OS-X-inverted llvm copy over with the lldb + # transfer portion. We'll see the complete picture of + # how this works when we get to specifying destinations + # later on in the config. + # + # We also exclude the "/build" and "/llvm-build" dir rooted in + # the OS X-side sources. The Xcode configuration on this + # OS X machine will dump lldb builds in the /build directory + # relative to the lldb dir, and it will build llvm+clang in + # the /llvm-build dir relative to the lldb dir. + # + # Note the first forward slash in "/build" indicates to the + # transfer agent that we only want to exclude the + # ~/work/lldb-tot/lldb/build dir, not just any file or + # directory named "build" somewhere underneath the lldb + # directory. Without the leading forward slash, any file + # or directory called build anywhere underneath the lldb dir + # will be excluded, which is definitely not what we want here. + # + # For the llvm dir, we do a source-side exclude for + # "/tools/clang". We manage the clang transfer as a separate + # entity, so we don't want the movement of llvm to also move + # clang. + # + # The llvm_dir exclusion of "/tools/lldb" is the first example + # of an exclude targeting a requirement of the destination + # side. Normally the transfer agent will delete anything on + # the destination that is not present on the source. It is + # trying to mirror, and ensure both sides have the same + # content. The source side of llvm on OS X does not have a + # "/tools/lldb", so at first this exclude looks non-sensical. + # But on the canonical destination layout, lldb lives in + # {some-dest-root}/llvm/tools/lldb. Without this exclude, + # the transfer agent would blow away the tools/lldb directory + # on the destination every time we transfer, and then have to + # copy the lldb dir all over again. For rsync+ssh, that + # totally would defeat the huge transfer efficiencies gained + # by using rsync in the first place. + # + # Note the overloading of both source and dest style excludes + # ultimately comes from the rsync-style exclude mechanism. + # If it wasn't for that, I would have separated source and + # dest excludes out better. + "source_excludes": { + "lldb_dir": ["/llvm", "/build", "/llvm-build"], + "llvm_dir": ["/tools/lldb", "/tools/clang"] + } + }, + + # Top of tree public, common settings for all destinations. + { + # The name for this config block. + "name": "common_tot", + + # Here is our first chaining back to a parent config block. + # Any settings in "common_tot" not specified here are going + # to be retrieved from the parent. + "parent": "base_tot_settings", + + # The transfer agent class to use. Right now, the only one + # available is this one here that uses rsync over ssh. + # If some other mechanism is needed to reach this destination, + # it can be specified here in full [[package.]module.]class form. + "transfer_class": "transfer.rsync.RsyncOverSsh", + + # Specifies the destination-root-relative directories. + # Here our desination is rooted at: + # {some-yet-to-be-specified-destination-root} + "base_dir". + # In other words, each destination will have some kind of root + # for all relative file placement. We'll see those defined + # later, as they can change per destination machine. + # The block below describes the settings relative to that + # destination root. + # + # As before, each dir-id used in this configuration is + # expected to have either: + # 1. an entry named {dir-id}_dir (e.g. llvm_dir), which + # specifies the destination directory for the given dir id + # relative to the dest_root+base_dir entries, OR + # 2. no entry, in which case the directory is assumed to + # be the same as {dir-id}. In the example below, the + # dest_root+base_dir-relative directory for the "llvm" dir-id is + # defaulted to being "llvm". That's exactly what + # we need in a canonical llvm/clang/lldb setup on + # Linux/FreeBSD. + # + # Note we see the untangling of the OS X lldb-centric + # directory structure to the canonical llvm, + # llvm/tools/clang, llvm/tools/lldb structure below. + # We are mapping lldb into a subdirectory of the llvm + # directory. + # + # The transfer logic figures out which directories to copy + # first by finding the shortest destination absolute path + # and doing them in that order. For our case, this ensures + # llvm is copied over before lldb or clang. + "dest": { + "base_dir": "work/mirror/git", + "lldb_dir": "llvm/tools/lldb", + "clang_dir": "llvm/tools/clang" + } + }, + + # Describe the lldb-linux destination. With this, + # we're done with the mapping for transfer setup + # for the lldb-linux box. This configuration can + # be used either by: + # 1. having a parent "default" blockthat points to this one, + # which then gets used by default, or + # 2. using the --configuration/-c CONFIG option to + # specify using this name on the syncsource.py command line. + { + "name": "lldb-linux" + "parent": "common_tot", + + # The ssh block is understood by the rsync+ssh transfer + # agent. Other agents would probably require different + # agent-specific details that they could read from + # other blocks. + "ssh": { + # This specifies the host name (or IP address) as would + # be used as the target for an ssh command. + "dest_host": "lldb-linux.example.com", + + # root_dir specifies the global root directory for + # this destination. All destinations on this target + # will be in a directory that is built from + # root_dir + base_dir + {dir_id}_dir. + "root_dir" : "/home/tfiala", + + # The ssh user is specified here. + "user": "tfiala", + + # The ssh port is specified here. + "port": 22 + } + }, + + # Describe the lldb-freebsd destination. + # Very similar to the lldb-linux one. + { + "name": "lldb-freebsd" + "parent": "common_tot", + "ssh": { + "dest_host": "lldb-freebsd.example.com", + # Specify a different destination-specific root dir here. + "root_dir" : "/mnt/ssd02/fialato", + "user": "fialato", + # The ssh port is specified here. + "port": 2022 + } + }, + + # If a block named "default" exists, and if no configuration + # is specified on the command line, then the default block + # will be used. Use this block to point to the most common + # transfer destination you would use. + { + "name": "default", + "parent": "lldb-linux" + } +] +} + +Using it + +Now that we have a .syncsourcerc file set up, we can do a transfer. +The .syncsourcerc file will be searched for as follows, using the +first one that is found: + +* First check the --rc-file RCFILE option. If this is specified + and doesn't exist, it will raise an error and quit. + +* Check if the current directory has a .syncsourcerc file. If so, + use that. + +* Use the .syncsourcerc file from the user's home directory. + +Run the command: +python /path/to/syncsource.rc -c {configuration-name} + +The -c {configuration-name} can be left off, in which case a +configuration with the name 'default' will be used. + +After that, the transfer will occur. With the rsync-over-ssh +transfer agent, one rsync per dir-id will be used. rsync output +is redirected to the console. + +FEEDBACK + +Feel free to pass feedback along to Todd Fiala (todd.fiala@gmail.com). diff --git a/utils/sync-source/lib/transfer/__init__.py b/utils/sync-source/lib/transfer/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/utils/sync-source/lib/transfer/protocol.py b/utils/sync-source/lib/transfer/protocol.py new file mode 100644 index 000000000000..6a4e6e0a3a45 --- /dev/null +++ b/utils/sync-source/lib/transfer/protocol.py @@ -0,0 +1,7 @@ +class Protocol(object): + def __init__(self, options, config): + self.options = options + self.config = config + + def transfer(transfer_specs, dry_run): + raise "transfer must be overridden by transfer implementation" diff --git a/utils/sync-source/lib/transfer/rsync.py b/utils/sync-source/lib/transfer/rsync.py new file mode 100644 index 000000000000..7a89344b6991 --- /dev/null +++ b/utils/sync-source/lib/transfer/rsync.py @@ -0,0 +1,60 @@ +import os.path +import pprint +import subprocess +import sys + +import transfer.protocol + + +class RsyncOverSsh(transfer.protocol.Protocol): + def __init__(self, options, config): + super(RsyncOverSsh, self).__init__(options, config) + self.ssh_config = config.get_value("ssh") + + def build_rsync_command(self, transfer_spec, dry_run): + dest_path = os.path.join( + self.ssh_config["root_dir"], + transfer_spec.dest_path) + flags = "-avz" + if dry_run: + flags += "n" + cmd = [ + "rsync", + flags, + "-e", + "ssh -p {}".format(self.ssh_config["port"]), + "--rsync-path", + # The following command needs to know the right way to do + # this on the dest platform - ensures the target dir exists. + "mkdir -p {} && rsync".format(dest_path) + ] + + # Add source dir exclusions + if transfer_spec.exclude_paths: + for exclude_path in transfer_spec.exclude_paths: + cmd.append("--exclude") + cmd.append(exclude_path) + + cmd.extend([ + "--delete", + transfer_spec.source_path + "/", + "{}@{}:{}".format( + self.ssh_config["user"], + self.ssh_config["dest_host"], + dest_path)]) + return cmd + + def transfer(self, transfer_specs, dry_run): + if self.options.verbose: + printer = pprint.PrettyPrinter() + for spec in transfer_specs: + printer.pprint(spec) + + for spec in transfer_specs: + cmd = self.build_rsync_command(spec, dry_run) + if self.options.verbose: + print "executing the following command:\n{}".format(cmd) + result = subprocess.call( + cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) + if result != 0: + return result diff --git a/utils/sync-source/lib/transfer/transfer_spec.py b/utils/sync-source/lib/transfer/transfer_spec.py new file mode 100644 index 000000000000..cc76c70bf41c --- /dev/null +++ b/utils/sync-source/lib/transfer/transfer_spec.py @@ -0,0 +1,11 @@ +class TransferSpec(object): + def __init__(self, source_path, exclude_paths, dest_path): + self.source_path = source_path + self.exclude_paths = exclude_paths + self.dest_path = dest_path + + def __repr__(self): + fmt = ( + "TransferSpec(source_path='{}', exclude_paths='{}', " + "dest_path='{}')") + return fmt.format(self.source_path, self.exclude_paths, self.dest_path) diff --git a/utils/sync-source/pylintrc b/utils/sync-source/pylintrc new file mode 100644 index 000000000000..d20e5bd4dea4 --- /dev/null +++ b/utils/sync-source/pylintrc @@ -0,0 +1,2 @@ +[Master] +init-hook='import os; import sys; sys.path.append(os.path.join(os.getcwd(), "lib")); print("hello from {}".format(os.getcwd()))' diff --git a/utils/sync-source/syncsource.py b/utils/sync-source/syncsource.py new file mode 100644 index 000000000000..736aefd9a35c --- /dev/null +++ b/utils/sync-source/syncsource.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python +""" + The LLVM Compiler Infrastructure + +This file is distributed under the University of Illinois Open Source +License. See LICENSE.TXT for details. + +Sync lldb and related source from a local machine to a remote machine. + +This facilitates working on the lldb sourcecode on multiple machines +and multiple OS types, verifying changes across all. +""" + +import argparse +import cStringIO +import importlib +import json +import os.path +import re +import sys + +# Add the local lib directory to the python path. +LOCAL_LIB_PATH = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "lib") +sys.path.append(LOCAL_LIB_PATH) + +import transfer.transfer_spec + + +DOTRC_BASE_FILENAME = ".syncsourcerc" + + +class Configuration(object): + """Provides chaining configuration lookup.""" + def __init__(self, rcdata_configs): + self.__rcdata_configs = rcdata_configs + + def get_value(self, key): + """ + Return the first value in the parent chain that has the key. + + The traversal starts from the most derived configuration (i.e. + child) and works all the way up the parent chain. + + @return the value of the first key in the parent chain that + contains a value for the given key. + """ + for config in self.__rcdata_configs: + if key in config: + return config[key] + return None + + def __getitem__(self, key): + value = self.get_value(key) + if value: + return value + else: + raise KeyError(key) + + +def parse_args(): + """@return options parsed from the command line.""" + parser = argparse.ArgumentParser() + parser.add_argument( + "--config-name", "-c", action="store", default="default", + help="specify configuration name to use") + parser.add_argument( + "--default-excludes", action="store", default="*.git,*.svn,*.pyc", + help=("comma-separated list of default file patterns to exclude " + "from each source directory and to protect from deletion " + "on each destination directory; if starting with forward " + "slash, it only matches at the top of the base directory")) + parser.add_argument( + "--dry-run", "-n", action="store_true", + help="do a dry run of the transfer operation, don't really transfer") + parser.add_argument( + "--rc-file", "-r", action="store", + help="specify the sync-source rc file to use for configurations") + parser.add_argument( + "--verbose", "-v", action="store_true", help="turn on verbose output") + return parser.parse_args() + + +def read_rcfile(filename): + """Returns the json-parsed contents of the input file.""" + + # First parse file contents, removing all comments but + # preserving the line count. + regex = re.compile(r"#.*$") + + comment_stripped_file = cStringIO.StringIO() + with open(filename, "r") as json_file: + for line in json_file: + comment_stripped_file.write(regex.sub("", line)) + return json.load(cStringIO.StringIO(comment_stripped_file.getvalue())) + + +def find_appropriate_rcfile(options): + # Use an options-specified rcfile if specified. + if options.rc_file and len(options.rc_file) > 0: + if not os.path.isfile(options.rc_file): + # If it doesn't exist, error out here. + raise "rcfile '{}' specified but doesn't exist".format( + options.rc_file) + return options.rc_file + + # Check if current directory .sync-sourcerc exists. If so, use it. + local_rc_filename = os.path.abspath(DOTRC_BASE_FILENAME) + if os.path.isfile(local_rc_filename): + return local_rc_filename + + # Check if home directory .sync-sourcerc exists. If so, use it. + homedir_rc_filename = os.path.abspath( + os.path.join(os.path.expanduser("~"), DOTRC_BASE_FILENAME)) + if os.path.isfile(homedir_rc_filename): + return homedir_rc_filename + + # Nothing matched. We don't have an rc filename candidate. + return None + + +def get_configuration(options, rcdata, config_name): + rcdata_configs = [] + next_config_name = config_name + while next_config_name: + # Find the next rcdata configuration for the given name. + rcdata_config = next( + config for config in rcdata["configurations"] + if config["name"] == next_config_name) + + # See if we found it. + if rcdata_config: + # This is our next configuration to use in the chain. + rcdata_configs.append(rcdata_config) + + # If we have a parent, check that next. + if "parent" in rcdata_config: + next_config_name = rcdata_config["parent"] + else: + next_config_name = None + else: + raise "failed to find specified parent config '{}'".format( + next_config_name) + return Configuration(rcdata_configs) + + +def create_transfer_agent(options, configuration): + transfer_class_spec = configuration.get_value("transfer_class") + if options.verbose: + print "specified transfer class: '{}'".format(transfer_class_spec) + + # Load the module (possibly package-qualified). + components = transfer_class_spec.split(".") + module = importlib.import_module(".".join(components[:-1])) + + # Create the class name we need to load. + clazz = getattr(module, components[-1]) + return clazz(options, configuration) + + +def sync_configured_sources(options, configuration, default_excludes): + # Look up the transfer method. + transfer_agent = create_transfer_agent(options, configuration) + + # For each configured dir_names source, do the following transfer: + # 1. Start with base_dir + {source-dir-name}_dir + # 2. Copy all files recursively, but exclude + # all dirs specified by source_excludes: + # skip all base_dir + {source-dir-name}_dir + + # {source-dir-name}_dir excludes. + source_dirs = configuration.get_value("source") + source_excludes = configuration.get_value("source_excludes") + dest_dirs = configuration.get_value("dest") + + source_base_dir = source_dirs["base_dir"] + dest_base_dir = dest_dirs["base_dir"] + dir_ids = configuration.get_value("dir_names") + transfer_specs = [] + + for dir_id in dir_ids: + dir_key = "{}_dir".format(dir_id) + + # Build the source dir (absolute) that we're copying from. + # Defaults the base-relative source dir to the source id (e.g. lldb) + rel_source_dir = source_dirs.get(dir_key, dir_id) + transfer_source_dir = os.path.expanduser( + os.path.join(source_base_dir, rel_source_dir)) + + # Exclude dirs do two things: + # 1) stop items from being copied on the source side, and + # 2) protect things from being deleted on the dest side. + # + # In both cases, they are specified relative to the base + # directory on either the source or dest side. + # + # Specifying a leading '/' in the directory will limit it to + # be rooted in the base directory. i.e. "/.git" will only + # match {base-dir}/.git, not {base-dir}/subdir/.git, but + # ".svn" will match {base-dir}/.svn and + # {base-dir}/subdir/.svn. + # + # If excludes are specified for this dir_id, then pass along + # the excludes. These are relative to the dir_id directory + # source, and get passed along that way as well. + transfer_source_excludes = [] + + # Add the source excludes for this dir. + skip_defaults = False + if source_excludes and dir_key in source_excludes: + transfer_source_excludes.extend(source_excludes[dir_key]) + if "" in source_excludes[dir_key]: + skip_defaults = True + transfer_source_excludes.remove("") + + if not skip_defaults and default_excludes is not None: + transfer_source_excludes.extend(list(default_excludes)) + + # Build the destination-base-relative dest dir into which + # we'll be syncing. Relative directory defaults to the + # dir id + rel_dest_dir = dest_dirs.get(dir_key, dir_id) + transfer_dest_dir = os.path.join(dest_base_dir, rel_dest_dir) + + # Add the exploded paths to the list that we'll ask the + # transfer agent to transfer for us. + transfer_specs.append( + transfer.transfer_spec.TransferSpec( + transfer_source_dir, + transfer_source_excludes, + transfer_dest_dir)) + + # Do the transfer. + if len(transfer_specs) > 0: + transfer_agent.transfer(transfer_specs, options.dry_run) + else: + raise Exception("nothing to transfer, bad configuration?") + + +def main(): + """Drives the main program.""" + options = parse_args() + + if options.default_excludes and len(options.default_excludes) > 0: + default_excludes = options.default_excludes.split(",") + else: + default_excludes = [] + + # Locate the rc filename to load, then load it. + rc_filename = find_appropriate_rcfile(options) + if rc_filename: + if options.verbose: + print "reading rc data from file '{}'".format(rc_filename) + rcdata = read_rcfile(rc_filename) + else: + sys.stderr.write("no rcfile specified, cannot guess configuration") + exit(1) + + # Find configuration. + configuration = get_configuration(options, rcdata, options.config_name) + if not configuration: + sys.stderr.write("failed to find configuration for {}".format( + options.config_data)) + exit(2) + + # Kick off the transfer. + sync_configured_sources(options, configuration, default_excludes) + +if __name__ == "__main__": + main() diff --git a/utils/test/README-disasm b/utils/test/README-disasm new file mode 100644 index 000000000000..00e9ab681a24 --- /dev/null +++ b/utils/test/README-disasm @@ -0,0 +1,406 @@ +This README describes a sample invocation of disasm.py whose purpose is to test +the low level ARM/Thumb disassembly functionality from llvm using the llvm-mc +command line. We invoke gdb on an executable, try to disassemble a function, +and then read the memory contents of the disassembled function. + +The byte contents are written into a file named disasm-input.txt and then we +invoke llvm-mc -disassemble plus options (set with the -o/--options) on the +byte contents. + +See the following for a sample session using this command: + +[16:26:57] johnny:/Volumes/data/Radar/9131529 $ /Volumes/data/lldb/svn/trunk/utils/test/disasm.py -C 'set shlib-path-substitutions /usr /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr /System /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System /Library /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/Library' -O '-arch armv7' -m /Volumes/data/lldb/llvm/Debug+Asserts/bin/llvm-mc -e /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib -f printf --options='-triple=thumb-apple-darwin -debug-only=arm-disassembler' +gdb commands: ['set shlib-path-substitutions /usr /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr /System /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System /Library /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/Library'] +gdb options: -arch armv7 +executable: /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib +function: printf +llvm-mc: /Volumes/data/lldb/llvm/Debug+Asserts/bin/llvm-mc +llvm-mc options: -triple=thumb-apple-darwin -debug-only=arm-disassembler +GNU gdb 6.3.50-20050815 (Apple version gdb-1518) (Sat Feb 12 02:56:02 UTC 2011) +Copyright 2004 Free Software Foundation, Inc. +GDB is free software, covered by the GNU General Public License, and you are +welcome to change it and/or distribute copies of it under certain conditions. +Type "show copying" to see the conditions. +There is absolutely no warranty for GDB. Type "show warranty" for details. +This GDB was configured as "--host=x86_64-apple-darwin --target=arm-apple-darwin". +: push {r0, r1, r2, r3} +0x0704cdd2 : push {r4, r5, r7, lr} +0x0704cdd4 : add r7, sp, #8 +0x0704cdd6 : sub sp, #4 +0x0704cdd8 : add r3, sp, #20 +0x0704cdda : ldr.w r5, [r3], #4 +0x0704cdde : str r3, [sp, #0] +0x0704cde0 : ldr r3, [pc, #52] (0x704ce18 ) +0x0704cde2 : add r3, pc +0x0704cde4 : ldr r0, [r3, #0] +0x0704cde6 : ldr r4, [r0, #0] +0x0704cde8 : ldr r0, [pc, #48] (0x704ce1c ) +0x0704cdea : add r0, pc +0x0704cdec : ldr r0, [r0, #0] +0x0704cdee : ldr r0, [r0, #0] +0x0704cdf0 : blx 0x707ba30 +0x0704cdf4 : cbnz r0, 0x704cdfe +0x0704cdf6 : ldr r1, [pc, #40] (0x704ce20 ) +0x0704cdf8 : add r1, pc +0x0704cdfa : ldr r1, [r1, #0] +0x0704cdfc : b.n 0x704ce00 +0x0704cdfe : mov r1, r0 +0x0704ce00 : mov r0, r4 +0x0704ce02 : mov r2, r5 +0x0704ce04 : ldr r3, [sp, #0] +0x0704ce06 : bl 0x704ad44 +0x0704ce0a : sub.w sp, r7, #8 ; 0x8 +0x0704ce0e : ldmia.w sp!, {r4, r5, r7, lr} +0x0704ce12 : add sp, #16 +0x0704ce14 : bx lr +0x0704ce16 : nop +0x0704ce18 : movs r3, #142 +0x0704ce1a : lsls r5, r0, #0 +0x0704ce1c : adds r1, #122 +0x0704ce1e : lsls r5, r0, #0 +0x0704ce20 : adds r1, #104 +0x0704ce22 : lsls r5, r0, #0 +End of assembler dump. +(gdb) x /2b 0x0704cdd0 +0x704cdd0 : 0x0f 0xb4 +(gdb) x /2b 0x0704cdd2 +0x704cdd2 : 0xb0 0xb5 +(gdb) x /2b 0x0704cdd4 +0x704cdd4 : 0x02 0xaf +(gdb) x /2b 0x0704cdd6 +0x704cdd6 : 0x81 0xb0 +(gdb) x /2b 0x0704cdd8 +0x704cdd8 : 0x05 0xab +(gdb) x /4b 0x0704cdda +0x704cdda : 0x53 0xf8 0x04 0x5b +(gdb) x /2b 0x0704cdde +0x704cdde : 0x00 0x93 +(gdb) x /2b 0x0704cde0 +0x704cde0 : 0x0d 0x4b +(gdb) x /2b 0x0704cde2 +0x704cde2 : 0x7b 0x44 +(gdb) x /2b 0x0704cde4 +0x704cde4 : 0x18 0x68 +(gdb) x /2b 0x0704cde6 +0x704cde6 : 0x04 0x68 +(gdb) x /2b 0x0704cde8 +0x704cde8 : 0x0c 0x48 +(gdb) x /2b 0x0704cdea +0x704cdea : 0x78 0x44 +(gdb) x /2b 0x0704cdec +0x704cdec : 0x00 0x68 +(gdb) x /2b 0x0704cdee +0x704cdee : 0x00 0x68 +(gdb) x /4b 0x0704cdf0 +0x704cdf0 : 0x2e 0xf0 0x1e 0xee +(gdb) x /2b 0x0704cdf4 +0x704cdf4 : 0x18 0xb9 +(gdb) x /2b 0x0704cdf6 +0x704cdf6 : 0x0a 0x49 +(gdb) x /2b 0x0704cdf8 +0x704cdf8 : 0x79 0x44 +(gdb) x /2b 0x0704cdfa +0x704cdfa : 0x09 0x68 +(gdb) x /2b 0x0704cdfc +0x704cdfc : 0x00 0xe0 +(gdb) x /2b 0x0704cdfe +0x704cdfe : 0x01 0x46 +(gdb) x /2b 0x0704ce00 +0x704ce00 : 0x20 0x46 +(gdb) x /2b 0x0704ce02 +0x704ce02 : 0x2a 0x46 +(gdb) x /2b 0x0704ce04 +0x704ce04 : 0x00 0x9b +(gdb) x /4b 0x0704ce06 +0x704ce06 : 0xfd 0xf7 0x9d 0xff +(gdb) x /4b 0x0704ce0a +0x704ce0a : 0xa7 0xf1 0x08 0x0d +(gdb) x /4b 0x0704ce0e +0x704ce0e : 0xbd 0xe8 0xb0 0x40 +(gdb) x /2b 0x0704ce12 +0x704ce12 : 0x04 0xb0 +(gdb) x /2b 0x0704ce14 +0x704ce14 : 0x70 0x47 +(gdb) x /2b 0x0704ce16 +0x704ce16 : 0x00 0xbf +(gdb) x /2b 0x0704ce18 +0x704ce18 : 0x8e 0x23 +(gdb) x /2b 0x0704ce1a +0x704ce1a : 0x05 0x00 +(gdb) x /2b 0x0704ce1c +0x704ce1c : 0x7a 0x31 +(gdb) x /2b 0x0704ce1e +0x704ce1e : 0x05 0x00 +(gdb) x /2b 0x0704ce20 +0x704ce20 : 0x68 0x31 +(gdb) x /2b 0x0704ce22 +0x704ce22 : 0x05 0x00 +(gdb) quit + +Executing command: /Volumes/data/lldb/llvm/Debug+Asserts/bin/llvm-mc -disassemble -triple=thumb-apple-darwin -debug-only=arm-disassembler disasm-input.txt +Opcode=2305 Name=tPUSH Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 1: 0: 0| 0: 0: 0: 0| 1: 1: 1: 1| +------------------------------------------------------------------------------------------------- + + push {r0, r1, r2, r3} +Opcode=2305 Name=tPUSH Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 1: 0: 1| 1: 0: 1: 1| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + push {r4, r5, r7, lr} +Opcode=2228 Name=tADDrSPi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 0| 1: 1: 1: 1| 0: 0: 0: 0| 0: 0: 1: 0| +------------------------------------------------------------------------------------------------- + + add r7, sp, #8 +Opcode=2328 Name=tSUBspi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 0: 0: 0| 1: 0: 0: 0| 0: 0: 0: 1| +------------------------------------------------------------------------------------------------- + + sub sp, #4 +Opcode=2228 Name=tADDrSPi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 0| 1: 0: 1: 1| 0: 0: 0: 0| 0: 1: 0: 1| +------------------------------------------------------------------------------------------------- + + add r3, sp, #20 +Opcode=1963 Name=t2LDR_POST Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 1: 1: 1: 1| 1: 0: 0: 0| 0: 1: 0: 1| 0: 0: 1: 1| 0: 1: 0: 1| 1: 0: 1: 1| 0: 0: 0: 0| 0: 1: 0: 0| +------------------------------------------------------------------------------------------------- + + ldr r5, [r3], #4 +Opcode=2324 Name=tSTRspi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 0: 1| 0: 0: 1: 1| 0: 0: 0: 0| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + str r3, [sp] +Opcode=2275 Name=tLDRpci Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 1: 0: 1: 1| 0: 0: 0: 0| 1: 1: 0: 1| +------------------------------------------------------------------------------------------------- + + ldr.n r3, #52 +Opcode=2223 Name=tADDhirr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 0: 0| 0: 1: 1: 1| 1: 0: 1: 1| +------------------------------------------------------------------------------------------------- + + add r3, pc +Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 1| 1: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + ldr r0, [r3] +Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| +------------------------------------------------------------------------------------------------- + + ldr r4, [r0] +Opcode=2275 Name=tLDRpci Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 1: 0: 0: 0| 0: 0: 0: 0| 1: 1: 0: 0| +------------------------------------------------------------------------------------------------- + + ldr.n r0, #48 +Opcode=2223 Name=tADDhirr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 0: 0| 0: 1: 1: 1| 1: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + add r0, pc +Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + ldr r0, [r0] +Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + ldr r0, [r0] +Opcode=2243 Name=tBLXi_r9 Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 1: 1: 1: 1| 0: 0: 0: 0| 0: 0: 1: 0| 1: 1: 1: 0| 1: 1: 1: 0| 1: 1: 1: 0| 0: 0: 0: 1| 1: 1: 1: 0| +------------------------------------------------------------------------------------------------- + + blx #191548 +Opcode=2255 Name=tCBNZ Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 1: 0: 0: 1| 0: 0: 0: 1| 1: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + cbnz r0, #6 +Opcode=2275 Name=tLDRpci Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 1: 0: 0: 1| 0: 0: 0: 0| 1: 0: 1: 0| +------------------------------------------------------------------------------------------------- + + ldr.n r1, #40 +Opcode=2223 Name=tADDhirr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 0: 0| 0: 1: 1: 1| 1: 0: 0: 1| +------------------------------------------------------------------------------------------------- + + add r1, pc +Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 0| 1: 0: 0: 1| +------------------------------------------------------------------------------------------------- + + ldr r1, [r1] +Opcode=2238 Name=tB Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 1: 1: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + b #0 +Opcode=2294 Name=tMOVr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 1: 0| 0: 0: 0: 0| 0: 0: 0: 1| +------------------------------------------------------------------------------------------------- + + mov r1, r0 +Opcode=2294 Name=tMOVr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 1: 0| 0: 0: 1: 0| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + mov r0, r4 +Opcode=2294 Name=tMOVr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 1: 0| 0: 0: 1: 0| 1: 0: 1: 0| +------------------------------------------------------------------------------------------------- + + mov r2, r5 +Opcode=2278 Name=tLDRspi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 0: 1| 1: 0: 1: 1| 0: 0: 0: 0| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + ldr r3, [sp] +Opcode=2246 Name=tBLr9 Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 1: 1: 1: 1| 0: 1: 1: 1| 1: 1: 1: 1| 1: 1: 0: 1| 1: 1: 1: 1| 1: 1: 1: 1| 1: 0: 0: 1| 1: 1: 0: 1| +------------------------------------------------------------------------------------------------- + + bl #-8390 +Opcode=2153 Name=t2SUBri Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 1: 1: 1: 1| 0: 0: 0: 1| 1: 0: 1: 0| 0: 1: 1: 1| 0: 0: 0: 0| 1: 1: 0: 1| 0: 0: 0: 0| 1: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + sub.w sp, r7, #8 +Opcode=1926 Name=t2LDMIA_UPD Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 1: 1: 1: 0| 1: 0: 0: 0| 1: 0: 1: 1| 1: 1: 0: 1| 0: 1: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + pop.w {r4, r5, r7, lr} +Opcode=2230 Name=tADDspi Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| +------------------------------------------------------------------------------------------------- + + add sp, #16 +Opcode=2250 Name=tBX_RET Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 1: 1| 0: 1: 1: 1| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + bx lr +Opcode=2300 Name=tNOP Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 1: 1: 1: 1| 0: 0: 0: 0| 0: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + nop +Opcode=2293 Name=tMOVi8 Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 1: 0| 0: 0: 1: 1| 1: 0: 0: 0| 1: 1: 1: 0| +------------------------------------------------------------------------------------------------- + + movs r3, #142 +Opcode=2290 Name=tMOVSr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 1| +------------------------------------------------------------------------------------------------- + + movs r5, r0 +Opcode=2225 Name=tADDi8 Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 1: 1| 0: 0: 0: 1| 0: 1: 1: 1| 1: 0: 1: 0| +------------------------------------------------------------------------------------------------- + + adds r1, #122 +Opcode=2290 Name=tMOVSr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 1| +------------------------------------------------------------------------------------------------- + + movs r5, r0 +Opcode=2225 Name=tADDi8 Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 1: 1| 0: 0: 0: 1| 0: 1: 1: 0| 1: 0: 0: 0| +------------------------------------------------------------------------------------------------- + + adds r1, #104 +Opcode=2290 Name=tMOVSr Format=ARM_FORMAT_THUMBFRM(25) + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +------------------------------------------------------------------------------------------------- +| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 1| +------------------------------------------------------------------------------------------------- + + movs r5, r0 +[16:28:00] johnny:/Volumes/data/Radar/9131529 $ diff --git a/utils/test/README-lldb-disasm b/utils/test/README-lldb-disasm new file mode 100644 index 000000000000..328658c326b1 --- /dev/null +++ b/utils/test/README-lldb-disasm @@ -0,0 +1,94 @@ +This README describes a sample invocation of lldb-disasm.py whose purpose is to test +the lldb 'disassemble' command. + +This is for the initial checkin of lldb-disasm.py which only reads an executable image and +dumps the symbol table from the imgae and its dependent libraries. The output was cut off +since it is too large. + +da0603a-dhcp191:9131529 johnny$ /Volumes/data/lldb/svn/trunk/utils/test/lldb-disasm.py -C 'platform create remote-ios' -e /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib +lldb commands: ['platform create remote-ios'] +lldb options: None +executable: /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib +sys.path: ['/Volumes/data/lldb/svn/trunk/utils/test', '/Volumes/data/lldb/svn/trunk/build/Debug/LLDB.framework/Resources/Python', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python26.zip', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload', '/Library/Python/2.6/site-packages', '/AppleInternal/Library/Python/2.6/site-packages', '/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/PyObjC', '/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/wx-2.8-mac-unicode', '/Volumes/data/lldb/svn/trunk/utils/test/../../test/pexpect-2.4', '/Volumes/data/lldb/svn/trunk/test'] +/Volumes/data/lldb/svn/trunk/test/lldbutil.py:80: SyntaxWarning: import * only allowed at module level + def int_to_bytearray(val, bytesize): +/Volumes/data/lldb/svn/trunk/test/lldbutil.py:105: SyntaxWarning: import * only allowed at module level + def bytearray_to_int(bytes, bytesize): +run command: platform create remote-ios +output: Platform: remote-ios +Not connected to a remote platform. +SDKROOT: "/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3 (8F190)" + +run command: file /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib +output: Current executable set to '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib' (armv7). + +run command: image dump symtab +output: Dumping symbol table for 18 modules. +Symtab, file = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib, num_symbols = 851: + Debug symbol + |Synthetic symbol + ||Externally Visible + ||| +Index UserID DSX Type File Address/Value Load Address Size Flags Name +------- ------ --- ------------ ------------------ ------------------ ------------------ ---------- ---------------------------------- +[ 0] 0 Code 0x0000000000001420 0x0000000000000000 0x000e0008 libSystem_initializer +[ 1] 1 Code 0x00000000000014c4 0x0000000000000000 0x001e0008 __keymgr_initializer +[ 2] 2 Code 0x00000000000014fc 0x0000000000000000 0x000e0008 dwarf2_unwind_dyld_add_image_hook +[ 3] 3 Code 0x0000000000001564 0x0000000000000000 0x000e0008 get_or_create_key_element +[ 4] 4 Code 0x0000000000001684 0x0000000000000000 0x000e0008 unlock_node +[ 5] 5 Code 0x0000000000001930 0x0000000000000000 0x000e0000 RsqrtTable +[ 6] 6 Code 0x0000000000001c30 0x0000000000000000 0x000e0000 acosf_crossover +[ 7] 7 Code 0x0000000000001c34 0x0000000000000000 0x000e0000 acosf_mid_poly +[ 8] 8 Code 0x0000000000001c48 0x0000000000000000 0x000e0000 Pi2_Q30 +[ 9] 9 Code 0x0000000000001c4c 0x0000000000000000 0x000e0000 Pi_Q30 +[ 10] 10 Code 0x0000000000001c78 0x0000000000000000 0x000e0000 acosf_approx +[ 11] 11 Code 0x0000000000001cec 0x0000000000000000 0x000e0000 acosf_pos_tail_poly +[ 12] 12 Code 0x0000000000001d00 0x0000000000000000 0x000e0000 acosf_tail +[ 13] 13 Code 0x0000000000001dfc 0x0000000000000000 0x000e0000 acosf_normalize +[ 14] 14 Code 0x0000000000001e10 0x0000000000000000 0x000e0000 acosf_round +[ 15] 15 Code 0x0000000000001e28 0x0000000000000000 0x000e0000 acosf_encode +[ 16] 16 Code 0x0000000000001e30 0x0000000000000000 0x000e0000 acosf_done +[ 17] 17 Code 0x0000000000001e38 0x0000000000000000 0x000e0000 acosf_special +[ 18] 18 Code 0x0000000000001e68 0x0000000000000000 0x000e0000 acosf_small +[ 19] 19 Code 0x0000000000001e9c 0x0000000000000000 0x000e0000 acosf_very_small +[ 20] 20 Code 0x0000000000001eb8 0x0000000000000000 0x000e0000 Pif +[ 21] 21 Code 0x000000000000220c 0x0000000000000000 0x000e0000 RsqrtTable +[ 22] 22 Code 0x000000000000250c 0x0000000000000000 0x000e0000 asinf_crossover +[ 23] 23 Code 0x0000000000002510 0x0000000000000000 0x000e0000 asinf_mid_poly +[ 24] 24 Code 0x0000000000002524 0x0000000000000000 0x000e0000 Pi2_Q30 +[ 25] 25 Code 0x0000000000002550 0x0000000000000000 0x000e0000 asinf_approx +[ 26] 26 Code 0x00000000000025e4 0x0000000000000000 0x000e0000 asinf_tail_poly +[ 27] 27 Code 0x0000000000002600 0x0000000000000000 0x000e0000 asinf_tail +[ 28] 28 Code 0x00000000000026e0 0x0000000000000000 0x000e0000 asinf_normalize +[ 29] 29 Code 0x00000000000026f4 0x0000000000000000 0x000e0000 asinf_round +[ 30] 30 Code 0x000000000000270c 0x0000000000000000 0x000e0000 asinf_encode +[ 31] 31 Code 0x0000000000002718 0x0000000000000000 0x000e0000 asinf_done +[ 32] 32 Code 0x0000000000002720 0x0000000000000000 0x000e0000 asinf_special +[ 33] 33 Code 0x0000000000002754 0x0000000000000000 0x000e0000 asinf_small +[ 34] 34 Code 0x0000000000002784 0x0000000000000000 0x000e0000 Pi2f +[ 35] 35 Code 0x0000000000005774 0x0000000000000000 0x000e0008 rem_pio2 +[ 36] 36 Code 0x00000000000076c4 0x0000000000000000 0x000e0008 __kernel_rem_pio2 +[ 37] 37 Code 0x0000000000008c90 0x0000000000000000 0x000e0008 __kernel_tan +[ 38] 38 Code 0x0000000000008ef0 0x0000000000000000 0x000e0008 lgammaApprox +[ 39] 39 Code 0x000000000000b3d4 0x0000000000000000 0x000e0000 powf_not_special +[ 40] 40 Code 0x000000000000b3dc 0x0000000000000000 0x000e0000 powf_ylgx +[ 41] 41 Code 0x000000000000b438 0x0000000000000000 0x000e0000 powf_done +[ 42] 42 Code 0x000000000000b43c 0x0000000000000000 0x000e0000 powf_special_y +[ 43] 43 Code 0x000000000000b4a8 0x0000000000000000 0x000e0000 powf_special_x +[ 44] 44 Code 0x000000000000b4cc 0x0000000000000000 0x000e0000 powf_mzero_minf +[ 45] 45 Code 0x000000000000b54c 0x0000000000000000 0x000e0000 powf_y_odd +[ 46] 46 Code 0x000000000000b57c 0x0000000000000000 0x000e0000 powf_y_nonint +[ 47] 47 Code 0x000000000000b588 0x0000000000000000 0x000e0000 powf_y_even +[ 48] 48 Code 0x000000000000b7a8 0x0000000000000000 0x000e0000 powf_log2_reduction +[ 49] 49 Code 0x000000000000b7a8 0x0000000000000000 0x000e0000 powf_log2 +[ 50] 50 Code 0x000000000000b814 0x0000000000000000 0x000e0000 powf_log2_approx +[ 51] 51 Code 0x000000000000b88c 0x0000000000000000 0x000e0000 powf_log2_synthesis +[ 52] 52 Code 0x000000000000b960 0x0000000000000000 0x000e0000 powf_log2_exactPowerOfTwo +[ 53] 53 Code 0x000000000000b980 0x0000000000000000 0x000e0000 powf_log2_near1 +[ 54] 54 Code 0x000000000000b9ec 0x0000000000000000 0x000e0000 powf_log2_synthesis_near1 +[ 55] 55 Code 0x000000000000ba04 0x0000000000000000 0x000e0000 Q32_minimax +[ 56] 56 Code 0x000000000000ba10 0x0000000000000000 0x000e0000 iexp2_lut +[ 57] 57 Code 0x000000000000ba94 0x0000000000000000 0x000e0000 powf_exp2 +[ 58] 58 Code 0x000000000000bb18 0x0000000000000000 0x000e0000 powf_exp2_exact_int +[ 59] 59 Code 0x000000000000bb24 0x0000000000000000 0x000e0000 powf_exp2_big +[ 60] 60 Code 0x000000000000bb74 0x0000000000000000 0x000e0000 powf_exp2_overflow diff --git a/utils/test/README-run-until-faulted b/utils/test/README-run-until-faulted new file mode 100644 index 000000000000..f89f8636d627 --- /dev/null +++ b/utils/test/README-run-until-faulted @@ -0,0 +1,18 @@ +A example usage of the Python script run-until-faulted.py: + +[18:20:29] johnny:/Volumes/data/lldb/svn/trunk/utils/test $ ./run-until-faulted.py -l /Volumes/data/lldb/svn/trunk/build/Debug/lldb -e './a.out' +lldb command: /Volumes/data/lldb/svn/trunk/build/Debug/lldb +executable: ./a.out +executable options: +sending file command.... +sending process launch -- (iteration: 0) + +* thread #1: tid = 0x2d03, 0x0000000100000eef a.out`main + 39 at main.c:7, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) + 4 { + 5 int *null_ptr = 0; + 6 printf("Hello, fault!\n"); +-> 7 printf("Now segfault %d\n", *null_ptr); + 8 } + +(lldb) q +[18:20:40] johnny:/Volumes/data/lldb/svn/trunk/utils/test $ diff --git a/utils/test/disasm.py b/utils/test/disasm.py new file mode 100755 index 000000000000..46660299f188 --- /dev/null +++ b/utils/test/disasm.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python + +""" +Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command, +and display the disassembly result. + +""" + +import os +import sys +from optparse import OptionParser + +def is_exe(fpath): + """Check whether fpath is an executable.""" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + """Find the full path to a program, or return None.""" + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +def do_llvm_mc_disassembly(gdb_commands, gdb_options, exe, func, mc, mc_options): + from cStringIO import StringIO + import pexpect + + gdb_prompt = "\r\n\(gdb\) " + gdb = pexpect.spawn(('gdb %s' % gdb_options) if gdb_options else 'gdb') + # Turn on logging for what gdb sends back. + gdb.logfile_read = sys.stdout + gdb.expect(gdb_prompt) + + # See if there any extra command(s) to execute before we issue the file command. + for cmd in gdb_commands: + gdb.sendline(cmd) + gdb.expect(gdb_prompt) + + # Now issue the file command. + gdb.sendline('file %s' % exe) + gdb.expect(gdb_prompt) + + # Send the disassemble command. + gdb.sendline('disassemble %s' % func) + gdb.expect(gdb_prompt) + + # Get the output from gdb. + gdb_output = gdb.before + + # Use StringIO to record the memory dump as well as the gdb assembler code. + mc_input = StringIO() + + # These keep track of the states of our simple gdb_output parser. + prev_line = None + prev_addr = None + curr_addr = None + addr_diff = 0 + looking = False + for line in gdb_output.split(os.linesep): + if line.startswith('Dump of assembler code'): + looking = True + continue + + if line.startswith('End of assembler dump.'): + looking = False + prev_addr = curr_addr + if mc_options and mc_options.find('arm') != -1: + addr_diff = 4 + if mc_options and mc_options.find('thumb') != -1: + # It is obviously wrong to assume the last instruction of the + # function has two bytes. + # FIXME + addr_diff = 2 + + if looking and line.startswith('0x'): + # It's an assembler code dump. + prev_addr = curr_addr + curr_addr = line.split(None, 1)[0] + if prev_addr and curr_addr: + addr_diff = int(curr_addr, 16) - int(prev_addr, 16) + + if prev_addr and addr_diff > 0: + # Feed the examining memory command to gdb. + gdb.sendline('x /%db %s' % (addr_diff, prev_addr)) + gdb.expect(gdb_prompt) + x_output = gdb.before + # Get the last output line from the gdb examine memory command, + # split the string into a 3-tuple with separator '>:' to handle + # objc method names. + memory_dump = x_output.split(os.linesep)[-1].partition('>:')[2].strip() + #print "\nbytes:", memory_dump + disasm_str = prev_line.partition('>:')[2] + print >> mc_input, '%s # %s' % (memory_dump, disasm_str) + + # We're done with the processing. Assign the current line to be prev_line. + prev_line = line + + # Close the gdb session now that we are done with it. + gdb.sendline('quit') + gdb.expect(pexpect.EOF) + gdb.close() + + # Write the memory dump into a file. + with open('disasm-input.txt', 'w') as f: + f.write(mc_input.getvalue()) + + mc_cmd = '%s -disassemble %s disasm-input.txt' % (mc, mc_options) + print "\nExecuting command:", mc_cmd + os.system(mc_cmd) + + # And invoke llvm-mc with the just recorded file. + #mc = pexpect.spawn('%s -disassemble %s disasm-input.txt' % (mc, mc_options)) + #mc.logfile_read = sys.stdout + #print "mc:", mc + #mc.close() + + +def main(): + # This is to set up the Python path to include the pexpect-2.4 dir. + # Remember to update this when/if things change. + scriptPath = sys.path[0] + sys.path.append(os.path.join(scriptPath, os.pardir, os.pardir, 'test', 'pexpect-2.4')) + + parser = OptionParser(usage="""\ +Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command, +and display the disassembly result. + +Usage: %prog [options] +""") + parser.add_option('-C', '--gdb-command', + type='string', action='append', metavar='COMMAND', + default=[], dest='gdb_commands', + help='Command(s) gdb executes after starting up (can be empty)') + parser.add_option('-O', '--gdb-options', + type='string', action='store', + dest='gdb_options', + help="""The options passed to 'gdb' command if specified.""") + parser.add_option('-e', '--executable', + type='string', action='store', + dest='executable', + help="""The executable to do disassembly on.""") + parser.add_option('-f', '--function', + type='string', action='store', + dest='function', + help="""The function name (could be an address to gdb) for disassembly.""") + parser.add_option('-m', '--llvm-mc', + type='string', action='store', + dest='llvm_mc', + help="""The llvm-mc executable full path, if specified. + Otherwise, it must be present in your PATH environment.""") + + parser.add_option('-o', '--options', + type='string', action='store', + dest='llvm_mc_options', + help="""The options passed to 'llvm-mc -disassemble' command if specified.""") + + opts, args = parser.parse_args() + + gdb_commands = opts.gdb_commands + gdb_options = opts.gdb_options + + if not opts.executable: + parser.print_help() + sys.exit(1) + executable = opts.executable + + if not opts.function: + parser.print_help() + sys.exit(1) + function = opts.function + + llvm_mc = opts.llvm_mc if opts.llvm_mc else which('llvm-mc') + if not llvm_mc: + parser.print_help() + sys.exit(1) + + # This is optional. For example: + # --options='-triple=arm-apple-darwin -debug-only=arm-disassembler' + llvm_mc_options = opts.llvm_mc_options + + # We have parsed the options. + print "gdb commands:", gdb_commands + print "gdb options:", gdb_options + print "executable:", executable + print "function:", function + print "llvm-mc:", llvm_mc + print "llvm-mc options:", llvm_mc_options + + do_llvm_mc_disassembly(gdb_commands, gdb_options, executable, function, llvm_mc, llvm_mc_options) + +if __name__ == '__main__': + main() diff --git a/utils/test/lldb-disasm.py b/utils/test/lldb-disasm.py new file mode 100755 index 000000000000..7987c6b01c37 --- /dev/null +++ b/utils/test/lldb-disasm.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python + +""" +Run lldb to disassemble all the available functions for an executable image. + +""" + +import os +import re +import sys +from optparse import OptionParser + +def setupSysPath(): + """ + Add LLDB.framework/Resources/Python and the test dir to the sys.path. + """ + # Get the directory containing the current script. + scriptPath = sys.path[0] + if not scriptPath.endswith(os.path.join('utils', 'test')): + print "This script expects to reside in lldb's utils/test directory." + sys.exit(-1) + + # This is our base name component. + base = os.path.abspath(os.path.join(scriptPath, os.pardir, os.pardir)) + + # This is for the goodies in the test directory under base. + sys.path.append(os.path.join(base,'test')) + + # These are for xcode build directories. + xcode3_build_dir = ['build'] + xcode4_build_dir = ['build', 'lldb', 'Build', 'Products'] + dbg = ['Debug'] + rel = ['Release'] + bai = ['BuildAndIntegration'] + python_resource_dir = ['LLDB.framework', 'Resources', 'Python'] + + dbgPath = os.path.join(base, *(xcode3_build_dir + dbg + python_resource_dir)) + dbgPath2 = os.path.join(base, *(xcode4_build_dir + dbg + python_resource_dir)) + relPath = os.path.join(base, *(xcode3_build_dir + rel + python_resource_dir)) + relPath2 = os.path.join(base, *(xcode4_build_dir + rel + python_resource_dir)) + baiPath = os.path.join(base, *(xcode3_build_dir + bai + python_resource_dir)) + baiPath2 = os.path.join(base, *(xcode4_build_dir + bai + python_resource_dir)) + + lldbPath = None + if os.path.isfile(os.path.join(dbgPath, 'lldb.py')): + lldbPath = dbgPath + elif os.path.isfile(os.path.join(dbgPath2, 'lldb.py')): + lldbPath = dbgPath2 + elif os.path.isfile(os.path.join(relPath, 'lldb.py')): + lldbPath = relPath + elif os.path.isfile(os.path.join(relPath2, 'lldb.py')): + lldbPath = relPath2 + elif os.path.isfile(os.path.join(baiPath, 'lldb.py')): + lldbPath = baiPath + elif os.path.isfile(os.path.join(baiPath2, 'lldb.py')): + lldbPath = baiPath2 + + if not lldbPath: + print 'This script requires lldb.py to be in either ' + dbgPath + ',', + print relPath + ', or ' + baiPath + sys.exit(-1) + + # This is to locate the lldb.py module. Insert it right after sys.path[0]. + sys.path[1:1] = [lldbPath] + #print "sys.path:", sys.path + + +def run_command(ci, cmd, res, echo=True): + if echo: + print "run command:", cmd + ci.HandleCommand(cmd, res) + if res.Succeeded(): + if echo: + print "run_command output:", res.GetOutput() + else: + if echo: + print "run command failed!" + print "run_command error:", res.GetError() + +def do_lldb_disassembly(lldb_commands, exe, disassemble_options, num_symbols, + symbols_to_disassemble, + re_symbol_pattern, + quiet_disassembly): + import lldb, atexit, re + + # Create the debugger instance now. + dbg = lldb.SBDebugger.Create() + if not dbg: + raise Exception('Invalid debugger instance') + + # Register an exit callback. + atexit.register(lambda: lldb.SBDebugger.Terminate()) + + # We want our debugger to be synchronous. + dbg.SetAsync(False) + + # Get the command interpreter from the debugger. + ci = dbg.GetCommandInterpreter() + if not ci: + raise Exception('Could not get the command interpreter') + + # And the associated result object. + res = lldb.SBCommandReturnObject() + + # See if there any extra command(s) to execute before we issue the file command. + for cmd in lldb_commands: + run_command(ci, cmd, res, not quiet_disassembly) + + # Now issue the file command. + run_command(ci, 'file %s' % exe, res, not quiet_disassembly) + + # Create a target. + #target = dbg.CreateTarget(exe) + target = dbg.GetSelectedTarget() + stream = lldb.SBStream() + + def IsCodeType(symbol): + """Check whether an SBSymbol represents code.""" + return symbol.GetType() == lldb.eSymbolTypeCode + + # Define a generator for the symbols to disassemble. + def symbol_iter(num, symbols, re_symbol_pattern, target, verbose): + # If we specify the symbols to disassemble, ignore symbol table dump. + if symbols: + for i in range(len(symbols)): + if verbose: + print "symbol:", symbols[i] + yield symbols[i] + else: + limited = True if num != -1 else False + if limited: + count = 0 + if re_symbol_pattern: + pattern = re.compile(re_symbol_pattern) + stream = lldb.SBStream() + for m in target.module_iter(): + if verbose: + print "module:", m + for s in m: + if limited and count >= num: + return + # If a regexp symbol pattern is supplied, consult it. + if re_symbol_pattern: + # If the pattern does not match, look for the next symbol. + if not pattern.match(s.GetName()): + continue + + # If we come here, we're ready to disassemble the symbol. + if verbose: + print "symbol:", s.GetName() + if IsCodeType(s): + if limited: + count = count + 1 + if verbose: + print "returning symbol:", s.GetName() + yield s.GetName() + if verbose: + print "start address:", s.GetStartAddress() + print "end address:", s.GetEndAddress() + s.GetDescription(stream) + print "symbol description:", stream.GetData() + stream.Clear() + + # Disassembly time. + for symbol in symbol_iter(num_symbols, symbols_to_disassemble, re_symbol_pattern, target, not quiet_disassembly): + cmd = "disassemble %s '%s'" % (disassemble_options, symbol) + run_command(ci, cmd, res, not quiet_disassembly) + + +def main(): + # This is to set up the Python path to include the pexpect-2.4 dir. + # Remember to update this when/if things change. + scriptPath = sys.path[0] + sys.path.append(os.path.join(scriptPath, os.pardir, os.pardir, 'test', 'pexpect-2.4')) + + parser = OptionParser(usage="""\ +Run lldb to disassemble all the available functions for an executable image. + +Usage: %prog [options] +""") + parser.add_option('-C', '--lldb-command', + type='string', action='append', metavar='COMMAND', + default=[], dest='lldb_commands', + help='Command(s) lldb executes after starting up (can be empty)') + parser.add_option('-e', '--executable', + type='string', action='store', + dest='executable', + help="""Mandatory: the executable to do disassembly on.""") + parser.add_option('-o', '--options', + type='string', action='store', + dest='disassemble_options', + help="""Mandatory: the options passed to lldb's 'disassemble' command.""") + parser.add_option('-q', '--quiet-disassembly', + action='store_true', default=False, + dest='quiet_disassembly', + help="""The symbol(s) to invoke lldb's 'disassemble' command on, if specified.""") + parser.add_option('-n', '--num-symbols', + type='int', action='store', default=-1, + dest='num_symbols', + help="""The number of symbols to disassemble, if specified.""") + parser.add_option('-p', '--symbol_pattern', + type='string', action='store', + dest='re_symbol_pattern', + help="""The regular expression of symbols to invoke lldb's 'disassemble' command.""") + parser.add_option('-s', '--symbol', + type='string', action='append', metavar='SYMBOL', default=[], + dest='symbols_to_disassemble', + help="""The symbol(s) to invoke lldb's 'disassemble' command on, if specified.""") + + opts, args = parser.parse_args() + + lldb_commands = opts.lldb_commands + + if not opts.executable or not opts.disassemble_options: + parser.print_help() + sys.exit(1) + + executable = opts.executable + disassemble_options = opts.disassemble_options + quiet_disassembly = opts.quiet_disassembly + num_symbols = opts.num_symbols + symbols_to_disassemble = opts.symbols_to_disassemble + re_symbol_pattern = opts.re_symbol_pattern + + # We have parsed the options. + if not quiet_disassembly: + print "lldb commands:", lldb_commands + print "executable:", executable + print "disassemble options:", disassemble_options + print "quiet disassembly output:", quiet_disassembly + print "num of symbols to disassemble:", num_symbols + print "symbols to disassemble:", symbols_to_disassemble + print "regular expression of symbols to disassemble:", re_symbol_pattern + + setupSysPath() + do_lldb_disassembly(lldb_commands, executable, disassemble_options, + num_symbols, + symbols_to_disassemble, + re_symbol_pattern, + quiet_disassembly) + +if __name__ == '__main__': + main() diff --git a/utils/test/llvm-mc-shell.py b/utils/test/llvm-mc-shell.py new file mode 100755 index 000000000000..38c12992b914 --- /dev/null +++ b/utils/test/llvm-mc-shell.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python + +""" +Run llvm-mc interactively. + +""" + +import os +import sys +from optparse import OptionParser + +def is_exe(fpath): + """Check whether fpath is an executable.""" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + """Find the full path to a program, or return None.""" + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +def llvm_mc_loop(mc, mc_options): + contents = [] + fname = 'mc-input.txt' + sys.stdout.write("Enter your input to llvm-mc. A line starting with 'END' terminates the current batch of input.\n") + sys.stdout.write("Enter 'quit' or Ctrl-D to quit the program.\n") + while True: + sys.stdout.write("> ") + next = sys.stdin.readline() + # EOF => terminate this llvm-mc shell + if not next or next.startswith('quit'): + sys.stdout.write('\n') + sys.exit(0) + # 'END' => send the current batch of input to llvm-mc + if next.startswith('END'): + # Write contents to our file and clear the contents. + with open(fname, 'w') as f: + f.writelines(contents) + # Clear the list: replace all items with an empty list. + contents[:] = [] + + # Invoke llvm-mc with our newly created file. + mc_cmd = '%s %s %s' % (mc, mc_options, fname) + sys.stdout.write("Executing command: %s\n" % mc_cmd) + os.system(mc_cmd) + else: + # Keep accumulating our input. + contents.append(next) + +def main(): + # This is to set up the Python path to include the pexpect-2.4 dir. + # Remember to update this when/if things change. + scriptPath = sys.path[0] + sys.path.append(os.path.join(scriptPath, os.pardir, os.pardir, 'test', 'pexpect-2.4')) + + parser = OptionParser(usage="""\ +Do llvm-mc interactively within a shell-like environment. A batch of input is +submitted to llvm-mc to execute whenever you terminate the current batch by +inputing a line which starts with 'END'. Quit the program by either 'quit' or +Ctrl-D. + +Usage: %prog [options] +""") + parser.add_option('-m', '--llvm-mc', + type='string', action='store', + dest='llvm_mc', + help="""The llvm-mc executable full path, if specified. + Otherwise, it must be present in your PATH environment.""") + + parser.add_option('-o', '--options', + type='string', action='store', + dest='llvm_mc_options', + help="""The options passed to 'llvm-mc' command if specified.""") + + opts, args = parser.parse_args() + + llvm_mc = opts.llvm_mc if opts.llvm_mc else which('llvm-mc') + if not llvm_mc: + parser.print_help() + sys.exit(1) + + # This is optional. For example: + # --options='-disassemble -triple=arm-apple-darwin -debug-only=arm-disassembler' + llvm_mc_options = opts.llvm_mc_options + + # We have parsed the options. + print "llvm-mc:", llvm_mc + print "llvm-mc options:", llvm_mc_options + + llvm_mc_loop(llvm_mc, llvm_mc_options) + +if __name__ == '__main__': + main() diff --git a/utils/test/main.c b/utils/test/main.c new file mode 100644 index 000000000000..c0fe2f25a488 --- /dev/null +++ b/utils/test/main.c @@ -0,0 +1,14 @@ +#include +#include + +int main(int argc, const char* argv[]) +{ + int *null_ptr = 0; + printf("Hello, fault!\n"); + u_int32_t val = (arc4random() & 0x0f); + printf("val=%u\n", val); + if (val == 0x07) // Lucky 7 :-) + printf("Now segfault %d\n", *null_ptr); + else + printf("Better luck next time!\n"); +} diff --git a/utils/test/ras.py b/utils/test/ras.py new file mode 100755 index 000000000000..a89cbae8c88d --- /dev/null +++ b/utils/test/ras.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python + +""" +Run the test suite and send the result as an email message. + +The code for sending of the directory is copied from +http://docs.python.org/library/email-examples.html. +""" + +import os +import sys +import shutil +import smtplib +# For guessing MIME type based on file name extension +import mimetypes + +from optparse import OptionParser + +from email import encoders +from email.message import Message +from email.mime.audio import MIMEAudio +from email.mime.base import MIMEBase +from email.mime.image import MIMEImage +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +def runTestsuite(testDir, sessDir, envs = None): + """Run the testsuite and return a (summary, output) tuple.""" + os.chdir(testDir) + + for env in envs: + list = env.split('=') + var = list[0].strip() + val = list[1].strip() + print var + "=" + val + os.environ[var] = val + + import shlex, subprocess + + command_line = "./dotest.py -w -s %s" % sessDir + # Apply correct tokenization for subprocess.Popen(). + args = shlex.split(command_line) + + # Use subprocess module to spawn a new process. + process = subprocess.Popen(args, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # Wait for subprocess to terminate. + stdout, stderr = process.communicate() + + # This will be used as the subject line of our email about this test. + cmd = "%s %s" % (' '.join(envs) if envs else "", command_line) + + return (cmd, stderr) + + +COMMASPACE = ', ' + +def main(): + parser = OptionParser(usage="""\ +Run lldb test suite and send the results as a MIME message. + +Usage: %prog [options] + +Unless the -o option is given, the email is sent by forwarding to the specified +SMTP server, which then does the normal delivery process. +""") + parser.add_option('-d', '--directory', + type='string', action='store', + dest='testDir', + help="""The LLDB test directory directly under the top dir. + Otherwise use the current directory.""") + # + # This is similar to TestBase.getRunSpec(self) from lldbtest.py. + # + parser.add_option('-e', '--environment', + type='string', action='append', metavar='ENVIRONMENT', + default=[], dest='environments', + help="""The environment setting as prefix to the test driver. + Example: -e 'CC=clang' -e 'ARCH=x86_64'""") + parser.add_option('-m', '--mailserver', + type='string', action='store', metavar='MAILSERVER', + dest='mailserver', + help="""The outgoing SMTP server.""") + parser.add_option('-o', '--output', + type='string', action='store', metavar='FILE', + help="""Print the composed message to FILE instead of + sending the message to the SMTP server.""") + parser.add_option('-s', '--sender', + type='string', action='store', metavar='SENDER', + help='The value of the From: header (required)') + parser.add_option('-r', '--recipient', + type='string', action='append', metavar='RECIPIENT', + default=[], dest='recipients', + help='A To: header value (at least one required)') + opts, args = parser.parse_args() + if not opts.sender or not opts.recipients: + parser.print_help() + sys.exit(1) + testDir = opts.testDir + if not testDir: + testDir = '.' + + sessDir = 'tmp-lldb-session' + if os.path.exists(sessDir): + shutil.rmtree(sessDir) + #print "environments:", opts.environments + summary, output = runTestsuite(testDir, sessDir, opts.environments) + + # Create the enclosing (outer) message + outer = MIMEMultipart() + outer['Subject'] = summary + outer['To'] = COMMASPACE.join(opts.recipients) + outer['From'] = opts.sender + outer.preamble = 'You will not see this in a MIME-aware mail reader.\n' + + # The sessDir contains all the session logs for failed/errored tests. + # Attach them all if it exists! + + if not os.path.exists(sessDir): + outer.attach(MIMEText(output, 'plain')) + else: + outer.attach(MIMEText("%s\n%s\n\n" % (output, + "Session logs of test failures/errors:"), + 'plain')) + + for filename in (os.listdir(sessDir) if os.path.exists(sessDir) else []): + path = os.path.join(sessDir, filename) + if not os.path.isfile(path): + continue + # Guess the content type based on the file's extension. Encoding + # will be ignored, although we should check for simple things like + # gzip'd or compressed files. + ctype, encoding = mimetypes.guess_type(path) + if ctype is None or encoding is not None: + # No guess could be made, or the file is encoded (compressed), so + # use a generic bag-of-bits type. + ctype = 'application/octet-stream' + maintype, subtype = ctype.split('/', 1) + if maintype == 'text': + fp = open(path) + # Note: we should handle calculating the charset + msg = MIMEText(fp.read(), _subtype=subtype) + fp.close() + elif maintype == 'image': + fp = open(path, 'rb') + msg = MIMEImage(fp.read(), _subtype=subtype) + fp.close() + elif maintype == 'audio': + fp = open(path, 'rb') + msg = MIMEAudio(fp.read(), _subtype=subtype) + fp.close() + else: + fp = open(path, 'rb') + msg = MIMEBase(maintype, subtype) + msg.set_payload(fp.read()) + fp.close() + # Encode the payload using Base64 + encoders.encode_base64(msg) + # Set the filename parameter + msg.add_header('Content-Disposition', 'attachment', filename=filename) + outer.attach(msg) + + # Now send or store the message + composed = outer.as_string() + if opts.output: + fp = open(opts.output, 'w') + fp.write(composed) + fp.close() + else: + s = smtplib.SMTP(opts.mailserver) + s.sendmail(opts.sender, opts.recipients, composed) + s.quit() + + +if __name__ == '__main__': + main() diff --git a/utils/test/run-dis.py b/utils/test/run-dis.py new file mode 100755 index 000000000000..a2437948e47d --- /dev/null +++ b/utils/test/run-dis.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python + +""" +Run lldb disassembler on all the binaries specified by a combination of root dir +and path pattern. +""" + +import os, sys, subprocess +import re +from optparse import OptionParser + +# The directory of this Python script as well as the lldb-disasm.py workhorse. +scriptPath = None + +# The root directory for the SDK symbols. +root_dir = None + +# The regular expression pattern to match the desired pathname to the binaries. +path_pattern = None + +# And the re-compiled regular expression object. +path_regexp = None + +# If specified, number of symbols to disassemble for each qualified binary. +num_symbols = -1 + +# Command template of the invocation of lldb disassembler. +template = '%s/lldb-disasm.py -C "platform select remote-ios" -o "-n" -q -e %s -n %s' + +# Regular expression for detecting file output for Mach-o binary. +mach_o = re.compile('\sMach-O.+binary') +def isbinary(path): + file_output = subprocess.Popen(["file", path], + stdout=subprocess.PIPE).stdout.read() + return (mach_o.search(file_output) is not None) + +def walk_and_invoke(sdk_root, path_regexp, suffix, num_symbols): + """Look for matched file and invoke lldb disassembly on it.""" + global scriptPath + + for root, dirs, files in os.walk(sdk_root, topdown=False): + for name in files: + path = os.path.join(root, name) + + # We're not interested in .h file. + if name.endswith(".h"): + continue + # Neither a symbolically linked file. + if os.path.islink(path): + continue + + # We'll be pattern matching based on the path relative to the SDK root. + replaced_path = path.replace(root_dir, "", 1) + # Check regular expression match for the replaced path. + if not path_regexp.search(replaced_path): + continue + # If a suffix is specified, check it, too. + if suffix and not name.endswith(suffix): + continue + if not isbinary(path): + continue + + command = template % (scriptPath, path, num_symbols if num_symbols > 0 else 1000) + print "Running %s" % (command) + os.system(command) + +def main(): + """Read the root dir and the path spec, invoke lldb-disasm.py on the file.""" + global scriptPath + global root_dir + global path_pattern + global path_regexp + global num_symbols + + scriptPath = sys.path[0] + + parser = OptionParser(usage="""\ +Run lldb disassembler on all the binaries specified by a combination of root dir +and path pattern. +""") + parser.add_option('-r', '--root-dir', + type='string', action='store', + dest='root_dir', + help='Mandatory: the root directory for the SDK symbols.') + parser.add_option('-p', '--path-pattern', + type='string', action='store', + dest='path_pattern', + help='Mandatory: regular expression pattern for the desired binaries.') + parser.add_option('-s', '--suffix', + type='string', action='store', default=None, + dest='suffix', + help='Specify the suffix of the binaries to look for.') + parser.add_option('-n', '--num-symbols', + type='int', action='store', default=-1, + dest='num_symbols', + help="""The number of symbols to disassemble, if specified.""") + + + opts, args = parser.parse_args() + if not opts.root_dir or not opts.path_pattern: + parser.print_help() + sys.exit(1) + + # Sanity check the root directory. + root_dir = opts.root_dir + root_dir = os.path.abspath(root_dir) + if not os.path.isdir(root_dir): + parser.print_help() + sys.exit(1) + + path_pattern = opts.path_pattern + path_regexp = re.compile(path_pattern) + suffix = opts.suffix + num_symbols = opts.num_symbols + + print "Root directory for SDK symbols:", root_dir + print "Regular expression for the binaries:", path_pattern + print "Suffix of the binaries to look for:", suffix + print "num of symbols to disassemble:", num_symbols + + walk_and_invoke(root_dir, path_regexp, suffix, num_symbols) + + +if __name__ == '__main__': + main() diff --git a/utils/test/run-until-faulted.py b/utils/test/run-until-faulted.py new file mode 100755 index 000000000000..d895f565a79e --- /dev/null +++ b/utils/test/run-until-faulted.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python + +""" +Run a program via lldb until it fails. +The lldb executable is located via your PATH env variable, if not specified. +""" + +import os +import sys +from optparse import OptionParser + +def is_exe(fpath): + """Check whether fpath is an executable.""" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + """Find the full path to a program, or return None.""" + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +def do_lldb_launch_loop(lldb_command, exe, exe_options): + from cStringIO import StringIO + import pexpect, time + + prompt = "\(lldb\) " + lldb = pexpect.spawn(lldb_command) + # Turn on logging for what lldb sends back. + lldb.logfile_read = sys.stdout + lldb.expect(prompt) + + # Now issue the file command. + #print "sending 'file %s' command..." % exe + lldb.sendline('file %s' % exe) + lldb.expect(prompt) + + # Loop until it faults.... + count = 0 + #while True: + # count = count + 1 + for i in range(100): + count = i + #print "sending 'process launch -- %s' command... (iteration: %d)" % (exe_options, count) + lldb.sendline('process launch -- %s' % exe_options) + index = lldb.expect(['Process .* exited with status', + 'Process .* stopped', + pexpect.TIMEOUT]) + if index == 0: + # We'll try again later. + time.sleep(3) + elif index == 1: + # Perfect, our process had stopped; break out of the loop. + break; + elif index == 2: + # Something went wrong. + print "TIMEOUT occurred:", str(lldb) + + # Give control of lldb shell to the user. + lldb.interact() + +def main(): + # This is to set up the Python path to include the pexpect-2.4 dir. + # Remember to update this when/if things change. + scriptPath = sys.path[0] + sys.path.append(os.path.join(scriptPath, os.pardir, os.pardir, 'test', 'pexpect-2.4')) + + parser = OptionParser(usage="""\ +%prog [options] +Run a program via lldb until it fails. +The lldb executable is located via your PATH env variable, if not specified.\ +""") + parser.add_option('-l', '--lldb-command', + type='string', action='store', metavar='LLDB_COMMAND', + default='lldb', dest='lldb_command', + help='Full path to your lldb command') + parser.add_option('-e', '--executable', + type='string', action='store', + dest='exe', + help="""(Mandatory) The executable to launch via lldb.""") + parser.add_option('-o', '--options', + type='string', action='store', + default = '', dest='exe_options', + help="""The args/options passed to the launched program, if specified.""") + + opts, args = parser.parse_args() + + lldb_command = which(opts.lldb_command) + + if not opts.exe: + parser.print_help() + sys.exit(1) + exe = opts.exe + + exe_options = opts.exe_options + + # We have parsed the options. + print "lldb command:", lldb_command + print "executable:", exe + print "executable options:", exe_options + + do_lldb_launch_loop(lldb_command, exe, exe_options) + +if __name__ == '__main__': + main() diff --git a/utils/vim-lldb/README b/utils/vim-lldb/README new file mode 100644 index 000000000000..721054a2c84b --- /dev/null +++ b/utils/vim-lldb/README @@ -0,0 +1,59 @@ + +================= +LLDB Vim Frontend +================= + +Prerequisites +------------- + +This plugin is known to work with the following flavours of Vim: + + * Linux (tested on Ubuntu 12.04/12.10): + * vim/gvim (from vim-gnome package version 7.3) + + * Mac OS X (tested on Mountain Lion) + * Vim command-line (7.3 from Xcode) + * MacVim 7.3 + +To install the plugin, ensure you have + * a working version of lldb on your path, or the environment variable LLDB + pointing to the lldb binary you would like to use. + * a python-enabled vim (check with ":python print 2") + + +Installation +------------ + +1) Install the Vim pathogen plugin (it keeps installed plugins organized): + + https://github.com/tpope/vim-pathogen + + Or, for the impatient: + +mkdir -p ~/.vim/autoload ~/.vim/bundle; \ +curl -Sso ~/.vim/autoload/pathogen.vim \ + https://raw.github.com/tpope/vim-pathogen/master/autoload/pathogen.vim + +2) Symlink (or copy) ~/.vim/bundle/vim-lldb to this directory: + +ln -sf /utils/vim-lldb ~/.vim/bundle/vim-lldb + +3) Update your help-tags. Start vim, do: + + :Helptags + +4) Have fun! + + +Usage/Getting Help +------------------ +All LLDB commands (with tab-completion) can be accessed in Vim's +command mode. Try it out by typing: + +:L + +There are several sources of help available: + +:help lldb -- Documentation for this plugin +:Lhelp -- LLDB's built-in help system (i.e lldb 'help' command) +:Lscript help (lldb) -- Complete LLDB Python API reference diff --git a/utils/vim-lldb/doc/lldb.txt b/utils/vim-lldb/doc/lldb.txt new file mode 100644 index 000000000000..e54e6f2db0dc --- /dev/null +++ b/utils/vim-lldb/doc/lldb.txt @@ -0,0 +1,115 @@ +*lldb.txt* A plugin that enables debugging from your favourite editor + +Author: Daniel Malea +License: Same terms as Vim itself (see |license|) + +INTRODUCTION *lldb* + +Installing this plugin enables a set of commands in Vim to control the +LLDB (http://lldb.llvm.org) debugger. + +COMMANDS *lldb-commands* + +The LLDB command interpreter is exposed to Vim's command mode using the +':L' prefix. Tab-completion is available and will cycle through commands. +Some commands have modified behaviour in Vim; for example, :Lbreakpoint +with no arguments will set a breakpoint at the current cursor, rather than +printing the standard help information for the LLDB command 'breakpoint'. + + *lldb-windows* + +In addition to the standard commands available under the LLDB interpreter, +there are also commands to display or hide informational debugger panes. + +Windows can be shown or hidden using the ':Lhide ' or ':Lshow ' +commands. + *lldb-:Lhide* +:Lhide [windowname] Hide informational debugger pane named 'windowname'. + + *lldb-:Lshow* +:Lshow [windowname] Show informational debugger pane named 'windowname'. + +Possible window name arguments to the Lhide and Lshow commands include: + + * backtrace + * breakpoints + * disassembly + * locals + * registers + * threads + *lldb-:Lattach* +:Lattach Attach to a process by name. + + *lldb-:Ldetach* +:Ldetach Detach from the current process. + + *lldb-:Ltarget* +:Ltarget [[create] executable] + Create a target with the specified executable. If + run with a single argument, that argument is assumed + to be a path to the executable to be debugged. + Otherwise, all arguments are passed into LLDB's command + interpreter. + + *lldb-:Lstart* +:Lstart Create a process by executing the current target + and wait for LLDB to attach. + + *lldb-:Lrun* +:Lrun Create a process by executing the current target + without waiting for LLDB to attach. + + *lldb-:Lcontinue* +:Lcontinue Continue execution of the process until the next + breakpoint is hit or the process exits. + + *lldb-:Lthread* +:Lthread Passes through to LLDB. See :Lhelp thread. + + *lldb-:Lstep* +:Lstep Step into the current function call. + + *lldb-:Lstepin* +:Lstepin Step into the current function call. + + *lldb-:Lstepinst* +:Lstepinst Step one instruction. + + *lldb-:Lstepinstover* +:Lstepinstover Step one instruction, but skip over jump or call + instructions. + + *lldb-:Lnext* +:Lnext Step to the next line. + + *lldb-:Lfinish* +:Lfinish Step out of the current function. + + *lldb-:Lbreakpoint* +:Lbreakpoint [args] When arguments are provided, the lldb breakpoint + command is invoked. If no arguments are provided, + a breakpoint at the location under the cursor. + + *lldb-:Lprint* + *lldb-:Lpo* + *lldb-:LpO* +:Lprint Aliases to the lldb print and po commands. Cursor +:Lpo word (cursor WORD for LpO) will be used when +:LpO expression omitted. + +MAPPINGS *lldb-mappings* + +On Mac OS X (under MacVim) , the following key mappings are available: + + Insert a breakpoint at the line under cursor + + +ABOUT *lldb-about* + +Grab the latest version of this plugin (and LLDB sources) with: + git clone http://llvm.org/git/lldb + +File any bugs at: + http://llvm.org/bugs/enter_bug.cgi?product=lldb + + vim:tw=78:et:ft=help:norl: diff --git a/utils/vim-lldb/plugin/lldb.vim b/utils/vim-lldb/plugin/lldb.vim new file mode 100644 index 000000000000..ac5cfe3fbb43 --- /dev/null +++ b/utils/vim-lldb/plugin/lldb.vim @@ -0,0 +1,151 @@ + +" Vim script glue code for LLDB integration + +function! s:FindPythonScriptDir() + for dir in pathogen#split(&runtimepath) + let searchstr = "python-vim-lldb" + let candidates = pathogen#glob_directories(dir . "/" . searchstr) + if len(candidates) > 0 + return candidates[0] + endif + endfor + return +endfunction() + +function! s:InitLldbPlugin() + if has('python') == 0 + call confirm('ERROR: This Vim installation does not have python support. lldb.vim will not work.') + return + endif + + " Key-Bindings + " FIXME: choose sensible keybindings for: + " - process: start, interrupt, continue, continue-to-cursor + " - step: instruction, in, over, out + " + if has('gui_macvim') + " Apple-B toggles breakpoint on cursor + map :Lbreakpoint + endif + + " + " Setup the python interpreter path + " + let vim_lldb_pydir = s:FindPythonScriptDir() + execute 'python import sys; sys.path.append("' . vim_lldb_pydir . '")' + + " + " Register :L + " The LLDB CommandInterpreter provides tab-completion in Vim's command mode. + " FIXME: this list of commands, at least partially should be auto-generated + " + + " Window show/hide commands + command -complete=custom,s:CompleteWindow -nargs=1 Lhide python ctrl.doHide('') + command -complete=custom,s:CompleteWindow -nargs=0 Lshow python ctrl.doShow('') + + " Launching convenience commands (no autocompletion) + command -nargs=* Lstart python ctrl.doLaunch(True, '') + command -nargs=* Lrun python ctrl.doLaunch(False, '') + command -nargs=1 Lattach python ctrl.doAttach('') + command -nargs=0 Ldetach python ctrl.doDetach() + + " Regexp-commands: because vim's command mode does not support '_' or '-' + " characters in command names, we omit them when creating the :L + " equivalents. + command -complete=custom,s:CompleteCommand -nargs=* Lregexpattach python ctrl.doCommand('_regexp-attach', '') + command -complete=custom,s:CompleteCommand -nargs=* Lregexpbreak python ctrl.doCommand('_regexp-break', '') + command -complete=custom,s:CompleteCommand -nargs=* Lregexpbt python ctrl.doCommand('_regexp-bt', '') + command -complete=custom,s:CompleteCommand -nargs=* Lregexpdown python ctrl.doCommand('_regexp-down', '') + command -complete=custom,s:CompleteCommand -nargs=* Lregexptbreak python ctrl.doCommand('_regexp-tbreak', '') + command -complete=custom,s:CompleteCommand -nargs=* Lregexpdisplay python ctrl.doCommand('_regexp-display', '') + command -complete=custom,s:CompleteCommand -nargs=* Lregexpundisplay python ctrl.doCommand('_regexp-undisplay', '') + command -complete=custom,s:CompleteCommand -nargs=* Lregexpup python ctrl.doCommand('_regexp-up', '') + + command -complete=custom,s:CompleteCommand -nargs=* Lapropos python ctrl.doCommand('apropos', '') + command -complete=custom,s:CompleteCommand -nargs=* Lbacktrace python ctrl.doCommand('bt', '') + command -complete=custom,s:CompleteCommand -nargs=* Lbreakpoint python ctrl.doBreakpoint('') + command -complete=custom,s:CompleteCommand -nargs=* Lcommand python ctrl.doCommand('command', '') + command -complete=custom,s:CompleteCommand -nargs=* Ldisassemble python ctrl.doCommand('disassemble', '') + command -complete=custom,s:CompleteCommand -nargs=* Lexpression python ctrl.doCommand('expression', '') + command -complete=custom,s:CompleteCommand -nargs=* Lhelp python ctrl.doCommand('help', '') + command -complete=custom,s:CompleteCommand -nargs=* Llog python ctrl.doCommand('log', '') + command -complete=custom,s:CompleteCommand -nargs=* Lplatform python ctrl.doCommand('platform','') + command -complete=custom,s:CompleteCommand -nargs=* Lplugin python ctrl.doCommand('plugin', '') + command -complete=custom,s:CompleteCommand -nargs=* Lprocess python ctrl.doProcess('') + command -complete=custom,s:CompleteCommand -nargs=* Lregister python ctrl.doCommand('register', '') + command -complete=custom,s:CompleteCommand -nargs=* Lscript python ctrl.doCommand('script', '') + command -complete=custom,s:CompleteCommand -nargs=* Lsettings python ctrl.doCommand('settings','') + command -complete=custom,s:CompleteCommand -nargs=* Lsource python ctrl.doCommand('source', '') + command -complete=custom,s:CompleteCommand -nargs=* Ltype python ctrl.doCommand('type', '') + command -complete=custom,s:CompleteCommand -nargs=* Lversion python ctrl.doCommand('version', '') + command -complete=custom,s:CompleteCommand -nargs=* Lwatchpoint python ctrl.doCommand('watchpoint', '') + + " Convenience (shortcut) LLDB commands + command -complete=custom,s:CompleteCommand -nargs=* Lprint python ctrl.doCommand('print', vim.eval("s:CursorWord('')")) + command -complete=custom,s:CompleteCommand -nargs=* Lpo python ctrl.doCommand('po', vim.eval("s:CursorWord('')")) + command -complete=custom,s:CompleteCommand -nargs=* LpO python ctrl.doCommand('po', vim.eval("s:CursorWORD('')")) + command -complete=custom,s:CompleteCommand -nargs=* Lbt python ctrl.doCommand('bt', '') + + " Frame/Thread-Selection (commands that also do an Uupdate but do not + " generate events in LLDB) + command -complete=custom,s:CompleteCommand -nargs=* Lframe python ctrl.doSelect('frame', '') + command -complete=custom,s:CompleteCommand -nargs=? Lup python ctrl.doCommand('up', '', print_on_success=False, goto_file=True) + command -complete=custom,s:CompleteCommand -nargs=? Ldown python ctrl.doCommand('down', '', print_on_success=False, goto_file=True) + command -complete=custom,s:CompleteCommand -nargs=* Lthread python ctrl.doSelect('thread', '') + + command -complete=custom,s:CompleteCommand -nargs=* Ltarget python ctrl.doTarget('') + + " Continue + command -complete=custom,s:CompleteCommand -nargs=* Lcontinue python ctrl.doContinue() + + " Thread-Stepping (no autocompletion) + command -nargs=0 Lstepinst python ctrl.doStep(StepType.INSTRUCTION) + command -nargs=0 Lstepinstover python ctrl.doStep(StepType.INSTRUCTION_OVER) + command -nargs=0 Lstepin python ctrl.doStep(StepType.INTO) + command -nargs=0 Lstep python ctrl.doStep(StepType.INTO) + command -nargs=0 Lnext python ctrl.doStep(StepType.OVER) + command -nargs=0 Lfinish python ctrl.doStep(StepType.OUT) + + " hack: service the LLDB event-queue when the cursor moves + " FIXME: some threaded solution would be better...but it + " would have to be designed carefully because Vim's APIs are non threadsafe; + " use of the vim module **MUST** be restricted to the main thread. + command -nargs=0 Lrefresh python ctrl.doRefresh() + autocmd CursorMoved * :Lrefresh + autocmd CursorHold * :Lrefresh + autocmd VimLeavePre * python ctrl.doExit() + + execute 'pyfile ' . vim_lldb_pydir . '/plugin.py' +endfunction() + +function! s:CompleteCommand(A, L, P) + python << EOF +a = vim.eval("a:A") +l = vim.eval("a:L") +p = vim.eval("a:P") +returnCompleteCommand(a, l, p) +EOF +endfunction() + +function! s:CompleteWindow(A, L, P) + python << EOF +a = vim.eval("a:A") +l = vim.eval("a:L") +p = vim.eval("a:P") +returnCompleteWindow(a, l, p) +EOF +endfunction() + +" Returns cword if search term is empty +function! s:CursorWord(term) + return empty(a:term) ? expand('') : a:term +endfunction() + +" Returns cleaned cWORD if search term is empty +function! s:CursorWORD(term) + " Will strip all non-alphabetic characters from both sides + return empty(a:term) ? substitute(expand(''), '^\A*\(.\{-}\)\A*$', '\1', '') : a:term +endfunction() + +call s:InitLldbPlugin() diff --git a/utils/vim-lldb/python-vim-lldb/import_lldb.py b/utils/vim-lldb/python-vim-lldb/import_lldb.py new file mode 100644 index 000000000000..a2145d504661 --- /dev/null +++ b/utils/vim-lldb/python-vim-lldb/import_lldb.py @@ -0,0 +1,61 @@ + +# Locate and load the lldb python module + +import os, sys + +def import_lldb(): + """ Find and import the lldb modules. This function tries to find the lldb module by: + 1. Simply by doing "import lldb" in case the system python installation is aware of lldb. If that fails, + 2. Executes the lldb executable pointed to by the LLDB environment variable (or if unset, the first lldb + on PATH") with the -P flag to determine the PYTHONPATH to set. If the lldb executable returns a valid + path, it is added to sys.path and the import is attempted again. If that fails, 3. On Mac OS X the + default Xcode 4.5 installation path. + """ + + # Try simple 'import lldb', in case of a system-wide install or a pre-configured PYTHONPATH + try: + import lldb + return True + except ImportError: + pass + + # Allow overriding default path to lldb executable with the LLDB environment variable + lldb_executable = 'lldb' + if 'LLDB' in os.environ and os.path.exists(os.environ['LLDB']): + lldb_executable = os.environ['LLDB'] + + # Try using builtin module location support ('lldb -P') + from subprocess import check_output, CalledProcessError + try: + with open(os.devnull, 'w') as fnull: + lldb_minus_p_path = check_output("%s -P" % lldb_executable, shell=True, stderr=fnull).strip() + if not os.path.exists(lldb_minus_p_path): + #lldb -P returned invalid path, probably too old + pass + else: + sys.path.append(lldb_minus_p_path) + import lldb + return True + except CalledProcessError: + # Cannot run 'lldb -P' to determine location of lldb python module + pass + except ImportError: + # Unable to import lldb module from path returned by `lldb -P` + pass + + # On Mac OS X, use the try the default path to XCode lldb module + if "darwin" in sys.platform: + xcode_python_path = "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/Current/Resources/Python/" + sys.path.append(xcode_python_path) + try: + import lldb + return True + except ImportError: + # Unable to import lldb module from default Xcode python path + pass + + return False + +if not import_lldb(): + import vim + vim.command('redraw | echo "%s"' % " Error loading lldb module; vim-lldb will be disabled. Check LLDB installation or set LLDB environment variable.") diff --git a/utils/vim-lldb/python-vim-lldb/lldb_controller.py b/utils/vim-lldb/python-vim-lldb/lldb_controller.py new file mode 100644 index 000000000000..923e771e6afe --- /dev/null +++ b/utils/vim-lldb/python-vim-lldb/lldb_controller.py @@ -0,0 +1,384 @@ + +# +# This file defines the layer that talks to lldb +# + +import os, re, sys +import lldb +import vim +from vim_ui import UI + +# ================================================= +# Convert some enum value to its string counterpart +# ================================================= + +# Shamelessly copy/pasted from lldbutil.py in the test suite +def state_type_to_str(enum): + """Returns the stateType string given an enum.""" + if enum == lldb.eStateInvalid: + return "invalid" + elif enum == lldb.eStateUnloaded: + return "unloaded" + elif enum == lldb.eStateConnected: + return "connected" + elif enum == lldb.eStateAttaching: + return "attaching" + elif enum == lldb.eStateLaunching: + return "launching" + elif enum == lldb.eStateStopped: + return "stopped" + elif enum == lldb.eStateRunning: + return "running" + elif enum == lldb.eStateStepping: + return "stepping" + elif enum == lldb.eStateCrashed: + return "crashed" + elif enum == lldb.eStateDetached: + return "detached" + elif enum == lldb.eStateExited: + return "exited" + elif enum == lldb.eStateSuspended: + return "suspended" + else: + raise Exception("Unknown StateType enum") + +class StepType: + INSTRUCTION = 1 + INSTRUCTION_OVER = 2 + INTO = 3 + OVER = 4 + OUT = 5 + +class LLDBController(object): + """ Handles Vim and LLDB events such as commands and lldb events. """ + + # Timeouts (sec) for waiting on new events. Because vim is not multi-threaded, we are restricted to + # servicing LLDB events from the main UI thread. Usually, we only process events that are already + # sitting on the queue. But in some situations (when we are expecting an event as a result of some + # user interaction) we want to wait for it. The constants below set these wait period in which the + # Vim UI is "blocked". Lower numbers will make Vim more responsive, but LLDB will be delayed and higher + # numbers will mean that LLDB events are processed faster, but the Vim UI may appear less responsive at + # times. + eventDelayStep = 2 + eventDelayLaunch = 1 + eventDelayContinue = 1 + + def __init__(self): + """ Creates the LLDB SBDebugger object and initializes the UI class. """ + self.target = None + self.process = None + self.load_dependent_modules = True + + self.dbg = lldb.SBDebugger.Create() + self.commandInterpreter = self.dbg.GetCommandInterpreter() + + self.ui = UI() + + def completeCommand(self, a, l, p): + """ Returns a list of viable completions for command a with length l and cursor at p """ + + assert l[0] == 'L' + # Remove first 'L' character that all commands start with + l = l[1:] + + # Adjust length as string has 1 less character + p = int(p) - 1 + + result = lldb.SBStringList() + num = self.commandInterpreter.HandleCompletion(l, p, 1, -1, result) + + if num == -1: + # FIXME: insert completion character... what's a completion character? + pass + elif num == -2: + # FIXME: replace line with result.GetStringAtIndex(0) + pass + + if result.GetSize() > 0: + results = filter(None, [result.GetStringAtIndex(x) for x in range(result.GetSize())]) + return results + else: + return [] + + def doStep(self, stepType): + """ Perform a step command and block the UI for eventDelayStep seconds in order to process + events on lldb's event queue. + FIXME: if the step does not complete in eventDelayStep seconds, we relinquish control to + the main thread to avoid the appearance of a "hang". If this happens, the UI will + update whenever; usually when the user moves the cursor. This is somewhat annoying. + """ + if not self.process: + sys.stderr.write("No process to step") + return + + t = self.process.GetSelectedThread() + if stepType == StepType.INSTRUCTION: + t.StepInstruction(False) + if stepType == StepType.INSTRUCTION_OVER: + t.StepInstruction(True) + elif stepType == StepType.INTO: + t.StepInto() + elif stepType == StepType.OVER: + t.StepOver() + elif stepType == StepType.OUT: + t.StepOut() + + self.processPendingEvents(self.eventDelayStep, True) + + def doSelect(self, command, args): + """ Like doCommand, but suppress output when "select" is the first argument.""" + a = args.split(' ') + return self.doCommand(command, args, "select" != a[0], True) + + def doProcess(self, args): + """ Handle 'process' command. If 'launch' is requested, use doLaunch() instead + of the command interpreter to start the inferior process. + """ + a = args.split(' ') + if len(args) == 0 or (len(a) > 0 and a[0] != 'launch'): + self.doCommand("process", args) + #self.ui.update(self.target, "", self) + else: + self.doLaunch('-s' not in args, "") + + def doAttach(self, process_name): + """ Handle process attach. """ + error = lldb.SBError() + + self.processListener = lldb.SBListener("process_event_listener") + self.target = self.dbg.CreateTarget('') + self.process = self.target.AttachToProcessWithName(self.processListener, process_name, False, error) + if not error.Success(): + sys.stderr.write("Error during attach: " + str(error)) + return + + self.ui.activate() + self.pid = self.process.GetProcessID() + + print "Attached to %s (pid=%d)" % (process_name, self.pid) + + def doDetach(self): + if self.process is not None and self.process.IsValid(): + pid = self.process.GetProcessID() + state = state_type_to_str(self.process.GetState()) + self.process.Detach() + self.processPendingEvents(self.eventDelayLaunch) + + def doLaunch(self, stop_at_entry, args): + """ Handle process launch. """ + error = lldb.SBError() + + fs = self.target.GetExecutable() + exe = os.path.join(fs.GetDirectory(), fs.GetFilename()) + if self.process is not None and self.process.IsValid(): + pid = self.process.GetProcessID() + state = state_type_to_str(self.process.GetState()) + self.process.Destroy() + + launchInfo = lldb.SBLaunchInfo(args.split(' ')) + self.process = self.target.Launch(launchInfo, error) + if not error.Success(): + sys.stderr.write("Error during launch: " + str(error)) + return + + # launch succeeded, store pid and add some event listeners + self.pid = self.process.GetProcessID() + self.processListener = lldb.SBListener("process_event_listener") + self.process.GetBroadcaster().AddListener(self.processListener, lldb.SBProcess.eBroadcastBitStateChanged) + + print "Launched %s %s (pid=%d)" % (exe, args, self.pid) + + if not stop_at_entry: + self.doContinue() + else: + self.processPendingEvents(self.eventDelayLaunch) + + def doTarget(self, args): + """ Pass target command to interpreter, except if argument is not one of the valid options, or + is create, in which case try to create a target with the argument as the executable. For example: + target list ==> handled by interpreter + target create blah ==> custom creation of target 'blah' + target blah ==> also creates target blah + """ + target_args = [#"create", + "delete", + "list", + "modules", + "select", + "stop-hook", + "symbols", + "variable"] + + a = args.split(' ') + if len(args) == 0 or (len(a) > 0 and a[0] in target_args): + self.doCommand("target", args) + return + elif len(a) > 1 and a[0] == "create": + exe = a[1] + elif len(a) == 1 and a[0] not in target_args: + exe = a[0] + + err = lldb.SBError() + self.target = self.dbg.CreateTarget(exe, None, None, self.load_dependent_modules, err) + if not self.target: + sys.stderr.write("Error creating target %s. %s" % (str(exe), str(err))) + return + + self.ui.activate() + self.ui.update(self.target, "created target %s" % str(exe), self) + + def doContinue(self): + """ Handle 'contiue' command. + FIXME: switch to doCommand("continue", ...) to handle -i ignore-count param. + """ + if not self.process or not self.process.IsValid(): + sys.stderr.write("No process to continue") + return + + self.process.Continue() + self.processPendingEvents(self.eventDelayContinue) + + def doBreakpoint(self, args): + """ Handle breakpoint command with command interpreter, except if the user calls + "breakpoint" with no other args, in which case add a breakpoint at the line + under the cursor. + """ + a = args.split(' ') + if len(args) == 0: + show_output = False + + # User called us with no args, so toggle the bp under cursor + cw = vim.current.window + cb = vim.current.buffer + name = cb.name + line = cw.cursor[0] + + # Since the UI is responsbile for placing signs at bp locations, we have to + # ask it if there already is one or more breakpoints at (file, line)... + if self.ui.haveBreakpoint(name, line): + bps = self.ui.getBreakpoints(name, line) + args = "delete %s" % " ".join([str(b.GetID()) for b in bps]) + self.ui.deleteBreakpoints(name, line) + else: + args = "set -f %s -l %d" % (name, line) + else: + show_output = True + + self.doCommand("breakpoint", args, show_output) + return + + def doRefresh(self): + """ process pending events and update UI on request """ + status = self.processPendingEvents() + + def doShow(self, name): + """ handle :Lshow """ + if not name: + self.ui.activate() + return + + if self.ui.showWindow(name): + self.ui.update(self.target, "", self) + + def doHide(self, name): + """ handle :Lhide """ + if self.ui.hideWindow(name): + self.ui.update(self.target, "", self) + + def doExit(self): + self.dbg.Terminate() + self.dbg = None + + def getCommandResult(self, command, command_args): + """ Run cmd in the command interpreter and returns (success, output) """ + result = lldb.SBCommandReturnObject() + cmd = "%s %s" % (command, command_args) + + self.commandInterpreter.HandleCommand(cmd, result) + return (result.Succeeded(), result.GetOutput() if result.Succeeded() else result.GetError()) + + def doCommand(self, command, command_args, print_on_success = True, goto_file=False): + """ Run cmd in interpreter and print result (success or failure) on the vim status line. """ + (success, output) = self.getCommandResult(command, command_args) + if success: + self.ui.update(self.target, "", self, goto_file) + if len(output) > 0 and print_on_success: + print output + else: + sys.stderr.write(output) + + def getCommandOutput(self, command, command_args=""): + """ runs cmd in the command interpreter andreturns (status, result) """ + result = lldb.SBCommandReturnObject() + cmd = "%s %s" % (command, command_args) + self.commandInterpreter.HandleCommand(cmd, result) + return (result.Succeeded(), result.GetOutput() if result.Succeeded() else result.GetError()) + + def processPendingEvents(self, wait_seconds=0, goto_file=True): + """ Handle any events that are queued from the inferior. + Blocks for at most wait_seconds, or if wait_seconds == 0, + process only events that are already queued. + """ + + status = None + num_events_handled = 0 + + if self.process is not None: + event = lldb.SBEvent() + old_state = self.process.GetState() + new_state = None + done = False + if old_state == lldb.eStateInvalid or old_state == lldb.eStateExited: + # Early-exit if we are in 'boring' states + pass + else: + while not done and self.processListener is not None: + if not self.processListener.PeekAtNextEvent(event): + if wait_seconds > 0: + # No events on the queue, but we are allowed to wait for wait_seconds + # for any events to show up. + self.processListener.WaitForEvent(wait_seconds, event) + new_state = lldb.SBProcess.GetStateFromEvent(event) + + num_events_handled += 1 + + done = not self.processListener.PeekAtNextEvent(event) + else: + # An event is on the queue, process it here. + self.processListener.GetNextEvent(event) + new_state = lldb.SBProcess.GetStateFromEvent(event) + + # continue if stopped after attaching + if old_state == lldb.eStateAttaching and new_state == lldb.eStateStopped: + self.process.Continue() + + # If needed, perform any event-specific behaviour here + num_events_handled += 1 + + if num_events_handled == 0: + pass + else: + if old_state == new_state: + status = "" + self.ui.update(self.target, status, self, goto_file) + + +def returnCompleteCommand(a, l, p): + """ Returns a "\n"-separated string with possible completion results + for command a with length l and cursor at p. + """ + separator = "\n" + results = ctrl.completeCommand(a, l, p) + vim.command('return "%s%s"' % (separator.join(results), separator)) + +def returnCompleteWindow(a, l, p): + """ Returns a "\n"-separated string with possible completion results + for commands that expect a window name parameter (like hide/show). + FIXME: connect to ctrl.ui instead of hardcoding the list here + """ + separator = "\n" + results = ['breakpoints', 'backtrace', 'disassembly', 'locals', 'threads', 'registers'] + vim.command('return "%s%s"' % (separator.join(results), separator)) + +global ctrl +ctrl = LLDBController() diff --git a/utils/vim-lldb/python-vim-lldb/plugin.py b/utils/vim-lldb/python-vim-lldb/plugin.py new file mode 100644 index 000000000000..694783a95b0e --- /dev/null +++ b/utils/vim-lldb/python-vim-lldb/plugin.py @@ -0,0 +1,14 @@ + +# Try to import all dependencies, catch and handle the error gracefully if it fails. + +import import_lldb + +try: + import lldb + import vim +except ImportError: + sys.stderr.write("Unable to load vim/lldb module. Check lldb is on the path is available (or LLDB is set) and that script is invoked inside Vim with :pyfile") + pass +else: + # Everthing went well, so use import to start the plugin controller + from lldb_controller import * diff --git a/utils/vim-lldb/python-vim-lldb/vim_panes.py b/utils/vim-lldb/python-vim-lldb/vim_panes.py new file mode 100644 index 000000000000..ec537199922c --- /dev/null +++ b/utils/vim-lldb/python-vim-lldb/vim_panes.py @@ -0,0 +1,618 @@ +# +# This file contains implementations of the LLDB display panes in VIM +# +# The most generic way to define a new window is to inherit from VimPane +# and to implement: +# - get_content() - returns a string with the pane contents +# +# Optionally, to highlight text, implement: +# - get_highlights() - returns a map +# +# And call: +# - define_highlight(unique_name, colour) +# at some point in the constructor. +# +# +# If the pane shows some key-value data that is in the context of a +# single frame, inherit from FrameKeyValuePane and implement: +# - get_frame_content(self, SBFrame frame) +# +# +# If the pane presents some information that can be retrieved with +# a simple LLDB command while the subprocess is stopped, inherit +# from StoppedCommandPane and call: +# - self.setCommand(command, command_args) +# at some point in the constructor. +# +# Optionally, you can implement: +# - get_selected_line() +# to highlight a selected line and place the cursor there. +# +# +# FIXME: implement WatchlistPane to displayed watched expressions +# FIXME: define interface for interactive panes, like catching enter +# presses to change selected frame/thread... +# + +import lldb +import vim + +import sys + +# ============================================================== +# Get the description of an lldb object or None if not available +# ============================================================== + +# Shamelessly copy/pasted from lldbutil.py in the test suite +def get_description(obj, option=None): + """Calls lldb_obj.GetDescription() and returns a string, or None. + + For SBTarget, SBBreakpointLocation, and SBWatchpoint lldb objects, an extra + option can be passed in to describe the detailed level of description + desired: + o lldb.eDescriptionLevelBrief + o lldb.eDescriptionLevelFull + o lldb.eDescriptionLevelVerbose + """ + method = getattr(obj, 'GetDescription') + if not method: + return None + tuple = (lldb.SBTarget, lldb.SBBreakpointLocation, lldb.SBWatchpoint) + if isinstance(obj, tuple): + if option is None: + option = lldb.eDescriptionLevelBrief + + stream = lldb.SBStream() + if option is None: + success = method(stream) + else: + success = method(stream, option) + if not success: + return None + return stream.GetData() + +def get_selected_thread(target): + """ Returns a tuple with (thread, error) where thread == None if error occurs """ + process = target.GetProcess() + if process is None or not process.IsValid(): + return (None, VimPane.MSG_NO_PROCESS) + + thread = process.GetSelectedThread() + if thread is None or not thread.IsValid(): + return (None, VimPane.MSG_NO_THREADS) + return (thread, "") + +def get_selected_frame(target): + """ Returns a tuple with (frame, error) where frame == None if error occurs """ + (thread, error) = get_selected_thread(target) + if thread is None: + return (None, error) + + frame = thread.GetSelectedFrame() + if frame is None or not frame.IsValid(): + return (None, VimPane.MSG_NO_FRAME) + return (frame, "") + +def _cmd(cmd): + vim.command("call confirm('%s')" % cmd) + vim.command(cmd) + +def move_cursor(line, col=0): + """ moves cursor to specified line and col """ + cw = vim.current.window + if cw.cursor[0] != line: + vim.command("execute \"normal %dgg\"" % line) + +def winnr(): + """ Returns currently selected window number """ + return int(vim.eval("winnr()")) + +def bufwinnr(name): + """ Returns window number corresponding with buffer name """ + return int(vim.eval("bufwinnr('%s')" % name)) + +def goto_window(nr): + """ go to window number nr""" + if nr != winnr(): + vim.command(str(nr) + ' wincmd w') + +def goto_next_window(): + """ go to next window. """ + vim.command('wincmd w') + return (winnr(), vim.current.buffer.name) + +def goto_previous_window(): + """ go to previously selected window """ + vim.command("execute \"normal \\p\"") + +def have_gui(): + """ Returns True if vim is in a gui (Gvim/MacVim), False otherwise. """ + return int(vim.eval("has('gui_running')")) == 1 + +class PaneLayout(object): + """ A container for a (vertical) group layout of VimPanes """ + + def __init__(self): + self.panes = {} + + def havePane(self, name): + """ Returns true if name is a registered pane, False otherwise """ + return name in self.panes + + def prepare(self, panes = []): + """ Draw panes on screen. If empty list is provided, show all. """ + + # If we can't select a window contained in the layout, we are doing a first draw + first_draw = not self.selectWindow(True) + did_first_draw = False + + # Prepare each registered pane + for name in self.panes: + if name in panes or len(panes) == 0: + if first_draw: + # First window in layout will be created with :vsp, and closed later + vim.command(":vsp") + first_draw = False + did_first_draw = True + self.panes[name].prepare() + + if did_first_draw: + # Close the split window + vim.command(":q") + + self.selectWindow(False) + + def contains(self, bufferName = None): + """ Returns True if window with name bufferName is contained in the layout, False otherwise. + If bufferName is None, the currently selected window is checked. + """ + if not bufferName: + bufferName = vim.current.buffer.name + + for p in self.panes: + if bufferName is not None and bufferName.endswith(p): + return True + return False + + def selectWindow(self, select_contained = True): + """ Selects a window contained in the layout (if select_contained = True) and returns True. + If select_contained = False, a window that is not contained is selected. Returns False + if no group windows can be selected. + """ + if select_contained == self.contains(): + # Simple case: we are already selected + return True + + # Otherwise, switch to next window until we find a contained window, or reach the first window again. + first = winnr() + (curnum, curname) = goto_next_window() + + while not select_contained == self.contains(curname) and curnum != first: + (curnum, curname) = goto_next_window() + + return self.contains(curname) == select_contained + + def hide(self, panes = []): + """ Hide panes specified. If empty list provided, hide all. """ + for name in self.panes: + if name in panes or len(panes) == 0: + self.panes[name].destroy() + + def registerForUpdates(self, p): + self.panes[p.name] = p + + def update(self, target, controller): + for name in self.panes: + self.panes[name].update(target, controller) + + +class VimPane(object): + """ A generic base class for a pane that displays stuff """ + CHANGED_VALUE_HIGHLIGHT_NAME_GUI = 'ColorColumn' + CHANGED_VALUE_HIGHLIGHT_NAME_TERM = 'lldb_changed' + CHANGED_VALUE_HIGHLIGHT_COLOUR_TERM = 'darkred' + + SELECTED_HIGHLIGHT_NAME_GUI = 'Cursor' + SELECTED_HIGHLIGHT_NAME_TERM = 'lldb_selected' + SELECTED_HIGHLIGHT_COLOUR_TERM = 'darkblue' + + MSG_NO_TARGET = "Target does not exist." + MSG_NO_PROCESS = "Process does not exist." + MSG_NO_THREADS = "No valid threads." + MSG_NO_FRAME = "No valid frame." + + # list of defined highlights, so we avoid re-defining them + highlightTypes = [] + + def __init__(self, owner, name, open_below=False, height=3): + self.owner = owner + self.name = name + self.buffer = None + self.maxHeight = 20 + self.openBelow = open_below + self.height = height + self.owner.registerForUpdates(self) + + def isPrepared(self): + """ check window is OK """ + if self.buffer == None or len(dir(self.buffer)) == 0 or bufwinnr(self.name) == -1: + return False + return True + + def prepare(self, method = 'new'): + """ check window is OK, if not then create """ + if not self.isPrepared(): + self.create(method) + + def on_create(self): + pass + + def destroy(self): + """ destroy window """ + if self.buffer == None or len(dir(self.buffer)) == 0: + return + vim.command('bdelete ' + self.name) + + def create(self, method): + """ create window """ + + if method != 'edit': + belowcmd = "below" if self.openBelow else "" + vim.command('silent %s %s %s' % (belowcmd, method, self.name)) + else: + vim.command('silent %s %s' % (method, self.name)) + + self.window = vim.current.window + + # Set LLDB pane options + vim.command("setlocal buftype=nofile") # Don't try to open a file + vim.command("setlocal noswapfile") # Don't use a swap file + vim.command("set nonumber") # Don't display line numbers + #vim.command("set nowrap") # Don't wrap text + + # Save some parameters and reference to buffer + self.buffer = vim.current.buffer + self.width = int( vim.eval("winwidth(0)") ) + self.height = int( vim.eval("winheight(0)") ) + + self.on_create() + goto_previous_window() + + def update(self, target, controller): + """ updates buffer contents """ + self.target = target + if not self.isPrepared(): + # Window is hidden, or otherwise not ready for an update + return + + original_cursor = self.window.cursor + + # Select pane + goto_window(bufwinnr(self.name)) + + # Clean and update content, and apply any highlights. + self.clean() + + if self.write(self.get_content(target, controller)): + self.apply_highlights() + + cursor = self.get_selected_line() + if cursor is None: + # Place the cursor at its original position in the window + cursor_line = min(original_cursor[0], len(self.buffer)) + cursor_col = min(original_cursor[1], len(self.buffer[cursor_line - 1])) + else: + # Place the cursor at the location requested by a VimPane implementation + cursor_line = min(cursor, len(self.buffer)) + cursor_col = self.window.cursor[1] + + self.window.cursor = (cursor_line, cursor_col) + + goto_previous_window() + + def get_selected_line(self): + """ Returns the line number to move the cursor to, or None to leave + it where the user last left it. + Subclasses implement this to define custom behaviour. + """ + return None + + def apply_highlights(self): + """ Highlights each set of lines in each highlight group """ + highlights = self.get_highlights() + for highlightType in highlights: + lines = highlights[highlightType] + if len(lines) == 0: + continue + + cmd = 'match %s /' % highlightType + lines = ['\%' + '%d' % line + 'l' for line in lines] + cmd += '\\|'.join(lines) + cmd += '/' + vim.command(cmd) + + def define_highlight(self, name, colour): + """ Defines highlihght """ + if name in VimPane.highlightTypes: + # highlight already defined + return + + vim.command("highlight %s ctermbg=%s guibg=%s" % (name, colour, colour)) + VimPane.highlightTypes.append(name) + + def write(self, msg): + """ replace buffer with msg""" + self.prepare() + + msg = str(msg.encode("utf-8", "replace")).split('\n') + try: + self.buffer.append(msg) + vim.command("execute \"normal ggdd\"") + except vim.error: + # cannot update window; happens when vim is exiting. + return False + + move_cursor(1, 0) + return True + + def clean(self): + """ clean all datas in buffer """ + self.prepare() + vim.command(':%d') + #self.buffer[:] = None + + def get_content(self, target, controller): + """ subclasses implement this to provide pane content """ + assert(0 and "pane subclass must implement this") + pass + + def get_highlights(self): + """ Subclasses implement this to provide pane highlights. + This function is expected to return a map of: + { highlight_name ==> [line_number, ...], ... } + """ + return {} + + +class FrameKeyValuePane(VimPane): + def __init__(self, owner, name, open_below): + """ Initialize parent, define member variables, choose which highlight + to use based on whether or not we have a gui (MacVim/Gvim). + """ + + VimPane.__init__(self, owner, name, open_below) + + # Map-of-maps key/value history { frame --> { variable_name, variable_value } } + self.frameValues = {} + + if have_gui(): + self.changedHighlight = VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_GUI + else: + self.changedHighlight = VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_TERM + self.define_highlight(VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_TERM, + VimPane.CHANGED_VALUE_HIGHLIGHT_COLOUR_TERM) + + def format_pair(self, key, value, changed = False): + """ Formats a key/value pair. Appends a '*' if changed == True """ + marker = '*' if changed else ' ' + return "%s %s = %s\n" % (marker, key, value) + + def get_content(self, target, controller): + """ Get content for a frame-aware pane. Also builds the list of lines that + need highlighting (i.e. changed values.) + """ + if target is None or not target.IsValid(): + return VimPane.MSG_NO_TARGET + + self.changedLines = [] + + (frame, err) = get_selected_frame(target) + if frame is None: + return err + + output = get_description(frame) + lineNum = 1 + + # Retrieve the last values displayed for this frame + frameId = get_description(frame.GetBlock()) + if frameId in self.frameValues: + frameOldValues = self.frameValues[frameId] + else: + frameOldValues = {} + + # Read the frame variables + vals = self.get_frame_content(frame) + for (key, value) in vals: + lineNum += 1 + if len(frameOldValues) == 0 or (key in frameOldValues and frameOldValues[key] == value): + output += self.format_pair(key, value) + else: + output += self.format_pair(key, value, True) + self.changedLines.append(lineNum) + + # Save values as oldValues + newValues = {} + for (key, value) in vals: + newValues[key] = value + self.frameValues[frameId] = newValues + + return output + + def get_highlights(self): + ret = {} + ret[self.changedHighlight] = self.changedLines + return ret + +class LocalsPane(FrameKeyValuePane): + """ Pane that displays local variables """ + def __init__(self, owner, name = 'locals'): + FrameKeyValuePane.__init__(self, owner, name, open_below=True) + + # FIXME: allow users to customize display of args/locals/statics/scope + self.arguments = True + self.show_locals = True + self.show_statics = True + self.show_in_scope_only = True + + def format_variable(self, var): + """ Returns a Tuple of strings "(Type) Name", "Value" for SBValue var """ + val = var.GetValue() + if val is None: + # If the value is too big, SBValue.GetValue() returns None; replace with ... + val = "..." + + return ("(%s) %s" % (var.GetTypeName(), var.GetName()), "%s" % val) + + def get_frame_content(self, frame): + """ Returns list of key-value pairs of local variables in frame """ + vals = frame.GetVariables(self.arguments, + self.show_locals, + self.show_statics, + self.show_in_scope_only) + return [self.format_variable(x) for x in vals] + +class RegistersPane(FrameKeyValuePane): + """ Pane that displays the contents of registers """ + def __init__(self, owner, name = 'registers'): + FrameKeyValuePane.__init__(self, owner, name, open_below=True) + + def format_register(self, reg): + """ Returns a tuple of strings ("name", "value") for SBRegister reg. """ + name = reg.GetName() + val = reg.GetValue() + if val is None: + val = "..." + return (name, val.strip()) + + def get_frame_content(self, frame): + """ Returns a list of key-value pairs ("name", "value") of registers in frame """ + + result = [] + for register_sets in frame.GetRegisters(): + # hack the register group name into the list of registers... + result.append((" = = %s =" % register_sets.GetName(), "")) + + for reg in register_sets: + result.append(self.format_register(reg)) + return result + +class CommandPane(VimPane): + """ Pane that displays the output of an LLDB command """ + def __init__(self, owner, name, open_below, process_required=True): + VimPane.__init__(self, owner, name, open_below) + self.process_required = process_required + + def setCommand(self, command, args = ""): + self.command = command + self.args = args + + def get_content(self, target, controller): + output = "" + if not target: + output = VimPane.MSG_NO_TARGET + elif self.process_required and not target.GetProcess(): + output = VimPane.MSG_NO_PROCESS + else: + (success, output) = controller.getCommandOutput(self.command, self.args) + return output + +class StoppedCommandPane(CommandPane): + """ Pane that displays the output of an LLDB command when the process is + stopped; otherwise displays process status. This class also implements + highlighting for a single line (to show a single-line selected entity.) + """ + def __init__(self, owner, name, open_below): + """ Initialize parent and define highlight to use for selected line. """ + CommandPane.__init__(self, owner, name, open_below) + if have_gui(): + self.selectedHighlight = VimPane.SELECTED_HIGHLIGHT_NAME_GUI + else: + self.selectedHighlight = VimPane.SELECTED_HIGHLIGHT_NAME_TERM + self.define_highlight(VimPane.SELECTED_HIGHLIGHT_NAME_TERM, + VimPane.SELECTED_HIGHLIGHT_COLOUR_TERM) + + def get_content(self, target, controller): + """ Returns the output of a command that relies on the process being stopped. + If the process is not in 'stopped' state, the process status is returned. + """ + output = "" + if not target or not target.IsValid(): + output = VimPane.MSG_NO_TARGET + elif not target.GetProcess() or not target.GetProcess().IsValid(): + output = VimPane.MSG_NO_PROCESS + elif target.GetProcess().GetState() == lldb.eStateStopped: + (success, output) = controller.getCommandOutput(self.command, self.args) + else: + (success, output) = controller.getCommandOutput("process", "status") + return output + + def get_highlights(self): + """ Highlight the line under the cursor. Users moving the cursor has + no effect on the selected line. + """ + ret = {} + line = self.get_selected_line() + if line is not None: + ret[self.selectedHighlight] = [line] + return ret + return ret + + def get_selected_line(self): + """ Subclasses implement this to control where the cursor (and selected highlight) + is placed. + """ + return None + +class DisassemblyPane(CommandPane): + """ Pane that displays disassembly around PC """ + def __init__(self, owner, name = 'disassembly'): + CommandPane.__init__(self, owner, name, open_below=True) + + # FIXME: let users customize the number of instructions to disassemble + self.setCommand("disassemble", "-c %d -p" % self.maxHeight) + +class ThreadPane(StoppedCommandPane): + """ Pane that displays threads list """ + def __init__(self, owner, name = 'threads'): + StoppedCommandPane.__init__(self, owner, name, open_below=False) + self.setCommand("thread", "list") + +# FIXME: the function below assumes threads are listed in sequential order, +# which turns out to not be the case. Highlighting of selected thread +# will be disabled until this can be fixed. LLDB prints a '*' anyways +# beside the selected thread, so this is not too big of a problem. +# def get_selected_line(self): +# """ Place the cursor on the line with the selected entity. +# Subclasses should override this to customize selection. +# Formula: selected_line = selected_thread_id + 1 +# """ +# (thread, err) = get_selected_thread(self.target) +# if thread is None: +# return None +# else: +# return thread.GetIndexID() + 1 + +class BacktracePane(StoppedCommandPane): + """ Pane that displays backtrace """ + def __init__(self, owner, name = 'backtrace'): + StoppedCommandPane.__init__(self, owner, name, open_below=False) + self.setCommand("bt", "") + + + def get_selected_line(self): + """ Returns the line number in the buffer with the selected frame. + Formula: selected_line = selected_frame_id + 2 + FIXME: the above formula hack does not work when the function return + value is printed in the bt window; the wrong line is highlighted. + """ + + (frame, err) = get_selected_frame(self.target) + if frame is None: + return None + else: + return frame.GetFrameID() + 2 + +class BreakpointsPane(CommandPane): + def __init__(self, owner, name = 'breakpoints'): + super(BreakpointsPane, self).__init__(owner, name, open_below=False, process_required=False) + self.setCommand("breakpoint", "list") diff --git a/utils/vim-lldb/python-vim-lldb/vim_signs.py b/utils/vim-lldb/python-vim-lldb/vim_signs.py new file mode 100644 index 000000000000..926cc29a7fca --- /dev/null +++ b/utils/vim-lldb/python-vim-lldb/vim_signs.py @@ -0,0 +1,73 @@ + +# Classes responsible for drawing signs in the Vim user interface. + +import vim + +class VimSign(object): + SIGN_TEXT_BREAKPOINT_RESOLVED = "B>" + SIGN_TEXT_BREAKPOINT_UNRESOLVED = "b>" + SIGN_TEXT_PC = "->" + SIGN_HIGHLIGHT_COLOUR_PC = 'darkblue' + + # unique sign id (for ':[sign/highlight] define) + sign_id = 1 + + # unique name id (for ':sign place') + name_id = 1 + + # Map of {(sign_text, highlight_colour) --> sign_name} + defined_signs = {} + + def __init__(self, sign_text, buffer, line_number, highlight_colour=None): + """ Define the sign and highlight (if applicable) and show the sign. """ + + # Get the sign name, either by defining it, or looking it up in the map of defined signs + key = (sign_text, highlight_colour) + if not key in VimSign.defined_signs: + name = self.define(sign_text, highlight_colour) + else: + name = VimSign.defined_signs[key] + + self.show(name, buffer.number, line_number) + pass + + def define(self, sign_text, highlight_colour): + """ Defines sign and highlight (if highlight_colour is not None). """ + sign_name = "sign%d" % VimSign.name_id + if highlight_colour is None: + vim.command("sign define %s text=%s" % (sign_name, sign_text)) + else: + self.highlight_name = "highlight%d" % VimSign.name_id + vim.command("highlight %s ctermbg=%s guibg=%s" % (self.highlight_name, + highlight_colour, + highlight_colour)) + vim.command("sign define %s text=%s linehl=%s texthl=%s" % (sign_name, + sign_text, + self.highlight_name, + self.highlight_name)) + VimSign.defined_signs[(sign_text, highlight_colour)] = sign_name + VimSign.name_id += 1 + return sign_name + + + def show(self, name, buffer_number, line_number): + self.id = VimSign.sign_id + VimSign.sign_id += 1 + vim.command("sign place %d name=%s line=%d buffer=%s" % (self.id, name, line_number, buffer_number)) + pass + + def hide(self): + vim.command("sign unplace %d" % self.id) + pass + +class BreakpointSign(VimSign): + def __init__(self, buffer, line_number, is_resolved): + txt = VimSign.SIGN_TEXT_BREAKPOINT_RESOLVED if is_resolved else VimSign.SIGN_TEXT_BREAKPOINT_UNRESOLVED + super(BreakpointSign, self).__init__(txt, buffer, line_number) + +class PCSign(VimSign): + def __init__(self, buffer, line_number, is_selected_thread): + super(PCSign, self).__init__(VimSign.SIGN_TEXT_PC, + buffer, + line_number, + VimSign.SIGN_HIGHLIGHT_COLOUR_PC if is_selected_thread else None) diff --git a/utils/vim-lldb/python-vim-lldb/vim_ui.py b/utils/vim-lldb/python-vim-lldb/vim_ui.py new file mode 100644 index 000000000000..4be346b96f0e --- /dev/null +++ b/utils/vim-lldb/python-vim-lldb/vim_ui.py @@ -0,0 +1,235 @@ + +# LLDB UI state in the Vim user interface. + +import os, re, sys +import lldb +import vim +from vim_panes import * +from vim_signs import * + +def is_same_file(a, b): + """ returns true if paths a and b are the same file """ + a = os.path.realpath(a) + b = os.path.realpath(b) + return a in b or b in a + +class UI: + def __init__(self): + """ Declare UI state variables """ + + # Default panes to display + self.defaultPanes = ['breakpoints', 'backtrace', 'locals', 'threads', 'registers', 'disassembly'] + + # map of tuples (filename, line) --> SBBreakpoint + self.markedBreakpoints = {} + + # Currently shown signs + self.breakpointSigns = {} + self.pcSigns = [] + + # Container for panes + self.paneCol = PaneLayout() + + # All possible LLDB panes + self.backtracePane = BacktracePane(self.paneCol) + self.threadPane = ThreadPane(self.paneCol) + self.disassemblyPane = DisassemblyPane(self.paneCol) + self.localsPane = LocalsPane(self.paneCol) + self.registersPane = RegistersPane(self.paneCol) + self.breakPane = BreakpointsPane(self.paneCol) + + def activate(self): + """ Activate UI: display default set of panes """ + self.paneCol.prepare(self.defaultPanes) + + def get_user_buffers(self, filter_name=None): + """ Returns a list of buffers that are not a part of the LLDB UI. That is, they + are not contained in the PaneLayout object self.paneCol. + """ + ret = [] + for w in vim.windows: + b = w.buffer + if not self.paneCol.contains(b.name): + if filter_name is None or filter_name in b.name: + ret.append(b) + return ret + + def update_pc(self, process, buffers, goto_file): + """ Place the PC sign on the PC location of each thread's selected frame """ + + def GetPCSourceLocation(thread): + """ Returns a tuple (thread_index, file, line, column) that represents where + the PC sign should be placed for a thread. + """ + + frame = thread.GetSelectedFrame() + frame_num = frame.GetFrameID() + le = frame.GetLineEntry() + while not le.IsValid() and frame_num < thread.GetNumFrames(): + frame_num += 1 + le = thread.GetFrameAtIndex(frame_num).GetLineEntry() + + if le.IsValid(): + path = os.path.join(le.GetFileSpec().GetDirectory(), le.GetFileSpec().GetFilename()) + return (thread.GetIndexID(), path, le.GetLine(), le.GetColumn()) + return None + + + # Clear all existing PC signs + del_list = [] + for sign in self.pcSigns: + sign.hide() + del_list.append(sign) + for sign in del_list: + self.pcSigns.remove(sign) + del sign + + # Select a user (non-lldb) window + if not self.paneCol.selectWindow(False): + # No user window found; avoid clobbering by splitting + vim.command(":vsp") + + # Show a PC marker for each thread + for thread in process: + loc = GetPCSourceLocation(thread) + if not loc: + # no valid source locations for PCs. hide all existing PC markers + continue + + buf = None + (tid, fname, line, col) = loc + buffers = self.get_user_buffers(fname) + is_selected = thread.GetIndexID() == process.GetSelectedThread().GetIndexID() + if len(buffers) == 1: + buf = buffers[0] + if buf != vim.current.buffer: + # Vim has an open buffer to the required file: select it + vim.command('execute ":%db"' % buf.number) + elif is_selected and vim.current.buffer.name not in fname and os.path.exists(fname) and goto_file: + # FIXME: If current buffer is modified, vim will complain when we try to switch away. + # Find a way to detect if the current buffer is modified, and...warn instead? + vim.command('execute ":e %s"' % fname) + buf = vim.current.buffer + elif len(buffers) > 1 and goto_file: + #FIXME: multiple open buffers match PC location + continue + else: + continue + + self.pcSigns.append(PCSign(buf, line, is_selected)) + + if is_selected and goto_file: + # if the selected file has a PC marker, move the cursor there too + curname = vim.current.buffer.name + if curname is not None and is_same_file(curname, fname): + move_cursor(line, 0) + elif move_cursor: + print "FIXME: not sure where to move cursor because %s != %s " % (vim.current.buffer.name, fname) + + def update_breakpoints(self, target, buffers): + """ Decorates buffer with signs corresponding to breakpoints in target. """ + + def GetBreakpointLocations(bp): + """ Returns a list of tuples (resolved, filename, line) where a breakpoint was resolved. """ + if not bp.IsValid(): + sys.stderr.write("breakpoint is invalid, no locations") + return [] + + ret = [] + numLocs = bp.GetNumLocations() + for i in range(numLocs): + loc = bp.GetLocationAtIndex(i) + desc = get_description(loc, lldb.eDescriptionLevelFull) + match = re.search('at\ ([^:]+):([\d]+)', desc) + try: + lineNum = int(match.group(2).strip()) + ret.append((loc.IsResolved(), match.group(1), lineNum)) + except ValueError as e: + sys.stderr.write("unable to parse breakpoint location line number: '%s'" % match.group(2)) + sys.stderr.write(str(e)) + + return ret + + + if target is None or not target.IsValid(): + return + + needed_bps = {} + for bp_index in range(target.GetNumBreakpoints()): + bp = target.GetBreakpointAtIndex(bp_index) + locations = GetBreakpointLocations(bp) + for (is_resolved, file, line) in GetBreakpointLocations(bp): + for buf in buffers: + if file in buf.name: + needed_bps[(buf, line, is_resolved)] = bp + + # Hide any signs that correspond with disabled breakpoints + del_list = [] + for (b, l, r) in self.breakpointSigns: + if (b, l, r) not in needed_bps: + self.breakpointSigns[(b, l, r)].hide() + del_list.append((b, l, r)) + for d in del_list: + del self.breakpointSigns[d] + + # Show any signs for new breakpoints + for (b, l, r) in needed_bps: + bp = needed_bps[(b, l, r)] + if self.haveBreakpoint(b.name, l): + self.markedBreakpoints[(b.name, l)].append(bp) + else: + self.markedBreakpoints[(b.name, l)] = [bp] + + if (b, l, r) not in self.breakpointSigns: + s = BreakpointSign(b, l, r) + self.breakpointSigns[(b, l, r)] = s + + def update(self, target, status, controller, goto_file=False): + """ Updates debugger info panels and breakpoint/pc marks and prints + status to the vim status line. If goto_file is True, the user's + cursor is moved to the source PC location in the selected frame. + """ + + self.paneCol.update(target, controller) + self.update_breakpoints(target, self.get_user_buffers()) + + if target is not None and target.IsValid(): + process = target.GetProcess() + if process is not None and process.IsValid(): + self.update_pc(process, self.get_user_buffers, goto_file) + + if status is not None and len(status) > 0: + print status + + def haveBreakpoint(self, file, line): + """ Returns True if we have a breakpoint at file:line, False otherwise """ + return (file, line) in self.markedBreakpoints + + def getBreakpoints(self, fname, line): + """ Returns the LLDB SBBreakpoint object at fname:line """ + if self.haveBreakpoint(fname, line): + return self.markedBreakpoints[(fname, line)] + else: + return None + + def deleteBreakpoints(self, name, line): + del self.markedBreakpoints[(name, line)] + + def showWindow(self, name): + """ Shows (un-hides) window pane specified by name """ + if not self.paneCol.havePane(name): + sys.stderr.write("unknown window: %s" % name) + return False + self.paneCol.prepare([name]) + return True + + def hideWindow(self, name): + """ Hides window pane specified by name """ + if not self.paneCol.havePane(name): + sys.stderr.write("unknown window: %s" % name) + return False + self.paneCol.hide([name]) + return True + +global ui +ui = UI() diff --git a/www/SB-api-coding-rules.html b/www/SB-api-coding-rules.html new file mode 100644 index 000000000000..0f7ef0e1eb5b --- /dev/null +++ b/www/SB-api-coding-rules.html @@ -0,0 +1,69 @@ + + + + + +LLDB Tutorial + + + +
+ The SB API Coding Rules +
+ +
+
+ +
+
+

SB API Coding Rules

+
+ + +

The SB APIs constitute the stable C++ API that lldb presents to external clients, + and which get processed by SWIG to produce the Python bindings to lldb. As such + it is important that they not suffer from the binary incompatibilities that C++ is + so susceptible to. We've established a few rules to ensure that this happens. + +

The classes in the SB API's are all called SB<SomeName>, where SomeName is in CamelCase + starting with an upper case letter. The method names are all CamelCase with initial + capital letter as well. + +

All the SB API classes are non-virtual, single inheritance classes. They should only include + SBDefines.h or other SB headers as needed. There should be no inlined method implementations + in the header files, they should all be in the implementation files. And there should be no + direct ivar access. + +

You also need to choose the ivars for the class with care, since you can't add or remove ivars + without breaking binary compatibility. In some cases, the SB class is a thin wrapper around + an interal lldb_private object. In that case, the class can have a single ivar, which is + either a pointer, shared_ptr or unique_ptr to the object in the lldb_private API. All the + lldb_private classes that get used this way are declared as opaque classes in lldb_forward.h, + which is included in SBDefines.h. So if you need an SB class to wrap an lldb_private class + that isn't in lldb_forward.h, add it there rather than making a direct opaque declaration in + the SB classes .h file. + +

If the SB Class needs some state of its own, as well as the backing object, don't include that + as a direct ivar in the SB Class. Instead, make an Impl class in the SB's .cpp file, and then + make the SB object hold a shared or unique pointer to the Impl object. The theory behind this is + that if you need more state in the SB object, those needs are likely to change over time, + and this way the impl class can pick up members without changing the size of the object. + An example of this is the SBValue class. + +

In order to fit into the Python API's, we need to be able to default construct all the SB objects. + Since the ivars of the classes are all pointers of one sort or other, this can easily be done, but + it means all the methods must be prepared to handle their opaque implementation pointer being + empty, and doing something reasonable. We also always have an "IsValid" method on all the SB + classes to report whether the object is empty or not. + +

Another piece of the SB API infrastructure is the Python (or other script interpreter) customization. + SWIG allows you to add property access, iterators and documentation to classes, but to do that you have to use + a Swig interface file in place of the .h file. Those files have a different format than a straight C++ header file. These + files are called SB<ClassName>.i, and live in "scripts/interface". They are constructed by + starting with the associated .h file, and adding documentation and the Python decorations, etc. We + do this in a decidedly low-tech way, by maintaining the two files in parallel. That simplifies the + build process, but it does mean that if you add a method to the C++ API's for an SB class, you have + to copy the interface to the .i file. +

+ + diff --git a/www/adding-language-support.html b/www/adding-language-support.html new file mode 100644 index 000000000000..b2bd5c5d1e8a --- /dev/null +++ b/www/adding-language-support.html @@ -0,0 +1,193 @@ + + + + + +Adding Programming Language Support to LLDB + + + +
+ The LLDB Debugger +
+
+
+ +
+
+

Adding Programming Language Support to LLDB

+
+

+ LLDB has been architected to make it straightforward to + add support for a programming language. Only a small + enum in core LLDB needs to be modified to make LLDB + aware of a new programming language. Everything else can + be supplied in derived classes that need not even be + present in the core LLDB repository. This makes it + convenient for developers adding language support either + in branches or downstream repositories since it + practically eliminates the potential for merge + conflicts. +

+

+ The basic steps needed are as follows: +

    +
  • Add the language to the LanguageType enum
  • +
  • Add a TypeSystem for the language
  • +
  • Add expression evaluation support
  • +
+

+

+ Additionally, you may want to create a Language and LanguageRuntime plugin for your language, which enables support for advanced features like dynamic typing and data formatting. +

+ +
+ +
+

Add the Language to the LanguageType enum

+
+

+ The LanguageType enum + (see lldb-enumerations.h) + contains a list of every language known to LLDB. It is + the one place where support for a language must live + that will need to merge cleanly with core LLDB if you + are developing your language support in a separate + branch. When adding support for a language previously + unknown to LLDB, start by adding an enumeration entry to + LanguageType. +

+
+ +
+
+

Add a TypeSystem for the Language

+
+

+ Both Module + and Target + support the retrieval of a TypeSystem instance via + GetTypeSystemForLanguage(). For Module, this method is + directly on the Module instance. For Target, this is + retrieved indirectly via the TypeSystemMap for the + Target instance. +

+

+ The TypeSystem instance returned by the Target is + expected to be capable of evaluating expressions, while + the TypeSystem instance returned by the Module is not. + If you will support expression evaluation for your + language, you could consider following one of these + approaches: +

    +
  • + implement a single TypeSystem class that supports + evaluation when given an optional Target, + implementing all the expression evaluation methods + on the TypeSystem in this case, OR +
  • +
  • + create multiple TypeSystem classes, one for + evaluation and one for static Module usage. +
  • +
+ + For clang and Swift, we chose to go with the latter, + primarily to make it clearer that evaluation with the + static Module-returned TypeSystem instances make no + sense, and have them error out on those calls. But + either approach is fine to pursue. +

+
+ +
+
+

Add Expression Evaluation Support

+
+

+ Expression Evaluation support is enabled by implementing + the relevant methods on a TypeSystem-derived class. + Search for "Expression" in the + TypeSystem header + to find relevant + methods to implement. +

+
+ +
+
+

Type Completion

+
+

+ There are three levels of type completion, each + requiring more type information: +

    +
  1. + Pointer size: when you have a forward decl or a + reference, and that's all you need. At this stage, + the pointer size is all you need. +
  2. +
  3. + Layout info: you need the size of an instance of the + type, but you still don't need to know all the guts + of the type. +
  4. +
  5. + Full type info. Here you need everything, because + you're playing with internals of it, such as + modifying a member variable. +
  6. +
+ Ensure you never complete more of a type than is needed + for a given situation. This will keep your type system + from doing more work than necessary. +

+
+ +
+
+

Creating Types

+
+

+ Your TypeSystem will need an approach for creating types + based on a set of Modules. If your type info is going + to come from DWARF info, you will want to subclass + DWARFASTParser. +

+
+ +
+ +
+

Language and LanguageRuntime plugins

+
+

+ If you followed the steps outlined above, you already have taught LLDB a great deal about your language. And if your language's runtime model and fundamental data types don't differ much from the C model, you are pretty much done. +
+ However, it is likely that your language offers its own data types for things like strings, arrays, ..., and probably has a notion of dynamic types, where the effective type of a variable can only be known at runtime. +

+

+ These tasks are covered by two plugins: +

    +
  • a LanguageRuntime plugin, which provides LLDB with a dynamic view of your language; this plugin answers questions that require a live process to acquire information (e.g. dynamic type resolution)
  • +
  • a Language plugin, which provides LLDB with a static view of your language; questions that are statically knoawble and do not require a process are answered by this plugin (e.g. data formatters)
  • +
+

+
+ +
+
+ +
+
+
+ + diff --git a/www/architecture.html b/www/architecture.html new file mode 100755 index 000000000000..eb178febde54 --- /dev/null +++ b/www/architecture.html @@ -0,0 +1,294 @@ + + + + + +LLDB Architecture + + + +
+ The LLDB Debugger +
+ +
+
+ + + +
+
+

Architecture

+
+ +

LLDB is a large and complex codebase. This section will help you become more familiar with + the pieces that make up LLDB and give a general overview of the general architecture.

+
+ +
+
+

Code Layout

+
+ +

LLDB has many code groupings that makeup the source base:

+ +
+ +
+ +
+

API

+
+ +

The API folder contains the public interface to LLDB.

+

We are currently vending a C++ API. In order to be able to add + methods to this API and allow people to link to our classes, + we have certain rules that we must follow:

+
    +
  • Classes can't inherit from any other classes.
  • +
  • Classes can't contain virtual methods.
  • +
  • Classes should be compatible with script bridging utilities like swig.
  • +
  • Classes should be lightweight and be backed by a single member. Pointers (or shared pointers) are the preferred choice since they allow changing the contents of the backend without affecting the public object layout.
  • +
  • The interface should be as minimal as possible in order to give a complete API.
  • +
+

By adhering to these rules we should be able to continue to + vend a C++ API, and make changes to the API as any additional + methods added to these classes will just be a dynamic loader + lookup and they won't affect the class layout (since they + aren't virtual methods, and no members can be added to the + class).

+
+ +
+ +
+

Breakpoint

+
+ +

A collection of classes that implement our breakpoint classes. + Breakpoints are resolved symbolically and always continue to + resolve themselves as your program runs. Whether settings breakpoints + by file and line, by symbol name, by symbol regular expression, + or by address, breakpoints will keep trying to resolve new locations + each time shared libraries are loaded. Breakpoints will of course + unresolve themselves when shared libraries are unloaded. Breakpoints + can also be scoped to be set only in a specific shared library. By + default, breakpoints can be set in any shared library and will continue + to attempt to be resolved with each shared library load.

+

Breakpoint options can be set on the breakpoint, + or on the individual locations. This allows flexibility when dealing + with breakpoints and allows us to do what the user wants.

+
+ +
+ +
+

Commands

+
+ +

The command source files represent objects that implement + the functionality for all textual commands available + in our command line interface.

+

Every command is backed by a lldb_private::CommandObject + or lldb_private::CommandObjectMultiword object.

+

lldb_private::CommandObjectMultiword are commands that + have subcommands and allow command line commands to be + logically grouped into a hierarchy.

+

lldb_private::CommandObject command line commands + are the objects that implement the functionality of the + command. They can optionally define + options for themselves, as well as group those options into + logical groups that can go together. The help system is + tied into these objects and can extract the syntax and + option groupings to display appropriate help for each + command.

+
+ +
+ +
+

Core

+
+ +

The Core source files contain basic functionality that + is required in the debugger. A wide variety of classes + are implemented:

+ +
    +
  • Address (section offset addressing)
  • +
  • AddressRange
  • +
  • Architecture specification
  • +
  • Broadcaster / Event / Listener
  • +
  • Communication classes that use Connection objects
  • +
  • Uniqued C strings
  • +
  • Data extraction
  • +
  • File specifications
  • +
  • Mangled names
  • +
  • Regular expressions
  • +
  • Source manager
  • +
  • Streams
  • +
  • Value objects
  • +
+
+ +
+ +
+

DataFormatters

+
+ +

A collection of classes that implement the data formatters subsystem.

+ +

For a general user-level introduction to data formatters, you can look here. +

A 10,000 foot view of the data formatters is based upon the DataVisualization class. + DataVisualization is the very high level entry point into the data formatters. It vends a stable interface in face of changing internals + and is the recommended entry point for components of LLDB that need to ask questions of the data formatters. + The main questions one can ask of DataVisualization are: +

    +
  • given a ValueObject, retrieve the formatters to be used for it
  • +
  • given a type, retrieve the formatters to be used for it. This is not an "exact" question, + i.e. one can retrieve a formatter from a type name which would not be used to then format ValueObjects of that type
  • +
  • given a name, retrieve a category of that name, optionally creating it if needed - more generally, categories management
  • +
  • given an identifier and a summary, store it as a named summary - more generally, named summary management
  • +
+ +

For people actively maintaining the data formatters subsystem itself, however, the FormatManager class is the relevant point of entry. + This class is subject to more frequent changes as the formatters evolve. Currently, it provides a thin caching layer on top of a list of categories + that each export a group of formatters. +

+

From an end-user perspective, the "type" LLDB command is the point of access to the data formatters. A large group of generally-useful formatters + is provided by default and loaded upon debugger startup. +

+ +
+ +
+

Expression

+
+ +

Expression parsing files cover everything from evaluating + DWARF expressions, to evaluating expressions using + Clang.

+

The DWARF expression parser has been heavily modified to + support type promotion, new opcodes needed for evaluating + expressions with symbolic variable references (expression local variables, + program variables), and other operators required by + typical expressions such as assign, address of, float/double/long + double floating point values, casting, and more. The + DWARF expression parser uses a stack of lldb_private::Value + objects. These objects know how to do the standard C type + promotion, and allow for symbolic references to variables + in the program and in the LLDB process (expression local + and expression global variables).

+

The expression parser uses a full instance of the Clang + compiler in order to accurately evaluate expressions. + Hooks have been put into Clang so that the compiler knows + to ask about identifiers it doesn't know about. Once + expressions have be compiled into an AST, we can then + traverse this AST and either generate a DWARF expression + that contains simple opcodes that can be quickly re-evaluated + each time an expression needs to be evaluated, or JIT'ed + up into code that can be run on the process being debugged.

+
+ +
+ +
+

Host

+
+ +

LLDB tries to abstract itself from the host upon which + it is currently running by providing a host abstraction + layer. This layer involves everything from spawning, detaching, + joining and killing native in-process threads, to getting + current information about the current host.

+

Host functionality includes abstraction layers for:

+
    +
  • Mutexes
  • +
  • Conditions
  • +
  • Timing functions
  • +
  • Thread functions
  • +
  • Host target triple
  • +
  • Host child process notifications
  • +
  • Host specific types
  • +
+
+ +
+ +
+

Interpreter

+
+ +

The interpreter classes are the classes responsible for + being the base classes needed for each command object, + and is responsible for tracking and running command line + commands.

+
+ +
+ +
+

Symbol

+
+

Symbol classes involve everything needed in order to parse + object files and debug symbols. All the needed classes + for compilation units (code and debug info for a source file), + functions, lexical blocks within functions, inlined + functions, types, declaration locations, and variables + are in this section.

+
+ +
+ +
+

Target

+
+ +

Classes that are related to a debug target include:

+
    +
  • Target
  • +
  • Process
  • +
  • Thread
  • +
  • Stack frames
  • +
  • Stack frame registers
  • +
  • ABI for function calling in process being debugged
  • +
  • Execution context batons
  • +
+
+ +
+ +
+

Utility

+
+ +

Utility files should be as stand alone as possible and + available for LLDB, plug-ins or related + applications to use.

+

Files found in the Utility section include:

+
    +
  • Pseudo-terminal support
  • +
  • Register numbering for specific architectures.
  • +
  • String data extractors
  • +
+
+ +
+
+
+
+ + diff --git a/www/architecture/index.html b/www/architecture/index.html new file mode 100755 index 000000000000..fd75603f6405 --- /dev/null +++ b/www/architecture/index.html @@ -0,0 +1,281 @@ + + + + + +LLDB Architecture + + + +
+ LLDB's Architecture +
+ +
+
+ + + +
+
+

Architecture

+
+ +

LLDB is a large and complex codebase. This section will help you become more familiar with + the pieces that make up LLDB and give a general overview of the general architecture.

+
+ +
+
+

Code Layout

+
+ +

LLDB has many code groupings that makeup the source base:

+ +
+ +
+ +
+

API

+
+ +

The API folder contains the public interface to LLDB.

+

We are currently vending a C++ API. In order to be able to add + methods to this API and allow people to link to our classes, + we have certain rules that we must follow:

+
    +
  • Classes can't inherit from any other classes.
  • +
  • Classes can't contain virtual methods.
  • +
  • Classes should be compatible with script bridging utilities like swig.
  • +
  • Classes should be lightweight and be backed by a single member. Pointers (or shared pointers) are the preferred choice since they allow changing the contents of the backend without affecting the public object layout.
  • +
  • The interface should be as minimal as possible in order to give a complete API.
  • +
+

By adhering to these rules we should be able to continue to + vend a C++ API, and make changes to the API as any additional + methods added to these classes will just be a dynamic loader + lookup and they won't affect the class layout (since they + aren't virtual methods, and no members can be added to the + class).

+
+ +
+ +
+

Breakpoint

+
+ +

A collection of classes that implement our breakpoint classes. + Breakpoints are resolved symbolically and always continue to + resolve themselves as your program runs. Whether settings breakpoints + by file and line, by symbol name, by symbol regular expression, + or by address, breakpoints will keep trying to resolve new locations + each time shared libraries are loaded. Breakpoints will of course + unresolve themselves when shared libraries are unloaded. Breakpoints + can also be scoped to be set only in a specific shared library. By + default, breakpoints can be set in any shared library and will continue + to attempt to be resolved with each shared library load.

+

Breakpoint options can be set on the breakpoint, + or on the individual locations. This allows flexibility when dealing + with breakpoints and allows us to do what the user wants.

+
+ +
+ +
+

Commands

+
+ +

The command source files represent objects that implement + the functionality for all textual commands available + in our command line interface.

+

Every command is backed by a lldb_private::CommandObject + or lldb_private::CommandObjectMultiword object.

+

lldb_private::CommandObjectMultiword are commands that + have subcommands and allow command line commands to be + logically grouped into a hierarchy.

+

lldb_private::CommandObject command line commands + are the objects that implement the functionality of the + command. They can optionally define + options for themselves, as well as group those options into + logical groups that can go together. The help system is + tied into these objects and can extract the syntax and + option groupings to display appropriate help for each + command.

+
+ +
+ +
+

Core

+
+ +

The Core source files contain basic functionality that + is required in the debugger. A wide variety of classes + are implemented:

+ +
    +
  • Address (section offset addressing)
  • +
  • AddressRange
  • +
  • Architecture specification
  • +
  • Broadcaster / Event / Listener
  • +
  • Communication classes that use Connection objects
  • +
  • Uniqued C strings
  • +
  • Data extraction
  • +
  • File specifications
  • +
  • Mangled names
  • +
  • Regular expressions
  • +
  • Source manager
  • +
  • Streams
  • +
  • Value objects
  • +
+
+ +
+ +
+

DataFormatters

+
+ +

A collection of classes that implement the data formatters subsystem.

+

Data formatters provide a set of user-tweakable hooks in the ValueObjects world that allow + to customize presentation aspects of variables. While users interact with formatters mostly through the + type command, inside LLDB there are a few layers to the implementation: DataVisualization at the highest + end of the spectrum, backed by classes implementing individual formatters, matching rules, ...

+ +

For a general user-level introduction to data formatters, you can look here. +

More details on the architecture are to be found here. +

+ +
+ +
+

Expression

+
+ +

Expression parsing files cover everything from evaluating + DWARF expressions, to evaluating expressions using + Clang.

+

The DWARF expression parser has been heavily modified to + support type promotion, new opcodes needed for evaluating + expressions with symbolic variable references (expression local variables, + program variables), and other operators required by + typical expressions such as assign, address of, float/double/long + double floating point values, casting, and more. The + DWARF expression parser uses a stack of lldb_private::Value + objects. These objects know how to do the standard C type + promotion, and allow for symbolic references to variables + in the program and in the LLDB process (expression local + and expression global variables).

+

The expression parser uses a full instance of the Clang + compiler in order to accurately evaluate expressions. + Hooks have been put into Clang so that the compiler knows + to ask about identifiers it doesn't know about. Once + expressions have be compiled into an AST, we can then + traverse this AST and either generate a DWARF expression + that contains simple opcodes that can be quickly re-evaluated + each time an expression needs to be evaluated, or JIT'ed + up into code that can be run on the process being debugged.

+
+ +
+ +
+

Host

+
+ +

LLDB tries to abstract itself from the host upon which + it is currently running by providing a host abstraction + layer. This layer involves everything from spawning, detaching, + joining and killing native in-process threads, to getting + current information about the current host.

+

Host functionality includes abstraction layers for:

+
    +
  • Mutexes
  • +
  • Conditions
  • +
  • Timing functions
  • +
  • Thread functions
  • +
  • Host target triple
  • +
  • Host child process notifications
  • +
  • Host specific types
  • +
+
+ +
+ +
+

Interpreter

+
+ +

The interpreter classes are the classes responsible for + being the base classes needed for each command object, + and is responsible for tracking and running command line + commands.

+
+ +
+ +
+

Symbol

+
+

Symbol classes involve everything needed in order to parse + object files and debug symbols. All the needed classes + for compilation units (code and debug info for a source file), + functions, lexical blocks within functions, inlined + functions, types, declaration locations, and variables + are in this section.

+
+ +
+ +
+

Target

+
+ +

Classes that are related to a debug target include:

+
    +
  • Target
  • +
  • Process
  • +
  • Thread
  • +
  • Stack frames
  • +
  • Stack frame registers
  • +
  • ABI for function calling in process being debugged
  • +
  • Execution context batons
  • +
+
+ +
+ +
+

Utility

+
+ +

Utility files should be as stand alone as possible and + available for LLDB, plug-ins or related + applications to use.

+

Files found in the Utility section include:

+
    +
  • Pseudo-terminal support
  • +
  • Register numbering for specific architectures.
  • +
  • String data extractors
  • +
+
+ +
+
+
+
+ + diff --git a/www/architecture/varformats.html b/www/architecture/varformats.html new file mode 100644 index 000000000000..6f530befc789 --- /dev/null +++ b/www/architecture/varformats.html @@ -0,0 +1,324 @@ + + + + + +LLDB Homepage + + + +
+ LLDB Data Formatters Architecture +
+ +
+
+ + + +
+
+

Bird's eye view

+
+

The LLDB data formatters subsystem is used to allow the debugger as well as the end-users to customize the way + their variables look upon inspection in the user interface (be it the command line tool, or one of the several + GUIs that are backed by LLDB) +

To this aim, they are hooked into the ValueObjects model, in order to provide entry points through which such customization + questions can be answered as what format should this number be printed as?, how many child elements does this + std::vector have? and more along those lines +

The architecture of the subsystem is layered, with the highest level layer being the user visible interaction features + (e.g. the "type ***" commands, the SB classes, ...). Other layers of interest that will be analyzed in this document include +

    +
  • Classes implementing individual data formatter types
  • +
  • Classes implementing formatters navigation, discovery and categorization
  • +
  • The FormatManager layer
  • +
  • The DataVisualization layer
  • +
  • The SWIG LLDB <---> communication layer
  • +
+
+ +
+
+

Data formatter types

+
+

As described in the user documentation, there are four types of formatters +

    +
  • formats
  • +
  • summaries
  • +
  • filters
  • +
  • synthetic children
  • +
+

Architecturally, these are implemented by classes in the source/DataFormatters/ folder
+ Formatters have descriptor classes, Type*Impl, which contain at least a "Flags" nested object, which contains both rules to be used + by the matching algorithm (e.g. should the formatter for type Foo apply to a Foo*?) or rules to be used + by the formatter itself (e.g. is this summary a oneliner?) +

Individual formatter descriptor classes then also contain data items useful to them for performing their functionality. + For instance TypeFormatImpl (backing formats) contains an lldb::Format that is the format to then be applied + were this formatter to be selected. Upon issuing a "type format add", a new TypeFormatImpl is created that wraps + the user-specified format, and matching options:

+ entry.reset(new TypeFormatImpl(format, + TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). + SetSkipPointers(m_command_options.m_skip_pointers). + SetSkipReferences(m_command_options.m_skip_references)));

+

While formats are fairly simple and only implemented by one class, the other formatter types are backed by a class hierarchy +

Summaries, for instance, can exist in one of three "flavors": +

    +
  • summary strings
  • +
  • Python script
  • +
  • native C++
  • +
+

The base class for summaries, TypeSummaryImpl, is a pure virtual class that wraps, again, the Flags, and exports among others a +

+ virtual bool + FormatObject (ValueObject *valobj, + std::string& dest) = 0; +

+

This is the core entry point, which allows subclasses to specify their mode of operation +

StringSummaryFormat, which is the class that implements summary strings, does a check as to whether + the summary is a one-liner, and if not, then uses its stored summary string to call into + Debugger::FormatPrompt, and obtain a string back, which it returns in dest as the resulting summary +

For a Python summary, implemented in ScriptSummaryFormat, FormatObject() calls into the ScriptInterpreter + which is supposed to hold the knowledge on how to bridge back and forth with the scripting language + (Python in the case of LLDB) in order to produce a valid string. Implementors of new ScriptInterpreters for other + languages are expected to provide a GetScriptedSummary() entrypoint for this purpose, if they desire to allow + users to provide formatters in the new language +

Lastly, C++ summaries (CXXFunctionSummaryFormat), wrap a function pointer and call into it to execute their duty. + It should be noted that there are no facilities for users to interact with C++ formatters, and as such they are extremely + opaque, effectively being a thin wrapper between plain function pointers and the LLDB formatters subsystem.
+ Also, dynamic loading of C++ formatters in LLDB is currently not implemented, and as such it is safe and reasonable + for these formatters to deal with internal ValueObjects instances instead of public SBValue objects +

An interesting data point is that summaries are expected to be stateless. While at the Python layer they are handed + an SBValue (since nothing else could be visible for scripts), it is not expected that the SBValue should be cached + and reused - any and all caching occurs on the LLDB side, completely transparent to the formatter itself


+

The design of synthetic children is somewhat more intricate, due to them being stateful objects.
+ The core idea of the design is that synthetic children act like a two-tier model, in which there is a backend + dataset (the underlying unformatted ValueObject), and an higher level view (frontend) which vends the computed + representation +

To implement a new type of synthetic children one would implement a subclass of SyntheticChildren, which akin to the TypeFormatImpl, + contains Flags for matching, and data items to be used for formatting. For instance, TypeFilterImpl (which implements filters), + stores the list of expression paths of the children to be displayed.
Filters are themselves synthetic children. Since all they + do is provide child values for a ValueObject, it does not truly matter whether these come from the real set of children or are + crafted through some intricate algorithm. As such, they perfectly fit within the realm of synthetic children and are only + shown as separate entities for user friendliness (to a user, picking a subset of elements to be shown with relative ease is a + valuable task, and they should not be concerned with writing scripts to do so) +

Once the descriptor of the synthetic children has been coded, in order to hook it up, one has to implement a subclass of + SyntheticChildrenFrontEnd. For a given type of synthetic children, there is a deep coupling with the matching front-end class, + given that the front-end usually needs data stored in the descriptor (e.g. a filter needs the list of child elements) +

The front-end answers the interesting questions that are the true raison d'être of synthetic children: +
+ +

    +
  • + virtual size_t + CalculateNumChildren () = 0; +
  • +
  • + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx) = 0; +
  • +
  • + virtual size_t + GetIndexOfChildWithName (const ConstString &name) = 0; +
  • +
  • + virtual bool + Update () = 0; +
  • +
  • + virtual bool + MightHaveChildren () = 0; +
  • +
+
+

Synthetic children providers (their front-ends) will be queried by LLDB for a number of children, and then for each of them + as necessary, they should be prepared to return a ValueObject describing the child. They might also be asked to provide a + name-to-index mapping (e.g. to allow LLDB to resolve queries like myFoo.myChild)
+ Update() and MightHaveChildren() are described in the user documentation, and they mostly serve bookkeeping purposes +

LLDB provides three kinds of synthetic children: filters, scripted synthetics, and the native C++ providers
+ Filters are implemented by TypeFilterImpl/TypeFilterImpl::FrontEnd

+ Scripted synthetics are implemented by ScriptedSyntheticChildren/ScriptedSyntheticChildren::FrontEnd, plus + a set of callbacks provided by the ScriptInterpteter infrastructure to allow LLDB to pass the front-end queries + down to the scripting languages

+ As for C++ native synthetics, there is a CXXSyntheticChildren, but no corresponding FrontEnd class. The reason for this design is + that CXXSyntheticChildren store a callback to a creator function, which is responsible for providing a FrontEnd. + Each individual formatter (e.g. LibstdcppMapIteratorSyntheticFrontEnd, NSDictionaryMSyntheticFrontEnd, ...) is a standalone + frontend, and once created retains to relation to its underlying SyntheticChildren object +

On a ValueObject level, upon being asked to generate synthetic children for a ValueObject, LLDB spawns a ValueObjectSynthetic object + which is a subclass of ValueObject. Building upon the ValueObject infrastructure, it stores a backend, and a shared pointer to + the SyntheticChildren.
+ Upon being asked queries about children, it will use the SyntheticChildren to generate a front-end for itself + and will let the front-end answer questions. The reason for not storing the FrontEnd itself is that there is no guarantee that across + updates, the same FrontEnd will be used over and over (e.g. a SyntheticChildren object could serve an entire class hierarchy + and vend different frontends for different subclasses) +

+ +
+
+

Formatters matching

+
+

The problem of formatters matching is going from + "I have a ValueObject" to "these are the formatters to be used for it"
+ There is a rather intricate set of user rules that are involved, and a rather intricate implementation of this model. All of these + relate to the type of the ValueObject. It is assumed that types are a strong enough contract that it is possible to format an object + entirely depending on its type. If this turns out to not be correct, then the existing model will have to be changed fairly deeply. +

The basic building block is that formatters can match by exact type name or by regular expressions, i.e. one can describe matching + by saying things like "this formatters matches type __NSDictionaryI", or "this formatter matches all type names like ^std::__1::vector<.+>(( )?&)?$"
This match happens in class FormattersContainer. For exact matches, this goes straight to the FormatMap + (the actual storage area for formatters), whereas for regular expression matches the regular expression is matched against the + provided candidate type name. If one were to introduce a new type of matching (say, match against number of $ signs present + in the typename, FormattersContainer is the place where such a change would have to be introduced).
It should be noted that this + code involves template specialization, and as such is somewhat trickier than other formatters code to update. +

On top of the string matching mechanism (exact or regex), there are a set of more advanced rules implemented + by the FormattersContainer, + with the aid of the FormattersMatchCandidate. Namely, it is assumed that any formatter class will have flags to say whether + it allows cascading (i.e. seeing through typedefs), allowing pointers-to-object and reference-to-object to be formatted. +
Upon verifying that a formatter would be a textual match, the Flags are checked, and if they do not allow the formatter + to be used (e.g. pointers are not allowed, and one is looking at a Foo*), then the formatter is rejected and the search continues. + If the flags also match, then the formatter is returned upstream and the search is over. +

One relevant fact to notice is that this entire mechanism is not dependent on the kind of formatter to be returned, which makes it + easier to devise new types of formatters as the lowest layers of the system. The demands on individual formatters are that they + define a few typedefs, and export a Flags object, and then they can be freely matched against types as needed. +

This mechanism is replicated across a number of categories. A category is a named bucket where formatters are grouped on + some basis. The most common reason for a category to exist is a library (e.g. libcxx formatters vs. libstdcpp formatters). +
+ Categories can be enabled or disabled, and they have a priority number, called position. The priority sets a strong order among + enabled categories. A category named "default" is always the highest priority one and it's the category where all formatters that + do not ask for a category of their own end up (e.g. "type summary add ...." without a "-w somecategory" flag passed)
+ The algorithm inquires each category, in the order of their priorities, for a formatter for a type, and upon receiving a positive + answer from a category, ends the search. Of course, no search occurs in disabled categories. +

At the individual category level, there is the first dependence on the type of formatter to be returned. Since both filters and + synthetic children proper are implemented through the same backing store, the matching code needs to ensure that, were both a + synthetic children provider and a filter to match a type, only the most recently added one is actually used. +
The details of the algorithm used are to be found in TypeCategoryImpl::Get().
+

It is quite obvious, even to a casual reader, that there are a number of complexities involved in this algorithm.
+ For starters, the entire search process has to be repeated for every variable.
+ Moreover, for each category, one has to repeat the entire process of crawling the types (go to pointee, ...).
+ This is exactly the algorithm initially implemented by LLDB. Over the course of the life of the formatters subsystem, + two main evolutions have been made to the matching mechanism: +

    +
  • A caching mechanism
  • +
  • A pregeneration of all possible type matches
  • +
+

The cache is a layer that sits between the FormatManager and the TypeCategoryMap. Upon being asked to figure out a formatter, + the FormatManager will first query the cache layer, and only if that fails, will the categories be queried using the full + search algorithm. The result of that full search will then be stored in the cache. Even a negative answer (no formatter) + gets stored. The negative answer is actually the most beneficial to cache as obtaining it requires traversing all possible + formatters in all categories just to get a no-op back.
+ Of course, once an answer is cached, getting it will be much quicker than going to a full category search, as the cached + answers are of the form "type foo" --> "formatter bar". But given how formatters can be edited or removed by the user, + either at the command line or via the API, there needs to be a way to invalidate the cache.
+ This happens through the FormatManager::Changed() method. In general, anything that changes the formatters causes + FormatManager::Changed() to be called through the IFormatChangeListener interface. This call increases the + FormatManager's revision and clears the cache. The revision number is a monotonically increasing integer counter + that essentially corresponds to the number of changes made to the formatters throughout the current LLDB session. + This counter is used by ValueObjects to know when their formatters are out of date. Since a search is a potentially + expensive operation, before caching was introduced, individual ValueObjects remembered which revision of the FormatManager + they used to search for their formatter, and stored it, so that they would not repeat the search unless a change in the + formatters had occurred. While caching has made this less critical of an optimization, it is still sensible and thus is kept. +
Lastly, as a side note, it is worth highlighting that any change in the formatters invalidates the + entire cache. It would likely not be impossible to be smarter and figure out a subset of cache entries + to be deleted, letting others persist, instead of having to rebuild the entire cache from scratch. However, given that formatters + are not that frequently changed during a debug session, and the algorithmic complexity to "get it right" seems larger than the + potential benefit to be had from doing it, the full cache invalidation is the chosen policy. The algorithm to selectively invalidate + entries is probably one of the major areas for improvements in formatters performance. +

The second major optimization, introduced fairly recently, is the pregeneration of type matches. The original algorithm was based upon + the notion of a FormatNavigator as a smart object, aware of all the intricacies of the matching rules. For each category, the + FormatNavigator would generate the possible matches (e.g. dynamic type, pointee type, ...), and check each one, one at a time. + If that failed for a category, the next one would again generate the same matches.
+ This worked well, but was of course inefficient. The FormattersMatchCandidate is the solution to this performance issue. + In top-of-tree LLDB, the FormatManager has the centralized notion of the matching rules, and the former FormatNavigators are now + FormattersContainers, whose only job is to guarantee a centralized storage of formatters, and thread-safe access to such storage. +
FormatManager::GetPossibleMatches() fills a vector of possible matches. The way it works is by applying each rule, + generating the corresponding typename, and storing the typename, plus the required Flags for that rule to be accepted + as a match candidate (e.g. if the match comes by fetching the pointee type, a formatter that matches will have to allow pointees + as part of its Flags object). The TypeCategoryMap, when tasked with finding a formatter for a type, generates all possible matches + and passes them down to each category. In this model, the type system only does its (expensive) job once, and textual or regex + matches are the core of the work. +

+ +
+
+

FormatManager and DataVisualization

+
+

There are two main entry points in the data formatters: the FormatManager and the DataVisualization
+ The FormatManager is the internal such entry point. In this context, internal refers to data formatters code + itself, compared to other parts of LLDB. For other components of the debugger, the DataVisualization provides a more + stable entry point. On the other hand, the FormatManager is an aggregator of all moving parts, and as such is less stable + in the face of refactoring.
People involved in the data formatters code itself, however, will most likely have to confront + the FormatManager for significant architecture changes. +

The FormatManager wraps a TypeCategoryMap (the list of all existing categories, enabled and not), the FormatCache, and several + utility objects. Plus, it is the repository of named summaries, since these don't logically belong anywhere else.
+ It is also responsible for creating all builtin formatters upon the launch of LLDB. It does so through a bunch + of methods Load***Formatters(), invoked as part of its constructor. The original design of data formatters anticipated + that individual libraries would load their formatters as part of their debug information. This work however has largely been + left unattended in practice, and as such core system libraries (mostly those for OSX/iOS development as of today) load their + formatters in an hardcoded fashion. +

For performance reasons, the FormatManager is constructed upon being first required. + This happens through the DataVisualization layer. Upon first being inquired for anything formatters, DataVisualization + calls its own local static function GetFormatManager(), which in turns constructs and returns a local static FormatManager.
+ Unlike most things in LLDB, the lifetime of the FormatManager is the same as the entire session, rather than a specific Debugger + or Target instance. This is an area to be improved, but as of now it has not caused enough grief to warrant action. If this work + were to be undertaken, one could conceivably devise a per-architecture-triple model, upon the assumption that an OS and CPU + combination are a good enough key to decide which formatters apply (e.g. Linux i386 is probably different from OSX x86_64, but two + OSX x86_64 targets will probably have the same formatters; of course versioning of the underlying OS is also to be considered, + but experience with OSX has shown that formatters can take care of that internally in most cases of interest). +

The public entry point is the DataVisualization layer. DataVisualization is a static class on which questions can be asked + in a relatively refactoring-safe manner. +
The main question asked of it is to obtain formatters for ValueObjects (or typenames). + One can also query DataVisualization for named summaries or individual categories, but of course those queries delve deeper + in the internal object model.
As said, the FormatManager holds a notion of revision number, which changes every time + formatters are edited (added, deleted, categories enabled or disabled, ...). Through DataVisualization::ForceUpdate() one + can cause the same effects of a formatters edit to happen without it actually having happened.
+ The main reason for this feature is that formatters can be dynamically created in Python, and one can then enter the + ScriptInterpreter and edit the formatter function or class. If formatters were not updated, one could find them to be out of sync + with the new definitions of these objects. To avoid the issue, whenever the user exits the scripting mode, formatters force + an update to make sure new potential definitions are reloaded on demand. +

+ +
+
+

The SWIG layer

+
+

In order to implement formatters written in Python, LLDB requires that ScriptInterpreter implementations provide a set + of functions that one can call to ask formatting questions of scripts.
+ For instance, in order to obtain a scripting summary, LLDB calls +
+ virtual bool
+ GetScriptedSummary (const char *function_name,
+ llldb::ValueObjectSP valobj,
+ lldb::ScriptInterpreterObjectSP& callee_wrapper_sp,
+ std::string& retval)
+

+

For Python, this function is implemented by first checking if the callee_wrapper_sp is valid. + If so, LLDB knows that it does not need to search a function with the passed name, and can directly + call the wrapped Python function object. Either way, the call is routed to a global callback g_swig_typescript_callback +

This callback pointer points to LLDBSwigPythonCallTypeScript, defined in python-wrapper.swig
+ The details of the implementation require familiarity with the Python C API, plus a few utility objects defined + by LLDB to ease the burden of dealing with the scripting world. However, as a sketch of what happens, the code + tries to find a Python function object with the given name (i.e. if you say "type summary add -F module.function", LLDB will scan + for "module" module, and then for a function named "function" inside the module's namespace). If the function object is found, + it is wrapped in a PyCallable, which is an LLDB utility class that wraps the callable and allows for easier calling. + The callable gets invoked, and the return value, if any, is cast into a string. Originally, if a non-string object was returned, + LLDB would refuse to use it. This disallowed such simple construct as +
def getSummary(value,*args):
   return 1
from working +

Similar considerations apply to other formatter (and non-formatter related) scripting callbacks +

+ +
+
+

Conclusion

+
+

This document is an introduction to the design of the LLDB data formatters subsystem
+ The intended target audience are people interested in understanding or modifying the formatters themselves + rather than writing a specific data formatter. For this latter purpose, the user documentation about formatters + is the main relevant document which one should refer to. +

On the other hand, this one page highlights some open areas for improvement to the general subsystem, and more evolutions + not anticipated here are certainly possible. As usual, the lldb-dev mailing list is the point of first contact for + discussing desired new features or changes of existing features. +

+ +
+
+
+
+ + diff --git a/www/build.html b/www/build.html new file mode 100755 index 000000000000..787ce6220a2c --- /dev/null +++ b/www/build.html @@ -0,0 +1,551 @@ + + + + + + Building LLDB + + +
+ The LLDB Debugger +
+ +
+
+ + + +
+

Continuous Integration

+
+

+ The following LLVM buildbots build and test LLDB trunk: +

+

+
+ +
+

Building LLDB

+ +
+ +
+

Building LLDB on Windows

+
+

Required Dependencies

+ +

Preliminaries

+

+ This section describes how to set up your system and install the required dependencies such that + they can be found when needed during the build process. The steps outlined here only need to + be performed once. +

+
    +
  1. Install Visual Studio and the Windows SDK.

  2. +
  3. +

    + Build Python from source using the solution file supplied with the Python 2.7 source + distribution. +

    +

    + Because LLDB functionality is compiled into a Python extension module, + the extension module must be compiled with the same version of Visual Studio that + Python itself was compiled with. The binary release of Python 2.7 is compiled with + Visual Studio 2008, so it is incompatible with linking against LLDB. +

    +

    + Note that if you plan to do both debug and release builds of LLDB, you will need to + compile both debug and release builds of Python. The same applies if you plan to build + both x86 and x64 configurations of LLDB +

    +
  4. +
  5. +

    Copy <python src dir>\PC\pyconfig.h to <python src dir>\Include.

    +

    + This is necessary because pyconfig.h is a hand-maintained file which is platform specific, + so multiple copies of this file are included with each source distribution. It appears to + be up to the person building Python to move the correct version of pyconfig.h to the Include + folder. +

    +
  6. +
  7. +

    + Run lldb/scripts/install_custom_python.py so to "install" your custom build of Python to a + canonical directory structure. +

    +
  8. +
  9. Install GnuWin32, making sure <GnuWin32 install dir>\bin is added to your PATH environment variable.

  10. +
  11. Install SWIG for Windows, making sure <SWIG install dir> is added to your PATH environment variable.

  12. +
+

Building LLDB

+

+ Any command prompt from which you build LLDB should have a valid Visual Studio environment setup. + This means you should run vcvarsall.bat or open an appropriate Visual Studio Command Prompt + corresponding to the version you wish to use. +

+

Finally, when you are ready to build LLDB, generate CMake with the following command line:

+ cmake -G Ninja <cmake variables> <path to root of llvm src tree> +

and run ninja to build LLDB. Information about running the LLDB test suite can be found on the test page.

+

+ Following is a description of some of the most important CMake variables which you are likely to encounter. + A variable FOO is set by adding -DFOO=value to the CMake command line. +

+
    +
  • + LLDB_TEST_DEBUG_TEST_CRASHES (Default=0): If set to 1, will cause Windows to generate a crash + dialog whenever lldb.exe or the python extension module crashes while running the test suite. If set to + 0, LLDB will silently crash. Setting to 1 allows a developer to attach a JIT debugger at the time of + a crash, rather than having to reproduce a failure or use a crash dump. +
  • +
  • + PYTHON_HOME (Required): Path the folder you specified in the --dest argument to install_custom_python.py. + Note that install_custom_python.py will create x86 and x64 subdirectories under this folder. PYTHON_HOME should + refer to the correct architecture-specific folder. +
  • +
  • + LLDB_RELOCATABLE_PYTHON (Default=0): When this is 0, LLDB will bind statically to the location specified + in the PYTHON_HOME CMake variable, ignoring any value of PYTHONHOME set in the environment. This is most useful for + developers who simply want to run LLDB after they build it. If you wish to move a build of LLDB to a different + machine where Python will be in a different location, setting LLDB_RELOCATABLE_PYTHON to 1 will cause Python to + use its default mechanism for finding the python installation at runtime (looking for installed Pythons, or using + the PYTHONHOME environment variable if it is specified). +
  • +
+
+
+
+

Building LLDB on Mac OS X

+
+

Building on Mac OS X is as easy as downloading the code and building the Xcode project or workspace:

+
+
+

Preliminaries

+
    +
  • XCode 4.3 or newer requires the "Command Line Tools" component (XCode->Preferences->Downloads->Components).
  • +
  • Mac OS X Lion or newer requires installing Swig.
  • +
+

Building LLDB

+
    +
  • Download the lldb sources.
  • +
  • Follow the code signing instructions in lldb/docs/code-signing.txt
  • +
  • In Xcode 3.x: lldb/lldb.xcodeproj, select the lldb-tool target, and build.
  • +
  • In Xcode 4.x: lldb/lldb.xcworkspace, select the lldb-tool scheme, and build.
  • +
+
+ +
+
+

Building LLDB on Linux, FreeBSD and NetBSD

+
+

This document describes the steps needed to compile LLDB on most Linux systems, FreeBSD and NetBSD.

+
+
+

Preliminaries

+

+ LLDB relies on many of the technologies developed by the larger LLVM project. + In particular, it requires both Clang and LLVM itself in order to build. Due to + this tight integration the Getting Started guides for both of these projects + come as prerequisite reading: +

+ +

Supported compilers for building LLDB on Linux include:

+
    +
  • Clang 3.2
  • +
  • GCC 4.6.2 (later versions should work as well)
  • +
+

It is recommended to use libstdc++ 4.6 (or higher) to build LLDB on Linux, but using libc++ is also known to work.

+

+ On FreeBSD the base system Clang and libc++ may be used to build LLDB, + or the GCC port or package. +

+

+ On NetBSD the base system GCC and libstdc++ are used to build LLDB, + Clang/LLVM and libc++ should also work. +

+

+ In addition to any dependencies required by LLVM and Clang, LLDB needs a few + development packages that may also need to be installed depending on your + system. The current list of dependencies are: +

+ +

So for example, on a Fedora system one might run:

+ > yum install libedit-devel libxml2-devel ncurses-devel python-devel swig +

On a Debian or Ubuntu system one might run:

+ > sudo apt-get install build-essential subversion swig python2.7-dev libedit-dev libncurses5-dev +

or

+ > sudo apt-get build-dep lldb-3.3 # or lldb-3.4 +

On FreeBSD one might run:

+ > pkg install swig python +

On NetBSD one might run:

+ > pkgin install swig python27 cmake ninja-build +

If you wish to build the optional reference documentation, additional dependencies are required:

+
    +
  • Graphviz (for the 'dot' tool). +
  • +
  • doxygen (only if you wish to build the C++ API reference) +
  • +
  • epydoc (only if you wish to build the Python API reference) +
  • +
+

To install the prerequisites for building the documentation (on Debian/Ubuntu) do:

+ +
> sudo apt-get install doxygen graphviz +
> sudo pip install epydoc # or install package python-epydoc +
+

Building LLDB

+

+ We first need to checkout the source trees into the appropriate locations. Both + Clang and LLDB build as subprojects of LLVM. This means we will be checking out + the source for both Clang and LLDB into the tools subdirectory of LLVM. We + will be setting up a directory hierarchy looking something like this: +

+

+

  
+                  llvm
+                  |
+                  `-- tools
+                      |
+                      +-- clang
+                      |
+                      `-- lldb
+                
+

+

+ For reference, we will call the root of the LLVM project tree $llvm, and the + roots of the Clang and LLDB source trees $clang and $lldb respectively. +

+

Change to the directory where you want to do development work and checkout LLVM:

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

Now switch to LLVM’s tools subdirectory and checkout both Clang and LLDB:

+ + > cd $llvm/tools +
> svn co http://llvm.org/svn/llvm-project/cfe/trunk clang +
> svn co http://llvm.org/svn/llvm-project/lldb/trunk lldb +
+ +

+ In general, building the LLDB trunk revision requires trunk revisions of both + LLVM and Clang. +

+

+ It is highly recommended that you build the system out of tree. Create a second + build directory and configure the LLVM project tree to your specifications as + outlined in LLVM’s Getting Started Guide. A typical build procedure + might be: +

+ + > cd $llvm/.. +
> mkdir build +
> cd build +
+

To build with CMake

+

+ Using CMake is documented on the Building LLVM with CMake + page. Building LLDB is possible using one of the following generators: +

+
    +
  • Ninja
  • +
  • Unix Makefiles
  • +
+

Using CMake + Ninja

+

+ Ninja is the fastest way to build LLDB! In order to use ninja, you need to have recent versions of CMake and + ninja on your system. To build using ninja: +

+ + > cmake .. -G Ninja +
> ninja lldb +
> ninja check-lldb +
+

Using CMake + Unix Makefiles

+

If you do not have Ninja, you can still use CMake to generate Unix Makefiles that build LLDB:

+ + > cmake .. +
> make +
> make check-lldb +
+

To build with autoconf

+

+ If you do not have CMake, it is still possible to build LLDB using the autoconf build system. If you are using + Clang or GCC 4.8+, run: +

+ + > $llvm/configure +
> make +
+

If you are building with a GCC that isn't the default gcc/g++, like gcc-4.9/g++-4.9

+ + > $llvm/configure CC=gcc-4.9 CXX=g++-4.9 +
> make CC=gcc-4.9 CXX=g++-4.9 +
+

+ If you are running in a system that doesn't have a lot of RAM (less than 4GB), you might want to disable + debug symbols by specifying DEBUG_SYMBOLS=0 when running make. You will know if you need to enable this + because you will fail to link clang (the linker will get a SIGKILL and exit with status 9). +

+ + > make DEBUG_SYMBOLS=0 + +

To run the LLDB test suite, run:

+ +
> make -C tools/lldb/test +
+

+ Note that once both LLVM and Clang have been configured and built it is not + necessary to perform a top-level make to rebuild changes made only to LLDB. + You can run make from the build/tools/lldb subdirectory as well. +

+

+ If you wish to build with libc++ instead of libstdc++ (the default), run configure with the + --enable-libcpp flag. +

+

If you wish to build a release version of LLDB, run configure with the --enable-optimized flag.

+ +

Building API reference documentation

+

+ LLDB exposes a C++ as well as a Python API. To build the reference documentation for these two APIs, ensure you have + the required dependencies installed, and build the lldb-python-doc and lldb-cpp-doc CMake targets. +

+

The output HTML reference documentation can be found in <build-dir>/tools/lldb/docs/.

+

Additional Notes

+

+

+ LLDB has a Python scripting capability and supplies its own Python module named lldb. + If a script is run inside the command line lldb application, the Python module + is made available automatically. However, if a script is to be run by a Python interpreter + outside the command line application, the PYTHONPATH environment variable can be used + to let the Python interpreter find the lldb module. +

+

+ Current stable NetBSD release doesn't ship with libpanel(3), therefore it's required to disable curses(3) support with + the -DLLDB_DISABLE_CURSES:BOOL=TRUE option. To make sure check if /usr/include/panel.h exists in your + system. +

+

The correct path can be obtained by invoking the command line lldb tool with the -P flag:

+ > export PYTHONPATH=`$llvm/build/Debug+Asserts/bin/lldb -P` +

+ If you used a different build directory or made a release build, you may need to adjust the + above to suit your needs. To test that the lldb Python module + is built correctly and is available to the default Python interpreter, run: +

+ > python -c 'import lldb'

+ +

Cross-compiling LLDB

+

+ In order to debug remote targets running different architectures than your host, you + will need to compile LLDB (or at least the server component) for the target. While + the easiest solution is to just compile it locally on the target, this is often not + feasible, and in these cases you will need to cross-compile LLDB on your host. +

+ +

+ Cross-compilation is often a daunting task and has a lot of quirks which depend on + the exact host and target architectures, so it is not possible to give a universal + guide which will work on all platforms. However, here we try to provide an overview + of the cross-compilation process along with the main things you should look out for. +

+ +

+ First, you will need a working toolchain which is capable of producing binaries for + the target architecture. Since you already have a checkout of clang and lldb, you + can compile a host version of clang in a separate folder and use that. + Alternatively you can use system clang or even cross-gcc if your distribution + provides such packages (e.g., g++-aarch64-linux-gnu on Ubuntu). On + Android, a working toolchain can be produced by downloading the Android NDK and + running the contained make-standalone-toolchain.sh script. +

+ +

+ Next, you will need a copy of the required target headers and libraries on your + host. The libraries can be usually obtained by copying from the target machine, + however the headers are often not found there, especially in case of embedded + platforms. In this case, you will need to obtain them from another source, either + a cross-package if one is available, or cross-compiling the respective library from + source. Fortunately the list of LLDB dependencies is not big and if you are only + interested in the server component, you can reduce this even further by passing the + appropriate cmake options, such as: +

+ + -DLLDB_DISABLE_LIBEDIT=1
+ -DLLDB_DISABLE_CURSES=1
+ -DLLDB_DISABLE_PYTHON=1
+ -DLLVM_ENABLE_TERMINFO=0 +
+

+ In this case you, will often not need anything other than the standard C and C++ + libraries. +

+ +

+ In the case of Android, all required headers and libraries are provided by the + aforementioned make-standalone-toolchain.sh script. +

+ +

+ Once all of the dependencies are in place, it's just a matter of configuring the + build system with the locations and arguments of all the necessary tools. The most + important cmake options here are: +

+
+
CMAKE_CROSSCOMPILING
+
Set to 1 to enable cross-compilation.
+ +
CMAKE_LIBRARY_ARCHITECTURE
+
Affects the cmake search path when looking for libraries. You may need to set + this to your architecture triple if you do not specify all your include and + library paths explicitly.
+ +
CMAKE_C_COMPILER, CMAKE_CXX_COMPILER
+
C and C++ compilers for the target architecture
+ +
CMAKE_C_FLAGS, CMAKE_CXX_FLAGS
+
The flags for the C and C++ target compilers. You may need to specify the + exact target cpu and abi besides the include paths for the target headers.
+ +
CMAKE_EXE_LINKER_FLAGS
+
The flags to be passed to the linker. Usually just a list of library search + paths referencing the target libraries.
+ +
LLVM_TABLEGEN, CLANG_TABLEGEN
+
Paths to llvm-tblgen and clang-tblgen for the host architecture. If + you already have built clang for the host, you can point these variables to the + executables in your build directory. If not, you will need to build the + llvm-tblgen and clang-tblgen host targets at least.
+ +
LLVM_HOST_TRIPLE
+
The triple of the system that lldb (or lldb-server) will run on. Not setting + this (or setting it incorrectly) can cause a lot of issues with remote debugging + as a lot of the choices lldb makes depend on the triple reported by the remote + platform.
+
+

+ You can of course also specify the usual cmake options like CMAKE_BUILD_TYPE, etc. +

+ +

Example 1: Cross-compiling for linux arm64 on Ubuntu host

+ +

+ Ubuntu already provides the packages necessary to cross-compile LLDB for arm64. It + is sufficient to install packages gcc-aarch64-linux-gnu, g++-aarch64-linux-gnu, + binutils-aarch64-linux-gnu. Then it is possible to prepare the cmake build with the + following parameters: +

+ + -DCMAKE_CROSSCOMPILING=1 \
+ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
+ -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
+ -DLLVM_HOST_TRIPLE=aarch64-unknown-linux-gnu \
+ -DLLVM_TABLEGEN=<path-to-host>/bin/llvm-tblgen \
+ -DCLANG_TABLEGEN=<path-to-host>/bin/clang-tblgen \
+ -DLLDB_DISABLE_PYTHON=1 \
+ -DLLDB_DISABLE_LIBEDIT=1 \
+ -DLLDB_DISABLE_CURSES=1 +
+ +

+ An alternative (and recommended) way to compile LLDB is with clang. Unfortunately, + clang is not able to find all the include paths necessary for a successful + cross-compile, so we need to help it with a couple of CFLAGS options. In my case it + was sufficient to add the following arguments to CMAKE_C_FLAGS and CMAKE_CXX_FLAGS + (in addition to changing CMAKE_C(XX)_COMPILER to point to clang compilers): +

+ + -target aarch64-linux-gnu \
+ -I /usr/aarch64-linux-gnu/include/c++/4.8.2/aarch64-linux-gnu \
+ -I /usr/aarch64-linux-gnu/include +
+ +

+ If you wanted to build a full version of LLDB and avoid passing + -DLLDB_DISABLE_PYTHON and other options, you would need to obtain the target + versions of the respective libraries. The easiest way to achieve this is to use the + qemu-debootstrap utility, which can prepare a system image using qemu + and chroot to simulate the target environment. Then you can install the necessary + packages in this environment (python-dev, libedit-dev, etc.) and point your + compiler to use them using the correct -I and -L arguments. +

+ +

Example 2: Cross-compiling for Android on Linux

+ +

+ All tools needed to build LLDB for android are available in the Android NDK. For + example, we can produce an x86 toolchain along with all the libraries and headers + by running +

+ + ./build/tools/make-standalone-toolchain.sh \
+ --platform=android-21 \
+ --toolchain=x86-4.9 \
+ --install-dir=$HOME/Toolchains/x86-android-toolchain +
+

+ from inside the unzipped NDK. Toolchains for other architectures can be produced in + a similar manner. +

+ +

+ For Android we provide a Android.cmake script which sets a lot of the required + options automatically. A cmake build can therefore be prepared with the following parameters: +

+ + -DCMAKE_TOOLCHAIN_FILE=cmake/platforms/Android.cmake \
+ -DANDROID_TOOLCHAIN_DIR=$HOME/Toolchains/x86-android-toolchain \
+ -DANDROID_ABI=x86 \
+ -DLLVM_HOST_TRIPLE=i386-unknown-linux-android \
+ -DLLVM_TABLEGEN=<path-to-host>/bin/llvm-tblgen \
+ -DCLANG_TABLEGEN=<path-to-host>/bin/clang-tblgen +
+ +

+ Note that the full LLVM build is not functional on android yet, so simply running + ninja will not work. You will need to manually specify the target you + want to build: lldb, lldb-server, etc. +

+
+ +
+
+
+
+ + diff --git a/www/cpp_reference/html/LLDB_8h.html b/www/cpp_reference/html/LLDB_8h.html new file mode 100644 index 000000000000..a563699bb595 --- /dev/null +++ b/www/cpp_reference/html/LLDB_8h.html @@ -0,0 +1,90 @@ + + + + + +LLVM: LLDB.h File Reference + + +

LLDB API Documentation

+ + + + +
+
+
+
LLDB.h File Reference
+
+ +
+ +
+ + + diff --git a/www/cpp_reference/html/LLDB_8h__incl.map b/www/cpp_reference/html/LLDB_8h__incl.map new file mode 100644 index 000000000000..4294d367a921 --- /dev/null +++ b/www/cpp_reference/html/LLDB_8h__incl.map @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/www/cpp_reference/html/LLDB_8h__incl.md5 b/www/cpp_reference/html/LLDB_8h__incl.md5 new file mode 100644 index 000000000000..a47854d87e39 --- /dev/null +++ b/www/cpp_reference/html/LLDB_8h__incl.md5 @@ -0,0 +1 @@ +68ca11fd4734439cf0840d83fa7b9ff5 \ No newline at end of file diff --git a/www/cpp_reference/html/LLDB_8h__incl.png b/www/cpp_reference/html/LLDB_8h__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..46433c00389589497d99ff2e79dcf83023296e01 GIT binary patch literal 667656 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

R?Y+*zdk*F06Xn+#$_bG_3XyvU9}l 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

F~(NMB@bgnyUXr)qbuh0d*UCIEe_uoGHEXO;C{ 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 T*=66<2X4q*rERScq9Ov} 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

rqJz%DTZ!Ip>Vb(WJD)CX+^2$i!@t< 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

Y0#WVUsF)J6*Fgig2H8k zld7Oo4jf;QuEY8#&HRUw*m8+N!dpzDq;POXTiCo3=G~_y_~O#IkhRWDICL76|Mn^W zA%MBA%7SlATN#D{U*GXi^1;asNf9X#yj5MsLTt#5$0!jir0b89c;_ zIQ~zsTIb0knp0^Cwbt~$X8isuX4CpI-fXwBt@?f#(&4C-+K~?Zx6P~K5_an|pa@1P zC5p_^)N&tct*xxKZYCJRkNoh{D)k`VW{-?t)u*#0u~QVkY;Pl6Ik)4Mm(Lf59ad(lu z1n!y^xrQ6$C3$AhQ}`c~0!gNg#YZMiUUFT`4brQ$V>c!%9OiZCZljFhm4*UU!to#c zovSVHaNUMuc42+tcOZUc!Z@*nBcx*vL)_3kH(sTiz2Yw53z=UVLg+#O#hhj0#CN>}E~$WCkI&ck6= zr9vi$Z(|%jF9I*RnFWj;r~fsbpSNYebckP}Vlj%2_xR1Vx8rhDA!P%DJ)DmEj1#3D zB~>m}3;fPWq&qiPAXJV<-?NdNSK8kO}sc}&>OQ9F7@9;emQ3xkG_nVjYPjq(5aoRS?sBCRg}%ZKWW)TBc?vEgTvk0 zXO>giQbD}=+gR~lYgYzWk>B8tioNEj@AOIHS1Je^&XXvVS$Am$ob6P8(?eOXBv?S5 zs-}mncrN9Li08aaU;3lsRqi`=j*Si1*^Vc&9z>KIZj{cy+bw9@RPzT(>||fmen4g2 zDi|Tkp}_61D@o$-e8U4HsgLhg!lpB{{2rg=G>rw{pf%@47DcJNCrw0|!kmEx{uM~B z0=?3XMp5{198M^9SUT;xi|+or+qt3Z1(ZlHEeGhUmaI7;3EUlGqndND9=wLsy+`wEAT3URoq|DYP>HzbR*I0bpgr(2B z9Or_H(Roio0y9iZ;8w%V0SIMs!h7+8A^+m$h)HNs?!&+G(QUBq-V0EJWuoFic$TS2 z)pDLC_!a3@NQEewhMC*aC?c?^%uP`w`Okg)ph~6sy_P{2a$1^6Di!hpW-aX2I&l9Y z^6(xzSe|18m6FB~0NieZc%{{cbvI<3H7m%&lZ~i0odixAxdDeS)6+LTc<-qpq(nfm z|Bi={efk@RnMp%CxUu{Tl`D*Mzj1j|Pg)=@##l+_MXl zlxvM~J3IwC@7EfcUT}a%b%4N7iT|kay>FyoD-wtmrrWo-X)am&r`HJWIAO9WhwR|p#$P9XLYOeXwWy30 zmKs_0xIBn)v2kgXTiM8?mlBoLd%AwTWoY*sm_O#z!LMlMdY;1muHH414^)T?NDVh( z@JjJlSulsPDk*q{2Y!@(6nH*Y>*8s2eP9ylM;150r^6g*9MFR<>P539gk*<(C~88| zQQNnwFYGe^xU$ckHU5-S+p5Cd-d+K0yGuohLN?b!B`LH7yTCL}*@I;!3tg&(iFnOo z7V(~lUP@)VcVGQjl{E;bp>wEr8g-ACPH{N8`{mm&gUo>nT)lQh2R0+;F3>$jIRl=@ z0JVyTN?_KH&C4T8|Ch~Mh2Fgl4F%JbPO4oLM;0hL)(o9m5n>30dE=Tnm5q3%wtRE8 zn2Msexse1AIi$J!R5^yQUQJ*n0R+l6n6W(?C`tRqa8q^3MVXzftgNR^YhTMdpU9_( ze*;0KVUN>8=HN>R5O^Mb_mvhDWNxqzPS}`SXt(2=oA|@_$P;)B9Z)=9bTIqgc}4NS zxOHH6Yof)kKz=k`ZT}!b% zJ=m||JK#yQU3b|3qvqJGC7^o#tbt@MXFX6INg0Rnqr>sRLcA`IG+v|9+&n1bYZtW+ zJ!c_L?`-t0^XWRuDvn+M}!BwgUR|zT>ELe-VeftaitD_%VoCB~sUh4OA zn@mqI7p(&j0bVtz0PFvT9oYSq1^nRQ7mu$p9hjzE^J^IvEkJ?yN~L9!Z}uhgCesxR zLLVOkA%ao=f#aub0>qP2i5&}wnv5VpX(dt|3LhcqY&onrh>)14f%P5-jFrChFA>$J zrjmPQOC>Q&w3Pb)@VV|n1=VGOl37Ijt&Gr^UHf_+tM&zFJatTSLM?Zsa@BTgBwF*zOOx$4vHg(2- znHYt!qNr^>LvmJwj~;Ry-yu(4~}!Y--Au_|^YtsUl*4Ig7wbJe{poa#eUN z>%TmvfB2HH)fNLC{cdDRtMO|!in5)v!3#NEBVC)-M?XG?tPkH>M$e9HDKZh9Ldu-9DghI>3CZthkg!!a$Nwc*O1jDU9$buT z;^OXXc&CSK>wbbMx)OCBm22L)IA7j6Y9aIE8Q4KXcrfMG+D66t^u=M_|3}qVMn&Cz z(GmhmhcpNdB_Z7n4v0z#Gju2|9YaZ{v>;N_AT@MLBS^PMw{#=j+=usn?}z(gmTS4t z8GiFT=j^l3KKp!eG$b;Dr|3l`goN~%wF_ay{rsQA9R==y!zo@W0Y(;eQD^_w7xz-Q z|D?E83&f|VC$h7Sn5!$yyokyA8Wt^9vN?GxrhF%4Md?N zKR?U$8-7(=hy9LL%>*(ATg)ld>tO7@kYi1V-3RtFhcz|< z>!?52LFM*xmcnVuoc5(ej`L0%s2;_@@J!LtH}0iYYA_-s71tL;j&cqIShDap)BO~} z+~s>UZ<0`4gJeY`Z-9kj*2g4EP8R8X!Jy~wptsOK7Al68TCn|j2(+LYKp(TCzr!NmN0ayqArpw&b~3{VU;)t-%$2Mku!Zf%+E6D&Ub z?a6mvr(u`_#&g-yGNsPsQq5Oe+6!JVuyE4Iq-#AT>mCj@S`V{->U$$sMju;HpIN$p zsU)*LO>$pKX~I`MDY<#vP1%;?=C}=I4x{gdHXzlh??jMhP`5s1xRIl#;uHxfx>Pad zei6ubb%pgA%|uEX6Iw?6?$bjJv&yNKsg4&6pTGV?`Q0!xl~Lnz>qf7yLggX*F>q{b zVBQe8H*n$}iikYsHAeA;%7kOcvx9Fjfc{fz z$^zMYi;W?7${s!z<;KI^cCW@Cw%h9p9LyKtTU*mZle9|M40lfMIf0o#PQ@6r^ABP{ z6vL-5xX?H?mF0ZQL&PbB*@Uuq1^6Vp(8bJa_l44kp^YF58)#v^FV;h8uFMTg7~uA_l76JC_& zWCes?RV9IPlB0u-Aa&a7UKr8pf5OtRZY<~lneTFU`TqnzzJT=yu9s#T>6`p_a|WC^efw({%XiMn5|faY`q?8xG3!yjO{a(B*BK?Vfo6V*ttnbLElBN zh8{g0FA42-V7zDH?Z+mBXb9+yBJQ$*K8`>NYw^j|tS(I^A#pRoGYq^86QPHb)ED~4 z%`g7yf^YgF$*W&mTYags|&&YBdvwqOZS%P@A)LWcTRX zbTXh(cntJW)qrseB~b~tuT*0LUzQ`j3XN31`vVSKHxT0UZKTcz6u* zJ*WV49yg=?^06>yF^GHp{2IW-2JJwcdl|oX!%{fh@?JGN5vebK=)(}Rk%~%Ys~aDe zpn$lZcQ#%}zowh8(Fg0~Y9FK)9R6~WK7MPw0!48-T7GLBkKTPHcW{yst93t8il>%_ za{iOn_00Xm7#KwFJnG?vX}ZdR_I&hLz#DdaY|>VK5g_@KV%-=>aN1F}p$8E>S_JH8hp2i`%c~Z<6iBr50qklP1jV!p}%s&@0ocNAfzimc?u~+5r6a_SOg<6 zQyo}AEN%Z*_OQIHEP3rveO0?JG?&!j@%BHq_!yCL6@JZFFw5=4>kngLw5KOt;t+^t zXb8Ifrv(ArnDax>6^k!2XnLlfzZ{$2rTS=DdB`rdJFMHx*naH*8x-;ldy|617?^=;vOWD`YIDuTFwX_xr+TU+s)LMR90Alo|l5#y-~6k>yvtCa(gOT zNi*S09r5{*Q7uRT1Ft_xi-MCj5Kv8;k?|dvu*zTZT*^LkP4NzB#e3 zxr`kf%VdA&YE@lSA^R7Di1OcW)9q@IzErELbqOdaT021=3L+k26*!|YKL4EQxFA%5 z!>}2B+Vok#GpWB-8^YU3amONKP8lRBwcmF2V`%N;x z+ExwBzVcNijJt$i_+k5pgkb-8szxX$6OEKkPEsSCrHCu-9%p%m0TbhMx3e zZH#aZGQu~Z{7E^_ZPePwIDCvi4AQJHBLqu@(->@4pR87QB%C8T7G~-;+;7$8O1bVk}x--14s6QEDZ%3SYyW^3_ z%{y`g85aF}APu!Al8}pwEy}^%k&^4@CAxr%SbxflZfZYMGWPLN7f3EF%v<0cNlNjP zgZ!&SZh6x)&Ha1U#?$KarN`7NVDPW-;IB>An#HS+l8I#@O;rsISC&G?o(}4Gkus}6 zfuZt3UNUi|0nHEBx?#x8=8;;le_wiXu`DhBVxjtwJ;jEd)~2wHpRM}X%aO}75^Eze zcrv$4DmKgY@7TD|v~=spRaHlVE-#_Znwniib!a$_WyoeGsaOw|0|I#67{!x~t=~S@ zlf#Kr?RkG6ck2|8QC>nkh`jKI=f(wX1|IxkV+bv?ydoyLanPL^EY=( z>YC)~ro{%!S4(miXoRuKazPiB``h?dyXCET#$&dO#OVYdP40ZgxDQUZERW9W>(6@FqG}hv!NKZ9DzqPz7mEM zS?V$g?Rk=tdWjv_O7B$Mqb0(VUaxbui<4H zh6Liks=uqpq~znzBcrpB4eiA+PT`;3CR7tp2wuyBI>H|qu9BOi#l?{`5~WKtL>DhW zUS&P;rFNs8sPm=GUh~&3Xm#t zL9_u`QaMb-AKbzp)}oU_ejEP8@QvmO}x59Ea2o%Ur z(Jd@&6;D;JmBgvNWEgz457`yZh#q-A&FJ;s5K)Orb(t0 zg9W5pecKhxEk zOkMjTxI?#BT*zL#mK^Bl=9=#NB|CAuGnH_QtStXgNX&WImgSA|n>XrLZdeLAW_nxh zwEH9@wS{|!l+2=$|2XVU(?!YmywhH5_$hOP>a*F*EfQ?gJgn3XN}o-{ zC>OX*m~at&lLVfDzfoQ(OUPmgR~0J}{BuQcJKyq7HI5${CBsc6OE&z~8KK8lNXOBg zDIUA5uryI+!zJ#7bIH#yA?3^Z@BLD9)CPQxK^lz6bRSG{+H)q+63GGLXwspV+2ve* z1_tURccb>q2lh1Pvm1X3P9tn)Rty%_fiit^hWYPxVXV5g$aeYQ;Ax5KGsLsS&K^=! zxO*+~meiHA^4X6poWYEUB|7i)%wWhg-r+I~S-=#nI`{)wsC!MdknlnV%wfj!GLBfZ zFChf#{GF`ZoZN=GMR+*u6)RK-uxt~ zhd<%Zj(>o5ZHb7wdgZabalOx@MlAjZr3NZr7fL@my+eJY%1A7RtgKwdYL$dgSe@kG z#_P7+Ht33THNS$M4$j4Lr`7(We@OeZZ7JhrzL^9eMjC39Av~e*+ZA6@r#Dxlqko=d zJ3Yo@HSQD0H1D2YZv@gC|A#R@`BoGuQ8h9{^xcTxD^@BApBki(SlN|opH86xfBn@5 zFv3ssnFDFYMYQV?Q$+AP$8vsRyk_U0>`d=2&g8!TY}$ZJ+7f2SV=m*w+}`*!?YX0c zq={8q9NDz>CT(aGY7|Nn&9#*TR}JP5T{zvtPXp;78qHu@8p0RixLymq2-IbWIWO)^ zbo$c6E~MDcmqGz?%wxL~^YPAd9794UEM~&x$(74B28l=fusP{vYmvczWci7r)6S=M zdEt}$dp8M9XSdh=)-{gJND;!sQY02y`r{b)3^xNCaE0VnK3HjEoPHqEH?>)>ALAaRF&Ze{8Cy#p7x_ zHos)=wlKR>?_x-ll-DbRJbX)Z`rv7@05rS&E3y&g(mzBzoxQvDn`(#v$Sbdy4@_?2 zpD!JWbGZ~%Uyl9u*f*R=eeIynUw^j&N~w;h(lo;xW58C$mwURDE_EY8?-U zC}y9@NYES2dB6D}hI+0KWRNO@nycm=uu0Qrx;2Req1+6QztDl*g}BxE@pdCY@$tX# zhpf(Ny7#TD$QLHZYp#c6%pGaWk6|Csvt-s2cj_CIJcM_aulm8P1|JW!hAWu$B~opn zwk$6sO8YKC{Rxa+64z2pY}XEqDSw9rnjdyp(dIqtPZ2qqpcbmJLl*r}K}`U199EU$ z;sF(q@;Qk(QS?TFB%!kAg=Xo<{QO32VTKLyC_cTOm54$>v_p+|KEGd@>9isy>T+@rhD7_KT{f0UO0n)V>#p{DNk z!WZRIH_;8>4q51)>X=rkM+NeFU_eq^Pn^q}d&7Xf1sn8eU>d>msDYj>f4OAL9cauO z8Qan(9M;B*FHMJ&ApBv7QVXkw%bbi@k)XZ-e#wg4>Lk7v{wAbl*BMNN@&{wm>}h1r zWK3Fvg7r_(ZQG+-M1+I{!f{yZGp>BUx{oxL zlSZc9iNfzv>ANseF&yQ{QDCfvSvWl_anz6)IXiAX!FGS6C6bNh6itwDFOL-ufHmz+F1+oQf^VK;LmYsJ&9|F5u{xEp9 z@~`XX4sW;SuYpq?q@`5?&AVc+Cbo4Txe^9o4u4oEJP`;kd1)YyWCwXa zvF$~cUj&lF+}{j~_-w^G zPI`b!aOM8ZQCEnDjZw2-3mr6>R9tjqsEaJWe;>r-X#lrNj37@TjkTy+3h~SgA}FjN z*l>c(zAK*nrYux~Eo#W~O~mPPLD1HE0e>gH;bnVg8Dv;>kP{}NA9(j&EfNSk`z%82 z`ODa1>x}e=wE-5f0!c>Gl?7|JUnkU6iMlFH@GI;xDE>0 zVK!P2yD+C{7*om7qKFhMdK+g~IZEoFqi#r-Jo5YZpC?+4S@GO}r&kd0C}$gkpj`IK zjYD7vXB$y4M^^3sgcb5BfF=`(h4MaHhv-5I-i&#BQ}CyVh5l`;+?6tq8WXNgcln_t z^0L-}045Ks%nK+|%E`S0vLBSWxw&SZ_pqFzFiw>3qwM48p(0L$br6~oQJiC0_*zK5 z@A0KK5h^5EusR*{`W;PH|K3R@ytN1@+aWhz3g_aXPTGL-uIAHO4MQpMI1D!mLFB)Cq?*v>)@zOr4N*gh4?P7C%0 zS=dEZIORr>bznn0T5%0YF3xqj4?wm#ZWyO8|M9%kma~rrRDFEwViR%1#oC7Y<-%(* z?W~7=ZAb;ye5}@G)%xUFTjTZOa=VOFcJEhJg@eX(=3&pLnEL2z9~R6(H?_$$8jbTJ z3rAvoZRKYClEuy7TF|PYC9y~Er0s|J+dM-^g&f?Yi3_7FZzUY@wdlIz!RK|mhDne8 zp`o3DbdqEQ&VX^pIdZ7WEfqKyv9vd?`CXwo zhn&N4Y2*!>@Rn$0At_NLi^R`E3=huy!tl}quN%<~Qjo*CKmvHhCUA4L{Pq`Xs)4sT zKUG7gj)*SQP^2Y&U^=1eqyFHpvT<(|6LnFEo7>C;PBy&Oo1T>lA57ZsfO-nwDq5u= z5&=#9)mOJTIx2hNH5;c`EU#UK!r9r~b1R)j)fWE(@+OK;6kcZW-RBUUS zf@#O=!S!W~)xIrNV61@#uI_kBI$6ETO!*Q^jgw(V9>W0g?rc9Bk?!M;LE5}e)oY(R zEySs8gvFDrIjbc}%)cn)P~9!Lm*0^rCAX%2b?}k*IqYC6C13Iw+4%D5e;mw;aC?lh zP&E`%jw5Gr*x2EktJbH9fAg%%p;K2^cl1S!!gXbaR0NdjcD2(%>G2KLhO9#1+>f<) z1-)W^)y2hqcO(chmM&6#`(Vbb&uCrwYOMYPX6((h2xfCnxB0IGNM6lAEB`@WN_T^u z6QpDPLzcp=X2_-Z0fVMRCU>C*Q#~XmxB==WrSPzuF_zDkKs4b93H&*gNJjXio1Y-! zM@C%>zO{gW;gC{ZMgKSyD)kgX?mMSB#$OnmQ1_QD=Xr8YWfodQCUUq73i&ts)0<#L za)BUSLwVm1gb}eA1&-%!XFYf@USEXs6uKW)fRd)CZ74U3 zkDmk120GHQn&(m(9?2Z5#G+#DXx^CrS}QP9Gy!9h`WI;-4Ihpy@{pCtTl@)mS!5s& zoXoSRIy0m4f$7cVzAfsv^&u6#qu+0mC3AZx8PY zv$D2^XEEG733ue^#Rf7aDs@ocz}&sWZT#r(8})+DZTTbduz>jjwl4HHFGpXOlyQ{q z-&CyP2)TwGj3x1zN6hr^FSDk*!*6S1n@&Z!wysFAki6-$L0d<#)d$0MoxbbUuCmwe zL>hjQHpL~xBLZ=8>aqIzGiE1A`Q^NKN1vc=;6CnPjR&`AD0cyKWC+?t1&-u-LXKJ= zi`P<-=i5;Iut@2W#qMA{TIWo{BbbvA5pX8Q-9Sa|yvK!R@6I91t6QnwMTV^5@NIlU zvK?Kc$lc((ophyTp?(Yu5&!F6-V$c-%{z5GDJ;I~FgiUwiRmdw9I6j#3(A;qGq{3` z1=~e+NoTOCU!RW`XJ$(qfE`a^6reKQyriF8NcfGyJS65rM|Afh14vu??6s^0F$5Kp zzT7y2Ik=eW-;k_^fz%mp*SY#N#bI~<3Xq+8kBlBg+fDrWWj6>oo(F(1O&K|u2zW|k zh7XvZh*B4009PGjm4)pM9(&0_KXKHn zYLYJuoSo;Ah|m;rStU$8Bl4Z2aIfm+EyvsN(<_Rtpmp$O(AzPM>(yf{-%Im+_!>3{PAla{Lp;%mTR7u;o`%UWJ9TIbh5KM0 zm!?gWamQIU3j#8kPyCUU&2@yT0^Q7y@*7@yG6bZ{!BMSXH(bK5cRP}&1zGyJ$=TQF zZZL7`&@R#FXCpoc7mFMZQ9t?Bac?jV7Ag*-ruZKY2;GlM^t-d*S*n$ zufFI^^Z}G#Y$rn>H1Qt&`zEx%cbZk034xLgpen-#NY83C_K9PJKnNwTsu8rMDJ;r) z`W`S^0-MK&Vxyb6>}mzMmFfk#4B$_RV!Vm`Lr%E&qw|33EH+Ylb=XDNzE${o2f1t; zsS2HtOZY=l2%^AD#lCV9mnmidki6jeUZkrRtZjoAPHa!Ex>`Z!3-HpeG#kjT&^RTD zIrH~?1Aijesfgj^kMvP$Zk20+6`(vgrm(v40dz>swMgNYFZZXBcgn3IM1qDN$FX$1 zV-H!Zf0$dY^Ag~%W$70~`(tAsQGI7we9dj|C9`{TK|24c_|IR#WW+DSJik}-O#%H! zV91KdkY(H%WW{7mO&h^BdGWLu?7cZC**^7>&<$J$h`uLd6dV*s^;|?Ly6TvSjCs-y z)Pg()?BuX#HYk}a@O&JBR#nwM3Cg)Vel05%^y|UFodFqhRu_P0HHbugFF@c=ng7zW z*`jW$BzR>>m$>|(-5bYEpMTo>_3*2(w`^l>bQ`X|%tN}K_JHs;|G^6$c5`VSYB->G ztL|a$=yJX7!i2i-)}XR?Obf*vB}>nW^NeqG>o$P0gW^LTeB4T7q)-m#99}J}NB3lL z?^wO_z@I;!-d$0&Rt7s8=h+SPK7XI1KcU)>%fG~t(cw!<3hKw*IA$jTWBKauM-yZW z3N80gC35ONcJ5-}uSiAAuBY<(f`(h7*jayqmrvh#f=c1?28piDdxcS4gyp! zk_~r~>cM}IEIPD3m&zw1%9~+OqnDuvN{jYH-XDs6UW+#U-OO+jG3JV$nq75QCwzpb zg~%+i{b%KAXovL({Zp=5+h2IN_oizMaNH4MUL9{%Ut?cr5QIAWrz!~RnonW zsUzM?Ne8R5$OLXWJBOd`d&q#nTUd2g38!S`HK%w=3Y4N^!@v4ZNNbIa&UmFzRNQ_0 z6422Vli>9@f^!D3RCsi3Of2E>j!S-^_K!OdCFdW+iYxWds3#oc65_9 zkvy!PW5B<0_jvPVrStYe`fTIH&se|MPlJfQDoVa6DeI#(k01W|JFzDgSo zJ~5McRoShkIP=YIOWNwN>Y8Cf2$e=$5@c4?Y3b?VdL70y%Req?9~_G^3_NTzc$+a7<(g#(rl>(*Gp z|2}=E1mG(;_g8YVwdBLf!af|Q?-$Ob8rRcUq<)V$OmteijGpogij{onS@kVi@Hz{) z1xyGQ9a85aL}8T{sUJXQzzP$c`?WKbX9k{Czs-kN*Crg(9O@*Fx6Yb4vXGSLZIL;! z9XZBhyu|`U{lD>y%Ez`c3h=+JO84xUzp{EWv35u`8n8}h#Y|E>T9DxKezXAxe1B7q z$x--!1nN1k>>Am0FCPG8%pmVn!E3AttC1k$=~`B)0vN++UdvhCoQuaCYV^4_NJnpV zxls!r`f-qrPz%}WmAhY0XdXU3ri44lk*7dn%R2e_3mre$?AC?;4_n5lr~Td}Rq-%r z2gQ^-Acbo$!iiH2)C!Uej~Y({q`mK(0E^cmJYB4m;PdT*`puH4BvgLr?j^7LYUTY0 z#2PQQ8iZztOXXVL5{E)tz9=WG3V#hf=GfTw$A3HDFXo#7F0Kzo>M!@P!~t%>L40(e zoP~;P?s8f94^S6`*ySvW^Ebp9 z<1E>#7SX=`bgLVqsuDrj~@~Pufp_Z;Sm6fY4y0wziAgB;)wz?1^ zHFH{*l{eKo!W8l=cpWz)D7%{Ske2&N*~%hIFz^dm@76m@osZ@!)h-BWV32>bo<5#w zF|X8}<>~5H>b{t`g+i*k(5k13?waVwrHH|~D{g%$cMNgaC1KSWv6#b>-iB8yffQo# zcI-0a#p7`8%3Pl}ab#8l1Qg1vnq3ZIVGp59gMNS_=M0tqb zV-TVqi4HNIaex}|!8R>YT;Wmbr{`EXRmNA`Bx<{VHdTZhRU-H8ocLIk8XsM>R*g$( z1{OnfOVdM6l*mB!#wKLDBW%3fdCC>wMlV%UbM*xfW<>ZJ-a%JI)KKS>yZ-GB;VcYd z;5I2|EnE2SCFI{lMqa*SFSNu}Jg{AVA6+(6Rr4udsYsMMqot+MOX_yHRxxQG;|S0C z;^NJJAPuX`dv;4${Djp7gd@8AIJ3NP3hmR;=~?PE?&=!g@!U_k@h{pj{*{cTZX#`-b! zM#_3JefDA$z==H9c`xY^I^hmF;ICJ-(qIh9VkiPd1W`A=sX8+M&f zcipd?4YKsimkKrFWW$qBDIBk+jXyIqGZ?YMnedqLthwbU{Is#G54^NXr<)AMhKdaD z=AJXWmhAa6_I$p$Uerp+8caJuC}QD1={O*VQWx`^bhCgOcc zs6s6gU!&EYgCDIn;z%@wNZn<|rc@gU(PD)eTzC({A8CEgKa?Y~1#m%!`JGp62OIq2 z-SAVIywdx=LE40U6Ea1}$NO(DypUKQ#O+>nP^8pb@Ymq3v;mO@^*s>}^qG^5=YIu~ zLF*vJqS)&h@#YWV)HVMMK9NCtAU(|DBi25C^-$v5z*bJ{U+}l!n7k*wS078> z-ujDG4fuJC)DOJGBV)+J&%y)jc@utgI-yJR(ft&nO0mb zY^&Y|C|2=sKy!*U&u(_|mXw~Y4KHKi6?1afKS=Z|me>!Vw81nt@7w^L3ZOly2lQt) zQyZ2a*35KC8b@Wi=%5L6j6(Y>dPCAB=|!yZ-aNh}<}-=6X3*~>8*s$adn8u~zrgMO zVp35cQfAL&)E?YtfG_%jr`Hg6fw;viBh?xnaX9nvh%YG6u5aL(YKk1u$M&Hx9=~eN zVyk3fF6Wzv<-CEif6}u4kI2CzTDlo`v>BGUWJxX>BTjLyJB}<`^yi}tQGK4({=A<1 zJ4jS$_UOLB*`P}F7^Zyv3bf$rD=Q_i|Arqlva!p#wdthcep>B8#l-q*BU2U1i`fyI zidN8ji>#6Th49n(;^8Beha^B}hY$3IVS8nz55>QN^L!|(^UuyP-JD!^$d~{l_A1&k@x;&h=-WHFa@-JrnZa~ z7Rvvro7`#gYQY?D%s_BLBcS06c6d>PmlacJ#jZ`^@JuIY1v$!_fI;<$>FIb0qR?Ln zVH;sUDP<@+hj=-hL}M;|4>v%ei%W1y?QrvaYPbVGCB1tTz<%>7%BBq0AV<+oswFYg%hQ|*0zMKlxAmR7c0dUeN$do z7`|^-tY5_T1~&Bq9{=LB7uM@6EVf#hyVmIC_Zi*%tt|T9CEtBBAB57g!d|BTm?za6 zQr+_q_Fna7*yiK%I21l}?+>8A+i<`We=v5ck$bu!?p z&4r;RF>ETiqXRy;qT%TD&)=Or71C9CmHvk24ahuP!0iCRaV{0~>uq#5<~{4!7Frgn zhiYI>6RMDa90YzO%fcNhW1ce5eOoKtQ|Pev>rkGeLoeS0zt$$yE{BxVB7G0|?<1qy zuno0E`PqNpNundEHLatdMhhs~8k)7|)8pf-caKEXd0yvHw`{wYzldDQi|Nj)%^PJ; zdvf_UN?ja$Qaa$DK0%#50rC&96Cc^77EvbZHYu6ZQLj8VG45voX?r5}6(_dnsEIKy zt5u9rvfu=Y6*w&3*R|*~=0j_h}!rvUf107AV~+s0P-xpS}nv&XmofE9wxfEtssSSfk>GnSc2rOx3{hXXS! z(9DqmfdmV5#f<1lji84#U@5dU?K5|rb*zw(7%eYn=k=Rbz`@wLVt~|N{!?c=j8l;q z`McN>^Laf=f6sv9T7ntG(h&I_HcMilIq#_zO~u?B7~ZUvJe=EC#v$)WoI#&p84??uKU?g zODpoR&;uSKXA=C!pUI-PfKVYOBj9oDSfM83via`qVq}a$SBK0x&E$d|f2af2Qf)6$}ieba3{RE=n%f~MROk zE6J4g2C&n7TnV$B(QbYHa1QWUrFej6#?=&+SeA{J3fthy8e#GP43R@hilW&?3rxfi z-a_?1AY&3cU^a>}jwDCKJDL#dv3z3`6JvSrDENEeLowSKA2inKSlr?ObQMk5$(;DWwMeY$hsUpzDZR0 zzNDT;?x6;?)mY8c41cb7B0Id9vRLq&warS-?eTX$n!zVqW@YwapR7?T&4*c2%AZ2? zA)*^7D)2S`AIlv)8ND$ON1yU1R{7rqiDsoyyOOlVkf0?TwJaR9{FTPa22CsJwLly+ zW*F$>YD4Wyke=lrP=if&!R|GLA;!Z*mX zMuN}4#$0g8wEHyPon<&Fx5>Np)mTU{klZBLZoL$)P*W14y4y7@7Bd#ed{JY$W z3BU5$lh_gNU;ud!^E?PS&u>0RuIIPT1AF^62CQzZpAbNS&|qGw4vVDd{}&Z=|I>Y) zV{e2ZK6YG!)cDkjIb)e;n%0>&kBI6}-G)hZ?JpU!8Va_OTG7XY=Yf$syyqSc6dp4X0hrAE2tHGc ze1Uh6VpZ(iHEApzR~s--j)?NhpVojEV}Jp%D0RuIUWzFq1a_AL4R~rx`2`=`sO|mP z7EplP=Ea-9$e7jE7eMF~DU(MT_3c}ZlMGpCHz|-u=>`lguY6bIstFs$%e(~VH5-(E zFEgYxNrkljIJ^$C#Ym+>g!L%H>!tNZFuEKtABS?dC$PON{B|GU5{e#(2yZ-&;v*z_ zg|dIt7!2B*-~yO9UIer%AuEcSCEo&{PouHv=^rCHBKQcqu{pcIzy^Z>R#s|3y;I&Y zlte7`5`1tggiDdgzckS{q@?;FIjIi2Glv7FeHVhrr*ic+@*e{%s9&ST15HHcL#4G; zEe!n4_4V8MI9x9n5R41MxES?v)u~ zMDz&)tJ{f z`sJa;x-V3G4Ia%>V{d|a(9Ym{S#riGUaQym?zOh2LcEdYpLALYa9|l=&VI-nwemr6 zF&cgSLn6Yt^7eVE|E^ou^FQ@QID^rkZOY~QCu}3O;7>$s*gNj(86j%V1?vn`c`+yX z%qta{9djFovyY;&z9EQkT#;5Md;oh2y6t$0ggc9ebN<*ZEW4>H-+Hs3oVhOewi2n-xjWqSDSW?C zYhgEM`0V`M0^JJ zrKhvOuf@+3z^*9c2;iD1qOVU|ejiAIoH;Ti?cpWr(Du3fe_SOXfz4qTKiJZb8-q)M zXbl9?`1o8GxJMY0=1JtZ*V7I0*nUj=*gmByYmJ5YE7t3GWv^0h71y$GmX~f;L_<9S zeLth!F=-AUrS{Rx*6i%iIqYY+$17>hx$T+DgzO(25ZV)P+xVU|inR3GNDn1v&# zYC9rT&6|L{-MyTCP$F*ZqU4cU?~KfXDf4Az&CbGSzz-bJzYLVsA;!Q3nY4xaxDUed znz@u?db#{|#VlY+FQe`ucBQCS$S_*TMw_&bF11jYJ=+HGH1vLduO?>gcG3H}>X2vI z;FpR0G@)Kia{gKHvTa@j5bkIV*EZmkzwDy$VIthyixANV+Q!Un~ZQkvxfcx0PMfqI)}ir7wr}TR+n}6d7OnM)z%vp zSX!`E3)*i>GV4jun$RE3gP~@)p9&5=0lyXuNUH{*w+mj^S|n)lmZHjk(5z5WVGo!3 zJSAI_WiNJMw^-RKBdzbZeeta9s~*;g=%UmL8{W$v_`^Lr2`-ccG_#m(3M^qdH|%rl zVN7w%6g zeC>(?fGP?ms{z#3Qxiust6^=-`^Gx>j@Y_?4nfAojH_3KdXnZEAuEYo9kuvl7Qj|g&5?dcu-SwK@7>3f-U%O{{c2> z&>jqaNWoVjMBlPU&Od>?D6Fi!*PyirU+tW#Z29pLMLCcY=Ff4$>Hh%BB?XkWj1XM20AlYz6LFR}7?QK)slJ5|Bm4wdqwnIWx( z*{|m9O|(m~i&ds{LPX;&-swtjn8nU2%SY*n0AHB+~sHEhXhUCP&a1j~WFwku{pBu5* zc}jE<6e3={)xrbfmG%8ZMZlmSX75F1Ni+v`BZ(W4R(I>n)w^`AFLyIIE0S2Yrt3qY zwb6wK&5^U)*GW(J_6s*8#Xf?br7QY{VRU+R^>Yu%hPbheK$U8o^X75CA($R;=zXJ7 z9yFcj*g;)6U7F5y%vgwZ)$%= zwPVh3ZEqXBGxhemNgBT4uqrM^fXFy#0M@{9UcrUo|D=P7Azfe}Mk`2-8qS zxHkyDp1BUF?#`leY&zd%Uf`1AmJ*O$`rv)qdP$==^5lv<`yRbL3gIE~ZXRDv`F~n~ z5i=1x!vNOrbUyjFmCckNZ6&Uh(W}K&7ra$`(s>VD7Bs==-0Jm{w2dPpmM&9g4ieA=}E&3(tWDM2x*d9*teP1Zv#r!U9u(%81!-oFCV z7=+`&rC1eXV}}=@@2&|XvzQhEY<+zf*RDJy@9`GRd}+W^)^q(u=9pn60fB$+|J5h& zAX}h%LV?#~u?Olg_FlMUQ*>qH3mR1ArQSw~VPE*{6c2kBW=7q4oP9{`8NR`h=t~n& zXviKI)_nWK2k2ZFkcWZ`6I{Rzkyw@Jh9w{9upbLpUEIgpjs+qRZ)cf`ke(WW?LgEz zl`;Cb0cfAZG7<{5BGV=gM*eqUMwJ$bmpqUko$lv#?r#jfbejeY;e(xM@OEU50`cvY zQA2g?$HR6=75#%o&?eM>rm?DlSSoCN*$v;8NYTSGHN@$GKa%v<`*igA@QjY&x_QEp z^rFUFrX=8$U}silCcRFYp>C`7IXcIvtAr+sX6*LP_2as*wwnu_GFBhm7q+VJBQ(X@ z&KM>frZrW}FVq}qM_Ewquq=J|&$LfTT9bYGx}AS{6Q9#9j$WJ$P%l*kh>)NAtZ*vY znoMjBg!?)j?lxooz&U?jr+k$j>rs41r<(3_9l4^+eGDT@X&+?a%qW{M%GZ;a$7y_W zE?#%n=NF&M4Viveq@0tRu*yLPXfZIC;gV3jW@}{{Uj0!EbcP-OYduy*W;)&$vGe!U z`hAY|*h9D35AbSEd9(LmykG^J6^6dBJ$5_P0}o737;>{|-u3&x*T5db#wrHk~Cz;{a;X;=9ON_B-&Y>5svaxFr-#JHkNvZeg*V>;M%mH zhivpAA3FgR6zH-b?rh4I%YL5reY_GqKwXqriE_T;%e>NtXt^|Jbg;hf?8bMzy1h}0 zn{$#1s!>w?{;FS*XzGJ}F@#BlI=e#^k(ttB6>Paq=D6@&LQ>xLos;YB+0ya*(=#fv z-=Bq}vm{Qr~4qFse5(^th)Sa!SBM{{Rr~W1wgJIQsSMRKS!{U!9ip$Na#1}s|(Uz zUS4f@)71XeOHR_Uog1Z&9S48~lOMMv`vdZyIdZzAHRwT@oCuxW<7Kx$KRd%uH}-#I zN8fcdV{g{7t(>>yscf5HEMRRLZfLuV)9*&f1p^QLw2r_nZ0jiTrxASy(1xebEOd1ipVCjY%((6uYUjc zJ^%Bbt^bX zBYg*y-#V@>NOw$l?fr~e$2VU6;kqC_=u7CYl_?pOj_N+QCp|okp%s$(K`Frd$NTAw z!Q%{`QT|D#E?s>FGq5>0`2r6({4w_AEc>ER@1t$PRwwr3#p}l*a^6}W!R%_%|HhWP z;_$HCu2N@sq+@!`yS(v^a6ty+u@U%8Q3JRm7tu1kRH69e4-;Uj84#2uDkGbiCOh^s zxVQ04JDRV)ds};_wH392vW(6c%O!1Stt~%XyZZHDDQ0KlaqaRKd5IARvg^!|fj@RF z#$K!wfA!~$KYNLe-Dr(c6=QA%-E{IhX18~DY{eYf=K20PA?M;SERjkE-k1{i&nOzAxpR9%V0G6=}lGiSZwYH zqI_PdGBvQ5AGofZPAc!FiwSj}ez_>3 z*S&8=DznJx%qmUp-`+od(MT3+XT%xs-z#BpZt;ADDeM&gIZOzmmT@7B$Bw&?h2 z@A4tv>b(LJhduvh8uNt-l^J)2ib%@+X^)Av;vTl|tDAAQGJFe_9(BvM6jI8<#k*zx z>(!&)kNe+PfzfQptHe^!d-~^V*)sFS$t&77$CLiBPX zRn&I0k=B%w^2G^MGl8SUrr@x7r^^v28<5}dEHwI~O7{fb8wU)>1E2CKod z?ZV&)#`XXFZMsY)yK^C!_548SnzYWjVt++&mCQHF=EyJGqpC-?Wb~2ZY0KVJ6H{Js z<>0)A{E~OMqyC>BevjC$x1Ng~n9hrQ)j1Y;eoq%f)zZw7Q=kU0OFnaTc znYxonr-j`ofB8#Bs;tb?Khg~x2s7UFF5h!(WKCD)-Z$!hbI!fOy2u@<+1v9GulZX) z50rxOB&(DnYdc*}1uGjy`}>#9r$V2;+m!b`UBB{z-mGG2Aan*s)i}VHL zS9hH=`PneYj_!VC#xMNLSKWDkuKJg+XyQ_DJ?n>zdovv}*HeEZ+Bv8Oj`u6=`$<}T zA^ispt;@^P-w$dQ&Z|KU4=jmIqdzR}yM1e$aPrwu6cbX!9 zKP5UmPLF*CHiv#l#`gWVubGl~-3`((01akXQKN__wGm!-pwKjU`-I%rw#^Czki?(# z$nyuFFDv3p1;MXT%w}=RuD2E0kBFD7ng3jFW!vX@el<+8Bg#r;-$tr+d3zp(WcGi1 z`x^o0I!(0TdsjMh)S{{3h4ju-C+4c7WLl!F9+buR{-#oNgV^nRj5ze4fPC;^G$}&5 zyJnuf;zNyfbEyNl?vAx_OLdiX;HF)mQ^$`?VP9w+nT(Ldl90fX9B&^c_5B(1WYCfS zTtQv5`?2c1KVz{&0`Z@20;hvD)=8RV?C3LTd~4N*n*HrjA&HW)c?F@IgRuozHjJc2 zb(J0s!}R>DUyGqI`mrN`F9v)f#=KG;I$JYTKTCKSr|X?)xGtVWaRpE~N6&ACDz`Xc zKkK32?-e%r=^T|Cw5)3E*9k5^szaWo$#+;TYwWr(#A+?>*PGO13u%Cqlo|rFWdbws z$(- zjT5U62ge(0?CsI9JQdLq_cqB&Urf<>jM{9Lqay;Nus#gugas}Oo+G+ z1b!FeTJ|xHF0gefxFQNDQ!Rfd4`s5=$q7uq@z>9iD7;MGPVV%cip*x$@@8_shjRDw zN#L`bEF(gIyt=n#pqxayB8JRe$$grn&UqkjM4d zy0cLrgY?kIWHiTib)@70#E%&%$3(5tK`!!bdf+{{Z>bFMio#v9v$GqVkutph>vx0C z>a5v!72x;o-z}C}2jd~JGXBLkz{>Wq3$F+9=jWGEL7X}1it^P(2Xa6lbRV%XAr>AhT8aTeJ_MxhezobRDAF})pcVS@+2{S5wso6t6lTt8 zN7O9alEBtOAd`7NkOfZyyq^=Vh`#RG#yMJdqP>>(ilUC{cj_DuttZ|;L#jT|Xny!9 ztX{ZgklDX;HOq3YH}ar9`I!mF>&LndT{jTr4d5taE)@RX-Jm?mXXo#Mdw&rr&4! zarRFG3B`l5iCbF%qj{4r1Zem5$=^Xr?zwO}c;c42|AL2;u$w-{Zx|VbJr3A83n|gF z94`^|_{PTMv3?CDlhMalx!M`FF5`dn)L7oE@t=BbqW>;|bjV&R+~AZHKO>6EZU;xT z)T5rN@K2K;q=E9!|K~r}moP&RKZXrg<{q)k+-(iSv~+WHktTiqFayx_%!@EUg2;Pm z)H8e7?K(ve(_8M#c?=#+EQW%3U3bgFUR=PWXm2d5<^uB9dvDqy0JB#{jZ=L5i-l=>obo0IcU8>{2mUYLt zBJ;YOakAHUEw2N#u-O-7&-r=T*osFWE7$4Bz-Lqt8HyDmbJnr6Tp+seotmkA7NC}H z+pZRrrQEpiB=BUeR0>n8c6h*L^6I9U)f6!YgpW_@xFWaQgCHHAd0)Hfro@=h+qQ=J z>O5uZ)*V`aiW-yD*s6~jJuUtB`arAM1LP2UQ>h}0Q8_P!ahwKtN-P2K-~zWR8!XEJ zEu^*OTRgz?xxSD;mFBB{tQ*e{I56w&j;->Iy};z>xv-;QZ0Ftm?J}Ym=$1qs)x_gE z+xBy&nrV$XHbcM@{Y4#eLL&1PT^4fOR0jD-?r5>i4A8VYV9wM+Oh*PUDNNbDd_LdxVN9BSIU%q z`3hvT_oYwY0gr0o;?!JjU}PL*&K`ncPc3_3Y&XbRi16%C2mJ?HB^bNXn)`{gS!c(3 z3bk$*IP-nE$JjY;P^NMIENgT}khwDzG`%0^xqs09FEy<(V%fN#Kbx5kv>mZp|6Db@y#2aq(-tInJ1 zuL)P-ugDB-sFyM)&Uft%Q6Dyeb}n9 zG<76d|FR-CNVnorn3osaS4Z-cjiO;jxHr;n+25jjcVq)n*IW!rEk`*fT^oBKldyw2 zL8{IEK{T{#Ft>ArTm$y!7EuT%I=Uv!;5#rZKjoglg&xgh|rXFk>Nou5Bf7~&@~ z+3#DXv*W2LH{nf;zX?o3X1DGSXClX+A9^m#M6K+3z7>}O(_7?IGH2`C1PLqurv1vn z8u{4Ht0fNd)*nt==nmS|0%#r{xR*OY56LE7<5@b#M!b$Y*?>~Q$eu(U(LDEhxxLCMQ%EWQB@{`&je42+tsX-gM<4LWzWBf zX{J6PaAWI!97A;em%Hx#m3e^LNaM@gFOS`^HSMx)j4R2u>2GCbM!aRZ5#~G=Lbd(& zPAI>H#^*qE2(|G*c!t%Nj2O`!^?W#)Gdx_7Rk`?1QM{|8Zo+nvo3s}dM!UK6-H+xS zwbg;2UrI73n(94E%m;*(vJ<1hGwnY3Y&}uI)rQhXD{IlP*Z^hNkuJDqx?LLnWT+`Y z^>b#Ts1Ff-+k4kA;P&`JK#HEmQlG3P55^y>Q#@L2V_94^5WIc(+-3>_{d< z4mJTBi1Rq{X5ej%0B``<09&G%+_@ICY3T7m6D$6WGfT8}`?_;I&E&KB);53RQ1+FL zzUSLSfGH~Ww)8u#Lu$bMs$0frwMIfi!!BB1m&#ZnUg|R51C!&scI&uL-^BK2+}J1H zJTOc0*!`CwO9`dt@X665DWH>PVxtK7=`24aJr^KkKmBE$DbNH`0O467xHK0iAc0E9-X(q}Dz`wbnYT7YjzVJ8Ao2b* zTG7X;#}Gz#H_kAq7UtAY)WAHLoaX2nKG+we;6FiVg|rqAtDY*uCy84dGEfe7tP$D zEtve*0O9v-UR2<3BTwe~ABY$02BmpmI_gv^w>jjrtCg*sZ-PoaxW5*sU9`+DHw z{#4^Y6Nv(JV`l5^{o&g$0tb)pTCJ1K;|T8K+kJyKmX21Pvmd!};%kw+={0Pv?W;Z= zmMO9QNJ}PUpcF3Rd}`H)C%4?iJ5KA#R|2EckAVWRrwITefIiF9+gPQj=zYqNsMzps zpGL?vd;2@PS$%yh628W91-9dV%wN~9{J$8$R@S=m1dIVo&kv;@%dJ~-Ee^GafCAxn zAvv<=JAdIsbwd@T7AB)w87ub?tc&jSDTy@gv#xBRCgh>H>U3d0PEAI0b!#>@zWdKP z{}Du-#l{bAPI#=aiL1J0*%k1?=t3g`&TdvmeAI#(EW5RZp4JeG6C8)~+(-uYQH=<9tOk3!Yf~ z6PYSr!vE!^CNEG&lj|q`e4Lnm%~!iSF%EJhN6U^jaOmII*iT8x`L0Mw3c-vOUSr-N z3wxxwKpa-c3~yU||6Q$!17nAXJuRu|DKz9Kr^{xZl^Eg80bfK@Lkme)BCP=M8+tYF zDo~k<-@OCFLJ-%Ubtu$i+y#g>L@D8mXEY+N`!jz!juf{z@I(X)FnLz`So(9K_Gj#3 zaAe7m5*v)LJhHH~m*eA)0y&{pirJORmtyaW&bNo5PJT^IWh8x?>q-i9&UNT~-c)Mk zd7sHU1QuBP;ZU^f`PUgbrS6V6;EYGXiD*Ry^;@*Z?zna|!8ZS^7*`3Q4hu_qqIWWJ z)jCT-anM^9Y zAnuELDrB{w2$)u^ADUc?*Vp?rw_UgcI`@@nBdE=J{ z=GU&sBvW6v73pNS%mWvt>4>`s>5&SH=mi(X=0Mw_@|jJK<5MA>$pDj|S1(gs=jZj2 zA5eRIcczw#YI)@7ROum6Vc)ZmY5lcP@T}m9IqF0mkeKke^V3~6?z+_i1HfMPKBaOr zgsS8IqyH$zt68183{i}v*;PS8KApe#>n~)|G-P_B2rx~MSoAkEl-4&OqYtxia z*5e*c-r?6Q1p2MT97Maz{j)jNtFj+0a4F7SLL3!>>5WgrcHFsuNj3}@Xm(YsIG0O& z)?GG5{Mt~&WSd6l_u*&z{QL005>Ztq?_}696&1<;=RjeMVL(3NJ?Prjap zvwoA&Jlo6WDc0+~X@74cPbIE?=H(rcAK0Z-KO<#tFSomF@DH@t9RuTFf%B> z+*F!Rr^*xw^#d;=f#pd~Wbd*&B&9$z|9S}gw=yQc$zXfa+gQ`Q`X4aroO6Oyks(Xy zELNChD_Im%oRuCY`lr)v3`zh7aA?Fj2*=l1YgYTd2&ocfql25ICUlTFvS`paAn55i^>WzjzBE z{#Xph?(YxiW!=^;Z0&yrgS15`QdjL$(vl^YPh>L>Wf$=qu1h{4-%`UYLw@QZ_HOlr z*ZLZ#>NVs=m~;~up@)f9UxfRYF0Zpg0-$E4@IJCPvd(Q{`WyWp0-&=26HzM7rPdXn<`$2klEJtK zgRlG5$trykR0$4t;Uk{^DyDbw_Tz4v7Sm0ODpy0cPX-=s65-!kVyeH$?KXlx+wrpE zRl*1PO|g=nH}@#+7DED1WI`HjoN0_~Du>@}_Z|Q~H7+G9m+sEEu?AGP&PcK;Os@Kf z^=>}&zn)%tR6LW}z=S{d0`29;$82}!!JNfT#~H^vT^};uiXJgdM4gVTHi+FcO?01X z&AYS8fmklmA@7$ELq)Cp4xca?&9_Yuh{eisSuKd8P1wlOPjy7S*OftNST(rq3lVyU z@2$vFViYTh2yTyjM?1$BHP4E1LBoXD)G7H>A3%JBfdS%KUq#6z50BD~tp-ue8_3Te z9LWYAgH8r5d&8dx+E=SWkaYfuLyfut02_M|QQMU`UVv5+V+})9Yl+#PvT?#? zpd0|EI9S2ogQZhc+vf=k1s+(70$^tPobZdHgM$&6liR;ArO4BdUFwHv2Ju%f#~h4H;>R^X&%l zlDt^kny2lA$C$p4>pyzEXJWl>ARiewH!}=mQy327ev!VmS$xIh51=Dk;_U#oipN)L z3tn~=0}Q^#U%jNIEvCTy!=%7Q#@6sCLJl(f07wG7dMC6%P|f4hI@*+FUHiQ%d|05y zX|tIE-x#kII_}|OT1*+r)r&de$H;>>6r{e_sMoTkch#}tXD)}0$!|3<+zeFPZlxkp zefNDLi_Aq6R5%u&3$r>(F=v^ok9p-f%!~bp+wBPUz{UUb`rtZ6Zp@1g9LBIP-nDA9 zI=(V0N_RCc;t#lpIXil^nr>i_b~Rq~8|lD3@N_|=TV3W-^~K*vqjK>I`Y7L4Ind6a zoZZJ*oBv6zqdWPN7ZLH15=19Kwl#iVE5?})7$x#96 z0O{sZ^&x?TnM`TM^Z4Ga|FFq!`Y@7Xb6*CInn$ixPfA&EE zZ|&~(nY5#^)mHfd#90#ay4yaH0}8iV@_lowS?IVHmk7r?OTLh#4Je6sHA9s940SDn zu?o1#1id8)K!4CeA8Q7!9m#|$EI>IXt#U&Z;E!W)?;*sl7xtHa-*8QU7*WfYUiv#x z^sR9x)yp5W0Azs?de`X#7^V=Q(FyegE7U>YW23fUwfj=RC#H)Djt~GN5?Kacx19|wN}U8mpWnF`~qFo`b9lV>t~J3hujvInLqxP9Ofl%cZnD; zdjDw1qg_LReoruI9QBx;Hk!5p^ACY4B!lxbe)v`C9nK)1D_QCc!j4o;VC578{$*vn zMUJ&SPR=^U_VzkA^>q_A?44SR_+Am;E>VqN4=A%uDWGQ@S~KX?=oFcs{}OPs1`ME^ zk|)UgkG3{lQZ4cQM&M(Iqgi`C$dObDl&uc#)Vf$&ypE6LN34;^VxMb)_beF|vlHWj zvO?SEx*AZ3fD({9NujSVRQu0C ziuDRCqZq961HttTaHmX}z`bQ6$j1lHaHX zWVMu6c|^_Q3cSIu8ei^{U4E1)d>S;rRhj+h5B8a8%`_OsO6aO)1=5TdEVILLH&|L@pg<+BE?IN1s;~JUocB8%Ci*~F;3 zrkhd9KDwmExo(*Zh|&#@Qs=giPhVlcdffm--avzds>p}dp6i4&I!+xCPR`DfOlD3U z>=NuQI6qHMCs1a&qG&L84PDA&X-<#S8ZvyqbQ#)zYkoH${tUn7OYA)EJ8cnMaZ2Q2iLBMzVt}HOp*!ISrcH1Z4jDk}z-%RGg?`qA|k(Etu%^@*O5Zu5* zbFFXf+M@o**Rc7HuljgcUHsmM4>t{gJ;I+huvo}7xy(mPJW!4D>aNj0C9GOhg&D2_ zv3sbW4kr3q-Qbyv&QEiR6xbCRE#;93kCV;|B$Xnri+w~ARh-HbytB z7ASzg+WPuEHY;&RiQdonPY?jhB~*}-QP$oD6nytrn0^Iq%dAI81sx_Y{rk6 zmK)hh0uNz%N+nXfsHGf5_VF5~S0hozOUXbH@4;#g-YYFjc#vyW99?+u0_`_bGjX== zetUXe7O*!41N(h9t*f9x767mz`@aC@F37##GTw{041G+6w z+^_3s;$Y`Q9J;6QUpmLc{^trdPMg1Pr1#S}*a_~bj~y#vu0~3S@0Yp~KzM1v$eyh8vHIH^;L3XXD_RU$c z*zw=)Hx2Jz$I&wT(_Vf-HZ<YhF4jn^z8}*`EBrHG|yg&X|@>B@}uG>2-W*btB=5vHjsGMAMmdmmhnzOFI+`8MdM~R z!FZI{tQOSVB$P9J{eprULqLwqE$71@x_gxRd;{5P&7lHPk}xl`lRYub8!B4LG*(OJ zR!f&m=u8LW(%S@Z*% zDtC`seTsqVRA7X2yq-| zSQqg}VN5Ver;`%a7E|GZ<*V>k;UKaOjJp@Kr`Gl$AIkOx4p^o%n_6@jVIy+E%I7Ly zurgxfPeYXABQ+h`?WzzJFkM@CsxnDsVD$|2)}q2Dou2(O%&uVRwJTt*=Y9ozhPVIf32-5Jpw#z!F^66p((}2er*P1$0brJ*ZY@G~U|tZcXvT@OBBK8YH@lpbHQ(X?45&gjf;T2l zBD9`7{JeU4_U8x$WsW!T+TR6|wc2o)*BT*zKK!%_g55V;1G`4wX0DTb8}mL5%!{x` z{Cxd*A)5kF2qFX0TD)9V$9x7F#MxM_j~^jueM#^)8V#M=UJb2=guLRR<;DcJ_w6#? zntz00xMe9mo7LFJs8vG=eFpg$*7YJfdJ<4Zm~8IyAMcH_&<#!1TY+r1l9uulejUx_ zdAkd1FQBqc87vdo^$e2)8IdIKfA*fYR?nW58-!Ppk+BTiX~>zX9PZahBKkdt%F*mU z)5^iF=%UXGN8RIzCLE3Utx$Z|0}xh!ENj?~|KW)OdS+TO-2Hy#LR)&O}@`Wzky7e zgLz7vKKij;5Qf;y*I0!Uo*m9K#M~fqJMjoYFjL+{Z5m+V*n)x;MCaTaAPr`AS;1N+ zYJ2M0&iL6S$9IOQAnrMK$O(UmaXM~rFYcL@m2J34QUc$P z;k49-Co$UxC>%BjhC*xa+)k6N6FC$x=dAf2h%=M8HAwOB2kQFbi>2B6j4m zdGoxPzv$SxwDWTx@{F7U2oSF}O3tak4Pp?c$K)(wHm!q6ba%-Puh0>By~4_q&PtcP zVTy*krOt0j`1<%nlWlQ<=(Q^CqLP`-;EiDF6WO_A3Nz^v)BxUMX&r6sGg$WjWaGV4>MQR(LdP< zFe|x0vFM5Ny7<^!%GVpVHj$hK@ zA2SL&%A$9dGQd^mxdmf8*L&<5`!AsKw;%gyEO~3EqWDpw!ywR+Qr=J#42tXqHbJKF?;m=S}> zJT+$HKKD;cQX#;Ja>FF^lu6rBT$7U{!9p9BI5yUy36K#zqvIq{(!Jue62{oj04!z{ zUA+bO6OSj#Cz)IT7_DZ`zK3N zsoIcO$s?>J%xqv)8e}p|3cU4*DqTsCQ}L=F&O%4`bePe1c!>iq-gU@{*Xjw%YE`w( zu$!p+0#nD0^)E5D{`&$a9p$loH7f2JzSsFM*`xwXSjisb=xoYt{>X8R- zgIL5b|4PG_)JooQ6O;1I+*fki6ed42$kBX5 zwqxoZ)~4Y})O0J3NxJzF$pSKUDd%2Ku@?U{mxRr0tOi;Ykqj`14sing*2*_IX8Q`q zL{(9;xr9R7e>D)21zd9Q{1{@kF^NQr?tVd?G(X!3IcNC&H{s=9!k@a)dskj*u@b1I zg>7+!0sD3lv#TrVQJoZ_Nt-u<$`Wv3kYmq397b8ps^Ck0p}wLdBAq}|>@j6aKk}dL zHc1iZ81i?M$mulB5y?Q=l|%lcI2>U?aIrY1fZ5)E@Y_rU78DP_?+O8k#u68$e`p_| z68NHq&7Ox4Acu_r8(7G^A>2vD{tg=e%&sB1k!Z9j3tzAt=SK)W5Vw69bAFF|M2dQZ zH}15IsI~y4228`YhOZDrlSjP^;kq#56gW(*Z8P&tZz}KB?Wa7Hxuj7;^7+xXo{xma zs5OV^yl2ZFxMOzp!A;~{*sa*MJi}NnMLK-bt)#?Hsg*&JQJYC}rVZisM~{HkKMfh% zMH9)t<~lkOoHo~*&c0uoEH9jv2spn(NG@kTItrjz`stK&iC6>vH&zWS=JEFy3rXz5M&?QjX9J*212 z5bI3{T~(ma9O_ne-#1*)X%Ifn^)~B>_yX>#%H$iC3We)FdcUx|Ixzbby7fq4(XVG! zO&}J2atE0X6Wm)3Xfou#&#iZ^)ZG;c7=tYbSsQ~jpTjht=OY9--H~T?t z!1LzPqG9eh2~ctLYDhl$f&S@Lcz-hbPoUt9ee4B<3K)^DiOknheoOEJzjMFp!{L{K z(ZJgd<4;E3FKZ%SFd|FDd-ES&InH}%=&hcAm=47DzXOx`CBB4#;8n;yMvUq8Lyn+NG6g=o&txA=C5 zwvUb$E1}?;B&?`4^ar?9JZPm=lN~?n4e&l#Bsx5Ou@5ftk{(z_&dl$AZz1By)H*Gr zrmCJ171?s?*`)bCG7_+bRlwf~@6unE^bg!QUYsf#4wQIcWjr3=)rgFYUm^kIIEdxM z+L(AeCqQ_k-DNqT3^og;(?i541$DwT5s3f};Kq}Q4h^EbD3B~D;PF!AOBTYN*DzlI zaowsa*uVhQG`+07zgi~2jTuo}yQKz93k4a@yK@G|u(@gP==b@{S&AcLzDgQ_@|L`a zmQWZYa+epkEsU1qk1h25sCW=;tFQ52x%^+M!aOZ(f4~Xi-?5L+HCf>VpOUxIkLVX?c%lL26q$px zn3+s*h@B4kZWu?HNG4MxO48Zu&_ddngx`n`FaYczM~ae@TEfKA2{}RL*r;qyu6ht3 z-CvvJF%&yal7K2JwKEDq{i=(Gk0@B*1xokhiVBvRt^fK}Qmf{VZ*Fb`{GrA3FK>1> zjc@YaHDyfAl5&-EcN3{w{`%|Bl67N4fb(lGizf3=(@;LvuH1a}{{2$3f6GyGaN$&H zmp)nH-OjPIOpSsAQ_Qv{e$c^)WZ|Gc-M}<(QebJgz+obwwxIRgX))0`Vf1)JEO!~X zdmZI=*OpkQ%I~rQ1+8%HdQF8Y301J(wa*_~K1W=xa40ssG*R^{B`-dIIg&$W3yM+WR1%Y(9kcP<7c;F|OwgG8Btppt!BqS@K8%vl%bCli?=qY@t8;-KbeeV@Tj0 zPx~?xX><3}W;(pph5SGW(Ui($oSt6n9=$`Sz~uz;kG$}0{~ETyA<>e<;P%*uv~zNZ zJL{4ymt8CU7+?KsZCg^%X4)G#NYB+{#l%bflC*wDqb^#EW%~^o%M@X6-jR!{8gYqucairskv`lQ2J{&*<^jTFUh+%^ ze9`@fA}+3C(Goa~zzS7;_~8kr2TwthN#>Q+RlBZdLENGhNZXwNg5>3_z&5s^V?Dq% zWXb^xORYrOjuX;O*ZV#cU0Kr+7`XL9!MF${veG{GC)GgY2sIM{^X{^Z>fMQ{*pEq8 z)&8GX4LwvOn|9h!zxg@8QGo>r!<|`yD~8@N@ZX>W66*C5D^&gAhb7<6N1l?x$9z|& zhD1KbjGfYcI;Aq>#Jkti?`w)vZ#r+(D!tVgw* zUjL~L3=?^Uu< z$M)afQrZshy}*b^$6q#qY&$zqGVAu!D!S1DXUCEm`C$|)QY||cn~HU&Mvego5U}f@ zDE=e`W{bAxRt(irC2t#>8P2})HiT39n=Nm-i{*r}4qhrIK2{P_b~VK3p-SibbFarr zAcK4zVWMIh6-Z|@zdv@=~guH6>?OMaIWKj@Yx#bmgvxF zVWKZ~jh`zCa!88IOj4~f2CXc3P#|y3AEJ=t&?&#L5D_qWF@J8ptDCbU-pLk(aLgl?KDN z|D+P8Rvno}+kp-1w+)jO*2 zuVY6mYr$Nqtc*?LFFV*6N`c*SDopGB&%Xbv>%A*1LlM(7Kq9?N+p1x=fh22vI*h_z zCLkU(nfYCBV~n1pmU0rbi8O1|K(F&xu%lZQxUof@=`Ra%nnod1=*lHeOq{DQZARsX z1`u3XQ(mD~NrG=%p={6{Y>I{I?$Z4_2EQeGQ=*Z(gMb|A>`lT7uw{ujYp^nCB^r?~ zsCNs2-{w^xd%=tZ&of^_NcUAp2NVb!>X7nt9Rp9)gBsOjok5n$JKr6KNjUYU1@a=~ zfUSn3qp@S_q}>0_K%vC5AQTWmbVn>8Y!(s?gOO^-R9K0~6c=csKniLz1j(lk`w_+U z;n@oh-*T#b);Il0-l@kEW9wtka0v1}5PyOfKx7bMWMqa&9CV!Q!FzdHKPNEK$=E;| zY%T~9QJm~bH%vG13A1v9cq*MN`z$B})E>(E%(Kfd1h!6iLNj|0VN!f$G^(*2h^ zB1g|0Jhj7}3ga!M%mu$2IQUm0AkC&gU}h8~izwV9{=_$O#eOFrD! zL;My2VUu8vB5(rS%_uQ=D1|Di+^?$_idd>ef1kR2M2>D!YdAnUeY$?i2!m ze0dlaoJ2ajS3ePwIge~yJ_NXt8i=NJoCeqj(Q>7H&0$3hqR6Oz%2ZVYwKfLDCu(gT zX~WMikb?O7!zQ4j?cq&=TaNyPh)A_+3XGy8`OanLhi9yyY4gyPA2+{Ox#9WU8k)>c zJhFlh{&t!C=0seVWIUFdbOqiy*Zfs>`xJ@H;K*@$H+1r@v^06$y(9rW-%-PjnQxy< z1aVBR`LifkDAzk4y6t!1#FT&)*-d$1ixYMD!RpJk{e}W#S0V%;M>IPG@~lQprG<|) zDi3;q|6$n%$wP4_q#rXu(mD4+dkC)B@)x_RAq$z^23gX%3+4qR7(h-#5KW+a)noUH zk_rR`Z*YIF0W&vsW}yIhN>!TA>JK@>ks>(piyL6`E4IRu1j&{a@0zvIxWdnqGs7QD z1HDjPd@Wx;oYGQ$K&vnUIr4P5@~u?)UPGbjQS7u3P<) zLZ3K0fAl;DaaQ?J-NPI7Ul;aTK}R~fmLHMHmmeU!7~UWMEr>5guY2;$?@(GH2{^2{ z=(C2wHhDg2n~Y{Js)P!~hk@va4b#u{srKfLeFu#}dt=mzZ~jVF(T^H&si<~+=re)f zhgZz!;lb@UGr4k{h)z6!TB4t#6GBmUA1&r(&rKR;3cIJlf7+nsUg1_DKC#$Mo$wLx z;px9v4NHvT?0|n+Yk-o0n+^{sqI~=CgB3asW*nSSA}1sB*;{SS40s6N&QAQk zZ_im%l0Z)$oa4((${^Y(?^ySW{)T3Wr2Q`x{>~h0fnqpoDMFM!}&Vlk+T) z2({rL^NC|6axZxHE`}`m3s|W+Oz1dH8Bw(#-l~+QoE^gbW##AUr=2U+==kfdOHTur zBDx^v|MdIy-Nk@KHx75ZIJ>y9F$rfooti`kd1}ABcwHEmwx=?*RvnyW3Ay6GqNt+s z!KgcNBAM3KF1*I7Uq1X5s1q!^lF3gT7{+9d02NBPg3tH0@BJtc;{<)j9emEuQGR9d zH~W8hOn?H*Jk7AX`Hya&g!uJutB{J&J>F-{)C7)2-*5%bDx)(ri4Xl=N|1;oDoQGe zo9&_zlQ!TOGwTnJlg!@UlFf(tHBaw-KDf^$m11*jo9MpkQTBYOTg8K?$BEV)%jaJX z8vpzRZ3PE4uo$28#vT9_)HYs}Vu_EgH)6!=thDFZN z+lQUNnf=5>61~rH%~W7xJhG4WQy>JBA3WV6?yi_^1&(*7!SoXp0nd8NrR6Z^hEaE$ zaqe<0I~@FHzt(=5#YA22>MJoSXCg;X&dQkl_Y!~ZO%NRIHWX-CO_ZC0qogy-l3v)Z zgI6F~^DQpMHb1A*)m;UgjezA36JfkWuh>aR;p}}07li-hg-S{T`9co4;bM}hCQmn5^HYAfm z==kyk8sR3LZ{VOPruo2 zb1S%FS7M5&rz{G3FJCe)ykINb_Xa}aKeeGGMH7B{UXFzf$O8e1I3qURtqF^ib(v#- zS>T}YtM)(r4=vP1gu@YXsEF8B0fiEUJc=>63J4t281c%{jM%#N%Xicj?i^`!BIY>I zlS!8dM(1)HY1IyPnAMtX)3&k+lEWgD&qRS)C26WP*lSSF21+jE|Lc8>HF%K!Q)l#` z#829&UcwcdT;%hIgRfXr< zbv3WlxGfGqoC{N!p$g6ke86Mb$O@9xc{&?E%yM=ptZnFBm-qX3kJ*Yq=?X{Dl~h!W zS{wH#vgEJaiddXK3Nyp5s?Kzu&LvcYY=4v-qQbp<&EmFyq)I5mAa5;6M4&Jr95oPx~~ z4@CqR^0$mF7fTCV2~RUz%O<`|ORd64)W-$951yql(3_ZPDh0_oaD1AebO4CO!9or7 zCe*=c2G!TE>oBko_%C&E&i95O!Uu<21+{CuPc_!d7N3F0=9!Yx0#|^1z@p@#17Fn| zOm;Q5z&1Y&eR+_ftayOjDlArQP7e4U)y}J-nVD=fM2J-nJ8Uc%dy#HcecHGcovlfN zFl9jswrAY{e*;nSFKc7JbRz29>^=LSNW{#I{P#7E^qZtxp+y0A9PBTK5itZ=f|p>dP2=}fpBG@5 z0+6N9;aL26rRT=Bi;nyGxnKW=_(R6Kc(4$|kawT&rzP#9yQnRwz^f;;wD9e=8(%1{KEIFlm=H_F*t(8MWzMi8C-Pn@ zj`QH2_8sq~=zp66Uvzjom5u}ta_=N{%=Z5Ei#s)D;;iZew{2^yN@pmV31k55-c!t7!rnWb$NTJS-eO#UJV|x6Lb#3$dD^$t)6OUblGL zT!^t5iqtP{t0+4c2rSAqVM_jUI2e@diV5JS>a109FRHp$NS}7>J`D;BxW}VEd8GF@ z$gH>yrMVsIQufEANLe8;AUIiCWj zp!tp^s5Svcx_HG(7`S;Gfgv$GCyn{Vl|=>#?LaC1xUW9}bZ4Wm!AJB+{{?q{X$+KK z6Le`=31u+g179LKnW+*Ls&H^C5q&RVj_OT8#jUIe;K(avIzl75f<%xK;S@!Xs21O0 z1n!Mv9~t*WC6F5Dwn@}`tYhoj{>`SxnbN%}D3jX@QK%tazOqIaf$M>!gn_|>BdWPP zilzper?X^S5gCTjBP?x|!}K|=c5~qjSO%MbeKQ54w{4a-BtoK(x(AviMa zgdMNbRf&3g$yf=O+2F4`yPv~XWEXIL+TC(Naa+pp$AP&-MFjW6OJp6^I#gCQ+ zKdP(QW!~W;XJcFv)B9I(u~y&uy32jHVv{4rZbcCJQ=9wfXYP#C6MOp_HiG`)JO-Y( z?@1Mz8!+O?wlOK}KYORW>?M|F=1X1?lE&qT{f#-}QVpVw>cN?x^OApakB&aFyW*Rd zY|P);NdtU8YA(f(kK0Z!4;}~vFI)1W1}GOl{~V=_p|BWV@23d~NxU3<+vmj^JzcghwV9^JYtGc1)+=?n6OGWoAVtj6 z1YP}3l9E|2fE)f)3G}Elm`j|Tztk9fF8vZ1uLe2kwu!Ky6=7g^Y0?mmeBkiP{_88U zMgn>dsi-Zu5R`4w5_glok3q!6U>WBu7u zKuNUoF<}trZT6S5ha&J&r`i4(35nLp_dIR2x^nM6UuOQZZzyzrKcvjo$WMlI%wwwb zfn1d?cS9uqA7p;Ng}!yv{$=O+@}NbED9oRMpqxD`YrnRxO84DA3B1a>iG_x>`;W-j zV}&`AZ0jAklc-u6GfCO%K8&L&FRTO2rS(hmkSfIPnYJohREjo%`oh{oNT#sv)QA>LiEfcZ2 zf`8O}Hj^>VZ60-`aO&uBBkxi7FN67jiOQ0@#fo%|#jllbZb@qFeaGyqWSrekfMGcy zZZdp;Lbg0c2Hep@)-#FU25zRrIW{ja6$kFmw*EIk`DoTUykCuNwR?QLAmY7AcEth}C*GJLGEE zjo8|g=@nfV93O+gyY{UG>TCzo>riux_;p8Q7RqAzac* zBZ3!gg_Hsve)asV8OqMUHvAa%h5MS&r)22B1#O*zu_$xZO@7SdqK~KqU^nE() z4m1|#$YuI?<$U6=CYewQ2xD>zYaX5ZBNpOjXL~+EM!Pb41e}AD=SI#=8gJxxuLXUJRc;b9Zt-~cj;f**d? z{IIP%F_8J!D}sEd-Za2Dz4Rks{G@lRNpj=I@@cy3%_@$?xY37Pvwaayvc1xtNs-I6 zBFy&`#G0I6oxBh?r4^Z)PV*@dG*IBiyALrDVj~G^`%vn7mTQr~dG(!UUtL9I?WCrr zV7@)0&%8qSowVqiVb|>B`)uX7&*A{^y%qN~u*|!SV`aiwHP$nf!IqM0dyl$`&oy{! z*M1K(4pp+(Z3UIx4fu%Bd#F`vp$r3eU#)$SamrHs$+cC$?1k-lFI88uqzTNU{?(Cp z=EMnlCWSkZ;OMw;5bV{`{E&eCAXl2o55CnY{?{ zzMK-!4&Fg)WYP^ygyHqJ1ru>||sRPn?W0iY|U2=By0 zV@sp=qJ6({e$(!)>;~~Qv#Ivc(cXJ(9`J=fhM?5J{_9ps#T%-`-Qe-b<5)Y2)^+i+ z-)nXI;Zz|qOL*ujEj^J9P6uehNU6#Ecl?&HnhaxwPbCA|2glOvWStN3^kKvhCx{IY zhSB4Edy0xf(#)v*kk-Xc-N`nTG5#xj*cbnnnd(LLl{ZvvZjjp#}TG9d#!Y_UlJiKQ3o1XYodgS+JQwWSIc9E)=@<|1>M-d=}C~yEa zaH%lneSvUo1S%R`|u5L;K3t9%sm?lhuxmM=rryX=ZKBZgEPv#>tOM&ZH`CSafh@^+d@8#z3#Y+ z)mQBWCw;cMRY~HNyQ9LM;1MFS-3p2*8pO+96prK%7ohwR3)U9!agJIM5g+?iArDKfgiwSMn4+OMZtPf%oZ=}eMy^=F&3o8j z0U76i{HNb3f`1RbFMZFSh7(J$P0V@&rOJ&mAHu5O(Y1{y?!Vz?W2G12+~1ZbttSq^ z^=ge`3wLeZPrWX6Tf;~3N!KClDb_KGg$#vD0mzrQEz*D;>0*LZbiGxjTO%V0h=PVh zRgdhzETY@xx2E}v%Q`_lxo~3(T`G@YBP(H~93o!u;5bPnum4#nZ}A#LISd{5oC#Y? z!}0-g3L}JphvC1B#M6fnr6m!<$hS7Zw=SV#YlD;_!u!k*tL=j;bQu-cVnt*i&r^NOT(4>uWLTnzc z_4L06!j?(+DZAI3XTE|sFJyN1k635-kde@NW(Y@5g)aBA2*kZXP0mI<8V{SQGT)rLWQe|I-Pn2ASuP;_PS*QL2S7ftu zdVK)4y#&SNJlmg;DRjPHlRPc}w*k>dq1XPOJ8GyERrsOp#DH)6KAj(f2XB~FC9tQJIJoQ zw~#1hZFj3u!uTq}xC{)^t{HS9aft=0ykD;fz{f+f!{UjZzib&`KVLdorFI8iXNhAt zK7VRRbHLGsEbzmPZNtwnyb~vtVU@PFI*ptJqpav1GvF&ym$+y{+~Ls0V*eKRGULQR zW9EtwWahV*N>-TIkj-NK?$@IRH5Hlb7Xg@EWVdf z48dgLf_3{wX zfj(!-9*?!@Url`!{#3b-X`#NaFmClcj*O7f@-EiqJw25m4iJj`sgR4?5{GQ;qp1!q zw`#m%RZBpPBmAMn{!miRrLiN7zJQX*$l-RFn4=77cdX6rqaJ_>STX1{ z%}C&G58&_VOW^dqPY{jks5RrIC^P$Xu=rEsLG0dM(=)&ry|k`xorPCkqr3U_1&XgS ztUNtFGe&f7>%IaMe!d}h!(aYOWA6GFkxErxyA!sPK!bOh)KBgsu)u$6flc}(U|9qp z8~OgjnQ~Q^K@x!0>fR1cb)osj$Hf}e@aj@y$oA?~fqC9-W1ar}AUVG^ zpuzK&8E{TnA9(Hffsd(sm2P`fbpH{$DO1(@+EkKUnF@4!?h9>EyzfAWd^0gI)4>$- zeZV_gVU_J4ZbBgRvhaN zx5F#Pa8wl+%0H^Db+BhHInMNH`rYcd8+V3R>}y3Wspn4F#o+^uP6>fsSyv9?L{cLs z_r>r~#NUyd(3J@w4N_Va8&s^6Q~rUtMMU4)kHFEsLbqnlt!n+X@v?-IwYLaE;Z=lU zkH-^Cwg_huee)i3R}Z(k;&`;U6l{KTBTai^&i61diL{yG_W|xXv`#}tJL2dfm(^IN zJhR>3X+4=!{CqRjIC~LC-(yaBkG(WaWNbfmD^e_}!@Q!Ap~q645M>%3dU^KF>ewm0 z{w4YibBlD-yn7D~i3#tX5jptFUr&Z?s`9cm&Y}E4aiWN0=uVIM!u+^d@BrJy!>Lg-?cO-Xgpkh zWvpS`y_%|O1X2s1=^9?4nER#t-;tbTg5PnoK}>Qb2?*jvwlJZ^fu>+jln;EF7RQ8R z6&Iqvce=IS>6|OSq0HA()%k&|&4%>N;4~+4`%N)!&*#sRq2_Sw&>eI5D9yL&Sd4GQ z=fisIrEc3vj#yReV--|+^sQR;(sH9u>LNB1u`7A36r6zuCqfTwmt|7h^{R_XKQ$Q4 z-tML+Cv38JZ8zZ`r_|vP4iP zcmW!7bvZTFRLQy}E+)M}`48|?1bYYP9`qzK_-sB#*C*f2ou}o^o95ri5+pBaofQAY z902$r1)>UkLYaF(fG0uA4~QY=Lp%YJFpwK=Mi-3_(Tom1N*Yul9 zA=)hXV+Fha)l}{xV`lsVwm`Tcjr(uOxJ7`#=KSZyAH7och_EuVvs%rs>h1Fuf}GTC z3(bmZFK=<^TW?Xf+6~!?+69tul%~BA!@iE)hN&Y~*(^eZUQJc6WwAWy%tKs+8+qP} z27{`mJ(d#GnTo=yYoc(5m2(w`2(doTRwse=(1q6;JOiuR*L(R#H$+jKU~2u~xR5hJ zAR&L>53U`n!2wkn!ObJX&h0kh0zX-hxEe$NF^&s_J6CSw4&APdQ2CA+ka{1y(h9q(jj zmg42t#kNZVA`{2-evBTa!!jHFY7p*ZLF*$a?IOW1PU-!p$hzL+wC609zx6xcS`n7m zHnX)w(XbS*J4bkQW^|jU6#YC*cptTF8AY)uErn+1eq5m}p2%8Vddh*-iEE|*hODdT za1)H84^a7H_yc1hQM0-VF8)jG3@ftG+j#k}(|Sc?vZKB2l1KyIHv!u3^;Zn&CTO== z)4cH7054Bs(lFn)bfun6aI-X@JXjyUFJjSF$ii9O>%41ogMW9M)0 z-tliGPeqT`Z&(0)`1h0IH)1h{5>M-uBB`s5oq~f!W8$rVJET(7AnF)E#zYGQ=*%0k;xPWThCJQsUX0_S4!E^V`R^{ zq@QAur?dMZ_?5gn8Bw)2^f+~`^zqX`=Dd}8g3As2B^UG=t=sq&jwS|$SUG3hnmmn5 zo~P8)7&Lf{Z5ddGVv4!B@$rK$xM=A}{jw=sj}Ajcf>Tb7C4ryfhPZW_&Y~y&yvrNU zcmpJXC+1k5Ex<}Gc#!^@11SV923_~kNggWh?*6U633q@Zd`|$O2GzAyUYh=P8UC1} zt7iuFmTulb^p)ig{Ux?ow+jK$MRc9}{%CD@-TyyVJ{|_~jtD{O>(C1+ zzAsm!#E5K_4pO-16Z|j1X~zOkK$uv}CzGBx2WE~2Ah#6AgZ3i?gpt*KU!@`B9ggBZLo0Tvob_RLgM*I>pgnR$8fO&Jv=}AwzNLqXp zT@SXd;*V4qO~@xab(IgUCto!=I=mLaIbt}Upz1-4^I`s(Y~@IZp+z zB0SJ4xYJz$%tICwfFH6u)PN+*mr~tLzft#x)wifYy;ZSS1vx{kzJBVvd}nzO29z3H zzqDAPf-@-W&44dr;AA~Tt3S~!Ez|U*4YKmAY5vVO%Wa!g;gmR8NA{NYKFz0#;0KjU z(zsg^oGO_v2TO#@R+lzYMN=ZhyY^EZLdBvx1mG1LT=H2OQZ?H{nU7*D4-+AVLi=81 z)Eq(I&g8Ht!h9A`wEq-k);$q4?^`G@tl=*hZ;vx@HtO zD!9q-FX&-d&@%PZ;piHt$cFvt6%`frgLQ92#JmS@Z+uQ`92DAaFETFad!!+{zuSHy zeiRg!K{V`@cTf1Kc!QTmv$#i2ae;G4onG+*vAw;me%JkPu;*UAZq^K#B^fL`L>HZV zyXE+s?5%{TJJ(A$(p1}TWOuYUO>~#c&E%X?`T=XxlsaM6=MdTphL&b;?*OpRnyt*S z)lHBYwJSxYRN`p1E;q1+ z6~Q_oz}K%nc=uG2S^4lhsY00G#y@^oG))axtew_Xsp5jyN1F_kxW|!rl_*Eo|5ig& zC}4R{6Mauhms*js9~H;h$Z#7|BG!yk^j@aU^$vAk@Hm&TBtq|(IEA`*oCayDC>eF^ zfAqVgMCFGRvI0YKFSnR1%fk_%)W+LYY=XP9U)tb2;AtYxNO@j@>u515(1r+nwa8AHIet%I)O>Qw%fdlO5pY)(Nx0!F; zpIZs&Tc9{ArhrgaRYYO$&9V<)g-3P}!8xd76l3X3F+>mzP9Q$4{B685JcT6iI?1wU zm^J^{BzT7do~^44Rmgq+o?Jia&u_4LST?lep!_{60O&o;jXvVI$;e8I10?|NOQ33F zLNZ&yAAiFfMirjWCIWIf3%yqyW1 zxdy3$G@T-CMJ>oD?a&v%SWY~|C1TMq-|}vV5VVMl-{sqcZvaA_0E*sFhPO~*|026_ z?%{Z5ji=1V)R8Z+p=+Y;*j2nMf(MRfewyDZqi7`t@oduapup3RwaiHBR*hJBoFZr) zyyX%lnW{j+YzK;yiFGdeY;2%bQ@+6~`8}C73%+;^+8S@r8S7pXHC+^#>cLEvJvz7@ ztlz-p%kHTc*o#}5sB9>;#DZeF8xRL_1+}gW+iF6wM#tM6wJXlrb@wUN6-(H0q784E z8V=E`KKXS%K>acUt}!mE`dpuP;$W;!T92EmPe~nDp^%bSS-E)?NBP4(xJ7a-a$oObgwzUcXIWaetFQG_OW*T{Vl!BKU=CZkcHcd#kHqbe{_S0Srs0aQrrm>>(T^u zzS}4@s>R*Yi^FTWk4~Vu)i_IYC<{1{#%x2@WCk6b@I=mBgKXO--3hw?E4*DS@%7Cr}E)s|I<+Zr+eL@Cg0pMuc zVscT@RJOn72af*tUm={{Jf1%=o$=}vR7_dMT-iUEQ_2lt;G1%F{;u$-+^P7#0|W}pj6C3`8YAp{B%$zu3uJMebN!GDgJ;9P~f@1@Cp0DV`6Jh!&h}ELyg@ zZK$!tP)XqcO(R!{(Kl|}`B{DBT`wp$zY}cSpa#B3ru(&to-@6m#fUrn)drHjyt-6V zn1O+#;MrY#!E60%MSqN{NqLz@&o%CeyGB#vgmK4EWY=-PhYw0es`+QU5ApQKG8B9C zK9n5m_WN;KW#lsx(wH*v#wx`- z;YwT3Z5!EPo^nrtvZXH1C{e=n#u5T%U<-0Xwv8-X;>e2!9L?_O{ZB#AEDgTI9~gvQ zwj2?&Kwoh5IxNJWfC;%f4BA1o?z6F0xj#U=nK?2}4|fOLuoHKlvG4N=ouE-0>hCoIM80DnLez7opWrATCB%%mjcTUd{qK4P)~yBA^IQJ?`PAp4HCcCuRX}#5I`TX zgYzb!ebZBPeu5*XJaJ)v{)91B`ehLG%Jx5Px?WXrvZhn%UeST9B}~1fUj7P2MG{kM zbjfSBPmtM*v2NNW=--QV#0(={F*a}~2m@f66Req9H2+m$=}u|dqyWOw^x*Tsb_*BR zv+Hotlqj+A*0h9JijYExxb)uU7Zx~?6%F|bL-U1)N zNCO6MfN#QHfU~lOT8f<=wql}awn!qRaTv`V#x-hb6%ES;T7J}DS!20(FW0y_MU`%Z zR?FPnGCnQdw?r}7KG6V_CK|07xNZf-GUL~&!{&wL3T`qe6*Wo-LyJ0Q)0Y5B_B&yB zGXxKqgD%jtXa*WB`9lZ;W$2Aj{=c^I|Kk=Dz>lJT;&r?4f}s@9W|rxSYD!`vn~pcX zqaG_(q(Ex+8nB}$MgLdiWhHFyjQCBxl&r+fm1Z~?vGGz*Ez$)=4Q(%DpA4u+l8w_VA*^5XjBly;qHXtr7(f-8tVZ+Rm$3Xb zdN6wpgdU5Av18aqwhJZ~nfvUqztAXuO`>00svr&#!jb@t$W25(kj)bRz*Ol(g4i;b zC5FJ^4s^_nnj(rXJjPbWGHv~HSjpZBTe0RMdAysnc$87>lh*gdj?nv+k%m&)SjpK(KvMbsp zf@M^buk}#_pbL|@i>4$5r3F(^y5+mqK z$AA6d++sZ!GwXmoaJ5i>n046LYm3Dr_jnX$mHR%v{@sH<;-JzykUCJzOY8~drfOB4 zNHyqW2XTlmyrkh3;3-}_w|f5Zn$#n^6-BnTi-@Lu_;85tmF>5>7%AmPhC&S#3vO5+ zGw%MOhYz`~!&WG7q9u9349n*c8*<(=A)y<_Cv*tmgm`l7RRF|}GUhydQ3M14U+wKp zCI?D4Zuhzo_bEI8bYVMUoypH`J`P>$Ng}4V(aY@|l(CvgTlb}+#l^E6Qb)<3fM_^i zWfv(v!V7)vbwh0zKBN9D4GJ*injf>j|I1non!Yf|5bG%`P>9pZnggi^sA6?F+ukVT z0YP)d=#8`gt+WPEKEcZak+0GHt^fCyqUMag_?I zh!fg0;M}YO27nXrieGGx{uk_BboFEcd_{+}-cnDgTmqpL@PFho7pkfxVi1}bXB zG;;#4gNqC>bJ|)7!{EK)N;gsh1S5fD!Dh?CDflug+o-I*)znuEu1R&Tj$pA|0GQF@ zLDng9d;uqWSNvU#)cY5Z06S#=wTbgIgOh(f4q*blVGw=%ssULMfkPNk^80gLDa9`u zmDC}lpu+Mm;rXv&eCy^v>Pn+XXLudKKj48njQal#!UaNu=jKSF|2#w1xonKLAMUSb zc@vgjHhvS3y5H#9ov^h|cT4zwKYb{lQ4YfPoSrFnZd zXvh3vgU>uy{xf5AIynJeaWZFI=jYE76jKz@T67kivYM<|#cY(v%2l3bXgOK*7vyt& z%wAD^$jZY_c+8V~FHP@WWa6TUklfK42o)&~Ed&l<$^&2I(=oFl5qYW-oknUbakAX> z>hF#lIHZ6AhqZTCpdxX1ILU@^JV~PyIv4yw5@#h_#HSYUp>!)#M|)hTOgEkmy%zEv zw;i4%Z9bTt@eI59@PGihi5%uaUh}`>yBB0^vEqfDMb4m*r$uRh*IxWUNgCsRNHk(O zV|b$nMo5?}`Uj9b&9)g|`;X}-B0D`JtM-0HMz-C?_TxKtq>Cg>M9PQwFI&!y&GP8r zT=2E{NDzE4fAQ>@uRwG2o%|1fV!!q@89-)0#r_S5S`sT{VH*%Hml=@)(Hk4x^|e)+ z#u}6Jne_hJSXSMOXD*Mo45`98$S~9;paTns*-%OGWE)Fnw%4L0Igrg`^;eAG^Qu4{ zN!GCTYAo*)WakjXl!Kt)^G@E|JFd)aZ5tK&Hi1`{4YDyIbReQS%Yl8Zv7tPEoTd$i zgv@C{4dZgoNx&L#E*w7`I`Q3~SQ>ng68t#JcvE4!GI!vac$Zh|SNj=Mc%aG5?n=@7FgckN?utcBY{y=&u<9&Ck##)AMup9Le$h)MuHLiV=?@7hDE0dG?58q^hz-FO zq=75&q+D`tAy{NVTK}j-kTj+d^e8@8UH)&Ww_fq7Jnk7UmIZKzNI6U0Yvu5{Wi=S)E{rD2N@a)BA0qE6>_1K{k%xuI^??{~*h{ zAmU|$b@sttaVy&=40-k|6#0er;1 zvo);Uum=)xP9n!C(|R_AAXg5&6V+e~ADcpCLoP{QcQ!@V-PMu+2bO#tm=$Q#b1H+k z>#e4y!RtJY69ks9iWI!DPAv!@&Qryx@#Z8*o3^d2n;KXRlm8lTp6Vkh$oV{1)=A;PA_+Ib(vl6d` zBK&EP=7;8Td>4{9T8JijOZF$_$IKqdyHAAKKdGWtWwJeKIAnolszsH~qO6-_kjG&% zF_q=>XijG`>!(Xclkda%rO#a3cdkd?;VBe;GHa_4Zqw~F@=G6S>+h7Z;EjB=ZBZi zTi?D5@P1uSC*v8g@my6yanOr(@~5NIEU}3+RT;G38}fx-fnoi8;>z0xb1RXRq3beQ z!xMc|C6AS;1sbqxJxk1xfK- z5OF|kU7%oeG+k1NV1*sIr;PNc1R>TY=LIW=d{;)BIW5PH&SaYb?mhXDf&IK{HbwHY zu3Vg9>2L4IxUc$wDy0U^bEOeXUd~@S+3|*+*=BgDA@5p8jf#Kn5O_U9>B&R~;z^np zO(}{!?2)Io}USV*3CF`CPgMNrf*;9Q))$`@U7{jGyV^ z!Hf;{)1-H^c8-G4pt09AfqSp-L~f)13h8u_=hafh}qC zZH?Xb>^4F*7|;JTuW4~430_}+_1eFEO(;)DJsR4r7<5j0ZUarss-_l2<1Ob1VYJ57(oe zDSHG#?VV|&BunkXGQbOQgtcWE@P(L!mFvNxIP)`S1Ubfb2pi1WXu~SF8$e@Zc1t)# zDLxjwZ=1|`$-To{_R_^SHBwPA;z>CXZgGJaesWurinV?J1e8Y$t*YEj!{`oEqxvY%A(e}gM}A>YsB%bOSc z1e!@2?bRoOjUN&cJtB(5BFJPGq)YhI1Z)b<{WGEUVAH!;#aI`hA0G2!aYN?hOr4u8$8F6E zU>vRqlKF>#7!Xz9xJT*BIvk^%3Vk6O+EMxB@%1W(3+0r476;kHS=hQwpDMaMNY1h( zeCVLAsEYD}BJkrpjbX=e7eyK!=QnWztBJ@Y31H!=;$jJE4oo2jA@KlF;0h<&S^;b! z@QVp~lr3puoVf!ANSJoBzJa9F3Orm+3(HLYPKYiKQVXz^wKPG4n+%r`Jdc5vh zzq2v^TLxTH!T$UXCEBm$xbhZw6|%O=t5mq*R}^0_LU0m4KB;t^m!^9o_z6tTqw%a` z_Nq1ZNQP{EOkoyLFjn~XDln(8=L`33+b{Lz-ZFIpLy2MvKG2qbz{;31IA-XL8Vy#w4 zTcv@=#y9%z-@iJ|fu=vqQ#?<6H#jFsL*YkrH72!^hxSxs(cly?T1nmSLEr(0v^~ zCTau*_hN=juFD*Ga(&3K_Qv+KPtVRPB!OtzbFWu#&GLztZ&&xpi4 z=0$Eth;>=AY?6lm1Bic&e?4JC0oT?ts`LQ2&*Xq-;%DPW*NeD3e`CSvDo;-WHfs!W za&36kYP{5wmtP+S3UEA?11Gj#Sr=s>BA@wHDy&-rUbkD)FpVEaX(!BSd4C`DegpQ& z;oxgC%um%9uxu&6SMl;%lC!u!o4oS7=6rx_xjy9^h8s1&=wwan)h`b4fe4Xg=y5{< zwHbd9{sP$PqyxggU)%qvqRPM1)FVf+b-;rbX$27}*macU6PN;9q11*Q1kKl?7bCOq zB7db1Ov7;C3ahS@k#WQhZT;S-iOTyi83bm3-!%sWuYji4T>@Rgdx06D!*9!3%)Qzk0749uv+LI!S~DL^^#dSc{z(@ z@(S=SK?L!EM2p?@t^EAAZv~$YGc`3{4YdL5{^AE71EOuJm{sJS5g>v5`K$hLzwl*O zZ%??!-mRX=hY#0>cql(}Sk;F=74Y*N>yMyc<`d0YnIY=#y0NhLPCJ|18P^gBWS^bf zH%$b6AIIq63pF*hon}Tx+sr7jc}q+6)`PGsDr#;U;r!a#9U6|l^}!ND0jhm7>3@^H zK%fS`7D;v+&9g|5T>=3pQp(Qm>`Q#qjoW2f1j_|Gp_qh-v2>uU^gqwA$p|?j{MQWw zQ^ghU0c5{n?Ei1No6CcK9N3iQy`8aT+}xC-#&IZ4+Cn4f0`n6me`l#t%A%)$TKVB* z9fAY00EY5Sn~9Py=_{Z0Czw+d4>KNS4pQS|n6JNZMShcZphhw`YWw6J6rG8qB9 zettQmKh9d9OLq}PD+5^qa7+eyiw02HnvYQJegDk@!1b+PL#Ti;NI7XQ9cd3;UBD-Q z%^*uMMyc;6-K)-w%La5~(GJiYXudl`0HTyB7`oCt(^VE++HUlG9NCU8%?O7Y3%MQl$nJvF&sv zg4^(?>w#s*-z?@D&s+sS-|})D`ir7fq)GCeet9ERN_xbJs4%2R3DZQ^^0zZNYIT1? zg3_Z>IHbOQ2G)JYr+If81W4`QYV57l5|WNJIIe_1s!pH13tE4A}6RJU%?^M*TwndH;Q`<-sb!o8<-4Fh~y<_8oIn+5Wp8 z#Q>yOGhw%{V(~YL?WvViYJXZpD<9wk3h$Lp>t3uNZ9-{m zsph|4k1Py|AW!-5AzYPhw>;SO@Q7;u(FpN2_5Ub}3SUSU+cvn)&w5{#6Zd0NQ|t9D zVz=?@Znc;JP`DSiJxaF!lEnzMdXp9MrE9qhyrRY!u6j*e*`vD3m2c<9ts2NW4jue% zmCuzej-xB7$^twkI2>x@aK1@z^#28pz$}V1Xv>b+82M!duSk^reh*eVQ?HjV zAxF3x*3mwG_Y9+(Lruv!s(>wg)1$~J3lepEsokUSt@h%{Sj4u9$}xUM5f0_a=M13Mm_6x3_`6W3f@;c*PZ zqCcQJB=ZheS#{qwZ-Eh6V1*nhPHx7&&m0Of5tMtw*aG$oxXSgz3%Ngk<}#*){kaBa z#NlgZ^07Ift#%_w5hAdRC{oU|m$~P8Z8RLzQn)R@n*L^;b9L|3NUcLDfj(JM3__SO z2Wwf2*k#V&d2;W6fv?z``bH2{_5MF;qJ)3*%jHfM^|qg=t;GJ#rx7By55SJSF6Yf% zuyrw#tkoVo-RWyyDp8#o9K1IztH{fQ%gbWLd}Y>b&ItYlC&vKr2g=$@}DvTEh%?@hMQn=9LxZpUs-kkOFF|^8gytD2#gi5In zdh}bsMRi2unbR`4KQ8|T419t=^nih#@9`f-6X)FGa8~4l~j(vi>GZi2E^3 zvtN|9g~b~7{z?MMm}U0>VbcUHD-Z3D=g_PDKvamMiyfK23iDUtdd~@+Z7K*{x4$U* zd?pZb3Aq@hNuL-V-b0V}$U!FtO7IRK95a$xCZ8Yw5wG~=X?#Qo%w8?#=C|t?<^c0t--&3ml6GH zBRS#~Jj(v2`S4xQzNRSH>%iC|fZis0J3cR;!CrF?QpJ1OY4Up_Uv%v}>RvrkT6)sc zBtoFQ_nf$R-Bu?#Bm)=0Q-OQWSYX_v$;e|jCohHF>2dZYAP(-ffT`8gIiBaIutFAV zuHCorWWsvHHfj;2Ed{t$%&yj_#6gVZ%s+2?aMKavd(hD(GT4-SrANDvR)_Z_FnVa6 zn)d!6QypA4C=C@Iy7?%QD8rZluNY31xXYeEV%;mQ05Ar>Pp4c{Zqq)o}V3eI93HrLpj2#E#`_h@~;y12aumRv_@g zC2a{~C7q@~1K1R40SM# zw(;XR>F^GI~~VnWej!JBn*k7LO#vE7^8+>T$I)bsA)rCV-2?QOq=xno7R211f>_kPw>& zl-{HGpdFFtd|LdBT2%q{K@AoS`A)DPXZu%3R7~eFgHtt}C1bGZpLV|94$OpX20Inc z#K0v%G&ziiIaE|>b5)$LLE(vDHrJnx4FPGRYRa$(w%wE7oCL+y=d|zHS(&JqB^f8h z`Sw*qnqdbW2%E3SKjLEF+*~H0XJv@w2Zb8F`0~h!0UqF>ne4Gnh($st-B6z)-j4db zgNXPa?uwraF9k}cUD&bUK6;pI%I*Djxm*n?m!(%g#yDFf6Gs?}Rw;Glvn=8Tek?n# z)c$;~DrMMTSpBOlOEfj~qHy`=P;G7PV+yw-UAqUELh9zE2er^-@bIH#!Sz4mDc^a9o(Q`Bhu7UsA* ztC_kbBk6|V)l^qF8K){B(dZ$410 zQubaE{pvyd@;vTN)qUvv<<(Iipj!KASU7dgVl$m>xqdbcwcp{>bo!_0N4&P~atih_ ze!0R+)&;{q=Sg;+`R8A!21mnb3r7!+*(EF%^lia^6w_r#;ud1}P*|sjrWJ$%fzlI5MPtIHS=6je;GVjZn?;Es}H%KO1)z$uQs2**fStztvf6Q9aJ2$#(T%h%FXtVa z$SoGW8nS}jcouo5Iom>hl%8f9)1rji-RlOv^iGYT>t0GGd_hr|wO>BI9S(BnOb+xe zF`&ko^lX;n#o1ts=|E!I)s9vxqA@HVreUZzQpMG%Gyb z_U>JWywY?3z&p@iEW6~G30$e!jNQQ4q`gMm<)_l2|A`N@c6b zg=5M(Uujiw5u~Iw@$gZ5+?b(5GTIpiDv+co{BeN$bi-)>m#Qdu?oj5&fEDa}$kod7y zlf@PLr9r;UA!qN)cL~F#hWc8xI%v=sF+&wP-_^Ci#igCkmo_J+yUQ$2wB}nD+zsur zc}Cf|;Jm)LeER~;()Z~e9y|bJaCcFc$J+1t$<|}xR1xtanE%7Jf^+{((a-%tbSuB? z3wJg)>Zvtd%+1eY>wuX$N2|9J{YNfbPuFZ7O(5VR)T819B2Hwe`^mCy$pFkXEyoU# zValNs;BAL}jTD0ht0n*59Q~Qy!Wtx)i>05oy?vp8fD{v+6enEjIoJobeT3Wtwn%jR z@W<$5eZlaE(Keh%Y!Et0%vXk1qc9D$2HaLz#H60C?lW$?uqZ80PdnA(g7pAO`)CYT zh3u?12rAU6a@`NA44e(9}-DL{QuXU(8v55&3b4 zB1N1A@?~G^YCOBf%=pL%y0w^KkBN22uK5AcSl^V!Uk&ITm_=|%(Sqq(sdX0E>X=mi zm&z}9w^oJ_I;U5CEYN9FQhoZ;Y-bU$if`tB(fK`5>+ARt5HS7EUUeZ<+&v|_I7_)P z(NWqWkMIY2vc5l~!Lf8<0Av3Lw;njeLE*q|;9&k}AsK4M8Q-OTqa63BHM)vIxKn`H zjUS3BuN54vNK!^sceWz9{;3UN4CyErVrl9J79J=?u|8pChJ9%28B@cs_&GF1Y`^JD zg~o))ZlwNUg(inqI$2Kr^BBg3#<5H4z-qB22X@)3ykLb4idGS1Y)7yuRioH7PNxUP zPF1O}9;y>aj_U7ftT+nM^83vw&B=E8I$_jQC8 z=$?sI2%!2ap%o8Pg^7LqdJM4}7ri8@f+P*Cq_etxD!woihgSdF+SuR(XM!3v0BxY{ zMdt8XpG5}q$}I16y3uM*5T+j34CF<lj$2|`(k$?2hURJFhr@8PBcu()Iq7}mS{2)f0 z*U_m;L+7t_o_EhjfBsi9+Dr-NBPDmXcuVS;PPZW2%)pE(ad3uXzf;LD*=JHeP%nrW zkd0IYX}VTDwPr0wp{<@Xmg7n~v4>%HV&Y))i#upU?XfM{M@&RVH`ld3$54Ue&L(II zb&B(D@O=B!)O3zB)@PLqJoo@oS4AaS)scC@zg#-3p6*!LzN9 zevg7yb$grpU!uC*fB;rs!EoH9q@?~7sb`*nqw)Dwp^~ZSseJd$N&_uF5gN}s3GLj& zYw}a{Ln8O-(8yr-%ZryX%f9u+yP{pIOxCPMGP6K>oqAzE`ZOSbR(9UEp8q27(CLX? z#1BOn?2MxI(C(Mma`9Q(W5>HO9gZX#3iR-rO%TxD<^G{ znEK0x`Nl=VG%@FDp@AxjT5xM>2|Pn}o=*q?_ZsY?IJgVJuzA_%jemUD$u)Tk6kKLZ z&Nxv4%6>X`BhzQMOG`7B;V0N022&rJ4(amGd~uX32WdQ$vhRl4-h*NcNPipxb(#Qb zb}W&(H_s`DcTcrkRBFGYusJ;-@)oi&2}Ue2s7VDbgy{xEMb$eqdBC#1_Hb=Nb&xG= z)X%M2=OoUEyW(3Tm;D_k*fS}M4cIS8AaR%(T0zgFmDA*)n)R%DrI4w>WSFUA%wqYb z+97}GZDj{Xjo-#5<#xL2z355`rI-(RNAwtjV0(QlBO7mWVc^(=?|wtc?rMX|X>8jh zOv)C26}<>A)*tq5tMl&9;^dfU31!Ew*IDQJK;<1XIwXh4r^Atj9Q#kZOnXSbIAge4 z7evXpRR)(q^Yt0Ym(-|{{pR*(-<4oZ?sHX24E7t|(_0h{P9_Zkj=x4w%_2{G)>T5? z6O9Lq1=MMR&`_9hgo#IYyMKf{9PyM){ruVb$D4JJljm}?h}@)UlQ1^>&)-vo)taKB zBHj}{x+y6txKob)J%F-aYHiH7^E#^KMIAx=VxU7GT!o; zYK4I`F+_s1^VCUD{u7pKCk$2XmG#q7D(pFqZs#*J2$9C|J*F5l0b>msr-cx{5Ggzk ztaP9%&@oeZUN$7c1{+OcDDuE<`=GV|Mo@13Yw(*_W_PU9GV7fwtsmaL;3tJLFQr8}5S8EfQG%uHN zeh#1;e5*QD&aW~T#uCRNWxF)AK7(6q6jyt~Hxa|i=!|Z6<;t#hyK@R*D4tJ853Ce?{$?$$YY zv&W8&qzeju+%g>=8*``Weu*QSaJF7|<*OlNINP-dF<`s4w^oDC49mgoUG<4Y+*|1E zbH9T^2`aGzx0%)r~Z+3 zNDp+o%$yKZKz{X^Wb1%NhLm7N`7thN^Libuf$qQiw>o)8%T1#=ZXX7^fHWAOIqxU= zVNSDPgGz>KZc@{PTN`)O?=n!1k)$MmGlvd;%nJ)D_phz5{ z^0R)_UV!cCVRE86{L!ky3Djez8Oh>iLmB|x|MhQPZv65ZG2tqjMwXkw{>?jG`^v%N8K6L<&1oBeVmzV` zu9Jck((UVf%y!)4DA0k`;o6TnWTJ|4#PM%18>+Nqm=qQ_K`Ga=dr9AZ-U!}sOXT;7 zxEZ`X#?il|hv&Rw+x=NY)^V5Uqrvn2iF`R)^@h=J47Jv+RM%NihL&F>T2aC9D_S{P zl!%tML4?Rqjjk^FU$YWlH;`O5>@ueXat(y|)_q7!v@&$KT$$WB3b0V5uuld;uG1MZ z_a~37nBVwoIV~&WHCNxcxQuXE97@O*z76OtL5s1YR?Y=B^4LgZj6c~RBb>LEqQ$(n z^y%BDE`Rc#YRUOXqC5iv<{L{(b&b6|CW%1@S-1pP{s^4BY}0^h6VGaD@_di9Q@r0z z25VYyOMbC$X4{kSr$tLw_pHvQ<<6ehSf7mI#)iVDAkV1}k}nJ+ln}gVE0XB(ONgVG z4`iM-6`wS}TsZ6M{_`Cfxz5BafBq#Cln8zIv8a_Z?+Kf=9iybhGtGT>ccjc%zVnA< zGqASfK|C}|brtEuGtCF)8OerB`+%>UCdcR#eVoo&V;v_F^JQr$gi=U|N2b%rf7png z^zl3z1RTepv9Mf2z$%&-KS3U$?i3WH8>hfaofLt!&pgBf8-zW}t^SKhfkO$-cJYuy z$W3(p70zek>aGf`66HU&mXlVoU zSJIGmVXaQ^Pcz6EIjK3L*Pu>Q7ri`**7=2UkfJ-m#oD|sQ!ww!W%57OqHD{>#zTH; z=Lv_h;k@7XnZ*)~M51{3;$2%XM8Q=Pcr{%4$&Y-suRhMniwi6092Y_rKzRwL)cLRv zx?FCS;T;`X1=yC6a}A3ZueM%7o3Q2H5Pn)j6``CR)!$=iG=59S_E2Khyvf&(^Olme zSwa6m8Tyw}lQztuWSJe5=1M0^$DD~TU8EAXF7I1J29I=-@gHC7Zu=e7RnR-Qxa>fK zu(^=LVkwt(vQ`GP@i#CAE8dlOv3N35(aA>}pl($5j}anzPpj|ap^t0a_1fd*??BX{ zSsWvphZsr*rv>BeH`BNW_n?z$?YbITF)A)}AN}lQx>1IMBA37-E0Z+6(2WNuQ zh%JA(4J8aH#Xk2yjn=6xXVjDKl`zxmH8&u3J+LGTC;CYpzM@7<-?4dI(ZdH*Tr@9aU9PO=t3r8Rs63+|+ zM?e3TW4nxGX}ng!e-9bbB5S{^Cpfu|8Azjr-QWu1izDfx&w|UQ!GqB5{e7 z)|X#bmjjvY+3w23sIMk19rnpaN*MEqXq6cL8CqIAIIcTos7~Jiswh^&tO$RDqQ8C2S4D^+ES?=Unu>H1kk`>vU8m z(W7*93#o#`C_(PYN#h05I~CGN>UN;nlrZe_XPc6QgpuMerqk?|h6tj>ugHbR@`L{F z!@i2~tC`*uu^$|rM)W`T-}k5^$~u`g#c|d5HFM6$c>vMF<_6%hYnP-!&Fn$$Q8+{x zXC`*59l~<<3ka&vOJyEPE>{ZF!DEIj8{gypJ~$)#;JtZ55-0fN|B+1xTpQnVrOw;< z&Ar54mEZEI9Ic*on}&Fl0?V(lexV5{EtrrzK*ry^N}<6nOmd~HC^*K~@ zt02-O%k>B^vi^|2pY1RK&$mKh_7sqO#(5`p8T)7)_oNh0g8K6*?12Kq<4@ zFu{lr!)(nRUl056&8j(G-H-8JJ(%DR{Z%&ALIjcv^%3{TrS$Y-SBP+Wj@Ud|G7XYF6Vi!mK%mZxzpSL6YOq|Tv90~1t~E&=bdhx*ce3EwnydkNPfVHQPOQ=aiGI`ZNZ66k>ArZ8fyeJo|IwbSTL~ra-J!gGBV#*!lWrf>2I9~N$Axv7y4@a18yXWXy-KO1&1Gq|gNBig z2?2|y7c-=t9laCq zT3oozZ};6gJij1faeEj12G&R`r&t-LmYViy(>8;tZ?>eGC?5Epk2L-K21WFu-zJ80 zwXw1_(&moYGJ*j}*Kteicp=TkV`VK@=Ow$O7|+s0oPL)?FZwGjTmu9&c6uH0c3t@M z>T)zJCicioc{_)XpTbSox7=w>aL3^CRkB`b$YQxM{lYeaJ@>jVahtBu4=Ft*;85|VHQH5Oo zGWZqH{zjso&EK!8$c1evV|}~C*WBA%#iU2)6?_`U_4_R&{38OZgX@X!>*;*xy3_Lk zJGT7hKSe;kO)|{7)DIrMSY!*BJiLE&J-Q1JP4S)AE8lDf%#QNF=kgTQUs&KKv*tSi z42#Pz@w*KY4IEv81O7Op_K46Sf-2*!bL+*d%XRyXj0IO>>ay4eHp`^o zKL|DS@o24zhoYb+x*|CMM1oj)Z661y*UHyCLcsF6@(IJ>Pzd1RX%3b;6L3%$Q zyPL<|uZ{NJJLVEzgF%fs+RAj$o*7hB;@NC&X2?fT0qS=j>+pfttC-zn+%_qGKcXyI zkWwqZ97d5xMSVnXivdE^e+YT3O*fUIafy7S7%>WaPCbtM^s9%1pA^4$M9#0ck!x|^GKzQHIqH`(c_gZLHijrCm-p{(Cz9mjmB!e;{_G;XUmjPCA#^&6oS zX}>3ZhXd^WY{zWSEI-91rL$yM?a-^t`Q%~0t735G7w?}akBKZNPH@mvX5NY zl!uY3XkMF6-@fSlr&akOsti^R8x(?q;1f~raG5|r;-w`zQb#Y;j{aPLvPFocbOa$& z_y-u4R(kkcayCduVKB z?&kc70-G2z;n9!>95sf{)T-3`7_X(UG~?_tohLSTb44A^1E=6ETVUXq8}&Ru>Po8b z`-I`a=(*-Ka`+f!XJ*biNQ*8)7sR;}H*K5I_8f=kb}qSh-b-jWF1bx_v7u(3tncn~ zt65t+dr@uNgX|sL{7on*X&r89caUICRjzbpX8mmeU0OiW6$Q%6L3f&f696@($*i}S>enh-D|%n*n=ZcpqA;JJd%{@!=L($RS) zjh9XqkW*T}2^SU)7(;xx8>*`mbGR?Y9|0JJ^K&xP?SfGO*5;;9xEcy0FPuuAC zxD6X0x{lh$X2`?&N}xgn$$V+uNgWE8Yho1W{I$EW;fswV#oN7C-Dc$O0E}t~oe5Ae z(`j)-jijw=IWkljeeabL+4T(b)UUM~ii%dTpto^i-%pK!O8&sQv`tx4b( zA*DUVw^dHV%<_-L<}ft#fAK&WeQuUyhv~OsB`^MOJ!8q=v)!*cS^1%4 zEt)M1MPVII2{g@@*1wi-9S3y9jTNT1tQk1^GP)T&p6=%x1W>-hM~7m2@c8`ntO^oR z%!kGohcE|v`!LJphA!D0<9r^o5C;OdzT9#mwSA(zBE;lh2X281)X!MB8rD>KD^t?a zQZh1zUK#7_pJGA_x;{$qQfvX?4YEhwhZK?Vd$OozdW;gc`8H=Rjd#+24)1#->+Z#V zxiNn9=){_yo|i}=-RaA1$tYjo%5)IdHqDEQF;MxN)3FFibGu37p!q0h{TB~8uYW|2 zDHmhLz;*dh5Mnfu!F{frMR4#GfeQ!batJ;M^oOy&B$rwz7)C9ZF9*3yjAdEE8nz>{ z%>yDJVOw{Ln`PanYVFINypI!HJ^HDW>H%!dr*%ioDlTUX&Hq7`1Wr7RhEqW;3j7^s z*az&p(97sy$FMTyb0!N)BZr-WQa-&_@y?2DlZ}13^MltH7mxkMMkUPA%LHK^Ww)2mN{AU>kkD^JYpG#7ZV+v1A~3)L~pr8a~< zs{zFB4|FbfWo@?35IpkITjdYv>39X+Xz$ohJGu1ks!EM{iZalLUdUTp@2D*=?o^Dw ziwD-6^@fj4$|kAmcB0FNyt3~l{upSPc^r!k^fIu_lR!aW>=gazUr?*`yk(81jM&!n z#lIp34KmtrKJ|O)=iq-}%J*@~e?ZQ!TA1(vxRg&7bqE{Ba#s;lxJiO54`9kdKdwHd zP(BOl9Jf9^k2nD50iWcbD$4`UjW!&&#og#c4XG)@EJ=LNtx$q*jrR$_6x?jJRFA-Q zSu-$h*8~*1UK|IC1r>s0cSFxOvd-mZEg@diB*e$O%@5o=>l}xP`Y|qOUp3pz>9LhO zJicCW_NZ3$#HsTK@*CFC$N?|Jo3}4s3?PL3PCkLPzG;lcc+}Q1nw+EqZ&iSJi(XQ# zf=DyTC^pPaA(0!Pg@lP#KKFxLG(Fw4AFnl_g>s4Us0!u>vDdt|+4TQVOd?F#8aTVo@5nG%wz}ya)4cB<~U&I+sx+NFAtE)O#k37)ATQ z0IL7~IzfyV+;Cxy^o!iX(Xz70>&22-j--3fw(6M-eECuFcKyFHevbjdmbYe<&vkmL z3DS#CZC<@B5+&+%|KUXtrUc&>-K(!QQo2w7G*56NHLmoz<1b+l61=xI(gAC^7=4ck&$Vl93J0PWEl9ahmPnm++SsSpW z{A~8<-Qj$uF`df@)$-8{QqLsh9H1#c?JoY)%{8jJuFKQi4nzJB*@2n)=(z?i6ENUm zL8~e!cscb7LkHji{50KH2_gz~>vYNoo+&UK1{;?NIa#M`87!sjj@aSm#fk-fnoCs7YHkE&>;J&K{H6$Q$)?))8)(k6`K1R+X?H4r&SFS zMO#BViWD@mE17^SNFhWGWh;fXYQ1XP3G%Pj-<2cexR$Bu8e2Q>$VgbOP%uWi~CWFcgX70tLuXk2lRg z&mtJDB!UVH2d!1hL|kTy>MYws2Aon0N_Qf|{I-vaIVrltEuWoYAzL2gF1n3Az$&$o zA}P%|a1-~aJver^}kTt%za2Jg29WgY5{X6AUnt^F%`@hE7(=!P#d(?faK*NRo2lqMTbLr z&PJNK&+-EMQ(ykzQvhP-&E|>`n}0!b;BQeSM8PyiMOM#;wrcol%nE&Tio4QBwoUi;)Nje~#7 zvJAAl*EnUO%-i|l5=0SG2U5GP5SB{)4hVIY7#kw{NvomeL62Q{6q-LNlKgy0E7 zSoGpB9WGEBR;k0Z3VvJ~JNzSReK2}&rrLSBbXy_!PyOe}3QB|+{+wFQA@gj@65}H> zQIgVcXPQI5;>Bd~s~J&tuqmE`>V}4Y(XoSsx+(7_)49of?qxB=F%rIBG5*v~UIaf( z_h^{shWBHg{Yjxoue0sz$@rp8P`jEbItzEp>R=7{7e@fz!BeWxhT!4ZfY}y_4FQnY zGg5i8JE;hf+>WNx)T9m$m+FBlA2aa7I3br9+*LHf$nU>PQ>BSH61kuQh^_a{qG-H7 zzFcasBr^Vdf+F-;QAzq*%TSidSRWl86s91^w={-~aMT#G^)2mMIXq#aI3g#AFe=8ZItO7a0>Z zHrcBJty^r_=&fZizG@0@e6Q}dQ4`Tr%yQNHj03BwL9#jn4@}@CY!%+On0irOkahNK z{|C3Ii&wugJUkywOmai&O`lOPJng&xFJT9xmryZQ`QO9)N@yndYg+bGFF?rAT6~NC2-{a^w038n|)_P)O%Jc>zy> zOX2I3ptrrWPTM?P+CRyGtXdN|^LKSkr-P(Wq$;cSdkMi2$^ra$j2LGpx+W6Q?f@@B zRm?D5n70IBAZtOP2{a#5jIM+I3^%L^w)K>kJ>~=cxEtgE4H|vAs(DDFFof@~)pmf% zgh|42%SdsMfu$8mF8OAyj?5$6mUyi%?Au#K9GO+dl>a`b^<#4sRBS@kAXY-0++ZEt z_LcIed`Jp&?!>89J-xy8_2Tdr`m=jkA9JLjc-RBXK^X9Xs_~g3bD296npZ9F1X~gm z7GBg|+&GsIg*+rc-R*4jDZz-@iXY@GK$pw!xP)EzQ2lY{{o5w6e{jYBg3V<-&X6=BRN>5TZcLU&3VA_a!^WxdVb$gUYNW^>jyqDuTOn z#BFWJW;zuMPfmE>*YDIdTM=V30}o?}GyWUME*JdO$nQiXVHLZ(+`@ct|`D&2+imESljKSp$PL2`#>VFO|G;NA&}>FKzWH-nVb_SZc3s_o zL$6$^R&ZvJw&Gc}F^#{C(u~DPpQVsw)b4MStN?KR%p6Cin=!U7x8daiqvDWy-sZ{- z!52(9^nx;4Ydyl1La7RaTNSbMwxCcNYWoiseDL_Yr~4{Qf^_imb=I*yh)qsFrzL51 zi9IQ4ZaLWba)_S`KB*@pV`IarDdpdS_(4s|(}nXMEe&&YL#;FGPL_%lZ?`aUw?>Bx>&PuJG#Rgz1%6c*VqZGsEt{V1dbi#DKO1?m9j}SCz~*w%ktLRymQKDI zN(L!Q^gV)&U{n-K{#MJby5^kgbj8+C88h|2(_N1e*BY9K@*tnkr82fD!C^K$dI2Dz&Pql1s7c5_$fu52ZnU-Y<3r|aqOi3)zPdE@)$8+c2Hl~o_SRWrO2Jdy!`V%`^ z2LNOUNHUMF`z{*~1_HHN(syp|c4I=(usyN@mk?kze^Mp{)?&@r;Q!;82R#z@VfT9t zL~jL4pyX;mxPU;4p6U+stFWAFsQ-s?r0;N^GXHW`Dh;U4`pRj~ovdd2`y+r9m=8RH zAKN1Z$6!cEg87b-Qoy+h8{oj0Np2K3-axV#a|F$^FMJf-YEoiKPH%^P%UI|WdCF`1 z1_cPC2$eG@)8*rCT}ri%6Km9Out@%tgs421P}X>ej#|$dA}Pe$VVxJ(+JrengKCw= zm9#Ktrei78D%)a=2u^v82?QglMgcBe`5tT-iA-XwsgYW5q0j`PV#k4qTANqDZc9TQ zWTo#j^V+R7xZCmz7X&b65iFWrP1vH#Fe0VIMPtEmDA-hAQtqvO-V9_qouT@qj^C*Mlj~7D;$&nemyT5Pi?tlBkEhh_nX#$tw-zkm zS85W-ShpmhJP%aS9`+4=4G5-(FFp#Ujm*qUk$YyuI&KVykbS4(ki;N-(?e7#-iRl2eZgP%vbK|&y+8HX(LIWOTMzUXWjyY@`tS*=TpXs*)fC!=*DDGQ7rx484jkFb^6lB&672$!8xzy(ZB^!mlt# z50d>-mg%w3PdKO!yz)Yd-0)wR9?60}DoRR+U(7MTeLYYg@k8{ZzntN=yx*#%Y|1)e zd~kkosq}YO>Hq8mT0@tRiG!+-o>Eug39yH8shsER*TNj(eZUUw$gqdZuj+-;A0Naw z&yrB`ZGGzO{82{ms0L~%9|DfYR;n%j`EGqK6OKf)5J3Crrf3crLRfrJs6sY$IhOr| z4nH<2`A<$l>afYd3DK#_j!0u)_-YxSS3deLEv?hXLa*^jr3ZMukDx7m zBjuP<9V5ZPLmFH;ul9%#{0(d&LIG{%z9a!+O;MC2baU#O#N*fpq9a?tdmYfyqN4zW z@}twSQnh=Ra#j*3AheQwDctzhsQE?wb77$?3u*;KGCpU%>l>nygPy+LS9uXqE{ne* z3g{|&Gy0omk13m_rxawd7IU(Tq8=OE+uDjIydoWxK>g87k)p^HpjXUkX4^mVcsYfA z;CuCKa{IP{CtBO&nbUwWTpJ#QHfP!_yFbRdY@i@Bd!awj8yM0N0z}&#;CSbMVn3IzJVxNV9cb22*%#b8`*@0Q$*N@^4Bnw&?#g!?$Lhv5tS5+Mk z0B}xcVmw)Ev`{9^f9W~9E4EACb7<9$7&=Ft$M0%fglwLeaIU|e?;iyHLqv}PXj;SK zRv;gfHvOCq;YX%}Iuc2_6c9V?8H22h6fgmuCcn39`s+rd4$4a%mfvHV5<9QfAychC zY(fYzXigPc%z0IB^Ownz7nQHl^=I#^O=ePBwdJ$SzvbED?@j#~r@-q7=ZSKPS6^T2 z@{@_jSjPQNdAt1%fh^`Fm$Lw_^iebpJS*RG81BM*5gi}FSY+MT#~NR#HB zI{uM-3>pqu1P+8lh70CcVw=53;4wuiJNZ^yCohLRPn0oKyDbEO*~mo$zQ;Q>6|cU^ z_fa7GY=k|-U}Oxuk7^3Wn5}#ikik9B&t5IA{n_3wA$m;TJe2b%;)KluC0`_6C!7zx zF0a&iLDXM%L+H_G1D>yNl97lu&0Z+&Hf}rzMv+q_{33`DFiyU9QrYf24T2bzEm*%K zVYkUXTwnWv{S$NZ)9lVUc3`Ly?ZP|$p;&n9g3FZfobRH`zYuAi85;lK2mT$BK3SzH z>gkg2KWhB7Owz1=z%|gzhfzL!YJ5ZG*=*o=nc^t(-hqaO;@SvV4DCGvHej1bcUz^d z@F2x>K8lFfyS1|))kV)b=N6&Z1jp{>D#EXHb*Z)Hu|9+~>wNROXstGg~Pr@J)s-oog zx$mp}$ArZXL@^)O*Qp?3=muj?(ZQ`3k$$Dk_3q0#ZovZ12LR=rDLQn`E~@4s5b=)cxQin zG&L>Ch>{pE0ANH$?I0DpSrC)T26{Z}hN`upMAH&|&b4rBt+Hy&$=K+DaJwfVb7BwO zikPkaxpey>)Pfy6hVi|}@9pBbN_;{(BiaYFy(;;^tKNB^nRLNhPHr_rMM#X$*6_AN zr`f<6Ni-+}b+-5rd7(e!PJVKc^SAghZ9&a{`*Ub4f*B&ln?SRPXD-?uXI6EBwz3kcB}muVC}` z6RP+jar0{?gyCNgTU;`XS6T0h`*L`ho)W_*a9JdOeyR+!2wKpRYt^)Wv73`{4>n|b z-yJNKw!W9k%R3!6^>L}uysQzo$|mD$x6R6odj?VOFUsrvfEps9GeM-nhoD094PXy*9;%SO1*NZ)`zVhU6%%2E{^93iixXB5zalF)riKE zjQB0;C|N<&`b{C`w-No=s7P-@4DFk6L-UCsI(9E~CiA1~r9ewNLA}7TS@fSW&9i3@ z$9*s2ow^2yr z7%M`?Ku_eQL3Y|BQ4w|c7W8I17>8ha-J>fV&Tx4B-LMXV;O4Lnx3sF-lWT{6lwKJE z%lC*qXPDdCt2^KO%lb}U>^!i1=?lv7rd6{L@sUJyq!Ewg8cZb(!KcCR^(P0>r)tcIC^hyZ4s$`9vMwj*TM8`0l8lMt*I6?ijJif>anhwT%GH`E z%wPIH-MIhYoX4QYP8m{zcIE16LVX9=YT~NU5RpBP8(XnN*bV>AL`0_;geX0Of zn)DhqiD}=gr-)85uA1ImL|r=!O#(OkD+> zKwwlEbytI~RJ4?~5#FMZ5g6o{x6*Z73qX zY?MEuqV2D=(Yx#V_bsCr1JRtQ&%K=Up@lU5D>cPzFT$Okk6!)vpBGX#;AV?dU z+HRm%`Kms}_sN-RV|V(pr!9m)I+`>{+hGf1j)9nu`IV}o6J~1yAHG1GXzW4xX8##Y zcM~i_{XhjWWASZ!M|?5gW)6*JB_gi`?(At;j0;2kxRSp$L67{Ygt|XTtMTXG_GPuz zeF6Mis*loF`;2$Q_BD9$ewSArD!G9((n`mX6`-R z!3icnBgX4Pxrndbp`g9%PRfHHjxL09^RMfi7v$pHAne#H#xT6zUbn+fkE75}jQZWI zl+X_xpfZ~(ws@Jdxs72t>66f(ErHMbgNk+4`aULpR8=l{w;bn+7!J>(kfY~}S12c7 zitFPNQ^JB#GTdT(NJ_DehiMO=EmX z#r6X*HX-?aMyk4Z2QS_#>;th^#zQUBbShE|e5nDkpczzD2p@pOoMOe zXWHZT_@7&h zm$R0uG#f9_4QRb;w);sR`?$fan%2d_CKZ-eGejNS#?SGsoeti-X|bKGuvT!zq)xM% z3=@}@p6Km+iDk|41i9i6rJ$?ppFcXfv}w627>?rr`CH&ph9`5HuGvXTr^~|b@^W_X zFoMVeuUv3hDlbNyq(Y%vodlDmjQQ1pR5A3BI6U#@VF(%M;SyO1LbVSwlZbKIZ~WtzITlhNkCJO?dg6lgP3?eaR&(5Py1M zB$V={nrAq!{=G>(+H=@27MN9yNgUJR_fxI=GA8(OPYBC*Z8o-#E2pz9^AGT{9iI0W zWAs>o5J=5J^=N^iWO)s2IS3-S(yy1$+GT&8uq)a`?nPm}DAqq=jD8`>@&UtDg9L1H zhI|<;NL%fs`1LExR{{=a^S2?2(#=DSGz0S_b@yIJJ#hy|9aoqb%Q)to4+$$QWTp?7 z#bETC@~dF}Ptqmx+__RE^VsqiK~JAP&6Uq$r2HP|At7eF$LGIdRU$64Mb;VWU~li} zIej*bl3{#S_t~I#b(9R_%7C-N#2fk8XZJ15*VTDyXh27wz3}TO`#TG+yJ?$Z`>Tk4 zmE6YJl5O$caaR@}`nfSku3(F#gq7({3Dijc;w$sJ2*?AvD1+`pI~Ye{=)&0zWnaGv z)kvsc36Y$BbKX)OaCj*O!h@mV#M*^N!{R3L@lHvR!+6!5_mc#QB}TnjTod66FV(R% z3&~R2`tJ|tXt~dPDOKO1z^Hq@^e8(1!vlXGFWdR#BYeK!&$cw7S+t|l^9Vw={mYw| zjX0zWpZ#9?NN{Q+oa4FII%>fxO+)-O#a2$X=m(B`D8`7r*H$y{KFF?AF#X%7F7-Px zKrOmQ;2n)Z&mmp(HK)e{VZmw-6`)wh_lq5rFh`2m)35#H_uqXv)hp((cXny1bZ2U* zDgS`>PK_6}miUc6toRP$o!6z0OKHs?BC)Z)Ko8*t**YFSG!0c3#r8>IX@$Z>E)EYO zLZYJL`||!Vge71>&4dbM^c;RiOvkO% zq!_#i(SO?X1S&tw|C75Kn*NlD$7pzPyP<*`VPdMQD_d0Usjf2q5m(;XN`w(hKpq48 zrP&Y}AHEw{g7c4#{ify)Q81^eKhJ`$Mn_4rgeGKvIM1({F{6JB;@PqZ7~rTJ}dgXB(Io#M1LIVEkw{ z33q~GyKPD@=iY3}?q9#^Z4}?wpg~SfO13;Y&;&W*3-P>U&Xqc6#*XiddHB;sbyL$v z>u(urGRr*oNabco=4UL`Wks?0C)KxD+a?hSXkze0qztiFp1|YJVf+z$I8YGv1KXqS zW?3;21HzoE-Hb~_zeaA{*xPhF)50QBa{0Ska>!;)`RA}D*|iml2{fodVTl*&gJ*Q} z<|VB#$+FU_y7oRbfmXM)=P_ z_)R2>DMB`(ZYOddPJ~s*j*>=YQCNs_@`o=WDg1+e$pT%?D$x3U@;`%BtlyxU;PE~5 zu1d|KYOlw0&;mddI}dxZS;kXx*w7SMa(~*!>W0Jjk%k|I$7)D3?)Yo@A-@x$Emv1p zUzHh{wU?wC@w7R$sarbI<%)6NS@P^!zpB|?>@Gh%!X!S&fL>cf5JM07vwJCfOmS43 zJ3Whtn8i&Mc0AL#-m6a88&1^Q!Y1;V_a#t|AdCHl3?e%Ahgvnb;-hzm?pAD{d{%&C zS%7|$7|on&2QJTTTFd-No#xD|^u>-{X1B%hiv6)9J~fJN;?Ljhet!xD9lPsYSg(u* zXJ!J$QSNR1Lc?KMzDbgRg><>OZieH&p`WfgS3WFuWMI3x>nB&+ z>5fD$8XMbvM9YQ!&f+Ord_h8VcC&?f?(Q%d_VaVVH9}a}PVeL>K}{oHkRT=&zFWy7 z-T)Q{wNfG?=_TE5l$8gXpcC>~QfYqQG0-u9$nf6Qf>dBv0ZwCMBf<%Rd*9zKCRTQ$ zS9&%lc+!%XKfzU6L2?TB^!{;8J9`Vw^pa~-QfuypWr?TuHP_lGUtE7TzE7R;kOwu` z;L$kxB63s=i6Mz*fHfzcyGO^#TSOrjGmhj;lRMys0<>=M5qbacm{_3y!h>;wFXSv+ zpxBMkMKb8|gY_R8Doo((Pt*K^$HWd4a!?c7-P^{bvLA4VJ&QJrqC3Cf1mmLi2NCW-Dt~~q&7Lx@E@oFh~Bn!c(e`O+CxuN* zC6_g0!(c@IRMN?4de)(v2Xgn&UPylggSl7z1+rd={{A3% zY@Co($eO72md|(;%wBv3fjICIwA&ZxEK1~yP;zyJ1=vkgMsGg0A?yC5U9dziKx+{t zBjRQiV5qOZ#DzMBc6j)^jvhzE(Oa(KY70pODCM5fq4c{VsRMF8k~Qpq`t4q$#pRHa+&WA|u0m_zGCO%asKUD>JzmK+IP8$P& zvmgh)teWldQe=TUi~+|Lhe=i~zp90=F-%+=32m`GtTHTubU|9KGz>t!+k#n;6ga?g zV2w;|zBaRf<1Dt!F7lccn#&>kMk{S=G@7P|aGCHM_9 z=lMgdm49R!7=1?V8u=>5xV~-#-9K2yCAa80JMHc5FY*^VGGSu$RI0gn7*hTw~?-}l2*75ho# zLzf&Ym-In03_g$uC@Qgf(!1fC7FAU@J3 zWev^?0>K85B^f{QRa8RR+w}G|MzAc#EtN4&j(XwnvC8{alZo%{IKx@>IA zlW|5Y#Jvejp@s5c9=|a6(!dB_YOj=6UtKL9z2EHQ$94@6N~yEu(4h009zU^J;e@7H z#!E^{emjy0=xHG{?un1oY<3yyDd&z}@wKu-=eQ`r+cSJq{_M%4TBos04yz_4p$6Iq z5|9?pq%uTZd`o>S$651yvhViw=4jP5m6pzDJnfaxxaX@ZLM#5=jYJgtRkMG0TwYB% zDNSd}0Xn?DtT4rmySo>fv6~FDON?`ub{REN86&V3(EM+CF%nr;Q`3K;H0~Q|^_@n} z3`a8LzxV@CG&Urc?|zD+vIXmn9jpmAd4zD+9*Nxds z?gu2ck;9vrSxzH<68{k3ENYN=LXIJZHJ1&Yki0_MBSRg+HaTZoPW~TTUma9c*!8;) zEq!Qd4&5N#pa-PELxYs0(jZ+&kP_*VMk(p;R1i3{f`oKSmk3DR&HLW(&b>4DA7`9l zaNPTO*8bI6E12o&Jj?gr5++9a0%_~kfHz1-h45aYwF>%(kNz=jZJm^c20`L=KpZ-4 zX@{G+*4u@#Yy{4>H3}O38m3a`X3yMmkZfuF_Zc9gT4IG|d#d+@g?hid=07asy0^s` zXh_HBhR9=>7PP0BPqxcrfJ&1{9BsPOe*QGh5=jF4$sI4KcO@ut^3`bK{?*tV(?wK z*IXU7HY*KAFxFBe{ub!1rJuoRaN~H zSzTAIrZ%+a4P?o=y|`h`4fsU()Y-C|0Yt2oI4%%a(>nVP>fHQx+N$CS3pUbAYJjNw z1=J}g;$H-)1^|Fg{U#K)hvC{`gVNyTjj8Amb6@DCO_VDsmp-Hndrnp_dyj4P^hU&0 z9XkK6M9@tGALzY`J`SH!AG&4rvXC?>gXoy!pFby>2+%yC z@i(%JYJi0I#nGP2SLp~nQ@SfvQEn~35ijJ2DT4E~uIu9oO+DqhoIa>p!|Uxpi@ERRs|H`F3wo z$}5LW&HP4-yj~Lrh9KWrg4_IR-|!+C^~8|m1qWEwk2xDof_6EPzmc4|JQ#R@1Z|58 z?dhQ)&PLq#Ec>~d2*t^m_3Do2VnHEWtPuf_6icd(<_?@k6%{7!>wZ>^BbDO>|exZcPL@&CFJg-iyQP=%G+iQ1lPP?jvXx<}wjXy5}?_JYd&bSkeh>4g*I4 z6nL)ZH8wi5ejU{fpibWPT72TpP+@c4={RM6RWJT0Xp*ssIuTgxF{WTkOSaCq@|R56 z&5=pP|8+vT7al!;4}dE=6C}rgKsJtcWB_P_Lbc1Zf5g}`9S>U@Ev=R^GTMp;@DIc* z#a`Bx$Z=LntA_LRAB5ogVYizANN6<>?Q17#% zOK7%JfAPD#FXu^XUq@?OK7D_mR-}{S=(*EjvL@& zl1gj2SV#E2wKe?5=%|ee_M1txS2PKWFeB&CdAE<>FEdG6NE&Pt58r+ER=J6_K}pHT z$Q)*zq`VOVk?QQpMuN*$^aK&Q0B*Mf%OPdd{4{0QQ6t@5C5|&j{4>?N<_6Edb(vQZ z5wN+~T+R>))$PMSw{(9&$|3Lapx5}npn?modpov>z$qCw9#qs$;C?Od0>*H47} z{-i#3wfT<0xdE;^ws5*Ou*PNly52g`rC6F4!uX-t8kKbidJVtLD{0D@HoTw#c-t+{z z|7JrxDmVH_8bNrkCVw3TDTI)%`VEDRO#(Z-IcCb;g6n6qz@B>FuZ=0!v>fz-*MKQY zCCXOK<$3F$(zu>cTPlZXVz;cnI*RKgB>9kA)!jsHaQd(jiPxi^-)gZ*%@4cN0=nGd zKy@2?T4Np?++XhFIAeqrI)9W93xbDSRn|iyskoY#FiUKz_w}|iNI`kmAn*qe4!h;v zwCtU_oM#$9XzOdHrkCPZjGQ9K+sZ@s+<$vcBn_!y-1VE^k=Lde*#Cmoge=!hyTT)P zy$;k?1&|T2rh#E>0dx3^!oqyB=k=dIO0CCdj)3wJnk#en0{lp=aD6Z}b(TgN(k~#Y z4lXAi^+QskUqtpo!Rl30twSDiyjI*Y>$v^^T;4YIY_oU1$Za7uT7_0&Rf@*Ed7L;( zNyETif}EF{ECU9H1WB|9 zY{_ZarwAgkyD6b@|CipGH1&z#PV5~Imay_eqHaeEPCl}PU6tqHD9q;1Gh&RMo+&{H zvm7i==AO}7LFuY(Ibbvwyx$+&+HM3ue?)p;yrv26nu*vcdU4mZ&0YnWCkK*OU%1Np+zlw>ClUjJAB z@0veVx+@cR!CL1N+ z%g-iwD=nW&xjLny*p;tK${qi>g~h-8JkUiEzzzzZWt86sMO&;2U%|T3;RVJHpOp*9 zDrwGD+6e~QVE^by4vi@mA(K)dxY~R^VLM1}R0a~}q_);SEKp^Utw9Q{(3OIUohLhh63L} z(F(BBx}Y2Zya|B3)dhIiTWW#Fa4r=|`GZH`>gM$-D@@J}SS=;Kv>yO%U5hBB5Y&FLYw_%2!d4f`-W&BXbt;395u-OgQT;;#UsWbMBuLM0*zu-sY zz#XnszqbCfgj-IPY|UM@o>3AuaBs>`qtqy7NHpZye8v~+ol}1tlUp$keQ7yu;ES4E z6TyFoBPaPFRx|?n4gTjQ-@$D%PWTTx<#tHG9VidR&tn+moG68U&T+gRN*%cz@GPcP zCcMhVF@L?Vy~JCzBKSllC~z~Z(KJ_AH5+??m6cV7Qc`p8A0wEER?GPHb=01!%wgc4 ztTRT@{Em1}FvjwW!de|V_8=3s&~VVS3z~UAkk23gj}t1<(kdy~SDr00Ww-rf^i^RV z@bKhGX7sGxuT%}v)6J67j8;~o6LUo6$xYu-HiE&E?8#_Y~q+IsBF{O=dHsw=WU zrw!^E_#O`{gKi{DI8$yUkV%m1xb?6S-H98H>X;Oey!0qOiijMryzj52=+1;S$4$mv z+lwAdm;L_x4_yjk8r`sPriZ5y0|_RB zUsY6W0VwOb1oV4D&eJjOk!+k(KR@(~BA{tJ-TUt+<%9gxWaIV0*PwU8*yJ7uETWI& z=NY{nG2Xi6;Y0~<5zUh)&)2&SldYt)@W^jsDR_C+@OG9~YQBGf#>eCv!1YsDZcjQC z-0SR{&&B2UcZ%9S(t@Nr8^B=3TF%Osem|;t>BoSpdCn-an}Mf%b{> z%u#%)v5b3RHyxjL=yAo=FFjnslIKuy7E^!_SyA&V%I_uiGWs9jqQD27w!R-#EkW}A zcJ-T;PUUV&QjhHSjEJTN5gF=1N_)rJdrOmtci%{T)vhx)i`*+`L}=DMs}#Q*1&;NW zpLr=70kS>#pbG2*+HYLw84l7n$Kdm~5cDz6HK7J2o)lI67U$J~#Nx}K6Fkc;E=?U( z$F@};s0iIMA3%ZjNGf}DvCeTeD^B!Jovz+bgCzaBDR*P8pBI@y_|lB-NnKKAXxbU5 zR0oJI+i3xh< zjq7W+pK1ZYlNIt$8qL4DuuN+NWUhz8J{ymvH9Vq%J|9Hi4& zk8VGjHX|CHX9Vx6d7*gO^=qzb5C060!=mL=@&R))B1uI?1|HJuEJp{?PYsMkgF|S;=O<_N zD%UsyS_NNO1z;%4yNBVlLNJg4$wQyyV5|e=(#lO!lEm8`gbXiv6~--HXM4LcVkMh?dW>GL(wgx2(0OtLxn*)5EJ?M)i9`ugk#lp)&`4f zUh_id*L+do)phuadU`^Q6UKLV@qSA#SzrjFde-Zj3OZkw{HvwGb4u>(=){tv)ZZu2 z78ML{|Ha=7l!Jog`L5va-~*D5TYBg(YY)W;22oYvHAVk9ft=fy+)=T-O|%_VoTY;A z6oPh67JyFXule97ZX3*lrz+m4yua@x2@ID(R-i2RFZ3QNFB=16DJd&=6uhclf6TKdghluS%n4A*B5X0m}7|s~RQAng@9r)blz#Bvc8pBZv-CMwgjm^bq=~#9j z7U^`1`O{UhinDXbu6b&T$$ZPD#zqdsoPL@C?t2*DbfYfW|0SeILEC6{6F6r3{!X zWyO<`VGX8Jr6D!-?(?ZQQLcI_rgU#hJ!Rmf%N*f)+Uu?&4sJ{q2gk*^ zfs0O+s@$9Cp#eA+Je)4*0h|WIjApl&WQzQqq3ZGGJySw1A)(9R{vni(->3C;HC zd z<}E-R{@pm6^^+Ewb$O&T&hJ@J4_qw@(gfA`2Nrfq3>|A09I5Y>ik#?0mJxHq~Xf8^68?D&i4cGu&iatif z$-}$_ryd<)ctG&sRSJ8$241Mjf21&@A-7+W$T=&Ug!P>;d}Pp*w|EmTJB7&NwAMv; zpT-X6mPr#MBL^7jr%u(>{OKQ*5jWSKv_IR}*e(OI*kI?}VM4|7cP7WfFmAtE})>g&S`!PPe<|C-yp+z-;hDnK~-L^x~>yIw8UXHbq^J`ZXg|>lq z$Bzo}0`2FUJ@%DZCh(WhmbBPP&-RvC!pas$Y0;`u=^Z-gg55UQGXug zvfBHvc4J5{E4dDm9C?Im@h$7I#Ic#4h?UeM@ps8RKme({K0Ig2cntdwX~|?3(bv%# z2KUTmHGXsf4GgWsDcyUC)^h6+7Q?iLI?R_2Fu{;JYoFR3!OPduGtgQR#hLVgIU3b` zx@N3H1@x>_%H~ zLk2Ech#=!=13ZNnrBgmeoBYjp^5p~?iH$|XB5#+4QpIzsYA@Ctp_3Z z9#~)?6=9kDB%{K-q`+xVc)L6GWJd#E7sv);B%&?~vSFct2qnZ*f zj6j`%Z;vN%#I5KM?K3A31jwT|n{du+mv`lh83ZLNM&QIw8UIqeC}zxa%4nTddoJb% zAgNHHkPBGW+yrwIziGxFvGXJYgZ@quj1$QcqI*%Q^U|$ntMxcrMOtfh@rw^h@rQLY z@050|0g@$#K&0=2Ks=jCtjA|LR`xlI^ z#3+r;s+KJKAd_nPS_TE8DW>||UMxYZ>AMk7iHXz1E+Q$Nk04_D1|E1+2N+J92K556 zCeVHlLo96k$D={;g^ie9YpV9?6GY@YsQq(#Ys2zAGjf z{WGHxVIErib#BXCWL`+JJCgahCCP6(YyoM^^wpV!8`#FGhIvcquhTPoKb-YXUw6Ud z_~wk{R!-@P0kzmqO&G9qs@BYP+#fq^HJ|E*CwNK9wYgef+&kzoW6x=21L^xHkXd$Y z6*OUkix}ok1qlbytoJbeIM+II98fH%|-_^ZG6TTPRR8>`r z5_hDLP(&r@Ee5~*n?&`EBRFWUzcaV|fs~rL5Lga|Jgp{_TV~FaOv19i&AbL{29boO zBL(BrtJ{;PC!PtWL0>G}A_K-wsTn|X+T3+vCZ}MYKsmhEt&CRwFgx!xr+lWaMgmSH z7qWzXwY5g3UO(d9vH;lc@f$dbj2y;p&EZXc9C-^0!hvv~G90dY+uD?Ec-_7mKrV3v={uD?{u- zos_>vQ%Cy#g4~ECUU7v5s_YS1upqwyx5}$=z^!!=9=iOKUN=d4tZJbs7s8D$(B)rJ zd11|!BK_&SHX}ob+uv`{v~W58GxBqjBTL)wDfgUFY*+@HoDR7Es*hQ?^v=3;{lFo+Vw7~Tz>!7jXst>auAGOx^L(%&g|c3+z! z_>9i%W<1yY<9*z2XdLg=ZgtjUHQ?}7#i+eQ=GhOn!#I6Le7I4eDN0^FQF56 z#Y1kkcZPGXMw}F@D{z`vZA<`@O_x;G8s)c#Xgf}TOX5kcqkh*<$_;DukEO46QjODS z(T+Ie3DLCCmgA7~TDA;4>xhf%tNC1X5K$0C{>hsC<+l(4LQafI8o9(?beQzOAu|2I_*=;p#CCWk4sMCy{6Up_hXG<^1cgQ!fF2;E1u#6s0!H!Jnl zho@&o*EY~*scyel1=hZJZqg(W=7~PmxW>;NW|Na9ExgAQ)(8GAIxM^s9>;~Q{q-5OsN7zN$R6ARWvD>-!LccAU=zZ=oStO~~ z2=<%BEd|u4pEx;vd^TTNy4vOtlg6v&GX3MxRW@jSuppa)KmU73G-TZ~o95 z20JNG9z%8Dz51^s(2CYQ4a~z{m~LLJXy|}*PdJ=h4zCGOPaw4LPfBm2A->vgi~V!X z(>}UB=4yJGf31|*4>XswNr`)NsC$cIrGiF>zC+-H5-seb@W_C<`u5;dr|Cn+uf_1{ z&*k_L1&AkAt{?A1+3H0FsmTE!L)P)gween00+I2I$aDC(`X{5erydhivyMjkTsY$x zM;1`}y(bd1pP!m)@ZspvnkQq~0#2;&z1YUa**ik1FQ4r9~1-#y?yJ8;XW>REsOU3ZmYrI**OG5VQ=f-{VuJ3;>ZQf0haI5zlN=* zsEUB=fRCr^`(#qdFWYweG|$_M{Ch_A@OijBAV`~yjr>ZJGxv&(OxW`iUmBjW(p9OK z3&##5b7+rx&YvzkkL-_n?%g*H+~6?A`I-%F3xwhIah+`R;+-&*p(vG{xxYH4d~Iww z^bEr5j)RNW7GSt8_^?OaMJ{W|FAm-``AwY4-nz{6)8A(m5#ngeE5=s*;CTLEGRqF- zzvtf&&*Ou0X+WpvhQ)vGh<(hS;m|jbNd*(~?@tc~d){Mbef?y5fvd7nLCF zDz1{REMk2c(k=tq!M<8!wb?2TVucs|_vz@H<7QB>h)m$`4eo3(B0G92itIR8Va}JPLt7XxUMtgfJ2_!I6%lA@hhjLNM$e zoY~1A%y<^9%9*<^{KF6Y=1wPl3A9GN(;H$yru5$u^l-SVZu2MYjC`!_tQZG*t}96! z=9}ABGe)=x2zNnLF&%qj6ZrpG^7pNbrvy`BRyrRpMHQVj()`HAUsn7)=v0 zWH#>&JZN^?GNXyI9>qSwbDzPCC3V?3;LCKt@~8fm*Wh`%L~Cc5HuZ`D2rYg6<+lk- zBgvcDEyTL)M5ANI2QiJ#uyy=|4bpJ;IYcMqoDi5p_hIPf1GoTWa>=(^lm$psoQ4J$ zIlp!Q>DyN^b&zq-HL0X#l5PmhT@IxdAa`j?^|1fmKCIWJD@m42?qxydL)v-1KCRb} zBW%EI12Y-;%P}mqfEKv#u^24S_ylBbm?M?QtSHf*t)8=p2d(&fg_!AC-)&motN}B>?<}FEW5C6{uA{ zW!d@{xdDlo^;3Wdny_f)a2ozsH;t3E(bTOyGK0{S5_%n-+>)-2T0r+qoCeDX8!MQJ z(4>y+rvahB2kawp`9r&+qPN_4E`ml5vxOBSJlb?PVBd>8JmiI=QLcDRXL;F>D&(T& zklijUyMIQSlubCfcp}>XEsJoOFk!Y^lfQ{6(*-SS^GZXk!Vk2751-*OPs!@l zDS!ta?BxTFQf-c(^73lj(SDeu>G!k}DQL&Yo2^~X`*QVfn##WA+~^-(OQLxRx^={S zjlF4FZdUAk4R$BQf{D>3(!#P5zxG(>&(QGzCu^lt00T(`M4Gl61E@esGlo1>ReRbo zW3Drf{SGvMiq4Lr6@4lrrSm+!UIn===dFWED5S^i0ovu4(k<5ih7Yf(kzrt$V~FxVD0-ME44yDkkf@lqUCQ(VR2o&SgQv9rLAURpIqV^v&y~cuUaG zmFbU?VS+hT#RdXOk2lcPnLE$7 z7eE6xXZb?{;(O<;*9X8LL+V?QmB{Mpo&8j>;3Zp8w%E8F7mhM7LZH;15ZF;MJrp}A zho!Pi7V<{e!=Gm#}HfScgd&qtA<25A{Sr{Op5A1MIS8_uJ? zWR=&l#p1}rXba5l%!bsK{|H*M_*NSC*U|`)D1kY?h5m>n-XwX@Y~hz=F)Ju{9Q%T> zh9`<*Nh^oUE4A}KEpK^L(-!2g{wO)+TB(#*lCpa|eMQl4=_~{WcoTac!rXhb{e`5x z9OpTCH>@ja+7Pu`&bT}PBcNUPui~=n&FHz&uFJc7uLvg8g-HytPCOLr)Hcoj@L7b9 z-4`Dz=TOl}-uK|@UP)j20O4tUFlJ{I?(vGwX9m8ql;RmmbF zr5dYbXC>BStg4pcoRse$3hji4;nDbg)zpuqWp^V+VZ+yJ1_!^h^E{u? zoeb9cpIB;3SovpGR`btD#1NzYh(A@D*eP^`uQdr%HrK$ig`Nx0JN&0~kDg0wh z15Zza!u|bhfAsmJUV2kga6yY@3xYuUNiAn)3S}Cf)1RJEV;&HzVxW`&Igv0t3FA{4 zC_atY!MuGinb&&m+#IC5?ss`*<;OqyajCH*wkganT3E6I9)_al>0`7-R^!G$Lo#OS zxn{-ID+kOLhvEN$&wveHg~`Pt*N% zMF^Y%!yfnH3oa}RP2vG^(BEgqsjW-nNk3fz^Qo!#IgHp?8~=n?{J;`hjS%t?@(oUB zo9ft0q6hr#1uOSmjcjM>Fli$UE9FJ>-xD$}%R`XlA954MGIg6Qr7yOwOm$4XKd6){>jryaw|NWR@%iVe-=bASuR6VVjVl*1yIrQ+$34gB)x72(SeOHH z=QrOC&ALm19c$VoveextyRhrQ&|f7G-L)M&zncY z{!JhE9{q40ZmKygjc4E~&Nv!P(a^Lhdz~SC=;ht<8S&spGzhuXE2~@C54fzSTesj6 zqHUt29ewETQX`#|82DYgIXyJWn!szImo<88uK%t|TyxbPloxBkxAz(hS1*Bko4{Jx zO~ywR$SGzsD~Z-^2Aj$5wNHds_Oimt9Bl~EWY(u1zh%8^`*?odBRS^rH|_ov#K}3+ zRC=~pKcXT45^%sS5STmx`X2)0p;K_i-d%2oP+VLY*WKNg$ANn4!OMx!4zZVG z$pGIwMNA`WPFytDW21uXbcFnIG}c)$PhomJk1>UjB1cFqhZ-b>j+3!g|9ykTLH78M zg2I{M>n zaGY=m5+|Hnk@XWcdO4tt<|>d!j#_CE>;Y_mR$7aPZS9boCf4QtYaE0;+N>i+c8Pb( zp$UO9Hk^dv*&U|f?J3+_6$hDjG4b*3wN*Hk1jfDcJ+&n4z|2z&JB*6L`0u|i zBua8?%Ga%67m^PM?$j)cA7@-;+PWkB{Z*c5nZDrV4x(rDP+b2~Tl?;iyiLVS*;QC{ z)S5TWapB<&z~mGSlPopw#wwq@B;ped!Cm5$}Em+h9j) zma?y4VxmdXoW2>ZT|HxQt}9>nnRb$)`H;IU*;l*THDJq%wp@#}4tIp~t8@@955Idi zRFszDnT5%y@S@0u6^s*4yS-xg28@{!a?-lD85zTnr*_CrXL2fLI zPVw=Q3K$X|VVYK$+v-1z(2vkb&N@Ga1pCnML=cXM{?%8Av0)trtYy0+@I#jlT`H?v zJ`F{yWbuUP0m0cLO3pJGA@)P6*XYYMl_pZW495O6OEnpX(Vv-rY)gz`Rj}teI)eSC zj6f*lmNLE=Zfz;N(^2g>dxl=i==l^i5|M+st~p)N1`Yuadt6!f`%XD9a&0(J&|W4p zzg5yG2i5%u-^qbs=#CyC`alit2v7t(Y2z!3`hhhL(I)D?nJiF{QvGr!y&)_ja$P0f z8SUS9xcVTX7r$R+fuufX|4VJ6CvtDAwWKFxibU9|uh%L8XOv_`_#*0bEOOxbwci4R z*kU;^VG(tzO&5mi9WVZHVsoD1QqLU3Ny_(qM4_m7q{Kg~Aa{5wB5{wz| zwlJBXu=CsL8twCD1aQE=8pxTQbM8EzfyLGNvaG!eXP)?fvH(RQ2kON-LLpNVOM)Z4 zM9SC0o;r$l&eldznTi6O*_w2;^{MfTtWAWMbjTavcEHnNH)Kjz`Gc)H3FjTlhE%MM zxB2vyyT@G$OPqJsucwkOao<+4Ilf`CNCH`jYE<0H0ij-NDu9$t-FNiRoU6BQ0h$lE zT4pw7u6?-j~mH-Kh{n@7Lg1}ri?%N^MX4*c_M!Eb1JXU95aYh}H%=m>T})BUtz zRC<2$^-VCkUzBg*{j(ogEiM>_T~hW8WKHX+Pt>lS$2Tvy?c@wLjnaJ)UgJ-=M!OCS zhOWGp56)Rv0SI3&O!`CA@VD(QPSM*rKM{CUxGp6VPHUtvspB(@>*||H))y44y@F;D zS$y2`rF0yfX()ZQDO*r(Z~xh8zSAy?f=+Cd3Juubw;>0Jqg+2v&Ek{{0;$oz&-w^i zlz?{tR#Ge6AsOveJVG0mDC8Aysmh5SuK?q~B*qlSAV%x_f`R-9Fe8&897|YVsFzC# zH0Wpc;{pF3CiEmQXKeo6n3v_J zcz=2wkG)Jsm=pUUHqc)p!)nia1DVqd;P^WCyy*(A?5w47^{im>X@2^@=TA>%nJF?w z3^}VP;t$a;qIQddqR{@)98ifsCmdyCtZBQ005kH0*?c9v>+NjsSCWmJ_&~KGojVGtXMMMFwk)r_e4scZ)=~la1fMr}0)$eEKxKf{X{M&iIYujpC#8iM#Xg zxEDf5zhD_A39E3;P^_`-#rKuf)%(5su$dlD9djJxp}!Ux$GP#>jk`sEag5bgwDTSP ze?oyG$cYB@(h$A?8K|1ro=kL-NOIDmAzN3C3j49bNufsjku2vRE~+i)Yk@SO1!YBD1VQqr zrpLX)YdR{6S|?!rx@yZi`(vl>(f7T;ue_q-HC!uu4mXL~#)y{>W!zuV!DTPP6sVxl z+h}sDg<${$a8Nr*HC3&tehabr#(>eBv|A(_7qezQGD?X7yuQPC0lgtQ?3tDUhh1&y?*os#r}c$ww?;d3-%bO2hek6Qs? zMOla=Jz<6%uyc6+PRScbT!!coX>HCx8USzEhK7=71HXd-QayiaT~&!i5vD#$Yqy4l zOAT%bdwIj!M}hD$)L*4MYGYCIqi4PITxfl8`GX8GqpQdI_a}81)3HQ_L~*&JkA|3p zu<%GPH!ui?VT*{2I1@Z}a1n4@{R4vfJup6>NP-{`#n*!+J}mcSc}4XIjsbbY0c`vUbqRfPaK;T{f3p_rV7#UL;Xl@=nq)j63-lSjN-2*XXkqp+-Jo}&%!jsN&G?{S}xEGzCV1aDTW#^Hi1`@Q~VD! zD81r$b}klk`hy93E5Dfvj$Bx&9uMS%48aV+OAx+gV!+;cN}mJ3J)i0AwvyL5wq24y zCxL)(0-v^CEx3Kc;eXS`d6AUB9I@0{_-TA_P%Wa;@cWO(js*#;8lzCu@OPFtOjk0b zDsY(4Yz~)OZDn5KNQQ;wp?Lot>Cj?O~9Z>sU@54P7Ni3Uk0%>5;8l6SbTU^O$PZY>z!AHW9Exf$&$m)R2l z!+R75*0#GN=}98G3@7|j!`a%z2#7nSe1(XX!>f!_cVbDu%;PmuW=Y9)X=o0S1rG`v zA~Ows+n1(1z;xInsM%_(L&fUeTLSQ~lam}uYVP>sEv)?v}>>}w6rGN6DGyGBpXuYq4;77zQQd92NdO6|8#PNNzbBIdM-weXROAAFiNjhRb3yR53r;~+QF@Zz^?|ecGAMU75K!hk_S7jsoWnwd9q+~ z`K+B&!YU>g5b&ecaXd61QZ*wS@HQ1Yd+7KDho?y?_rZWJ(m~Ou&hn`79^9X@cxP6Z z_nNPG_g}vnH}=cCKTD4KIjql0J=dc(H0tDVjVI(m<90fFxvik4Ywh`Y(74|}?$neE z9;A2lK6R#=uAhi{>F(W!&0&7@f6gQi5xJY<9_IMQI`Qkc$>uw#_k+TubpXr6wk|nf zsQ-cu=>Rc1Z0$)3>y&$ufr?gX3GY>jP5Ngw?)Ug{V_ZqN(;W6o-r~d^dteRn5_|k{ zDM`Ya+g}P22KyLq<@P6oU60ZegL%z^oFv8fFZ<(08m@`EH^9g=;$M4TpD#h@gL)n; zZO+GWz0n}%hYM028|$x10+f~$usJZ7WuV_jidQ%;QVugwtRFcv97%MtRkfAAvXHa~o0-RGuw^t0QjXNmj!w8bsyrHZY9T1EtobDAQ zeC1HkISLHKBI`GUT-ZF7-a5dm^*I}(Bhd%^fkkHKL&;H3Zog9((oKWE;Pt(h$kXy# zXaxy|4WKZj+K#y#IIW>UV66UerGM_MOZk@x)egk{&F<7f7Nw<(MFxsL{)8mp2^}yF zbQOpQg@0uDqjBl`*ie`92c_ZEdlYQPh6DxJUfu z`gLn#=GEiJxDn6!Zq`_)-hr!YIAyc#34?@r0S8CkE5VKCR3T3oz#A(3;< zYK=H$6SJ1LG>Q^aJc3JRxANHYb|~7nj{DF2tyh4B8mrK6i8sVyRIpoL-4`z6x!5s^ z6}QUFUvxJ`%x8^jQmdsz0Ozjd0NBU;OQ`CT`+Xf}S!A)n5e*n~0Qey@4Dk$ThImKr z0JHL?4O>!+rNew3s1M~%HJfvq{T6S~a_*=Euh2=S%Bmq$0!>~5NeST_r;d9Y7Dc0z z9}tRfrZN}}jJjvF7AlnCauTWw2}buev;;Rs`&dnE`wmGRHtqKY!J4VN$$&dX-xiR& z;=>&iEHhAL$}MmVpm1c7JLL7zS?D!v7a)FwW;CRd%B)_Y0 z@bVjskbc3GA6rN-Elb+Si4a^S_!aC<1@;J& z8pJmz_7c&tduntxICD$nH`^(jt%x@mV1ftK0(|8vswik|Qbve1wvRBf z3XMu}RSxC@hs$JX68Tq75LbO#tz9WBq-#s6m14|=Ts(yCX}mzYzW9Pm;u3Y_gt#pf zSvqO|>wP*=(yWB(8CX%8(GoYc=%S)Yyi$k^P{CkN+9lwZfj&3*9c4huuE}p&$MyrR zN~A$HqcBKPaoq4)vVo;)^)VJkD)}9CcfSXnyjb(tVQ0kLb1}~#ft|;=4}*#LBq`&Q zl=;RbuR?1dF;6V=odHtOS2ED~51Pfc7+j*(We?>#i_~|E+{dEv!Q0N75Q#rwOjGcT z=@S7IpVGvJCPt;h$-@Pq05;e9!gZ z;x|x7z^eWMvL@^bn>>pQp;)$`R^M#xGqfE2H@D#HFQE^A*aUS6?!!Lgxp&G11!x$V zn4HeB^r2^C>90bbKd>N1*-ck)OLWMsGoO+C1PBR@;lWnx14){EymV4$Nm^!|?jsp# zO9aMR1AeJr#uA87q>VC8@l&BeT7%zn&631EraYJGbK9&MDQk|8cPaq6_#5M0J9t4s znMx5(J;I(!cwmnAvywNHRIFTt473!GFZRZMRhNV3OqoSTj>L zeZmalNp^hvVt1{rI+iYNcL|tg&MIqgQ`4E)o6_iK8wFh1q!e`HLXl`5l8bL3bIDgEhbhQ|BzSb#W4W3ns*WWHhkgBb>pi z#o%Z*_TIE%GYTe65%lIo>bj2cE++C;Z5nnIueGhN$?97$b>S;1vudyc@gHA7l`LZE5S9dJOX7Aq@^kSl;#VYVx~}>=Ljj|9z9hF8@khlGNPKHFhQRj)l2U8`fLgyakZmf*{bGIC_NIifT3tMpYydhqGn> z#lnd45W}Ym-H@09>x?dy+#BKOP>3NBSrp z6n+}G_&$LR-U+)!rA-HJ6Q3GoE?l9scmsk*F)?yXm(4g7Xgd$4I}U>9J{Jl#%Av$; z*66`$YXf$WaWS`KZwMdSQLOoL^TFBT`=kupRYmz%6Wgtr9iF33A3^P{vf;KRG6QWu zb^#xXWGAm>s(O2B}T8{mL_SWMHvc#N9_;#%dg9&j)NTtJ0$_6deR zgese+))?au@F7@+L0z8dBwf*0chAlH{_kSN0J+u^{O0qHZZ1}iImBzaLW&FgVyg3a zeIE^nUS>`S_zRd314M1kJDHbLQ&BeG?ii3mqI?G<4-GojS{=H8;V}%BDq;!D?m&g; z-@(MpPje!*WPJ(Kho34$Ke$ZB2GsBUgD);zec-IiZd-gxpsQxas4}ya+x!=Zgj2rf z^V5<3uiC}y7A&6q~)hcyHFWimz z#8jh~t88qWIUQeAAkoNF`GS?!r9ocEdmM^K?Kl{aC@D$YEye-0$LV~?~WGkdOTl>^O z#6TX~3nN|^NUg~<5)wrSZTaH4YrAQZ@RH4$)z|hs9FGPR?GO96|Fl=Q8;y7pwi{rW z?`6e93|Cr91ZS$_LmKJi;0~VnQC5&yba_1FQ)NEDIG1m9b?6!z%^E9!KTlv$G3EYB z()2toPb7?tpuYTJ9VDe2%QORmtbn<xt1kavT)kyLRAJXHJal(A3?@!p?aJ6swD%Ze1xuJcFpcWB55)5JnWDGBUULEArUp~jTh5r-;hiM;Fk~I~5=8zbD0X}v> zWXu5cQEvvgG0w&+KGs*^Pb+k+iGacdkkRq`LX14P*T;>C4Bl4osyb@*K2u3q*;Zh- zYXg_*Hc(zU273F$`A;-6%Ft;@fgu*0)!L5_Kbk9g(5czMo7jDU$zSyv7h7P4_T|0N zjnbA=kn?`Got&9rlhLOV)WCVs1YvAr(P_O^v4M17+o5&O81JIs{FU~I%~i9_&afh$ z5tTMW459_G((f(h5Ix%ineF@dw?Dks;62XaGG8$(QUBoAKpPQ+ynfERlz;FvbdeW3 zYH)k_&0qwKpMX-O%_H1r`z;4Bir46OZ=7_mPVi88A}7+BRPeu z!Y!)0n0XYv!gMbPBS%4cxsqP~FGmL&6Pc=2&+ZXdqxg;L-oBwDH1jVnlfnGPvb z7qS@blq(fm25?D~JKOGwzhyBDnq7-osVY<6{J?!h#MIQBt7zgU{L!Q0CY>a>?yPZ( zn9ow^6**{u!O-C9|3l3q=~AYuNvyl9>}2Mt0*#5+?r~e8%A7;uRMLeC^axW=x`kihR}RypPC+UhD~*6Z?e0Az4UJO8b8a-9 z0k_<#?&m6ra=sQT^+(wN5C(ySVwv8}g^L)kML9-8TfpS?TRD#mUf?HLAp>8vjR8Qn z!sEjkv++jP`$Ax1K=(D9_|xqp)<2yCs57KebRi950ud&;kYTqiI9KZ#>x_=cmmOx| z?SC_B2a5=0SNk(+U(>)vCDN8)v9QJZVI{=C49LC6+xoMW!6(GpOh7>HHAl@k1m9-D zuLuF?3hwP;+KWB5c~Bn*Vo%p_Nn6>_L6to+l}7r1V-ceRl9}gl85&XdTRY)^Qsmb- z_EWY^x8SsP2YZ8nCb~(&zA8OZ#*miT!w;SYvhDP@2yGJZ1Z2X*F=s(;?bjG~^;j%1=?~LEQJ8sW&564FXU(}A1)a?qn~=!ByYMtX zu#%H@fRYALC5(_T-$m@_2OLb>?L<&b+G0VAuHXI_4owjy8pzKJPPV@tY!7NVwho0L z=CXi^5|_hwO@q4ITVfXfog18*!~tMdpu3`Dj<8|6g^6GF+G8DXi|PR7xp#;G4XO9^ zq&{S~0jy2@jTIkpVd1<>G@b+0%J}z7hq)4uSjuh}t!y0eyq*jv&04&nA9l6`X)`jb zhKsm$OmF{ZR|L=j_4#?pXY_g^5EgWy?QNKz7DH_KNvi$Yxik1F7Z~mUD0D26M@(8K zbtJxDhrAU=Nx+eydhIaZL^6l7hf&j3&)pFm%l$`JM?foq3EdzHj>S-tnci(@;>p!* z4hIr4Wa@_$PMyH<>;T|P!CS%J36EwnGk+Ferm8M0&hS!as{EyYZdsSa^58?B;0(4= zXdR^hf^I2-6MN7zplN*qlqj-Pf}V&EKgdgNmh+nIFp4t-GX}bfiiiykT+lNBHEcdY~Y z#(jOh8vxUUCjU_yfwqJthi=nV0|CuA3^MoYw{72BTjalN`pLTEX!tKU+YtR$);Grc z#vX=_51E)jWt=pNmGW+A{>|7{^K-SW;3=Tm2qGk7GSbI#p%YBZuc z;R)y?c?aNO$>>e}7Gg z#WMEq3k%f`hdW0AR7~K7Q@1g7=OL!g@t=&eJHAXw`ql+v9XN&b=M)GUYf*-Ff^U?! zhXI;S)R-gny`vBpmr+pVPyGEHr)fR1A$Y~!WNauw#APga@1GuFK-13xfhTjkdK^T) zAi^~~i-|a+l6ZOZ9T?A!!SPuJO;6SR`ip_+R4Y+}0mR8cDBFkHN||O563acI*#de{ za&gU6xU&N8g6WT_@kLKNYl9 z=3xy}E33V$O%C+kj`5Iaf6lkBGETJVl7tHaHlx8&$6#d57Xe_Tm;Nxam%gch?@vm| zk@rt)8`5sxH%V36c8f+u^|p{J0br$G|Nqni%pSG{VoJRW1g8}`1tZrW7Oqu`o^8@{ zG&iMCrky(c0EA5;YBIMfhAknLpOYnVRe;j`7zIVaK|d|yMDEq)c1_>;b>{PF@R|#N ziW*_6Uj^r#H8|j_N@_+j(nS%B8W$gq{?(XZ>dy6=>dx0AilP|_w%r6BA8wG(@&Eu0 zA;_`a!Di}TYyfUCmeJRvMM_+t0Ko{bJu>JVL1#Jp-X(;#Syp575Y~!4dr%F4=o@on zbhNfTK`QjIt`z(2-#ORGc^D{p(7Vw_D5~`YcmVkFoLgtgQQxpKLUGm+W8XIUz-8^iH==DurczPrJmNrL%X!goVkhZQ8*1`~*MM#@wZ4f*7;e)K|=wYWXdoU{bG za37XGrf5tJmM}N(kCv$m8$gIlYG<<&9UQnjkHWFH_xd`pU#q@k`NPQl-8*|zvFmh; z?Gn1`-p?}7Olq*y1uc$ajhoNLg~%w284wN z{P_-`B9z07D7=!iY6IIRIomA7XoOO9YI;#|sj%IB3dV(+3wbHAK@Qe9zszJs2xnb{ zF)6j2KOngpWPc2%p%DY7nK9{Jp5$P}r_*0YD3meXf9eRoQe|b)!GaPlRXv*n4oB#; zTmCKQ#IC1&+o=2d&}I>#!i2*aNe;8i$Yd(Ot7E`3s2%KsLoedy5#{g^3EDDnm{=>G z9Z8&vh;r&9j0Q?~u>06R9x_;QKf05=cW=Rh#Mle(a^rD9Nzz_j$Us;yM(P9VaBvxY zC>>!6)dz&>{#z;|6&>|0(z^NAyY0e>4lg)xshxwvTMCKRs8$%h|0AKOq2fWz;_V1? z>1Vt#I<+DG2}itLkpr-xYu$qXgBNbx@eu=DREPQKWUONWwE?GxX^35|+5g-UQtv#K z!~?8-yHlq7+SMJ8_X~OCE%-vBj+7k)D(glVp@L;k*jzT-iUFAf`JtbY1bzG$5GDqq z<`nhx8n9r`l7D8Ald{`U0P~AB70knT9YDaF15+;@)&Uz`ApdOZRM>Q?sKzDt|D_z; z6%G6(kz4^`!dNA7N=p>Mr!K%!ub}rL48W+V_7mjqyk2F=K|01#4NkwObxiXD=5SM` z7g?Fi^>1$^X24|9vlr%qKsuFUv6t|0`Xu-Ej%!lWhU47;&Ha9Mzr875PivbJw0Yqk zDO(dvVA3<`w6%98%N0_Qtk1n1Rhm_nHpPBE6Si#q^eSJ$)ADJf|LOvb~t`#BpV0(M zez4H{UogEKh&<>SN-p&pcOpU7V3R0FLu!hbEPM>8vzV`2!DMG(6PY@Nj!0YU!dW7F z^CLn+N=h0gfprgMp3#`JFiy~ZhQ-sNTYEK7KUz!*cA=~d-U>m?lk?VPGLElP#wuY5}erP7i08E9A*mTXjb+3KYpS7{Q=iey_2dM`JcNKE=zw zoeO%2x^AO?k1^MXM`sD=hnD~c@q&S-=xK?t^?#J_{@&D?HR@FpZh$WkA4!f59n5n_ z$N#SW3Hcx;6!FkEWKqM8Rp+Zs)S!ib#<&=B$h(e{Dzk=kWNOE7;eX=dxyWg2H*Q6K=EUe*_C#&;U$^Be4=c@3RX^Da)XBrwQTRU7h|PTm-@&vQ-%iBR;4J8RFtm*{TLvsR z|L#oW2v;!Q)|HJz6ok!-JTU%8SlM(m=2AH@^!l5VO03yDdrwI_ik73SVh=$N1~jF{ zQ0}Q8WB{39qFQ$h{hpuiu~#V1CtIn%;zGuR1`NCG-kDz~ZLAIs+Zorf_{P(%ST3u~ zbFL3Kn4(t7&j?dHfhqk^023V)7q3tuR9U)y_&Bw^aH&>>PlyOS1x~>F!~%YGfRU~3 zE-6mia6PW9HAP!q51sbI?_ifAWCvTT(&yR^0QK}J!KcC86-543XXT34wj0U;P11N5 z?~EIgHRdF|jQw-)%(%@3;9PJ)Gy;0ljvzrmFMcj|VkS}zpXx`uH4kvd*T8pNFltoqg`tWa9wg#b|#GoBCZJ9+y^|TySAi0%59Iwi+^g8W#vD*liKXTI#iv1 zlSd!NHb49|GJ1Mm_fyj`iLCJCuW1msSqIKmpxnLwv=9(ra7ZvYc?}Mb{_&GO;Goe@ zOt1mFcarj&@R_UioGZ+$+CkHgu;o?2K{OZ3S*piPiy3jI6G-#g?TZ)mbf~XJ*oBi| zfH^Q}=VY_d3AKddrU>AkBUC1bB%F)*lLxJ=J%bUcO^1{=2RY|qLBdHF%0Rjl{Kr#< z8cWSnHql1Tvn5B_@}wb2>DV!RRO|Cs*0Z0eq}Eg@OPZdD1d)-EAR`mD3yS1VOM-eBdW)%gWmS7pAlv-?acWxc!XDy1q81rs;B>Z&{DFv*r8& zQI|VtKe<03HnvP4bg_x;KQ({+dfdu|=A^yObX2fc#&Pd&ML>m@76Rtu@MgyE=YF+Z zUVrJrC{rcMKWfLAy190>x2n&>f*XKeMmO3jVL-z;eUz7=83gPv%SfnF5TgM9;8rsYCy zkUfZy8bEXCQQ43I>R`D-_|z0TL@4Yw$jOxfqdL+(4(t$K@51*)$w~UXR&XTdAO32*W^e+Z4T={qd=%8DyBN4 zm(ot}s*P+F4YXl;sh#u8>BWxP%YZdFhGN!MrM_eJUwdfx(s~=kVJ>+91DnMq+}v{6 zWBVP>hTU3JU0hN*itdsGI6LRja!yHK@uxD7P&bkH-MbLd?wUSkWW(UQ&3@ho+V{nr zvv2@=k-pGrWI|`BIonSL;-Ja=7!TBewUCki*+A7@i>vpj;=<@XP(u9Z;n`!&Fp1k& z17d(#!GqR|DE@Lt))1#ZuioMPMFfziml*5GCl~&sby{#r8^sjc#JaHD30&T0I=h=} zb;aUk8(csO-}NdXCE=+1%0o{I%g1aTO}?F9LqlzOhcqk&dV5px5bIv_H8x%#Vz3zae@;{6thY%kej<>!_%cU;7fCiMxJTp_T&^&)B zE?)HkT?ZN3;R9Zp9dX7~RO#r;qXsZUP&0>72dI=l-1WxshEW3$^{OWq2*W~1Nb3Zt zbXINv4UOU=X#a2y1{3j08IVIP#Gaf!Jp!Ov^;5%;w#4?EAO#4+?$Q0)+Q;YgU9U<< zIY87>09E2A`X+Dxpr`+l%R$F;mH>Q$fvxvq+&)*~Q!)kgY0*%PyYNx&JO8s_3)JPks^-4ou4rCI^xBIm9m~jcgG8~r$;FdihL9P45@zw z*v!nKn|(Y+ij;8XF0UNA%sonY&Io%Z%}aq&ElKQnvbrqs_s(A&3G6g1&T zWuK!)%7IJqS1|vu-=o$azRCfa}g&9|%-N96Zb@3$Qzsd#==rjVPD}C1mPzV)#brT@U zcoJ=mD@^rhr2i(fkyd5R0>ABH@9hD=T+8y)0;1 zXFqIs9xKQ1*k0STE7ak}AmuUkFG*B@;TCQgF2O)94issp)mN`8G%_#@ zc7v$Vho}QnQj>VBN|#6-{GsNHYdsjMo~!uQu%R*;4GPDia1=?RGBHN$+9K&wo)OKXu$p%)xZ##P4sQ zlk?g9VG7D>-J19=UHS~p#CQ!bpvoNOoq#!86Ps>Am-zHz*!~VuO?}>NjVZ*KEfc89 zOE4_IkZ#dQ09{B;AwFrRWrwk;mqa6bQ#eN(V5h@|WLWwhC{oBjLgjYy}7&M4JC^?$m(Iqje5678lh zdGVQm#b!PBRA7QcHmOwy9@zI`d&J6eE-R6Wl<%$wjQMQKTzr63?X3wDyQ17uNcrRq z5cBdCNn$09?=^t#U8dPth04rwdj|VZgjB3!!w0JRHasL<3lRJN2As;#K`)Xm?0#gR! zp|k8!8oZR~I7E;Jw=R#=cG{266kI;ne=I*?D)^3Q;aQP8`;HKhR|$N96TaRbX5y|bYeDE>3UXeCeQfpluWm6C!NOzp%I(I zZ-KZ-8T8dxHeSr#j84Nja=(c@Smenc5o^OWE3*5SAqy^eLwCydURHK^_Tyru_c7a) z?F5Jz1=`xZ_!X&NWGx|~pI*Pm0SV}VLk2mfrN*gZl6l-&C&~@x)7@}zZ0>BkA8oLa zzTZv|(@Vo26d7|r0znh4+;(|Fk*NHvtgWx{>+Q&?P3u?aJzxQTj3?lQ%xQ@WqC_=%^T1AG zwlgK5%oD&p$^zp@C}D|v@?aFYpYj(r4{0v^n;dJiO_s6IKga4fYImKKZ^s7^DxDb4 z*0%on1{fC&G;bUG@bl*uMq6C*np|vsrDhY@1*f-g2jB6l=Oz!E&9~4gdY+FUa>Tsq zf`Z+TgC9gp*G8E@+h7-7>KGV4mZYc+vRzy385GcI;au5p=xlAlZWhllQ<1}I?B$>d z(Q)O6tC15WLWq~URsHm9X?0Fr#r`=ZP8W4{^`i~|=Lm3K$P!FL7jb5NttN!!$0@D zEyCWUMg#C%^8c2yp|HwdTT7Z2tm-NU7FO(rpQ@hLZhgq}+U{`@W`3!|7Ab%r=tbYD z6(waPWb!{SjeNZ}9r-G!vvLRF7iOySun$n07k{}604nhj#a`~jQbsFVSvgeONj_r; zqkrbYU$H}j2vN@>PT5AtW#S&H>3Z%uoCdx63iG!gAA!0zkY*`CoK0Ah9h$(?p$FP9 zqaeya(U2$6oJfGLe92KY-^A{#+#Dl-;ACCEd6CqrQ_;Ao}7ypOLvE@y%43XMfm0eecb0Mj0*`B>BX zJ`+a*qD_=rW1tAN_Z{j3|A7e-D4uH)pIX;PagquOZdpMDqm&jCR!{it>r03jcAjRmMp>=deh`ysguc zH$4klg%=}RKjOt)7RnhDMz4zY6_FOJ{V{BpJbaAbBqtB3X=qd_D0)iql0I(dNhWot z*SP-~N>2VjlW7pVQ1SXXF77iN3^{RR!uGD-!SmEuAF)Imo!&dw%UpGxd$z`=1u7V5 zWZpGCj{o@!e6-Wc*HPBZT_M#}Feyc>m75YTX>5Msz(Ag4u5z{BEhcwHaZvJSJNn=u z9N+)6yuz>|I<*9jefYs!Ar5yiO2?S=#83S(23-R%ACb;rd!=7Kos9@QY0JF^4@jQH z@JAPCNY??@)icDbe!ncn1IoLlZw@C^>vx|`EcS#9DX4>+*$<=&zqsh4smBCLNh6z^ zv1T?lIFY9#-~>AzbA_={nWnTrf3Y?XYL8CWW63a#24#9Yhb2M9aDr81_5#3Yg5Glw z<;m|~2093v^Oiy{;=Y!Y46>RhTelS_8A`bIO1RwqHi(fpAD!NH{={H@^XBCCJ8Sf| z3(cq0=>t6$hoFc4`-TXb}KtG>E33bPbC=i$ky5Xy>0aYgr|tUFK> z?ptiho%H8W?DX<#&Ooz7#b5I|`@Q8e#ob5sbd9y>8Xkf>Vke>LocJBj-jd)={6Y0` zekjNNjNseZg! z07lLlhJ5HXl+(7X*9NIA=ukIHd_dBk39mcX(tN%N$TGyscnXH*h31H|As6Q$`8j}V zOvkrxsENOE+3TLw$WnDV^RhUeykl?4P|mzz>tNG{PE76%kdT^qyicOK^tV8i>|`RG z3k{Jd2~vS8juvE#8h9&?iX<;KQKnRndJdW02-x^VZS_ESz@n-1b;3ioIa)DgUV7i& zAGg;1$X}hTPW1sO(bW6UgeQnfm*yDAp<^w-;G9Jp+t&Lrr86&tHB9Y;WZ^5e=C=b#vDB;eko%prAt402G9I++ zJNz_ZaMq0^9Y%R6V?OEhPMfp}i)xGwezlKe1KM{kETDSodheAS9hcaRlwU4+tB;Pg z_9!xxZWBQfls+m)P@|Y5!E|c1CFl9wvEXWC#>i5%uWHidvRuVfa`rIXdDd@`dE|ti zZsyLt61*=)k7n_l4Xb@;^fh}((y{aj;#Cjb3{G_u^Y=89{fC;KO-&eoeBtQs!5EIp z75BX<6QFn#oUpare4J*tb~9Ew@n$cs6a(nk^+$ZV9o~Us%A*O3t6!;JTU+DsdxQMc z5t4VPYHvT#hsDEA!GlZ;tUJ=R-KHCyxMn* zro0a}azS6lu8i;GY|dH;59Krfr)}Z(I=(tngf{y`=j}yXlcevXx2t9wgs-)|ah+R# zhVM@7petki?}wRu@4OVmye0IiDn-k_-VTV_pC-^@0W;>Mq53JhLEA{@q zfwm$iEkY`l3%5-R&V`x-)|x3=IJ&`Fn!iKH{|X9yOd+}4=Mdw_Jf>8nx(PlQCck|- zFMFT+$}#;e2u8CwQe+*gpuC>aEjIEAZ96_x4D&+pV5csC4kts+Of4jvuTf|z`~ z*8lM|Xi&M@cN@*)ME^Mg&Jk@q-TBw~%HJd`i5h!TN(#JnExic^Y>W`x_)J=hNjqVe z$D7v%Q?_!`@6WEYX@(z=h{^w&J2_36SlQbXsXE#UPaGc`+)N=WH=wI3H`1!0d)}?E zRs=8fPJk}=8P~R*_X#9>xxf#nd8Z)Da;|58=(7wYu(U>!W`}2{s4!1T&ioy!wY4tGiigX&!$rfs=yqHq zrhN6Gm2Q+f;~5WhFX1ymWABO&ZZJ@w{J`2#K?O4OIcMTI(HZyO55v+JnSXY<|M^t zbX4=$H?2mM$-mAHym;fa=Ep$XCehUL^aG#KLRL!*jO#j}*Mp6XBg0`lFk@q*TDG9M z46`)= z@>NHkv!=f}|Jr3oseQfn5E{_ljSc^xqv^jwS%<*(SWV!JsCjMUq$@pAb^(7c?-N=j z-}67U0AL@(rhe2Nd2rn>)Ct-^ej?ZjbTs7{QbiuAunSR`Hg_vG_23EO?vhFNGCn@E zFMe^eU~?Lw+3$GwPMHWD{i+8m9p%@yU}xZZ`$>e1;B^oV{G_);AS;7m{Ay|)x8p_; z&j+MUn90F~9Mf^>2+1>_Y13foUa4t&YAk#RAuyjIFxsogbu3u&+qYsrY0Y)&gun(V zm`BAYoHJ{ONeWEux2rkVgdLl5YGLuda{1 zaJ9a4;#=W>SF3%$RhZ`SgapM)%*n>e2aGwJq{VBRm2SwF=s8B}PXFZQKD^>1iJ1NQ!577)Rm3?^Z6i*~rDuwH2W#y5m6Jz<+wVk3!LI}fxgFKF8Nf`4 zy(CF5^(OB!n_3p+Z#Um~=VlVW?L79~1~Eo_F79k8{Nlzb$j8w>J05mP!dInirFq!GHTVFkbhYnO4Q*>R>6yc z!b8*Kb0&dsTdVAub%D|b5I(~gnFV^Q+*iqlM7xA!1^RjY}Y@7wVjwZ zi7dyYuYbr(oVWg20!{SZhnXP051Jj^Z-xu!8h`FZ3g(@LxzfnlKI)|KBFZ;{?e&53 zgqffSe{P%H>m3aHjur^V4$k^;HRG4=9=45!_4~M{g+Np>cUU&~6)~r8#9R&m6Vayo zcn+83o64X4KLB22ureYp8+-FLJC z85P*$DKn3is8UUyb`*-#he7xEr&u9S87NU*dS^pR%NnY-_MedA;(H)P3I|q}Glo3m zBb#~Gg~OJU!nKJb?{0T@>!?CR6Wa@RLM-=OCb2?6+s&-EAHiyIOB^aL@|Xk%ZE@zTIHyPhd85BkvN+KJfg>!G6Sy1tuVJ}K`DA$$t&We27gRI4pKa+9#sPe;<>j>cE~S%Y{@nBlmU znd|CVR<3lmi6gx<06)<^G;*Vud5Hr6JnXo`6*>^!il14uaR+wQ9<#CmA<9Cp_x?pr z|8M3l6=FA%Ff3uO;r^p>Gi*?~JlCzrSQl|T~v*T!o5)d8Dc^)J#UWeKcFnr4e;hQ~?|B?$tWlJ;*P~xuOLK#l ztc5_>C$Or=TouM|ul0JrXe+6Vn?(;I2=7dblZD9VUDcc-o~(9ydv{@ww`eoa-)m;a zvv^jv%znU&8RV9)+fimEjG^DOY(;D(F`CSx=h4&MekHu&8&i}NFZwgiUHr;)_EO2r zLJ^?}4(-uxIeAc?(KXD`W_{6}rf~f|{$M|(5UczqG*oW`k((#&Jqy^X?hDoXAeLZH zSj>D*NhU@(Di{m6TbO-ZREkEp(7&QMue?y--u1AZ2g9^iQS$I}$OJ)L+9ykF!Ahmp z*_WHp3x2r9-^VX#z7X*ZM7NZzOG_iMe6M%p2k;wcN(cc;%ePuyR7_j09i-_9`f1{? z-=3O!}@v5yLNh}~MDCfqM4;o^H zmV||%XD&COo+92Lb0kM|T8-B21nFH&osJRMHERQp1h`+?tWAB0)1d|Y{Ti-Ha4RHD zIH}LW(d=KoLE)kM&8ABxBp5N7Uot$h%X6xjKbkjmQDvi7xahFba#>D(&8S;myxcy2 zTk7(?93DJSE+WeQ-4#eTw7F_W?HlUdQ@0cm6W|vzU*EJfnka95&vV}Nc1V*rf)H93 zfh6ZuYIpb13qg}T?Hq|JUGq~(B%Ob*bomp>RcD*4WnhICqk5DeW$oPU4qi8EF9h80 zE4}-8pE3^Pw2p>_0{iNPqXVCn_frblBeb#+jFt)u;6#C#5k_g=6qVb`=+zt_qw7DD zl1tZ=Sg_t6^Uc~r@xLwFT#bUrJWhX$2W5V5wxsjh92HWb)@!!2h07tIYP~%D#@72s z!T9$?hnvj%IirXj`07)|4&>uM(~^_pKJzy zp_-p0_6(k8wgBUv69rEtC zRo~)i-v9hPq4#NFCQ)bs^<0@ZJNT4wlOleMszin2X*-oIi?m5PiRhIIs>JuZ>>W1= zW&G;5i-Q>IcFjt*BvOE2_~7=c5B*LRdK@N&%JMTzM@i6Hj2a)Q6~)W+<+9+rlk$D*hH|U+SXXZu zI!8MvRV(2gV{Nlk`s|TDhCWtSLj0pxKLSYM-h0#olr=(G7i^dFZkM@Gaw==4J_fwM{fv=2CS$)QH=gNv_~rNS;M?*W4j6C3 zGt&jA^2*;V-+Z2t#RCdAx5D3d-8on;dm9xrD(tJP8fcP+6!0&PxKXi5{2meK^ z90@66jT-p6A2TBGp^Yi&P=~Nd^g4_=J`(ae+4O}9P>*hb;>}^~tRXsV18kIfkYwIx zXF`M0&#`1RKB#wkFYkj@EH^q|nu{5%iTdD!Jp|)Oq1X#h0&1}zjWlJm_yJ>4l6j24 z37?B4J2i50GAMe_n)Ch{Pm&GfSde4vPL8u;tZ_$7|bFXJ4PS z^7$Qh+|H6)iL1y-r@AH*c?6X#aB%;H#s9qZzhe{u{9`s!!M_OrV#MKX!TOt=wx6r~ z0w6}Pj223PR9-#m=H{$Jkkg35g=t>1iDtJt!lYo|FZ+f(pGF6;oMCV6z9>iu z6)m-q-Vx*z_Ya0W{|%(Wm%o!&Z%u>2`W-k0ss=g0VAA7%N^ z#qKdIh7^K%KbkgU|WnHdKRN6}b0+rihla_t@4EY)w6E-e7Vn#=dO^Z5+w`@;Q0B<^U~C4uY+E;FP=% zqu|Dv=tDC68aeDrMM*q;3)hV-DhV~ronYU@26D*EOrl@@>DVBrS9$qgwG@tWO#l3v z({`6X+kCRv2M7`lM%11L^UYrnmb$DyVeiHf%_vI3B`z-^3%3U_eP;>MjN5tYZfUXo zI8dx>ld!m0)Br*3hI^_47Q1_^uf2~D0r#_f2hLk}DPl{oUsKib_PxLV$C~)noGmNs zjBE zI;^0IkDizexH%<1vGfJ5_uSemwiq-mxR*pu+G7l9xsQFO9qDn)3hGxVsN#z!SBcFU z)^8bcN5>$oi|J?qnWL4~ z6Xw-CF>1(LAEj^4xS;G%Y$=*hjT~4pl{cj4c2bG2O}#upvm$;TVox+-r@64)aGV79UgP_1Uf0{|hhi1=~?q;%T$0(t?$PC~S59>6q$J!M^kI*`=ixJ9hwQur{fHwf1zC5 zQb%_ERH?7yCG@=W_kTjTLpxh=+T3O^)&2AaEF0R5o+XVfQc+;~VBFP3KHWD1yqhw~ z+H2eL2Y#GMg_4dQ7wQ_rn8p^4L*$SPA z8<6iR%Gn*Pt-Jh>S0}fGmTaOC&^lK_L%qt~>^D07)P4`LK9~Q+UKTbKq~S|A(Dn#IU1}<(Knn3ccxKSzb#qTv`gj9=kF*nK>O{3kYLMOL^O z>Q@$3$>w^HvxJ3(G-R)DJnZ9Ei$^~*$Cv(WC}=JqS5|JL)$DUN(Q@nKW;bgP6=1~O zfQ!TPo4eh^8uHpV<6bWc^}%}M?HoQQxq<0b$`9(nMsPVqy#uMhOmwbbf+}^Ku65w0}Z0D`Kwb zHOv66A>2-Dq%8K`a7=AgP0a#of{KV`q|@V{h3ulFHa5bsml?%3QnlPY{$3fJ5&J)2 zL?Sq;43M9`ihA4doKf{={AH_kB6Ct0?LL-h`Yu?*-i8?#g7S*tC*G3{Bc}AGlr!Su zJ6f$}j-=#YC?&K@;@s;8ZIC#eE6S-iO)Ybvo$-sXLD8}477SFNmp)7iufO{{{@#Y^ z!c%wb*KYl2Ba(e!SL6zL7NA5nuIaQBRbXMX(c`8Ezl;UhsiSKx*o%YebtAyPo^ zMzqMD9ZUD*2Y~&T7j*=8`;iZ_Os0OaWHB~fbp8>>sGFP1-we2;@4uXP8-2Oe#^$xhPt*$z z2mbzW>ML-%K=4;wwH|BYj`Mj-NQc-MFR4QIG+9Qn>Deep3}bO zyx<&I1OF*K$%4^rH2dHCGE{u6TVTvwxeL#km}XAmghz%_@%|UP=Eos@hxar}P0KzX z+^Er;+Bw6*$-!%)*h9&|x+((L^p{m!#pi?xj~e9?&n|^9+u+RDa|f|Is4G_V zr#P1eh}I{pZZ(d}hp`$i>Qo5H2E$;%F4ueF25a?VYBYq8M#cLctP1w`2j>d{mk@xyeFLG)<2Kr)Lj?Se+Q@D1MF6whMSS*|oRA#*Np5Z`7omQ(c~ah?`x^J4Tn4cqS{qZxU9`&v`cBrrx;{=yx#;P?F1UMTF=*KE!!be{X)KMcohJnIMgeMbDcoO)m2#b5@SWVq zt>QL{L&39%O~hv&P-iLkc^7ik<-0l5&MYtY#J7M=~3HB zI7igruB2wh-s20`q1$bF;#9fn>RJ~O+0HuZ#T%#k$N-4hJ`Z=s$?Hn}-p?xawU1G8 zal2^1;lm6mb}x5<8DZ-_6=%#o5#sx2!nmL}$qJ(AilggmDzdb4!T=o$WF)IQF*e(17(Ah5>n zbVb^tEW6IypS6k1=; z78?OW`S5ri$0a+hSi|%cXw1Zunuf^79?l1P?-MF4^;3fbohUnok*Po z)Y?nOZjt@>uT2|ku7p5}D1+o;s_7Od2*tIhkosJ_=oot%d@jLcPJio{JD>ijPaKFZ zFu#yQP_wed1L(IJ?%9onw6Qt^zuc;`QNd3l&GusS0@o4NGuG)7->d0ceq2;VzZ@r* zU!EwBj#xC~NvI;Arwq|;EF$b^-b$J*PRkW>hD%}N`gx*#+*Pe{R%i;&vPp&)gX=eD z4AbGodH$r8B?iS8dlHJI6~^Pceq?oX&@zXyhT?O+LK3rcokj`i&fy3R8QRiy z9QV+i8jM$u8gyR$GnFHKF_L0ju3Txg8*~1{OtP`v8>5Ry;m;`V{jw**$}5Y;8Ca7) zM4MU!yk34raiv~1)GT_xGIyYz*4WIRZT`ixhHC@Uv@@xo*^<1C_LT&Ke|=G=y(;yy z+$J#WA%<(?J;0H>-(iWCxbO1(Nqi6VHeKMGcBTw@*(lkh92IY9JXSP$4Ba-b`7Wo= zZ+l;|XX7-P?*wywYT+7H(pwu$#1yo#?En`3j)WZfgbsNHb8Q&nMa{dIes%O*k752z zfJW%m5PR+c(2{Iw^Rj5b&fXd-ynEIqFM!cpDf^$qw!Ltg(M zTVEX&W&5o?!;sP-oeoGNg3_&Yh;)~9hjb1~gM@Sm(w)*UgwjaE5K__(jdXnv@9(_l zob|1<7K=Y-v4(-?-g{rywXeNzqle()UgD3I4`8+x?u9WDEioJNd;ccaQfb(u^L;Vz zGbQIigBj`zU;8R1ntXKzULAWqYQx*RU>%JhspqVJ1;Ayi=yTvbWDuuI6o3?=0A2N; zAa`oyF41$8EfEBMCrz(cfUK;o?NmeJJEcF0y89h~ zcRYj}2N6c?1vA%z_P5SXx!YD5EOeRSUgzfl%q`D^^#UuCYw}-bXq76mukER4qumX{ zqkjGBUi$dHths8n@&zf=pmt+0$sa|NgsIu29 zmSSzuUdh%48ZVSParsXt5GSxp5kG(NQ#kM-4EVio1!Qc7PJQ3!AZrpVG{^L+vfo6y z1dt91JdfXC)(>1v8HW?g=o?3)JS|^)#x-}pe{-itM`jeGSD7;FI-y-|6h6*Jp~yU?EPE$rFa99QwRgT1plS`Q3e|Jf%MThoo-tP=cV820oU9UL3xCY-B7cMF|2H+LQ; zac^zvI&Fdni#;JOEAE=JaYKRp+5$#CS&}Q&JgZk&niJU6QaW!_m5aBpT~@d{8A!Sx zi}j#IX+u;9O2tT=xR^As{CS)Z{Fs{LCC-kl2cX(cCVJgfIMwINlLQA&PC!?ejW}#k zD)i-f!<24q-@=(~X?pOjG5YPT*Nf^X=gz^Br=}_xK3LjX7j!FG!E8MXFuUVb)C+>c zDnA3yYEK`og38H8BvVT1A5ZBEJw9P%KJ%9*7sV+)8aW{$HT6&OPl+SWIslj$#7jV( zHQ>ex5U>5vk#qz3QFpHNtrjCI==gn8_%9}?zSQ~;CCjsTpk71*N?7{evZ&$gj`g%o z!P1cpKn^$%h(qje%l#aP()H^~owDs4aDX&p2)xnM`q0LmLX`<*S-P~J9-IT_KEP)8 zsyQ82co0~S1`{2<&~L4CF~p*l=RsG8)BVD|8f8_t%%ZCo#qD0EF0y}}XF-$zwBA{} z@v(+nd1EX!pAy!Sm`T*l?W+__ii*!wuZHds8gz^!Rg=8984wwd+{^KaW)fx~NLU?g zF*u9k`*g-5nhL7oU#qH(r^OHdfhf#ulo2{dm@@+aN|(86Pnt`QpK3bt_O>G2xkMOk zTmO6lP%6)g(bxeV^OfZsRbRv{iSq{uyXv;0_gE)7Q5M}g9q=NqgdH&~^OEp@5%Po9 zJOc|iB_IJ%!K9&n)}u?maOw5Y(Ot>3h-C>-KZ2mwI9eqXQeMg7TIw=UDEdZA84EBn z!pbEc16{-boiENT_y%1^g9$#K*cyghV9{=PhQks0!gYB=AM;v@kGOJPCjvMCTQzs^ zu+^L27QyT^dyIahR;qItiq}(7L)jsFs}n;($9ogLTn1(HzMMUUNmp;!H+nxmCLiD` zJ1uDh^AlAXX$_wSk6O*M9F(Q{oU2re=_%!HY<*f;#+5WSMx@&}Y;6K);3M#q3E@>T zQcI<5o*-<=l!$F(Lxduri#h^m+i%PoRoM-5c;HYAXw=%;I&pH2K zNRHis)_T|1#HqBRkF@x&J=?~;vc^6G<^HT(!>mgKASa7huX}dO9cifJaituP1&|$Y z*A*M!7jL$oICS6V=xkr^?Y4jRcN!0%V`7sx62XhJ9+@gsz2`QS{EfGw z)LG$NRX|7Q^%K)nA$y#y+v-KV`L%jyUgr>2_JT^#LGlknKgSY>3@O)6Dc1I0 zGEFCBuNzM`IUWl}1UCW|ql|mW5pn8RiqNEu2CjjqeB9JzlUt?9R%;TyMYV=YzE+{c zsHWWntPbQZU%7qRn@Iwq5ncRT_9V*qWPf*YK=>vBONoB^3?v#g4Z?@mk z{%Cf#ebjvNs`KKynwgqiZo?YLY(f02ik3IiFwknpKP@0%69tx8D`zw|Hxl2X9;ShN5ro z7%1T78I05te@kypxtOWfA9F?czTDWiz2Y0h4Zb5_I5%8UyeL;GbZu_^PhIu9^%v;y z!f8U0TaUdA7SkPh2-=}bYu+7;Hvd+Gt7o4AU!Lpv+}+Js$RbCld5qY9^7`meIEoC& z1?X0&?4iG5F*CCh+dYwWF)ldQDZ9gJxOsPSVm-tL)LiR6U?H(PnF`Qquj$SI3~YZ} zBzMZ={zAFtz~8HHqXewfI}Ka2A4 zm`T^+p~709I1}769dKh$>U4UOA~?k7u8?;f#4?dZ>8=;fx!55hSFbfHF*FhobhtMR zbYz`w-4X5YqhhotL-i_0p8xW$zlte6OKG5jCs+! z@Gjajkj69g6JiCN;mEF@d1gtHg<-ndI`Q6>&7Kao96gdbpedLn!Dd&{u<+nPrR?<* z$70?udIR)qArVL>d|qImY4mv_plKscg%;+YeGxxSQ=QlWqO_vd>z)el&AAU?>RYQ9 z3vvqtn!(2JqPJA07%Lv=Sqx+C*o?=~-=ik2JEJN0guWQ+`HAhO%Elk$PQ~rF9}qGQ ziVUVoY!=ZepXTv~E{NYuDD3kvJ@kihWC0@P)c&$M24&%{Ch(Lc6w6q-y`+gV-;3xN zyBNOoQcd>gZYnPa9YZk%aFll0oe)bdKX={k&CO%r<&Bp!B#gZA?vP_l771Lh^idV|FW^z<}DHbvs1mJgKna1`gZMoGUB#VQW zCN|kVf^-#3?a_@+_=qZjWM59vz(8azF>kR`8@lMiw_2`{TZT)Eda-&c76e>8LG<9O z?Lpbph~BA6^#)l+c9@i=R!OsmlrY@Y4UlF?0TJy5@MOv^j|kF@Ma=8Dm&m#$|B%s; z)@Pjp=>7n}@k49W6`TlJ?O4vtK!fq4-&QXS#EeQ}tpl%PnUDet>C{_RHAdJy`25{pG!~VRmvj4=JU#K&h9p?RYb|Q6Eq2V`!EYqeF+fy1%WJ1h8;r2x! z!P(+$YrQcL+?M_4zXGl)Oy;=WcCBQ}<3;B3Cj@o6TJLvehdb>BFK6Ud)?P;&)RfSn^dpgAP7r|Nd#!USnhp2@6;nUc@Sx6H>4Mu zcO^ewus=V_k6>=QQo=TXHSA{578cV2+!u3X!|&Fw_hIQCnVo$DIo^HaliGm*+h{yW_jk<2%qRn+%QoTpL`f=8(lwn44q zVr@1csF)Fo;7rUsc(mP-m0a4PHA-rH_D!a(+ymJqgZePQ;H&bKIxLz^U0*+CWd1+m z(hJIm4mm~_thl!j509_@CGREoGQRMMW)?*Z!x8-$YtAG{DpL!FO9hvC*61C`<%Cq- zC@Eah_(fV-MFJFMnjoLh(dbakiW{RS{-4NNk^(ZPsW5bmY{^ia24TPT7Gr8+4pM0L-ri1l1A$d$7q$B+-6ZpmSjLS1!N+A-`U9Ks zSI@9*Jo8;W+fzm>Iu4Ydb_kGdgVj1mgGi(iToz3MM=03E&)O4tzQ7G5#(&vy14^xy z9;+o|NBK+*6Sqas_VM`n$K>0xhNU{|FV&5<7 z%~ob?OFs|&rLGqO&HLu`IFfG*{ta$Ffk)ndQP>cWQW|z=jMW!Pgq8ULR`M6N^U_1p z6rsYS@Vs6bhN6Ue;BicFY({_DP@;~%IylPoVwKoGw20ChLx=$&^3YJ@r?pJ_;nh;2O0;f`CDc2N9c z)SY5(`fhpEqMLf;3)#ebO!h(SAmduuujOfPgZ7lpo`HU$XR6gY2~WE>f!kWvg$Uqd z_f!`F0m2Z!)WZ<*utBup53!C_&TZ5MTr1rkT&pj1l9r89@896RcjV@q90FQKMQqsw z@nwLRSuks*4oEwADo^%AK{x14!PW1c;22|-NKrP0SO1n2`=YB6K9Q43P!~Bew!6IzATgn-vzx0OQJ+I6V0_#wt0-ZyDh3QK* zf^~RP!0mCLj7T${9OE+)BSx<>+e4-%Go?p?-@`dX=yN?`<(UHUA)-xy_DXM6?0P9> z)W>tVvn^MD4PQb(u23p0^IdYdQ^@zBrjxr>&d99a)6>H~>=8B(u;m9eWJ1 zxxaC$kh|x*A<|MJ!&Lr*35v-=QIgS(LfZA~c~m7&>JEx`lTxWDO1K|@r3ki!a(|Zoppmr%760LQRlhR5)Yk;(;A%A&Rc8n=mSKWG&g z8<^^JD?tJFf9gDDK+=HBOgsvGtY`{0Bcntl#D#`HNHk z&ab64#Nhe)Hbn6s)|`XcHt)-Z@xE<)((#%2?=)Vd9NYF3}JJ`K=v7w=$<$H zf3ZzLC@3#QH*B6Pt|VZ3=m=|Qh$`xP_nHe#8bpolN0g~$cGVbk9tbT*(VhDxVz)70 zpJhhzdg^UBusTU(dV*)m3TSbmU(?QJ*dlAt21PTNqq@H@UEVwYpF#dm!E@qd@wM@b zJ<2;@2k&L}L72g0|Eb@8s;ZsEj*udeEqI&WLay3l%6AnsRS zy*(B-3hOKP!gpxhA2Hegf+mB8u-JFo0ojVYgn?kiE_m=wAVveqP!QeQ=IRs2?nl=h zGU1J1pZ8vWW1~}@9s!ht+Ty)5sh2*0Zp*ZCe|#j4e<2rePc6e=eQLQf8O)Y&+o`r< z{VGJEc=GRF%K%^qqabul31{wV|1bZ#);t>P?fJ7kef( ziaMzNek}EQd>B{y#eYn)%fbE5IBAT6Y|bYO^W2*wP_GL}8faQxz^PFqOUs}4QfZJ7vL=4b{$j$1{R5nZ^}{6Y>sM9@*vYq%px5Lo zO;5khcY&&bP1$iYs{sw`YfD%-;nsMJ;w$=JP){|#pGdtWW zjTvwSa>U2~d=}W0$8hSpy!A-4eJFM6cxUo`N=!ZHAiz(tQFc(Z%7i7CE690vw0xg-3`ftPT%}fdS2>XwvFGm>(N4~1^k{K();2Euy1`ty9#O~6bp+b z-K+Ws>VJo?fd@aK6`vV7=*^|bD6{_q@!d(Bg`C+BS-ZF1lREP_4+G-Z_ziJ+td^qa z`qroI;P0a zal1;_BN>Ro>5EU$I4$C3dpo{;5L7c`hMnlkr=lu&PgnU5=#Q-N4!9k|^TYFkmB+CRaM?W5w+c;FX3}F2}4UtJe1X49U3&i(-+SeHFXLgAG?bXROeR-&AznuUmfPxsNehPlGsLVU;G8NWyRD<{0dBK<0Sh=v_G+ zpw~_>jlbz|xCa9*)fh5jc&cp(JEn1_C(%e#6Q-A;e5yrfEGCBAU0n6&>d3MM5Nwr!t3>y~K+?0mJoqjM=K zgdQ*P`jUb}Vh&K$gXkcNp;&>~hqSxcaG+OGVv(~Iz74b>N5!&Cl1**?y6JP$G5X8Xsl=WQkKLhshr< zfMM5plpr2k^E3qNijP{QRMcwcf#LvN&{5cw^PqjN&#CsgloEW~ZHEmcr;%Gv) z?MS$YHsVO=QGyPk2lv0xyhEkWZGHXZ<01953IF!9KGq+E>ub?_S{B;xPsSVx1GNdh z?e6dG&AaD(@%;gKE4*dYovvO!PjN2j33xTVgMhq_HgNKRRht#j%qnF~=gGY0=(Y0C z_7r?`qt`u+#%VFzR3i+@Op`OX&^&YkdC$5hq!dk7AarVAH8l@rI-s#rBouSU6hC{l zW1o!wkPAK!p_Q4{r2bfYJ<70qSb?Q6_Y?|vl89I@Ys~z@Tw| zURH)mn?OliR?4dQDh0wwG)W4#?Pe}*GQ%i69K?DW60K#fg7=RdIF6jH^gMAz{|iJ=2M zhCzuTuenO* z^5*IVxWX%dD@;5`d*l!)j1{67!}^F{&zP+j$N`mAn#7~d4*cIQ7rlOd1tSwattYJY z==h&VU8|c|<5NANFr73v5`jj^mtsyK*su6|F^k!^$udk6e-{BE%CB!=5sMn@ z7=Q)HC6?V(>;K@u*(=F0Y}j|0(~oyP?oLjl*?A2WD8E}u_{;vFb=tw0rT_LC#AM7= z9k6O%JCN%EwAa>#!b>V?0hl?%j*Lx;+Qf$dD z<>S*br;@>)Is23fh?E>O{w6Z~n-$L@U{+1OT2Gw)8a9$qLYW~XFgryQwPU~RnQq1{ zikV7_Dp?mk$wu@NGAUVz9`sEhhBhDQc-KjV6Y_QUN?NY>L>30UfmlV#kvAxOZ1{*1 zD{pR0lvl<=PV>LdN3bY=zgp=vL>kb|t4FN8HIFr^88 zw_>AUHfM?hhC>zyYc56TY6bD?`!prb^Vu&)`QIA?r{?4YD zvSPSz7o^PPg%(eziz`@7qCGJrm?lM8kA)O(|1;KzJC*|T6G$jAYIA>>3o25Ir~r#) z54Z)EKlsNa$Nq-184p^vro=23YQ&bpIWqR{&TD$PRRJaPt){mK=3f$jf6)$_zG7xg z!Tt#XB{%V~3Oc<%5fR5*@?u{))fkk_1MG&ll37Xw@9WmsWn&+@kb_TwIpZH*nBPVo zS^_Yir3pcJ^_E0wKGLc0{btaffA(CmhpVT$8{18M>mQ?a{MgOY!V30`4Ko4fKvP zVmL6;^}J=3&0A_WI25@XZpYQ^W^YE-*ryUZrqr)<08JS~uBi3Rr}^Z88165ngEk%` z{p-xM_2BwZR_?Ja^q{Gu(p6GLi-(@+M~Q>C8%d?dq4o_(GSvn18l6`_0le^@Y`w2INj23;r$w8X%hClPCMfKzY>@sd zefL8Zs)dk0Hq8jXC~c8J6~~<26u|kg4+37MGH$tn|EU+U@y{5B3Wo-{k!uo3N6$NN5wQ$2(-r>)~m&%=;Mtec~v z;tf>O;(VVq8-n@45`T)0JoR|)_NCn?%%TG;YT{QMn_I5U0yU|#cN+gQ8Zn2jv0<*c zPl?}!2D^Djw)`^?HsS@XITh@Lj+se{F%bz*kvm}kG^N~)djCDO7GT}oqr|7OB^CsG zy+c7I<48zYy$ZL#k1t3BIIK^qF%0gsZkKXz`i28l>7@7-Lv3!6{%5`;H#9z**14|Msaz zJQkL-Mx9^4_qHTJ<;*F|Yv_3=I5wPCXI^-KJP%@!Fv|GpAxd7a3`__DSN1rmPwQ)L zG#&{Qot+uA3fgA$CIEWCltIjUX%FLawu(+=IGvv(Fhty!+fYIMprC$)^*|t3;5eI7 zKXpbgZKk^2bJYbc_NM*MDX3sFFg_lxvNr)!cK<8xFgm#TU<^&0ie`$wB(9?1=CCf0 zI^!A5N^IKjh+=L-A8`3K7DKT zx5_^Tm3|*E2kS>P(GJK&YTJkTVKB7{m+u z2x5atTJMFap_+g}Qf{kSplZ4}Fi|@UC(?wflLo}YIC*&x&sam#nS(hAGY0_|Wb%Ff z2rVTWgD_rm4*i+d!@u(TxPSs)jyXkYTgqGFjI2@m z=L|;Kzm9OEb&e(MQZ$S|AoX214iVZfxdn+1d%BVg& zM-D9vE*kry4D;3jh2@L8?P( zaD5y5sr|q3Nj`)J!@F-_)GZN4&*(ymC)C#jtPknWE{>X?2QvOWZ*%> zM!zz8k?^k|V)&29K^ECSxsBogEP?f4$mHS*z@La2Q5ia=AY*gUl^> zbla^(a5d5dUy#ZbsGWS<)RunB`GJa)-E*(3%82Z|rGY>4(!TW^2X?>FlF9HPMc!cJ zWZ)+aO~o1;>Y9YWNUl)D`vbCgLC|KLHPI9Kzp*`WA&U;nj#*DW#98cQv{&}9C|g*} zD1(?R>+TYQGwY6QI5HD*1&urU z{jigB7F=#+zGY@;5u|AzX}c61?iLZiuriJ~#EloAKd3p66@Vn3VQ+ncXv#@%cPQNNUHeS8=72%0fVzG&2AT6LcFF*U) zfV*ZgGuQkZ?(B6Gudv}*!*QFlCe=X|i!eB{T>YHdW&IQdR7Jlm`x^zcjB;qQYPAfi zwlCV-{n-f42i*_>`KQ$66&a3M8+W$qzn{`;F!LOEIB(BwG!|7-+VH#8q)_m zPDL#@Hx91m!w;zOwLQEHqf~wDg(;sN$rBYhEZyAZBTlJ*{dY=6+0lyIetNVc z$0$T-5`t-KgfA0k3rYg9Cw^x8ObkwhsIhiaR;WJv@Cd{!fybEhIOp_j2Lwu29fJd- z$yb2<0-IpKj3lle89NcJ@=17rb{W!`#_>(U)IO7%(!uTw-ZVt6H^g_*nDORV{vBv% zyeoQa)&Da=nGyfICGwW|MDWevlW8~DCq_zXbD_>1V6j#zI(;!O$hj=seu|O`bO~Hv z+s^TeoWT!1AKB9KUPVjcmYuev0b*uCdST8pWxKFbC-3?6xNe*L+Inw(mLAqnqQ149 zHgpNc>fehYzU3;IofscKJ8!S-l(5>W=Ms8zBBJKlxVLQ<@#9#(y|Uz6zYys3>)7W% z8n!l7h-naPW(HefBkt$bQp`=XNT{k>OH1V(>dN4vPkbA$bf_nu-Y?wOl5 zjL%RRTTEZun;fqzGDw`EfQ*-`pYT1uq0}?>$h59P{+3JB*?uJ}YuDkxj~|{hgVg&^ z;sd_-KD*_X*x%c>-aD_2RL+=h0sS)6d8_TIt4SXxO1olDR427ALL|8m(*W@zkK<*e zeZ!=WFva<1n395@DHXZV0>5sn?0Fu|y<-eo0-K!s_nY=(K<$6+z)yw!MW-5Pmv-P3 zAV+o%VLQ+m=1NG`J_GEwxmsjO1&P~L^Y58`s3ASTNj`IAil{dtGbPU}fZzR=*V8Wp zD?MKA=vy1=HTP*Wjpk2aHl`f+IYdBRFx~lZNy_00}uxO$fD^2ehGw zj0=Ah@mxv)(}V)J9TO`a2Yh)g_15SDjw8pAfeQ_6H>5`icYXqk0@rH>Lm1USYS3M> zcrX#`XI2R!s1mp(TLS`n6Thv^c3(fkCY>uz(2o~)mx!S0!Rd6r4LrQ84Jf!_hBWmd zeLRYI`6K9e-D!mF?4xJQd9Dc5(i2}1vnoS>$5n%0f_Dg5LE9*^EFU~zdr*T4ULu8( zCS-1VDI20W;C67)ia_`I)o4Gt-QbWFU-$}FekgDfK&4shK7{4W8^+Kp7%~{#EUbac)?iqo`^x#-4)8@4YvLF-PW)_$OR5;*m z`Ch_B80NBJ{wYbCJ<9VQB-{TS}^APN{wZkQ#H=`R9@A;hW2K);v zW)9UDCvIrtcdO*5kuA-{d=7e-TtJZvy}Al1vdy{@qn8a|Axkb0pBk_E{j>@>pXR`o z{^k#M((h)*!tnkoP$q4x_IMvNovl=~Fc4 z49o^_t+Jh?y21o>7czFxVh>(-p71>0zh*bvU1+j4IfxL2DZsD>ScqT3lrJgeG4~W`0d-} zlAaR!>g$Z?-g}&!S~c10E58H6nmc}KBfmDm$^qZgY2@z)nmwXbRhIZM0AY2h>z#Ok z${up$8DpBgZyNwD$GdV)Arb*j3!Lorz4+2zU&1r7%LhYWDV9p}kVPK09$l2u^ehO` zVK}=U&1h?6BoYy~nEZ>hT|fYrzyRxsWwSLS`C8dCw<=jV6oXPn8+>Cx<#Qm*cvn_> z?Kux0Dgu8cRD4KnVJ`H1ECW?NR;XfQS-Q#cpu>hd^r)~B{ZJPq4msom34?%JF`2=c z)e8qkXe&ego|{dComsQIiK|7RO4P9kzmAxvpcpjg$1plbIM|D#(+9Nm7{EcZMqYlH z5}vC_;Px?t3;%!7G!T)|gjPT<-zO+|s|Jz4HDjb+55j)8z}W{9q3PU_uq=+h7+111 zD+)J`FRbcV#a(a|!{}6Iyh|Or9NpP>Tx>vKBnk9I(BiIK>#f~Fj};}dDPkMjSKJM& zGVG+GBh1p~v@2hr9~?padda`0#>eM(+}-mmj9-z$;2rxVA8)5hPO=PQmQkm84Gk;y z^0aT|((U4JP*}w|v*waNO*}_TqRylY< z&Jn^Wo%`bccI;Lzs$Z+38F)>q+@W&FDBG#JvC#6kVHL0Q+ukNGNGx8-Nc~5bq>Rlf z9a!)ul5L5aAFQUO-)izsWpuFOyRorS$oKY7$stTc%1}4C*;!T%un7e#K^LAHA1E4P zO}1)VG5#!f;r7n{n!V~*>ilG~Qt=1w&yT7)n6ZbR9cQb;uey{!fl|eYr%O3afrRnJ_9vOOh)e`%X*sHzocoFEFwZIivIXZ+)SLlBM6_`LmJqmgE$>L8QvfP>U zs=#TvT>d3Kw5hAsIcCQ1NqliL4QOx>y@MVF4`-Vys@39@pmVdg*ZQY*80+3gZMl)d z7R-MSPjrnOsujqq2|)UNNWC*-jug4J8IMJD(5kayf?6uXnJ;1#a${+nUH`oyPbHyN zhE;?b^YH7qSfl8}V}y%QD@S78zQkFRC;(@M`(1SBfj)p}CB;}yn->E-(O}DvV$h*5 z=*08-@*I>1x!{FSuD>Q0)4Z6Q4sl-w))H6m$F7`qhG2kiXe2~ov;fe(pjO)c@Lw$V zAqew+0Qg!`pv~)1T*gWZ%NczaDd>Gv+X&GUlVx97HuL7;jb5DHJ{#&UxH!Fj|6n ze7}dzs3N5%0pS0Baev-^hE7c(3`8=a7bc}47yLx+73eio4Lv8*95b$alpH0$ybAv< z8%70KBIxq_rDTCQHY+R9EtDF>i1r+EoUpo-)XFR zm9hv$5%}iu-c>#>?Osi$SYyWNolOh(ftkOgHWDc)L#8VmA?jWfQOkBy*~;L%V#i|St03LeihPlF_Or6JeJa*;Im)l7qERE2ro6qneCg8a zg;g)g;$l0laEGka7~==Q5a&?W0?>jZEr{_M6#1=p-Vj1j%1>?mR?TYbRG+%&slxGA z%%rQY$Wn{XK`hUb!m0?vra;O~_|!6?>iMk4U$F^?H%!`4PBg7HC3-3?^QDvZeve_Lq3&yo$8FQ|N9-HcXqZ4F1I zVdmwh&AwJ7G%Du}(90yKK{Qw1m03g#ntO@Be50E8p%=`s*w{{=zEk(Gch%()7L~1> z>d|ZF@i1w;n4PmzLOx0aci+*eiy5P)cE^3SL1iOXg13d@Glc(h)RLHftB=5c4fUgP z!a$IH;C)oyge57aQd&;Etma>vd%l6gOP78L z%pgI;-+1yDOZtYlL3<=5FML7YAO_6c^)S7y79C-=uVuZ5A>X6egMKl~H4h{8A`6`T zT{KO*9ul;GcRWl6`kPic*3+P8S|N6+@)3{vD8gY#w?zJ#f3 zSD%^GbiH@K(FlLpBmchD`E+sgjD8GjWw-q%=CNZSJZyOYa&HR4L!-LyMQAOcp~?QX89 zrpnL1Pn5qrx0zsGPElAq9a}MZH$3{o$ee~{{Qh=*fJ~X*|2!`Bo$m47$jA{@Qyksf z-2ley+%JMOajw7scaw~rzo#}La*^0O$WQN}iS&{GI(asiMxD*%&p@89tQh&*2M5kx zE(L1s;^2!Xgqc}#-|li#k#r81QvP<_upxO5$b|t^^(2ZY4b6zUXhQ>Z(*LVZZd&$T zlG>kSFTc4HP@IyF!73(2L|jOgG?bGW*!1utko5gnJwj?-5k&j^WBsQrKu;9Wv@-zr zKaG$=5?`Nhr&hrO-n%~|E7lssWeuUM(5%O_l`A1N<&B^ueC&9#3{k_CV@Z(LhKN0V z6^8;NAPj%5n5!M!o}VvY2--ng_lC9W)oHWl*C%&#`X;Hh|F0T)L?N&ih}Um3^t&}E zHw;Pfv~!Ef>v;yQTFGt%=q?*^x~o+3i>bnYOivknsCJj?boiWb=!t0uZ7GZiq%0wX z+U5}q*OW3s8GAEGNysQiL-rN%^^q?R+g4`~>_ttR-{ur>%+-@hQ{#r*ooH{z(BkLk zn-2g@#O?I#?&#+TyC>lz{|lD*oQ|R`?LY`@em=HC7TMM1cI7#W*YP>W>Guy^SqO6} zjqu&&M1w^r^#4X15Rp zXevJ*4k|nz5N%I^bTc_U?Gd~7(pd3b!J1TdWM7InAnO>ZKN^kGnF3P$0w4|0Ut@Ii%)Am$NR=Ese&=L2S=jJwA?8k9Dl2jV z+t3T#`L)xaEc|QJ+Fi@{!-%PwfOlFzo=8x*J=<9=FKP2D6m|K%o7d9nbUS(YVWPJ_ z9v-9n86l^bWub{?XDjBc;dJ0Xy6L*(GT93km-1sHNae22&9#Nnzrw>YuhVb9!SgsZ zuRG#Q8)j$4M^CsV>MUcZUeZ?maULl*rmf{6XZT#eA##~6?e~eMHbg4${_2#0u7zGr zllR&oDM@x@*nFNJ$5Y&KHznT3A0^N0=J(08W?~9|$n9N;I!e&t<9La;NTvIgmCdEq z&}XP0Pp6ZT`she;-Z;$GtX-1%`X1cM*8JgpIsBzz; zq0&Qb4eA9A)gz}c77~twy&f}R50UoKzKpJwzi*X)VyMi*Z%f?${bQZTyVO3#zbL=M z_2u3@PaGv<P?4oiBY)HnZ<6UjAWM1y(Z?_J>g?Cef&AYw`Ik*Bm_u`Z=s!nd~pnI zs1y@8Ft%EJ843lH=OQoC7sN0V|K2%yULq{k@+C}U1PH3XUpBbJ-`#D^&{%JOu+T(h zQcAr&J3jWL{?pRlp26=Vkb16aL^*Kg1dt5F)UgoJY7V}5awR6=sAhHAM){5e=ImQy zSQQT5$lak%_NQADRdUQ*JEAZai}tFz_j6HzCSk8$$RY4!t4>UI&|pH;097A+6S{sW zmfcd7m*?)pYKDlB$ue@uw2uA}2D%l4`8o_gmoZ`_azJ(M01HU73f9-Xzf9eSQAJ3J zxA_YvN6{<&)+c6aV#ltV_9?C0uVw2H_ho{4Gsc1sq4X)&w~)rf?VKJ{svUrb_xM3~ zkf0mxGy^fmXSGw)(heXi&_!Ec0r}H+o5MQvS#RhndG{sm+NWqEUS_lBzJlV|a9TiF zFvyAG2?hx0pAh2t8=~ibt*28Ugnbz48^=MJMWL65K82RNvhHRFsloC=e-nHpSd&oP z6zr0Yuj^PRve{(D#U&)oEp}> z%tDar5|61rG`MsZWbS$yx;pjBQbJ2hPs<~~VuMhTjeS}4Qz8J{I?wSc!N?i|ZG=7= z4`{N`kbKbcRyqH1_ZxF2`aaM_G-fHd+hszR=Zeh(+q{1n{R`bAox@94vJF>wJ+#>g zq{CyY;*zPzl(LNfBPK>lZ7Y1`L3x>LSUld7%3Twh+;1xL5^-yGD2hc;dIrBzj1g^G zLORKaENXG6DX0rwRP(Fc+fj{0xAT8Ch`f9jSGF7^%RRZ2Y#I^3yGkV^wTyVrXcxE28H$Xp)!rWMp?AewCAX2pcv%+00M{ z^x34*6k)CW-S!}f+oLX%TbK3m1C%((Ar5P70T z1R~*h{kYQq(O~)m(dr|Q9 zYr&HHktyNulhX*86v&=1UOYjkf>+DaeNmvCz@pDa@Z&lWs7~sFCFj}xUi&$1Dq<_O z#;ouCY>gSBZNX;p8P0}lP+Go_2%Rm*3;6)h3eE&kUxcN68`}f4)|03rUe${Sw$*H< zH`VY%FK}4#857v~eGP}K=N_~nkS7`G)U=@%38r~0hCs#-4j6mlF&YhXI>;P1A`I-Q z46XUiier{7_1g;;NHrpK76jx`el<;ZKgt!02&skp=*`d6W?qG~WPgrLk+pcXSWw`>RbQdU5aEmu=Z7`Eq5%K@#Kxx=CJN%=f)7R2DzJ z5fGvN((wuvs^-E5Ye$bwN};Ql3&wUK*t3!uuRdQ6Imt*kdGbCLawrFS2E&lPSVJt( zYe(B2A`)4#@oG4bu~0d7O;4d=a|1JZ?jA2#aOZ#d1y{VL3pEO*DH5_ zFg%>#9YZXd@B<+qz?WvAN|ZH*?-4IIBoDoL3d2nSmE=YV@7&<>x4IW)i1{cVt|E=q!ZPs|QLW4W1BNODlFhW6cY7G~GHBu7N z0wS%Xlt}joNQk7u(B0h~d-?7Dcc0}AZ|0e}-+RwFpE&0nx&YiU3uNW1gBCwL|Amr8 zJOp{DHp~(JVk*wB zZ^>g8*exy7xdl~@&)?mp6iT^@<)UByoNyK(r}J~RzrFIOmLO(x#|f2@E}?N~W9EAXR`DYj}@C^D29kw7vct{--yb3~4W(a|Gzi_C%*eH|{O=u7(U<--iCN zIyIFTktF{hiuGRqv-AZgQ2;xg@yjvQv;5e1LG!IMiVj|-gz&E-pOTpChWrnZE z@fvkqyo~4r6QEyrV)V)_5o?d-unIiGP8)}53V%C$awR;pf16TB9>X+c08>u)Nyuux zQI_04G0~gzT?2Ng>mXSoS(0pYuP9v1b%O#)agE~`V2{&pFPX-bD< zbxjLcl|}u8-V2HtfZZ2IS#T`0Iot?!VNs3sq`ubP{F*mXYL1;zoH}aVWGBbPC+G=0 zN>6{tuj+PiaJwFeRL%|^aNbr;&jp#$t1x9NrudN=+oRqD=c{OSn zj}IEQqI5Q6l^$E;jAyxQ3r&WGad00|{+(!)a%kv9{NCPpn0772SEfRT=JjIa`Fo$r zZL<(8f9Vx5DCgxPDB&m@XmXnSViVO=GKAewW~23(k+5WOyd-0tpbx zctOi%ojC5SJls*in3%dOcZY=-wfM2C^5De)^IpzMEO2kkLvghffnNowv=@4GNtqQm zkKvPumBZj`z_{pG{C=6^8>*WzY&8JbmuRb=U3&KpLAz{=F$|{mD+86Br;OD+zaQfY z$4HYYF^`b~+Mxg8UNC8z$`K~U9U|Plb$x8|2IPgm%n!*cT(aPYKd~7=N)jK{t1EsX z>z&Ure)cv99|d4ojLyZBAdI3bII3s#iTX27xL^ULCtm!eS1QY?cn-3zHIz2GQ_=^7 zNjaxY1m-J|#h7w3wAOD7McT@)CA5hN>i>M_*tNm;Uw?!xpK~G zdZZze24jJ)a$AsswV)F7KvIP6>C}fQkmJj$qZJiz-)rniQSZ^{oG^rq2C%{TQby^O zVA1YD0(1qSaZRHHiOGY)o3rc?NZxl22#^i4JY^zJIuTY8%JV$IhN}VrA$TGqW1a(I zLvp$}Ub_o_z_yDk8(@`j0$7NLr6t{Aw)hy;&AK@}O=;6bi-j9x%GJ!F@kMq`{1Rzj z*33Tf!wjT@NI;HG)Z`-7k6aEzY}YRxn~wsL(xP**5sr*oxQ7?Ds^r$UG9~OM5Fe>) zqsI(uj+b?!&l8^!BRJs3%mp+1^c*$YuC(x4cp33ekV8J7SsR#F^*c1^e%3DTf&qx< z2CaH>7UT~bo3nCQusu4S^co_9zo15NsB@vaXsN0;4b^=eO8CLtp3|kj$5NzZOMq%K zIu+<}k{xyU_Tv#fwi@adz0vra25>6GMjmd^Fmhd(Z~+MfwmHf=lUKD-$8%Hj6MIlb zn35<8v^1!*oh!oDV@x6?IaYQp|5{ag`ojKBK3> z;Y1sjeullk1ugyfsFmc3Oewm5_B>Q;k$-@L z5TxhFrih4eXLrMZxUMuMmV&CEby?d$fDYU3qO{pBowfzWy4e6mw)MnMxVf2K`zcB8 zKPLZ(etqHIqoZNmp8Ia)KJmgFwXjO~SnP-~J=|?yF z+Blm?7F9!odB9WdTY;>l!Lu6tfL`acThVWI`?$cwzMPhb6lxmz@ev{+GE6Q4V4E3w zlt2$;$tFmcT3wPLK1Sxt70T7b9LTGy=c}+4sKZrk%hlf@Bm#^vK7gMKGK;m6(p=yk zlKJOJ!GvEMYP#JS1!_Q8(&CdP5UbAktRdnAb(jV4iNQs>m%B8WHd*FHNhf2`LSGuu zix?8}$l*vYBuzx#;L-jcV}$&S=ZKM3%JvL$5>GmT`HjX$79kaor7mJ!&Bk_d;(AXE zIAH2v=|zgGE9?>^_A>2bo(MAD_{jDg11p%`t^|nfs z3pd_vjz4xDzF^@@xuB26liHWh{bM(E9|j%c_xJfDwnh^2bet^JP${eFBCFBh44bGi zrEB>z5Z5Z&g$r&A3s5lSeup~xF4WJis9^IvOS(-i`B>8qgl88@W94s@k$cjgf~rgZAb~!QZ9JI~)pAOp?WVGJ6Y`&o@h=JOk3??0 z^b#L%mnrvgjp6$!Q9l#3GCZsXQn@Py+8H@J_~bcU4OhTqpbHQt`^63w?QMfU3j*X2 z?lVKmEe%e>DYvD4(4vA_srMtA$zu$3mrc- zK)>>WAkk%TMZfsJvG8#`Z?!|4;i-EmBH%mMTzijeMx+kNM{3%3zxzPYV;h9YPz_|t zE!~1Xw+(cccC7YqwN2dV4uMkil$DcS$3?&#Mz3f}N@MkT?S3}z%dpO4Yu42a{N5x<#xgq;vtR%yD=gB( z%qse)Upm@tAAzj)8ffCFsSA=atKKHaHpg)HDXrqwOYU3ng0m!DC^3$lDL#~6o}Ou1 zm$&Jq5Ch<#!hww3@5OZS|1H3h_5lzU;9_E+c#~MwP*i&n>JC#WGaiUom|8Eo93KL0 zz`@O7>Q*ty5YwT-v&pjtP>p>7UbN>w*V%Jm9eRjzRJGFDf6=*Q3oxRRF{<$WV?|X} zKj^}76dpPY7LX)Oh@c~KdOV3*oCXmodExi5gG|Ct?j&L3@N)R;KSZk8KttoUd(-nT zo@XzC`!uuQ*-Q;Ve1KI_9dDXiW8922;y~A?07_#YX9}mW4CQ0t4Ew_Fj78yX$ z+e(iBK&aX~Dv^7oS|IqzFWXqU66UJPIO2u86VsEXd*V8?eO~t4jyNAf*OMqIggv-k0_koU$l)|*951X+LF4oIdK)i zdsMsi?yv8iOM}~%+OXo|guD`-=H>Rp;s<$yPojo6JRvP_^5p0p9xe62Ju5ezv*GLn zg}*9uvYyg!?PuvH!9pti9iY!T*SROQ$KI?iD~KCDO=~^!26G?X#FuKpS3)U8ZJPY` z0d-Oi+JSOq4DW|}v+0s*Kv0S8wlD7*=ha7N%oAOybEahE9TQa7B|q}iTwQcQU}V1w z^fcj}i^~4ymiBoC`>}9f;kMeeSXSCg_vj}|WemlBp>i|N#{mUuHh$*h{+OV)xd&&} zw388Y$o|;a>)>52>N?Yw6c?XGVfi_Xg+iZ}U7^ZtNiMdeu-P_!xa6R;u-m(<>^b)@51BcnY_+|L%|Lsi62gOAs6Jw1mRN)alS0 zB-sLuMU1ZN)v;7RNbALIZ!Zrpy2u6braLr~S`8n*DhD&&%e22mZDQBV8Gw9LuN^d| zrhBWRV*D&|;;hD&Ji@KfgfA)vPytq$lgj&6j_{vUiJFUm&djEWsbw#Gx9tCF^t(=Z zk`M|P0XBNTg9306{*1B0lo-GeI&Oel?Ad40bByV(Z^T-YPt_ZshDPpd*sr5GVO;T) z1Du1=mOP+WeYD&6Ojeq#fy;K9a|qQy;}dqn^hr89J}eG6C_{R-jC^x=7L|iil_RLK z|6V=_RZVrm%cFLDYunHeDYoSq{$QLKa3#AFHVF)0c@KC2N3kFn;_TeC-Vzv4lmj`n zY^83|$|CF4amG}K4^oQco4KDxr z@o~r&%;5d-8LZxcDCwT?0)x^j?lryB)6;!M*DvFr&=;hM1ONkxdqXsD`&e=7>Z_ki z!4-oKg;R{g6+1u$%#?k+^Rl+qmvycGFUY+BaVDYkLL|bYVKUUk=MIl38+U=x{kz_k zL$f6v!a2&sRAlaquX1~m9v`1`wIXH+@(Ht{Cla*VJ3-2(mk%w6{0-+R9!sop0@TuF z%StHu^)%Kars*I2$>DJIt$AVLO3t6SPX5-@oH@WV{)8{`K{chNyL5!VAAwMm6d^Kt z%gP`K)I+Xzsxo`U$Rx?UNHr8rjR;+o^W0|}c-^fyhW{pu5hGq0A(wd5!9&d&xRO%X ze3UB*+qu~}fTDm3L)M--R>F@PDZ>`AN!(FJDCQ^V8_=pszQFy%@tQ-k83Zmaiob$=pfJz*NodtT>PKtf zt&qx=?~K||0A5T*DR2pu62QGPH~1FI zL#VS(pDd|G2)db-)&E+YKl9xV`212$SL~Bi4MGmaPz_b9qBbz)PG=u5!}O)IIf?d5 z>J}V{mubiG6ZdmJ{-sp(SEE1z2odX`ks+e0oyPhNZOJ}#M73-AmU+bzjns<9NJ9r$ zk=i!&?r45}@`lk((^RG&T|u6QJ`QLueaqt0Ga8})-5{<-?} za{{r-Ha|leiGcI#CyC+fF~u2>#+papg_670r7bWds&=nl56mQW(Ym)>b7%$g4 z#)J$`T+=NL*b<3IE@A2|o0enVzYJNCcqqL`PY1>;oBSm9*KV0vP4AADY4BwiPS-xgp?Eba-SY zC63m;9oQL>i#&~G0);oyBzn@HC5IP3VvUgQJf)S!KHtXo?ooVHx~?>>*C#T3!M>m| z$Q4GCJ46@Mez@qPCRfglr1oO+GgXghfoyb(MTEpc++seyzvWIi$IfrpQDX*I(ArJd zt^4Hd(=mS`I2F;{nkVNkG91p5in5&?$!|jHTQzGiz*C`*q}-F1!4pIoJlKej1_Un{ z(by=i_>(74zEexokI$ei=)!{;wfNyq@L5>%ZO~bQlPYNa5~b*@B%1bWll;x0J~a0| zk8{^kv>WpHsPgaE7Ot*|C)n@xt#rsf*Nn%hf0wsK;i>tDnw~r8EjR@6L6>L-Wz!2} z$9_zVB`VI=$D`dItaP=v@E^evwS^%r|z>WuB+q4OVXQL`+T*NBH}FE zEdt9yLgFpL^$RxCzDl1#>ZSgfnfLkFbo8~7qO7UOej`XFC1YtvFX&D?HS%Vh-kGyH2)%~9rQxKi_07APQY8lKd>hC3ED%ip>JpvOGk zbjPVbb&eZsX*gbOyS!@UMb8)KWaMxE)%gjmj6rhw1-xl?zlSeT?&KIOYUb&6NWGgR zjtK!zd4ZM-;qZ4=*)ctsboIN@V&!A<0BEeja>-nqgtg%<{C#psIR~18ou@~@KtmZN z?JN&UzvAlC&prfdVYOar1tCC`|NZH#+4QVS+)yvsdRZi<>Z`$6y zJBgQ8Q#>mBk0b`U9}Ojt=X%5V?EOcuJn~qM~uo7|;7;McfPi~_zI+&SE@>d<5`gJZ`qJOQtpER*l=k?2X zxLUc6R=H|#5_-Qde1vgEzEzV(YaxXYA)`?^f9RZjEdMB(%$Lk%`!wJ2nS0Rwt$CrE z0{aY^s{;=BuO~V9w9iCVUwlFW2)_rs{S4 zj0qr>IWTHQ^{PpRiC}RorUv{~AG$gLI>vzx9<-YESPI@iZ;3HbMUX`P5DjP#g22(U zwS}>Hs$sWtp7uGv9baypKb~Ez-MMCEbM@Ze(z_&kM4}8B*u19=;oNh@Z2Ig zs!h&QS6Hqva6PbRvqc)u@+}l`7q>FOFzkwMXF&E;{lyDUCVOOsu$hL9sddpAU&Pyv z&x?C|cTSM+E%_D~f3gH6UrwLWn=|%0BM5eLeMH_k;t^Jpy-)ID zL8gn*9zekVkp5&8B9eH zR&qBJe0ZAsW8BYd>9T(8Z~=anzJ3S|`a)%rifR#c=bB@l8Y_uk8jpu5_3z(8qJRqx zbp)3~XKx~TS6@Z7+#kIr4=3xqZEVMkbtYzohS-0q>LuO# z=5RR>)KsFMM}OBp^Z3x`s1#n2;N$mHtG#&m6we|%O`ucn=F`glySTs6o~XCiQNIR- zrE&JqzEkIFfbTc$cYFg5WOy{3uI|E!P=}r;oXw=B3KHfLDqm=6vqU&gXW$ zG3z#A z83IGau3XOA+rxe7$Q&o_VD~G!LQCICuK3%xS$-^mG1o72a7G+M3E`;4@QSbAW0XVs z=WOytC<4s)h9{9-inN&Rj!qTMY#!mlPcz0Gpm%isceM-&vp=a}0niDO zCLmv()4R!O)tkwk7p~D%v~-@Pg5-x>*okC|Oi&mk>1(D1NH6``Jn_8Wh^4wB2Cobo zQuzi8p{1q8ch^!n`@Nne;nk83)coDddU01DtQ)X+K821{u6Ql>iTV68$}-)mRrv340yo5}n)B6T34bj|vCA24HT7Y- z-v?sE-9${S$5vsYUbIUhkK>v+(i`Z9(;h`|g+(iIymXJ`os40c`Lv&#et$U?3cN*s zb@gn??eDY5IeA|q4Stm`|j7qfuw4{;{nD&BNr$c+PwkqY6bw8W%% z6agqgIlQ8!+RXZ-KjH>t?rmw z81@jF`J|)ta@(nm?Nh>`&yp1EJW)-`jvl_u6a-Og>uLocjRPpvWu8t?Q*$fSuz(}?>x}NB zxwiOkB_-^-)mF3bA;Mzf^y;W4jJee?f@qoMInr-9F4$3pO3X(AL7{;;L|~v{qItd4 zA6{VzXMo28pTJw-o&*qa-ZC*6h5*b8nRp9+eFSc^T^Rt~lUe}r<>=_Br!zu!wzTHo z4kkRww`sU`2W#Hh0LvtAJ*!?kHwL)vK86nIm)if`Jet1lVIHn82M(qs$>01@P(0`0 z<`zGT#Hapj1nzRq;r8m`Wq+z7vQ%E5?PI#!tsQMO7Xp{mz81j~>M^^$5j@PU!_Tm6zYPC)oRqDVaeK+dp)# z&%ul3zZ=w4-{7@~GJi1%H>;895gOpB1; z2|fx-lIA{+j}}JE2>fQnz&JR4hYK}E_le#|&Kqq+OX~Bzpo&S^b;LVQ_W8~$B)4=2 zSCFsKi|K*P${%6({9GOwICX5*HRSM`d)z&-uXuq64~NDp6m8S*3{Xjeaq^#c*u>}P zi2v~dpxc5!VzWjQ-U!;|~c)2rzB?_)93@>WVUDWTbeen}*s+UlsnbY-$j zTz(UHFT0UkGJY#J5y=*qr?^JNCbW7cP+L!?^~;P;mXbIVZ39n&KKc7U3IigvM14K| zO2bL&7dzj6|8_r5`0`#+06LtQl{JB+kX-tke{20{;o8v9lJGn{A)GTMMNnIwBjpD< zX;bRnTf;1hxyh1cITda-><_=J;$WePa2cvjf!stuMj;Xs386{9*7X<3h~1IFxm5lQ z?8qdQ0{M_~*kN?7j`R;w#8Y@F+(c_{$U$e8Jk-Q5WC=c8`x{>>*Vc~K4eM{k z$G6mgA&+zWXAigg3AGDaw`*W zb}Be!GSd>Fs??#OkeQ>wp><4&B?XCkO|5|j2W|eH{Ya0dJ6E7*Eri+=y#y1i4S55!TD>t0J`^Dhm1SzCRyasQ_eezx&rHz+ zKgRtNQ(OD(%ysb%ipz>JmY(I>U?x{7ef{gauQJN_SHDx-jb{_}MmHNvMYvtL5u3f} zG$eiAiGf%Py5SXH#J4th$bkd75ob0F`%$#(UdrGb^BzDJwyRKZ5q%b=B5Z*Fh}xsclj+Lx(ZfG*b)L-`)S=a*hqV-MY`cLa5~RG|Fb?bOF$he9rBO!@R*S2fg$!%MX>DhU<^ z3aTiJqh)VBu>AY?48eho@|<*gg0_zsQhs=QdQRSy`VQeaG*65bkrZ&nF!c5kg)L#F2r2Mvx zDuzki=>r#oZ}oPaF%E3~V=BS*=ActH$FqABo`T`mB~MM@?~-O`A1U!u$N0RcF}{cu6?8G<{E>p`)(mMxa7yuofh+mm$(XIm0C@d=f^-&PLhRkYi8}0lt-P z{}vLS1_cORxJE$x-JB$n};Bmermo z-4yK;=ICztVSLkZnEF&#SL+SAd6avvQbc{PzVX>h^Y!ZvPPUOsCK~3$5Y2Bv1b#j! z^Wf3ec@1mJWusV#qvzr;>`J)k35l+YW<*CTbEMMP<1r_H!m~-Lb%8hl-y^%6fdQUw z4kxFI{l9t7veRxp(rNgE}?%c?ieVc3mvyBTzBzA>`x!GPDq_Zj-U@Sj)}5OjfD|R?A>)o=6#C zRaqh~6P#TNdDSG2*k2Jlf2X1$_1M+Tnwka;`p=sVpJcp$ns5SZ0J>4KR^bqAc^`a< z=zM3+wd4SQjeVzPOZ`-?tm($`dxI$_pZw1c-_u!OW|#$k#-891w{s-IYmahHzl{JTAS3i3|y>_Y5dFP>^r=H zO7d+14Ke)oaZmgGBmpFa=%R05DJJ3I-NJ2~^Hq)zb!>K^q_+0v@(Al5Sj=4!p>Cf} zvjEv>|6WIz5NGTY-b;zhWi#`VBD`@7RawyhEv-JHSGl^-3dI5|XI_?WbNQWNnRJh) z!?#1Ffl5nRamyl`zERGXL7lY1Zg}>-lqJ@N^=JJhn#+d5N6n%=B3K*O+1kzZ`}}R1 z8+KQ?L&CxIYz-6(9P{l?ubs1y=hp2TO||?9Wu?Up2X!uZoSImd;X$$EU%0?jRs^Q9 zy7>mzzlz98eoV+GtWWZAW;tE*VHr3s&r6J2lP*bTC1*bvu+?eO^3ymEy@y}s#D60;E*rMFy`24$ZC6hILLF-;w_g> zHJY1m(JvA2y&~R2-zyF%xh@@k3zSoW@#>CxGyMZkK4NBuikvF+H8sB#y@P$mbOebrSnbm4a!(S^Vh`NPkG9Kvpa^Q& zN50Pi*VPq$dn(9^<${yz4huv+T#0o03f>qPTG;if@cQXwjD^TyOm`X8!@nX!g(Fxq?E7ere zBGv;&1`G^ZhIDKgGs@?GBak#SfD2MXu`ndx?6WL$?GW5%RaqwD9S?qAe;@#F!5WOM zi;~Fa!`#cqW@khkbiIT2_sV=k zd|@T*5GB|tA@Ke>Xt_fYNs1FLnb-Ch&u}8*Mkeuj2fmg*Y1GF%?p*a}eD%Y%VRc;f zlws}YD*`GmH_@cO_$O$=Qa<))`1levya^b5vU?h?^lV$zZ69(hEaB|w`C64DA|vfx z@3EaY=OF>uG1nYy91z^>v}+BSdFo0V)#=3)?&0raveY(%0*2#)UL11 z<`|&h^?#|XfchEMhkig0CxqCc)i9(`9V+oUo=s>qK4QKl85V7uck$2L-h9jgX$bbm zU>0@WT5zcQ7b~6F5Jtg9$n&JY;1fy|1we>kwSOat)A&x!f~g!WfjRNV+W{Co=AWm~ z(hvz-M8(GjyVc)ET8rzx`^PO^&#`mReLUK}dKAaoJJKG0*{*YDa0NgR=%Dx?1~j~0 z=^**Q@^c`Z-Gl>PC4nqdLzgp%xmh#9#&wJ7Y~;ZD?l7EddUwravu{w`f%)VTEqCbT z&xm&%uT2^c4KSkGP5td#(sJ2YAG8RdBJ54OU^##|!qYh>mmhL(rC47x=DaE}&okvcKIveQ~(R(^4EB3lh2KVK!K zrbZMQ@*h3{wBNqXKJPKRa!%bZtGDGn_iw4r&Cthqw!>*^!j}yfGnmV&}{B=x!L)}`yrN|tFz2RZ9Rj`N=rpAkfGJrNrie^~NfA1@j zU5ue7b<{#DS_nq4=U`DPB_zZso^)alV*L8r#A<#0%H4_VdDWur3?poFo4HyOtJH z5Bj?`{nF;q&2Dxu^lsex&Rvp@Tg5ZGLvU$nC+HC-0K2>h>!BTkPD8C=Zf)bpN;$^T z17+jwHP!0&u8@koin-oI+pX>41$G(rPGaB!B8zmmiq1kxL8!Hx3 zub+4MN`|Xhi&O@R+xtGZ6-Te3f1I8N7$+Y9Q5pY(3T?W){K4Sl3i_F_<*yYl(s*ya z>?7|XuU(WWa83(J*(=T=7y5SrL!v`mLsp7*Fg`JHFYPCbBuVA~FTYcoK@-~_>%ZRb zmjx94njTAobP2_knBjNI{KJ8MM|`eDNSpYx|JUGa(v z!{5#Ko7H&x-x4Z?dJ8t*x7Jf&4j?13bj9q+3!Gvi+)_ z@7hP(@zDw*M*ffb=z-683$N5oCnQ-(aKgQGz;aZX3f;vruas3q<12hH?II>Mm3o|{ z=`%>o8t>nH?CL4UC+Ju0B8`3XwQ%3^!!fP73l5wgWKTitF2f;B$t~pO$M6`5IvDFnOhBrG_K7QA zE-&znNi%s;6rz!Y8~KLZ4Y}735kX&YN~(nXJgVS2p4u8aEkSAf;P^cT!dwa#?a`}3 zfAngSBR_!EbwXRfy~dkF7exh+Zx$-}D6yE|p@^*fwseIQ@lkrzkflTE+I&Sh!*N4e zpS~SNwrHhpI*bLv4DmFDvHM9+Vq6&5-*T`gFhBS^oco`0WDk;#a6v&{xgw*XQ*Ru9 zB^-6y{Qn`}@AZHN!9F{!N?mO(buZ`Epv4z+_lyvpw`TYV?B4J-d{i`~;}A*||M%An zQhULQMl*Bx^|N2OG8FP$S9)=Sh@&jl6m&Lx`!3;vJ?Hg1YjfhYb>rgezERb%nLXcH z)r+9zf$)kF{+gOHpE*x|jax=7`gS8h?YHowEIr_ZC$9d> zk8fh6MSv z;c;K1T@Gv(N^|ivm7xbh<>g^Z7X)bM-aLk9L;f7LiSOLqH9RgF200TYfCaen@WwX_ zt21_bqrR>R%LX>o=bc-ifes@x4^Tr`C^H9gL50`^DJs>bYnuNwrSUS+xD`zX_nNL# zU=P^oEBTknm=P9W{RV2nEH6aS7<*<9;pm;s$Hdhr`S&-_^2u1mQOlG(v{3P4eyo%_ z+U4smW9NHXK9q8C%2b=jB#lx;M> z{B`NF#qDC}xsL7!?Cd;RjW@1~PUBv;IlMY6*PYz#9-?_ppg3}iYbdQ6^UI0=($p;w z$0yUFBVm4&72gd|pjx~K;9-Co9~>#ik@j6})IQ%gcExRezO{hRs9qeOVRUlxZRk0N z5jRN%CR&dx24q7c548WEBwDOUOK1QzPY*dI&XdYcZ`XoP&g~hX)sPk=@NeJyZ;h2= zKfb*I|78g8=SWUTasY&}CY)scMn^jHmF<=OgH6?24nnb)h=2Y|hp&uTd`o!!etHtu zTRbb?E=7ilmY0-Nl>T_;`i<9T#*^J%gYD$9s^;d)UbzF#lpRB-4q(otG8nj@&}}h4 zRzf-qwI7qV)3q)#vVDL<*~P-`Z)~F1nc`DU%o%*d=qg)zvYc9KSq8No0@@b|kjeq- z9jD+LTjrE+_ugQx^*u&qIIPJJ1#Vh~#~dnZYlG6andwqJ$R1P@9CoS!0}fMV+s*IP z^#LkNBe1&^Le&Y|HsfGt%hi)#<3jCqFMYs8jPoi5Th!tk=OFFt|Id>+IOR_YKqo=; zp;F<@X*81(eDH|ln)g5JIIOWg6V~sTSzGJ2#?_L2I(BG|qz&ML_XC-4i-jan`$&q) z9G0`7?_YnN>|=jxF#mP7P>TM~1BQyA!LUgRi;}qh37@1L_55fOC;qMe#0P>?pL1udc9`LS?0HFO!DDNP z0?Ic=*Jj>NqoeBcyR^1ad3R2`Q>DnoEZ63G@gDc>XXAks-b?1$*Y5zfm^x^`?K`{?k6_?p<_r7zy z+_j_q*S{d>tzKS`1yuyj@Cx1H`MD)9uqmgHK^X zz)cJuDp_6kaT1HdRb!Zc1Oa|qmB%Lj;`**i?+Z;cWv5nFJ0KR5hYXPoXN01{xf5*| zJ^2y8Ucn2;$4#A2YORv9z31-WT*)4I2p0J;Q``sPc*-*H%B+SZkwez$iRH#D>!Kv| zxD(y-7aLjWZAce=f&!WBISb-J4DpEyBL-TZZi3wC%~_k*t2ylz{znF!rZPga7ixLi zls4bK*`4EuRNSKqpe*o~Xm;k{8vcy~!aAV9ZCF8qm+W$@ISnPB1H>}Ye z!5t_~7}$UVp`#1~dCsYt2_dQhj(+DayV?QmH>60%;F#yvU*#mpX=Dw@Q4;0y^M`94 z;=x(^B?VxN;-Pu|{;BO_guK3gVL`#- zrccY+J#dRve68U+BQ?~Y|F@yGuyAj%uFkV4@mq|H>yK?_=O5#ZKLmuHkfqJcn}5mf zvIHfAcJI~Sjg@3gh1o|zV9&>xrzJIi#WZ_;gT6Ua+<@v{w&V}0TsF>M%_x*^^BRq^ zk4$vEJ#Y_iudcxfo}!v>$1k5Gfg0T|QZ7^Uk&u3odZ%@h({&Jh4+Ix17k4`#EnGq_ z0{PU5YuGhj1wj~s8o!H4m`PalNb#G$=O{cW|0bs!DuhS%Zp(xR!To3Tl%I~+hDuwc zM;(ymqNA>V(jO!_`h!wz?4c&7r7lnlL(abGub>dp4Bp>B1b>L4!OYDYK0UGQwy1<$ z(EnIY>UjH%TZ(xbHcJ)HQYjAtU>tqb;&yk#eH^%~FhLvv`6!tT$4f`NO&S$9MudEl z7z%61Ov9uUz}?H8A6z1^*IECZ8>DG1L63VtfO!iNTG_Mh%$!L&6yC*8Co!=k#I1{&0-% zW%%#;dilQ8*JEF#QSW5B>2UQ?c6YxWl|+)^obq4t?iSvd^LZxfILv0pg?={RLvfml zbo~;R7uR{Cla&hCFxKMnD7t0vY#Dcw{q)&J%8FsyG+69y6r_>;E~z8C-`Y%G2~C?P zFpUJ&JGab*$FjcnJRB$u!&86eF-EV{qc9Z?FgrYh#6Fbql4bZN^ZrbS zb77$b^m`JwyVCgkx*un1;~1K6gvL^7QTJP262i08&)oadh8(#)l=Fr`^M(}xFSCFK z=sEFEY@z@DAMKSbvcEC%x23M`Y#x}PA7%reU`J8kHnqx&+=TG`@W;5*JyRRXjkWcI zjkCT6P)cBAPs9o=5ej2@y9EKnJAlx^UY{RgBA5#xaP#TvQ~805^WnTnYrm(k!ph?o z>8$+y2U5^5m?)Ydp>;y*tx6?IgZCo=f7A=sl2*tLzDW%Xr5D8~c1021s^6gq_vNLU zy$CQi=GN2oonwI+KAtoOL|z{W%vau{cAP6e;MJyAAYOG`g>~$@=|}#8$|g^cFWiHO zaM=IXavzm?J!(VWIrTb8Q}*xQn_A;SX$k`Ph*G$UV!@3t#uCQr9269ZxHgfnpw9{~ zX4F98g2K;eH~bZX9pFsZS-cF}7h|GRrNi5x1K4_aF$>jalO^fmRq1U%2ueLGQlJ7I zD7KXtnbR?kH-j|3vBIPDJ%V(VkLaOXtmCic!@s+lUvofHpkm1B^|^ndX&xfj6=65b z=Dwg1VI+&$FULS$jHm}%UUgt5{1_KeVPw*sr^pXMT#T@)>w9_q=2jO*>SZRWAjOBL zEBQ2iDl!CKca$XEN`eP`(3VS|^hYD#tMZ%ObZH~y>y`uaXyO>HK-T$Iryb!Oe`ieg zkEj_a76~XD>-^saSsEv0#^Oj3ElQ7#<@ZagC609M52GZte?-;R;qvraJbO*uwBW0O zsV}_2-u;P9ecd970MV{vKeVyK(ab;3X^$wbs9@jTvU9F^+rF8n)(p-Te`9E?mZtHfQVl*WVJ+b~W$pb>&uk3OBOceTrn!Cv>WFpmXo2^WB-)C4;49C-?4- zgZQ{>rZI{}98Kgh|%#TkAzhleUov$Qk0BJ_|(r-zjb>jo| z3F;+R&7zfOyx7`6Yp^pd4g|g|kfxyXS>du?#kv{ef4l$=mj+@9dC^_s&HEpYQRih1 zMY3-&ql_`%WrHa!dCvKb!_%>YkZtB*?2X@BTTRV4v{$trn=>j%#dR`7bJoWu^ckwHXS;F?1Wo#Z-m& zP$k1A(ct1m>ea^Fz5SF4TX^5kq~N-9)7FESJo9P6CM&(}5i(y3-mkP}@DL5tm@^h+ zr^!JKSWRC!9h(IcH1QDw_{A<`U9hyaa9qQjRGXU_qmBz{2^mq2GUuoc{P|x@ePvWs4b<+LA*DN|hLG-(9F=Y<>Fxn(X&4ZY?vRjBQVBsqkOoQV z7&-(&KvIx~dw9S5t$XJuKUiyK&g}iv-uvfI{8v1p-oz-E_UB#HKr? zw;(ZNNh4-Qsp#<;7*0oukp{@P15<@3*p!ep;gC&oo9PVjVhDH4xpK z26^}5I9fC@$(5IfjwWL=aA+|b^yz$qLglv)kf&BWqse8%Lr(rcj7I)l_#|r!!S5c> z^lHTBvkB0Zk_lodUr`N|N~aM8BPVz)P3o(~?ZBZQQb?yf^6~H22R>j=rT#4YB_jmH zS*hCP4DIy@#R{RL^N<9Vy=@>NlL+FLU!OfaZ*;r$lz=6kR;<>Bb zLD@fceL2&G0hXXQU_H7r{$vR7CH8S8qNUiMZbh_%e5RrK<(_h{Md+5E+0NCu8AvOY zdsmMb;limkS5V&ykUM2Ik&T&ymBg1*@6N68(*u0&^z^NuE~9{yBGQI`)YQ@9iYoKs zHc@;3LL|4nZeHj4+y>8J%3_@pj;fk$U-4wRa$!f-Wh#r8gCP1Lfb#Z{mW-KdC2{>! zru6kfBfpUD{x^~~yrW=!zM%W@Rx_gNk!De=(ob_PF48v*b;98}YtNQOyi>8D*@`>8 zx*v~bKVWl`s^jNk zGt}hS4nP75r34840VtY@COgzp3<2N5b#=~7$AuIU&FDt$m~D%KD#z+8 zj84}Z0q%a;`xc1yngHN`-em)iXBjeS6jjfD!^#Z}ivY!$N8?zi7cTDzfWuEuUp&XY z5V$oCZC?0!RNseI!TfGZitj=Z;qE74dgoh+z-zU>FN~O>`>Y~^?p|`U;VilpQm(z0 zNYCV=Q8XvWd22ka>dWnShj$X|etRpaUINH(LRl$7ASoZukq%kb@);1R>5(HzWepJT z5J%Pn6fLB+W7k$SE2@;Az^eY0aqN^O_|H{*$nLEWKfglBRpK{mkIb|u*tlQDlv zcV)$Gw*w(ZabE+M?`z=ea1XfU?=HLJoY6M8;Gbjdz}dNIV-~!4IbZEU-iJd&_V`>p zd(BZ(!f;m=J0ppuqu9fz4O`PwQ|Wc}^>+ktHj>U4-MT%?>weK_g4Bxy1rNtSQu&M! zkMUFrrOpEUz&!lo<$rTxdhVlopC&sZE4bkT1|ip`6LY4f|MD6dn(nq6-uu9-o*gwS z&sYhTE8qKFV-`R)+Ieh|xw4%lRqf%e)WJ0l;`Ie`vvd+)c6odyv9B}{T}=e(gk*p{ zruqB<01@>UFy1bs74#X`q<08Xeh48oFyUmK?|J%aOi3*9!2CTf;BR)^&hidt zl}h;+K%eYezP{QBGA*>=(EH4c>dpcQuUY+0wwGouGv&u5hY|0^K-b?;AZgP>@Enuh z1{3+o|9ygKy1AC0x9zVcF+*by2=V~(di1PievXy(SG*-VUJ{r`1~b8`z%@UGBxvnd zqNI&}XJ1D8WerR0w<_|Y3uNcJiCzrYs{vkN;Y*(H&Nzj%tuPvDANS8EPzHvWnS!Ac zJ{DK#2?%|7HHss}PEZ2LGW? z__~^;A7V*Au>A-hrEZ4%djhoQmJEL>4(mM_Qfg7L%BnZ8i~GB?+G$!HakuBA2I9W+-uTOt zk|Yd-K!zM8@{Q7P>i<~&=7xkI$RQNP%vAdK7YIU9+6#{7zoE?l;|d(In)O7+akhO{ z1G{lG=9x+Sm<$;I>l%ngoNY9}1UnMv%S;==tkS>MLxt~%&`W3lC2l@v)&ns9vwULK zB!Oyb*dx6-XjDe-078r2q@R>zFlQq!4!)LN{`~3;A`}~5st0INCIurUlWz&9mVWu| zCM0_aME8N2qkgIYN7bR+cFN|i(3d??+usyFZ1RyPY@t!&%;&<${HvRlq}u+;P5ej_ z4y{X65w}c~9y}PvYB~A3C~bIE3CLg_Uh95r(-gI!by%jYEfDVr>@nwj?i+gX#)|R$cdBm*fAFM_U8N~7j-$5L%8yp|oW zJQisXe!S7Bz>wLXh$_~Tm*7)g7=D-GCFkk+j7hY2%$xL)>bn=o?3w(HMMb+G-E<5N zz?k@+rc!X67lF&mJi6dlr!gD}yZXVj9mN3s4;#0Wo$2_X+AmqVGiG?Jg34b&45NxZK3-eb}aW%Kc_Kj30gITisz zQib_ILQlddCpy^1uW~e8bC<=OJC4`hG zSxFkRQX6xQ`|I!N>DjFHdJzP!lpTdBF&;YBW%jM78J^V9K}D+tZUVpI1$_|umfgOr zpFLK34D&6xKEab)B^D7L03+nV9w5delc(uH9c}vrX-iJto!#BJx-b7xxobbq$1MH8 zyz%|mDSGGt6*GD$2Zt^`IK!hXr~?EUYLgd~0-n?s`^v?3?Pg`oG06g#0?16A@-Zx^N+yeNY_e3>ocY3aTpuJ!pD#ljl}lJw1tK34uqGs{ z;9p(9bFUXnB&4q;MiFl#M<+EC`Ye1|u^yX!|iib{(#vD0ni^YaE?q3BC; z0EMb}*;s_LDiNb)9L4wb`qH9FOVqB`L0N^`LnodDv_D&)_rAYZOud#T%zO_%_2Yj@ z2vu8{bK<4IEYba7&;H6WApvzt>wC~hPZ?I&0?t(kQKxO$**nc9Vn+z%9q!c4XTF!1 z9BIB%hG@T`52EvML>9=4cr(u3q=J{n?91};rS5Xo8I{1m4&?h zf(xw?3V`tCg$Zxc69!St0OeWSrldJ#5)D>?r#NHdYwVPw(Y7fW$vM697as-e3}vDm z;Q>BwQfxl2oMcnb&(p#$dM%!S@X*PnwE;wJ``DwZ^jwY<1<@p#N)kc$^#HH09rOhr zt*~s$O1G;1Z!58{{qf5fu5N>E%@p{HK66F2H@itGI

BLAI(*906v@ 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

~ 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

m=Jj~D?x2o_96Z( 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 + + + + +LLVM: LLDB.h Source File + + +

LLDB API Documentation

+ +
+ + +

+
+
+
LLDB.h
+
+
+Go to the documentation of this file.
1 //===-- LLDB.h --------------------------------------------------*- C++ -*-===//
+
2 //
+
3 // The LLVM Compiler Infrastructure
+
4 //
+
5 // This file is distributed under the University of Illinois Open Source
+
6 // License. See LICENSE.TXT for details.
+
7 //
+
8 //===----------------------------------------------------------------------===//
+
9 
+
10 #ifndef LLDB_LLDB_h_
+
11 #define LLDB_LLDB_h_
+
12 
+
13 // C Includes
+
14 // C++ Includes
+
15 // Other libraries and framework includes
+
16 // Project includes
+
17 #include "lldb/API/SBDefines.h"
+
18 #include "lldb/API/SBAddress.h"
+
19 #include "lldb/API/SBBlock.h"
+
20 #include "lldb/API/SBBreakpoint.h"
+ +
22 #include "lldb/API/SBBroadcaster.h"
+ + + +
26 #include "lldb/API/SBCompileUnit.h"
+
27 #include "lldb/API/SBData.h"
+
28 #include "lldb/API/SBDebugger.h"
+
29 #include "lldb/API/SBDeclaration.h"
+
30 #include "lldb/API/SBError.h"
+
31 #include "lldb/API/SBEvent.h"
+
32 #include "lldb/API/SBFileSpec.h"
+
33 #include "lldb/API/SBFrame.h"
+
34 #include "lldb/API/SBFunction.h"
+
35 #include "lldb/API/SBHostOS.h"
+
36 #include "lldb/API/SBInputReader.h"
+
37 #include "lldb/API/SBInstruction.h"
+ +
39 #include "lldb/API/SBLineEntry.h"
+
40 #include "lldb/API/SBListener.h"
+
41 #include "lldb/API/SBModule.h"
+
42 #include "lldb/API/SBProcess.h"
+ +
44 #include "lldb/API/SBStream.h"
+
45 #include "lldb/API/SBStringList.h"
+
46 #include "lldb/API/SBSymbol.h"
+ +
48 #include "lldb/API/SBTarget.h"
+
49 #include "lldb/API/SBThread.h"
+
50 #include "lldb/API/SBType.h"
+
51 #include "lldb/API/SBValue.h"
+
52 #include "lldb/API/SBValueList.h"
+
53 
+
54 #endif // LLDB_LLDB_h_
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBAddress_8h.html b/www/cpp_reference/html/SBAddress_8h.html new file mode 100644 index 000000000000..fa68a49ac415 --- /dev/null +++ b/www/cpp_reference/html/SBAddress_8h.html @@ -0,0 +1,75 @@ + + + + + +LLVM: SBAddress.h File Reference + + +

LLDB API Documentation

+ + + + +
+
+ +
+
SBAddress.h File Reference
+
+
+
#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBModule.h"
+
+Include dependency graph for SBAddress.h:
+
+
+ + +
+
+This graph shows which files directly or indirectly include this file:
+
+
+ + +
+
+

Go to the source code of this file.

+ + + +

+Classes

class  lldb::SBAddress
+ + +

+Namespaces

namespace  lldb
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBAddress_8h__dep__incl.map b/www/cpp_reference/html/SBAddress_8h__dep__incl.map new file mode 100644 index 000000000000..2334bcd58570 --- /dev/null +++ b/www/cpp_reference/html/SBAddress_8h__dep__incl.map @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/www/cpp_reference/html/SBAddress_8h__dep__incl.md5 b/www/cpp_reference/html/SBAddress_8h__dep__incl.md5 new file mode 100644 index 000000000000..5a0d67a72a41 --- /dev/null +++ b/www/cpp_reference/html/SBAddress_8h__dep__incl.md5 @@ -0,0 +1 @@ +499d855ef1d02aef5f6ad1874547cf70 \ No newline at end of file diff --git a/www/cpp_reference/html/SBAddress_8h__dep__incl.png b/www/cpp_reference/html/SBAddress_8h__dep__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..07b670c3199f41c520c6509f1221d35e16e51d30 GIT binary patch literal 72494 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�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 literal 0 HcmV?d00001 diff --git a/www/cpp_reference/html/SBAddress_8h__incl.map b/www/cpp_reference/html/SBAddress_8h__incl.map new file mode 100644 index 000000000000..f2d44809cc0e --- /dev/null +++ b/www/cpp_reference/html/SBAddress_8h__incl.map @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/www/cpp_reference/html/SBAddress_8h__incl.md5 b/www/cpp_reference/html/SBAddress_8h__incl.md5 new file mode 100644 index 000000000000..f6453bf90bd3 --- /dev/null +++ b/www/cpp_reference/html/SBAddress_8h__incl.md5 @@ -0,0 +1 @@ +f9fa7157c5eb8a669cfde07c25747d38 \ No newline at end of file diff --git a/www/cpp_reference/html/SBAddress_8h__incl.png b/www/cpp_reference/html/SBAddress_8h__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..4094f9d3908c206c416b0e92323f1ef84e1d3ee0 GIT binary patch literal 230920 zcmcHgcT`i~^92mwgpRb(m1;yv5D}%7lpn#*}pVXSnSODM#)KH3gUWp4s{(ko?-wN-KxECC}5OsKw z#5?4h$NMlsxiRR1bozzQDl6<~xwNN#jO)GB?o8Le•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

tJFP&lI=G<9=`4^73=m1=h=CPuTKe^>7M{s-uq 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�ra^=m?G<;`f&T=h;{pNfi6 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 aiE}pfsc=8!=3F`!(A1T+L8VHTFa8gTxBlz^ literal 0 HcmV?d00001 diff --git a/www/cpp_reference/html/SBAddress_8h_source.html b/www/cpp_reference/html/SBAddress_8h_source.html new file mode 100644 index 000000000000..be836ca7b0c4 --- /dev/null +++ b/www/cpp_reference/html/SBAddress_8h_source.html @@ -0,0 +1,195 @@ + + + + + +LLVM: SBAddress.h Source File + + +

LLDB API Documentation

+ + + + + +
+
+
SBAddress.h
+
+
+Go to the documentation of this file.
1 //===-- SBAddress.h ---------------------------------------------*- C++ -*-===//
+
2 //
+
3 // The LLVM Compiler Infrastructure
+
4 //
+
5 // This file is distributed under the University of Illinois Open Source
+
6 // License. See LICENSE.TXT for details.
+
7 //
+
8 //===----------------------------------------------------------------------===//
+
9 
+
10 #ifndef LLDB_SBAddress_h_
+
11 #define LLDB_SBAddress_h_
+
12 
+
13 #include "lldb/API/SBDefines.h"
+
14 #include "lldb/API/SBModule.h"
+
15 
+
16 namespace lldb {
+
17 
+
18 class SBAddress
+
19 {
+
20 public:
+
21 
+
22  SBAddress ();
+
23 
+
24  SBAddress (const lldb::SBAddress &rhs);
+
25 
+
26  SBAddress (lldb::SBSection section, lldb::addr_t offset);
+
27 
+
28  // Create an address by resolving a load address using the supplied target
+
29  SBAddress (lldb::addr_t load_addr, lldb::SBTarget &target);
+
30 
+
31  ~SBAddress ();
+
32 
+
33  const lldb::SBAddress &
+
34  operator = (const lldb::SBAddress &rhs);
+
35 
+
36  bool
+
37  IsValid () const;
+
38 
+
39  void
+
40  Clear ();
+
41 
+
42  addr_t
+
43  GetFileAddress () const;
+
44 
+
45  addr_t
+
46  GetLoadAddress (const lldb::SBTarget &target) const;
+
47 
+
48  void
+
49  SetAddress (lldb::SBSection section, lldb::addr_t offset);
+
50 
+
51  void
+
52  SetLoadAddress (lldb::addr_t load_addr,
+
53  lldb::SBTarget &target);
+
54  bool
+
55  OffsetAddress (addr_t offset);
+
56 
+
57  bool
+
58  GetDescription (lldb::SBStream &description);
+
59 
+
60  // The following queries can lookup symbol information for a given address.
+
61  // An address might refer to code or data from an existing module, or it
+
62  // might refer to something on the stack or heap. The following functions
+
63  // will only return valid values if the address has been resolved to a code
+
64  // or data address using "void SBAddress::SetLoadAddress(...)" or
+
65  // "lldb::SBAddress SBTarget::ResolveLoadAddress (...)".
+ +
67  GetSymbolContext (uint32_t resolve_scope);
+
68 
+
69 
+
70  // The following functions grab individual objects for a given address and
+
71  // are less efficient if you want more than one symbol related objects.
+
72  // Use one of the following when you want multiple debug symbol related
+
73  // objects for an address:
+
74  // lldb::SBSymbolContext SBAddress::GetSymbolContext (uint32_t resolve_scope);
+
75  // lldb::SBSymbolContext SBTarget::ResolveSymbolContextForAddress (const SBAddress &addr, uint32_t resolve_scope);
+
76  // One or more bits from the SymbolContextItem enumerations can be logically
+
77  // OR'ed together to more efficiently retrieve multiple symbol objects.
+
78 
+ +
80  GetSection ();
+
81 
+
82  lldb::addr_t
+
83  GetOffset ();
+
84 
+ +
86  GetModule ();
+
87 
+ +
89  GetCompileUnit ();
+
90 
+ +
92  GetFunction ();
+
93 
+ +
95  GetBlock ();
+
96 
+ +
98  GetSymbol ();
+
99 
+ +
101  GetLineEntry ();
+
102 
+
103  lldb::AddressClass
+
104  GetAddressClass ();
+
105 
+
106 protected:
+
107 
+
108  friend class SBBlock;
+
109  friend class SBBreakpointLocation;
+
110  friend class SBFrame;
+
111  friend class SBFunction;
+
112  friend class SBLineEntry;
+
113  friend class SBInstruction;
+
114  friend class SBModule;
+
115  friend class SBSection;
+
116  friend class SBSymbol;
+
117  friend class SBSymbolContext;
+
118  friend class SBTarget;
+
119  friend class SBThread;
+
120  friend class SBValue;
+
121 
+
122  lldb_private::Address *
+
123  operator->();
+
124 
+
125  const lldb_private::Address *
+
126  operator->() const;
+
127 
+
128  lldb_private::Address *
+
129  get ();
+
130 
+
131  lldb_private::Address &
+
132  ref();
+
133 
+
134  const lldb_private::Address &
+
135  ref() const;
+
136 
+
137  SBAddress (const lldb_private::Address *lldb_object_ptr);
+
138 
+
139  void
+
140  SetAddress (const lldb_private::Address *lldb_object_ptr);
+
141 
+
142 private:
+
143 
+
144  std::unique_ptr<lldb_private::Address> m_opaque_ap;
+
145 };
+
146 
+
147 
+
148 } // namespace lldb
+
149 
+
150 #endif // LLDB_SBAddress_h_
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBBlock_8h.html b/www/cpp_reference/html/SBBlock_8h.html new file mode 100644 index 000000000000..7e3091331eb6 --- /dev/null +++ b/www/cpp_reference/html/SBBlock_8h.html @@ -0,0 +1,77 @@ + + + + + +LLVM: SBBlock.h File Reference + + +

LLDB API Documentation

+ + + + + +
+ +
+
SBBlock.h File Reference
+
+
+
+Include dependency graph for SBBlock.h:
+
+
+ + +
+
+This graph shows which files directly or indirectly include this file:
+
+
+ + +
+
+

Go to the source code of this file.

+ + + +

+Classes

class  lldb::SBBlock
+ + +

+Namespaces

namespace  lldb
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBBlock_8h__dep__incl.map b/www/cpp_reference/html/SBBlock_8h__dep__incl.map new file mode 100644 index 000000000000..b1cc8a2fa877 --- /dev/null +++ b/www/cpp_reference/html/SBBlock_8h__dep__incl.map @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/www/cpp_reference/html/SBBlock_8h__dep__incl.md5 b/www/cpp_reference/html/SBBlock_8h__dep__incl.md5 new file mode 100644 index 000000000000..983eb10f70ab --- /dev/null +++ b/www/cpp_reference/html/SBBlock_8h__dep__incl.md5 @@ -0,0 +1 @@ +6c8c0c68e994f49d3128e0d962eacac2 \ No newline at end of file diff --git a/www/cpp_reference/html/SBBlock_8h__dep__incl.png b/www/cpp_reference/html/SBBlock_8h__dep__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..07eac60ad441607054f633c239794c7d576457e8 GIT binary patch literal 68310 zcma%ibyQT}7cU{9G^ijY4yAN=${-yALx+HLcc(}<4qeg=-O?!1E!{0JbT_;^et&Ph zf8TPcYbp2MefHV=v-kPz&k0jjl)`yN`V0vP2}ed6tb&Aua*l+Ae1(Y){7XL<;V|$Q zx{;g|80iu5E4#HY0SSo;Nd_#g=AOQ{=;5uld46>~(#UB@^qJntE+;2ny{pWnVydkE z!001bC(e=)@|@#~dRZNJz4~sqJW>*?rCteS!qTC5txY4pu3pnh@5;jQ@JX!X=cg%+ zTbDiZ&*NwZKGO|&wClGF${z@g$k41Vm<>BovUQUfN3#F_{SmGpOX1I+5tJz4&(iIC ziFgq0IkO8=xAV)M*M36OQqfEh$GZlPsa}VzN&2mx+_$8NcRAas z`x?s&x<6cR@t+QI&YC3Yk+6M2fCi{WLEEh@rSFyDOIH0so_Xue-(f_v4m}j@ zgzH1R%Y(}g7rX_E88=XO#ve%sb_b&eG5^`4lmAM4Rp*Sx*|w%7#NlKoG6GgWyi z@BR*F3^cUg+qll#@S@{U-a&T>!+s}GMqp0sI^jgG*KU*gdmS`}6lVFR%CR&C`QG2K zyTH}To8Sz-xAV&<9jJIm2#*aIJj92GhmVN*t^{C&f92}&ZX>oKCX$J|&inL18eMqcy!wAin{|Fd(J)b9cg zYV)}TfwDv|r*!P9wlcjOr}f-y&aB7;oVT}(5xZ1iT$#6&?8@eQ=dSN_J~{Flk5VWZ z@W$xqXywm5N=KLD*m?5#KUTrG6yb-rS)#asE2oVZzz$xo(`ETw%qFSdmXwsp&=3ke z-ksi^W<4&f+$9bo5|tw6UtXZ%^7-$UH%Ct!_+==GTnAmBZaMAOP5bJTj^|kr?U`ly zbmvG$B=bAbQGbZM^5$-7oJJz|-2T1*gj);X%!#Qf6;;(vAfF_Fpy$6k>TEop(z%{) zf9P84iV$UeYH4nMb$2f8c}Ddzf*tUyO7Zg)d31k%r@!8t!5xS-QbgE%<1V1M+cOi* zbvs;=&k%r0M!aOVUlDU%bkSgG-m7NSYq+4B$rSeDaNLlFm6WvCk}joNAt?&%(5RPb zcUAbFm$16DBl8{qCbF=$-uNTOYBF1GV%>T^rR#gWx$7f^2y-Fee-SB~?!FeL>pE+q z>%RK+N4ZYy;lH$eHnJqeyV!lSI@$Vn#0*C*B(3d+@oX)E^)C?IOwO8%{WT*Z1GGuH zC>PB=fYtBb0%^S14WE>Yanw5itUwx;0^Nvg_rO$k`A*_Oy`SLJxwuUGs=zh>yipJ@aLS51jk~+kc;PW%3yMci`t8~oi6J*R3h6_uogV;vz*G3wTOV+97_a^ktz2{!F3reA0FeNV99*G+-RO5-rr}#G@w9{# zGs!Elp!@NijXXB%nKMR2N*c@l?zfXq;&%M(5<@GOxc4g9F?QX4CH%$SI-m8G8oe0w z1Hn+BQ@~+G3$7{8OGbTQKf9Bss`HkdUqPW^ONPB|hP`h<$4 zoyt9MVI{j(qqT?@8s0mf%?g9RNQrFuVgB(%uIGD`?Hc#!ecz=l@%N9A#1ihj_YNx9CsAOSxwHUUAue{+qN0+Xh)YI|IgPrjt06sP&9xtLbV z)rbwL6ixri&z>5iuC!9^b49~Ioe{2m?n=ZWC}u@V3nYNFHZtgWC#d|JUk%)jXU+JF z1Coj210ozNw6ZF^ub?&VHae!3bk_`u5mUUp-yqt5?mGrlgIRq#qGGf$`GQiT)n6rl z$|$U^j^O$@IC570;{mDSU(nMz zAC3X16Iog%9KHalBX;(AChd8_hEdg(J;5t9&*znRm9Ki|VS}#ZnL#gDZyyGs`O!7h zPKz*Jf4hL}{sm`&Iw`|$VKc_wB`oVghQh+}J})fxaztD`8;JK*C4BKh)wR#G(-_Q$ zK7&(>yx{@yc+Y+7?sH%3mn6_VoKwxg4oaK!ej1OGW0-uc?Q=E+^vO7ICC;fAH!A}Gr*#O;&1+PT#@5O2k{x=5dHLS|$ zLRcsEBpl6cBG=*;)O)0W3m(BFRToa68-wMF+44~*5B*7AG1KC;;^4x(@o`G#Gjc@c zTJQr&Cf<0ku?SAjg>1eZwL@W)+^Ay1e(jpRo!jO&l%vu!1O1jn@9IeIW95^D)oEXN z@zxktDt|U?>Oy)%6uTo{xuz@VF^#|+WjVoo>PX6faQ)hOKn zaN_%YivzGz{&YsR)dJez|2tIvgA!pJk+>U@hzhiw4;SzHA$-evv;Hm3%M#cN0=w3% z=axRAb+r5L_Ue+7-qY5JTA(xHxP<2SBYuGE3OIq(=h6X8w1ITKhV_$-Qh?oG|KP+n zQP=V6NlPp#3OytWZfU8q`ZYRR*yal_*?%QvVq&CB;+O`bEMOqc8}BQ24kU61Ff6$o zz@EVmHi!h^A$wH_PbUms#T65_#|tXFdZ=y8kFF3LFy-%aXw&u_r_l(^rAOXRotU1Y zBS!6U*I)A=! z?E@nlqs|&W&g92wy=tV-E8P|2N`W1$tW%GiBN2yW!3&JSdxb3-43b`NzJ9FQ5g4$0FyDX<;U(??AW-q!lxt10RyEx%il z+OmYcPgHRoxBPxPIXJQ_3b zK}o{p#|}ZHMOsr+@gXjSfWu*RH6?EeJU5fLec}@1uKVEu(SCh3mhmCxLewH95iQr! z&`iqURX2!-0ho2fw9(~fVCs!Zk6BaJSFDO#Y?9rku&3FaLUyyZW7{c(-e+Uyak}ZAJ+SFvBWE_H=f=h)N5BTi(H9@P{P-(RT}BMsvPg9_c=z8iednc+ z#-}j|1#>hq9wAzZs~xcGzU<+*21m2v}n_lkq83-gjTBriPr8Lxjbk4dfv; zpEmW{M4e2Eb2$r`y+zM4Mk3ma`|smITL>Z`QHqFIchN<}x|xxT4+amSTVT}q&|S>J zF9oSbGcD;4*l|8-ROvC5e{<~%Ka>bgZ*bC@O`VtAFc~Rn>bG^5L<4#p09-C+Ka;jPOj zgxyl6%*=vF(@}PZO~>=(A#RczZwY(}g2&1NsjeC4xy+k#Z*EdUB-4c@N${giK6qSz zYn7u&K!i!0mWsjq7cUapcbkrh0~!MM#iFqSAE>=Wxm(*F;4ku{N0L=wC22RdK(4of z-G>~0nF+H#jQ=l3)YN@dz1KX#a@Ef7*ghLTJo+7e9bWkkMdafSCP$lQ2;4q8EnZPh zjVu8xyk^iTX3f!L^3C4u&eE7{M|!hu2>2CWZaZrBEnMhT!C;^!lSr3)hzz< zFBA#o;1N_W<@R=(j_JYfA0VV+7kGga58}$F?#0HO1vTcd8Vq#F+$g(mNgDf*|DrGY zSNx6Z^9acPYSS@4=!Ta7WVpSTph%DRG;jk8@OScXQSmUP`=Y}J|Njfw=nhNxyf^~y z*)|wf@Ky|${Y^@3L_-`q)UGX<4BtVmwv<1mHpOj$e&KHsO2x_<0~wXj1+-T2p2PDGzucjmok)Z0 z6$@3Tb>8{2cYV}I@*H=V+WX!g620D{tFP~(R&0!LU-(e`je2+&-RspRs9k^}Mx#XD zu~nOalcbG}BLv~U=Him(iv%iZAA_LcXllQ(F2Z(*GqyV&qAY1XncK8eRSueYNy*aNe92?~~|Ejq}>j1r$f10xU}x zTG=*4^K?6VpP%>Painm2p*p?s{&`9$%H$%C$Q7@!Zvy_&=D$N_guTw^E~h~Y=zp^N z!*-nE!Ahb&lRQ6DaZ3!^eY%mD3?AU6Dm{Nbmg-*Lq>hNpuPp&5Svz#kp}%Q|%mYd7 zH@g3!<+CB4{_@-$P}@lO3rC{u#1&nVC-;RCE(HiTYFnxEWc)=LDY?ml>27%(?^8(m zT4%rz6^2&bH$g)yg=7Or+RoS{vE{zc{cV&_`toScx}9-=O{&4fC~^Em0_T(nc@X*^ zu)eR#qqJXytpleCO8aD3G)~}z0~WD9lm!UWs><4q;2LAQrkw1x-hTmC53iSL z-J<=#&2s@DKSoKg6uLMZBjRO#ph}NPq=jF^OTEU0ORE?N^i_vXCUHf#Vm3DbCt)tl zQPrIx8kd&lnm1mM4D6e1Y3KL&c`u;?EXMLxn>?dUwv)G1)4&;EKj$O=B+n?oIqgI? zP0F@zLH4IdEB9ATy9qswo2saa8riWsjV1Yn(k7)E7ET5O@}v6@QC4hw z;(8iXt!Z7e9KOvD5?Y$kyilFToZs0;m4D}gPa%^fBItp{sPQns>cT`ZsI~%C&{NYF z@sR3qphVYu5j;x`-!=2r44OfdtHA9O#&ZA$8T1-P$O#lew4)&SPZdNu2e9_ z;(!=nT_(J#;LBWChAO}+vMvs#!EI>OqYnLlyN$w7=lFRLQT(at3|I+mTLsMR??c?w z>&eOdB_Qq!J}Dv{e2;jf;IS#_&|F#i+x7_&e;TDg)JCG;Ti{PD02LgOb7+4gHQ@B$ z8f|kvKpvktx{YMsyVV3Ok&Kfcw(_>V!Bi0?T)m`f*b-a^e85hjowVz#`mf%c+m6~H zuYaNPkU}KBvjF_I*b)djk`2$12)oT&M*5DyZWRnuV6ml2N!rD3i!g~AEgz!KY?V^@ zc|axau1!6hUcmOuW5_kG8loiQJGy)L&(m62ED00Mwjkt_%Fq_lu%iqr>HES7_E*LN zqRSO2M8VZrp|7Ddqjg+`rj(cWd6+4ZD+d`=9; z%uVgG#c6KCcj;VN|K;W2oPdjpC;*m?Tj##|LG0O5PsHJF8%=s7H6pD6%gBeQB>|o* zhMx}W%Ew$Dm{9ef;99Tpea-HORd2K1&vWux`S=Mf7X1Nld;0J8oPkyQgX{5e59gh{ z=@4?qvUt{c%kQ!OMb00K1kaJQlObvFlCn#Gr}70(U}_Ig4|k3VkaPXV{h0&qdnshk zxGJx;rzxIb4?fOv`wiW9m@<NwHUrG^gO~mzub`xam)>Itn|ucO&^ple1pmihLqzRIg1!dA-%s ziZwGjwEuP`W>@G^-8cQk5Q$ts?&&cL`mp}4hyT|NLOWy@B#}V_UaK;PVWfv)VrJ(; z6hG||l3gdE#`y0_{xBR}RYLLDQeJXJm2~ z^{mw4BWN5Ogu|*xcEZ!8I;s^Lgy&y$*oNI#HtQ%0{mG_C{dgLKE`QvuOHF&U3~5id ziQAg^NIBAq#Kg;GE~8$6G*nKXA8om`1s6bXpd0;RJbC=a_s|7}4)bh+?Z&CC)cE6? zfXBGzfNSjst90mNdMu+t6Vl9=1)N{m=aE$`Xp~eM@11`MIPq#Umrd}#W6i2=*zMlc z<9%o_?1c5tJFy@VJ)y)^h}Ab^x0_6lSEPJmEE)GZw~TN{LtokRcC?@l-4!n@AK%53 zG~W&Q$nKS+#ZY(m4qhn=LvJgYI>ui6)D^~GQCFTL_ser#tfQejC#=3jd9GI}e!i!5 z`qNz93_O_=?emdU(R%c4Z>)UEch)^R@TN(7sNL4bYdu?W8r@JHu*mDCT+(FW?TrDA zX(fvZp8h8%zj?~Z{CZr7UjMN5zEx=d1%Sl6Qd|RHPDD>z-n=0K@nr(#?u)$}N52nf zk7JCQP0{^x&v1lK#$uqQSbo<{-4MT+?vRMA6tCm4oTck{`%Bx{H}TQ#M?jC2mt9%R za^IsHX5+A+LaIo)?#S*|0^=KP4b}U{mqR2aw$#Vmv#Gi%O5@FB3cTV9>)uF0AIhKX zj73G(l+T$C>N);blB-e0iIGR3NlNdW>1hCGn&t{PtB8K?^|)6rj}&46VSJ(dd)qe) zWUI5~BXYFvt^?9tpl(FJ_^ywnqAIhy>Iq=t0j&m}NFwWWOv6NIoyfxn-*nHmw_}E7 zG56;!4+M^zVpe1_Q<}vB;t5ArQ9rj-a)AE{+<_!TGU%J~(++`FdEJqQuW3R31t13}`aI4%w zo3yY5wuQFJsP@T?f0%u?g14dlUKXo+HeFZvmhe-OOZReXN({H?Uq$bas(yI(1dX3P z@Y;&LWVHsc2X7kx2w#BRMR%NTXgmM;Rh3Fvq33&ge%I4Z*O21w=Ew`;jlazlFUR$z zY;2_`7^<>JwnS!x;0(AL@+owIs^JVVwu5(TY zGd66w`u&%;vL2uONUNZ0w~A@RVxxbo%j;Xr<7&+v{ZloiSeiwxQ6u_Rba->4jd-BX zrrbvRyghy)c$%(#pr_`5L}5LvC9`qr@&~_c+ytsYZA)a=+W05=H#!-J>5{?Nsz9%G z<7Nsf8JU+5+AtCUf5&B^uP!`UKExTN5weSW>E_Oihf;x@1Qm$2mG6U!E$W2#0Vh?| zQBjnbh|;^b!x3Gra6ZIxeJEfBzP+)hHzpjylxVLKhfLedp0@7f-NafET>G@mQ7<{8 zym(=T!}7a1NIdq=i9`Sc_4a*M;lRL%-sK`WB8{nX(%SO)g0cB4479Y?4aDoi!M{)g zKi%A>`9j$#{;YPCb=dhoYRoAQc1MM|FC`j;46?wgu_{X#mY+%Vi`yX$Tkm3@pQ{>B z@L2y5iId)7@9Aq%`N+O`ggE5e-aru@=c!9*p6~W(m{0B)cTAuyD!HbP;JzOa-pM>a zvRHX?QgMw|I!W%P&~?!SMtiuTBm{R|i5O`0-yd6=P^tW*4-z+%2F|$L(dQoj)Cs zOEFQ9Ia+cJezG zT3XjTUof)HTt3OR)fVtS^^qWf^YyGUFJCTy(VBga@fRq5qcpu|RNGnpa9kftfL~3H zF8loSBh$;rDoj5lerkZwkb4obQw#8;L&qL+1DZ@CwWk18=hNO+m^8n z0Un?`LG0_K^=+Z$itD1XpN+QO_(QeuVI zzC(@qp+B8HGlYsOTy@=M+r*V`9lc6X(nfspm0E!&2BRDu;2M-S7)?U8R+PjmPbPrI z)aK2?PlwMmy|}Q*CyTiDU~@I-V;CFWnN4itEYm4`H{tc4z%OI{1M3)4AR(liOG@F- zXLr}d`Dj;7fZLrpC6z)M7)xA-O*hd#x#H(_kDaKBM+LCPdm@2B*c1YkaZ6<+vL9_e z%~tbsan*VpyX5AK1)LqU3b%UMjldv>mEInwWZ7WQedvdf(b1z-bdlxP{?@Kq;zoKJ zhfDKgqik7h);mVkZ1yW-rTX`jB^vD!mIZoi^P%UIBAXsz5@Hn}O$zT+QK){ALK z<9CI*n5KQ*&g)?ugy|IIA#vHw+>`qemGY1!R39C%G&S+U|J4F)1)RDX8=sv6Je927 z+m4fyK0$%#)3?2jaYsk*Td+O>$>;UXQbp9;-Ma%m*~0QGo-LA6&A{(G+-TUY>6`4z zyp<^X=iWZEL@3T|)=OI?4IABB>scoaVC-<{DwEgWk8U&7a42&74GN(o4?BC%wjCaf z|B7W-(QhhhW4=Tpaf?Ihd`FAVGc~>J_@?x_AO28jD5{9Wq-k$eU1@*}2Blq+3OFxt zQ<*Sov^fnFW{F(OIvMDwt1sEDbac{#L8z`8L%vfpnvZ8@S*;gfiOxHvXB1oLE!Y#~ zkE+I?D$Opw6%K;nc*eeuqwM0!<2m(t*C&~A(Yb=Ke#H*yPhQz(w(=rso6FIiq7V;G z?9HK@n+*(wie;mSnpSzbn^4Tn74n^1)yJ{$ib((`cp-y%`Tzs;1=M6_oa=2hmZPTp zyHSA`KcWlo`D)6`78R$9$5CYZ2lL?BG;P6!gk3bab~`7wnc zOUK@dszYm#@!Yi`W(DI9sQE17h?$gzMCYF{m@d<=zp!d!fsFL&i@1{9%m!~Ti7CM7 zn@K?y31(i*q%IsT=Xa!aq`^*1wY07b={h%8tnnhB0gQ`H3@~z#cT=v-TRL5vgx!I# zT^<2Ks{MtP{&-|ibLJM+i9hX{`XZ&mJ=r!$IRzx7+x3QL)-#5n2_ zFF>*o{}IH2%m;>El1te+l5cg-&26k*M0k&%V5nsXnkzpp49ZIqn6FqF;}ILdV*yOzF;!v7N!>ZkiIo-pm`$1nr{4aLgFk`+xnj7u zfg5CG%}tsz!d#2iOxMuRJ6)(5Bqdg{@Na`N`E=Rb>(7MtOudc}Q&SG@alPffc|`PB z*IPH}MzuvF&ju_g6o%p#ao}y(Oxm;XIRTT+`I$CETlM@+F)OR6msQWZG%Nu@OqKO{ zFOl(kY*H?Z(@f!v1To)ddu`u$)hwRB0Z8r7h*R0u23G}LG3-dTys0Vd8e4Yok8K!{ESfzPKN8n2X>bdeg3H$q7B|J|= zNtf3_U|Wsgo*1lTW5bcjI>i6+H@~Ps{7HPM?&@t{$WB2RY?sa#X;rVoI4<-wcVRgz z3VK+H;!gCrylk7ySpIUXT*im#W&8V}nqO=S&27GO?ZCNUT-aTIGSP5b{`uJJ_+9_g zZ?NILNvoB;f-hdWybo9q7J~2CYT{r>Ilxi*&5F=}$*g(t`4RUF14P!O2*$Nh3_NH$ zer}(LX$%l#pPbg;8mi!RR2n9k=SuS5z*xp|@&mFgzNWtKr0g_SyqEIl7 zdKd`Lx%|ykcSm<5T5^&lop-$5)MPb1&R-;;t>0#SD-FI>Hc=WC7I#I-fn9-Ga9dbF z6;in+29MjhyGhS)cL6EYxbGY@5HJ;U0}dOsqcwrqe;?PW;Dp{}d-B30j@GyYP>Mo1BGI_7(f#GXfbKF(6_v}cnc&vcy`1NTW2A zXnbeFhdJVVt&Goba_E-F7ObBZ)Kc7EEFOEKr1Qafv9CEm73~gxgF5@e=dLIOQEzB; zKyFm^TbjU!X?j2(_w>H_wcQP5atxUD@V04=nHh}g1%zTE_v(%|Ha-eeSQpjABl>{~ zz4)8&67&~QAH42}&N8Tq_^XBgvgs`&W~sgxDV*xRQ?Q%Jj)bQ-^%f`m>YXDI zmN7Fj&O;_=nJ=Aa6*dLEa~c9Ix$UB?{Ez0Q0vom}f(%P{Pm$#nOtydHe|G+mcXUdN+w*J*}QNGq%Q03wPD^ zo}7@4a?Y#3rpDrL=%7%Dpc#i_2xh0auYQqAQFwR_dP$-J%Gxzyp_N7%JVm#9Bi!UG zNjo(eyOGKWl##ljghLkGbL%@K{59jdfcD4ZonE%VgQvs#pr%3w!Pt4u>gQ(=h`s~4 zzE-QiosQJQEAH;(gPd#`pSJm-wCvNsY*XhqJv@347p|8&Jla7Fqjsov{!;*;9xX+; zk&;Bv(uHqxjgo!o5N)vxM zIC}}A+sur==3y9F|i7XIcZu6Wk5$7w&Xx@@bi7y7nm_xPTim;u7cZDhDD+_Hep%-*i2J>K!p-{QF)Vyps~8DY7Y;=gSSjnacdkbqWK_g=zvxoTwKO8Er$?H2XphKg@1v*E8xGbihrhQ8{Q`GPwe#VNYJ# z{x@{0-eM@L(z_f}^z?p0;cHCIDebujFp=&9c|u4|G->|0Yk%h%D<3)?p2hvOTm(_&=d86n!skI(4lyN>zw1RthO&HRwE2t13MQ|p8 zLuQ>Lg>mHU4<35Jjcep_M}GihP&N=5F;{d_GgthxapgTg$cLluwiQ`4}>O~=VIxQj9V4KMjTlNfx&doaX@ z*7w_4v)bP3ti!pw>Sr<)0^HRA(PQdt{WcnI#j2NS7{H*qq-lVU8a=LtJO!8mDWb>>{2 z9&LkvnF+2N{~Kc3tTsV<0`3UhnF!-v+rppE1GswyfyrBp1c~q}Z7IUvG+55pm~Ei1 zfMa*Qpr1c_c-hz^SaZW7Rf>=@wZ{CaIJjhlin!Mx_>TXNhmqWurI7hw36Rk`?rTUlpf;oE<g>HPYT`>Upk>KB{2bZ14NH!l>u*}kF8kD366r6rRlM!GwI3~-v1um zM4*kiLC9$(mAiGAcuWSwqzZ*oX*$ShNC%&!X^jw%VR^Uz4WEiq0W=4HEMVzR9o5P0JamgqX|=E=XUNZ#~n~lHyRD` z!#G~4fzK{yt!E?4kKMB8!nzU*zO1dNiBCO$kpQzqDYvaB@B;+(fT zJ_)c0BWa^jbt5<(F=8xOfphSSg{)N%)p}(dKa5DCF*EWIcN?1_oqcWt_D#Iv)Mp&I zGa+g(#z(&s-6lfM7X-2S!9fyYZm4=VCpn=a#V{rGpV@xSlNHM(<34o^#Uokh0tdUtv_D7xx=_Wv^(Hl+WCy zp^K1LkU@dJ<;fGg4vVk^aa*LN;NE1aAAcX~w!8t|9-3zqgHDm`9|!fj?n|?B>ka3D37*8%LiZ264_ z^E9LZbpisx2rPLjHXn;xR}i!0ey9PlZ8h)kYv>H?;c+3inW76qH-16ny7 zgo*!3C~C(tDVbN~fjmaXi}xNLMr1vGWosqy?;U?TFsSe%*qW8H2C*`#&H;+6mallE zo#Nqp=mJ29pUzRg9cNclOXudp9$=w%Gap$#rF~Zc6)1n-4<+Ij%42A*GcoarU>?5U zNZDQ&^II83OzN@&cqBBG;BPJ!rD;@Lb3-E`P{#3{2jY6SSCYP*S1&sF2q>RQ3Wr4yy@AdhI811s|`G9gkyUNHM{8Ka&2h zJI;?}0;eP3hm4pWagA{MqQhx#HF&bD%}gcCv1e7e$01r}6Mrpyw?9c$L=c#Iz$8{aIxE5-=zBE;Z}UMlNn=vn4Hjx8v1t6UnQR$iZxG|T(GB0Y7QN^ z0HazOW?8s!1>6gE0;b7nc7{gd)0rU}qREo$C`>byM@$JbN1yu%4LJ%8FA{)eo(9Ej zlS773maS~wNdlDwkgGVXEM^MxH`Nsmy_9v6gRh>V9z8tSdy|78E`__2dEFeVm)`zr zGkmLxfp_q@dm%tm(u(t%@-=Yf!oWX6Ec!oLOfj|NqEmg~pkc>%st_cffJTI>)X>#T zpl+IEoV)HV;%I`=@eVI=qiz31>^lL6#os#}hvG>Qy|Z1GKQ*8IcNFpw!q!S2N~D;Z zI}pw!Uey=RXeP!2$rHvA78Q-8yOC&p=3m|r{<1n+ z-TcJ)=j4q_i#JMp)y4aYE=vc@ui_JNjaLl;3Y>gF#Qgw1xy#N?(eKn;&-`w27 zjR14hQ2`yo2J`-_Z|`sc@`y0q3UV$TEBXyu_TZtxRA)gLLHi**NGULHUWH-3DXdfM zkk~Ns5bx@pn%Of7D=X*aJdvJLSU0SHsAIX4&C}cGeiAF3-LCD)F5M?3g5rz&xsth> zB;Q*E=@!20pXD!)9k?;5A~m74AqXeO@qBJiWsbff85=@D@8qYqcs8ffV;;G4lbQjH z&V_LAK72MQr7o3CttUZ{b~WOxXebc6=QUZL@BXKYRRN1`k4fO#*%U|ac1xkUh1|7~vzGoX$RL@#U`wi4K1Ij0GXGq4YYH5o7^dOH=#p(2^Oyrac zh?Z6zaQDG2bh{p;cKZM;*1Jba)nuwF3>7J7aH25lVlQibcisZ)gTwD67f!kjj z<4x9K1-?=aQf=z&Ed}^Z?+qwi7W1bg*hH`qteGvddp6o9nftl6 zqMDKOnu>dqvTpF?x)j9nW;CcD#Wm^`&7^L+G{}_-wjO z^eFY>h+j{)YxQ)0JG&d}t(As~s@+kArh0AjDb> zxci_(kiu>M>9I%9ikH?{eus;*xZh_o0A*J#V_GZy2)-mDz1Yy@;{IRB_mfH=ozBmeANF4!HY~o8SydrbLpb0@^`i9pz%JutCT4Bg*C1SJkaL zwvn^=W~#(s&{!^5lgECgV=J5>)}lE){7M9v%&5I$1})&X`IVyX$)|vtb*cYsC-2-2^1w+wmN%$CzCik_h9w6F5ijs%Pj9UZ46bx0k_Q z^*HEVKSbw%7;DD`S0h6UDpe{gw9?b(F2pRw`tRS?8ws#YBqX6+( za!F!wUcB1ROyVPj<>p6E0!eG_n7`z*t`u7N`%=|70NUefUMKliyupe-ud=2b^ z(SJ(pL3AG>O!cTmWsNLduYn^I$W}8Tw5+QWO=8t((g`xFp52}?n)=iL1@sLAP0VDh z-$V5xgYha3>P(0J6vNWrfHZD>AF%|-9}*G0?u{IBBS^CjVedBH)MY!Gx)vUK_`gK^OsPWdeDjMv&9r4i$VcJh8d66nnsbQfKB` zf0#vkJ_Kmz#{J3sMFpepe$0@G0Tq)Zb{=x=#+AzdJ=x{K27;)@(c|u~dwoFybNw6< zBQ8YE5!;#&MWp&CK2qR=r=5GPJ7;GNauiwY1zbF-05DH}r;3pTRRdzk>T-mgz})P=J;M9V^2L_rICgDQYkO{NR{x_G+aQ%ILK}S*Rqzz5SVh z<=q9`<~yLc=4d4Q*~}5>WHH!AoGrVm#IK|p7aHnqDH#O3+rpASzdw(C`8XR-nx3*`fFD zfYMh1y}m5P$4eeM6@7gNF`8fRVC3}gN7yzhI`#o9F>5G5O=d4}n*IGj%gk@owhlg@ z@&gcyEii)ohCWY`FDw$?AWF%4FA+%S#-((e5~#BegAi{ zC@JuP3((y%nJSyA!g9--ozdBluORq&EV)F95w%~py4v&mRY5IW_ACBlX_h*#B4{t3 zKQ%y`?QK>Sc!Oza%j8_z1qgu4c>kvtx79HD28)&}v@(4mh(4`whjga6@^!MrD_Rf1 zM0Q1guM?_(TSJPGQwW~)@h$FMew0!NsIYHCjzZh-O6z{|5I(glFDvsX0c60&&&<9& zjo#`4j{IHLJc33)19uLIIp5Y8HloH=`@I~CgE<=E(>!X!lf#)`rqzCt-q8A_59q(F zJrj%pN>6SJMG%F|3N;A=J#?KCewsMZFo~cPc zx4^0eD|-oVqJe9yG6qiR-CJS}Dmd-)6hxjt3#LMI^48pDB=attUr)}cik2R=x$-!O zQdtUY2Hcu}G0E2!IHf?(?M=5?x>);~TMTnpWrc-cM=#c2p>X5&F{@QDCI%*Oi}c`ikU*M`yowN}Yj?6D)pgQ}P31kH&bw*w(MJ8(o2A*|D)C_%xnM zC$VwMbo$D;PTVk!kEMi)sNryYoby-mtsPf20oMIJ1rd~%#k7Kvogw2 zWXcJb-44uDCB+~uBq9AtR_)*SDk#9erj`)Nx60JXSC4ooCb2 z$<6H~a`>1f)<+vn)E*K#{K8~J_y1}EXmhfjphU%>e~F818`Ej^)R&NyynZ@(N6y)5 zp87p~_Kr10p2@5r7kN)zWzFjyelmxwD_y?T%!ZKV7LeMI&saQ>Z`G^k^8948gu=NBc+*mh*KQ8*{j4HXb-r_+boh%thy;UvKh-paNLC$*qNF%6<3pKu^(G}S zLK->m4q8~@JGPwOq(-(x*=BARt&Z7~mmMk9(>b^Cym-I_Iq-3cD*e_#J}@hIBm4OG=N(*~exJJS!miKxJNnt`*>rEoHR&-tP+Q%`ToA! z+)y;DVtPXUzT+u6b#oJoC{{LPd|}G5b<0lvOH0NnnibKNsZ@b~M)61zJ4yz}B zO_|Wy^vCVBZEWroW>dcHCuO!ivih^4R*H5Ua*U^of9Zexs7GNFpP}^e8%wxnC#DiA z+fe;F3`@FOlDpG99b<(UiKTk~3RT#9g$4-^PnX4mtL*vvvzGbP0w{2KEn#$_<(Yy) zmRKnAqO?O=Gly9`0~{l9S=5K?BisWNIqKq&9%;pjP3`jk-GrTWIhx6~1N2>w`rF*t1P{e7`!t8|CnJTCH~yB zX?n=)NJ0M~2O4~n6M{|6q2&t7w~OyfaY=+Ge^mc_Fg!?%N-{hcfkWN0FdxaJ;miAi zR@9kYRf&%Ch3-69>qAtNk+hs#=Qx-zI7QJRonL8SIPb0O7AdJ|DbagTf)0%Lp&EA& zqd!xISNOstmp&+YD^iWx8{ocBver7b`a|ZuXpy>Fv?nIJT&Alvxd$XiJ;md9d~eK- zEL86pL#}eJNNMwlBF5leAilq5N5t4l(h}?qet0Jj+`SFH_U=;R9QPgcPGA4656zJI z_{~O+&dheg<%ZpVvq^Cz_3uQaLEZu1u^-3yJFs)AA(zM#d6s_X;3ZkRqjN0NyKXj6 zn6B1`JZA(U*)v-x>lk&6Y^BIP_U8BU{(e5c$M^eZgZqBnuj_SP&+GYoUiZ!Lixca%c@W{+ z9(F|=DQSpNQ0;cer2fF@iB*GB@^86ch;*`$ulUfh$AFa&ubSiF-w+*3Wl~*CO-uVZ z9}%(1g`87Guh#YS6n5InzMSi@g#A;8bS0y{=tZ%y05X!gX}IPK3-?tvwK@5l+fFZk z5KsTe@Rm$`GzNb=L*#c=Nl*t_Ax2NZaRrr~^XGkW#G#d#3_F(AaYm!dK;k2}lK}M&R66taeP*aLl|6 zi@D0fRk<7D6f%9E;~7(Kpv!}yW!Yrk!1`qgF8(vpmj+(87kix7nt2jq=`S$~x*^Fx#aV; zvbw7WIAi4wt>aUtP>-{eQ+t!j8NRkV&^H#AS~=^qc&+pMZItFa)Q2#aBnuAsS^^Ip z!R-dwnuOaYF{3h5qhVz?r^FYS5n4Z)__AFd1XBiCLxbZFiy{sKE>i1G^?6paz+8qr zWu5xcKCeW^ENvu>Z#nhT(v+PL^xXXryjyJ<+$V*NBHoFz_3Xd{grBe2}v%# zIJJkIUlBMA3>ZHpW>U7^gz|{JnUy%O1Hbwv)1ezRsK>sFY6SiE6iQq}fD`$dxw5aPM$y6`w822iaHR%ju+aP(-s$X35zDxw$fyf&LA- zP9D_AIECy}A2HT(Bg|F%+2N7?fSC%Lc#q7zQOU!EFwPr&grgv(f6Bv%JNiB)m8pk3 z5UbqhcT!vKoAEP>o^GSVN$`zP9UVS({&?iFor9(`tjw0bR+{L^#ECxzc5{1F7kW%W zxQrbb=h&jaR&GpoPjdQ9$zt|k{=&*%8i^0X{w6qTLBd}$z4@{MZ!`3TCvl3ByK;a@J`0ipcrp@l2jkb6Y|F2d&=I(?ab#0rQ3(g zS`Z!z-k54TBlbofhUTAHkj=_KdQ~X2EH=AUFprLSmYguNcoaZ2E9C7)LL0hAX6K@Pcx=jU8o^^hUm`3S@-F z8LqLLctm5HMK(kzQLuE^?fY4340kOr8b2d?qS&gJhW;l94mED`Mn%M#2E}-CmJMs)e&>qpo%%6BF?m{vTOp(QEarOtyyGN zmQW($!f*cXI~9pLgru1i2($iVhQ}MZ8eE(_4Yjg*f5oKNtiOb){k(4K9j*I5;)m$0 zwDPkadb`QJFlV=nKQ-9?UB{Gbnj{q%Pv4A({0g0ppK{Z4CLd5&D!`Rd@Ky^2%ME=Tat^}!zbu}CwySIyeO*67DJL2P0yd*sS&PoT0fiZE2u4pO+ux^_Fr#sj@E3=h0xZvke5o$mqWT_ z1n*yU(ib7u5Zr>!JY&TWFNza+wgY8n?fk$>S*W3;?^)2+L1W{I$5?fyxP z57|1wo+{e|N>)7uIEOI*f@ME;Y?GvLq5<1|nAL=?HZeV4PD?u4J7PsCf;~xUA=nknV1p*N`?P0cO^b^^c0kDBxnZ8SN3Im=9VKsfv@-DcYVjInWN zyCL^k^(aN@$-EfUviN(!1X@>~B>vSTkV3cagT2Ahn%-xW5LHoR;(Sj)0cMq{5vcji z<++Z|Z(<#MH2D+k@@D)^!=LRnbG6XtFhnJBP*-uq*0oJz!xhpA5da}A#kby-2r5WM z5sG?x1Z#&`Z|QGlU0*tioe3P)tuJYzZhidd`pv0|dAGRCLbg$uF@@XfT^|)_8WBfRGd_8lK1`eEcc?3>DT2gEARl5cKjg058$#wKtYBZphIahQ@ zNL{g@sPQTJWp4qb#%ML3k7+w1=MP0^c(K(t6R7`2)F79=T|x2d7J*<44>H?^^-E|_ zI0wqazS>UWlmVWt9L$x99 zM3KvF{Uyts%b)=~$CceQNz6y&@RJaPcss=&y0Thf3*G3>iQOU&dZMfr(p&~WAj1X# zDjnJvTu-6LgFXp7{+tRhnxXS!Lps*KdN5MB8<7h1*OX6O^M-JvxAZX zK!V8EG&wmyE#&)jA^L}}#&O!+(qK18+bPV|f}O+md%#x4t;m6WuPD&SeHcJ{R|+Yd zR4vz9VG~GxtxY?SmZUAYw`-8a(=*)e4rMfH*HiP2dQctIF<1E}aBkJB#yQo@zC^}L zuIP(C0<6@lf~eG-ZSn_0Zuv@9OWExJ^@lU zHNB!;p3P{k7wD>#zC9j@_>p}-`*8Kmn&^olba5PWd~fWztmAU}^jMyriNl|+Z1(9v z!gh=K-b5Zwp|XUAOTk)H2_O9P=ar6m^ymqulAbNi)O6GpJw1dE7`+9Ba!1OB6e#S} z4M^Vh6~miC7bh^s3vo8*N1SMz3Z1bivymJ3bbI_f2UI(c%2N>TsCXpVf388!QHsSnfy5n{TyKEK(qqK{+;DSiXB@Nq73k7A06EKS{ zc)cg^p5>WljjN+Lb?cL{%RV-PZ_U+{68!_SlP-4Z<~DZ|N*Zt_N2__(?X(lMAM85C z`S?(Vw49AcpMU&l@8d1pgK)?l|~4_Ul*M>k$#hxBZ(p zZ>1SNq^9^XISI|$dZYfKU%k6cmq`IFkG3RA0dk=-;AGP+gjRlE9{ZT%#>#B<@~G&U zCF14{7a9A-!FTCU=1y{gigdQy?W#OT>x;K$SoS2=_yNeQw<+DJqV3Ssla@S+_2 zm{QO%V~~BH_Utg9AZ_U1Iz$10-9bR8@xX6hGCzB!b-HSGWOapblcB;QzeJ-kclzgBHkyL z*eWt%tku%ow?g|tZ~2$`%^@UsTSN`@6G>I;>=+TUf`|Bh@$*s;MSqTx@)i}z%ECJq#sDwds1Cs5dJ^k(0;yGXN#qnVRY$-DFq6#iY?@g~ ze3Zrn)AiISf66d})Wx^w37$8yUcr^F^NtXAn+gvto0=mh{e#jV&DuwRtOLmFGV-d# zRse3?H%RHvg`K1Kc5FLTW2AU$3u3}$Wyg5H0kMHy@>JdrxE*|p!_5J+mlT`N+nd(n z)J4B%hs{#u*K$vRp~Wh_JH^@tQqliND8&_CgGlG2Z>-9LGUxBFYRdh-nZ?Fz(5 z!|NAWbiPd+{3-4il~^vRwfk4>lH7e7GuOcdZ>weDUi%>#4G58R01Hme_vr>m^1&cY zho%PVskCTawdLyRk6(WArK*t;8<=WfWf1A!L}KG>o~Q7fjWW|QqJ3Tvg- zB~t9k==xbIu~W2K=j8?|?LTV|3vP5KWwM_&Hf+4mdOJ;RkA&!#d&Scaz9W_-vp#rg zoy5fVrH$Dyb+6_a=v1zhTZu^+g90Ia1wj3A%^4{6bQbL zi?TB`vzAE@6bvwFes45?=|SGhzQO%bMFLAOH=4h1F556v)-Ww1e0es-4cG~2L0443 zyXAUc`tNw?tgf!Ed>ovUa$8ZZ%TkSTqaK+eqy#DW#h#T8>xm6=sQy(d`rzajWyRC& z#B(x7EGU%@^Zz3W&9#D0pAOr=p+jyxhjba*_Vl^w>+fPja?1*HnqMkenFput;XU2O z>6t<8lEr8Eu)?#of^`*sU*%}sLvns(6ErfELiK;Eg!xGLT0`0YZV5NzoLSFYG!>Z40pZy8wsscU|i?eQ%zh@7+$5lN0+iFNOD^AJAWpel9s=Q3S=FjR~5*-kJI z+oyw0%Jb0u3*bJ9FIS~beBNh*RxfC;*lh8q&ex7(TzKGXR(Xy_>U@9z5EA6n$xZ7d z^5AWC5RXg@fm^)k{TdyYDI{2)7A8U@HYV1l7@wN^!EXP7O5+O(t*3RYyEle^#6V&h zCXJV<)vnt++Dla0zWZA))VO%>v=60yHW?&Ib92W zC@ZK)GpMZSCj<5)gp-OQSX4Z{Z=|07&@^&@=yTYZKSkJKec*je#j43Cv*Z|(&%>S@ zhyT7CU}8ZLmQPAFHoSK)fLp0D#l8L8>%$5wv8vJ)GtFOsKS*;@3&2I$!qP6N_*TGU zD|8q=(e8q>=2~YQIBgTT_f1V&W#>GXO^|En-sRu#!wSMvS=iL}XQq96U5@W7cT4f{ zwU-i?F}yvpD=954ZRxw*hJa|1QJ$4{fB(^)10{X^t#aJTzTe?;K%gBL=#wW6{^RO? zzQ(P=aE{C`rU7ZgHB{6TNBOaU9MTrCnV*0rvzxs&7vBT2GJ|3Zn(T}Ei~Bjawl=@b z0*O7_^=Mtx1FJ%tl;gt*#E@XvKes%SgFvv195vwupK-BM77_gnt;p>XZKaqf$eF(&l^3{ENpqzEM|Gpjm1c`NnjxW7E3MX*3 z>ZB$0A$R;8z(qlBd`Dk}LY^?rDmI81rK4reW5#a5;Ay-)pMrg!VEF#HlGRd-9UA;L zVw~j5u#Z3m5|^mlesIp4n56p3VvQtUTikt>@`L=G&@L|i$u_(9YA{PWia;c_CP39l1XCd=r5r4cjN4h#sg}{V6Ie7=U%VizUFYK6ja4pHd{No(;Sv9?P2t~}F!?f*0!B1(f4HyQb zD1)*oJ4LYGc}$r*k94?B=w@U-4t!oge;jKaJdl*B)vZWsIpqBCIxdPKyOhttO9RKz z$cFSE*%%ogM>WUBs$J|X{J1AOU$sI3-a@0m_%<;QVdBUdOKcCGh}Ip-5^iO|O5fVa zEBg9olgG~>XHWA$jroUXr{^?g2Ol)g_$yqATjCCehp+agu8ls3EKZy;(X2i`N{E^y zFDee)0vDIXPG~yKLzb#_AE4Q(t4J+H>xpRfM!jbTw?LZ!27uxw&cnwF z$SNsc3vqrFvFiryVcO@brvsM;jGoyAvD|yb?qepKQ(dv6w0tiLos^k(Ov5kqMhJs# zrVHQK;CV!loe!Sgke&bY_>&SJ@{#jGBgNYbtr@Yr?IAcJkUhU>v*e3rG%2BzPHg}l z!F@)&MG*&S+!A8BPnI-?dtP5yXvYQZ!Q=+S+Vnozr)XL-Khfvffu(461QwTOH@ur@ zPgD>P(arV!epK@?;JwTD5pi|=6G({*{@s$scFG|HPMJFY=ib;s!pZ}~c?!jk>;1p7tn5o|FPeX$o8NBnY->pVId zUIwBZQnPdcfBmu+nINTh6X<%X|AUH3CNeKCs1VK@5+H!OBXZt4FL>hatkPOhwg%dW z6yNB3QdP0p!vRi1#XwKugFfX!J}6qMkMOl(3rGl|&?);9mU%5Iqlsv~uqO)0$CM;I z58fQl-|42S;qWydbqYkMkrRXfF)D01O)Z%EpiEU>3C`nV=GoXyec92Ar|2O7OFSqo zkvl+n>zN2Jn~9}(mg=5Mulk9a*Uv~y##8!y^>Nz$0g)8q=nlKU1WS;Zet5bvATdlY zp842*+!Yc$rH5zgTnPXSC2F|n;}5P!k8IKbYL;(hgrd-xGyAk(0!w>ZW1OT1y*8`{ zfl9%*Q{SN%k--!V;qetZ*G|6HFqSULu69P%;Zj^# zUyG8RgN|ncb?>+%ebB25kYT};EixSJh^SqDm*;=w_&jSTudAv!5XnV^C9|n{BUvYpa{>QSH zJ75qUe_LlBqF%ReTyRRD)$&cZs>;3qd`c8`$NHCfm@)It$+zVcTa%63H6FMxzo6{5 zvh&xv?T-1NK+=EF{~;3;-S<2v$Pl(Bf3V%)G!Z@ur6~osr=_!T^(H&_VH`#N*9$xhlZM{t2w21 z4&Cam9M8i3-9$eGq2_cv30Q=4oDbH_Uava}Q*w6hwzP97FFHsMew!^}8`y4~Y?NvH ze?%rCEU|fVc40w^gf;+c*`S**^1e@@z~0KLnk2b4|@{mItBNb6fc0!-3F9PI1f2$A6VKCS`t@7ogV)ty|1H zi-h*WF|qyp)e z9M<@8In1HGf~6C|Nemc2-z`^~deC15Bh=}wgXzDP7E1rn40K+8b6bTU=0{)I&?edL z(9@r57xUu!QcwjWlu%tY_`V{Z>7pw?G*e`^iV1ws(Dj1TG(5&^2WUDR>YLg6gcRtuo0D%Za9LxUm#)t(`S9d4E4Ut$V#dLMJP|~j(ak<=QS>NV zkvjqZA#k8Fhwh75oV+hhLHFT zsO1_;{hf7Q=8$T|*xO=T-QR=49MAUe;Hg{IJ#ndKS5WUH#LQ|BVsOUmTh-G-C@Ki_ z3nEw+$=7Rrozddw2*}-iOI+oNnNj1~p5Bxxh%UwF#Sf-VU_R*@jgG9p8t%UgZhIi6 z>R(SYw5F1FeF+)u#qi>Rezxkk>^#M-Bk?&Mo-{Ysm*@CF_OaP#GmJuR;PB|c6hxqb z0^k(E;Fyv_#E13}_2^&49$F;*d4&S6QG6-~Kh?umu?Pyb9|0}>4?ifswW~ktaDJTF zXOhA@rG5>p=8LH>1eRXWcfuy`o5}g5t_Zyf#Sq;UM!l*4%;xSp*G^Hb+=y@R@8&oP zDQVkt2!GDhf?GI~=?^=C`558FZeuRCHoQ$weGJj}#7SQ|>GwreA4o#$ltIZl&z7vu zcevM`Au`3C8+)a-7m&BZcY6h?G`}$IsCcl$r;AFWZ*=$H(@=~0{8$G26h!u?;nA6S zV?(*|$(1#VK^-W2uKqaJI+%r3&07@Y>{EJX6?U6E+mq8lrwTlTo$Hu~dA|&spI?5Q z3OafNpf@c-Ex00ihqTza^DRyWGv=-h$1+gM)!(Lo9GOfJH!7U^Mx|mwo0B>BKKO02 zef~AcuV002o7N{EJdM9556WeJPL7`gx|yjp2vU&f>tc}VO*sPffDA7DLozLuMpy(^ zRuXAR^s`fvtt5^E{kG^_5^{Cs!FHw`2ms7}8PIeCIiRPi4BtM1OQy@z#=N=OH#UggaoLWkpV9%rs50)YX{#iX%C#=3jyS)NCv~;TUaFsURT87%G96 zgQ!5gxoeJ0oVk!wuy;^(0x5+JHGoWViAP=|*z=>hf61oo{7=jZ!K0HsGL?&tq%9kv zb|t}$je_|FhyXtLnKV)r&$f9-z3KoBIY&lR*fyQDn*5Q~Jx+$^8Gdo)A-W@^4K+_U zA71ihGo<_yDlI){j;H1lK_yFwfs2xZ((#DnoKlKYzTI&PJ=E3zZri-xw*QKGzI^yU zC#A{p$w^~Z;N2MCzfwzaIY)){LV`|WCMjVglURjXwlS*2EN%MxIY zI1Y{Oz$9j0*poenZ=pEvuafQhRyp93KU%;gS%{7pf60&Cybw#zPI6yATMv|N&@W$n zY996fbfb^R`zB{~o;+bj>T%uN9Di4_1p&0oN$M>Ce=Hg-&nP6gi{fd9%^HJlSPjF; zcXm_vLDO8v0Q652ZDyM`FLBOlfmZ5FAPf|TBV_zTA6^)o6n0P5pY!7x93ns+FrIhBjo# zJo@iZ|3mxSvI71#2MnD}-k19R^@2#pE%j-Zo#{0F@z*AP+$gP}3k>Dgm$rVUNd=l@ zuccIKfoHoHz76(sxLMRxN)l(R-cd`;)SWbT2$~m7v@ndI0|dbD6iC zZ<6h`x6Ok~?67n9{mnqTNP(Q9npFTd%qq1MaTy>D|M)%CraYQyW-M(OGl-u8ZsceW z?K|p5kPm+!(SJwawv}tCI@dVRIu#c?CEhj>h90owwR`4S9V0ftaO}9 zK+zriAf0uorzmNLiFH?YzC(|@jSf0Z2|GonC1!)F%S5e4)=OL94R+fZ)=b{sVA1eL_ z>bd0*v5h*kW8p>07q6-L@CDQ=bU)^h2(V87b#;Ju5yLCOPAiw6Ry&2f2Psh*qd=;W z8^H)^Mn*goiOtM?Z9gmo5h$^WUHx%)l9)}2939}_5qT!NDUT4J>>}*@PRDEJ7@<`k zEF+M`$}hctrU!NvSH+8c1OgWxOF`5+`z0-{Q1pqX=L1W?3tS0yV~5X++eR#JUO)E} zJsW2oJGfxtf8t7uJYiP4riDo*E$iaTnSo?F*RwHUE0U>p6Xgz<YNvBPS>ChsDnf zCBj@$Nmlc`VPeys2lB&sh1x&<`gB(8@Zva*9D2CZRqX3?KjoOofB37YvgrTv5^*<+ zfv&S9aA~??i}^Z_>s9Pqr;_w5C@r?$2zkocKuH}YGb7ds-A1xOcU)EJfYGrBc=`Q# z`hV5#&T#hV(e(ob`1IrtyoLEfEY*eCz__%+t%jTm^K@@X3l^b$nCP047JW+@Z zkZcW@&q*G3DU5SUVh+r&uHGkyEbQ}=ho5GwB#xdL9G_bALRf^ey@P-XPV&-SdVb-YoJ+wcrre8md{s8S9X(Av^{-xkzg*I(sEC2yKPmseVnHG;4MogWL5 zv0=m7O%jyQ-h8jS4VQ`GagN<)$#38by--u-cat%!W_Q<*^mLfF#=Mzz)@Sr@q|afB z_MoKj1*;gPx4+oII>F6aHi&TD3EoNjCQ1(6TJf284(P_tnvuJ}^6EQU^t`O7+H*D7 zbD6WkenEgxs``(+)S)SC5{|>t`@CEcB7G#;`Y6u=hP4pQq_;r=5$2O(B~#pOSg>z^ zwDV?XtP+>dF8>1g!9rORcdMFU={dJv=1Nd)pu5JGH)mJ3K zj_+#_@wis)%sP8SC}~9->G>N`o(z2d%!O^KS;{|_=&ttM;A>1@e8cu z5jkC8s;-ItTIltq+(FNMSnI;X2lNs%U#Hme0PLsyM19e@n*nkoHX~H-K%kMEG&Y>i zx=LbUKaz#)>)HS*A%aKGf@McVj-nL}vfYp)B>c%}z`*Sz6JICz!daO#s|4S0V6vB@9ol)``@& zoj-CX?`r4hi5(R~R!C=^AU2o`v_>RWHLysg*16*9_wVSloYkDq`7^r4b5FI2TBQJb z`!M`^lQ?-R0t$dx31N9>t0R3WTgxz=98^{+ht6l);xR78v==u|xP@P0$c* zpMBaTfzmA`zmLqDrA?Y>|3YgYbbYqzb9E~8mbJB-fL_uflkuIl8kr_mdP!EjB8bvb z2Q6|Rv<4%5+RkF_xws-zUI={*Bh)H!`{k}E^%x#HM=dWtulOCkq*A%DRN+9P)CSIS#WT2Pf|fm1#h(wePi93JWE2m5ysLSdfz@-{cjb<;zC z7fUmtNs4vVxk^kYu!2)UTC@HxJ~j&D&VSk=@eG+R)|tXD|{S zWQu38bR~2SSA!K9-t~yJO;p2DC1%eTvnF>&B33-L8ro`l(UioE`iK&H$-A;Ykc?me zUBtSqhk6AHP(@C`k8L+9?*ketjJR zuM?%G5KYQ_PmfUZu5$`;i_d)jCIxNx&PlDpRlWVr&(p}#8ow4Ym=pG%5KX&Q7mkkH)70k&X5@)mx8b_L*G}MYW!m6I(5@&1aXqk#?cbPts|& zFxAzp1uCePh@#ck+5@&<>+ai`y(R9=Wv=(a>(|+?-}qqq+3A@z>#x96GU+bt^)UGRZMD&x*0)8af`1?5@_T{hF@uEcDjqL-2H6s3ujek|l7rX1Sg7&V? zy7+ksxhUSSd{xqE`=VBI(Z~C%H%+6Sf^61%+ui7Ku8Cop>8TwPf5a$k-QnTTs{Xb+95bs;ks~;o6nw_lJmEtqEv-1@MK-V>YFu-3Siy$#;Yi^hlej zI-U2|V`)N!FZ3Cj{)%>exsXg4$*CD*F(v!$IyJg`ypY#o37c&h=WuTGeJ8AQcESd& zI%xLd5e@ZC@?=kx4t;4R0^a@8=88NVv;`9ypKD{d9P7wIsacx+)bpjcRWtMulaa$>7*0T&mp%( z9n!{_0>re?iI|JY3)|TSEzj}5mg1xFBHgn(#>SY$2{eA(!sF%QQ7Kj$S#@DrmazUM zy>D@8H~P_!zzE$nHmjtJj&>yX8`RY1(*9-xMyF zN51fJO0D_E)ay=5BI65g6$^{58Tt7>ve90W-2bKE2uYH+W_S(e9Zk1mF08@C$rRZW z;>KN9=1he)e2wl?;>JkZfnez^WX{y+v!6dm^T|vt*kM+qm>IX_<{Pdr%M$}cGy|O$ z7asZY5~v$JOBwNsy@CV_Kd{BTPT3g}??VdkEdPYy%+Nd%&zEk`x*xWQ86Xg40Aig7sPu6j-X}EjtjMDAqUEjH1 zeI9hLq~|eSC0TaskL48Tr}JrNpp+jkw>Q=X_($$V>N|8K>3L z23pyoP>(rxBpRFOFU>Y#U%!$zZ6ly_lUf^OJ~i>r?szLuPAeU=HnyG7c)RwDyDT?H zpHi=XU2yl7Pw8wu`rObb3S*}$WoxJQmXB^3`?k6VvT`h`H!t?fnAx$(NRbq7`^vNA zsh0^Dz%OtkGxa)H5WvKsG&FkhJ|SS_|14h^yYNf+_VX6H)P+{ln$1g%x#G!R*lIan zO7AvNmKM){HJ59qou)oGC5%+IKyG*RCVted-ZWA#{nC*4mS}=A#!Xjo?>(WOqL1{6 z7yp7I{Zvdvyv+RbdQGm2bA>OboZGhDJdqt*J#PcrkqjO-+A?Tqp+V;w)Hhw2g$#D>&Q zDz+C}0=elGUQy`f-xI#Qyu9%EA9&CDeM4P&U5Zswo@smqLpE?vW%1##1emdXn8 zUFD%0CQ2{rzCNs+w+0-Z+JnIbPIG=PXy32TuKX6fZ2}O- zeEl)*T$uzzPK(kpZOcOMcloc;11LwPjA(StPHYr!VV<@BcT3M4{Q;6BPMMcElW(dX zMi4xDTIt&+KKFWRRRo;e2ks?4B{vED0MK2eKqE>_4yJ#%-Koa4boZF}mO)-Y zKxPre4bj(b_N>RO0R32${F=tlYThMfeA%Su)CL$Bf`lI-RA}*%B1G7}bbk{?gLOC+ zlu?XMug!KHIH=A0*5SAG3QQCXC3x3KbEaVR0N+U`VK9_T1EhvZP- zg7eM%iI?Ct2P5BR@RAu61mnvQNzoj>&pVr^B694+!PSVdL)!k)2;}^AJP?)IDHLZW zCw~?=c{9we!#8G9oZKd&W6Fy9_+gXpP-MtfA(E`EgL@6YZK7~x4V!l7RryPSb?Avf ziJ9EkpqMQOJjwBrC4e<0AAu~|xx#rn*vi>?BNAw?8vl)dA%;)X5YVFo9T5sL-bCfF z%HY9%?c9AwtU1<_tg5iMxcTeOPOYagwPH)+&5De$7$PMV;H0<#^DCU|VDUV>VyXeY z(3cMkjS~Y4pD1of${pMf%&g9}-zp`en_lhiOINHtTF#J{a`_p28xX>t&ZefzBa+gl zud#v=5!coC{n|C9ia)E2YH7W493SwM&S^Xz>W1iGpA$^jl&^&3&-f+hp;&47+UOyL z%YNkN)+Qnd{+guhDqhjjk|KyyrK)4S(mO;F6{rPCfgimAMh9FLc0ZKXG)4_{Y0}5epwvcNrnac`A zpZKT$|G0*C1MfU_=h$|JbprU} zj6Xvo2WO&18Svo%_*z82owK}ou5w)+8ZmTzpmOaDt{ccql3mK4n|owMnDvJ~+%@uw zSio}8_W=E-!VWw5tjResm}xVN!#u9VBmColh& z(s{3@6zqa>52WL)phryGe_zk8ug!BZfR5$Ns~+Kue~zcgl`3ZE7rA6FrydNvS37CE zHR`Sn1`%GK%AM7GxcB;+2xq5Qj!cjap6oWBjCgvJAEvYyeU<*5WA6_A^wPDp zGK~mCF`>rEEmV!cU6*;M_1G|HkO!RsT+Mso_I!phmpuDo6)cxjFK^oegO4&0IqzXg zgzj^r(CSinGq3BFDMYjT+c7#gJ|l@*Tq3(}(DkLnApH>b^r>FyxlZ-S(Gpj=*I^>) z$MOh^n1in>9;WUa1P9-SXMbkD{5`9Dz7^6G*KKTmc6BWsSm}OxZXh8esF4f)FhxIO z8YZp!!E)kkpyv~yyxM`1b8^}x-fg$iej1q74}#1PjDqmq6KDjiOwO5EhxWeSt(j*k z*M3O4Zg{!<Rrs05d>1K zM5p9!+gF1n+>aWA5h&LjL-url_MW+S*m^j+>+1 zT{WRCU#O|9hDr0ehKBj#ls|WhzyI#}bvdXtVI9mQQ_BD$Yay!kxS> zsq`bkt&-I*pY5I#h0xo3*x}7|>WzAX4kkY31L(Hh$;ep8n8*auH0s%t6gvgS}#WWOD1s_K%voooS8wrS9hJh3|}}X?1%tKQz?+F>}@b z>jm)YpY#RaF8$c}b^7nEJcMEd!kycfNXZ2H)o<|SfK9@7uFQJXDEVp>p|k7nca;w? zRuMkM+~rC=kBmCKyS3Z}ZIlk)1dW6)_s#T>)%p?w`;g0$!;T zb1&k0<4?)ULHqu=6tY>CTk1$wnf~LX?s(CYN9yW*GvQVF9`XSRb61kdIFf&tg1;~B zMThpiVMrF++J(k{(Li1tB>hBSjNx9Opzj;a+98dm-zfCZ$F##A$f*715V&RQN&rq< z5jLnC%?to0RQF zA;}C4PRF$5^?OS#U))rl!r{A;Q9nz-?5DAiWkByz+9h`N4lc$GgIEo}n76j~j%``X zKaEd>2J-NoTxiAAHH3Or)_ueAU30!qrp&jaDNsOVuVuG4#n|p4kBPPJupr&HSK`#{ zQ2bbUix_S}T5yHZCD-brR;vDjPRrP8D9zL4pc+ktZjuBi&zfvsHbkEk2Xvb623liY zMiw@r^*Sp}CT?XgrN4p5 zmz+~44v$dmb)N7XkZ8C0W&)OyB6%b4q4Z<+06C@J;936VWruZU5A6uWl*Kzd2r`Ig zNkGxX;oDSnd_bM{I|0>ucbe{9sK(yE(-d>*((gGMUd-^)aPeyuVDVQIa8Gc}_o^(~ zZu}y1-BRP~vy%%t&*-f4nCmLiAH%J6lNS;Gow!#btV}BGadB%>QTvG8l@>uapYJci z<&krYqs*PeU40hH8R;9}n27?(8P28x4mM2<8TAFRiAlqfL1A?Ed8$w}dj4z3_oWo{ z@niEC3-F;?2N%Ryhk}L%c8!q;|03G1%%tgXT>4s)zKF4l`oKxks)H+>;&Ak5HL z<9#mNw%Zx;n>HH*^RqW3ZeRSV!tnhlY1q%X(AN55@6&L178R8u*ym2M9F_#xXg|}d zyJsiA+g1E@t|IqTCv-V!VOw_3Oq8eMa(JF=wu{;}hu*37;sRvUrnL+;+?lR4dL7ITv!ZS7Si%*A%C5Wk4N zgOKS=J*uJ~T8+Tf?IqMb60hj_5a*N2q^u%0m?{%erC~S1;wgVGJA$5(1h=o<3p`_O+!AUJdO*BL~47v!Nn2g%m3&YBb>ax+5Yq3!6 zpVL4?r!?cw2OEDFdDVey+Q|r36&_Y{qwinFl$YM0M9B>c+_Bnd4KnqQdZxYXySt{r zsRP6WIMTdZd)@F}YV`Fl0>Zry%ih(D$g64!!Iecuc77VY1S|OLVyMt3bcsq{ts~pZ zgZijNBJ=~(o%5Y>ycd`D`y-e3v)$eC6T(_{x>XG;|9@`MM0#Qaxr{^Cbo+?z6WlQ&n!3}*rgpR)_%3E zG(9cufMv&Ktm42mNosHWiFdpJ$ys!2K=Y&FCGd9UpRsjze$qVO=NgZClV>W%-G4-a zXn5x;tp{LqW-t@c-Y15PA0{;}0TaQLbNxgF5;*6sg`#HlBge2c0g@)Qj&$gl^QYg9 z1VkJ{(Y$W6zWR%5!L|zDPQ-c`Jx|>A!u6LLSL$1wzH!+kl@c||!}~olhQPbAn>6}h zjj2xktWIgwdoUTi=YexgW(1>qu zH)Giow~Yx2*&lkkb8|?@Jk7Col?D$xY@qk@Bq2vq}dkofNS(uUnmk9^e#3tf#yK2%O@Z; zI$^WvY2u=tMBf=n**(qEhlPiCA*)-VuF{z_gZ{YVGN*5Pl(Iaz-u0Z>w1aahjh{h0 zeoMdL9cyZUTr-128E=VgF>57X9e)}9U@=Q`iMa(1UCzB!xZZtZ;#f&K>Ywnv-}Vq0 zJab8=`+aZf8$(8XMw4)50|}OPR;=?*bHdqY*ZfidBv zk$gxo(ugqrs0DIL1Y!U>rbVJl9}XX8A0C>h(|qX5%lq@O&0f||yF|NlO$d)r447wa zIlVZ*!_vx&yL$)SE&SBAVUdStvKzMtGGNtP-g`dz%jN5g8yqUfLQdaJfXl`g?|qhIXz;@yHdMr^dr{FV!w;{J=I|i>l!4{}4^me7-AYhRrceGLrY5t#vCwIn{BLv&f{XDB%yAw>e4dQ%b=a&3 z`j|Z>FA>G5NWXE4`>cg`=A6m0Rki#<7AS%B^0HdkuekJOaL(7z37oZyHzWWRC8a@7I;6Wh-{JFpe}DO(%+51&@44rmb0??*ov?J^ zk#b@!II_3R7QZ{3Hg4Etm1P)lyZD^WSXp7@LkgiTq!lsSneSN$3D*vlb z#E@J;ZslflQIZkbT!i|3Y9iI<+>?S0YU;bToHul<-kT3CMF{5#CiG6MOFJs|$K3Z5 zO5Ad55m+8PE}0WD8-au2n7g$Lp?)O?Vx96_g#|yup;VH0hTNfJP7fm4Y_GQKUndaK z!N2D53?qVu5_v+$Bn=`|HWU+76npwMpmXL(5RMM4HlF&*^DemjUV>WDHgV2eK0PZL|N%NbmjlEVya(Vv* z74Ol0URo%K*wsfl>=%WMeVIucz-TE``gXOX$qSb(jOw4-KRfrUx~s7VdAhwpW} z27dmDDCihk1_a4W4Xo>S*{U2Z#ECcIk5F&Ji*>Ow&sat z7>N7C|H!LT8nX5B4!`veoqvE%Ut9W@*K^zkT^tW5h@N=wZV*W`Rs2xvtE=m=Iwfp! zwC^w#?qTM}m^BZwCSLGlqt?LoRTC!Fw5j&tMRArgq0Eh zL8@hkYOrUlMGk5H{!NW7b#a80*enrB3W_+YG;le%d^D+&0_|Ero%m*p!vq9-=lfFQ z&AhZdi5D!W(!1b;;vy}W0%Y2BNcO^VDU&F{s={pgh2#{@L{PE0L(dX=!{|?%;?x~( zcdOg2;<;eq)}WB7=-B6ZxifrLPIeSUHyujN z<{e>)j|d$dQ!iu&-)Mafxf}3AZ`6(+^Yxty_e_VpZ9Jhz;#$MjqLHBPrQ7TgQ&#S> z;OAc;H12>yCloRw2I0=h+lz5)#BLM-ib!3$Am^pH-9)eR-uC8Z9i}a02N29LL(7Hg z$+x8i{?7TsDN9}DJpI4#2%dFjM~8bq*YIsuLW5p;SU)@pB56eGa$;J)i{4iZk4hXK ziJqcxef39j)XMQLVU4(an-8=v2SAwXAkSH`p3FvL0lN)3%UNX8_T*UnnE7PE=Y%sj zl1m^oQdKFL0--=`nZ_qLd#G!b5N^S*3Qe`f+i;MiC9@5`5$bV$q;mOhHGP4W)n52W z(K2-*W?sq)lXk$^0vuly7<)xO?!I3C$J_6#y2$O#B+Z;Yx^`2n-t=LL=VjuV;>!|* zRT87z$APQ!SV`wt<#CDJ;Mu9){??=fD8%U4F>Q%E2 zzkBkbB;1P+N!w!5uzHewj9&4pYU^x(_@4?ye;d1ToupTUTmDtwOR^eHXml7HB6Ob# z7By;7k~fIB=3Di|ij$HXH3Or~BI5TrdK8SPU8NMf^0N*1v}8Z7f))W4c$NsL%K5>wH_E)E4rMK0IZgI~PCrx9 z`|!dEkR8~Oz|>l{x6qj~UwrtK&ok@DHBV44E&uz%`+n@*)P-!L!H;E~fJNh-GFTDcnDu`%ZlNI)kgF3Iw;E`6au zCg{zXyVQhu0*|4B(BPY|T$r>`>i+*FU(>erXFC21#rTuwMDRLfaJWbE$iN>=SwAa^ zAw}n-Ucsj->r*+3celWvIVLV&b0N0~vZ~(6Y}{o8O{`^Mg`v8IhfY|h=1^WS06#Sf z6l<}h5ok|J;??sx$Q##-mGXG|Wi%i3D9p&Wk#oC%Q$31JtIE&QH|}${mJ6!<$!4YG zPv)wm!2Ms`FX``%nKgb9JsWRnPp_P%1Er?hjHz-IOpDLhxYaQU;c`Q&rytIYknsEkR1#( z(Vdrv(zttC6BEDoybCWlfB3?w zC5^~m7L#%1&8Z+3vmloeAJwz`Wa+qXAtdZ%0Nx6ml`aL(sSBLmyD(mPvL>xQF9NUY z1z%T!>3EvA@6-zt#hFu?r1P)^U-=vysdqWIun!j(ReT)jJscWvH~nMCu7pFePxL~s z6RuFMEy6;<^oPl}rjL}f@Oi}T$`q)I1^Xx;z>;O!JYuuNC!7A=Wi0=Q^LF3^&Q(PQ zqgWNq)qR-=o&}6%y-DXGwm#!!C+Iuu+K8n}6&&mkmgAZv4kZNxg`h#9g7mr7?`-Sr+((CG2xW`h zRzyNA7Z$BXPQjNUZ_^HY%PGTcgj#Hsaci#fzN@8gzRhL4jf@AU)eST_MBmkH77~2wuMmOxcmMNq{7@%8#LstKgeYFte_|f@mKgFWNLh1 zzOShdsX!_|0J-NCFQ zsd-*xs*|Hiqv3Xwu-l_!$evMBu{&k#?msoC6Pq6mq(oh7-L5sTB|g}}48URQWjzcT;hgUs~IEZIo8&30pDJ9#s`J$6=p_8|V z32J7|asU6wL+ibB=-<&leUy_&P3Pwc1iijkBxvG(&9;9o2Mpp=Vc(zOY>_x=_3ksY zEI`#feSf)PiUzh1bCmKj7!B!N8SPya?5{l_mG} z62V;K97a=ebG6tEpUP}}Byzf`D(Ot)?Hl`8SWfDCnY1dmhI565+GbCQ%VzC@MO z#Fa+w!=SjV+2!RCYDgHO*9gBp&ji!IMwfCkUgnxjc3B0VVpt{{QxpeaT9VO-BtGG> zj25m|=MFsugOaYFeGse|4{y~jQ5px}M^oSyS3CfNuCSYEe4Ugk+~|;NQuCFz3ac;) z1tC)6(#6^MH++cjZSUsj`C#?-jEyqIgf(|dg$eh4Q~qMTSsN<E42hgH5fM0m$qz z|KlOb?n_9Q=2}zi6(zjEl~~c!-rq}KSSvbqKM{l4ADVA%ZH-?jlDr3@hp{CLGT467 z#g>t)wGlU!X&E5hC9~lrixiS|CIScxbEj%}HNXfRK)zRxK8TQ`y6R@mqxKKGKf`D= zIl5lQ@uqY=FH`C)S08}6k|78Un+>N%y(~NVvZ6wE*(IV9q~UYi7s{gxvk&Hl<5?^w z*YH@^dTP|i!D)?d(X-Ut#Lfog18g?}I&X4dR{XjfJ8^qCt-z?;!J%mZsa4}#Z0+L2 zo7(0^W=T+EMG=coqXI!Sc`R({#Cq?&jasjwvZ+M3mbO{uqb+rUxiM0Xcd{E#bjYo!A8q zs+TDX(js81t(=aIv>HBb~Wi|(p z$uQ$YDt5K==l^kU*0cz%SL)!(rYSrm=UJfZl)r)l09#n%xS7?vTHXJZ(r#nDLu}nB zz@-C>)x9&vfDE^HOg+gqa$w7$ERN$HnIwy(LUAG-;1e&@G`{F*4{JfG%%mr(38d6I z-A(aj6zPSk>j!Qcxltc1NJmpWO;HZ$t}}9nYItx|Y&Zc^ z$2?yjlK-O7LYQRxxafXGlfPx_?mveCo~#+SY4k>o*-vQ-JbDA{HP~~<-$U?%u^oI` zLqK|ZPw#hb6a7H_u;>|)PK|VVp>sAb+B*mW)T6Bn?2_kA4xb$KwoFt zeo1s?tuvl@lXX-nihPG4<#~?wqqRCI7lv2YDDXfIlzNu%Yax={4e!Gi^`qyN{6UK9hmZ%wFPDJPzfg$j0xYbjNy|8q*8D=@sI zGy=1U@EPOLQH2L-iyw)^6S8i)bnQ89EQSA$$#?DG?%EgHqtvEQINIvePk{laM~H2D zsK)*Zr{+n2d;nqP72rDG_8y8Lm-zXgWacc5_Df7^)~%daHxkFA?P_m%;)-%rZY?{Z zefFm6B1*Q%Ax8|mNx4?sf&yiFJOSdp0|-kCSB0l8dd)tGZMc>awcUG(IzO5yyu&`6 z@gn$ILQ0tUD7llEk&UUFFG_uHeZve}fh7(5bolj;tzcF0#-TeP^PU&nW?r`#UUmAA zUNK~FDU7?bI%)ueHsXeq6y-2x>aA#ty~DeVE_G-QFrYo?3A!1Vv9|BJ zRHjl2qm3L%>SDeAypY|jwTA1LrzN7oRrlLQ4mNER?~~h- zFeN&MNA>D-57B9z*bs&jG45_V*#8g}+Kl##?dLxe=*o@3A<=U46%xpBxMKgfZkJme zOkz>wsy`H$-3ZwYIV6>gNV#lI0Ixf!V* zj??b3-Zq{eu)YfAM!=H-tgu$&0CSafC*q@$<%~XLM6k<6O*OA^v@}}s&fLRE|#`yoOj$$VMM+|4jLiHw%?i$O)%cWY}_8|Lz)baMcL`$EA zTx%_I{E@68gfZQ@0Ni^>nQ|aa>UUBfyTYt=|yV#RU+} zDvFbS4NmQknS0y_l0_tM_Dam!6tU)QdQJuWSBUy=L#ZFs$a=50V$KOmH_;EmiSc}Q|WsT|?onSamRnwA*v4_BMal!PWCr|Z2n|C#~z zG>q5zX?REi%!J+}w*79`eXHR!6=Dma1f_lDNfiT>5>UAr?B===zoZI;gFTzw8!Dn;3l~u(`bAnw(2HosckNk$XkUeD>97i_l=0ua%qV z5h*jGM+2I`L|K*8Z^!)o`*CPy`x}QWS;k=7#84K|m|oDF`2T4EYIB$IG$tm*u04;| zKVBPz`@@jdQr0aWhL4TvkJ~*aCa82`3MSPX16c2=gvmT^dXVV`F<8is%dQhV@TNe` z!d`1f;)>2O9b!YCWv-DiGaX?&f|+R#%6fIr#;s1YyJrI=a3{s^W)7w_Tf}@TEc?V~ zwsx8l1uPdQs&2d;rt8SX_7SI{~1H9 z1@zwme_@7;WP<$1K&oD^dlHB<`_*}v95$s9{u`s8?Z=)(W4wN$*9 zneJap9@0@^nH7(@lXrVb5@pirIiC-=jqM*N>#H~S3CvPUf51TW^@L%ueBuq} z0Qv-5%j{rceo)9?+37ajNuS~4;@U|6komY<;x<{t#6aE085DQ^^rFexIe7%=mWbVx z#{IkdFPut-_iB@PX8aTjc!&?J*)!3F^_r<|1dZuihoTZlLf!LB_#wdS^9 z>KOtvDCnl{C%wqOB87m= z$ZKg8yfM(th>-_vrf(E8pN_IN;u-7QOlW{VCJO!-6I>fjb03fv$vj_ibhsB{b92pW zmmw@93FMDE_Z&)s%ps9bj=Z}IoHhJVD(3DVy6UxtE&boCf_O{k2M2iZvs;*511q(I zF6*N=0fVJksMLcg%%2e?KRfX(y%LC%$amCPB@1RD=p& z*cU-|2DOembJdFyp=_WgOk-Z;^Er;wJu)PZ*CZ`0ozvq&-7(1rHsW5Pyi)w?bJh>2 zi7=i0GfIGj@_+l6}5&T`SYnM#4u?U#9%z+oAmvzgW8e{2XHTXdu;jH?Px!0dJAu1o0g zv!KwP>1yoVyO0x_XLFK62W)VkZsUA;mY-1^b7noi2znJ;Zv_1yov~Rz+P(i*=!p|O zWWDygU&=Fwp&8m`X&C>!4Sg|^{9_Ohn2d>n?NTCb4Fyr)%LuoNQocMKfr2zFn zn?%OUkSf}b<{tOJbGAc|S?nFx<3qaY4V8e@2veTw%P-quX#*}EM|EH?fIK}x1VRx) zbS<$rvWr!NW6z54UaT*`;JfSd_XyEH(yGA}U! zgA5{etSESZsv`Prbq?7`+R9|9S=q+%S?5wc@ZoV|N?@RB{xPk$?pN=K2$Te!NSn|@ z^*@E*f9b2z>D`+EPyBU(J*rXzJm$xspw-kM60_k3-#GP3akuqes@C9Qpnr<+wJvFP zdWQ{*#{)^_AyNlM6LsP_N)$Q-IldDt%yO$x)@fuq%3JZj?-kKVu+y4<_a}8#CYr4+mU$2G-P}m8C7S7DWb`<*m`ljr{tR zlEICC-=+r4{)N5#yK-z7bYfBV;sX49?Bo%2hSp#|dx{}gsg|0NxDRt{@SAP>^!es% zu~~nb&#NXEWd_xgxcc;LDV-e`yComjDrF6Q+G$EfnT}%4uM0GPpe+fOA4pKIu3F7y zn|FOv2z~dZPk|cBHqRp4Dm7pWhrN((3t*AHkfy+!Gulk6cJwv(>)0=k>UHYL}qTpel*-9%~+HET6 z%WsbH1eu(H?lh*q>1ub$|HO?K(Mt#JMcX?)|(yefNck$NKhD zQQ8^1w{wjiqZvPh{-sWAsep_q*f7*p0h=0sF8o$}Mg4axv~xe?)!(C|S>^|_e~V!d zJmc$mw_3Q_8{CGIMtT%A&C?Q;KuTv&7{{!Y$FB>o^iI3E&adhz)e7r5uELPhEW+aq5P>p`p=_rPWoXeBliA9M2}(^)Pne1q%?xu7cKb3$=VVeOXttbrBtk zA2RG*j*OqFY)R^Hz(<-YhFxPYz)H6@KI(j)z}RUm)tLTU|4-X9OV8-7U?D&KjL2Xy zX7$!On*EiuDfjMy*Y?`G)fx5=g71ECAq#e8h|{*ql~u{Rk8=k%GKSG>{RiH9?78NB z^66(*p*dkkx=DVBSaVyT;{)1@pTcZB6LBaq@#qCnLHxO#S3dPE!rRCeBPvJiT{BPq zqxxl2^o9=&@eBMn@kKw(^CvgsG%{XySBysNT}`k8Ps#g}w=TjB zq=`0=O2=|!A=9p{dcFS(1H2uy;){QMTMcL^!OxoqTw@}w{l@bv2g%KSw)Q1S933}5 zFW$L{H`G?v1nQ6u8{qq`Wz~;9&l`ZT2kA)e%FJBdJ4xocr5-W^peOEg#c#9~DA)aj zbCkOGQhjh6KEL9C1kq0s$j&*xvaNOU-Q`~aXsB7}lG7sq+gseQp3dc-2Rq9jn<7DW z%W*J>n0NoBgPfJ4=qJxqu3-rl z(!RPAPqh#{D{q0$BqqJXW_vuu7nX3$JYRAY@+tFl2g7p-^9HYUF=Jj=`&<-EYixUl zZu1d`Q^Gb{bFF_IYQitow4y}nK?t-kIm&%-aV96HwIGRyK6S4P^}c=dNN0IFry8Ge ztH;^a`5YIlh@+LWID$<2HW0QrpEBL6e&-{exBlyg_uzp)TO^76F)?b-N3POv9; zX7+gQ%wRUqAN}~489k;ge+3xIQM6yA0Aq|GqA-m$S#*V zFnfDPx{8rffqT-Fud>^EcDsYlKSTH*Yj$HZA1i1*b|oQ3e;L?RY^)s_U(o|guXZ1H z2Xa(0`2r4XWH_Pt#`;RPv#7d-nCF2*^YxkQ`2J*X(yPX*Gqs zdbW$W))T$eeHR0dr;r?rraC=RY5Dv+SJ!$D#~4{uo4@d5vd%r+Y&5q;$xaD2U4 z5t+jD-ZtA%ExM2=Kk7U-Hfa=gs?@FCi^?Q>3-i5$s^QX-Y5-#=J4=trq$z!Ato-qi zmW@rJ!p(-H4A!$_oKzn6vx4)>k z5zlk~3;o5=jB(Zm!UOT2dN>2W_f|)i1|(RVt52}rdSoveoYxcykU+tU3gYPM3XCLF za+wZ^6@)-XNbX0FQ8Ie-gtS&t8I_@#y{+AIp{bB)_*YILNc?TYgLZ{k?gL&p&8n<^ ziLfazLu5TP>SUF1s%6Ob`~0XhqxmKVF?{@&If>(1CqTzX=I1%1)i z59Wyy);9br+c#50zw$16sEd=M;AxZWf>iwLih?lIMAbk5{hzey0KH820Zz4gbY9ao z$H{Oyztx;X0n?A+gcl~ro8K3P3InGG@K=Lrq6Rui5^ate!(-abS?UlpkkwO`Z*y0M zkbZ`Es1i5wh4nYiw#Np;G6KW|pNVa1+qn}$zF5fMFmzjvHZ*{eGzzNsTk}0!eSbp5 zZ@Z)T_Jx6u&w<}+Yuu$t>!GG{pF0W5s6nm&RRF8oR?vUNa7j|O*oA-^PKka69+JG9 zCTak@bKo~AELxMVirvKfy4REvb@%f0-D4)zM=e%>kJz~S#I=H{dA2ddXGPn_{s@xr z^RxGIaVadkNhPflUWNAg0XP1m5Xsas9QDY-!&y<$ z$EPgi?`S~4yXb#>$OD%!IGL>+@*t-i+ z$EzJ3)k+<*BGu|jc4%DLpGW4JP(qb8L`gz})xj@_c3LIY-svh?$;49Xvhc_$tY8Tm zM5HX$UE)gUK!XqxXJ`CaBxdkJV%U=sgv!IfrftPG0;p)HpO@HlVHTLTSNb_gd9o~B zB#At6uL9_|7+O9j%H(kvBt90%0Pt<#_4Mlp2#+RlQj&l#T@OF2tmY}n<1@FcPL&$* zg$6xs-!6K*G}s3^vbb~T$aAkqwWa(b>XZ9O0eEp*RZO=8AAgv`L&=ZCi?ZtC;lVm* zB%^a*abTxdB$84%upf^-($wDqvC}wrW(4WpH1vqUqa!+@y!VBLpjg>AAw1+pU8Te9 z$$f&}Y%Jn#RQY{-9>do!%fG~Y zb=*<>MlD#v_x+4IsG}FxAgxy4Xacmj*By_kAcj*ODm}bN9zi@08;OfqBmEb-O=ls4P13BR(JEV_$A5ERsE@GPDc(4`iT&!2Y5a1# z8WY~&q`P)4mgI-r{)ks71>;-_#L?GJ>eX(klKPI4iiVb}NuG8-X-u(`XIUgm%q z?;H=lZ9M|9qFww_kEF3xrlU*px9F+7-_#KCJ7svkdt(yLXwvegf3z3Nr{+54bvwHX zCp2sHHU^4+_iwOX6miB9^a?{e^R01(dimM@c;kSuH1>S^QRYX9NZHih<1^BX*FUaE zkevN76Nj%;(A8u+x%q1<2ZDdW-vN8U*4MPB!_tduAW$p z%*%q;u#12rA48#=>xD!-tQb z6=YJi$c<(V<7XWaz|Zd@b)Zd?$pS?h=0DcDeIDp9vA`Rjgz#^er>o1$XVNi}78MR) zax3RylS(Icc+{(jmAMdnvTJ8C{PYDSguA2Bq$Zj8(Mv8a3=XW<_6epe8JM(EhZG2v zJ3?PGez^(Hz)I9?Q2vK??sq;;#g79C%&TR2czgZ$o16#`%xQlHNPc=~hqJHPnBbFXpCk z9C9NZ!+|ViB(3^hJCW$4BlUR}D2iD*@#$QGA}cBjqIXP0$Y9a_dNKUl`9KKjf$SMU)|dAluc41KA#xvRpZFQFhvs@o}Vh&xyc{o{4wx1Ajt;@P~xB%NUB zR4PiiNF70cks~M;B4sK|C3E@I(w{i}KFgUzkAlj8`EsL{wGR*qqn++#5YtagiJXL8 zAFe%F3zV7|hXj@sJof`X%=vRjGGm-*3UbBQYU2ow|KFNhKSo@UBf~PjoZ9MUvSfsZ z4`xAs4$*Bcr<0ck%tO7fe|bBHEIg!a^+pPC}mm?U!%LkyDG2+<#a zBei=U{)U@WvINWeG@2zuax{gkt+yU_=+3f0bq6@ZegMzQJv3F)y+?4b?-op|UFN@U z)*Xx{XFP2+G|t7ZwkG+w(td3|f;DhpaG3NrpT^E?ZG00<$(G{E+RcXfI{IC)YpoYy zWOimZmGP9}q7c3FA9&j}cx%1?2tsds2g~r)b|4DlGlGeDE#McCg6k-mF1Px`Pc!QL zeOtPI-s{j!nN*&$w_p_xo05h37TUOG<_=v;d7>LqN(AEs5D0br1XfPyyQ6_!WY$p# zF_JbzX8i)R)GnxG?0HCT&?RDniK@PhsNG5y=k+P&)->P`a~kdscW#g zWU)TwS(~0e<~RD%H5mtZ=!gLPxq;q8<>kSPs|X{t^U_f*=7cpE{JQGa0-}p25d9#Z z)H@mn$UxhOkA#pr=!ll9fYxPYsr)1ULe?A`+Tn!yB)T_vXoG;BE*Rlq!BkTY10|S4 z{9epd=F>inynYe$X`wAk$SRB~X3O2*(IVYbRD!sLqj2*#pU%!)%xPp|e|f~MOb5E* zLZo5LX~Bg$%D<{c3lkbRr6_(t|A1nwPU#1u9T!-KLz_@acrXvOfQjiJZ*mx=9t$Ff z`YFpN5XfkX6z)6sRBL>A5S2R=^>J_2}rbZ!MoWd$~= ziR0;^+0i%mB}b&-RrTh)o)rPbgI18!ij(=DuJYf4Xg_#v#D+s>w?t!;>EvE?Ybznd zJ|?I#WiB`fkB@oX+=ep9caqz3I{8AE1;#S;2!lbY%<9yd-YKZ~sPHmZ4m3r0s6T0Mo9w+(bJo7Sl_ZOABx7G`9oHV>jXtFC3zQ0p#WMrvZER{ ze|d9QgS3>-New345C(phRyz2TF~Qd@y&n47>p=WTS@=qDiVzyNe?vZo7=}TOFDCK} z^&dKqAO9!*Z{}dy>FZq?JN_}; z+zU-pMY6hkD1qGC+;y&a%qe}Cy%j|f5iSVa9}Tewce3fy@sT`!30;zE-42(h31+W! z#4zTvUdZ{ft$GHP+iO?Cki?mz|7!#UpFEr=SEScH8h4-UQECmo(heT1hiKtU0iACI zFcN#wB_29U7Zdx^BZ>IKn!;wtQ}^#7Qems#osSD1PqVNV)NE0U!FH3Qef>igSe|w! z+z22?Vd@%JF_efKYLwwGeXItqY$+^gVp6B5T{K!j8aNG#XIOLRd#3`+6eRc@qVS;J zsa9`7x@D3J-UsYVBXWMJ@==*2KtWhBK8W4>gfcy0p)+E-{mpMYHsiOPq?u+g2z@XY z+ofTM6aqdp3v@jm1GUQ+uDEv)+YBvTllBLVYB~7%H1TT!&@gBbKLDxt@em&{T)zT4 z9(}!)R@IP}pNA5Q-4fGH*9^*h4LC|B^SM^1)h5XSWeK?IA#^f9IFebtxR;5&5 zn-rjJ8sgK#+8u|kB8aKofim#j|Ma-n;QZH9p+!*&d1?Hj#LAfg$xt}@;*KnNzg0CO26}p0ANM1gf^hfO*`XH^T5OA z&5OJNZ!sh#t%w58!5xMi!L1_V-&+p)mO@{2V`fHx(5?ueC?O$RF~$~r_;p}(2Ccnt zLP@qwu`)v7E-I$iReN?BbrL7&bFWcS3iKqbSQy{`I|U zA`U;Sn^*%XdW1}(B@p%gbD+QTbCzk|$ldir4e+d1-BxVx!@1IcT*OR94k^Dncy$= zfjUIU{X19*n~q_P!0Kt;C#lQ}!~rcdZ9duMI=JWAhJf5}m3tVpA-t7GEZ`_FvOzaV zII}ET5=*AKU7fIoXAdngX?tD(12Wzm*G`j9=9pJj4N7ST9dI25WVk#)GFc3D6yce% zsS2>QLJSyIJ`>@(G^!u?W3>d$yPaJ`A?;*nS{6R(id*DXt>l5_5@9G;! z(XljXLp4n__hyognn);BW!!$U0xHiSF48@UmN@b%afeS~>|z41rIt84E=f+vsr_(r zF=qq&UB4BY|c29@G4lK}QlFdZ|v5 z?D6zoYC6vBJ>w+d4LuD#+Ne5#n(n7xe0c5QoV*wlj%a3DU9CfS?dP~5eKAnZ%2@~R zl4UB`N-N1q(F;oqakbN;yn7~o5TmRlT{U}wd6nuZuMajdrnf}M!tfE02>U?VBwhY7 zbKDF3$e_{D8vmh{L9!fGc;srAlpJ)}`kxPA@3DT=*G@>nr~a>l`hQIjOW&A;`*B?5 z3`)cJ55!uz|27cqDnoSWl_?Iwl(fGTik7$mLzNgFo_GN(*;>U(Gfc2*JEer*9-<=p zqC(D9hRh`B+^{6J(w0MCpgAnT39F6iy-8s|>rbe;js19Mpg%!rcoO=HFf&S9P>;@B zJMW$TkS-j5rah_h=zX{>i;_Yp4Efqq8DRN3w~4oqZH_BFm%j0mG++lTDF}e{!V*cY zm@wT{tr4f$HO{2hY4r2XG5~Q9=SQHR_DK`?uh}Er z>5(2EiApuB&RFAfLwKO_*sAo&NCUVS7D|>)yZ7e1Z)6PSnzw7BO~(vFW_|GY=<(@c zusm~KL4O+8*X_TL#v))!X0aNP;}Wjy=kFoG-~dwOhx`H?eVp|0-~n5E zCHp(Tkmu7&fj+1QiL#4HccO{p%d9CQ{6E=r7=jf+-Z}~sJ>_4A*`Bg+N{yP$GPewb zybf9D^?A7J>20bj_zh#XI1 zgNAHOuzA6LKX;=d0$BJedM_CyIMnyw#>OP4c0HWM7piUo8eorL?z9BXif<$I@5A#b zq+Y@?it4!laidyLvd^vUwc)Je&Gj}e%LEjDRBz?igQgNCoM_uK*}qH^}yFg zSlsqo6T35FA9Ej1^MX8S_K-(FtWqkrPTMsJGC0Or$ygT!TN+K93@u}n#yzRzTfrIm_M{L;}u zctEYJLY?y4YflQi;6E4Ol_A_+2UJ8h4i2n6eU>Z*I43lv_ znD5n^yi-QdU&fk4`(7LuYx_6Du`MVz z%qQ1E7z7KPHP-As&Fn*2lL|`23u@TIu~PKo$5@2J5pJMNy24##Vc^R2@ieqF8t94M zdZZT~bzK)cg~ex4kw7m?tE&1kR-{-#T4ZKsgWx_v7T0<7h@hY#r&bI-`pVxwIW;Wm zN|8AF=4%5I1H674*zd$J_Y2-E^jG<~RN|<9`mS1*#T#0s9RG!iDKjvzEUQ0Uh%W0w z(+%+wP@|SgSC)n6S|qhr$kz3WiHQ+%13^FWmt5~c??;xN`!BOCU1M~)Y1F@;$?0}z zN=qPd6A}FbhAE*n6x$}$c|RcaQ&@-j1}tfzn)kgLvvKyMY2Ei zt_?qfFPfIUDqSafFOm8pW_o@7ck3tGBop84pL!KRMzAcYZ&|%9WBYKp8Pcu4MMp;; z8{!F`7XGewWazL}=>QfaOZ;6zYeQ<0d)8@c=zyxH&KIYJ`kodYMAvVrpZoo1dtZ*? zhdfx+2zBJDi#q*r@e0~pKKL;ue)L>IrHy|FOA1%eXUn!+x%4@yrpr9{_~{GxKUrWl zek+RWd|Sw1d;O~#4A?cK@hbb*7Z#^fI}q>U?8d!RQh8j-$*D7tU%t^6 zLJ*^dZHg}{Ew}#jfcFF~0`sjQL6liL1%kC{)-{fm=+i2fiGuXV9=;`9q}X=rPm&oP z?%&D6pEJ?~-Q0It+P%-8lUPQ(gzXRBA1VAzim{*UkZ1ThdC|tKW5h|oMDlqR4)!yf zH~@Zf>}Z+Y{6y$3gjeJ6x-PBC$8&`<7=wkHO!EumpFShLBw3uUuD;C3snxeX(Tx=$ zz}Ii`tsPfvr+6(bzYg0!yQE}Dt9Gc_B=6IUO+~l2nQV^HlyF9V$ z6uDdShPpJSu~)1{YiisoiY(qja7f)cRK_1K%7KkoDLaz|jH zXJKwt4$tnV*pcRwnazMg=a68&homd^y5HVpy;v3Z3&9bu-WvIkzd-rT)>ftuUF^fu zb>q{S@)$uTurg)i5*AY!zjD2Re(YK6V5(4iOz8U zbF=m2n&QTKu0L=0OW`p3G^Hmhbp@I40^u}h;EMF-7yCB9A?#hd-Np&i&j{I-gLCFD zWzQIl{7$Be8uHCpGp$hek{0+4nM_B}-jB;y7_;T`h|fUes4$myh&uDaavMk{M{ zL|IzoNbJ<@>=q`~IyKD)ps4Ofn(@ct(#jxuQ|qi8D8UF_l1BtTKRuSfBk$|x`|<#I zrX|1$G(3O&ou04F$A2@KP|NRS%=1rlu{;l-su-YukF&sxiev9ORqHf6G!#?i_;S+F z=WE<}(;Q5!!}KDiSkvr=;s=h&;p&0xg!}pLYJnM&p<(GmtJ+r=0!_1HWRAZDPk(G5 zQvJDn)nkhB(ub%6bG2~4lJ$X|ot-$j8+*r~`Uoh{s`w9n1oxBuTX%D#dCWX+zZ_9` zE<6|1;yy34CyEwYSZvT_=0sS{UOQ0kI)HmuJ?j%q98ViC)=>=Z6e&y`Sc=|W-Pzz5 zc?x#_S_!Vc^?d$?{fyxPUz3?;G=+D{Eowzar{-$7{1Bg{F+VHI8E?R@&VFy+PK+j) zBJ+j5&+$vJLJ|SWZ@8l14Wfk`*iKTNw^{l<(E_64Chp6Dk-EBo4*Dk!4u7nI2O|e` zrYPLl-$$`lcGj%(X2z5EW%QIM8Trj)i7Ch>Wi@7~A_~6gysh*x?=gf=%-C zhBvi6NB*aGbOOXo40j*#Y1TXL`~La&S#wEJtM*y8nceM@y1w>^ukIYk7E~W^mOXpH zLq6up&3uSi*zK#Oq2q-A&0l_9)Ty7fXI{VX+zq(3$LUun9{0o=R7*;!68~cuRrX_E z_rzlMQ~FSPy!^{!rq-Qumv|Ldny{M!{zyqKtekzrk;IUi2kvyggOE-i+UZy*D8yf<%gp8#VdRySude(9QtQcFij_}=V? zrnQuOGb)OGzAvw*7pv7|P4Q{3Vn@(DXMXi)Sr`&yc#MoB*VqK47Bv&lr(Q6A=Z>pz z54Txq^jKe`bGwKlcFc z{L(4hs50Lqj5PoUtx0WhF!UMwD+;z*O!=xx^VH8EwR^tu(52;OzrUc|^Si3Y$+?id zcd0OZ<6~HITXj?vZQe0gU|)|RS%_c%2_o;}I_8FRzgA&lb!a7`cjZXz%U;>b-J^`? z!H-nVsDoUiMxHO{PYbYToA+noyp*Ta8;|miPtMMxR{DG~pU|I{ZRGk?eII@)sCD(8 z_2TF_B%IYF`1|K^_TJTSDfm_GYm;wNa~ENw0EVgAYiBHGEEK=u9CgbTBf7*k#U}GH zDRf|Fu6JDL0`rN38%kJ>cCUKnQ$3cb+eK8LzMfv=|MO=n0+!Ti`g5kQuL7%5%evsj zm6JiE34XA@>N-rM^bM!=`@&Z?lLd`f^T}83CkRR{N3}nb8|%L(rGS&2BtoAED)VgE z+Gu<#mSk}B*t#e+EsTd|DV^ zv0RcQ*5(#95b}$b!3pE7@6Q9Oh%QTNBeuU!ySq;Jj`WMLxbkhKId#eoUMR0V-{<4b zn40vVUNVya(0!KP?~O@ThSBKXzoP^rUKh{Zj`Ay>T~lIsd%$_jq(HRA0`F`oN*^Z7 z#Lj*Ae}%nuRFrSjHcEq10!j!7j)XLb(kTOo5)wm5gMiZA4N8N6bV@TcNY@Aw(#X)M zq%(9k-_7rRzjf9+Yn^rGPekT?qLRS8YsgDq=;gZF$x#bJR7~*&%i!& z3WHJH3#$TTe}n*wJ*QbJXX7?_xKg4q($XD9MP zP_QmkI|R${dRGQ*-go+Ux25|+wxPHB`Et|0tDNYO1Q>Xvoq^a&zBm# zGw}zB92Rk;?L~Px2|m*!)0Pdr%D(9*lpsXLI86kyK_`?SJO1tx73RjJ#`i;jr&n9L z`^6b#XPDWlAkC7|f3G0!ziwuWk%F6AwRNbK6Pa(f=KQ^OH3l!Ss81$WK1=c$Dzpz~ zj6P(Ao>!j=`O$x_g5) zJL_-}NGI>hSe7Gp}zEQ(NHCni#c<>_OLzc#0)9tgOk&s@M(brF0Z z6YxRu(R|*EC4c3PjCJJkn@LuQ>xgo{CcIK( z;I^j|bw$1>^T1lUhC&*hkbFm)R#s!!jPYD=3Wb&TbBdPP#9c-@T}4J!ix^S7b8S69mi%w&W#cM>%xuortSvI zdaz82Kh3g4gHMgin>W?S=9PjwLHdfxPbfqr^vPa+Tmj*PSVoxZ8`KN(HtnJ#K26Ty zMsEBO>h}LosV8`4`bAV4R6FWMYqy-HlIBLkslr`+%CAy000*udMuAxQ%)Hd=* z$9wYJhK>M%_+(SqwklMNs8i?9iO!!BQr?I3D7@%g(YEWcpGW?Qj=$S?C4wTER9YNu zEZKakacZF6qmWWA-|9vrZl~06Uwe{dDA#{zXWnJd(N6R9(!!HhneXdg#a8m``J`a3 zIw2=e;r0l8>FY_Q#NVG~R1LDwo>A(j-uk4b;jaxV#uqNHXt9gH)EH4N1Mk5Q;4bjt z)g#rVto@DmSjPi)R22$;~9H_0H;pB+0U+Lw`G9Af0C2K)prlV|2k?p z&+Xo*m2AVz34gsD{qVgp5^odwEJG6K`N$6x9Z<{r#=km91KKg|T%6MJ13v0Yv=dh1 z6fr73kH$_7AoYI(<~BdtWmxK~?G_`~)}X#PLAW!LToSq0^KRSuSE2o88JwPf!Ck=b z^Q4!D@VPYWm7`>hbo;Aa4_XRlbEAu6ZkPH^`L37QA2-}KtQKrV$&yWgJs?kDt=Lykxu6# z=JQ9WI7?WWeW$7T$S0Quz0~xpho1H7qAe0g5bPRl;q!Is6yf(Z6&}L*kLvU#haA*y zfYXlW&4-}e`}FM0|C--p5p=_1#oQJ#sBsmqCTG#OI5bL+53p$VPRA@qKfAsP{xz)n zDTd)4dbzBr<(PTt0O1-OHX;)ohJ4vk9c~lQG!y~qjz^ISg_E8u_|D=;!5Jeu`D&kGq-}J2judt%XyB` z@nYhr4`3pF{JMzpYa3AyV`SA=LeQgZ%t6g2>}TX&D}&kVPs@3W;LUl$sdWC)q$>Ex z%(arlyo#}(9qWQ<$GP*kbDlDn^2|gbXev@A zed;aH9u&MR-Y6wsuwd>(J8k7jVi@4HRZa-WQ(a$DqT4eK@RhgrUp)4H$%wE*?CH0y zkuQ8D;_<$e_=4I$8hVy<CUX%;UhU<9p!Xw&o-0^qD{syRkfd7HSU45d72G zAd>9sCwyO^n)6ROECDV!a-q8B?0-!Ff@b9#=g~lZ$huHJx0V?dSwClF)X=M?;oy9} zELO#)Ec|Q4dB2tfBd4AH9Sgo@{-L`@+xo#9a`aMW1q@_>-fSor<+IxxU7D4d zF0x&=34>QEfB!$^}Q+z7!C_gehzazIx&M;4NCH zp9FpN)NKuxk0}H$Z`SlwNz`xi*AyTlv1q$B<4SdTOoEz5FY zMN~T==Cz?<70|G~4E@&cpmrjnj4E!6`fUU**Lk8mAB%&L6eyjdGJ>87q73SKyP_+%$%)R0Blx(K90XQqs`iQ{diZ~Dh~ z~gD`Wlp(fKDCzTiq;gjLJwh|0GS5F~J;QL4|sPjj~9-gq*}g zotUs)K{D}Xp5;^DF?~dhv{b>XZ#38-B72~PVOYm0{+sDgX2`zd*#Clpo9TH|F+fqf z9n{>4Q8tk09t%Z0NyabjsJrF9+NhEKVS$10@@uF8fn&yUUO7+CvqbBTGHWRvKq=hPqUE0Yeza-?xy`v_4g;&-4Uc?rvxp^%;5fnK<`j1~Zdo zm=VeeA}0!52iK3fD&Ks4wB(WUS$bl=n>RABS{X%%JV@mx#1dBwHcjHzzdmhnSf5eF zA6Ew?ae{>o{YCM<>pk8{z$(~X7JbXl&HXzGDXrp6`JLqOPB-nEXzodlq~MF`)yI0& zC0f`@ZCQLZ-(|-1gReT3M3-#sK{f&~ZnJy4L>q=+A`9-Y)ww#-0TT(5>~Ulsogz($ z@%?e5S18O6Ha4FA_9yj&%X@R6_3^?~U}79lzJc^U+55qY0_+i??lDb9B4^_zHY|nf zCA?s*Md{*MqM6R(gV-{Cx3l{2`wu|~ zSHNh5=?Eh;$wQdnuPA(9V8gpdA^&Mjia&t7$J-X&pZA`@J%eudX>uat>PPhqzm%ES zf~%Zi_s5SZXNmB+($_M#_(5z3f4KGEpPyg%_bp;OYbUyVXQSnfJaPi4NqJhE3~cAY zp8z;}9fzWO!u|fg!x#_^3<{{T;nIEB8JT?qTrBrt%d$|dO?lcr$SGUU1-6ewk2ijG ze)gf9&8#~Sm_=&Ip0g0;;t<1cQQ4#Q9bkG5afL}623*vg2xHFr!gmA(iXD0FBbSN% zTGDmhDAgN;)chVp#z6kvdHzh3vl|z|8+%C_aX(Y_{Z4!P-`68N?-E_!J2@@?E?VY` zR9kH-?t~ril%TXUR<#$0)7w6%n(K=nKhaXx$aUMll!@F-d6|r#P-F?TasTK<^K80kOZ6!E ztV{N@mbRv>V%{@zolbTr`(MZdzSzFIv3(7%T6P@<&X((!y;DeOVz{2UlyuCr9|H;J$|!i36sHryX!SIz!J%KIciy@ws+U zWV>>*h^C612gJV`pf=Vy(eo$g!3i@>@{=h8_ z+MmcZt1v$E$l2@5mb4_UHlrWDKE^YZgtO_*UcpHi%fF`L6By0GsD(y=maIvkCkPzz zw&$t4mw0i2U`$_s|KLnaFv$Bg)bFoHZiWA6MpQ#n$*tYy;Fz_0yZUPB<>Ij3-493y z;^&h}2J|?N4Q6HMdFz8Z<4IL?tXJYAgaJHfzH2+OcO4L4y?~(d*O*i}e#Yj7k{gKP z-mVB04`WnR#by!1aUgRNU_b2=(D7xCyuRL76MOEL~Sn)>!7nAF0Oo-fhg< z*7lr3aeCS%L<#$>c$rgDh%(LdwbMbVACz9#jZ??*3?ETdmm#JAEw9}|K88EK{2wAu z;0tGelQyiqOyrWNBNb_Ia21;*I<#-Hr+gmy!Su@nNPzRT@6X&2b1r z_ccog)X0bSrpRnheYpQ}>SXZbmEY7Yjp^pBkwM4=JMw{@xeAExPXwg8{|~+CI3C|L zfUv88lf0tcc*97j5mXQ!etGG(*xHA_N;%Z|f-S`WuBJ{|nbuW_`%VY%=gh~V>Wc|z z7S`gvD1;DMftSG=OW)IPHCudf_dhe3=aSq<^rM@M@7r1@;ej{;j=TE zdU)@6Ang^UNH&@Lp;{1Rab~T*7+DATpSNKT;80xa*Ytdf>0y#sN)aY*$8cK zxCQo2vKDzGuTA65YlCZ#9f^(6rL;3M`|I5LA{Uat>*)U}51hmBewxGTZZHPe>W2gX zi&TW|5Mv$N*bIs-Bvr}9Ncg>n!Di8jPQbS!A)lsp`uK|7(pPF3S-VY|bhXJX4(mH^ z94sR2OWB`5R~{KfuSa>e9I%BgGg-jL>#HA*~kbl2I>fGEMm>!Laj zK7he4d-(VG7;bfUH9?kI@&&wA|M=9MhL~i8q(%E_u1>b)AgyJ7+GjrMlsw;gz)`V= z_YTYU0*%C5<$UV8j8w)B8$`5J&`!Oc0bD#X0vw>2a3ik;_}0aF=#|6H&^ZnuhxKk>a}thTC4BC^&RJI7_W|4Ot0LmWWzH+|pDbm$k!yz82U3)wIE4FW z;aca*Z2rd7qt6ts@PZ=};IvZ1WTSJ@xM|Gf*%v!4s1 zwY6h)#DPmdY}0Y@&<{Derzhys4!0FGfnFcnvYi}qkypj(ah^R2JCAc>a51C6UR>Tv z8_2_oXLS-`Dh=wJEa=n(Lxx#pN!FJzkY$Wa*0ReL-5yGULs57-A(NEt;JQ=NVL?y3 zO(Bc_c4PFjR2ZTLGJvmhFT#a$W7b5oleh1a0;>6O?5izP9X)Jh`0m)B&I6^@FDsZ+ zu2&YaCW2wR`HWvuOnahR?0M_6ftjC%43Ljj9M&EdmD{3N4?NCF2#7N$opq{AHhEU# zkU+9+cB#Bu&Ya~oaU zxlLVh#k<-*&u0XA1=ZDHa17wWb&!0-l%Hr%nR?%k3_@3In}j*+4oLy6L4ng)1AWi_ zwz#iWI-6hca>Z1}?D_Zb-ZA(9)g4M8uDHtAK05hq{8cg@dGgj~YuYp@d;jG~jkLih zqtqQiae5cMdDW?eT}mU4FFR+~z0*N^AwI@#Q(6INA4&dJyWAJr(KVNMjB5By<+k+t zRD}B;7rV_P2jO&>n`$UQg;nF%pKM4)^`He{I_@CEv-Z_4x^3Ko)`!d%34I4u!PJbf zPcZvc>f++|P;gsMl+tQZ=4a}6O`1W=B{Nuxh0cJW+4IlF){zP*uj0yszX=FT$i5VJ z(wn0KZpUn!8YQ3czJ!TS5J&EfK*q|p_(6d-N*BE@&8e?wb28za3ZDy#utUwnd_S^p zqX*BZC^@=KIF9lb9O4Rt^avfl?g%bWxQMNmetE7;Hfo2t9MA)OpSb=)^8IU`oLl0G zKrm8-z+E54!yxKaN4>F_D|Ff`hFO2P(1 z0g0}s3(J>e54qlTartKx$e5hV?&YsXfm5|ZTsH#>k>;`JsAJaKMkZ;vXO**f^sJ4Ap#!jS4xjda&8j`>nGymV?WC1Zm~D7Z+Yv9Zal zI%y?m$~Sf|Y#uo3!Sp7|%f{)vTs4KhIueqdguC`a#=ia=TjvB z`UQ4$e1E>-CVPr;_;)l{ewb}lEh=zmA|ndnNG3dg@Niv@UZX1VksT ze~*UC&{S3K&0D;i9-?>4-5IqCoSGih1nL1QSWcbq+-TIF=AMaFvTdZ=%WLXnKO872 z>K*=Xw-52-kyNTmN`1wpD;Mx|yi>^K&jYzDIFjKT3(!k_)Y(J(UqKt{s^b{Gc8-1C zuH0IQhU`ZYB%Rbcvuw_EEJ6cm#VbC_Rhe)mswtudJEj&aLuZexpEM2h@xT%cyGHr{X{GD>+imZMMPMV+!HqNK+@c(=RTK)e8h0;|^BR zx$gnb8;{4jxA`<@qNrBF4pv3P2Oq@vfPCRN`JXJI724E&Vd~;+}b?=j! zC99UDLaR+7P$^lGMIW{dw1OkobZw#On*yIR()poePBj@YSxXxQ-!{?lR!!*ZNJ_#` zfupYA6D<=H-SxSVIMD?Z3`J|#cys6rB@0TP?l!jkyoI_k!z#|J zc=n_V-!**jRN;DVGi%b>77-hFq_}t|2l_}$iQRIIasP0)2#;Ji3b$^wo}^wsbm;wT zOpP{+JI4eC0Q6jMeDnGOvMaEt&aOASUc3D<3R-RIZ!2R@!b)VPGOx)k=Q#qPxB`=T zWPR!7LH0r@*_qEpoY1tZV|C~piKjm>gK7gpUL_iF=P{JseG!?0`<1_2+j4n@&1bh` zKk1yc20Vc86Oy%d9d7N|(as@1^6ipWk|z^*0pmzcgS`Exxx|xN1)wH?S6b(%F(6oEW~H)qxss`b&FqqDuttimzwkd(523 zKN?F41}wy60NoRE@6m(Q1vc~aR@J)-K2vwtlS$R)M^zhZ6h6PaB`K=3w>e?TxTvQC zeOg*EZS5NEJN^G@1t3-8(9$iz%HlC62mmb@$CvJX!Dt{36mFBWlFqY3fV~V+RAYEN ztE_Y{_BoFFT|!HXZj%AFF4r&&7Z-I&8l0*K4VP-TSV5{vZQam$JJ`%(`ygr`f`+Ms zXT+`a79GK#Q9xxijLqTQZkAOv`TFZiken17hS@*e7%F%~=P-ZRhPV$CC;EWhsPMwg z=f4;=h*q&~>hHj4qn^to&FRpyRE(9VT0s|hna?6g;Hg43EA}jwAJw0=V^>@KYNloR zOIM;!Ue7M3Bj#*gCqGPk@}-E4f#~RHS}LBlway|_T`GY4)W71Tm?CLVWjgj5Zgx~a zIvfR~3zSt?@yL`s48}?qqD^l_pXY%DT8FE6->7qxQD6dG9YGCrrMGSSukMg_vv0bY zPGM1Baqv~YAzot}fcNUxe#tH>0UoB&db&D8L)2oaIdSxZ=1)(SO)3V?4|WyjZ`Kz* z{s%H~_QpElTk}I7=uPVxQv7*RnfTU zDP(bf`x|?LP-ci9#+tjq0fnlhK0i_CQ|x@sl-lE<$79@!PFQQBJp^_ zdI@E0j7!nV6#;X_aL2pGG(9DZlXbPLz7_&>s7CuMlIDus$O3xzs!9%YU}a>%S=15p zY8cd$gRCHQV-K-*KQmE7qqK3%+TR?k9=OzT6Gn~O;XzXE6@5dW7(UcgPmi{<)sj3n zPU~3;$+p0}c{|QvR-@!5N3Z23q>Qpa;KI+N7P`fZUs`JDrnL~Af8MbmfdrbKG0z<1olvL8vFNWCWY8s27Bkr&+!oKQQHU zwY7aT6=jJh0^+_E4cN!~qff*t{l(gNt5`PB9^-MiS)fW9eu77u6JK`77Ta#fj#<~lGmmf zMWf!X1A|=(!NhfgheITvrcgvUSg{Q%H`+SJ!7uMm5hho#FAkg2W`zrt9wn<8&vyFp zbYw#4=9=Y?<4vR0FQ}Kwb<}H@rG}PB-m4jI_#YM#P|4086-oxz&j0ubcYL->Lv~%j zt_6erqi6Ye+3>%C_KX9)+4H;mCmlqWVz4!f8jeNOpvmpY5d-=jdY1hrp0|%7r1;YI z7G`D_R|U!oyhNQ$M?q3St3n*dTI2k(LIk@3XvfBILv90aSpn3B$OF&L8%{l1#N10q z!$33Pc++xSuORkCjOY`fXc4FX22voFtVvwN%SF4hb19c8>Y_Y}SRsoy{o(Bd`*}Np z$_0v)vTh*&pa^(T%ga%LC^Of*HvL&QmUUGW`FURU@BprXu>)@2y#eesfZ$x!x6LD! zi|yyh47J98As!>l5zBB-E6zl(%KTN-NyOe~U`z1`fd>b>I`)B*eVjxFNv8XMzjAipobsH8V7C@ zLI&w_8^*YEd*jXBGbvel`PK3uV`J-Yv&cPfWc%fdFFNL$Z8gm*l~&&f2e*O1zVDSp z8wdLG=zIL;r(b+M4sQ6uSjj}l&ngNH;*{~TKr}>kd_@P&Y*4%c3O^sO-$iRvCOMC; zyzliAN&nxO#KeITReAgyrEMNOSDdw%_R?X4pTp4wJnG{#t@GJH`D@{Cfc;;@KgJ;K z`r@7f;QDQ`Nvb)m)&ZzzcQomN7l>BAh_kfE+a)H0OK*QN0-3;Igh<^7#b{=7m#nC+ zzzhvD)(+a8Eq|e1JF@H2zAV{8TEUW*lg z_Z93(q4ojmX52gf_Q2pRRcLf**ZZF9Ota0Myta|Co7ewgEjv3gPKWhSlWv0kKEL=F z^VLPlQQ<~9!T*(i5i|9qT;Ca=Q%+_(-qSZvR}wZu&q(%-f&BiJUH+M3oa9&fC`EHRR!%3M^vwfBBT)^2zrQT30NG~ z-%T4}Cz3#U9#x>X8f@n~XJd{=+{Mt(M~C3>p6jSYXYhXe)>LM5=1s=Fe|B0{ro z7rNjn-T{0VTyhX#(rTM~7rLM}h6jk|DDBe;Iy-bc)_|nBC#1t%C+w%NZurmRjo-@q z2Z(}OtjP6Ab2X(j&|7G2=J!3$#>dtTFqht@h{ZQrHSJ106C~=ShAXN`;@GsE3EN5- zI>fQF2KrLE>q2d7N-u4I?)I8Y|NVbgE)t$exX%ppP-JT8I7xfN{I@H)E<;2;xCGj; zaPSW0RV{ZLpJVM4`pe$}WPV*!h6+BA*oD68Ls>EpaLQxy6)C%~cS>6>0T2dagZ)Ic zcTfXs+Np$EV)tU6EhaCMTNI3)qpTvV_wi+Yshk1Ts3j&2(!~moJyDX$$kI!+3j;8|xOQDb8W9@UzC{J53gzen_li&9`8ubhE zx<`Tj&gG*OqD8y(F_k@_=jY*!^q~x0cid{`yp=U|J#20drp^8}{R!KZ4`cMJa}KA1HOz6$UOQ1Zuxd-`#*oFw$6KI-I+`~g@4Ik38QN!z53IiYlTKm9|X zE63gvu6}-rQP#b;D&ZcJz2fXbOomYVGLX3go-beZXZ))?Nei8GjNT9<`i%>%>*nSj z5#@dIFCQ5%&U}~oyQ0e^Z?T~3hbste&MCH;LLpkp1@B0qfEdfw8?KzmZj&?Ji^6m! za{U>FzHP>M=wbXP*SInWsHivqp4Vyu$B#aAzMvi*wva+3BH=^<-i~;{q zSXU`NKuLAaMM*mUGrrv-!IDMG^2MkkijAiYU& z@@M}-`y+KaKCekzVDXYpFsY=0H1J&|$v^2g%2iuPJK0l9KzUGk{jsi!2hXpB%}o`H zEncxVY7(hqeM`e}_QUa`F$HK8YxZx2|9cI!>h~&}LzS^4RVJiE!{$rwaREDcRmFWBhY%oIG ziBO+?`{4%7Fv5Y~w51PTB>2^KJ!JC%cejM=CxYC={OWGZeCf@ja!5Y0br&MEZYlJQkvSa@nurX|_aRB&T2S62z$8Phx z@&y_~L%UY~cEzusrP;I;h{`_@xZ{QCK7OK0!mb&pS(+!o=@H(_*r7MvPbVJ+VCSOy z5fsvb6tT?#!kLqoP+_iybfKzmj?>jw=w*a(Z;l0d=3R6DeTv4DzEZi=z6fb8_0iH0> zg$)4XSSC;>s=Kkci&<9iNi8Af5>T1WQbu9vtnS;L=Ae-83g3 z-7+p!p_h$Ld}!2o0uSZEFD19e+w!70z$SA*Hi;GQKQ3hF36#J*FWRZ_wOz7JBnA(^ zz^l+YYVj#${Yk)+JI!{bl(8ZH%|K=&{`?-{F@~k3nz9*lVTofRL9PT!c0s7VeHf^t z=L1AxXEAWP@r)*uS>;S7NPVb194L-Ay@-62QC$vd?fA!Gp2pU|)V&U7gZ%i#cxQ;BbA59prwf(r{pC2xS{A$vsb zcPcWUL;}MDH~B`D{<2pv_jQ~SxgEHV&%u4%5d5TM-&PLC$2P?7#;^<9xH@YQL(Vjz zQm%dT3^&{)aSxfuKB}*}GLHEXTG9TkYyE2h@qE;4y=W+G+PPB&ihBA1-EP6vKhp(D zFlT4^PnbdL0VF0w3}atr(XOSTp6~GUEwY_%tm7HSF2F3zv1lYb^qWW8S}jTB)Fwcv z7p;$85)~0Kh$^=({!zX4Agij>D2egQJoG))h)Tp4(*BQutg@wr`)5;^MKefn9jUKC z?Ah%PgyYcuyCj+kGXDB^bpH&JTJ5cFw=sRET^1UX{Pr-NEoTu^4!-{dew|Z0eqrjz zSq)@q{}`3BRf_Of%>eyVu~YjWVkc&>_nfyhC&#D$KbYBWXnHoUtrFu%mh5T_!4+xq z|Ay7rI6J;?9saCEEwxKE#Qwg3?CV*OFD{hxaOuSD<%T=m*3)TfRZPpmT**fDq3^X| za##!)!x6ij8<&70(uw~`3!I`YOR*rtc=l)+G4lpWLiT5bmn?P?*0@-mjP`hvwHf2+ ze{1FRqD}Aqx?;-K2~N{6ZRd0yz5tT!8na(pp9TZkQU=X4^%kwHz!i|i9mbB3i|pN5 zOl!sPj7Pe0N7i!Y<>va-UuPO`w=`mjtKx)olwPu=;he};-C#=H!r(9tH}o@X!>75U zFBl2$W+be^G*;H-8)IRU<-(9n5|NMes}iw2AAt*xd_EV3fpM2Z0V=Hl1*J2UgH#R67{a7tA=&8lF9*ff&)W4k8pM3M1xA!3zsVM7gm-- zv)^-amLHDT1r9voH(zN)bFhKCHXQzQtpo+gtg*dA`|cL1+i?$5;;?DHv_VePud^mQ z$AP-}C(QucR8OGgfT7JSr*be{n|OR{tKU^y#pDuiLktFryXnQ8xi1sxrQ9^e z>3_`7I6oiJe0t=eJf51>-<74?avE-};c$D5@+PG<9l^pT&%>(-$`_?<$7k1C z+J*}$Eq*N|1~rfg{{M%vkhG<1F8QyJ@ZKWkdwe7Lj&kAJPdx3|EqUz2?=-I)|8%-Y zYpA@qwKl)a;yiQn#wRkx1+prpbfG7>(^RKSmG6MMf3zo1iowq2$bel_-+Sr@i*Gf1 zVQA%x-TU^Y+>^d4hHuo#!w%OuBNhXqsYaJ4lU{-0*@|T4c+tloPn)N2X9dPaMf~m0 zCPQfZx`$Gd%Y~9Pvd4V&1W)tQ&+CMew#%o>;nK_8+Il+~Zvxj^Pv$q0@og6~SHnvi zHlKgYYNqg43b3QcYVY#7xL_K^8bxA9cg}xDz%^dKeWdpLspTX1f*Sm9_|#k5cQ2m` zVbYr3!+QB+YKp&*ls`f+nz&BKOXu?9Rf)dWwdi%sR!Gf_@Qu^lR=xT4Uw5~w48a}w zabtHhzqxc%O2Pw#VkL+9^Y#6u2pwf|qwC8Zv%bE-S_b8lANYbJSo}qa^(s=o^ zZo8{-m`;=uJG)u@L(@u_n#_EEaCmr`B=pFGw4$$%8RPJf$b(`Do1Gn%EONB0*LuEA zmgUE^Hso{J8Xn#x!f4fPSD`=8`0dWV^{9XKeYr1U;N4&jHuLYdZ=ld^8SwmHbyyDO&7|-7E<{Fcw_zsV z(Q{sBFuy+GJ#0Ohfqwk=d!4bOB={T)YTsdU>@ib}WL1sDJ z*`~O(v|ZS3TdU5+b=#5~sSv;DSJ^GD71pg8P#Ca>=o5fm3}x*Ize^t~9RA9qCzSg* zdijBQCgGA6*1>N~s(>fvuGD{nuCDe1{-{+#q0sJ^(yd}|_xJY%n=YsVbeLAJ7W&Fo z@G%GJ=|$z+a54+)j;G;mZG9}eb{J@XC!>m#vxtlg06&sgu@8)YP1ma_^J?@hDPQ=_ z`&K#b$h-|w#^^jc@Q!f-|M_Bzj>l$SLRP!qaLuE9XKZw}ka>Pe1&(I&=$D}?1|dve z9d!EEF z8~XkTWbpni?)lK2ytJwQWOKSd$vUJID%%aQ}2ukrE;SOY|o7XLJ+PfS%r{LSN|b-~w7 z>W^ANTZzU)%)Mqd2o`t!$qnVBa^$$OzGlAA-Fh$l?r=Fk3B`tlDo zIFSqevtu}!kr9zITS{L)exfwQEsf5x zYNg`2-sXwqE^><_$-(co=VZlF#uW8W3ZcQaClt zfb407L;b_uCL!}!9rEvKRZR0xLWL#Yi3|aLNt-nr2kXKhMj;=x<}}+2YjU#KUj?O8 z6!+M~y>iOAtEDL2?_|SKXVZeVm;X*E#A1iBLKG|y{Ak<*nC4fH9(QACU@qcaV;~Dm zSru;=wXMyncEpfER+_kSYQPd<<^RygfsUVxW(y-}@B{t=?s0cjs}W@uiHse3W5c6D z=x9S0m=~41DA~J=Wl6p^fqt7VOP1%S%nA#qt`gM9_3kecI~a9wI7fC=o-ZFfIC3h1 zw>8le@#RvsP{tuz>rYUZ?=}2WPW)@rJVZxG>KIz)=V#}aT~EUKms>nEq0p6p9I$Uc z_U`Vg$2~aH+dd^SQa%@{SMwth5*`rFWIQu^3yOnIyFReIh1Gt{NU6XTC0w}kJ*Den zIr%{Pp0ga84d_<5O()I#m<)VkbEZJ5cJ@lRvPr7PykOBVQ_y_(36_Lgh*O&!`K2A+ zxqo=G!4H3NW1)3o_X6+xJGcJ{iy&MalZ-~o>XpDxT3*7_7nQUGAvEnl?-OodaiSjl z@&}zc8Byos9<>L%z1-urs+S5 z2n~IO`61sRuVEWG?ory)En>dw?vhRk92;3*G1cv|usMJ&1m+m}Iz7#=%qd;;6mlQPa-CGGIGq3f~wvQIWd+Gyne| bmW~_Dz*<$DY3`+B4DeBqRfd+oGz$Db + + + + + + + + + + + + + + + + + + + + + + + diff --git a/www/cpp_reference/html/SBBlock_8h__incl.md5 b/www/cpp_reference/html/SBBlock_8h__incl.md5 new file mode 100644 index 000000000000..cdcd1265a0d2 --- /dev/null +++ b/www/cpp_reference/html/SBBlock_8h__incl.md5 @@ -0,0 +1 @@ +1f4749d3c85445ddb5d9d2deb85ee401 \ No newline at end of file diff --git a/www/cpp_reference/html/SBBlock_8h__incl.png b/www/cpp_reference/html/SBBlock_8h__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..ca71091f00eaed2dd66c25d8e1623075543e72a5 GIT binary patch literal 227766 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

5&jG(it@f59LM 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 literal 0 HcmV?d00001 diff --git a/www/cpp_reference/html/SBBlock_8h_source.html b/www/cpp_reference/html/SBBlock_8h_source.html new file mode 100644 index 000000000000..ce1d6b4dcf65 --- /dev/null +++ b/www/cpp_reference/html/SBBlock_8h_source.html @@ -0,0 +1,168 @@ + + + + + +LLVM: SBBlock.h Source File + + +

LLDB API Documentation

+ + + + + +
+
+
SBBlock.h
+
+
+Go to the documentation of this file.
1 //===-- SBBlock.h -----------------------------------------------*- C++ -*-===//
+
2 //
+
3 // The LLVM Compiler Infrastructure
+
4 //
+
5 // This file is distributed under the University of Illinois Open Source
+
6 // License. See LICENSE.TXT for details.
+
7 //
+
8 //===----------------------------------------------------------------------===//
+
9 
+
10 #ifndef LLDB_SBBlock_h_
+
11 #define LLDB_SBBlock_h_
+
12 
+
13 #include "lldb/API/SBDefines.h"
+
14 #include "lldb/API/SBFrame.h"
+
15 #include "lldb/API/SBTarget.h"
+
16 #include "lldb/API/SBValueList.h"
+
17 
+
18 namespace lldb {
+
19 
+
20 class SBBlock
+
21 {
+
22 public:
+
23 
+
24  SBBlock ();
+
25 
+
26  SBBlock (const lldb::SBBlock &rhs);
+
27 
+
28  ~SBBlock ();
+
29 
+
30  const lldb::SBBlock &
+
31  operator = (const lldb::SBBlock &rhs);
+
32 
+
33  bool
+
34  IsInlined () const;
+
35 
+
36  bool
+
37  IsValid () const;
+
38 
+
39  const char *
+
40  GetInlinedName () const;
+
41 
+ +
43  GetInlinedCallSiteFile () const;
+
44 
+
45  uint32_t
+
46  GetInlinedCallSiteLine () const;
+
47 
+
48  uint32_t
+
49  GetInlinedCallSiteColumn () const;
+
50 
+ +
52  GetParent ();
+
53 
+ +
55  GetSibling ();
+
56 
+ +
58  GetFirstChild ();
+
59 
+
60  uint32_t
+
61  GetNumRanges ();
+
62 
+ +
64  GetRangeStartAddress (uint32_t idx);
+
65 
+ +
67  GetRangeEndAddress (uint32_t idx);
+
68 
+
69  uint32_t
+ +
71 
+ + +
74  bool arguments,
+
75  bool locals,
+
76  bool statics,
+
77  lldb::DynamicValueType use_dynamic);
+
78 
+ + +
81  bool arguments,
+
82  bool locals,
+
83  bool statics);
+
84  //------------------------------------------------------------------
+
85  /// Get the inlined block that contains this block.
+
86  ///
+
87  /// @return
+
88  /// If this block is inlined, it will return this block, else
+
89  /// parent blocks will be searched to see if any contain this
+
90  /// block and are themselves inlined. An invalid SBBlock will
+
91  /// be returned if this block nor any parent blocks are inlined
+
92  /// function blocks.
+
93  //------------------------------------------------------------------
+ + +
96 
+
97  bool
+
98  GetDescription (lldb::SBStream &description);
+
99 
+
100 private:
+
101  friend class SBAddress;
+
102  friend class SBFrame;
+
103  friend class SBFunction;
+
104  friend class SBSymbolContext;
+
105 
+
106  lldb_private::Block *
+
107  GetPtr ();
+
108 
+
109  void
+
110  SetPtr (lldb_private::Block *lldb_object_ptr);
+
111 
+
112  SBBlock (lldb_private::Block *lldb_object_ptr);
+
113 
+
114  void
+
115  AppendVariables (bool can_create, bool get_parent_variables, lldb_private::VariableList *var_list);
+
116 
+
117  lldb_private::Block *m_opaque_ptr;
+
118 };
+
119 
+
120 
+
121 } // namespace lldb
+
122 
+
123 #endif // LLDB_SBBlock_h_
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBBreakpointLocation_8h.html b/www/cpp_reference/html/SBBreakpointLocation_8h.html new file mode 100644 index 000000000000..812e217badd8 --- /dev/null +++ b/www/cpp_reference/html/SBBreakpointLocation_8h.html @@ -0,0 +1,75 @@ + + + + + +LLVM: SBBreakpointLocation.h File Reference + + +

LLDB API Documentation

+ + + + + +
+ +
+
SBBreakpointLocation.h File Reference
+
+
+
+Include dependency graph for SBBreakpointLocation.h:
+
+
+ + +
+
+This graph shows which files directly or indirectly include this file:
+
+
+ + +
+
+

Go to the source code of this file.

+ + + +

+Classes

class  lldb::SBBreakpointLocation
+ + +

+Namespaces

namespace  lldb
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.map b/www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.map new file mode 100644 index 000000000000..a2316b0f8748 --- /dev/null +++ b/www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.map @@ -0,0 +1,3 @@ + + + diff --git a/www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.md5 b/www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.md5 new file mode 100644 index 000000000000..7a62aaeb0c1e --- /dev/null +++ b/www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.md5 @@ -0,0 +1 @@ +d09e2f9bf58fef5d7d10cbf8012848bf \ No newline at end of file diff --git a/www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.png b/www/cpp_reference/html/SBBreakpointLocation_8h__dep__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..5b75ba68470e5bdd5673b06821867fc4715ae936 GIT binary patch literal 3066 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 + + + diff --git a/www/cpp_reference/html/SBBreakpointLocation_8h__incl.md5 b/www/cpp_reference/html/SBBreakpointLocation_8h__incl.md5 new file mode 100644 index 000000000000..77902693c083 --- /dev/null +++ b/www/cpp_reference/html/SBBreakpointLocation_8h__incl.md5 @@ -0,0 +1 @@ +6dafd495404ecbce588f5bfc6969db81 \ No newline at end of file diff --git a/www/cpp_reference/html/SBBreakpointLocation_8h__incl.png b/www/cpp_reference/html/SBBreakpointLocation_8h__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..a71846c43179202b60a0b05428d5915dad0b8d5a GIT binary patch literal 18998 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 z + + + + +LLVM: SBBreakpointLocation.h Source File + + +

LLDB API Documentation

+ + + + + +
+
+
SBBreakpointLocation.h
+
+
+Go to the documentation of this file.
1 //===-- SBBreakpointLocation.h ----------------------------------*- C++ -*-===//
+
2 //
+
3 // The LLVM Compiler Infrastructure
+
4 //
+
5 // This file is distributed under the University of Illinois Open Source
+
6 // License. See LICENSE.TXT for details.
+
7 //
+
8 //===----------------------------------------------------------------------===//
+
9 
+
10 #ifndef LLDB_SBBreakpointLocation_h_
+
11 #define LLDB_SBBreakpointLocation_h_
+
12 
+
13 #include "lldb/API/SBDefines.h"
+
14 #include "lldb/API/SBBreakpoint.h"
+
15 
+
16 namespace lldb {
+
17 
+ +
19 {
+
20 public:
+
21 
+ +
23 
+ +
25 
+ +
27 
+ + +
30 
+
31  break_id_t
+
32  GetID ();
+
33 
+
34  bool
+
35  IsValid() const;
+
36 
+ +
38  GetAddress ();
+
39 
+
40  lldb::addr_t
+
41  GetLoadAddress ();
+
42 
+
43  void
+
44  SetEnabled(bool enabled);
+
45 
+
46  bool
+
47  IsEnabled ();
+
48 
+
49  uint32_t
+
50  GetIgnoreCount ();
+
51 
+
52  void
+
53  SetIgnoreCount (uint32_t n);
+
54 
+
55  void
+
56  SetCondition (const char *condition);
+
57 
+
58  const char *
+
59  GetCondition ();
+
60 
+
61  void
+
62  SetThreadID (lldb::tid_t sb_thread_id);
+
63 
+
64  lldb::tid_t
+
65  GetThreadID ();
+
66 
+
67  void
+
68  SetThreadIndex (uint32_t index);
+
69 
+
70  uint32_t
+
71  GetThreadIndex() const;
+
72 
+
73  void
+
74  SetThreadName (const char *thread_name);
+
75 
+
76  const char *
+
77  GetThreadName () const;
+
78 
+
79  void
+
80  SetQueueName (const char *queue_name);
+
81 
+
82  const char *
+
83  GetQueueName () const;
+
84 
+
85  bool
+
86  IsResolved ();
+
87 
+
88  bool
+
89  GetDescription (lldb::SBStream &description, DescriptionLevel level);
+
90 
+ +
92  GetBreakpoint ();
+
93 
+
94  SBBreakpointLocation (const lldb::BreakpointLocationSP &break_loc_sp);
+
95 
+
96 private:
+
97  friend class SBBreakpoint;
+
98 #ifndef LLDB_DISABLE_PYTHON
+ +
100 #endif
+
101  void
+
102  SetLocation (const lldb::BreakpointLocationSP &break_loc_sp);
+
103 
+
104  lldb::BreakpointLocationSP m_opaque_sp;
+
105 
+
106 };
+
107 
+
108 } // namespace lldb
+
109 
+
110 #endif // LLDB_SBBreakpointLocation_h_
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBBreakpoint_8h.html b/www/cpp_reference/html/SBBreakpoint_8h.html new file mode 100644 index 000000000000..df956e00c19c --- /dev/null +++ b/www/cpp_reference/html/SBBreakpoint_8h.html @@ -0,0 +1,74 @@ + + + + + +LLVM: SBBreakpoint.h File Reference + + +

LLDB API Documentation

+ + + + + +
+ +
+
SBBreakpoint.h File Reference
+
+
+
+Include dependency graph for SBBreakpoint.h:
+
+
+ + +
+
+This graph shows which files directly or indirectly include this file:
+
+
+ + +
+
+

Go to the source code of this file.

+ + + +

+Classes

class  lldb::SBBreakpoint
+ + +

+Namespaces

namespace  lldb
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBBreakpoint_8h__dep__incl.map b/www/cpp_reference/html/SBBreakpoint_8h__dep__incl.map new file mode 100644 index 000000000000..a5823aa33073 --- /dev/null +++ b/www/cpp_reference/html/SBBreakpoint_8h__dep__incl.map @@ -0,0 +1,4 @@ + + + + diff --git a/www/cpp_reference/html/SBBreakpoint_8h__dep__incl.md5 b/www/cpp_reference/html/SBBreakpoint_8h__dep__incl.md5 new file mode 100644 index 000000000000..9a2575c8b5fc --- /dev/null +++ b/www/cpp_reference/html/SBBreakpoint_8h__dep__incl.md5 @@ -0,0 +1 @@ +7e1d25595d16f8018947281fb5bb8f19 \ No newline at end of file diff --git a/www/cpp_reference/html/SBBreakpoint_8h__dep__incl.png b/www/cpp_reference/html/SBBreakpoint_8h__dep__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..754f53ab40f3521b46958617cb4b794b8b8c6121 GIT binary patch literal 7871 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 literal 0 HcmV?d00001 diff --git a/www/cpp_reference/html/SBBreakpoint_8h__incl.map b/www/cpp_reference/html/SBBreakpoint_8h__incl.map new file mode 100644 index 000000000000..df883f7def5d --- /dev/null +++ b/www/cpp_reference/html/SBBreakpoint_8h__incl.map @@ -0,0 +1,3 @@ + + + diff --git a/www/cpp_reference/html/SBBreakpoint_8h__incl.md5 b/www/cpp_reference/html/SBBreakpoint_8h__incl.md5 new file mode 100644 index 000000000000..92fb9f8e172f --- /dev/null +++ b/www/cpp_reference/html/SBBreakpoint_8h__incl.md5 @@ -0,0 +1 @@ +d0b32d27bcb5b7db6ccbf8ef7b3f9e9f \ No newline at end of file diff --git a/www/cpp_reference/html/SBBreakpoint_8h__incl.png b/www/cpp_reference/html/SBBreakpoint_8h__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..4093254c308001410746b8c88c026b5419880a5a GIT binary patch literal 13030 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 + + + + +LLVM: SBBreakpoint.h Source File + + +

LLDB API Documentation

+ + + + + +
+
+
SBBreakpoint.h
+
+
+Go to the documentation of this file.
1 //===-- SBBreakpoint.h ------------------------------------------*- C++ -*-===//
+
2 //
+
3 // The LLVM Compiler Infrastructure
+
4 //
+
5 // This file is distributed under the University of Illinois Open Source
+
6 // License. See LICENSE.TXT for details.
+
7 //
+
8 //===----------------------------------------------------------------------===//
+
9 
+
10 #ifndef LLDB_SBBreakpoint_h_
+
11 #define LLDB_SBBreakpoint_h_
+
12 
+
13 #include "lldb/API/SBDefines.h"
+
14 
+
15 namespace lldb {
+
16 
+ +
18 {
+
19 public:
+
20 
+
21  typedef bool (*BreakpointHitCallback) (void *baton,
+
22  SBProcess &process,
+
23  SBThread &thread,
+
24  lldb::SBBreakpointLocation &location);
+
25 
+
26  SBBreakpoint ();
+
27 
+
28  SBBreakpoint (const lldb::SBBreakpoint& rhs);
+
29 
+
30  ~SBBreakpoint();
+
31 
+
32  const lldb::SBBreakpoint &
+
33  operator = (const lldb::SBBreakpoint& rhs);
+
34 
+
35  // Tests to see if the opaque breakpoint object in this object matches the
+
36  // opaque breakpoint object in "rhs".
+
37  bool
+
38  operator == (const lldb::SBBreakpoint& rhs);
+
39 
+
40  bool
+
41  operator != (const lldb::SBBreakpoint& rhs);
+
42 
+
43  break_id_t
+
44  GetID () const;
+
45 
+
46  bool
+
47  IsValid() const;
+
48 
+
49  void
+ +
51 
+ +
53  FindLocationByAddress (lldb::addr_t vm_addr);
+
54 
+
55  lldb::break_id_t
+
56  FindLocationIDByAddress (lldb::addr_t vm_addr);
+
57 
+ +
59  FindLocationByID (lldb::break_id_t bp_loc_id);
+
60 
+ +
62  GetLocationAtIndex (uint32_t index);
+
63 
+
64  void
+
65  SetEnabled (bool enable);
+
66 
+
67  bool
+
68  IsEnabled ();
+
69 
+
70  void
+
71  SetOneShot (bool one_shot);
+
72 
+
73  bool
+
74  IsOneShot () const;
+
75 
+
76  bool
+
77  IsInternal ();
+
78 
+
79  uint32_t
+
80  GetHitCount () const;
+
81 
+
82  void
+
83  SetIgnoreCount (uint32_t count);
+
84 
+
85  uint32_t
+
86  GetIgnoreCount () const;
+
87 
+
88  void
+
89  SetCondition (const char *condition);
+
90 
+
91  const char *
+
92  GetCondition ();
+
93 
+
94  void
+
95  SetThreadID (lldb::tid_t sb_thread_id);
+
96 
+
97  lldb::tid_t
+
98  GetThreadID ();
+
99 
+
100  void
+
101  SetThreadIndex (uint32_t index);
+
102 
+
103  uint32_t
+
104  GetThreadIndex() const;
+
105 
+
106  void
+
107  SetThreadName (const char *thread_name);
+
108 
+
109  const char *
+
110  GetThreadName () const;
+
111 
+
112  void
+
113  SetQueueName (const char *queue_name);
+
114 
+
115  const char *
+
116  GetQueueName () const;
+
117 
+
118  void
+
119  SetCallback (BreakpointHitCallback callback, void *baton);
+
120 
+
121  size_t
+
122  GetNumResolvedLocations() const;
+
123 
+
124  size_t
+
125  GetNumLocations() const;
+
126 
+
127  bool
+
128  GetDescription (lldb::SBStream &description);
+
129 
+
130  static bool
+
131  EventIsBreakpointEvent (const lldb::SBEvent &event);
+
132 
+
133  static lldb::BreakpointEventType
+ +
135 
+
136  static lldb::SBBreakpoint
+
137  GetBreakpointFromEvent (const lldb::SBEvent& event);
+
138 
+ +
140  GetBreakpointLocationAtIndexFromEvent (const lldb::SBEvent& event, uint32_t loc_idx);
+
141 
+
142  static uint32_t
+ +
144 
+
145 
+
146 private:
+
147  friend class SBBreakpointLocation;
+
148  friend class SBTarget;
+
149 
+
150  SBBreakpoint (const lldb::BreakpointSP &bp_sp);
+
151 
+
152  lldb_private::Breakpoint *
+
153  operator->() const;
+
154 
+
155  lldb_private::Breakpoint *
+
156  get() const;
+
157 
+
158  lldb::BreakpointSP &
+
159  operator *();
+
160 
+
161  const lldb::BreakpointSP &
+
162  operator *() const;
+
163 
+
164  static bool
+
165  PrivateBreakpointHitCallback (void *baton,
+
166  lldb_private::StoppointCallbackContext *context,
+
167  lldb::user_id_t break_id,
+
168  lldb::user_id_t break_loc_id);
+
169 
+
170  lldb::BreakpointSP m_opaque_sp;
+
171 };
+
172 
+
173 } // namespace lldb
+
174 
+
175 #endif // LLDB_SBBreakpoint_h_
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBBroadcaster_8h.html b/www/cpp_reference/html/SBBroadcaster_8h.html new file mode 100644 index 000000000000..06b06c565983 --- /dev/null +++ b/www/cpp_reference/html/SBBroadcaster_8h.html @@ -0,0 +1,74 @@ + + + + + +LLVM: SBBroadcaster.h File Reference + + +

LLDB API Documentation

+ + + + + +
+ +
+
SBBroadcaster.h File Reference
+
+
+
+Include dependency graph for SBBroadcaster.h:
+
+
+ + +
+
+This graph shows which files directly or indirectly include this file:
+
+
+ + +
+
+

Go to the source code of this file.

+ + + +

+Classes

class  lldb::SBBroadcaster
+ + +

+Namespaces

namespace  lldb
+
+
+ +
+ + + diff --git a/www/cpp_reference/html/SBBroadcaster_8h__dep__incl.map b/www/cpp_reference/html/SBBroadcaster_8h__dep__incl.map new file mode 100644 index 000000000000..059b8ce85aaf --- /dev/null +++ b/www/cpp_reference/html/SBBroadcaster_8h__dep__incl.map @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/www/cpp_reference/html/SBBroadcaster_8h__dep__incl.md5 b/www/cpp_reference/html/SBBroadcaster_8h__dep__incl.md5 new file mode 100644 index 000000000000..860dc9d4be64 --- /dev/null +++ b/www/cpp_reference/html/SBBroadcaster_8h__dep__incl.md5 @@ -0,0 +1 @@ +263122f61bbc94575f5c75905605f2df \ No newline at end of file diff --git a/www/cpp_reference/html/SBBroadcaster_8h__dep__incl.png b/www/cpp_reference/html/SBBroadcaster_8h__dep__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..d5958026c70905bf258c4c6b9dda40e043a5f85e GIT binary patch literal 85064 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